xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision 8ef24a0d4b28fe230e20637f56869cc4148cd2ca)
1 /*-
2  * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/systm.h>
39 
40 #include <machine/bus.h>
41 
42 #include "bcmavar.h"
43 
44 #include "bcma_eromreg.h"
45 #include "bcma_eromvar.h"
46 
47 int
48 bcma_probe(device_t dev)
49 {
50 	device_set_desc(dev, "BCMA BHND bus");
51 	return (BUS_PROBE_DEFAULT);
52 }
53 
54 int
55 bcma_attach(device_t dev)
56 {
57 	struct bcma_devinfo	*dinfo;
58 	device_t		*devs, child;
59 	int			 ndevs;
60 	int			 error;
61 
62 
63 	if ((error = device_get_children(dev, &devs, &ndevs)))
64 		return (error);
65 
66 	/*
67 	 * Map our children's agent register block.
68 	 */
69 	for (int i = 0; i < ndevs; i++) {
70 		bhnd_addr_t	addr;
71 		bhnd_size_t	size;
72 		rman_res_t	r_start, r_count, r_end;
73 
74 		child = devs[i];
75 		dinfo = device_get_ivars(child);
76 
77 		KASSERT(!device_is_suspended(child),
78 		    ("bcma(4) stateful suspend handling requires that devices "
79 		        "not be suspended before bcma_attach()"));
80 
81 		/* Verify that the agent register block exists and is
82 		 * mappable */
83 		if (bhnd_get_port_rid(child, BHND_PORT_AGENT, 0, 0) == -1)
84 			continue;
85 
86 		/* Fetch the address of the agent register block */
87 		error = bhnd_get_region_addr(child, BHND_PORT_AGENT, 0, 0,
88 		    &addr, &size);
89 		if (error) {
90 			device_printf(dev, "failed fetching agent register "
91 			    "block address for core %d\n", i);
92 			goto cleanup;
93 		}
94 
95 		/* Allocate the resource */
96 		r_start = addr;
97 		r_count = size;
98 		r_end = r_start + r_count - 1;
99 
100 		dinfo->rid_agent = i + 1;
101 		dinfo->res_agent = bhnd_alloc_resource(dev, SYS_RES_MEMORY,
102 		    &dinfo->rid_agent, r_start, r_end, r_count, RF_ACTIVE);
103 		if (dinfo->res_agent == NULL) {
104 			device_printf(dev, "failed allocating agent register "
105 			    "block for core %d\n", i);
106 			error = ENXIO;
107 			goto cleanup;
108 		}
109 	}
110 
111 cleanup:
112 	free(devs, M_BHND);
113 	if (error)
114 		return (error);
115 
116 	return (bhnd_generic_attach(dev));
117 }
118 
119 int
120 bcma_detach(device_t dev)
121 {
122 	return (bhnd_generic_detach(dev));
123 }
124 
125 static int
126 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
127 {
128 	const struct bcma_devinfo *dinfo;
129 	const struct bhnd_core_info *ci;
130 
131 	dinfo = device_get_ivars(child);
132 	ci = &dinfo->corecfg->core_info;
133 
134 	switch (index) {
135 	case BHND_IVAR_VENDOR:
136 		*result = ci->vendor;
137 		return (0);
138 	case BHND_IVAR_DEVICE:
139 		*result = ci->device;
140 		return (0);
141 	case BHND_IVAR_HWREV:
142 		*result = ci->hwrev;
143 		return (0);
144 	case BHND_IVAR_DEVICE_CLASS:
145 		*result = bhnd_core_class(ci);
146 		return (0);
147 	case BHND_IVAR_VENDOR_NAME:
148 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
149 		return (0);
150 	case BHND_IVAR_DEVICE_NAME:
151 		*result = (uintptr_t) bhnd_core_name(ci);
152 		return (0);
153 	case BHND_IVAR_CORE_INDEX:
154 		*result = ci->core_idx;
155 		return (0);
156 	case BHND_IVAR_CORE_UNIT:
157 		*result = ci->unit;
158 		return (0);
159 	default:
160 		return (ENOENT);
161 	}
162 }
163 
164 static int
165 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
166 {
167 	switch (index) {
168 	case BHND_IVAR_VENDOR:
169 	case BHND_IVAR_DEVICE:
170 	case BHND_IVAR_HWREV:
171 	case BHND_IVAR_DEVICE_CLASS:
172 	case BHND_IVAR_VENDOR_NAME:
173 	case BHND_IVAR_DEVICE_NAME:
174 	case BHND_IVAR_CORE_INDEX:
175 	case BHND_IVAR_CORE_UNIT:
176 		return (EINVAL);
177 	default:
178 		return (ENOENT);
179 	}
180 }
181 
182 static void
183 bcma_child_deleted(device_t dev, device_t child)
184 {
185 	struct bcma_devinfo *dinfo = device_get_ivars(child);
186 	if (dinfo != NULL)
187 		bcma_free_dinfo(dev, dinfo);
188 }
189 
190 static struct resource_list *
191 bcma_get_resource_list(device_t dev, device_t child)
192 {
193 	struct bcma_devinfo *dinfo = device_get_ivars(child);
194 	return (&dinfo->resources);
195 }
196 
197 static device_t
198 bcma_find_hostb_device(device_t dev)
199 {
200 	struct bcma_softc *sc = device_get_softc(dev);
201 
202 	/* This is set (or not) by the concrete bcma driver subclass. */
203 	return (sc->hostb_dev);
204 }
205 
206 static int
207 bcma_reset_core(device_t dev, device_t child, uint16_t flags)
208 {
209 	struct bcma_devinfo *dinfo;
210 
211 	if (device_get_parent(child) != dev)
212 		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
213 
214 	dinfo = device_get_ivars(child);
215 
216 	/* Can't reset the core without access to the agent registers */
217 	if (dinfo->res_agent == NULL)
218 		return (ENODEV);
219 
220 	// TODO - perform reset
221 
222 	return (ENXIO);
223 }
224 
225 static int
226 bcma_suspend_core(device_t dev, device_t child)
227 {
228 	struct bcma_devinfo *dinfo;
229 
230 	if (device_get_parent(child) != dev)
231 		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
232 
233 	dinfo = device_get_ivars(child);
234 
235 	/* Can't suspend the core without access to the agent registers */
236 	if (dinfo->res_agent == NULL)
237 		return (ENODEV);
238 
239 	// TODO - perform suspend
240 
241 	return (ENXIO);
242 }
243 
244 static u_int
245 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
246 {
247 	struct bcma_devinfo *dinfo;
248 
249 	/* delegate non-bus-attached devices to our parent */
250 	if (device_get_parent(child) != dev)
251 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
252 		    type));
253 
254 	dinfo = device_get_ivars(child);
255 	switch (type) {
256 	case BHND_PORT_DEVICE:
257 		return (dinfo->corecfg->num_dev_ports);
258 	case BHND_PORT_BRIDGE:
259 		return (dinfo->corecfg->num_bridge_ports);
260 	case BHND_PORT_AGENT:
261 		return (dinfo->corecfg->num_wrapper_ports);
262 	default:
263 		device_printf(dev, "%s: unknown type (%d)\n",
264 		    __func__,
265 		    type);
266 		return (0);
267 	}
268 }
269 
270 static u_int
271 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
272     u_int port_num)
273 {
274 	struct bcma_devinfo	*dinfo;
275 	struct bcma_sport_list	*ports;
276 	struct bcma_sport	*port;
277 
278 	/* delegate non-bus-attached devices to our parent */
279 	if (device_get_parent(child) != dev)
280 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
281 		    type, port_num));
282 
283 	dinfo = device_get_ivars(child);
284 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
285 
286 	STAILQ_FOREACH(port, ports, sp_link) {
287 		if (port->sp_num == port_num)
288 			return (port->sp_num_maps);
289 	}
290 
291 	/* not found */
292 	return (0);
293 }
294 
295 static int
296 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
297     u_int port_num, u_int region_num)
298 {
299 	struct bcma_devinfo	*dinfo;
300 	struct bcma_map		*map;
301 	struct bcma_sport_list	*ports;
302 	struct bcma_sport	*port;
303 
304 	dinfo = device_get_ivars(child);
305 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
306 
307 	STAILQ_FOREACH(port, ports, sp_link) {
308 		if (port->sp_num != port_num)
309 			continue;
310 
311 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
312 			if (map->m_region_num == region_num)
313 				return map->m_rid;
314 	}
315 
316 	return -1;
317 }
318 
319 static int
320 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
321     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
322 {
323 	struct bcma_devinfo	*dinfo;
324 	struct bcma_map		*map;
325 	struct bcma_sport_list	*ports;
326 	struct bcma_sport	*port;
327 
328 	dinfo = device_get_ivars(child);
329 
330 	/* Ports are always memory mapped */
331 	if (type != SYS_RES_MEMORY)
332 		return (EINVAL);
333 
334 	/* Starting with the most likely device list, search all three port
335 	 * lists */
336 	bhnd_port_type types[] = {
337 	    BHND_PORT_DEVICE,
338 	    BHND_PORT_AGENT,
339 	    BHND_PORT_BRIDGE
340 	};
341 
342 	for (int i = 0; i < nitems(types); i++) {
343 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
344 
345 		STAILQ_FOREACH(port, ports, sp_link) {
346 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
347 				if (map->m_rid != rid)
348 					continue;
349 
350 				*port_type = port->sp_type;
351 				*port_num = port->sp_num;
352 				*region_num = map->m_region_num;
353 				return (0);
354 			}
355 		}
356 	}
357 
358 	return (ENOENT);
359 }
360 
361 static int
362 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
363     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
364 {
365 	struct bcma_devinfo	*dinfo;
366 	struct bcma_map		*map;
367 	struct bcma_sport_list	*ports;
368 	struct bcma_sport	*port;
369 
370 	dinfo = device_get_ivars(child);
371 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
372 
373 	/* Search the port list */
374 	STAILQ_FOREACH(port, ports, sp_link) {
375 		if (port->sp_num != port_num)
376 			continue;
377 
378 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
379 			if (map->m_region_num != region_num)
380 				continue;
381 
382 			/* Found! */
383 			*addr = map->m_base;
384 			*size = map->m_size;
385 			return (0);
386 		}
387 	}
388 
389 	return (ENOENT);
390 }
391 
392 /**
393  * Scan a device enumeration ROM table, adding all valid discovered cores to
394  * the bus.
395  *
396  * @param bus The bcma bus.
397  * @param erom_res An active resource mapping the EROM core.
398  * @param erom_offset Base offset of the EROM core's register mapping.
399  */
400 int
401 bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
402 {
403 	struct bcma_erom	 erom;
404 	struct bcma_corecfg	*corecfg;
405 	struct bcma_devinfo	*dinfo;
406 	device_t		 child;
407 	int			 error;
408 
409 	dinfo = NULL;
410 	corecfg = NULL;
411 
412 	/* Initialize our reader */
413 	error = bcma_erom_open(&erom, erom_res, erom_offset);
414 	if (error)
415 		return (error);
416 
417 	/* Add all cores. */
418 	while (!error) {
419 		/* Parse next core */
420 		error = bcma_erom_parse_corecfg(&erom, &corecfg);
421 		if (error && error == ENOENT) {
422 			return (0);
423 		} else if (error) {
424 			goto failed;
425 		}
426 
427 		/* Allocate per-device bus info */
428 		dinfo = bcma_alloc_dinfo(bus, corecfg);
429 		if (dinfo == NULL) {
430 			error = ENXIO;
431 			goto failed;
432 		}
433 
434 		/* The dinfo instance now owns the corecfg value */
435 		corecfg = NULL;
436 
437 		/* Add the child device */
438 		child = device_add_child(bus, NULL, -1);
439 		if (child == NULL) {
440 			error = ENXIO;
441 			goto failed;
442 		}
443 
444 		/* The child device now owns the dinfo pointer */
445 		device_set_ivars(child, dinfo);
446 		dinfo = NULL;
447 
448 		/* If pins are floating or the hardware is otherwise
449 		 * unpopulated, the device shouldn't be used. */
450 		if (bhnd_is_hw_disabled(child))
451 			device_disable(child);
452 	}
453 
454 	/* Hit EOF parsing cores? */
455 	if (error == ENOENT)
456 		return (0);
457 
458 failed:
459 	if (dinfo != NULL)
460 		bcma_free_dinfo(bus, dinfo);
461 
462 	if (corecfg != NULL)
463 		bcma_free_corecfg(corecfg);
464 
465 	return (error);
466 }
467 
468 
469 static device_method_t bcma_methods[] = {
470 	/* Device interface */
471 	DEVMETHOD(device_probe,			bcma_probe),
472 	DEVMETHOD(device_attach,		bcma_attach),
473 	DEVMETHOD(device_detach,		bcma_detach),
474 
475 	/* Bus interface */
476 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
477 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
478 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
479 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
480 
481 	/* BHND interface */
482 	DEVMETHOD(bhnd_bus_find_hostb_device,	bcma_find_hostb_device),
483 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
484 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
485 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
486 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
487 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
488 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
489 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
490 
491 	DEVMETHOD_END
492 };
493 
494 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
495 MODULE_VERSION(bcma, 1);
496 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
497