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