1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * (c) Copyright 2006 Benjamin Herrenschmidt, IBM Corp. |
4 | * <benh@kernel.crashing.org> |
5 | */ |
6 | |
7 | #undef DEBUG |
8 | |
9 | #include <linux/kernel.h> |
10 | #include <linux/export.h> |
11 | #include <linux/of_address.h> |
12 | #include <asm/dcr.h> |
13 | |
14 | #ifdef CONFIG_PPC_DCR_MMIO |
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_property_read_bool(par, "dcr-controller" )) |
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 | } |
33 | #endif |
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 |
44 | return false; |
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 | |
115 | unsigned int dcr_resource_start(const struct device_node *np, |
116 | unsigned int index) |
117 | { |
118 | unsigned int ds; |
119 | const u32 *dr = of_get_property(node: np, name: "dcr-reg" , lenp: &ds); |
120 | |
121 | if (dr == NULL || ds & 1 || index >= (ds / 8)) |
122 | return 0; |
123 | |
124 | return dr[index * 2]; |
125 | } |
126 | EXPORT_SYMBOL_GPL(dcr_resource_start); |
127 | |
128 | unsigned int dcr_resource_len(const struct device_node *np, unsigned int index) |
129 | { |
130 | unsigned int ds; |
131 | const u32 *dr = of_get_property(node: np, name: "dcr-reg" , lenp: &ds); |
132 | |
133 | if (dr == NULL || ds & 1 || index >= (ds / 8)) |
134 | return 0; |
135 | |
136 | return dr[index * 2 + 1]; |
137 | } |
138 | EXPORT_SYMBOL_GPL(dcr_resource_len); |
139 | |
140 | #ifdef CONFIG_PPC_DCR_MMIO |
141 | |
142 | static u64 of_translate_dcr_address(struct device_node *dev, |
143 | unsigned int dcr_n, |
144 | unsigned int *out_stride) |
145 | { |
146 | struct device_node *dp; |
147 | const u32 *p; |
148 | unsigned int stride; |
149 | u64 ret = OF_BAD_ADDR; |
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 */ |
156 | p = of_get_property(dp, "dcr-mmio-stride" , NULL); |
157 | stride = (p == NULL) ? 0x10 : *p; |
158 | |
159 | /* XXX FIXME: Which property name is to use of the 2 following ? */ |
160 | p = of_get_property(dp, "dcr-mmio-range" , NULL); |
161 | if (p == NULL) |
162 | p = of_get_property(dp, "dcr-mmio-space" , NULL); |
163 | if (p == NULL) |
164 | goto done; |
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; |
172 | |
173 | done: |
174 | of_node_put(dp); |
175 | return ret; |
176 | } |
177 | |
178 | dcr_host_mmio_t dcr_map_mmio(struct device_node *dev, |
179 | unsigned int dcr_n, |
180 | unsigned int dcr_c) |
181 | { |
182 | dcr_host_mmio_t ret = { .token = NULL, .stride = 0, .base = dcr_n }; |
183 | u64 addr; |
184 | |
185 | pr_debug("dcr_map(%pOF, 0x%x, 0x%x)\n" , |
186 | dev, dcr_n, dcr_c); |
187 | |
188 | addr = of_translate_dcr_address(dev, dcr_n, &ret.stride); |
189 | pr_debug("translates to addr: 0x%llx, stride: 0x%x\n" , |
190 | (unsigned long long) addr, ret.stride); |
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 | } |
202 | EXPORT_SYMBOL_GPL(dcr_map_mmio); |
203 | |
204 | void dcr_unmap_mmio(dcr_host_mmio_t host, unsigned int dcr_c) |
205 | { |
206 | dcr_host_mmio_t h = host; |
207 | |
208 | if (h.token == NULL) |
209 | return; |
210 | h.token += host.base * h.stride; |
211 | iounmap(h.token); |
212 | h.token = NULL; |
213 | } |
214 | EXPORT_SYMBOL_GPL(dcr_unmap_mmio); |
215 | |
216 | #endif /* defined(CONFIG_PPC_DCR_MMIO) */ |
217 | |
218 | #ifdef CONFIG_PPC_DCR_NATIVE |
219 | DEFINE_SPINLOCK(dcr_ind_lock); |
220 | EXPORT_SYMBOL_GPL(dcr_ind_lock); |
221 | #endif /* defined(CONFIG_PPC_DCR_NATIVE) */ |
222 | |
223 | |