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