xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision f061a2215f9bf0bea98ac601a34750f89428db67)
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_BUS_ALLOC_RESOURCE(dev, dev,
102 		    SYS_RES_MEMORY, &dinfo->rid_agent, r_start, r_end, r_count,
103 		    RF_ACTIVE);
104 		if (dinfo->res_agent == NULL) {
105 			device_printf(dev, "failed allocating agent register "
106 			    "block for core %d\n", i);
107 			error = ENXIO;
108 			goto cleanup;
109 		}
110 	}
111 
112 cleanup:
113 	free(devs, M_BHND);
114 	if (error)
115 		return (error);
116 
117 	return (bhnd_generic_attach(dev));
118 }
119 
120 int
121 bcma_detach(device_t dev)
122 {
123 	return (bhnd_generic_detach(dev));
124 }
125 
126 static int
127 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
128 {
129 	const struct bcma_devinfo *dinfo;
130 	const struct bhnd_core_info *ci;
131 
132 	dinfo = device_get_ivars(child);
133 	ci = &dinfo->corecfg->core_info;
134 
135 	switch (index) {
136 	case BHND_IVAR_VENDOR:
137 		*result = ci->vendor;
138 		return (0);
139 	case BHND_IVAR_DEVICE:
140 		*result = ci->device;
141 		return (0);
142 	case BHND_IVAR_HWREV:
143 		*result = ci->hwrev;
144 		return (0);
145 	case BHND_IVAR_DEVICE_CLASS:
146 		*result = bhnd_core_class(ci);
147 		return (0);
148 	case BHND_IVAR_VENDOR_NAME:
149 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
150 		return (0);
151 	case BHND_IVAR_DEVICE_NAME:
152 		*result = (uintptr_t) bhnd_core_name(ci);
153 		return (0);
154 	case BHND_IVAR_CORE_INDEX:
155 		*result = ci->core_idx;
156 		return (0);
157 	case BHND_IVAR_CORE_UNIT:
158 		*result = ci->unit;
159 		return (0);
160 	default:
161 		return (ENOENT);
162 	}
163 }
164 
165 static int
166 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
167 {
168 	switch (index) {
169 	case BHND_IVAR_VENDOR:
170 	case BHND_IVAR_DEVICE:
171 	case BHND_IVAR_HWREV:
172 	case BHND_IVAR_DEVICE_CLASS:
173 	case BHND_IVAR_VENDOR_NAME:
174 	case BHND_IVAR_DEVICE_NAME:
175 	case BHND_IVAR_CORE_INDEX:
176 	case BHND_IVAR_CORE_UNIT:
177 		return (EINVAL);
178 	default:
179 		return (ENOENT);
180 	}
181 }
182 
183 static void
184 bcma_child_deleted(device_t dev, device_t child)
185 {
186 	struct bcma_devinfo *dinfo = device_get_ivars(child);
187 	if (dinfo != NULL)
188 		bcma_free_dinfo(dev, dinfo);
189 }
190 
191 static struct resource_list *
192 bcma_get_resource_list(device_t dev, device_t child)
193 {
194 	struct bcma_devinfo *dinfo = device_get_ivars(child);
195 	return (&dinfo->resources);
196 }
197 
198 static device_t
199 bcma_find_hostb_device(device_t dev)
200 {
201 	struct bcma_softc *sc = device_get_softc(dev);
202 
203 	/* This is set (or not) by the concrete bcma driver subclass. */
204 	return (sc->hostb_dev);
205 }
206 
207 static int
208 bcma_reset_core(device_t dev, device_t child, uint16_t flags)
209 {
210 	struct bcma_devinfo *dinfo;
211 
212 	if (device_get_parent(child) != dev)
213 		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
214 
215 	dinfo = device_get_ivars(child);
216 
217 	/* Can't reset the core without access to the agent registers */
218 	if (dinfo->res_agent == NULL)
219 		return (ENODEV);
220 
221 	// TODO - perform reset
222 
223 	return (ENXIO);
224 }
225 
226 static int
227 bcma_suspend_core(device_t dev, device_t child)
228 {
229 	struct bcma_devinfo *dinfo;
230 
231 	if (device_get_parent(child) != dev)
232 		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
233 
234 	dinfo = device_get_ivars(child);
235 
236 	/* Can't suspend the core without access to the agent registers */
237 	if (dinfo->res_agent == NULL)
238 		return (ENODEV);
239 
240 	// TODO - perform suspend
241 
242 	return (ENXIO);
243 }
244 
245 static u_int
246 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
247 {
248 	struct bcma_devinfo *dinfo;
249 
250 	/* delegate non-bus-attached devices to our parent */
251 	if (device_get_parent(child) != dev)
252 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
253 		    type));
254 
255 	dinfo = device_get_ivars(child);
256 	switch (type) {
257 	case BHND_PORT_DEVICE:
258 		return (dinfo->corecfg->num_dev_ports);
259 	case BHND_PORT_BRIDGE:
260 		return (dinfo->corecfg->num_bridge_ports);
261 	case BHND_PORT_AGENT:
262 		return (dinfo->corecfg->num_wrapper_ports);
263 	default:
264 		device_printf(dev, "%s: unknown type (%d)\n",
265 		    __func__,
266 		    type);
267 		return (0);
268 	}
269 }
270 
271 static u_int
272 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
273     u_int port_num)
274 {
275 	struct bcma_devinfo	*dinfo;
276 	struct bcma_sport_list	*ports;
277 	struct bcma_sport	*port;
278 
279 	/* delegate non-bus-attached devices to our parent */
280 	if (device_get_parent(child) != dev)
281 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
282 		    type, port_num));
283 
284 	dinfo = device_get_ivars(child);
285 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
286 
287 	STAILQ_FOREACH(port, ports, sp_link) {
288 		if (port->sp_num == port_num)
289 			return (port->sp_num_maps);
290 	}
291 
292 	/* not found */
293 	return (0);
294 }
295 
296 static int
297 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
298     u_int port_num, u_int region_num)
299 {
300 	struct bcma_devinfo	*dinfo;
301 	struct bcma_map		*map;
302 	struct bcma_sport_list	*ports;
303 	struct bcma_sport	*port;
304 
305 	dinfo = device_get_ivars(child);
306 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
307 
308 	STAILQ_FOREACH(port, ports, sp_link) {
309 		if (port->sp_num != port_num)
310 			continue;
311 
312 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
313 			if (map->m_region_num == region_num)
314 				return map->m_rid;
315 	}
316 
317 	return -1;
318 }
319 
320 static int
321 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
322     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
323 {
324 	struct bcma_devinfo	*dinfo;
325 	struct bcma_map		*map;
326 	struct bcma_sport_list	*ports;
327 	struct bcma_sport	*port;
328 
329 	dinfo = device_get_ivars(child);
330 
331 	/* Ports are always memory mapped */
332 	if (type != SYS_RES_MEMORY)
333 		return (EINVAL);
334 
335 	/* Starting with the most likely device list, search all three port
336 	 * lists */
337 	bhnd_port_type types[] = {
338 	    BHND_PORT_DEVICE,
339 	    BHND_PORT_AGENT,
340 	    BHND_PORT_BRIDGE
341 	};
342 
343 	for (int i = 0; i < nitems(types); i++) {
344 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
345 
346 		STAILQ_FOREACH(port, ports, sp_link) {
347 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
348 				if (map->m_rid != rid)
349 					continue;
350 
351 				*port_type = port->sp_type;
352 				*port_num = port->sp_num;
353 				*region_num = map->m_region_num;
354 				return (0);
355 			}
356 		}
357 	}
358 
359 	return (ENOENT);
360 }
361 
362 static int
363 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
364     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
365 {
366 	struct bcma_devinfo	*dinfo;
367 	struct bcma_map		*map;
368 	struct bcma_sport_list	*ports;
369 	struct bcma_sport	*port;
370 
371 	dinfo = device_get_ivars(child);
372 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
373 
374 	/* Search the port list */
375 	STAILQ_FOREACH(port, ports, sp_link) {
376 		if (port->sp_num != port_num)
377 			continue;
378 
379 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
380 			if (map->m_region_num != region_num)
381 				continue;
382 
383 			/* Found! */
384 			*addr = map->m_base;
385 			*size = map->m_size;
386 			return (0);
387 		}
388 	}
389 
390 	return (ENOENT);
391 }
392 
393 /**
394  * Scan a device enumeration ROM table, adding all valid discovered cores to
395  * the bus.
396  *
397  * @param bus The bcma bus.
398  * @param erom_res An active resource mapping the EROM core.
399  * @param erom_offset Base offset of the EROM core's register mapping.
400  */
401 int
402 bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
403 {
404 	struct bcma_erom	 erom;
405 	struct bcma_corecfg	*corecfg;
406 	struct bcma_devinfo	*dinfo;
407 	device_t		 child;
408 	int			 error;
409 
410 	dinfo = NULL;
411 	corecfg = NULL;
412 
413 	/* Initialize our reader */
414 	error = bcma_erom_open(&erom, erom_res, erom_offset);
415 	if (error)
416 		return (error);
417 
418 	/* Add all cores. */
419 	while (!error) {
420 		/* Parse next core */
421 		error = bcma_erom_parse_corecfg(&erom, &corecfg);
422 		if (error && error == ENOENT) {
423 			return (0);
424 		} else if (error) {
425 			goto failed;
426 		}
427 
428 		/* Allocate per-device bus info */
429 		dinfo = bcma_alloc_dinfo(bus, corecfg);
430 		if (dinfo == NULL) {
431 			error = ENXIO;
432 			goto failed;
433 		}
434 
435 		/* The dinfo instance now owns the corecfg value */
436 		corecfg = NULL;
437 
438 		/* Add the child device */
439 		child = device_add_child(bus, NULL, -1);
440 		if (child == NULL) {
441 			error = ENXIO;
442 			goto failed;
443 		}
444 
445 		/* The child device now owns the dinfo pointer */
446 		device_set_ivars(child, dinfo);
447 		dinfo = NULL;
448 
449 		/* If pins are floating or the hardware is otherwise
450 		 * unpopulated, the device shouldn't be used. */
451 		if (bhnd_is_hw_disabled(child))
452 			device_disable(child);
453 	}
454 
455 	/* Hit EOF parsing cores? */
456 	if (error == ENOENT)
457 		return (0);
458 
459 failed:
460 	if (dinfo != NULL)
461 		bcma_free_dinfo(bus, dinfo);
462 
463 	if (corecfg != NULL)
464 		bcma_free_corecfg(corecfg);
465 
466 	return (error);
467 }
468 
469 
470 static device_method_t bcma_methods[] = {
471 	/* Device interface */
472 	DEVMETHOD(device_probe,			bcma_probe),
473 	DEVMETHOD(device_attach,		bcma_attach),
474 	DEVMETHOD(device_detach,		bcma_detach),
475 
476 	/* Bus interface */
477 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
478 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
479 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
480 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
481 
482 	/* BHND interface */
483 	DEVMETHOD(bhnd_bus_find_hostb_device,	bcma_find_hostb_device),
484 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
485 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
486 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
487 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
488 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
489 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
490 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
491 
492 	DEVMETHOD_END
493 };
494 
495 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
496 MODULE_VERSION(bcma, 1);
497 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
498