xref: /freebsd-12.1/sys/dev/le/lebuffer_sbus.c (revision 217d17bc)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 Marius Strobl <[email protected]>
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 AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, 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 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 #include <sys/resource.h>
38 #include <sys/rman.h>
39 
40 #include <dev/ofw/ofw_bus.h>
41 #include <dev/ofw/ofw_bus_subr.h>
42 #include <dev/ofw/openfirm.h>
43 
44 #include <machine/bus.h>
45 #include <machine/bus_common.h>
46 #include <machine/resource.h>
47 
48 #include <sparc64/sbus/ofw_sbus.h>
49 #include <sparc64/sbus/sbusreg.h>
50 #include <sparc64/sbus/sbusvar.h>
51 
52 struct lebuffer_devinfo {
53 	struct ofw_bus_devinfo	ldi_obdinfo;
54 	struct resource_list	ldi_rl;
55 };
56 
57 static devclass_t lebuffer_devclass;
58 
59 static device_probe_t lebuffer_probe;
60 static device_attach_t lebuffer_attach;
61 static device_detach_t lebuffer_detach;
62 static bus_print_child_t lebuffer_print_child;
63 static bus_probe_nomatch_t lebuffer_probe_nomatch;
64 static bus_get_resource_list_t lebuffer_get_resource_list;
65 static ofw_bus_get_devinfo_t lebuffer_get_devinfo;
66 
67 static struct lebuffer_devinfo *lebuffer_setup_dinfo(device_t, phandle_t);
68 static void lebuffer_destroy_dinfo(struct lebuffer_devinfo *);
69 static int lebuffer_print_res(struct lebuffer_devinfo *);
70 
71 static device_method_t lebuffer_methods[] = {
72 	/* Device interface */
73 	DEVMETHOD(device_probe,		lebuffer_probe),
74 	DEVMETHOD(device_attach,	lebuffer_attach),
75 	DEVMETHOD(device_detach,	lebuffer_detach),
76 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
77 	DEVMETHOD(device_suspend,	bus_generic_suspend),
78 	DEVMETHOD(device_resume,	bus_generic_resume),
79 
80 	/* Bus interface */
81 	DEVMETHOD(bus_print_child,	lebuffer_print_child),
82 	DEVMETHOD(bus_probe_nomatch,	lebuffer_probe_nomatch),
83 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
84 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
85 	DEVMETHOD(bus_alloc_resource,	bus_generic_rl_alloc_resource),
86 	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
87 	DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource),
88 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
89 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
90 	DEVMETHOD(bus_get_resource_list, lebuffer_get_resource_list),
91 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
92 
93 	/* ofw_bus interface */
94 	DEVMETHOD(ofw_bus_get_devinfo,	lebuffer_get_devinfo),
95 	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
96 	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
97 	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
98 	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
99 	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
100 
101 	{ 0, 0 }
102 };
103 
104 DEFINE_CLASS_0(lebuffer, lebuffer_driver, lebuffer_methods, 1);
105 DRIVER_MODULE(lebuffer, sbus, lebuffer_driver, lebuffer_devclass, 0, 0);
106 MODULE_DEPEND(lebuffer, sbus, 1, 1, 1);
107 MODULE_VERSION(lebuffer, 1);
108 
109 static int
lebuffer_probe(device_t dev)110 lebuffer_probe(device_t dev)
111 {
112 	const char *name;
113 
114 	name = ofw_bus_get_name(dev);
115 	if (strcmp(name, "lebuffer") == 0) {
116 		device_set_desc_copy(dev, name);
117 		return (0);
118 	}
119 	return (ENXIO);
120 }
121 
122 static int
lebuffer_attach(device_t dev)123 lebuffer_attach(device_t dev)
124 {
125 	struct lebuffer_devinfo *ldi;
126 	device_t cdev;
127 	phandle_t child;
128 	int children;
129 
130 	children = 0;
131 	for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
132 	    child = OF_peer(child)) {
133 		if ((ldi = lebuffer_setup_dinfo(dev, child)) == NULL)
134 			continue;
135 		if (children != 0) {
136 			device_printf(dev,
137 			    "<%s>: only one child per buffer supported\n",
138 			    ldi->ldi_obdinfo.obd_name);
139 			lebuffer_destroy_dinfo(ldi);
140 			continue;
141 		}
142 		if ((cdev = device_add_child(dev, NULL, -1)) == NULL) {
143 			device_printf(dev, "<%s>: device_add_child failed\n",
144 			    ldi->ldi_obdinfo.obd_name);
145 			lebuffer_destroy_dinfo(ldi);
146 			continue;
147 		}
148 		device_set_ivars(cdev, ldi);
149 		children++;
150 	}
151 	return (bus_generic_attach(dev));
152 }
153 
154 static int
lebuffer_detach(device_t dev)155 lebuffer_detach(device_t dev)
156 {
157 	device_t *children;
158 	int i, nchildren;
159 
160 	bus_generic_detach(dev);
161 	if (device_get_children(dev, &children, &nchildren) == 0) {
162 		for (i = 0; i < nchildren; i++) {
163 			lebuffer_destroy_dinfo(device_get_ivars(children[i]));
164 			device_delete_child(dev, children[i]);
165 		}
166 		free(children, M_TEMP);
167 	}
168 	return (0);
169 }
170 
171 static struct lebuffer_devinfo *
lebuffer_setup_dinfo(device_t dev,phandle_t node)172 lebuffer_setup_dinfo(device_t dev, phandle_t node)
173 {
174 	struct lebuffer_devinfo *ldi;
175 	struct sbus_regs *reg;
176 	uint32_t base, iv, *intr;
177 	int i, nreg, nintr, slot, rslot;
178 
179 	ldi = malloc(sizeof(*ldi), M_DEVBUF, M_WAITOK | M_ZERO);
180 	if (ofw_bus_gen_setup_devinfo(&ldi->ldi_obdinfo, node) != 0) {
181 		free(ldi, M_DEVBUF);
182 		return (NULL);
183 	}
184 	resource_list_init(&ldi->ldi_rl);
185 	slot = -1;
186 	nreg = OF_getprop_alloc_multi(node, "reg", sizeof(*reg), (void **)&reg);
187 	if (nreg == -1) {
188 		device_printf(dev, "<%s>: incomplete\n",
189 		    ldi->ldi_obdinfo.obd_name);
190 		goto fail;
191 	}
192 	for (i = 0; i < nreg; i++) {
193 		base = reg[i].sbr_offset;
194 		if (SBUS_ABS(base)) {
195 			rslot = SBUS_ABS_TO_SLOT(base);
196 			base = SBUS_ABS_TO_OFFSET(base);
197 		} else
198 			rslot = reg[i].sbr_slot;
199 		if (slot != -1 && slot != rslot) {
200 			device_printf(dev, "<%s>: multiple slots\n",
201 			    ldi->ldi_obdinfo.obd_name);
202 			OF_prop_free(reg);
203 			goto fail;
204 		}
205 		slot = rslot;
206 
207 		resource_list_add(&ldi->ldi_rl, SYS_RES_MEMORY, i, base,
208 		    base + reg[i].sbr_size, reg[i].sbr_size);
209 	}
210 	OF_prop_free(reg);
211 	if (slot != sbus_get_slot(dev)) {
212 		device_printf(dev, "<%s>: parent and child slot do not match\n",
213 		    ldi->ldi_obdinfo.obd_name);
214 		goto fail;
215 	}
216 
217 	/*
218 	 * The `interrupts' property contains the SBus interrupt level.
219 	 */
220 	nintr = OF_getprop_alloc_multi(node, "interrupts", sizeof(*intr),
221 	    (void **)&intr);
222 	if (nintr != -1) {
223 		for (i = 0; i < nintr; i++) {
224 			iv = intr[i];
225 			/*
226 			 * SBus card devices need the slot number encoded into
227 			 * the vector as this is generally not done.
228 			 */
229 			if ((iv & INTMAP_OBIO_MASK) == 0)
230 				iv |= slot << 3;
231 			/* Set the IGN as appropriate. */
232 			iv |= sbus_get_ign(dev) << INTMAP_IGN_SHIFT;
233 			resource_list_add(&ldi->ldi_rl, SYS_RES_IRQ, i,
234 			    iv, iv, 1);
235 		}
236 		OF_prop_free(intr);
237 	}
238 	return (ldi);
239 
240  fail:
241 	lebuffer_destroy_dinfo(ldi);
242 	return (NULL);
243 }
244 
245 static void
lebuffer_destroy_dinfo(struct lebuffer_devinfo * dinfo)246 lebuffer_destroy_dinfo(struct lebuffer_devinfo *dinfo)
247 {
248 
249 	resource_list_free(&dinfo->ldi_rl);
250 	ofw_bus_gen_destroy_devinfo(&dinfo->ldi_obdinfo);
251 	free(dinfo, M_DEVBUF);
252 }
253 
254 static int
lebuffer_print_child(device_t dev,device_t child)255 lebuffer_print_child(device_t dev, device_t child)
256 {
257 	int rv;
258 
259 	rv = bus_print_child_header(dev, child);
260 	rv += lebuffer_print_res(device_get_ivars(child));
261 	rv += bus_print_child_footer(dev, child);
262 	return (rv);
263 }
264 
265 static void
lebuffer_probe_nomatch(device_t dev,device_t child)266 lebuffer_probe_nomatch(device_t dev, device_t child)
267 {
268 	const char *type;
269 
270 	device_printf(dev, "<%s>", ofw_bus_get_name(child));
271 	lebuffer_print_res(device_get_ivars(child));
272 	type = ofw_bus_get_type(child);
273 	printf(" type %s (no driver attached)\n",
274 	    type != NULL ? type : "unknown");
275 }
276 
277 static struct resource_list *
lebuffer_get_resource_list(device_t dev,device_t child)278 lebuffer_get_resource_list(device_t dev, device_t child)
279 {
280 	struct lebuffer_devinfo *ldi;
281 
282 	ldi = device_get_ivars(child);
283 	return (&ldi->ldi_rl);
284 }
285 
286 static const struct ofw_bus_devinfo *
lebuffer_get_devinfo(device_t bus,device_t child)287 lebuffer_get_devinfo(device_t bus, device_t child)
288 {
289 	struct lebuffer_devinfo *ldi;
290 
291 	ldi = device_get_ivars(child);
292 	return (&ldi->ldi_obdinfo);
293 }
294 
295 static int
lebuffer_print_res(struct lebuffer_devinfo * ldi)296 lebuffer_print_res(struct lebuffer_devinfo *ldi)
297 {
298 	int rv;
299 
300 	rv = 0;
301 	rv += resource_list_print_type(&ldi->ldi_rl, "mem", SYS_RES_MEMORY,
302 	    "%#jx");
303 	rv += resource_list_print_type(&ldi->ldi_rl, "irq", SYS_RES_IRQ, "%jd");
304 	return (rv);
305 }
306