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