diff options
Diffstat (limited to 'arch/sparc/kernel/of_device.c')
| -rw-r--r-- | arch/sparc/kernel/of_device.c | 173 |
1 files changed, 113 insertions, 60 deletions
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index bc956c530376..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 | 461 | ||
| 428 | memcpy(addr, ranges + na, 4 * pna); | 462 | return 1; |
| 429 | |||
| 430 | finish: | ||
| 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 */ |
| @@ -695,9 +746,11 @@ int of_device_register(struct of_device *ofdev) | |||
| 695 | if (rc) | 746 | if (rc) |
| 696 | return rc; | 747 | return rc; |
| 697 | 748 | ||
| 698 | device_create_file(&ofdev->dev, &dev_attr_devspec); | 749 | rc = device_create_file(&ofdev->dev, &dev_attr_devspec); |
| 750 | if (rc) | ||
| 751 | device_unregister(&ofdev->dev); | ||
| 699 | 752 | ||
| 700 | return 0; | 753 | return rc; |
| 701 | } | 754 | } |
| 702 | 755 | ||
| 703 | void of_device_unregister(struct of_device *ofdev) | 756 | void of_device_unregister(struct of_device *ofdev) |
