fdtbus.c revision 1.12 1 /* $NetBSD: fdtbus.c,v 1.12 2017/04/29 12:49:05 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) 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.12 2017/04/29 12:49:05 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 "locators.h"
44
45 #define FDT_MAX_PATH 256
46
47 struct fdt_node {
48 device_t n_bus;
49 device_t n_dev;
50 int n_phandle;
51 char *n_name;
52
53 u_int n_order;
54
55 TAILQ_ENTRY(fdt_node) n_nodes;
56 };
57
58 static TAILQ_HEAD(, fdt_node) fdt_nodes =
59 TAILQ_HEAD_INITIALIZER(fdt_nodes);
60
61 struct fdt_softc {
62 device_t sc_dev;
63 int sc_phandle;
64 struct fdt_attach_args sc_faa;
65 };
66
67 static int fdt_match(device_t, cfdata_t, void *);
68 static void fdt_attach(device_t, device_t, void *);
69 static int fdt_scan_submatch(device_t, cfdata_t, const int *, void *);
70 static void fdt_scan_bus(struct fdt_softc *);
71 static void fdt_scan(struct fdt_softc *, int);
72 static void fdt_add_node(struct fdt_node *);
73 static u_int fdt_get_order(int);
74
75 static const char * const fdtbus_compatible[] =
76 { "simple-bus", NULL };
77
78 CFATTACH_DECL_NEW(fdt, sizeof(struct fdt_softc),
79 fdt_match, fdt_attach, NULL, NULL);
80
81 static int
82 fdt_match(device_t parent, cfdata_t cf, void *aux)
83 {
84 const struct fdt_attach_args *faa = aux;
85 int match;
86
87 match = of_match_compatible(faa->faa_phandle, fdtbus_compatible);
88 if (match)
89 return match;
90
91 return OF_finddevice("/") == faa->faa_phandle;
92 }
93
94 static void
95 fdt_attach(device_t parent, device_t self, void *aux)
96 {
97 struct fdt_softc *sc = device_private(self);
98 const struct fdt_attach_args *faa = aux;
99 const int phandle = faa->faa_phandle;
100 struct fdt_node *node;
101 char *model, *name;
102 int len, child;
103
104 sc->sc_dev = self;
105 sc->sc_phandle = phandle;
106 sc->sc_faa = *faa;
107
108 aprint_naive("\n");
109 len = OF_getproplen(phandle, "model");
110 if (len > 0) {
111 model = kmem_zalloc(len, KM_SLEEP);
112 if (OF_getprop(phandle, "model", model, len) == len) {
113 aprint_normal(": %s\n", model);
114 } else {
115 aprint_normal("\n");
116 }
117 kmem_free(model, len);
118 } else {
119 aprint_normal("\n");
120 }
121
122 for (child = OF_child(phandle); child; child = OF_peer(child)) {
123 if (!fdtbus_status_okay(child))
124 continue;
125
126 len = OF_getproplen(child, "name");
127 if (len <= 0)
128 continue;
129
130 name = kmem_zalloc(len, KM_SLEEP);
131 if (OF_getprop(child, "name", name, len) != len)
132 continue;
133
134 /* Add the node to our device list */
135 node = kmem_alloc(sizeof(*node), KM_SLEEP);
136 node->n_bus = self;
137 node->n_dev = NULL;
138 node->n_phandle = child;
139 node->n_name = name;
140 node->n_order = fdt_get_order(node->n_phandle);
141 fdt_add_node(node);
142 }
143
144 /* Scan and attach all known busses in the tree. */
145 fdt_scan_bus(sc);
146
147 /* Only the root bus should scan for devices */
148 if (OF_finddevice("/") != faa->faa_phandle)
149 return;
150
151 aprint_debug_dev(sc->sc_dev, " order phandle bus path\n");
152 aprint_debug_dev(sc->sc_dev, " ===== ======= === ====\n");
153 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
154 char buf[FDT_MAX_PATH];
155 const char *path = buf;
156 if (!fdtbus_get_path(node->n_phandle, buf, sizeof(buf)))
157 path = node->n_name;
158 aprint_debug_dev(sc->sc_dev, " %04x 0x%04x %s %s\n",
159 node->n_order & 0xffff, node->n_phandle,
160 device_xname(node->n_bus), path);
161 }
162
163 /* Scan devices */
164 for (int pass = 1; pass <= FDTCF_PASS_DEFAULT; pass++)
165 fdt_scan(sc, pass);
166 }
167
168 static void
169 fdt_init_attach_args(struct fdt_softc *sc, struct fdt_node *node,
170 bool quiet, struct fdt_attach_args *faa)
171 {
172 *faa = sc->sc_faa;
173 faa->faa_phandle = node->n_phandle;
174 faa->faa_name = node->n_name;
175 faa->faa_quiet = quiet;
176 }
177
178 static void
179 fdt_scan_bus(struct fdt_softc *sc)
180 {
181 struct fdt_node *node;
182 struct fdt_attach_args faa;
183 cfdata_t cf;
184
185 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
186 if (node->n_bus != sc->sc_dev)
187 continue;
188 if (node->n_dev != NULL)
189 continue;
190
191 fdt_init_attach_args(sc, node, true, &faa);
192
193 /*
194 * Only attach busses to nodes where this driver is the best
195 * match.
196 */
197 cf = config_search_loc(NULL, node->n_bus, NULL, NULL, &faa);
198 if (cf == NULL || strcmp(cf->cf_name, "fdt") != 0)
199 continue;
200
201 /*
202 * Attach the bus.
203 */
204 node->n_dev = config_found(node->n_bus, &faa, fdtbus_print);
205 }
206 }
207
208 static int
209 fdt_scan_submatch(device_t parent, cfdata_t cf, const int *locs, void *aux)
210 {
211 if (locs[FDTCF_PASS] != FDTCF_PASS_DEFAULT &&
212 locs[FDTCF_PASS] != cf->cf_loc[FDTCF_PASS])
213 return 0;
214
215 return config_stdsubmatch(parent, cf, locs, aux);
216 }
217
218 static void
219 fdt_scan(struct fdt_softc *sc, int pass)
220 {
221 struct fdt_node *node;
222 struct fdt_attach_args faa;
223 const int locs[FDTCF_NLOCS] = {
224 [FDTCF_PASS] = pass
225 };
226 bool quiet = pass != FDTCF_PASS_DEFAULT;
227
228 TAILQ_FOREACH(node, &fdt_nodes, n_nodes) {
229 if (node->n_dev != NULL)
230 continue;
231
232 fdt_init_attach_args(sc, node, quiet, &faa);
233
234 /*
235 * Attach the device.
236 */
237 node->n_dev = config_found_sm_loc(node->n_bus, "fdt", locs,
238 &faa, fdtbus_print, fdt_scan_submatch);
239 }
240 }
241
242 static void
243 fdt_add_node(struct fdt_node *new_node)
244 {
245 struct fdt_node *node;
246
247 TAILQ_FOREACH(node, &fdt_nodes, n_nodes)
248 if (node->n_order > new_node->n_order) {
249 TAILQ_INSERT_BEFORE(node, new_node, n_nodes);
250 return;
251 }
252 TAILQ_INSERT_TAIL(&fdt_nodes, new_node, n_nodes);
253 }
254
255 static u_int
256 fdt_get_order(int phandle)
257 {
258 u_int val = UINT_MAX;
259 int child;
260
261 of_getprop_uint32(phandle, "phandle", &val);
262
263 for (child = OF_child(phandle); child; child = OF_peer(child)) {
264 u_int child_val = fdt_get_order(child);
265 if (child_val < val)
266 val = child_val;
267 }
268
269 return val;
270 }
271
272 int
273 fdtbus_print(void *aux, const char *pnp)
274 {
275 const struct fdt_attach_args * const faa = aux;
276 char buf[FDT_MAX_PATH];
277 const char *name = buf;
278
279 if (pnp && faa->faa_quiet)
280 return QUIET;
281
282 /* Skip "not configured" for nodes w/o compatible property */
283 if (pnp && OF_getproplen(faa->faa_phandle, "compatible") <= 0)
284 return QUIET;
285
286 if (!fdtbus_get_path(faa->faa_phandle, buf, sizeof(buf)))
287 name = faa->faa_name;
288
289 if (pnp)
290 aprint_normal("%s at %s", name, pnp);
291 else
292 aprint_debug(" (%s)", name);
293
294 return UNCONF;
295 }
296