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/sparc | |
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/sparc')
-rw-r--r-- | arch/sparc/kernel/of_device.c | 422 |
1 files changed, 420 insertions, 2 deletions
diff --git a/arch/sparc/kernel/of_device.c b/arch/sparc/kernel/of_device.c index 80a809478781..13a70bd87817 100644 --- a/arch/sparc/kernel/of_device.c +++ b/arch/sparc/kernel/of_device.c | |||
@@ -153,10 +153,424 @@ struct bus_type sbus_bus_type = { | |||
153 | EXPORT_SYMBOL(sbus_bus_type); | 153 | EXPORT_SYMBOL(sbus_bus_type); |
154 | #endif | 154 | #endif |
155 | 155 | ||
156 | struct bus_type of_bus_type = { | ||
157 | .name = "of", | ||
158 | .match = of_platform_bus_match, | ||
159 | .probe = of_device_probe, | ||
160 | .remove = of_device_remove, | ||
161 | .suspend = of_device_suspend, | ||
162 | .resume = of_device_resume, | ||
163 | }; | ||
164 | EXPORT_SYMBOL(of_bus_type); | ||
165 | |||
166 | static inline u64 of_read_addr(u32 *cell, int size) | ||
167 | { | ||
168 | u64 r = 0; | ||
169 | while (size--) | ||
170 | r = (r << 32) | *(cell++); | ||
171 | return r; | ||
172 | } | ||
173 | |||
174 | static void __init get_cells(struct device_node *dp, | ||
175 | int *addrc, int *sizec) | ||
176 | { | ||
177 | if (addrc) | ||
178 | *addrc = of_n_addr_cells(dp); | ||
179 | if (sizec) | ||
180 | *sizec = of_n_size_cells(dp); | ||
181 | } | ||
182 | |||
183 | /* Max address size we deal with */ | ||
184 | #define OF_MAX_ADDR_CELLS 4 | ||
185 | |||
186 | struct of_bus { | ||
187 | const char *name; | ||
188 | const char *addr_prop_name; | ||
189 | int (*match)(struct device_node *parent); | ||
190 | void (*count_cells)(struct device_node *child, | ||
191 | int *addrc, int *sizec); | ||
192 | u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); | ||
193 | int (*translate)(u32 *addr, u64 offset, int na); | ||
194 | unsigned int (*get_flags)(u32 *addr); | ||
195 | }; | ||
196 | |||
197 | /* | ||
198 | * Default translator (generic bus) | ||
199 | */ | ||
200 | |||
201 | static void of_bus_default_count_cells(struct device_node *dev, | ||
202 | int *addrc, int *sizec) | ||
203 | { | ||
204 | get_cells(dev, addrc, sizec); | ||
205 | } | ||
206 | |||
207 | static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
208 | { | ||
209 | u64 cp, s, da; | ||
210 | |||
211 | cp = of_read_addr(range, na); | ||
212 | s = of_read_addr(range + na + pna, ns); | ||
213 | da = of_read_addr(addr, na); | ||
214 | |||
215 | if (da < cp || da >= (cp + s)) | ||
216 | return OF_BAD_ADDR; | ||
217 | return da - cp; | ||
218 | } | ||
219 | |||
220 | static int of_bus_default_translate(u32 *addr, u64 offset, int na) | ||
221 | { | ||
222 | u64 a = of_read_addr(addr, na); | ||
223 | memset(addr, 0, na * 4); | ||
224 | a += offset; | ||
225 | if (na > 1) | ||
226 | addr[na - 2] = a >> 32; | ||
227 | addr[na - 1] = a & 0xffffffffu; | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static unsigned int of_bus_default_get_flags(u32 *addr) | ||
233 | { | ||
234 | return IORESOURCE_MEM; | ||
235 | } | ||
236 | |||
237 | |||
238 | /* | ||
239 | * PCI bus specific translator | ||
240 | */ | ||
241 | |||
242 | static int of_bus_pci_match(struct device_node *np) | ||
243 | { | ||
244 | return !strcmp(np->type, "pci") || !strcmp(np->type, "pciex"); | ||
245 | } | ||
246 | |||
247 | static void of_bus_pci_count_cells(struct device_node *np, | ||
248 | int *addrc, int *sizec) | ||
249 | { | ||
250 | if (addrc) | ||
251 | *addrc = 3; | ||
252 | if (sizec) | ||
253 | *sizec = 2; | ||
254 | } | ||
255 | |||
256 | static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
257 | { | ||
258 | u64 cp, s, da; | ||
259 | |||
260 | /* Check address type match */ | ||
261 | if ((addr[0] ^ range[0]) & 0x03000000) | ||
262 | return OF_BAD_ADDR; | ||
263 | |||
264 | /* Read address values, skipping high cell */ | ||
265 | cp = of_read_addr(range + 1, na - 1); | ||
266 | s = of_read_addr(range + na + pna, ns); | ||
267 | da = of_read_addr(addr + 1, na - 1); | ||
268 | |||
269 | if (da < cp || da >= (cp + s)) | ||
270 | return OF_BAD_ADDR; | ||
271 | return da - cp; | ||
272 | } | ||
273 | |||
274 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) | ||
275 | { | ||
276 | return of_bus_default_translate(addr + 1, offset, na - 1); | ||
277 | } | ||
278 | |||
279 | static unsigned int of_bus_pci_get_flags(u32 *addr) | ||
280 | { | ||
281 | unsigned int flags = 0; | ||
282 | u32 w = addr[0]; | ||
283 | |||
284 | switch((w >> 24) & 0x03) { | ||
285 | case 0x01: | ||
286 | flags |= IORESOURCE_IO; | ||
287 | case 0x02: /* 32 bits */ | ||
288 | case 0x03: /* 64 bits */ | ||
289 | flags |= IORESOURCE_MEM; | ||
290 | } | ||
291 | if (w & 0x40000000) | ||
292 | flags |= IORESOURCE_PREFETCH; | ||
293 | return flags; | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * SBUS bus specific translator | ||
298 | */ | ||
299 | |||
300 | static int of_bus_sbus_match(struct device_node *np) | ||
301 | { | ||
302 | return !strcmp(np->name, "sbus") || | ||
303 | !strcmp(np->name, "sbi"); | ||
304 | } | ||
305 | |||
306 | static void of_bus_sbus_count_cells(struct device_node *child, | ||
307 | int *addrc, int *sizec) | ||
308 | { | ||
309 | if (addrc) | ||
310 | *addrc = 2; | ||
311 | if (sizec) | ||
312 | *sizec = 1; | ||
313 | } | ||
314 | |||
315 | static u64 of_bus_sbus_map(u32 *addr, u32 *range, int na, int ns, int pna) | ||
316 | { | ||
317 | return of_bus_default_map(addr, range, na, ns, pna); | ||
318 | } | ||
319 | |||
320 | static int of_bus_sbus_translate(u32 *addr, u64 offset, int na) | ||
321 | { | ||
322 | return of_bus_default_translate(addr, offset, na); | ||
323 | } | ||
324 | |||
325 | static unsigned int of_bus_sbus_get_flags(u32 *addr) | ||
326 | { | ||
327 | return IORESOURCE_MEM; | ||
328 | } | ||
329 | |||
330 | |||
331 | /* | ||
332 | * Array of bus specific translators | ||
333 | */ | ||
334 | |||
335 | static struct of_bus of_busses[] = { | ||
336 | /* PCI */ | ||
337 | { | ||
338 | .name = "pci", | ||
339 | .addr_prop_name = "assigned-addresses", | ||
340 | .match = of_bus_pci_match, | ||
341 | .count_cells = of_bus_pci_count_cells, | ||
342 | .map = of_bus_pci_map, | ||
343 | .translate = of_bus_pci_translate, | ||
344 | .get_flags = of_bus_pci_get_flags, | ||
345 | }, | ||
346 | /* SBUS */ | ||
347 | { | ||
348 | .name = "sbus", | ||
349 | .addr_prop_name = "reg", | ||
350 | .match = of_bus_sbus_match, | ||
351 | .count_cells = of_bus_sbus_count_cells, | ||
352 | .map = of_bus_sbus_map, | ||
353 | .translate = of_bus_sbus_translate, | ||
354 | .get_flags = of_bus_sbus_get_flags, | ||
355 | }, | ||
356 | /* Default */ | ||
357 | { | ||
358 | .name = "default", | ||
359 | .addr_prop_name = "reg", | ||
360 | .match = NULL, | ||
361 | .count_cells = of_bus_default_count_cells, | ||
362 | .map = of_bus_default_map, | ||
363 | .translate = of_bus_default_translate, | ||
364 | .get_flags = of_bus_default_get_flags, | ||
365 | }, | ||
366 | }; | ||
367 | |||
368 | static struct of_bus *of_match_bus(struct device_node *np) | ||
369 | { | ||
370 | int i; | ||
371 | |||
372 | for (i = 0; i < ARRAY_SIZE(of_busses); i ++) | ||
373 | if (!of_busses[i].match || of_busses[i].match(np)) | ||
374 | return &of_busses[i]; | ||
375 | BUG(); | ||
376 | return NULL; | ||
377 | } | ||
378 | |||
379 | static int __init build_one_resource(struct device_node *parent, | ||
380 | struct of_bus *bus, | ||
381 | struct of_bus *pbus, | ||
382 | u32 *addr, | ||
383 | int na, int ns, int pna) | ||
384 | { | ||
385 | u32 *ranges; | ||
386 | unsigned int rlen; | ||
387 | int rone; | ||
388 | u64 offset = OF_BAD_ADDR; | ||
389 | |||
390 | ranges = of_get_property(parent, "ranges", &rlen); | ||
391 | if (ranges == NULL || rlen == 0) { | ||
392 | offset = of_read_addr(addr, na); | ||
393 | memset(addr, 0, pna * 4); | ||
394 | goto finish; | ||
395 | } | ||
396 | |||
397 | /* Now walk through the ranges */ | ||
398 | rlen /= 4; | ||
399 | rone = na + pna + ns; | ||
400 | for (; rlen >= rone; rlen -= rone, ranges += rone) { | ||
401 | offset = bus->map(addr, ranges, na, ns, pna); | ||
402 | if (offset != OF_BAD_ADDR) | ||
403 | break; | ||
404 | } | ||
405 | if (offset == OF_BAD_ADDR) | ||
406 | return 1; | ||
407 | |||
408 | memcpy(addr, ranges + na, 4 * pna); | ||
409 | |||
410 | finish: | ||
411 | /* Translate it into parent bus space */ | ||
412 | return pbus->translate(addr, offset, pna); | ||
413 | } | ||
414 | |||
415 | static void __init build_device_resources(struct of_device *op, | ||
416 | struct device *parent) | ||
417 | { | ||
418 | struct of_device *p_op; | ||
419 | struct of_bus *bus; | ||
420 | int na, ns; | ||
421 | int index, num_reg; | ||
422 | void *preg; | ||
423 | |||
424 | if (!parent) | ||
425 | return; | ||
426 | |||
427 | p_op = to_of_device(parent); | ||
428 | bus = of_match_bus(p_op->node); | ||
429 | bus->count_cells(op->node, &na, &ns); | ||
430 | |||
431 | preg = of_get_property(op->node, bus->addr_prop_name, &num_reg); | ||
432 | if (!preg || num_reg == 0) | ||
433 | return; | ||
434 | |||
435 | /* Convert to num-cells. */ | ||
436 | num_reg /= 4; | ||
437 | |||
438 | /* Conver to num-entries. */ | ||
439 | num_reg /= na + ns; | ||
440 | |||
441 | for (index = 0; index < num_reg; index++) { | ||
442 | struct resource *r = &op->resource[index]; | ||
443 | u32 addr[OF_MAX_ADDR_CELLS]; | ||
444 | u32 *reg = (preg + (index * ((na + ns) * 4))); | ||
445 | struct device_node *dp = op->node; | ||
446 | struct device_node *pp = p_op->node; | ||
447 | struct of_bus *pbus; | ||
448 | u64 size, result = OF_BAD_ADDR; | ||
449 | unsigned long flags; | ||
450 | int dna, dns; | ||
451 | int pna, pns; | ||
452 | |||
453 | size = of_read_addr(reg + na, ns); | ||
454 | flags = bus->get_flags(reg); | ||
455 | |||
456 | memcpy(addr, reg, na * 4); | ||
457 | |||
458 | /* If the immediate parent has no ranges property to apply, | ||
459 | * just use a 1<->1 mapping. | ||
460 | */ | ||
461 | if (of_find_property(pp, "ranges", NULL) == NULL) { | ||
462 | result = of_read_addr(addr, na); | ||
463 | goto build_res; | ||
464 | } | ||
465 | |||
466 | dna = na; | ||
467 | dns = ns; | ||
468 | |||
469 | while (1) { | ||
470 | dp = pp; | ||
471 | pp = dp->parent; | ||
472 | if (!pp) { | ||
473 | result = of_read_addr(addr, dna); | ||
474 | break; | ||
475 | } | ||
476 | |||
477 | pbus = of_match_bus(pp); | ||
478 | pbus->count_cells(dp, &pna, &pns); | ||
479 | |||
480 | if (build_one_resource(dp, bus, pbus, addr, dna, dns, pna)) | ||
481 | break; | ||
482 | |||
483 | dna = pna; | ||
484 | dns = pns; | ||
485 | bus = pbus; | ||
486 | } | ||
487 | |||
488 | build_res: | ||
489 | memset(r, 0, sizeof(*r)); | ||
490 | if (result != OF_BAD_ADDR) { | ||
491 | r->start = result; | ||
492 | r->end = result + size - 1; | ||
493 | r->flags = flags; | ||
494 | } else { | ||
495 | r->start = ~0UL; | ||
496 | r->end = ~0UL; | ||
497 | } | ||
498 | r->name = op->node->name; | ||
499 | } | ||
500 | } | ||
501 | |||
502 | static struct of_device * __init scan_one_device(struct device_node *dp, | ||
503 | struct device *parent) | ||
504 | { | ||
505 | struct of_device *op = kzalloc(sizeof(*op), GFP_KERNEL); | ||
506 | unsigned int *irq; | ||
507 | int len; | ||
508 | |||
509 | if (!op) | ||
510 | return NULL; | ||
511 | |||
512 | op->node = dp; | ||
513 | |||
514 | op->clock_freq = of_getintprop_default(dp, "clock-frequency", | ||
515 | (25*1000*1000)); | ||
516 | op->portid = of_getintprop_default(dp, "upa-portid", -1); | ||
517 | if (op->portid == -1) | ||
518 | op->portid = of_getintprop_default(dp, "portid", -1); | ||
519 | |||
520 | irq = of_get_property(dp, "interrupts", &len); | ||
521 | if (irq) | ||
522 | op->irq = *irq; | ||
523 | else | ||
524 | op->irq = 0xffffffff; | ||
525 | |||
526 | build_device_resources(op, parent); | ||
527 | |||
528 | op->dev.parent = parent; | ||
529 | op->dev.bus = &of_bus_type; | ||
530 | if (!parent) | ||
531 | strcpy(op->dev.bus_id, "root"); | ||
532 | else | ||
533 | strcpy(op->dev.bus_id, dp->path_component_name); | ||
534 | |||
535 | if (of_device_register(op)) { | ||
536 | printk("%s: Could not register of device.\n", | ||
537 | dp->full_name); | ||
538 | kfree(op); | ||
539 | op = NULL; | ||
540 | } | ||
541 | |||
542 | return op; | ||
543 | } | ||
544 | |||
545 | static void __init scan_tree(struct device_node *dp, struct device *parent) | ||
546 | { | ||
547 | while (dp) { | ||
548 | struct of_device *op = scan_one_device(dp, parent); | ||
549 | |||
550 | if (op) | ||
551 | scan_tree(dp->child, &op->dev); | ||
552 | |||
553 | dp = dp->sibling; | ||
554 | } | ||
555 | } | ||
556 | |||
557 | static void __init scan_of_devices(void) | ||
558 | { | ||
559 | struct device_node *root = of_find_node_by_path("/"); | ||
560 | struct of_device *parent; | ||
561 | |||
562 | parent = scan_one_device(root, NULL); | ||
563 | if (!parent) | ||
564 | return; | ||
565 | |||
566 | scan_tree(root->child, &parent->dev); | ||
567 | } | ||
568 | |||
156 | static int __init of_bus_driver_init(void) | 569 | static int __init of_bus_driver_init(void) |
157 | { | 570 | { |
158 | int err = 0; | 571 | int err; |
159 | 572 | ||
573 | err = bus_register(&of_bus_type); | ||
160 | #ifdef CONFIG_PCI | 574 | #ifdef CONFIG_PCI |
161 | if (!err) | 575 | if (!err) |
162 | err = bus_register(&ebus_bus_type); | 576 | err = bus_register(&ebus_bus_type); |
@@ -165,7 +579,11 @@ static int __init of_bus_driver_init(void) | |||
165 | if (!err) | 579 | if (!err) |
166 | err = bus_register(&sbus_bus_type); | 580 | err = bus_register(&sbus_bus_type); |
167 | #endif | 581 | #endif |
168 | return 0; | 582 | |
583 | if (!err) | ||
584 | scan_of_devices(); | ||
585 | |||
586 | return err; | ||
169 | } | 587 | } |
170 | 588 | ||
171 | postcore_initcall(of_bus_driver_init); | 589 | postcore_initcall(of_bus_driver_init); |