xref: /netbsd-src/sys/dev/fdt/fdtbus.c (revision 796c32c94f6e154afc9de0f63da35c91bb739b45)
1 /* $NetBSD: fdtbus.c,v 1.15 2017/08/27 19:13:31 jmcneill Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: fdtbus.c,v 1.15 2017/08/27 19:13:31 jmcneill Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/kmem.h>
36 
37 #include <sys/bus.h>
38 
39 #include <dev/ofw/openfirm.h>
40 
41 #include <dev/fdt/fdtvar.h>
42 
43 #include <libfdt.h>
44 
45 #include "locators.h"
46 
47 #define	FDT_MAX_PATH	256
48 
49 struct fdt_node {
50 	device_t	n_bus;
51 	device_t	n_dev;
52 	int		n_phandle;
53 	char		*n_name;
54 
55 	u_int		n_order;
56 
57 	TAILQ_ENTRY(fdt_node) n_nodes;
58 };
59 
60 static TAILQ_HEAD(, fdt_node) fdt_nodes =
61     TAILQ_HEAD_INITIALIZER(fdt_nodes);
62 
63 struct fdt_softc {
64 	device_t	sc_dev;
65 	int		sc_phandle;
66 	struct fdt_attach_args sc_faa;
67 };
68 
69 static int	fdt_match(device_t, cfdata_t, void *);
70 static void	fdt_attach(device_t, device_t, void *);
71 static int	fdt_scan_submatch(device_t, cfdata_t, const int *, void *);
72 static void	fdt_scan_bus(struct fdt_softc *);
73 static void	fdt_scan(struct fdt_softc *, int);
74 static void	fdt_add_node(struct fdt_node *);
75 static u_int	fdt_get_order(int);
76 
77 static const char * const fdtbus_compatible[] =
78     { "simple-bus", NULL };
79 
80 CFATTACH_DECL_NEW(fdt, sizeof(struct fdt_softc),
81     fdt_match, fdt_attach, NULL, NULL);
82 
83 static int
84 fdt_match(device_t parent, cfdata_t cf, void *aux)
85 {
86 	const struct fdt_attach_args *faa = aux;
87 	const int phandle = faa->faa_phandle;
88 	int match;
89 
90 	/* Check compatible string */
91 	match = of_match_compatible(phandle, fdtbus_compatible);
92 	if (match)
93 		return match;
94 
95 	/* Some nodes have no compatible string */
96 	if (!of_hasprop(phandle, "compatible")) {
97 		if (OF_finddevice("/clocks") == phandle)
98 			return 1;
99 		if (OF_finddevice("/chosen") == phandle)
100 			return 1;
101 	}
102 
103 	/* Always match the root node */
104 	return OF_finddevice("/") == phandle;
105 }
106 
107 static void
108 fdt_attach(device_t parent, device_t self, void *aux)
109 {
110 	struct fdt_softc *sc = device_private(self);
111 	const struct fdt_attach_args *faa = aux;
112 	const int phandle = faa->faa_phandle;
113 	struct fdt_node *node;
114 	char *model, *name;
115 	int len, child;
116 
117 	sc->sc_dev = self;
118 	sc->sc_phandle = phandle;
119 	sc->sc_faa = *faa;
120 
121 	aprint_naive("\n");
122 	len = OF_getproplen(phandle, "model");
123 	if (len > 0) {
124 		model = kmem_zalloc(len, KM_SLEEP);
125 		if (OF_getprop(phandle, "model", model, len) == len) {
126 			aprint_normal(": %s\n", model);
127 		} else {
128 			aprint_normal("\n");
129 		}
130 		kmem_free(model, len);
131 	} else {
132 		aprint_normal("\n");
133 	}
134 
135 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
136 		if (!fdtbus_status_okay(child))
137 			continue;
138 
139 		len = OF_getproplen(child, "name");
140 		if (len <= 0)
141 			continue;
142 
143 		name = kmem_zalloc(len, KM_SLEEP);
144 		if (OF_getprop(child, "name", name, len) != len)
145 			continue;
146 
147 		/* Add the node to our device list */
148 		node = kmem_alloc(sizeof(*node), KM_SLEEP);
149 		node->n_bus = self;
150 		node->n_dev = NULL;
151 		node->n_phandle = child;
152 		node->n_name = name;
153 		node->n_order = fdt_get_order(node->n_phandle);
154 		fdt_add_node(node);
155 	}
156 
157 	/* Scan and attach all known busses in the tree. */
158 	fdt_scan_bus(sc);
159 
160 	/* Only the root bus should scan for devices */
161 	if (OF_finddevice("/") != faa->faa_phandle)
162 		return;
163 
164 	aprint_debug_dev(sc->sc_dev, "  order   phandle   bus    path\n");
165 	aprint_debug_dev(sc->sc_dev, "  =====   =======   ===    ====\n");
166 	TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
167 		char buf[FDT_MAX_PATH];
168 		const char *path = buf;
169 		if (!fdtbus_get_path(node->n_phandle, buf, sizeof(buf)))
170 			path = node->n_name;
171 		aprint_debug_dev(sc->sc_dev, "   %04x   0x%04x    %s   %s\n",
172 		    node->n_order & 0xffff, node->n_phandle,
173 		    device_xname(node->n_bus), path);
174 	}
175 
176 	/* Scan devices */
177 	for (int pass = 0; pass <= FDTCF_PASS_DEFAULT; pass++)
178 		fdt_scan(sc, pass);
179 }
180 
181 static void
182 fdt_init_attach_args(struct fdt_softc *sc, struct fdt_node *node,
183     bool quiet, struct fdt_attach_args *faa)
184 {
185 	*faa = sc->sc_faa;
186 	faa->faa_phandle = node->n_phandle;
187 	faa->faa_name = node->n_name;
188 	faa->faa_quiet = quiet;
189 }
190 
191 static void
192 fdt_scan_bus(struct fdt_softc *sc)
193 {
194 	struct fdt_node *node;
195 	struct fdt_attach_args faa;
196 	cfdata_t cf;
197 
198 	TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
199 		if (node->n_bus != sc->sc_dev)
200 			continue;
201 		if (node->n_dev != NULL)
202 			continue;
203 
204 		fdt_init_attach_args(sc, node, true, &faa);
205 
206 		/*
207 		 * Only attach busses to nodes where this driver is the best
208 		 * match.
209 		 */
210 		cf = config_search_loc(NULL, node->n_bus, NULL, NULL, &faa);
211 		if (cf == NULL || strcmp(cf->cf_name, "fdt") != 0)
212 			continue;
213 
214 		/*
215 		 * Attach the bus.
216 		 */
217 		node->n_dev = config_found(node->n_bus, &faa, fdtbus_print);
218 	}
219 }
220 
221 static int
222 fdt_scan_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux)
223 {
224 	if (locs[FDTCF_PASS] != FDTCF_PASS_DEFAULT &&
225 	    locs[FDTCF_PASS] != cf->cf_loc[FDTCF_PASS])
226 		return 0;
227 
228 	return config_stdsubmatch(parent, cf, locs, aux);
229 }
230 
231 static void
232 fdt_scan(struct fdt_softc *sc, int pass)
233 {
234 	struct fdt_node *node;
235 	struct fdt_attach_args faa;
236 	const int locs[FDTCF_NLOCS] = {
237 		[FDTCF_PASS] = pass
238 	};
239 	bool quiet = pass != FDTCF_PASS_DEFAULT;
240 
241 	TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
242 		if (node->n_dev != NULL)
243 			continue;
244 
245 		fdt_init_attach_args(sc, node, quiet, &faa);
246 
247 		/*
248 		 * Attach the device.
249 		 */
250 		node->n_dev = config_found_sm_loc(node->n_bus, "fdt", locs,
251 		    &faa, fdtbus_print, fdt_scan_submatch);
252 	}
253 }
254 
255 static void
256 fdt_add_node(struct fdt_node *new_node)
257 {
258 	struct fdt_node *node;
259 
260 	TAILQ_FOREACH(node, &fdt_nodes, n_nodes)
261 		if (node->n_order > new_node->n_order) {
262 			TAILQ_INSERT_BEFORE(node, new_node, n_nodes);
263 			return;
264 		}
265 	TAILQ_INSERT_TAIL(&fdt_nodes, new_node, n_nodes);
266 }
267 
268 static u_int
269 fdt_get_order(int phandle)
270 {
271 	u_int val = UINT_MAX;
272 	int child;
273 
274 	of_getprop_uint32(phandle, "phandle", &val);
275 
276 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
277 		u_int child_val = fdt_get_order(child);
278 		if (child_val < val)
279 			val = child_val;
280 	}
281 
282 	return val;
283 }
284 
285 int
286 fdtbus_print(void *aux, const char *pnp)
287 {
288 	const struct fdt_attach_args * const faa = aux;
289 	char buf[FDT_MAX_PATH];
290 	const char *name = buf;
291 	int len;
292 
293 	if (pnp && faa->faa_quiet)
294 		return QUIET;
295 
296 	/* Skip "not configured" for nodes w/o compatible property */
297 	if (pnp && OF_getproplen(faa->faa_phandle, "compatible") <= 0)
298 		return QUIET;
299 
300 	if (!fdtbus_get_path(faa->faa_phandle, buf, sizeof(buf)))
301 		name = faa->faa_name;
302 
303 	if (pnp) {
304 		aprint_normal("%s at %s", name, pnp);
305 		const char *compat = fdt_getprop(fdtbus_get_data(),
306 		    fdtbus_phandle2offset(faa->faa_phandle), "compatible",
307 		    &len);
308 		while (len > 0) {
309 			aprint_debug(" <%s>", compat);
310 			len -= (strlen(compat) + 1);
311 			compat += (strlen(compat) + 1);
312 		}
313 	} else
314 		aprint_debug(" (%s)", name);
315 
316 	return UNCONF;
317 }
318