]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/fdt/fdt_common.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.git] / sys / dev / fdt / fdt_common.c
1 /*-
2  * Copyright (c) 2009-2010 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Semihalf under sponsorship from
6  * the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/limits.h>
39
40 #include <machine/fdt.h>
41 #include <machine/resource.h>
42
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus.h>
45 #include <dev/ofw/ofw_bus_subr.h>
46 #include <dev/ofw/openfirm.h>
47
48 #include "ofw_bus_if.h"
49
50 #ifdef DEBUG
51 #define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
52     printf(fmt,##args); } while (0)
53 #else
54 #define debugf(fmt, args...)
55 #endif
56
57 #define FDT_COMPAT_LEN  255
58 #define FDT_TYPE_LEN    64
59
60 #define FDT_REG_CELLS   4
61
62 vm_paddr_t fdt_immr_pa;
63 vm_offset_t fdt_immr_va;
64 vm_offset_t fdt_immr_size;
65
66 int
67 fdt_get_range(phandle_t node, int range_id, u_long *base, u_long *size)
68 {
69         pcell_t ranges[6], *rangesptr;
70         pcell_t addr_cells, size_cells, par_addr_cells;
71         int len, tuple_size, tuples;
72
73         if ((fdt_addrsize_cells(node, &addr_cells, &size_cells)) != 0)
74                 return (ENXIO);
75         /*
76          * Process 'ranges' property.
77          */
78         par_addr_cells = fdt_parent_addr_cells(node);
79         if (par_addr_cells > 2)
80                 return (ERANGE);
81
82         len = OF_getproplen(node, "ranges");
83         if (len > sizeof(ranges))
84                 return (ENOMEM);
85         if (len == 0) {
86                 *base = 0;
87                 *size = ULONG_MAX;
88                 return (0);
89         }
90
91         if (!(range_id < len))
92                 return (ERANGE);
93
94         if (OF_getprop(node, "ranges", ranges, sizeof(ranges)) <= 0)
95                 return (EINVAL);
96
97         tuple_size = sizeof(pcell_t) * (addr_cells + par_addr_cells +
98             size_cells);
99         tuples = len / tuple_size;
100
101         if (fdt_ranges_verify(ranges, tuples, par_addr_cells,
102             addr_cells, size_cells)) {
103                 return (ERANGE);
104         }
105         *base = 0;
106         *size = 0;
107         rangesptr = &ranges[range_id];
108
109         *base = fdt_data_get((void *)rangesptr, addr_cells);
110         rangesptr += addr_cells;
111         *base += fdt_data_get((void *)rangesptr, par_addr_cells);
112         rangesptr += par_addr_cells;
113         *size = fdt_data_get((void *)rangesptr, size_cells);
114         return (0);
115 }
116
117 int
118 fdt_immr_addr(vm_offset_t immr_va)
119 {
120         phandle_t node;
121         u_long base, size;
122         int r;
123
124         /*
125          * Try to access the SOC node directly i.e. through /aliases/.
126          */
127         if ((node = OF_finddevice("soc")) != 0)
128                 if (fdt_is_compatible_strict(node, "simple-bus"))
129                         goto moveon;
130         /*
131          * Find the node the long way.
132          */
133         if ((node = OF_finddevice("/")) == 0)
134                 return (ENXIO);
135
136         if ((node = fdt_find_compatible(node, "simple-bus", 1)) == 0)
137                 return (ENXIO);
138
139 moveon:
140         if ((r = fdt_get_range(node, 0, &base, &size)) == 0) {
141                 fdt_immr_pa = base;
142                 fdt_immr_va = immr_va;
143                 fdt_immr_size = size;
144         }
145
146         return (r);
147 }
148
149 /*
150  * This routine is an early-usage version of the ofw_bus_is_compatible() when
151  * the ofw_bus I/F is not available (like early console routines and similar).
152  * Note the buffer has to be on the stack since malloc() is usually not
153  * available in such cases either.
154  */
155 int
156 fdt_is_compatible(phandle_t node, const char *compatstr)
157 {
158         char buf[FDT_COMPAT_LEN];
159         char *compat;
160         int len, onelen, l, rv;
161
162         if ((len = OF_getproplen(node, "compatible")) <= 0)
163                 return (0);
164
165         compat = (char *)&buf;
166         bzero(compat, FDT_COMPAT_LEN);
167
168         if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0)
169                 return (0);
170
171         onelen = strlen(compatstr);
172         rv = 0;
173         while (len > 0) {
174                 if (strncasecmp(compat, compatstr, onelen) == 0) {
175                         /* Found it. */
176                         rv = 1;
177                         break;
178                 }
179                 /* Slide to the next sub-string. */
180                 l = strlen(compat) + 1;
181                 compat += l;
182                 len -= l;
183         }
184
185         return (rv);
186 }
187
188 int
189 fdt_is_compatible_strict(phandle_t node, const char *compatible)
190 {
191         char compat[FDT_COMPAT_LEN];
192
193         if (OF_getproplen(node, "compatible") <= 0)
194                 return (0);
195
196         if (OF_getprop(node, "compatible", compat, FDT_COMPAT_LEN) < 0)
197                 return (0);
198
199         if (strncasecmp(compat, compatible, FDT_COMPAT_LEN) == 0)
200                 /* This fits. */
201                 return (1);
202
203         return (0);
204 }
205
206 phandle_t
207 fdt_find_compatible(phandle_t start, const char *compat, int strict)
208 {
209         phandle_t child;
210
211         /*
212          * Traverse all children of 'start' node, and find first with
213          * matching 'compatible' property.
214          */
215         for (child = OF_child(start); child != 0; child = OF_peer(child))
216                 if (fdt_is_compatible(child, compat)) {
217                         if (strict)
218                                 if (!fdt_is_compatible_strict(child, compat))
219                                         continue;
220                         return (child);
221                 }
222         return (0);
223 }
224
225 int
226 fdt_is_enabled(phandle_t node)
227 {
228         char *stat;
229         int ena, len;
230
231         len = OF_getprop_alloc(node, "status", sizeof(char),
232             (void **)&stat);
233
234         if (len <= 0)
235                 /* It is OK if no 'status' property. */
236                 return (1);
237
238         /* Anything other than 'okay' means disabled. */
239         ena = 0;
240         if (strncmp((char *)stat, "okay", len) == 0)
241                 ena = 1;
242
243         free(stat, M_OFWPROP);
244         return (ena);
245 }
246
247 int
248 fdt_is_type(phandle_t node, const char *typestr)
249 {
250         char type[FDT_TYPE_LEN];
251
252         if (OF_getproplen(node, "device_type") <= 0)
253                 return (0);
254
255         if (OF_getprop(node, "device_type", type, FDT_TYPE_LEN) < 0)
256                 return (0);
257
258         if (strncasecmp(type, typestr, FDT_TYPE_LEN) == 0)
259                 /* This fits. */
260                 return (1);
261
262         return (0);
263 }
264
265 int
266 fdt_parent_addr_cells(phandle_t node)
267 {
268         pcell_t addr_cells;
269
270         /* Find out #address-cells of the superior bus. */
271         if (OF_searchprop(OF_parent(node), "#address-cells", &addr_cells,
272             sizeof(addr_cells)) <= 0)
273                 addr_cells = 2;
274
275         return ((int)fdt32_to_cpu(addr_cells));
276 }
277
278 int
279 fdt_data_verify(void *data, int cells)
280 {
281         uint64_t d64;
282
283         if (cells > 1) {
284                 d64 = fdt64_to_cpu(*((uint64_t *)data));
285                 if (((d64 >> 32) & 0xffffffffull) != 0 || cells > 2)
286                         return (ERANGE);
287         }
288
289         return (0);
290 }
291
292 int
293 fdt_pm_is_enabled(phandle_t node)
294 {
295         int ret;
296
297         ret = 1;
298
299 #if defined(SOC_MV_KIRKWOOD) || defined(SOC_MV_DISCOVERY)
300         ret = fdt_pm(node);
301 #endif
302         return (ret);
303 }
304
305 u_long
306 fdt_data_get(void *data, int cells)
307 {
308
309         if (cells == 1)
310                 return (fdt32_to_cpu(*((uint32_t *)data)));
311
312         return (fdt64_to_cpu(*((uint64_t *)data)));
313 }
314
315 int
316 fdt_addrsize_cells(phandle_t node, int *addr_cells, int *size_cells)
317 {
318         pcell_t cell;
319         int cell_size;
320
321         /*
322          * Retrieve #{address,size}-cells.
323          */
324         cell_size = sizeof(cell);
325         if (OF_getprop(node, "#address-cells", &cell, cell_size) < cell_size)
326                 cell = 2;
327         *addr_cells = fdt32_to_cpu((int)cell);
328
329         if (OF_getprop(node, "#size-cells", &cell, cell_size) < cell_size)
330                 cell = 1;
331         *size_cells = fdt32_to_cpu((int)cell);
332
333         if (*addr_cells > 3 || *size_cells > 2)
334                 return (ERANGE);
335         return (0);
336 }
337
338 int
339 fdt_ranges_verify(pcell_t *ranges, int tuples, int par_addr_cells,
340     int this_addr_cells, int this_size_cells)
341 {
342         int i, rv, ulsz;
343
344         if (par_addr_cells > 2 || this_addr_cells > 2 || this_size_cells > 2)
345                 return (ERANGE);
346
347         /*
348          * This is the max size the resource manager can handle for addresses
349          * and sizes.
350          */
351         ulsz = sizeof(u_long);
352         if (par_addr_cells <= ulsz && this_addr_cells <= ulsz &&
353             this_size_cells <= ulsz)
354                 /* We can handle everything */
355                 return (0);
356
357         rv = 0;
358         for (i = 0; i < tuples; i++) {
359
360                 if (fdt_data_verify((void *)ranges, par_addr_cells))
361                         goto err;
362                 ranges += par_addr_cells;
363
364                 if (fdt_data_verify((void *)ranges, this_addr_cells))
365                         goto err;
366                 ranges += this_addr_cells;
367
368                 if (fdt_data_verify((void *)ranges, this_size_cells))
369                         goto err;
370                 ranges += this_size_cells;
371         }
372
373         return (0);
374
375 err:
376         debugf("using address range >%d-bit not supported\n", ulsz * 8);
377         return (ERANGE);
378 }
379
380 int
381 fdt_data_to_res(pcell_t *data, int addr_cells, int size_cells, u_long *start,
382     u_long *count)
383 {
384
385         /* Address portion. */
386         if (fdt_data_verify((void *)data, addr_cells))
387                 return (ERANGE);
388
389         *start = fdt_data_get((void *)data, addr_cells);
390         data += addr_cells;
391
392         /* Size portion. */
393         if (fdt_data_verify((void *)data, size_cells))
394                 return (ERANGE);
395
396         *count = fdt_data_get((void *)data, size_cells);
397         return (0);
398 }
399
400 int
401 fdt_regsize(phandle_t node, u_long *base, u_long *size)
402 {
403         pcell_t reg[4];
404         int addr_cells, len, size_cells;
405
406         if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells))
407                 return (ENXIO);
408
409         if ((sizeof(pcell_t) * (addr_cells + size_cells)) > sizeof(reg))
410                 return (ENOMEM);
411
412         len = OF_getprop(node, "reg", &reg, sizeof(reg));
413         if (len <= 0)
414                 return (EINVAL);
415
416         *base = fdt_data_get(&reg[0], addr_cells);
417         *size = fdt_data_get(&reg[addr_cells], size_cells);
418         return (0);
419 }
420
421 int
422 fdt_reg_to_rl(phandle_t node, struct resource_list *rl)
423 {
424         u_long end, count, start;
425         pcell_t *reg, *regptr;
426         pcell_t addr_cells, size_cells;
427         int tuple_size, tuples;
428         int i, rv;
429         long busaddr, bussize;
430
431         if (fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells) != 0)
432                 return (ENXIO);
433         if (fdt_get_range(OF_parent(node), 0, &busaddr, &bussize)) {
434                 busaddr = 0;
435                 bussize = 0;
436         }
437
438         tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
439         tuples = OF_getprop_alloc(node, "reg", tuple_size, (void **)&reg);
440         debugf("addr_cells = %d, size_cells = %d\n", addr_cells, size_cells);
441         debugf("tuples = %d, tuple size = %d\n", tuples, tuple_size);
442         if (tuples <= 0)
443                 /* No 'reg' property in this node. */
444                 return (0);
445
446         regptr = reg;
447         for (i = 0; i < tuples; i++) {
448
449                 rv = fdt_data_to_res(reg, addr_cells, size_cells, &start,
450                     &count);
451                 if (rv != 0) {
452                         resource_list_free(rl);
453                         goto out;
454                 }
455                 reg += addr_cells + size_cells;
456
457                 /* Calculate address range relative to base. */
458                 start += busaddr;
459                 end = start + count - 1;
460
461                 debugf("reg addr start = %lx, end = %lx, count = %lx\n", start,
462                     end, count);
463
464                 resource_list_add(rl, SYS_RES_MEMORY, i, start, end,
465                     count);
466         }
467         rv = 0;
468
469 out:
470         free(regptr, M_OFWPROP);
471         return (rv);
472 }
473
474 int
475 fdt_intr_decode(phandle_t intr_parent, pcell_t *intr, int *interrupt,
476     int *trig, int *pol)
477 {
478         fdt_pic_decode_t intr_decode;
479         int i, rv;
480
481         for (i = 0; fdt_pic_table[i] != NULL; i++) {
482
483                 /* XXX check if pic_handle has interrupt-controller prop? */
484
485                 intr_decode = fdt_pic_table[i];
486                 rv = intr_decode(intr_parent, intr, interrupt, trig, pol);
487
488                 if (rv == 0)
489                         /* This was recognized as our PIC and decoded. */
490                         return (0);
491         }
492
493         return (ENXIO);
494 }
495
496 int
497 fdt_intr_to_rl(phandle_t node, struct resource_list *rl,
498     struct fdt_sense_level *intr_sl)
499 {
500         phandle_t intr_par;
501         ihandle_t iph;
502         pcell_t *intr;
503         pcell_t intr_cells;
504         int interrupt, trig, pol;
505         int i, intr_num, irq, rv;
506
507         if (OF_getproplen(node, "interrupts") <= 0)
508                 /* Node does not have 'interrupts' property. */
509                 return (0);
510
511         /*
512          * Find #interrupt-cells of the interrupt domain.
513          */
514         if (OF_getprop(node, "interrupt-parent", &iph, sizeof(iph)) <= 0) {
515                 debugf("no intr-parent phandle\n");
516                 intr_par = OF_parent(node);
517         } else {
518                 iph = fdt32_to_cpu(iph);
519                 intr_par = OF_instance_to_package(iph);
520         }
521
522         if (OF_getprop(intr_par, "#interrupt-cells", &intr_cells,
523             sizeof(intr_cells)) <= 0) {
524                 debugf("no intr-cells defined, defaulting to 1\n");
525                 intr_cells = 1;
526         }
527         else 
528                 intr_cells = fdt32_to_cpu(intr_cells);
529
530         intr_num = OF_getprop_alloc(node, "interrupts",
531             intr_cells * sizeof(pcell_t), (void **)&intr);
532         if (intr_num <= 0 || intr_num > DI_MAX_INTR_NUM)
533                 return (ERANGE);
534
535         rv = 0;
536         for (i = 0; i < intr_num; i++) {
537
538                 interrupt = -1;
539                 trig = pol = 0;
540
541                 if (fdt_intr_decode(intr_par, &intr[i * intr_cells],
542                     &interrupt, &trig, &pol) != 0) {
543                         rv = ENXIO;
544                         goto out;
545                 }
546
547                 if (interrupt < 0) {
548                         rv = ERANGE;
549                         goto out;
550                 }
551
552                 debugf("decoded intr = %d, trig = %d, pol = %d\n", interrupt,
553                     trig, pol);
554
555                 intr_sl[i].trig = trig;
556                 intr_sl[i].pol = pol;
557
558                 irq = FDT_MAP_IRQ(intr_par, interrupt);
559                 resource_list_add(rl, SYS_RES_IRQ, i, irq, irq, 1);
560         }
561
562 out:
563         free(intr, M_OFWPROP);
564         return (rv);
565 }
566
567 int
568 fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc)
569 {
570         phandle_t phy_node;
571         ihandle_t phy_ihandle;
572         pcell_t phy_handle, phy_reg;
573         uint32_t i;
574         device_t parent, child;
575
576         if (OF_getprop(node, "phy-handle", (void *)&phy_handle,
577             sizeof(phy_handle)) <= 0)
578                 return (ENXIO);
579
580         phy_ihandle = (ihandle_t)phy_handle;
581         phy_ihandle = fdt32_to_cpu(phy_ihandle);
582         phy_node = OF_instance_to_package(phy_ihandle);
583
584         if (OF_getprop(phy_node, "reg", (void *)&phy_reg,
585             sizeof(phy_reg)) <= 0)
586                 return (ENXIO);
587
588         *phy_addr = fdt32_to_cpu(phy_reg);
589
590         /*
591          * Search for softc used to communicate with phy.
592          */
593
594         /*
595          * Step 1: Search for ancestor of the phy-node with a "phy-handle"
596          * property set.
597          */
598         phy_node = OF_parent(phy_node);
599         while (phy_node != 0) {
600                 if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle,
601                     sizeof(phy_handle)) > 0)
602                         break;
603                 phy_node = OF_parent(phy_node);
604         }
605         if (phy_node == 0)
606                 return (ENXIO);
607
608         /*
609          * Step 2: For each device with the same parent and name as ours
610          * compare its node with the one found in step 1, ancestor of phy
611          * node (stored in phy_node).
612          */
613         parent = device_get_parent(dev);
614         i = 0;
615         child = device_find_child(parent, device_get_name(dev), i);
616         while (child != NULL) {
617                 if (ofw_bus_get_node(child) == phy_node)
618                         break;
619                 i++;
620                 child = device_find_child(parent, device_get_name(dev), i);
621         }
622         if (child == NULL)
623                 return (ENXIO);
624
625         /*
626          * Use softc of the device found.
627          */
628         *phy_sc = (void *)device_get_softc(child);
629
630         return (0);
631 }
632
633 int
634 fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt)
635 {
636         pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS];
637         pcell_t *reservep;
638         phandle_t memory, root;
639         uint32_t memory_size;
640         int addr_cells, size_cells;
641         int i, max_size, res_len, rv, tuple_size, tuples;
642
643         max_size = sizeof(reserve);
644         root = OF_finddevice("/");
645         memory = OF_finddevice("/memory");
646         if (memory == -1) {
647                 rv = ENXIO;
648                 goto out;
649         }
650
651         if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
652             &size_cells)) != 0)
653                 goto out;
654
655         if (addr_cells > 2) {
656                 rv = ERANGE;
657                 goto out;
658         }
659
660         tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
661
662         res_len = OF_getproplen(root, "memreserve");
663         if (res_len <= 0 || res_len > sizeof(reserve)) {
664                 rv = ERANGE;
665                 goto out;
666         }
667
668         if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) {
669                 rv = ENXIO;
670                 goto out;
671         }
672
673         memory_size = 0;
674         tuples = res_len / tuple_size;
675         reservep = (pcell_t *)&reserve;
676         for (i = 0; i < tuples; i++) {
677
678                 rv = fdt_data_to_res(reservep, addr_cells, size_cells,
679                         (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
680
681                 if (rv != 0)
682                         goto out;
683
684                 reservep += addr_cells + size_cells;
685         }
686
687         *mrcnt = i;
688         rv = 0;
689 out:
690         return (rv);
691 }
692
693 int
694 fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint32_t *memsize)
695 {
696         pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
697         pcell_t *regp;
698         phandle_t memory;
699         uint32_t memory_size;
700         int addr_cells, size_cells;
701         int i, max_size, reg_len, rv, tuple_size, tuples;
702
703         max_size = sizeof(reg);
704         memory = OF_finddevice("/memory");
705         if (memory == -1) {
706                 rv = ENXIO;
707                 goto out;
708         }
709
710         if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
711             &size_cells)) != 0)
712                 goto out;
713
714         if (addr_cells > 2) {
715                 rv = ERANGE;
716                 goto out;
717         }
718
719         tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
720         reg_len = OF_getproplen(memory, "reg");
721         if (reg_len <= 0 || reg_len > sizeof(reg)) {
722                 rv = ERANGE;
723                 goto out;
724         }
725
726         if (OF_getprop(memory, "reg", reg, reg_len) <= 0) {
727                 rv = ENXIO;
728                 goto out;
729         }
730
731         memory_size = 0;
732         tuples = reg_len / tuple_size;
733         regp = (pcell_t *)&reg;
734         for (i = 0; i < tuples; i++) {
735
736                 rv = fdt_data_to_res(regp, addr_cells, size_cells,
737                         (u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
738
739                 if (rv != 0)
740                         goto out;
741
742                 regp += addr_cells + size_cells;
743                 memory_size += mr[i].mr_size;
744         }
745
746         if (memory_size == 0) {
747                 rv = ERANGE;
748                 goto out;
749         }
750
751         *mrcnt = i;
752         *memsize = memory_size;
753         rv = 0;
754 out:
755         return (rv);
756 }
757
758 int
759 fdt_get_unit(device_t dev)
760 {
761         const char * name;
762
763         name = ofw_bus_get_name(dev);
764         name = strchr(name, '@') + 1;
765
766         return (strtol(name,NULL,0));
767 }