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
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	int i, rv;
482
483	for (i = 0; fdt_pic_table[i] != NULL; i++) {
484
485		/* XXX check if pic_handle has interrupt-controller prop? */
486
487		intr_decode = fdt_pic_table[i];
488		rv = intr_decode(intr_parent, intr, interrupt, trig, pol);
489
490		if (rv == 0)
491			/* This was recognized as our PIC and decoded. */
492			return (0);
493	}
494
495	return (ENXIO);
496}
497
498int
499fdt_intr_to_rl(phandle_t node, struct resource_list *rl,
500    struct fdt_sense_level *intr_sl)
501{
502	phandle_t intr_par;
503	ihandle_t iph;
504	pcell_t *intr;
505	pcell_t intr_cells;
506	int interrupt, trig, pol;
507	int i, intr_num, irq, rv;
508
509	if (OF_getproplen(node, "interrupts") <= 0)
510		/* Node does not have 'interrupts' property. */
511		return (0);
512
513	/*
514	 * Find #interrupt-cells of the interrupt domain.
515	 */
516	if (OF_getprop(node, "interrupt-parent", &iph, sizeof(iph)) <= 0) {
517		debugf("no intr-parent phandle\n");
518		intr_par = OF_parent(node);
519	} else {
520		iph = fdt32_to_cpu(iph);
521		intr_par = OF_instance_to_package(iph);
522	}
523
524	if (OF_getprop(intr_par, "#interrupt-cells", &intr_cells,
525	    sizeof(intr_cells)) <= 0) {
526		debugf("no intr-cells defined, defaulting to 1\n");
527		intr_cells = 1;
528	}
529	else
530		intr_cells = fdt32_to_cpu(intr_cells);
531
532	intr_num = OF_getprop_alloc(node, "interrupts",
533	    intr_cells * sizeof(pcell_t), (void **)&intr);
534	if (intr_num <= 0 || intr_num > DI_MAX_INTR_NUM)
535		return (ERANGE);
536
537	rv = 0;
538	for (i = 0; i < intr_num; i++) {
539
540		interrupt = -1;
541		trig = pol = 0;
542
543		if (fdt_intr_decode(intr_par, &intr[i * intr_cells],
544		    &interrupt, &trig, &pol) != 0) {
545			rv = ENXIO;
546			goto out;
547		}
548
549		if (interrupt < 0) {
550			rv = ERANGE;
551			goto out;
552		}
553
554		debugf("decoded intr = %d, trig = %d, pol = %d\n", interrupt,
555		    trig, pol);
556
557		intr_sl[i].trig = trig;
558		intr_sl[i].pol = pol;
559
560		irq = FDT_MAP_IRQ(intr_par, interrupt);
561		resource_list_add(rl, SYS_RES_IRQ, i, irq, irq, 1);
562	}
563
564out:
565	free(intr, M_OFWPROP);
566	return (rv);
567}
568
569int
570fdt_get_phyaddr(phandle_t node, device_t dev, int *phy_addr, void **phy_sc)
571{
572	phandle_t phy_node;
573	ihandle_t phy_ihandle;
574	pcell_t phy_handle, phy_reg;
575	uint32_t i;
576	device_t parent, child;
577
578	if (OF_getprop(node, "phy-handle", (void *)&phy_handle,
579	    sizeof(phy_handle)) <= 0)
580		return (ENXIO);
581
582	phy_ihandle = (ihandle_t)phy_handle;
583	phy_ihandle = fdt32_to_cpu(phy_ihandle);
584	phy_node = OF_instance_to_package(phy_ihandle);
585
586	if (OF_getprop(phy_node, "reg", (void *)&phy_reg,
587	    sizeof(phy_reg)) <= 0)
588		return (ENXIO);
589
590	*phy_addr = fdt32_to_cpu(phy_reg);
591
592	/*
593	 * Search for softc used to communicate with phy.
594	 */
595
596	/*
597	 * Step 1: Search for ancestor of the phy-node with a "phy-handle"
598	 * property set.
599	 */
600	phy_node = OF_parent(phy_node);
601	while (phy_node != 0) {
602		if (OF_getprop(phy_node, "phy-handle", (void *)&phy_handle,
603		    sizeof(phy_handle)) > 0)
604			break;
605		phy_node = OF_parent(phy_node);
606	}
607	if (phy_node == 0)
608		return (ENXIO);
609
610	/*
611	 * Step 2: For each device with the same parent and name as ours
612	 * compare its node with the one found in step 1, ancestor of phy
613	 * node (stored in phy_node).
614	 */
615	parent = device_get_parent(dev);
616	i = 0;
617	child = device_find_child(parent, device_get_name(dev), i);
618	while (child != NULL) {
619		if (ofw_bus_get_node(child) == phy_node)
620			break;
621		i++;
622		child = device_find_child(parent, device_get_name(dev), i);
623	}
624	if (child == NULL)
625		return (ENXIO);
626
627	/*
628	 * Use softc of the device found.
629	 */
630	*phy_sc = (void *)device_get_softc(child);
631
632	return (0);
633}
634
635int
636fdt_get_reserved_regions(struct mem_region *mr, int *mrcnt)
637{
638	pcell_t reserve[FDT_REG_CELLS * FDT_MEM_REGIONS];
639	pcell_t *reservep;
640	phandle_t memory, root;
641	uint32_t memory_size;
642	int addr_cells, size_cells;
643	int i, max_size, res_len, rv, tuple_size, tuples;
644
645	max_size = sizeof(reserve);
646	root = OF_finddevice("/");
647	memory = OF_finddevice("/memory");
648	if (memory == -1) {
649		rv = ENXIO;
650		goto out;
651	}
652
653	if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
654	    &size_cells)) != 0)
655		goto out;
656
657	if (addr_cells > 2) {
658		rv = ERANGE;
659		goto out;
660	}
661
662	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
663
664	res_len = OF_getproplen(root, "memreserve");
665	if (res_len <= 0 || res_len > sizeof(reserve)) {
666		rv = ERANGE;
667		goto out;
668	}
669
670	if (OF_getprop(root, "memreserve", reserve, res_len) <= 0) {
671		rv = ENXIO;
672		goto out;
673	}
674
675	memory_size = 0;
676	tuples = res_len / tuple_size;
677	reservep = (pcell_t *)&reserve;
678	for (i = 0; i < tuples; i++) {
679
680		rv = fdt_data_to_res(reservep, addr_cells, size_cells,
681			(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
682
683		if (rv != 0)
684			goto out;
685
686		reservep += addr_cells + size_cells;
687	}
688
689	*mrcnt = i;
690	rv = 0;
691out:
692	return (rv);
693}
694
695int
696fdt_get_mem_regions(struct mem_region *mr, int *mrcnt, uint32_t *memsize)
697{
698	pcell_t reg[FDT_REG_CELLS * FDT_MEM_REGIONS];
699	pcell_t *regp;
700	phandle_t memory;
701	uint32_t memory_size;
702	int addr_cells, size_cells;
703	int i, max_size, reg_len, rv, tuple_size, tuples;
704
705	max_size = sizeof(reg);
706	memory = OF_finddevice("/memory");
707	if (memory == -1) {
708		rv = ENXIO;
709		goto out;
710	}
711
712	if ((rv = fdt_addrsize_cells(OF_parent(memory), &addr_cells,
713	    &size_cells)) != 0)
714		goto out;
715
716	if (addr_cells > 2) {
717		rv = ERANGE;
718		goto out;
719	}
720
721	tuple_size = sizeof(pcell_t) * (addr_cells + size_cells);
722	reg_len = OF_getproplen(memory, "reg");
723	if (reg_len <= 0 || reg_len > sizeof(reg)) {
724		rv = ERANGE;
725		goto out;
726	}
727
728	if (OF_getprop(memory, "reg", reg, reg_len) <= 0) {
729		rv = ENXIO;
730		goto out;
731	}
732
733	memory_size = 0;
734	tuples = reg_len / tuple_size;
735	regp = (pcell_t *)&reg;
736	for (i = 0; i < tuples; i++) {
737
738		rv = fdt_data_to_res(regp, addr_cells, size_cells,
739			(u_long *)&mr[i].mr_start, (u_long *)&mr[i].mr_size);
740
741		if (rv != 0)
742			goto out;
743
744		regp += addr_cells + size_cells;
745		memory_size += mr[i].mr_size;
746	}
747
748	if (memory_size == 0) {
749		rv = ERANGE;
750		goto out;
751	}
752
753	*mrcnt = i;
754	*memsize = memory_size;
755	rv = 0;
756out:
757	return (rv);
758}
759
760int
761fdt_get_unit(device_t dev)
762{
763	const char * name;
764
765	name = ofw_bus_get_name(dev);
766	name = strchr(name, '@') + 1;
767
768	return (strtol(name,NULL,0));
769}
770