diff options
author | Scott Wood <scottwood@freescale.com> | 2007-03-26 16:52:24 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-04-12 13:55:15 -0400 |
commit | 6e1af384f1c1742ae6d86bbf779d4fa020c509bc (patch) | |
tree | e07f1cc421fdc4e51847d26bb9bdccc53341e1cc /arch/powerpc/boot/devtree.c | |
parent | dbf8eefa2b814d6922492120bfa46d4bc42ceb20 (diff) |
[POWERPC] bootwrapper: Add dt_xlate_reg(), and use it to find serial registers.
dt_xlate_reg() uses the ranges properties of a node's parentage to find
the absolute physical address of the node's registers.
The ns16550 driver uses this when no virtual-reg property is found.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/boot/devtree.c')
-rw-r--r-- | arch/powerpc/boot/devtree.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/arch/powerpc/boot/devtree.c b/arch/powerpc/boot/devtree.c index 708cadebeb46..23492d7fb556 100644 --- a/arch/powerpc/boot/devtree.c +++ b/arch/powerpc/boot/devtree.c | |||
@@ -109,3 +109,181 @@ void __dt_fixup_mac_addresses(u32 startindex, ...) | |||
109 | } | 109 | } |
110 | va_end(ap); | 110 | va_end(ap); |
111 | } | 111 | } |
112 | |||
113 | #define MAX_ADDR_CELLS 4 | ||
114 | #define MAX_RANGES 8 | ||
115 | |||
116 | static void get_reg_format(void *node, u32 *naddr, u32 *nsize) | ||
117 | { | ||
118 | if (getprop(node, "#address-cells", naddr, 4) != 4) | ||
119 | *naddr = 2; | ||
120 | if (getprop(node, "#size-cells", nsize, 4) != 4) | ||
121 | *nsize = 1; | ||
122 | } | ||
123 | |||
124 | static void copy_val(u32 *dest, u32 *src, int naddr) | ||
125 | { | ||
126 | memset(dest, 0, (MAX_ADDR_CELLS - naddr) * 4); | ||
127 | memcpy(dest, src, naddr * 4); | ||
128 | } | ||
129 | |||
130 | static int sub_reg(u32 *reg, u32 *sub) | ||
131 | { | ||
132 | int i, borrow = 0; | ||
133 | |||
134 | for (i = 0; i < MAX_ADDR_CELLS; i++) { | ||
135 | int prev_borrow = borrow; | ||
136 | borrow = reg[i] < sub[i] + prev_borrow; | ||
137 | reg[i] -= sub[i] + prev_borrow; | ||
138 | } | ||
139 | |||
140 | return !borrow; | ||
141 | } | ||
142 | |||
143 | static int add_reg(u32 *reg, u32 *add) | ||
144 | { | ||
145 | int i, carry = 0; | ||
146 | |||
147 | for (i = 0; i < MAX_ADDR_CELLS; i++) { | ||
148 | u64 tmp = (u64)reg[i] + add[i] + carry; | ||
149 | carry = tmp >> 32; | ||
150 | reg[i] = (u32)tmp; | ||
151 | } | ||
152 | |||
153 | return !carry; | ||
154 | } | ||
155 | |||
156 | /* It is assumed that if the first byte of reg fits in a | ||
157 | * range, then the whole reg block fits. | ||
158 | */ | ||
159 | static int compare_reg(u32 *reg, u32 *range, u32 *rangesize) | ||
160 | { | ||
161 | int i; | ||
162 | u32 end; | ||
163 | |||
164 | for (i = 0; i < MAX_ADDR_CELLS; i++) { | ||
165 | if (reg[i] < range[i]) | ||
166 | return 0; | ||
167 | if (reg[i] > range[i]) | ||
168 | break; | ||
169 | } | ||
170 | |||
171 | for (i = 0; i < MAX_ADDR_CELLS; i++) { | ||
172 | end = range[i] + rangesize[i]; | ||
173 | |||
174 | if (reg[i] < end) | ||
175 | break; | ||
176 | if (reg[i] > end) | ||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | return reg[i] != end; | ||
181 | } | ||
182 | |||
183 | /* reg must be MAX_ADDR_CELLS */ | ||
184 | static int find_range(u32 *reg, u32 *ranges, int nregaddr, | ||
185 | int naddr, int nsize, int buflen) | ||
186 | { | ||
187 | int nrange = nregaddr + naddr + nsize; | ||
188 | int i; | ||
189 | |||
190 | for (i = 0; i + nrange <= buflen; i += nrange) { | ||
191 | u32 range_addr[MAX_ADDR_CELLS]; | ||
192 | u32 range_size[MAX_ADDR_CELLS]; | ||
193 | |||
194 | copy_val(range_addr, ranges + i, naddr); | ||
195 | copy_val(range_size, ranges + i + nregaddr + naddr, nsize); | ||
196 | |||
197 | if (compare_reg(reg, range_addr, range_size)) | ||
198 | return i; | ||
199 | } | ||
200 | |||
201 | return -1; | ||
202 | } | ||
203 | |||
204 | /* Currently only generic buses without special encodings are supported. | ||
205 | * In particular, PCI is not supported. Also, only the beginning of the | ||
206 | * reg block is tracked; size is ignored except in ranges. | ||
207 | */ | ||
208 | int dt_xlate_reg(void *node, int res, unsigned long *addr, | ||
209 | unsigned long *size) | ||
210 | { | ||
211 | u32 last_addr[MAX_ADDR_CELLS]; | ||
212 | u32 this_addr[MAX_ADDR_CELLS]; | ||
213 | u32 buf[MAX_ADDR_CELLS * MAX_RANGES * 3]; | ||
214 | void *parent; | ||
215 | u64 ret_addr, ret_size; | ||
216 | u32 naddr, nsize, prev_naddr; | ||
217 | int buflen, offset; | ||
218 | |||
219 | parent = get_parent(node); | ||
220 | if (!parent) | ||
221 | return 0; | ||
222 | |||
223 | get_reg_format(parent, &naddr, &nsize); | ||
224 | |||
225 | if (nsize > 2) | ||
226 | return 0; | ||
227 | |||
228 | buflen = getprop(node, "reg", buf, sizeof(buf)) / 4; | ||
229 | offset = (naddr + nsize) * res; | ||
230 | |||
231 | if (buflen < offset + naddr + nsize) | ||
232 | return 0; | ||
233 | |||
234 | copy_val(last_addr, buf + offset, naddr); | ||
235 | |||
236 | ret_size = buf[offset + naddr]; | ||
237 | if (nsize == 2) { | ||
238 | ret_size <<= 32; | ||
239 | ret_size |= buf[offset + naddr + 1]; | ||
240 | } | ||
241 | |||
242 | while ((node = get_parent(node))) { | ||
243 | prev_naddr = naddr; | ||
244 | |||
245 | get_reg_format(node, &naddr, &nsize); | ||
246 | |||
247 | buflen = getprop(node, "ranges", buf, sizeof(buf)); | ||
248 | if (buflen < 0) | ||
249 | continue; | ||
250 | if (buflen > sizeof(buf)) | ||
251 | return 0; | ||
252 | |||
253 | offset = find_range(last_addr, buf, prev_naddr, | ||
254 | naddr, nsize, buflen / 4); | ||
255 | |||
256 | if (offset < 0) | ||
257 | return 0; | ||
258 | |||
259 | copy_val(this_addr, buf + offset, prev_naddr); | ||
260 | |||
261 | if (!sub_reg(last_addr, this_addr)) | ||
262 | return 0; | ||
263 | |||
264 | copy_val(this_addr, buf + offset + prev_naddr, naddr); | ||
265 | |||
266 | if (!add_reg(last_addr, this_addr)) | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | if (naddr > 2) | ||
271 | return 0; | ||
272 | |||
273 | ret_addr = last_addr[0]; | ||
274 | if (naddr == 2) { | ||
275 | ret_addr <<= 32; | ||
276 | ret_addr |= last_addr[1]; | ||
277 | } | ||
278 | |||
279 | if (sizeof(void *) == 4 && | ||
280 | (ret_addr >= 0x100000000ULL || ret_size > 0x100000000ULL || | ||
281 | ret_addr + ret_size > 0x100000000ULL)) | ||
282 | return 0; | ||
283 | |||
284 | *addr = ret_addr; | ||
285 | if (size) | ||
286 | *size = ret_size; | ||
287 | |||
288 | return 1; | ||
289 | } | ||