xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision d9f0ce31900a48d1a2bfc1c8c86f79d1e831451a)
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 = 0;
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 
198 static int
199 bcma_reset_core(device_t dev, device_t child, uint16_t flags)
200 {
201 	struct bcma_devinfo *dinfo;
202 
203 	if (device_get_parent(child) != dev)
204 		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
205 
206 	dinfo = device_get_ivars(child);
207 
208 	/* Can't reset the core without access to the agent registers */
209 	if (dinfo->res_agent == NULL)
210 		return (ENODEV);
211 
212 	// TODO - perform reset
213 
214 	return (ENXIO);
215 }
216 
217 static int
218 bcma_suspend_core(device_t dev, device_t child)
219 {
220 	struct bcma_devinfo *dinfo;
221 
222 	if (device_get_parent(child) != dev)
223 		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
224 
225 	dinfo = device_get_ivars(child);
226 
227 	/* Can't suspend the core without access to the agent registers */
228 	if (dinfo->res_agent == NULL)
229 		return (ENODEV);
230 
231 	// TODO - perform suspend
232 
233 	return (ENXIO);
234 }
235 
236 static u_int
237 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
238 {
239 	struct bcma_devinfo *dinfo;
240 
241 	/* delegate non-bus-attached devices to our parent */
242 	if (device_get_parent(child) != dev)
243 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
244 		    type));
245 
246 	dinfo = device_get_ivars(child);
247 	switch (type) {
248 	case BHND_PORT_DEVICE:
249 		return (dinfo->corecfg->num_dev_ports);
250 	case BHND_PORT_BRIDGE:
251 		return (dinfo->corecfg->num_bridge_ports);
252 	case BHND_PORT_AGENT:
253 		return (dinfo->corecfg->num_wrapper_ports);
254 	}
255 }
256 
257 static u_int
258 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
259     u_int port_num)
260 {
261 	struct bcma_devinfo	*dinfo;
262 	struct bcma_sport_list	*ports;
263 	struct bcma_sport	*port;
264 
265 	/* delegate non-bus-attached devices to our parent */
266 	if (device_get_parent(child) != dev)
267 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
268 		    type, port_num));
269 
270 	dinfo = device_get_ivars(child);
271 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
272 
273 	STAILQ_FOREACH(port, ports, sp_link) {
274 		if (port->sp_num == port_num)
275 			return (port->sp_num_maps);
276 	}
277 
278 	/* not found */
279 	return (0);
280 }
281 
282 static int
283 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
284     u_int port_num, u_int region_num)
285 {
286 	struct bcma_devinfo	*dinfo;
287 	struct bcma_map		*map;
288 	struct bcma_sport_list	*ports;
289 	struct bcma_sport	*port;
290 
291 	dinfo = device_get_ivars(child);
292 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
293 
294 	STAILQ_FOREACH(port, ports, sp_link) {
295 		if (port->sp_num != port_num)
296 			continue;
297 
298 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
299 			if (map->m_region_num == region_num)
300 				return map->m_rid;
301 	}
302 
303 	return -1;
304 }
305 
306 static int
307 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
308     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
309 {
310 	struct bcma_devinfo	*dinfo;
311 	struct bcma_map		*map;
312 	struct bcma_sport_list	*ports;
313 	struct bcma_sport	*port;
314 
315 	dinfo = device_get_ivars(child);
316 
317 	/* Ports are always memory mapped */
318 	if (type != SYS_RES_MEMORY)
319 		return (EINVAL);
320 
321 	/* Starting with the most likely device list, search all three port
322 	 * lists */
323 	bhnd_port_type types[] = {
324 	    BHND_PORT_DEVICE,
325 	    BHND_PORT_AGENT,
326 	    BHND_PORT_BRIDGE
327 	};
328 
329 	for (int i = 0; i < nitems(types); i++) {
330 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
331 
332 		STAILQ_FOREACH(port, ports, sp_link) {
333 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
334 				if (map->m_rid != rid)
335 					continue;
336 
337 				*port_type = port->sp_type;
338 				*port_num = port->sp_num;
339 				*region_num = map->m_region_num;
340 				return (0);
341 			}
342 		}
343 	}
344 
345 	return (ENOENT);
346 }
347 
348 static int
349 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
350     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
351 {
352 	struct bcma_devinfo	*dinfo;
353 	struct bcma_map		*map;
354 	struct bcma_sport_list	*ports;
355 	struct bcma_sport	*port;
356 
357 	dinfo = device_get_ivars(child);
358 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
359 
360 	/* Search the port list */
361 	STAILQ_FOREACH(port, ports, sp_link) {
362 		if (port->sp_num != port_num)
363 			continue;
364 
365 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
366 			if (map->m_region_num != region_num)
367 				continue;
368 
369 			/* Found! */
370 			*addr = map->m_base;
371 			*size = map->m_size;
372 			return (0);
373 		}
374 	}
375 
376 	return (ENOENT);
377 }
378 
379 /**
380  * Scan a device enumeration ROM table, adding all valid discovered cores to
381  * the bus.
382  *
383  * @param bus The bcma bus.
384  * @param erom_res An active resource mapping the EROM core.
385  * @param erom_offset Base offset of the EROM core's register mapping.
386  */
387 int
388 bcma_add_children(device_t bus, struct resource *erom_res, bus_size_t erom_offset)
389 {
390 	struct bcma_erom	 erom;
391 	struct bcma_corecfg	*corecfg;
392 	struct bcma_devinfo	*dinfo;
393 	device_t		 child;
394 	int			 error;
395 
396 	dinfo = NULL;
397 	corecfg = NULL;
398 
399 	/* Initialize our reader */
400 	error = bcma_erom_open(&erom, erom_res, erom_offset);
401 	if (error)
402 		return (error);
403 
404 	/* Add all cores. */
405 	while (!error) {
406 		/* Parse next core */
407 		error = bcma_erom_parse_corecfg(&erom, &corecfg);
408 		if (error && error == ENOENT) {
409 			return (0);
410 		} else if (error) {
411 			goto failed;
412 		}
413 
414 		/* Allocate per-device bus info */
415 		dinfo = bcma_alloc_dinfo(bus, corecfg);
416 		if (dinfo == NULL) {
417 			error = ENXIO;
418 			goto failed;
419 		}
420 
421 		/* The dinfo instance now owns the corecfg value */
422 		corecfg = NULL;
423 
424 		/* Add the child device */
425 		child = device_add_child(bus, NULL, -1);
426 		if (child == NULL) {
427 			error = ENXIO;
428 			goto failed;
429 		}
430 
431 		/* The child device now owns the dinfo pointer */
432 		device_set_ivars(child, dinfo);
433 		dinfo = NULL;
434 
435 		/* If pins are floating or the hardware is otherwise
436 		 * unpopulated, the device shouldn't be used. */
437 		if (bhnd_is_hw_disabled(child))
438 			device_disable(child);
439 	}
440 
441 	/* Hit EOF parsing cores? */
442 	if (error == ENOENT)
443 		return (0);
444 
445 failed:
446 	if (dinfo != NULL)
447 		bcma_free_dinfo(bus, dinfo);
448 
449 	if (corecfg != NULL)
450 		bcma_free_corecfg(corecfg);
451 
452 	return (error);
453 }
454 
455 
456 static device_method_t bcma_methods[] = {
457 	/* Device interface */
458 	DEVMETHOD(device_probe,			bcma_probe),
459 	DEVMETHOD(device_attach,		bcma_attach),
460 	DEVMETHOD(device_detach,		bcma_detach),
461 
462 	/* Bus interface */
463 	DEVMETHOD(bus_child_deleted,		bcma_child_deleted),
464 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
465 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
466 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
467 
468 	/* BHND interface */
469 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
470 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
471 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
472 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
473 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
474 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
475 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
476 
477 	DEVMETHOD_END
478 };
479 
480 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
481 MODULE_VERSION(bcma, 1);
482 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
483