Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
4c75a6f4 BH |
2 | /* |
3 | * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp. | |
4 | * <benh@kernel.crashing.org> | |
4c75a6f4 BH |
5 | */ |
6 | ||
7 | #undef DEBUG | |
8 | ||
9 | #include <linux/kernel.h> | |
66b15db6 | 10 | #include <linux/export.h> |
4c75a6f4 BH |
11 | #include <asm/prom.h> |
12 | #include <asm/dcr.h> | |
13 | ||
0723abd0 | 14 | #ifdef CONFIG_PPC_DCR_MMIO |
b786af11 SN |
15 | static struct device_node *find_dcr_parent(struct device_node *node) |
16 | { | |
17 | struct device_node *par, *tmp; | |
18 | const u32 *p; | |
19 | ||
20 | for (par = of_node_get(node); par;) { | |
21 | if (of_get_property(par, "dcr-controller", NULL)) | |
22 | break; | |
23 | p = of_get_property(par, "dcr-parent", NULL); | |
24 | tmp = par; | |
25 | if (p == NULL) | |
26 | par = of_get_parent(par); | |
27 | else | |
28 | par = of_find_node_by_phandle(*p); | |
29 | of_node_put(tmp); | |
30 | } | |
31 | return par; | |
32 | } | |
0723abd0 | 33 | #endif |
b786af11 SN |
34 | |
35 | #if defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) | |
36 | ||
37 | bool dcr_map_ok_generic(dcr_host_t host) | |
38 | { | |
39 | if (host.type == DCR_HOST_NATIVE) | |
40 | return dcr_map_ok_native(host.host.native); | |
41 | else if (host.type == DCR_HOST_MMIO) | |
42 | return dcr_map_ok_mmio(host.host.mmio); | |
43 | else | |
acdb6685 | 44 | return false; |
b786af11 SN |
45 | } |
46 | EXPORT_SYMBOL_GPL(dcr_map_ok_generic); | |
47 | ||
48 | dcr_host_t dcr_map_generic(struct device_node *dev, | |
49 | unsigned int dcr_n, | |
50 | unsigned int dcr_c) | |
51 | { | |
52 | dcr_host_t host; | |
53 | struct device_node *dp; | |
54 | const char *prop; | |
55 | ||
56 | host.type = DCR_HOST_INVALID; | |
57 | ||
58 | dp = find_dcr_parent(dev); | |
59 | if (dp == NULL) | |
60 | return host; | |
61 | ||
62 | prop = of_get_property(dp, "dcr-access-method", NULL); | |
63 | ||
64 | pr_debug("dcr_map_generic(dcr-access-method = %s)\n", prop); | |
65 | ||
66 | if (!strcmp(prop, "native")) { | |
67 | host.type = DCR_HOST_NATIVE; | |
68 | host.host.native = dcr_map_native(dev, dcr_n, dcr_c); | |
69 | } else if (!strcmp(prop, "mmio")) { | |
70 | host.type = DCR_HOST_MMIO; | |
71 | host.host.mmio = dcr_map_mmio(dev, dcr_n, dcr_c); | |
72 | } | |
73 | ||
74 | of_node_put(dp); | |
75 | return host; | |
76 | } | |
77 | EXPORT_SYMBOL_GPL(dcr_map_generic); | |
78 | ||
79 | void dcr_unmap_generic(dcr_host_t host, unsigned int dcr_c) | |
80 | { | |
81 | if (host.type == DCR_HOST_NATIVE) | |
82 | dcr_unmap_native(host.host.native, dcr_c); | |
83 | else if (host.type == DCR_HOST_MMIO) | |
84 | dcr_unmap_mmio(host.host.mmio, dcr_c); | |
85 | else /* host.type == DCR_HOST_INVALID */ | |
86 | WARN_ON(true); | |
87 | } | |
88 | EXPORT_SYMBOL_GPL(dcr_unmap_generic); | |
89 | ||
90 | u32 dcr_read_generic(dcr_host_t host, unsigned int dcr_n) | |
91 | { | |
92 | if (host.type == DCR_HOST_NATIVE) | |
93 | return dcr_read_native(host.host.native, dcr_n); | |
94 | else if (host.type == DCR_HOST_MMIO) | |
95 | return dcr_read_mmio(host.host.mmio, dcr_n); | |
96 | else /* host.type == DCR_HOST_INVALID */ | |
97 | WARN_ON(true); | |
98 | return 0; | |
99 | } | |
100 | EXPORT_SYMBOL_GPL(dcr_read_generic); | |
101 | ||
102 | void dcr_write_generic(dcr_host_t host, unsigned int dcr_n, u32 value) | |
103 | { | |
104 | if (host.type == DCR_HOST_NATIVE) | |
105 | dcr_write_native(host.host.native, dcr_n, value); | |
106 | else if (host.type == DCR_HOST_MMIO) | |
107 | dcr_write_mmio(host.host.mmio, dcr_n, value); | |
108 | else /* host.type == DCR_HOST_INVALID */ | |
109 | WARN_ON(true); | |
110 | } | |
111 | EXPORT_SYMBOL_GPL(dcr_write_generic); | |
112 | ||
113 | #endif /* defined(CONFIG_PPC_DCR_NATIVE) && defined(CONFIG_PPC_DCR_MMIO) */ | |
114 | ||
e14d7749 GE |
115 | unsigned int dcr_resource_start(const struct device_node *np, |
116 | unsigned int index) | |
4c75a6f4 BH |
117 | { |
118 | unsigned int ds; | |
e2eb6392 | 119 | const u32 *dr = of_get_property(np, "dcr-reg", &ds); |
4c75a6f4 BH |
120 | |
121 | if (dr == NULL || ds & 1 || index >= (ds / 8)) | |
122 | return 0; | |
123 | ||
124 | return dr[index * 2]; | |
125 | } | |
96b952dd | 126 | EXPORT_SYMBOL_GPL(dcr_resource_start); |
4c75a6f4 | 127 | |
e14d7749 | 128 | unsigned int dcr_resource_len(const struct device_node *np, unsigned int index) |
4c75a6f4 BH |
129 | { |
130 | unsigned int ds; | |
e2eb6392 | 131 | const u32 *dr = of_get_property(np, "dcr-reg", &ds); |
4c75a6f4 BH |
132 | |
133 | if (dr == NULL || ds & 1 || index >= (ds / 8)) | |
134 | return 0; | |
135 | ||
136 | return dr[index * 2 + 1]; | |
137 | } | |
96b952dd | 138 | EXPORT_SYMBOL_GPL(dcr_resource_len); |
4c75a6f4 | 139 | |
b786af11 | 140 | #ifdef CONFIG_PPC_DCR_MMIO |
4c75a6f4 | 141 | |
ba52464a GL |
142 | static u64 of_translate_dcr_address(struct device_node *dev, |
143 | unsigned int dcr_n, | |
144 | unsigned int *out_stride) | |
4c75a6f4 BH |
145 | { |
146 | struct device_node *dp; | |
147 | const u32 *p; | |
148 | unsigned int stride; | |
b786af11 | 149 | u64 ret = OF_BAD_ADDR; |
4c75a6f4 BH |
150 | |
151 | dp = find_dcr_parent(dev); | |
152 | if (dp == NULL) | |
153 | return OF_BAD_ADDR; | |
154 | ||
155 | /* Stride is not properly defined yet, default to 0x10 for Axon */ | |
e2eb6392 | 156 | p = of_get_property(dp, "dcr-mmio-stride", NULL); |
4c75a6f4 BH |
157 | stride = (p == NULL) ? 0x10 : *p; |
158 | ||
159 | /* XXX FIXME: Which property name is to use of the 2 following ? */ | |
e2eb6392 | 160 | p = of_get_property(dp, "dcr-mmio-range", NULL); |
4c75a6f4 | 161 | if (p == NULL) |
e2eb6392 | 162 | p = of_get_property(dp, "dcr-mmio-space", NULL); |
4c75a6f4 | 163 | if (p == NULL) |
b786af11 | 164 | goto done; |
4c75a6f4 BH |
165 | |
166 | /* Maybe could do some better range checking here */ | |
167 | ret = of_translate_address(dp, p); | |
168 | if (ret != OF_BAD_ADDR) | |
169 | ret += (u64)(stride) * (u64)dcr_n; | |
170 | if (out_stride) | |
171 | *out_stride = stride; | |
b786af11 SN |
172 | |
173 | done: | |
174 | of_node_put(dp); | |
4c75a6f4 BH |
175 | return ret; |
176 | } | |
177 | ||
b786af11 SN |
178 | dcr_host_mmio_t dcr_map_mmio(struct device_node *dev, |
179 | unsigned int dcr_n, | |
180 | unsigned int dcr_c) | |
4c75a6f4 | 181 | { |
b786af11 | 182 | dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n }; |
4c75a6f4 BH |
183 | u64 addr; |
184 | ||
b7c670d6 RH |
185 | pr_debug("dcr_map(%pOF, 0x%x, 0x%x)\n", |
186 | dev, dcr_n, dcr_c); | |
4c75a6f4 BH |
187 | |
188 | addr = of_translate_dcr_address(dev, dcr_n, &ret.stride); | |
b786af11 SN |
189 | pr_debug("translates to addr: 0x%llx, stride: 0x%x\n", |
190 | (unsigned long long) addr, ret.stride); | |
4c75a6f4 BH |
191 | if (addr == OF_BAD_ADDR) |
192 | return ret; | |
193 | pr_debug("mapping 0x%x bytes\n", dcr_c * ret.stride); | |
194 | ret.token = ioremap(addr, dcr_c * ret.stride); | |
195 | if (ret.token == NULL) | |
196 | return ret; | |
197 | pr_debug("mapped at 0x%p -> base is 0x%p\n", | |
198 | ret.token, ret.token - dcr_n * ret.stride); | |
199 | ret.token -= dcr_n * ret.stride; | |
200 | return ret; | |
201 | } | |
b786af11 | 202 | EXPORT_SYMBOL_GPL(dcr_map_mmio); |
4c75a6f4 | 203 | |
b786af11 | 204 | void dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c) |
4c75a6f4 | 205 | { |
b786af11 | 206 | dcr_host_mmio_t h = host; |
4c75a6f4 BH |
207 | |
208 | if (h.token == NULL) | |
209 | return; | |
cdbd3865 | 210 | h.token += host.base * h.stride; |
4c75a6f4 BH |
211 | iounmap(h.token); |
212 | h.token = NULL; | |
213 | } | |
b786af11 SN |
214 | EXPORT_SYMBOL_GPL(dcr_unmap_mmio); |
215 | ||
216 | #endif /* defined(CONFIG_PPC_DCR_MMIO) */ | |
217 | ||
218 | #ifdef CONFIG_PPC_DCR_NATIVE | |
853265e5 | 219 | DEFINE_SPINLOCK(dcr_ind_lock); |
22e55fcf | 220 | EXPORT_SYMBOL_GPL(dcr_ind_lock); |
b786af11 SN |
221 | #endif /* defined(CONFIG_PPC_DCR_NATIVE) */ |
222 |