xref: /openbsd-src/sys/arch/arm/mainbus/mainbus.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: mainbus.c,v 1.13 2016/08/06 00:04:39 jsg Exp $ */
2 /*
3  * Copyright (c) 2016 Patrick Wildt <patrick@blueri.se>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/kernel.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 
24 #include <dev/ofw/openfirm.h>
25 #include <dev/ofw/fdt.h>
26 
27 #include <arm/mainbus/mainbus.h>
28 
29 int mainbus_match(struct device *, void *, void *);
30 void mainbus_attach(struct device *, struct device *, void *);
31 
32 void mainbus_attach_node(struct device *, int);
33 
34 int mainbus_legacy_search(struct device *, void *, void *);
35 
36 struct mainbus_softc {
37 	struct device		 sc_dev;
38 	bus_space_tag_t		 sc_iot;
39 	bus_dma_tag_t		 sc_dmat;
40 	int			 sc_acells;
41 	int			 sc_scells;
42 	int			*sc_ranges;
43 	int			 sc_rangeslen;
44 };
45 
46 struct cfattach mainbus_ca = {
47 	sizeof(struct mainbus_softc), mainbus_match, mainbus_attach, NULL,
48 	config_activate_children
49 };
50 
51 struct cfdriver mainbus_cd = {
52 	NULL, "mainbus", DV_DULL
53 };
54 
55 struct arm32_bus_dma_tag mainbus_dma_tag = {
56 	0,
57 	0,
58 	NULL,
59 	_bus_dmamap_create,
60 	_bus_dmamap_destroy,
61 	_bus_dmamap_load,
62 	_bus_dmamap_load_mbuf,
63 	_bus_dmamap_load_uio,
64 	_bus_dmamap_load_raw,
65 	_bus_dmamap_unload,
66 	_bus_dmamap_sync,
67 	_bus_dmamem_alloc,
68 	_bus_dmamem_free,
69 	_bus_dmamem_map,
70 	_bus_dmamem_unmap,
71 	_bus_dmamem_mmap,
72 };
73 
74 /*
75  * Mainbus takes care of FDT and non-FDT machines, so we
76  * always attach.
77  */
78 int
79 mainbus_match(struct device *parent, void *cfdata, void *aux)
80 {
81 	return (1);
82 }
83 
84 extern char *hw_prod;
85 
86 void
87 mainbus_attach(struct device *parent, struct device *self, void *aux)
88 {
89 	struct mainbus_softc *sc = (struct mainbus_softc *)self;
90 	char buffer[128];
91 	int node, len;
92 
93 	if ((node = OF_peer(0)) == 0) {
94 		printf(": no device tree\n");
95 		config_search(mainbus_legacy_search, self, aux);
96 		return;
97 	}
98 
99 #ifdef CPU_ARMv7
100 	arm_intr_init_fdt();
101 #endif
102 
103 #ifdef CPU_ARMv7
104 	extern struct bus_space armv7_bs_tag;
105 	sc->sc_iot = &armv7_bs_tag;
106 #endif
107 	sc->sc_dmat = &mainbus_dma_tag;
108 	sc->sc_acells = OF_getpropint(OF_peer(0), "#address-cells", 1);
109 	sc->sc_scells = OF_getpropint(OF_peer(0), "#size-cells", 1);
110 
111 	if ((len = OF_getprop(node, "model", buffer, sizeof(buffer))) > 0) {
112 		printf(": %s\n", buffer);
113 		hw_prod = malloc(len, M_DEVBUF, M_NOWAIT);
114 		if (hw_prod)
115 			strlcpy(hw_prod, buffer, len);
116 	} else
117 		printf(": unknown model\n");
118 
119 	/* Attach CPU first. */
120 	mainbus_legacy_found(self, "cpu");
121 #ifdef CPU_ARMv7
122 	extern void platform_init_mainbus(struct device *);
123 	platform_init_mainbus(self);
124 #endif
125 
126 	/* TODO: Scan for interrupt controllers and attach them first? */
127 
128 	sc->sc_rangeslen = OF_getproplen(OF_peer(0), "ranges");
129 	if (sc->sc_rangeslen > 0 && !(sc->sc_rangeslen % sizeof(uint32_t))) {
130 		sc->sc_ranges = malloc(sc->sc_rangeslen, M_TEMP, M_WAITOK);
131 		OF_getpropintarray(OF_peer(0), "ranges", sc->sc_ranges,
132 		    sc->sc_rangeslen);
133 	}
134 
135 	/* Scan the whole tree. */
136 	for (node = OF_child(node);
137 	    node != 0;
138 	    node = OF_peer(node))
139 	{
140 		mainbus_attach_node(self, node);
141 	}
142 }
143 
144 /*
145  * Look for a driver that wants to be attached to this node.
146  */
147 void
148 mainbus_attach_node(struct device *self, int node)
149 {
150 	struct mainbus_softc	*sc = (struct mainbus_softc *)self;
151 	struct fdt_attach_args	 fa;
152 	char			 buffer[128];
153 	int			 i, len, line;
154 	uint32_t		*cell, *reg;
155 
156 	if (!OF_getprop(node, "compatible", buffer, sizeof(buffer)))
157 		return;
158 
159 	if (OF_getprop(node, "status", buffer, sizeof(buffer)))
160 		if (!strcmp(buffer, "disabled"))
161 			return;
162 
163 	memset(&fa, 0, sizeof(fa));
164 	fa.fa_name = "";
165 	fa.fa_node = node;
166 	fa.fa_iot = sc->sc_iot;
167 	fa.fa_dmat = sc->sc_dmat;
168 	fa.fa_acells = sc->sc_acells;
169 	fa.fa_scells = sc->sc_scells;
170 
171 	len = OF_getproplen(node, "reg");
172 	line = (sc->sc_acells + sc->sc_scells) * sizeof(uint32_t);
173 	if (len > 0 && (len % line) == 0) {
174 		reg = malloc(len, M_TEMP, M_WAITOK);
175 		OF_getpropintarray(node, "reg", reg, len);
176 
177 		fa.fa_reg = malloc((len / line) * sizeof(struct fdt_reg),
178 		    M_DEVBUF, M_WAITOK);
179 		fa.fa_nreg = (len / line);
180 
181 		for (i = 0, cell = reg; i < len / line; i++) {
182 			if (sc->sc_acells >= 1)
183 				fa.fa_reg[i].addr = cell[0];
184 			if (sc->sc_acells == 2) {
185 				fa.fa_reg[i].addr <<= 32;
186 				fa.fa_reg[i].addr |= cell[1];
187 			}
188 			cell += sc->sc_acells;
189 			if (sc->sc_scells >= 1)
190 				fa.fa_reg[i].size = cell[0];
191 			if (sc->sc_scells == 2) {
192 				fa.fa_reg[i].size <<= 32;
193 				fa.fa_reg[i].size |= cell[1];
194 			}
195 			cell += sc->sc_scells;
196 		}
197 
198 		free(reg, M_TEMP, len);
199 	}
200 
201 	len = OF_getproplen(node, "interrupts");
202 	if (len > 0 && (len % sizeof(uint32_t)) == 0) {
203 		fa.fa_intr = malloc(len, M_DEVBUF, M_WAITOK);
204 		fa.fa_nintr = len / sizeof(uint32_t);
205 
206 		OF_getpropintarray(node, "interrupts", fa.fa_intr, len);
207 	}
208 
209 	/* TODO: attach the device's clocks first? */
210 
211 	config_found(self, &fa, NULL);
212 
213 	free(fa.fa_reg, M_DEVBUF, fa.fa_nreg * sizeof(struct fdt_reg));
214 	free(fa.fa_intr, M_DEVBUF, fa.fa_nintr * sizeof(uint32_t));
215 }
216 
217 /*
218  * Legacy support for SoCs that do not use FDT.
219  */
220 int
221 mainbus_legacy_search(struct device *parent, void *match, void *aux)
222 {
223 	union mainbus_attach_args ma;
224 	struct cfdata		*cf = match;
225 
226 	memset(&ma, 0, sizeof(ma));
227 	ma.ma_name = cf->cf_driver->cd_name;
228 
229 	/* allow for devices to be disabled in UKC */
230 	if ((*cf->cf_attach->ca_match)(parent, cf, &ma) == 0)
231 		return 0;
232 
233 	config_attach(parent, cf, &ma, NULL);
234 	return 1;
235 }
236 
237 void
238 mainbus_legacy_found(struct device *self, char *name)
239 {
240 	union mainbus_attach_args ma;
241 
242 	memset(&ma, 0, sizeof(ma));
243 	ma.ma_name = name;
244 
245 	config_found(self, &ma, NULL);
246 }
247