diff options
author | David S. Miller <davem@davemloft.net> | 2006-06-29 17:34:50 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-06-29 19:37:12 -0400 |
commit | cf44bbc26cf1361b692ab68c884f6a0df7da2fdb (patch) | |
tree | e16c980a25df8a31445a005d75da2c37f30a7b67 /arch/sparc64 | |
parent | 3ae9a3489a4e2ba665a344a9250c2af05b7b0c59 (diff) |
[SPARC]: Beginnings of generic of_device framework.
The idea is to fully construct the device register and
interrupt values into these of_device objects, and convert
all of SBUS, EBUS, ISA drivers to use this new stuff.
Much ideas and code taken from Ben H.'s powerpc work.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/of_device.c | 490 |
1 files changed, 488 insertions, 2 deletions
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 768475bbce82..00502dc280ef 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c | |||
@@ -163,10 +163,492 @@ struct bus_type sbus_bus_type = { | |||
163 | EXPORT_SYMBOL(sbus_bus_type); | 163 | EXPORT_SYMBOL(sbus_bus_type); |
164 | #endif | 164 | #endif |
165 | 165 | ||
166 | struct bus_type of_bus_type = { | ||
167 | .name = "of", | ||
168 | .match = of_platform_bus_match, | ||
169 | .probe = of_device_probe, | ||
170 | .remove = of_device_remove, | ||
171 | .suspend = of_device_suspend, | ||
172 | .resume = of_device_resume, | ||
173 | }; | ||
174 | EXPORT_SYMBOL(of_bus_type); | ||
175 | |||
176 | static inline u64 of_read_addr(u32 *cell, int size) | ||
177 | { | ||
178 | u64 r = 0; | ||
179 | while (size--) | ||
180 | r = (r << 32) | *(cell++); | ||
181 | return r; | ||
182 | } | ||
183 | |||
184 | static void __init get_cells(struct device_node *dp, | ||
185 | int *addrc, int *sizec) | ||
186 | { | ||
187 | if (addrc) | ||
188 | *addrc = of_n_addr_cells(dp); | ||
189 | if (sizec) | ||
190 | *sizec = of_n_size_cells(dp); | ||
191 | } | ||
192 | |||
193 | /* Max address size we deal with */ | ||
194 | #define OF_MAX_ADDR_CELLS 4 | ||
195 | |||
196 | struct of_bus { | ||
197 | const char *name; | ||
198 | const char *addr_prop_name; | ||
199 | int (*match)(struct device_node *parent); | ||
200 | void (*count_cells)(struct device_node *child, | ||
201 | int *addrc, int *sizec); | ||
202 | u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); | ||
203 | int (*translate)(u32 *addr, u64 offset, int na); | ||
204 | unsigned int (*get_flags)(u32 *addr); | ||
205 | }; | ||
206 | |||
207 | /* | ||
208 | * Default translator (generic bus) | ||
209 | */ | ||
210 | |||
211 | static void of_bus_default_count_cells(struct device_node *dev, | ||
212 | int *addrc, int *sizec) | ||
213 | { | ||
214 | get_cells(dev, addrc, sizec); | ||
215 | } | ||
216 | |||
217 | static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
218 | { | ||
219 | u64 cp, s, da; | ||
220 | |||
221 | cp = of_read_addr(range, na); | ||
222 | s = of_read_addr(range + na + pna, ns); | ||
223 | da = of_read_addr(addr, na); | ||
224 | |||
225 | if (da < cp || da >= (cp + s)) | ||
226 | return OF_BAD_ADDR; | ||
227 | return da - cp; | ||
228 | } | ||
229 | |||
230 | static int of_bus_default_translate(u32 *addr, u64 offset, int na) | ||
231 | { | ||
232 | u64 a = of_read_addr(addr, na); | ||
233 | memset(addr, 0, na * 4); | ||
234 | a += offset; | ||
235 | if (na > 1) | ||
236 | addr[na - 2] = a >> 32; | ||
237 | addr[na - 1] = a & 0xffffffffu; | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | static unsigned int of_bus_default_get_flags(u32 *addr) | ||
243 | { | ||
244 | return IORESOURCE_MEM; | ||
245 | } | ||
246 | |||
247 | |||
248 | /* | ||
249 | * PCI bus specific translator | ||
250 | */ | ||
251 | |||
252 | static int of_bus_pci_match(struct device_node *np) | ||
253 | { | ||
254 | return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex"); | ||
255 | } | ||
256 | |||
257 | static void of_bus_pci_count_cells(struct device_node *np, | ||
258 | int *addrc, int *sizec) | ||
259 | { | ||
260 | if (addrc) | ||
261 | *addrc = 3; | ||
262 | if (sizec) | ||
263 | *sizec = 2; | ||
264 | } | ||
265 | |||
266 | static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
267 | { | ||
268 | u64 cp, s, da; | ||
269 | |||
270 | /* Check address type match */ | ||
271 | if ((addr[0] ^ range[0]) & 0x03000000) | ||
272 | return OF_BAD_ADDR; | ||
273 | |||
274 | /* Read address values, skipping high cell */ | ||
275 | cp = of_read_addr(range + 1, na - 1); | ||
276 | s = of_read_addr(range + na + pna, ns); | ||
277 | da = of_read_addr(addr + 1, na - 1); | ||
278 | |||
279 | if (da < cp || da >= (cp + s)) | ||
280 | return OF_BAD_ADDR; | ||
281 | return da - cp; | ||
282 | } | ||
283 | |||
284 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) | ||
285 | { | ||
286 | return of_bus_default_translate(addr + 1, offset, na - 1); | ||
287 | } | ||
288 | |||
289 | static unsigned int of_bus_pci_get_flags(u32 *addr) | ||
290 | { | ||
291 | unsigned int flags = 0; | ||
292 | u32 w = addr[0]; | ||
293 | |||
294 | switch((w >> 24) & 0x03) { | ||
295 | case 0x01: | ||
296 | flags |= IORESOURCE_IO; | ||
297 | case 0x02: /* 32 bits */ | ||
298 | case 0x03: /* 64 bits */ | ||
299 | flags |= IORESOURCE_MEM; | ||
300 | } | ||
301 | if (w & 0x40000000) | ||
302 | flags |= IORESOURCE_PREFETCH; | ||
303 | return flags; | ||
304 | } | ||
305 | |||
306 | /* | ||
307 | * ISA bus specific translator | ||
308 | */ | ||
309 | |||
310 | static int of_bus_isa_match(struct device_node *np) | ||
311 | { | ||
312 | return !strcmp(np->name, "isa"); | ||
313 | } | ||
314 | |||
315 | static void of_bus_isa_count_cells(struct device_node *child, | ||
316 | int *addrc, int *sizec) | ||
317 | { | ||
318 | if (addrc) | ||
319 | *addrc = 2; | ||
320 | if (sizec) | ||
321 | *sizec = 1; | ||
322 | } | ||
323 | |||
324 | static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
325 | { | ||
326 | u64 cp, s, da; | ||
327 | |||
328 | /* Check address type match */ | ||
329 | if ((addr[0] ^ range[0]) & 0x00000001) | ||
330 | return OF_BAD_ADDR; | ||
331 | |||
332 | /* Read address values, skipping high cell */ | ||
333 | cp = of_read_addr(range + 1, na - 1); | ||
334 | s = of_read_addr(range + na + pna, ns); | ||
335 | da = of_read_addr(addr + 1, na - 1); | ||
336 | |||
337 | if (da < cp || da >= (cp + s)) | ||
338 | return OF_BAD_ADDR; | ||
339 | return da - cp; | ||
340 | } | ||
341 | |||
342 | static int of_bus_isa_translate(u32 *addr, u64 offset, int na) | ||
343 | { | ||
344 | return of_bus_default_translate(addr + 1, offset, na - 1); | ||
345 | } | ||
346 | |||
347 | static unsigned int of_bus_isa_get_flags(u32 *addr) | ||
348 | { | ||
349 | unsigned int flags = 0; | ||
350 | u32 w = addr[0]; | ||
351 | |||
352 | if (w & 1) | ||
353 | flags |= IORESOURCE_IO; | ||
354 | else | ||
355 | flags |= IORESOURCE_MEM; | ||
356 | return flags; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * SBUS bus specific translator | ||
361 | */ | ||
362 | |||
363 | static int of_bus_sbus_match(struct device_node *np) | ||
364 | { | ||
365 | return !strcmp(np->name, "sbus") || | ||
366 | !strcmp(np->name, "sbi"); | ||
367 | } | ||
368 | |||
369 | static void of_bus_sbus_count_cells(struct device_node *child, | ||
370 | int *addrc, int *sizec) | ||
371 | { | ||
372 | if (addrc) | ||
373 | *addrc = 2; | ||
374 | if (sizec) | ||
375 | *sizec = 1; | ||
376 | } | ||
377 | |||
378 | static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
379 | { | ||
380 | return of_bus_default_map(addr, range, na, ns, pna); | ||
381 | } | ||
382 | |||
383 | static int of_bus_sbus_translate(u32 *addr, u64 offset, int na) | ||
384 | { | ||
385 | return of_bus_default_translate(addr, offset, na); | ||
386 | } | ||
387 | |||
388 | static unsigned int of_bus_sbus_get_flags(u32 *addr) | ||
389 | { | ||
390 | return IORESOURCE_MEM; | ||
391 | } | ||
392 | |||
393 | |||
394 | /* | ||
395 | * Array of bus specific translators | ||
396 | */ | ||
397 | |||
398 | static struct of_bus of_busses[] = { | ||
399 | /* PCI */ | ||
400 | { | ||
401 | .name = "pci", | ||
402 | .addr_prop_name = "assigned-addresses", | ||
403 | .match = of_bus_pci_match, | ||
404 | .count_cells = of_bus_pci_count_cells, | ||
405 | .map = of_bus_pci_map, | ||
406 | .translate = of_bus_pci_translate, | ||
407 | .get_flags = of_bus_pci_get_flags, | ||
408 | }, | ||
409 | /* ISA */ | ||
410 | { | ||
411 | .name = "isa", | ||
412 | .addr_prop_name = "reg", | ||
413 | .match = of_bus_isa_match, | ||
414 | .count_cells = of_bus_isa_count_cells, | ||
415 | .map = of_bus_isa_map, | ||
416 | .translate = of_bus_isa_translate, | ||
417 | .get_flags = of_bus_isa_get_flags, | ||
418 | }, | ||
419 | /* SBUS */ | ||
420 | { | ||
421 | .name = "sbus", | ||
422 | .addr_prop_name = "reg", | ||
423 | .match = of_bus_sbus_match, | ||
424 | .count_cells = of_bus_sbus_count_cells, | ||
425 | .map = of_bus_sbus_map, | ||
426 | .translate = of_bus_sbus_translate, | ||
427 | .get_flags = of_bus_sbus_get_flags, | ||
428 | }, | ||
429 | /* Default */ | ||
430 | { | ||
431 | .name = "default", | ||
432 | .addr_prop_name = "reg", | ||
433 | .match = NULL, | ||
434 | .count_cells = of_bus_default_count_cells, | ||
435 | .map = of_bus_default_map, | ||
436 | .translate = of_bus_default_translate, | ||
437 | .get_flags = of_bus_default_get_flags, | ||
438 | }, | ||
439 | }; | ||
440 | |||
441 | static struct of_bus *of_match_bus(struct device_node *np) | ||
442 | { | ||
443 | int i; | ||
444 | |||
445 | for (i = 0; i < ARRAY_SIZE(of_busses); i ++) | ||
446 | if (!of_busses[i].match || of_busses[i].match(np)) | ||
447 | return &of_busses[i]; | ||
448 | BUG(); | ||
449 | return NULL; | ||
450 | } | ||
451 | |||
452 | static int __init build_one_resource(struct device_node *parent, | ||
453 | struct of_bus *bus, | ||
454 | struct of_bus *pbus, | ||
455 | u32 *addr, | ||
456 | int na, int ns, int pna) | ||
457 | { | ||
458 | u32 *ranges; | ||
459 | unsigned int rlen; | ||
460 | int rone; | ||
461 | u64 offset = OF_BAD_ADDR; | ||
462 | |||
463 | ranges = of_get_property(parent, "ranges", &rlen); | ||
464 | if (ranges == NULL || rlen == 0) { | ||
465 | offset = of_read_addr(addr, na); | ||
466 | memset(addr, 0, pna * 4); | ||
467 | goto finish; | ||
468 | } | ||
469 | |||
470 | /* Now walk through the ranges */ | ||
471 | rlen /= 4; | ||
472 | rone = na + pna + ns; | ||
473 | for (; rlen >= rone; rlen -= rone, ranges += rone) { | ||
474 | offset = bus->map(addr, ranges, na, ns, pna); | ||
475 | if (offset != OF_BAD_ADDR) | ||
476 | break; | ||
477 | } | ||
478 | if (offset == OF_BAD_ADDR) | ||
479 | return 1; | ||
480 | |||
481 | memcpy(addr, ranges + na, 4 * pna); | ||
482 | |||
483 | finish: | ||
484 | /* Translate it into parent bus space */ | ||
485 | return pbus->translate(addr, offset, pna); | ||
486 | } | ||
487 | |||
488 | static void __init build_device_resources(struct of_device *op, | ||
489 | struct device *parent) | ||
490 | { | ||
491 | struct of_device *p_op; | ||
492 | struct of_bus *bus; | ||
493 | int na, ns; | ||
494 | int index, num_reg; | ||
495 | void *preg; | ||
496 | |||
497 | if (!parent) | ||
498 | return; | ||
499 | |||
500 | p_op = to_of_device(parent); | ||
501 | bus = of_match_bus(p_op->node); | ||
502 | bus->count_cells(op->node, &na, &ns); | ||
503 | |||
504 | preg = of_get_property(op->node, bus->addr_prop_name, &num_reg); | ||
505 | if (!preg || num_reg == 0) | ||
506 | return; | ||
507 | |||
508 | /* Convert to num-cells. */ | ||
509 | num_reg /= 4; | ||
510 | |||
511 | /* Conver to num-entries. */ | ||
512 | num_reg /= na + ns; | ||
513 | |||
514 | for (index = 0; index < num_reg; index++) { | ||
515 | struct resource *r = &op->resource[index]; | ||
516 | u32 addr[OF_MAX_ADDR_CELLS]; | ||
517 | u32 *reg = (preg + (index * ((na + ns) * 4))); | ||
518 | struct device_node *dp = op->node; | ||
519 | struct device_node *pp = p_op->node; | ||
520 | struct of_bus *pbus; | ||
521 | u64 size, result = OF_BAD_ADDR; | ||
522 | unsigned long flags; | ||
523 | int dna, dns; | ||
524 | int pna, pns; | ||
525 | |||
526 | size = of_read_addr(reg + na, ns); | ||
527 | flags = bus->get_flags(reg); | ||
528 | |||
529 | memcpy(addr, reg, na * 4); | ||
530 | |||
531 | /* If the immediate parent has no ranges property to apply, | ||
532 | * just use a 1<->1 mapping. Unless it is the 'dma' child | ||
533 | * of an isa bus, which must be passed up towards the root. | ||
534 | * | ||
535 | * Also, don't try to translate PMU bus device registers. | ||
536 | */ | ||
537 | if ((of_find_property(pp, "ranges", NULL) == NULL && | ||
538 | strcmp(pp->name, "dma") != 0) || | ||
539 | !strcmp(pp->name, "pmu")) { | ||
540 | result = of_read_addr(addr, na); | ||
541 | goto build_res; | ||
542 | } | ||
543 | |||
544 | dna = na; | ||
545 | dns = ns; | ||
546 | |||
547 | while (1) { | ||
548 | dp = pp; | ||
549 | pp = dp->parent; | ||
550 | if (!pp) { | ||
551 | result = of_read_addr(addr, dna); | ||
552 | break; | ||
553 | } | ||
554 | |||
555 | pbus = of_match_bus(pp); | ||
556 | pbus->count_cells(dp, &pna, &pns); | ||
557 | |||
558 | if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna)) | ||
559 | break; | ||
560 | |||
561 | dna = pna; | ||
562 | dns = pns; | ||
563 | bus = pbus; | ||
564 | } | ||
565 | |||
566 | build_res: | ||
567 | memset(r, 0, sizeof(*r)); | ||
568 | if (result != OF_BAD_ADDR) { | ||
569 | r->start = result; | ||
570 | r->end = result + size - 1; | ||
571 | r->flags = flags; | ||
572 | } else { | ||
573 | r->start = ~0UL; | ||
574 | r->end = ~0UL; | ||
575 | } | ||
576 | r->name = op->node->name; | ||
577 | } | ||
578 | } | ||
579 | |||
580 | static struct of_device * __init scan_one_device(struct device_node *dp, | ||
581 | struct device *parent) | ||
582 | { | ||
583 | struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); | ||
584 | unsigned int *irq; | ||
585 | int len; | ||
586 | |||
587 | if (!op) | ||
588 | return NULL; | ||
589 | |||
590 | op->node = dp; | ||
591 | |||
592 | op->clock_freq = of_getintprop_default(dp, "clock-frequency", | ||
593 | (25*1000*1000)); | ||
594 | op->portid = of_getintprop_default(dp, "upa-portid", -1); | ||
595 | if (op->portid == -1) | ||
596 | op->portid = of_getintprop_default(dp, "portid", -1); | ||
597 | |||
598 | irq = of_get_property(dp, "interrupts", &len); | ||
599 | if (irq) | ||
600 | op->irq = *irq; | ||
601 | else | ||
602 | op->irq = 0xffffffff; | ||
603 | |||
604 | build_device_resources(op, parent); | ||
605 | |||
606 | op->dev.parent = parent; | ||
607 | op->dev.bus = &of_bus_type; | ||
608 | if (!parent) | ||
609 | strcpy(op->dev.bus_id, "root"); | ||
610 | else | ||
611 | strcpy(op->dev.bus_id, dp->path_component_name); | ||
612 | |||
613 | if (of_device_register(op)) { | ||
614 | printk("%s: Could not register of device.\n", | ||
615 | dp->full_name); | ||
616 | kfree(op); | ||
617 | op = NULL; | ||
618 | } | ||
619 | |||
620 | return op; | ||
621 | } | ||
622 | |||
623 | static void __init scan_tree(struct device_node *dp, struct device *parent) | ||
624 | { | ||
625 | while (dp) { | ||
626 | struct of_device *op = scan_one_device(dp, parent); | ||
627 | |||
628 | if (op) | ||
629 | scan_tree(dp->child, &op->dev); | ||
630 | |||
631 | dp = dp->sibling; | ||
632 | } | ||
633 | } | ||
634 | |||
635 | static void __init scan_of_devices(void) | ||
636 | { | ||
637 | struct device_node *root = of_find_node_by_path("/"); | ||
638 | struct of_device *parent; | ||
639 | |||
640 | parent = scan_one_device(root, NULL); | ||
641 | if (!parent) | ||
642 | return; | ||
643 | |||
644 | scan_tree(root->child, &parent->dev); | ||
645 | } | ||
646 | |||
166 | static int __init of_bus_driver_init(void) | 647 | static int __init of_bus_driver_init(void) |
167 | { | 648 | { |
168 | int err = 0; | 649 | int err; |
169 | 650 | ||
651 | err = bus_register(&of_bus_type); | ||
170 | #ifdef CONFIG_PCI | 652 | #ifdef CONFIG_PCI |
171 | if (!err) | 653 | if (!err) |
172 | err = bus_register(&isa_bus_type); | 654 | err = bus_register(&isa_bus_type); |
@@ -177,7 +659,11 @@ static int __init of_bus_driver_init(void) | |||
177 | if (!err) | 659 | if (!err) |
178 | err = bus_register(&sbus_bus_type); | 660 | err = bus_register(&sbus_bus_type); |
179 | #endif | 661 | #endif |
180 | return 0; | 662 | |
663 | if (!err) | ||
664 | scan_of_devices(); | ||
665 | |||
666 | return err; | ||
181 | } | 667 | } |
182 | 668 | ||
183 | postcore_initcall(of_bus_driver_init); | 669 | postcore_initcall(of_bus_driver_init); |