diff options
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/of_device.c | 167 |
1 files changed, 109 insertions, 58 deletions
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index bbd3203b86af..5a2faad5d043 100644 --- a/arch/sparc/kernel/of_device.c +++ b/arch/sparc/kernel/of_device.c | |||
@@ -183,7 +183,7 @@ struct bus_type of_bus_type = { | |||
183 | }; | 183 | }; |
184 | EXPORT_SYMBOL(of_bus_type); | 184 | EXPORT_SYMBOL(of_bus_type); |
185 | 185 | ||
186 | static inline u64 of_read_addr(u32 *cell, int size) | 186 | static inline u64 of_read_addr(const u32 *cell, int size) |
187 | { | 187 | { |
188 | u64 r = 0; | 188 | u64 r = 0; |
189 | while (size--) | 189 | while (size--) |
@@ -209,8 +209,8 @@ struct of_bus { | |||
209 | int (*match)(struct device_node *parent); | 209 | int (*match)(struct device_node *parent); |
210 | void (*count_cells)(struct device_node *child, | 210 | void (*count_cells)(struct device_node *child, |
211 | int *addrc, int *sizec); | 211 | int *addrc, int *sizec); |
212 | u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); | 212 | int (*map)(u32 *addr, const u32 *range, |
213 | int (*translate)(u32 *addr, u64 offset, int na); | 213 | int na, int ns, int pna); |
214 | unsigned int (*get_flags)(u32 *addr); | 214 | unsigned int (*get_flags)(u32 *addr); |
215 | }; | 215 | }; |
216 | 216 | ||
@@ -224,27 +224,49 @@ static void of_bus_default_count_cells(struct device_node *dev, | |||
224 | get_cells(dev, addrc, sizec); | 224 | get_cells(dev, addrc, sizec); |
225 | } | 225 | } |
226 | 226 | ||
227 | static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) | 227 | /* Make sure the least significant 64-bits are in-range. Even |
228 | * for 3 or 4 cell values it is a good enough approximation. | ||
229 | */ | ||
230 | static int of_out_of_range(const u32 *addr, const u32 *base, | ||
231 | const u32 *size, int na, int ns) | ||
228 | { | 232 | { |
229 | u64 cp, s, da; | 233 | u64 a = of_read_addr(addr, na); |
234 | u64 b = of_read_addr(base, na); | ||
235 | |||
236 | if (a < b) | ||
237 | return 1; | ||
230 | 238 | ||
231 | cp = of_read_addr(range, na); | 239 | b += of_read_addr(size, ns); |
232 | s = of_read_addr(range + na + pna, ns); | 240 | if (a >= b) |
233 | da = of_read_addr(addr, na); | 241 | return 1; |
234 | 242 | ||
235 | if (da < cp || da >= (cp + s)) | 243 | return 0; |
236 | return OF_BAD_ADDR; | ||
237 | return da - cp; | ||
238 | } | 244 | } |
239 | 245 | ||
240 | static int of_bus_default_translate(u32 *addr, u64 offset, int na) | 246 | static int of_bus_default_map(u32 *addr, const u32 *range, |
247 | int na, int ns, int pna) | ||
241 | { | 248 | { |
242 | u64 a = of_read_addr(addr, na); | 249 | u32 result[OF_MAX_ADDR_CELLS]; |
243 | memset(addr, 0, na * 4); | 250 | int i; |
244 | a += offset; | 251 | |
245 | if (na > 1) | 252 | if (ns > 2) { |
246 | addr[na - 2] = a >> 32; | 253 | printk("of_device: Cannot handle size cells (%d) > 2.", ns); |
247 | addr[na - 1] = a & 0xffffffffu; | 254 | return -EINVAL; |
255 | } | ||
256 | |||
257 | if (of_out_of_range(addr, range, range + na + pna, na, ns)) | ||
258 | return -EINVAL; | ||
259 | |||
260 | /* Start with the parent range base. */ | ||
261 | memcpy(result, range + na, pna * 4); | ||
262 | |||
263 | /* Add in the child address offset. */ | ||
264 | for (i = 0; i < na; i++) | ||
265 | result[pna - 1 - i] += | ||
266 | (addr[na - 1 - i] - | ||
267 | range[na - 1 - i]); | ||
268 | |||
269 | memcpy(addr, result, pna * 4); | ||
248 | 270 | ||
249 | return 0; | 271 | return 0; |
250 | } | 272 | } |
@@ -254,14 +276,26 @@ static unsigned int of_bus_default_get_flags(u32 *addr) | |||
254 | return IORESOURCE_MEM; | 276 | return IORESOURCE_MEM; |
255 | } | 277 | } |
256 | 278 | ||
257 | |||
258 | /* | 279 | /* |
259 | * PCI bus specific translator | 280 | * PCI bus specific translator |
260 | */ | 281 | */ |
261 | 282 | ||
262 | static int of_bus_pci_match(struct device_node *np) | 283 | static int of_bus_pci_match(struct device_node *np) |
263 | { | 284 | { |
264 | return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex"); | 285 | if (!strcmp(np->type, "pci") || !strcmp(np->type, "pciex")) { |
286 | /* Do not do PCI specific frobbing if the | ||
287 | * PCI bridge lacks a ranges property. We | ||
288 | * want to pass it through up to the next | ||
289 | * parent as-is, not with the PCI translate | ||
290 | * method which chops off the top address cell. | ||
291 | */ | ||
292 | if (!of_find_property(np, "ranges", NULL)) | ||
293 | return 0; | ||
294 | |||
295 | return 1; | ||
296 | } | ||
297 | |||
298 | return 0; | ||
265 | } | 299 | } |
266 | 300 | ||
267 | static void of_bus_pci_count_cells(struct device_node *np, | 301 | static void of_bus_pci_count_cells(struct device_node *np, |
@@ -273,27 +307,32 @@ static void of_bus_pci_count_cells(struct device_node *np, | |||
273 | *sizec = 2; | 307 | *sizec = 2; |
274 | } | 308 | } |
275 | 309 | ||
276 | static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) | 310 | static int of_bus_pci_map(u32 *addr, const u32 *range, |
311 | int na, int ns, int pna) | ||
277 | { | 312 | { |
278 | u64 cp, s, da; | 313 | u32 result[OF_MAX_ADDR_CELLS]; |
314 | int i; | ||
279 | 315 | ||
280 | /* Check address type match */ | 316 | /* Check address type match */ |
281 | if ((addr[0] ^ range[0]) & 0x03000000) | 317 | if ((addr[0] ^ range[0]) & 0x03000000) |
282 | return OF_BAD_ADDR; | 318 | return -EINVAL; |
283 | 319 | ||
284 | /* Read address values, skipping high cell */ | 320 | if (of_out_of_range(addr + 1, range + 1, range + na + pna, |
285 | cp = of_read_addr(range + 1, na - 1); | 321 | na - 1, ns)) |
286 | s = of_read_addr(range + na + pna, ns); | 322 | return -EINVAL; |
287 | da = of_read_addr(addr + 1, na - 1); | ||
288 | 323 | ||
289 | if (da < cp || da >= (cp + s)) | 324 | /* Start with the parent range base. */ |
290 | return OF_BAD_ADDR; | 325 | memcpy(result, range + na, pna * 4); |
291 | return da - cp; | ||
292 | } | ||
293 | 326 | ||
294 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) | 327 | /* Add in the child address offset, skipping high cell. */ |
295 | { | 328 | for (i = 0; i < na - 1; i++) |
296 | return of_bus_default_translate(addr + 1, offset, na - 1); | 329 | result[pna - 1 - i] += |
330 | (addr[na - 1 - i] - | ||
331 | range[na - 1 - i]); | ||
332 | |||
333 | memcpy(addr, result, pna * 4); | ||
334 | |||
335 | return 0; | ||
297 | } | 336 | } |
298 | 337 | ||
299 | static unsigned int of_bus_pci_get_flags(u32 *addr) | 338 | static unsigned int of_bus_pci_get_flags(u32 *addr) |
@@ -332,16 +371,11 @@ static void of_bus_sbus_count_cells(struct device_node *child, | |||
332 | *sizec = 1; | 371 | *sizec = 1; |
333 | } | 372 | } |
334 | 373 | ||
335 | static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna) | 374 | static int of_bus_sbus_map(u32 *addr, const u32 *range, int na, int ns, int pna) |
336 | { | 375 | { |
337 | return of_bus_default_map(addr, range, na, ns, pna); | 376 | return of_bus_default_map(addr, range, na, ns, pna); |
338 | } | 377 | } |
339 | 378 | ||
340 | static int of_bus_sbus_translate(u32 *addr, u64 offset, int na) | ||
341 | { | ||
342 | return of_bus_default_translate(addr, offset, na); | ||
343 | } | ||
344 | |||
345 | static unsigned int of_bus_sbus_get_flags(u32 *addr) | 379 | static unsigned int of_bus_sbus_get_flags(u32 *addr) |
346 | { | 380 | { |
347 | return IORESOURCE_MEM; | 381 | return IORESOURCE_MEM; |
@@ -360,7 +394,6 @@ static struct of_bus of_busses[] = { | |||
360 | .match = of_bus_pci_match, | 394 | .match = of_bus_pci_match, |
361 | .count_cells = of_bus_pci_count_cells, | 395 | .count_cells = of_bus_pci_count_cells, |
362 | .map = of_bus_pci_map, | 396 | .map = of_bus_pci_map, |
363 | .translate = of_bus_pci_translate, | ||
364 | .get_flags = of_bus_pci_get_flags, | 397 | .get_flags = of_bus_pci_get_flags, |
365 | }, | 398 | }, |
366 | /* SBUS */ | 399 | /* SBUS */ |
@@ -370,7 +403,6 @@ static struct of_bus of_busses[] = { | |||
370 | .match = of_bus_sbus_match, | 403 | .match = of_bus_sbus_match, |
371 | .count_cells = of_bus_sbus_count_cells, | 404 | .count_cells = of_bus_sbus_count_cells, |
372 | .map = of_bus_sbus_map, | 405 | .map = of_bus_sbus_map, |
373 | .translate = of_bus_sbus_translate, | ||
374 | .get_flags = of_bus_sbus_get_flags, | 406 | .get_flags = of_bus_sbus_get_flags, |
375 | }, | 407 | }, |
376 | /* Default */ | 408 | /* Default */ |
@@ -380,7 +412,6 @@ static struct of_bus of_busses[] = { | |||
380 | .match = NULL, | 412 | .match = NULL, |
381 | .count_cells = of_bus_default_count_cells, | 413 | .count_cells = of_bus_default_count_cells, |
382 | .map = of_bus_default_map, | 414 | .map = of_bus_default_map, |
383 | .translate = of_bus_default_translate, | ||
384 | .get_flags = of_bus_default_get_flags, | 415 | .get_flags = of_bus_default_get_flags, |
385 | }, | 416 | }, |
386 | }; | 417 | }; |
@@ -405,33 +436,34 @@ static int __init build_one_resource(struct device_node *parent, | |||
405 | u32 *ranges; | 436 | u32 *ranges; |
406 | unsigned int rlen; | 437 | unsigned int rlen; |
407 | int rone; | 438 | int rone; |
408 | u64 offset = OF_BAD_ADDR; | ||
409 | 439 | ||
410 | ranges = of_get_property(parent, "ranges", &rlen); | 440 | ranges = of_get_property(parent, "ranges", &rlen); |
411 | if (ranges == NULL || rlen == 0) { | 441 | if (ranges == NULL || rlen == 0) { |
412 | offset = of_read_addr(addr, na); | 442 | u32 result[OF_MAX_ADDR_CELLS]; |
413 | memset(addr, 0, pna * 4); | 443 | int i; |
414 | goto finish; | 444 | |
445 | memset(result, 0, pna * 4); | ||
446 | for (i = 0; i < na; i++) | ||
447 | result[pna - 1 - i] = | ||
448 | addr[na - 1 - i]; | ||
449 | |||
450 | memcpy(addr, result, pna * 4); | ||
451 | return 0; | ||
415 | } | 452 | } |
416 | 453 | ||
417 | /* Now walk through the ranges */ | 454 | /* Now walk through the ranges */ |
418 | rlen /= 4; | 455 | rlen /= 4; |
419 | rone = na + pna + ns; | 456 | rone = na + pna + ns; |
420 | for (; rlen >= rone; rlen -= rone, ranges += rone) { | 457 | for (; rlen >= rone; rlen -= rone, ranges += rone) { |
421 | offset = bus->map(addr, ranges, na, ns, pna); | 458 | if (!bus->map(addr, ranges, na, ns, pna)) |
422 | if (offset != OF_BAD_ADDR) | 459 | return 0; |
423 | break; | ||
424 | } | 460 | } |
425 | if (offset == OF_BAD_ADDR) | ||
426 | return 1; | ||
427 | |||
428 | memcpy(addr, ranges + na, 4 * pna); | ||
429 | 461 | ||
430 | finish: | 462 | return 1; |
431 | /* Translate it into parent bus space */ | ||
432 | return pbus->translate(addr, offset, pna); | ||
433 | } | 463 | } |
434 | 464 | ||
465 | static int of_resource_verbose; | ||
466 | |||
435 | static void __init build_device_resources(struct of_device *op, | 467 | static void __init build_device_resources(struct of_device *op, |
436 | struct device *parent) | 468 | struct device *parent) |
437 | { | 469 | { |
@@ -497,7 +529,8 @@ static void __init build_device_resources(struct of_device *op, | |||
497 | pbus = of_match_bus(pp); | 529 | pbus = of_match_bus(pp); |
498 | pbus->count_cells(dp, &pna, &pns); | 530 | pbus->count_cells(dp, &pna, &pns); |
499 | 531 | ||
500 | if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna)) | 532 | if (build_one_resource(dp, bus, pbus, addr, |
533 | dna, dns, pna)) | ||
501 | break; | 534 | break; |
502 | 535 | ||
503 | dna = pna; | 536 | dna = pna; |
@@ -507,6 +540,12 @@ static void __init build_device_resources(struct of_device *op, | |||
507 | 540 | ||
508 | build_res: | 541 | build_res: |
509 | memset(r, 0, sizeof(*r)); | 542 | memset(r, 0, sizeof(*r)); |
543 | |||
544 | if (of_resource_verbose) | ||
545 | printk("%s reg[%d] -> %llx\n", | ||
546 | op->node->full_name, index, | ||
547 | result); | ||
548 | |||
510 | if (result != OF_BAD_ADDR) { | 549 | if (result != OF_BAD_ADDR) { |
511 | r->start = result & 0xffffffff; | 550 | r->start = result & 0xffffffff; |
512 | r->end = result + size - 1; | 551 | r->end = result + size - 1; |
@@ -643,6 +682,18 @@ static int __init of_bus_driver_init(void) | |||
643 | 682 | ||
644 | postcore_initcall(of_bus_driver_init); | 683 | postcore_initcall(of_bus_driver_init); |
645 | 684 | ||
685 | static int __init of_debug(char *str) | ||
686 | { | ||
687 | int val = 0; | ||
688 | |||
689 | get_option(&str, &val); | ||
690 | if (val & 1) | ||
691 | of_resource_verbose = 1; | ||
692 | return 1; | ||
693 | } | ||
694 | |||
695 | __setup("of_debug=", of_debug); | ||
696 | |||
646 | int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus) | 697 | int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus) |
647 | { | 698 | { |
648 | /* initialize common driver fields */ | 699 | /* initialize common driver fields */ |