xref: /freebsd/sys/dev/bhnd/bcma/bcma.c (revision 09a53ad8f1318c5daae6cfb19d97f4f6459f0013)
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_dmp.h"
45 
46 #include "bcma_eromreg.h"
47 #include "bcma_eromvar.h"
48 
49 #include <dev/bhnd/bhnd_core.h>
50 
51 /* RID used when allocating EROM table */
52 #define	BCMA_EROM_RID	0
53 
54 static bhnd_erom_class_t *
55 bcma_get_erom_class(driver_t *driver)
56 {
57 	return (&bcma_erom_parser);
58 }
59 
60 int
61 bcma_probe(device_t dev)
62 {
63 	device_set_desc(dev, "BCMA BHND bus");
64 	return (BUS_PROBE_DEFAULT);
65 }
66 
67 /**
68  * Default bcma(4) bus driver implementation of DEVICE_ATTACH().
69  *
70  * This implementation initializes internal bcma(4) state and performs
71  * bus enumeration, and must be called by subclassing drivers in
72  * DEVICE_ATTACH() before any other bus methods.
73  */
74 int
75 bcma_attach(device_t dev)
76 {
77 	int error;
78 
79 	/* Enumerate children */
80 	if ((error = bcma_add_children(dev))) {
81 		device_delete_children(dev);
82 		return (error);
83 	}
84 
85 	return (0);
86 }
87 
88 int
89 bcma_detach(device_t dev)
90 {
91 	return (bhnd_generic_detach(dev));
92 }
93 
94 static int
95 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
96 {
97 	const struct bcma_devinfo *dinfo;
98 	const struct bhnd_core_info *ci;
99 
100 	dinfo = device_get_ivars(child);
101 	ci = &dinfo->corecfg->core_info;
102 
103 	switch (index) {
104 	case BHND_IVAR_VENDOR:
105 		*result = ci->vendor;
106 		return (0);
107 	case BHND_IVAR_DEVICE:
108 		*result = ci->device;
109 		return (0);
110 	case BHND_IVAR_HWREV:
111 		*result = ci->hwrev;
112 		return (0);
113 	case BHND_IVAR_DEVICE_CLASS:
114 		*result = bhnd_core_class(ci);
115 		return (0);
116 	case BHND_IVAR_VENDOR_NAME:
117 		*result = (uintptr_t) bhnd_vendor_name(ci->vendor);
118 		return (0);
119 	case BHND_IVAR_DEVICE_NAME:
120 		*result = (uintptr_t) bhnd_core_name(ci);
121 		return (0);
122 	case BHND_IVAR_CORE_INDEX:
123 		*result = ci->core_idx;
124 		return (0);
125 	case BHND_IVAR_CORE_UNIT:
126 		*result = ci->unit;
127 		return (0);
128 	default:
129 		return (ENOENT);
130 	}
131 }
132 
133 static int
134 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
135 {
136 	switch (index) {
137 	case BHND_IVAR_VENDOR:
138 	case BHND_IVAR_DEVICE:
139 	case BHND_IVAR_HWREV:
140 	case BHND_IVAR_DEVICE_CLASS:
141 	case BHND_IVAR_VENDOR_NAME:
142 	case BHND_IVAR_DEVICE_NAME:
143 	case BHND_IVAR_CORE_INDEX:
144 	case BHND_IVAR_CORE_UNIT:
145 		return (EINVAL);
146 	default:
147 		return (ENOENT);
148 	}
149 }
150 
151 static struct resource_list *
152 bcma_get_resource_list(device_t dev, device_t child)
153 {
154 	struct bcma_devinfo *dinfo = device_get_ivars(child);
155 	return (&dinfo->resources);
156 }
157 
158 static int
159 bcma_reset_core(device_t dev, device_t child, uint16_t flags)
160 {
161 	struct bcma_devinfo *dinfo;
162 
163 	if (device_get_parent(child) != dev)
164 		BHND_BUS_RESET_CORE(device_get_parent(dev), child, flags);
165 
166 	dinfo = device_get_ivars(child);
167 
168 	/* Can't reset the core without access to the agent registers */
169 	if (dinfo->res_agent == NULL)
170 		return (ENODEV);
171 
172 	/* Start reset */
173 	bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, BHND_RESET_CF_ENABLE);
174 	bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
175 	DELAY(10);
176 
177 	/* Disable clock */
178 	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, flags);
179 	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
180 	DELAY(10);
181 
182 	/* Enable clocks & force clock gating */
183 	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN |
184 	    BHND_CF_FGC | flags);
185 	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
186 	DELAY(10);
187 
188 	/* Complete reset */
189 	bhnd_bus_write_4(dinfo->res_agent, BHND_RESET_CF, 0);
190 	bhnd_bus_read_4(dinfo->res_agent, BHND_RESET_CF);
191 	DELAY(10);
192 
193 	/* Release force clock gating */
194 	bhnd_bus_write_4(dinfo->res_agent, BHND_CF, BHND_CF_CLOCK_EN | flags);
195 	bhnd_bus_read_4(dinfo->res_agent, BHND_CF);
196 	DELAY(10);
197 
198 	return (0);
199 }
200 
201 static int
202 bcma_suspend_core(device_t dev, device_t child)
203 {
204 	struct bcma_devinfo *dinfo;
205 
206 	if (device_get_parent(child) != dev)
207 		BHND_BUS_SUSPEND_CORE(device_get_parent(dev), child);
208 
209 	dinfo = device_get_ivars(child);
210 
211 	/* Can't suspend the core without access to the agent registers */
212 	if (dinfo->res_agent == NULL)
213 		return (ENODEV);
214 
215 	// TODO - perform suspend
216 
217 	return (ENXIO);
218 }
219 
220 static uint32_t
221 bcma_read_config(device_t dev, device_t child, bus_size_t offset, u_int width)
222 {
223 	struct bcma_devinfo	*dinfo;
224 	struct bhnd_resource	*r;
225 
226 	/* Must be a directly attached child core */
227 	if (device_get_parent(child) != dev)
228 		return (UINT32_MAX);
229 
230 	/* Fetch the agent registers */
231 	dinfo = device_get_ivars(child);
232 	if ((r = dinfo->res_agent) == NULL)
233 		return (UINT32_MAX);
234 
235 	/* Verify bounds */
236 	if (offset > rman_get_size(r->res))
237 		return (UINT32_MAX);
238 
239 	if (rman_get_size(r->res) - offset < width)
240 		return (UINT32_MAX);
241 
242 	switch (width) {
243 	case 1:
244 		return (bhnd_bus_read_1(r, offset));
245 	case 2:
246 		return (bhnd_bus_read_2(r, offset));
247 	case 4:
248 		return (bhnd_bus_read_4(r, offset));
249 	default:
250 		return (UINT32_MAX);
251 	}
252 }
253 
254 static void
255 bcma_write_config(device_t dev, device_t child, bus_size_t offset, uint32_t val,
256     u_int width)
257 {
258 	struct bcma_devinfo	*dinfo;
259 	struct bhnd_resource	*r;
260 
261 	/* Must be a directly attached child core */
262 	if (device_get_parent(child) != dev)
263 		return;
264 
265 	/* Fetch the agent registers */
266 	dinfo = device_get_ivars(child);
267 	if ((r = dinfo->res_agent) == NULL)
268 		return;
269 
270 	/* Verify bounds */
271 	if (offset > rman_get_size(r->res))
272 		return;
273 
274 	if (rman_get_size(r->res) - offset < width)
275 		return;
276 
277 	switch (width) {
278 	case 1:
279 		bhnd_bus_write_1(r, offset, val);
280 		break;
281 	case 2:
282 		bhnd_bus_write_2(r, offset, val);
283 		break;
284 	case 4:
285 		bhnd_bus_write_4(r, offset, val);
286 		break;
287 	default:
288 		break;
289 	}
290 }
291 
292 static u_int
293 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
294 {
295 	struct bcma_devinfo *dinfo;
296 
297 	/* delegate non-bus-attached devices to our parent */
298 	if (device_get_parent(child) != dev)
299 		return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
300 		    type));
301 
302 	dinfo = device_get_ivars(child);
303 	switch (type) {
304 	case BHND_PORT_DEVICE:
305 		return (dinfo->corecfg->num_dev_ports);
306 	case BHND_PORT_BRIDGE:
307 		return (dinfo->corecfg->num_bridge_ports);
308 	case BHND_PORT_AGENT:
309 		return (dinfo->corecfg->num_wrapper_ports);
310 	default:
311 		device_printf(dev, "%s: unknown type (%d)\n",
312 		    __func__,
313 		    type);
314 		return (0);
315 	}
316 }
317 
318 static u_int
319 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
320     u_int port_num)
321 {
322 	struct bcma_devinfo	*dinfo;
323 	struct bcma_sport_list	*ports;
324 	struct bcma_sport	*port;
325 
326 	/* delegate non-bus-attached devices to our parent */
327 	if (device_get_parent(child) != dev)
328 		return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
329 		    type, port_num));
330 
331 	dinfo = device_get_ivars(child);
332 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
333 
334 	STAILQ_FOREACH(port, ports, sp_link) {
335 		if (port->sp_num == port_num)
336 			return (port->sp_num_maps);
337 	}
338 
339 	/* not found */
340 	return (0);
341 }
342 
343 static int
344 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
345     u_int port_num, u_int region_num)
346 {
347 	struct bcma_devinfo	*dinfo;
348 	struct bcma_map		*map;
349 	struct bcma_sport_list	*ports;
350 	struct bcma_sport	*port;
351 
352 	dinfo = device_get_ivars(child);
353 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
354 
355 	STAILQ_FOREACH(port, ports, sp_link) {
356 		if (port->sp_num != port_num)
357 			continue;
358 
359 		STAILQ_FOREACH(map, &port->sp_maps, m_link)
360 			if (map->m_region_num == region_num)
361 				return map->m_rid;
362 	}
363 
364 	return -1;
365 }
366 
367 static int
368 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
369     bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
370 {
371 	struct bcma_devinfo	*dinfo;
372 	struct bcma_map		*map;
373 	struct bcma_sport_list	*ports;
374 	struct bcma_sport	*port;
375 
376 	dinfo = device_get_ivars(child);
377 
378 	/* Ports are always memory mapped */
379 	if (type != SYS_RES_MEMORY)
380 		return (EINVAL);
381 
382 	/* Starting with the most likely device list, search all three port
383 	 * lists */
384 	bhnd_port_type types[] = {
385 	    BHND_PORT_DEVICE,
386 	    BHND_PORT_AGENT,
387 	    BHND_PORT_BRIDGE
388 	};
389 
390 	for (int i = 0; i < nitems(types); i++) {
391 		ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
392 
393 		STAILQ_FOREACH(port, ports, sp_link) {
394 			STAILQ_FOREACH(map, &port->sp_maps, m_link) {
395 				if (map->m_rid != rid)
396 					continue;
397 
398 				*port_type = port->sp_type;
399 				*port_num = port->sp_num;
400 				*region_num = map->m_region_num;
401 				return (0);
402 			}
403 		}
404 	}
405 
406 	return (ENOENT);
407 }
408 
409 static int
410 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
411     u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
412 {
413 	struct bcma_devinfo	*dinfo;
414 	struct bcma_map		*map;
415 	struct bcma_sport_list	*ports;
416 	struct bcma_sport	*port;
417 
418 	dinfo = device_get_ivars(child);
419 	ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
420 
421 	/* Search the port list */
422 	STAILQ_FOREACH(port, ports, sp_link) {
423 		if (port->sp_num != port_num)
424 			continue;
425 
426 		STAILQ_FOREACH(map, &port->sp_maps, m_link) {
427 			if (map->m_region_num != region_num)
428 				continue;
429 
430 			/* Found! */
431 			*addr = map->m_base;
432 			*size = map->m_size;
433 			return (0);
434 		}
435 	}
436 
437 	return (ENOENT);
438 }
439 
440 /**
441  * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
442  *
443  * This implementation consults @p child's agent register block,
444  * returning the number of interrupt output lines routed to @p child.
445  */
446 int
447 bcma_get_intr_count(device_t dev, device_t child)
448 {
449 	struct bcma_devinfo	*dinfo;
450 	uint32_t		 dmpcfg, oobw;
451 
452 	dinfo = device_get_ivars(child);
453 
454 	/* Agent block must be mapped */
455 	if (dinfo->res_agent == NULL)
456 		return (0);
457 
458 	/* Agent must support OOB */
459 	dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG);
460 	if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB))
461 		return (0);
462 
463 	/* Return OOB width as interrupt count */
464 	oobw = bhnd_bus_read_4(dinfo->res_agent,
465 	    BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR));
466 	if (oobw > BCMA_OOB_NUM_SEL) {
467 		device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: "
468 		    "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw);
469 		return (0);
470 	}
471 
472 	return (oobw);
473 }
474 
475 /**
476  * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC().
477  *
478  * This implementation consults @p child's agent register block,
479  * returning the interrupt output line routed to @p child, at OOB selector
480  * @p intr.
481  */
482 int
483 bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
484 {
485 	struct bcma_devinfo	*dinfo;
486 	uint32_t		 oobsel;
487 
488 	dinfo = device_get_ivars(child);
489 
490 	/* Interrupt ID must be valid. */
491 	if (intr >= bcma_get_intr_count(dev, child))
492 		return (ENXIO);
493 
494 	/* Fetch OOBSEL busline value */
495 	KASSERT(dinfo->res_agent != NULL, ("missing agent registers"));
496 	oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT(
497 	    BCMA_OOB_BANK_INTR, intr));
498 	*ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) &
499 	    BCMA_DMP_OOBSEL_BUSLINE_MASK;
500 
501 	return (0);
502 }
503 
504 static struct bhnd_devinfo *
505 bcma_alloc_bhnd_dinfo(device_t dev)
506 {
507 	struct bcma_devinfo *dinfo = bcma_alloc_dinfo(dev);
508 	return ((struct bhnd_devinfo *)dinfo);
509 }
510 
511 static void
512 bcma_free_bhnd_dinfo(device_t dev, struct bhnd_devinfo *dinfo)
513 {
514 	bcma_free_dinfo(dev, (struct bcma_devinfo *)dinfo);
515 }
516 
517 /**
518  * Scan the device enumeration ROM table, adding all valid discovered cores to
519  * the bus.
520  *
521  * @param bus The bcma bus.
522  */
523 int
524 bcma_add_children(device_t bus)
525 {
526 	bhnd_erom_t			*erom;
527 	struct bcma_erom		*bcma_erom;
528 	const struct bhnd_chipid	*cid;
529 	struct bcma_corecfg		*corecfg;
530 	struct bcma_devinfo		*dinfo;
531 	device_t			 child;
532 	int				 error;
533 
534 	cid = BHND_BUS_GET_CHIPID(bus, bus);
535 	corecfg = NULL;
536 
537 	/* Allocate our EROM parser */
538 	erom = bhnd_erom_alloc(&bcma_erom_parser, cid, bus, BCMA_EROM_RID);
539 	if (erom == NULL)
540 		return (ENODEV);
541 
542 	/* Add all cores. */
543 	bcma_erom = (struct bcma_erom *)erom;
544 	while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
545 		int nintr;
546 
547 		/* Add the child device */
548 		child = BUS_ADD_CHILD(bus, 0, NULL, -1);
549 		if (child == NULL) {
550 			error = ENXIO;
551 			goto cleanup;
552 		}
553 
554 		/* Initialize device ivars */
555 		dinfo = device_get_ivars(child);
556 		if ((error = bcma_init_dinfo(bus, dinfo, corecfg)))
557 			goto cleanup;
558 
559 		/* The dinfo instance now owns the corecfg value */
560 		corecfg = NULL;
561 
562 		/* Allocate device's agent registers, if any */
563 		if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo)))
564 			goto cleanup;
565 
566 		/* Assign interrupts */
567 		nintr = bhnd_get_intr_count(child);
568 		for (int rid = 0; rid < nintr; rid++) {
569 			error = BHND_BUS_ASSIGN_INTR(bus, child, rid);
570 			if (error) {
571 				device_printf(bus, "failed to assign interrupt "
572 				    "%d to core %u: %d\n", rid,
573 				    BCMA_DINFO_COREIDX(dinfo), error);
574 			}
575 		}
576 
577 		/* If pins are floating or the hardware is otherwise
578 		 * unpopulated, the device shouldn't be used. */
579 		if (bhnd_is_hw_disabled(child))
580 			device_disable(child);
581 
582 		/* Issue bus callback for fully initialized child. */
583 		BHND_BUS_CHILD_ADDED(bus, child);
584 	}
585 
586 	/* EOF while parsing cores is expected */
587 	if (error == ENOENT)
588 		error = 0;
589 
590 cleanup:
591 	bhnd_erom_free(erom);
592 
593 	if (corecfg != NULL)
594 		bcma_free_corecfg(corecfg);
595 
596 	if (error)
597 		device_delete_children(bus);
598 
599 	return (error);
600 }
601 
602 
603 static device_method_t bcma_methods[] = {
604 	/* Device interface */
605 	DEVMETHOD(device_probe,			bcma_probe),
606 	DEVMETHOD(device_attach,		bcma_attach),
607 	DEVMETHOD(device_detach,		bcma_detach),
608 
609 	/* Bus interface */
610 	DEVMETHOD(bus_read_ivar,		bcma_read_ivar),
611 	DEVMETHOD(bus_write_ivar,		bcma_write_ivar),
612 	DEVMETHOD(bus_get_resource_list,	bcma_get_resource_list),
613 
614 	/* BHND interface */
615 	DEVMETHOD(bhnd_bus_get_erom_class,	bcma_get_erom_class),
616 	DEVMETHOD(bhnd_bus_alloc_devinfo,	bcma_alloc_bhnd_dinfo),
617 	DEVMETHOD(bhnd_bus_free_devinfo,	bcma_free_bhnd_dinfo),
618 	DEVMETHOD(bhnd_bus_reset_core,		bcma_reset_core),
619 	DEVMETHOD(bhnd_bus_suspend_core,	bcma_suspend_core),
620 	DEVMETHOD(bhnd_bus_read_config,		bcma_read_config),
621 	DEVMETHOD(bhnd_bus_write_config,	bcma_write_config),
622 	DEVMETHOD(bhnd_bus_get_port_count,	bcma_get_port_count),
623 	DEVMETHOD(bhnd_bus_get_region_count,	bcma_get_region_count),
624 	DEVMETHOD(bhnd_bus_get_port_rid,	bcma_get_port_rid),
625 	DEVMETHOD(bhnd_bus_decode_port_rid,	bcma_decode_port_rid),
626 	DEVMETHOD(bhnd_bus_get_region_addr,	bcma_get_region_addr),
627 	DEVMETHOD(bhnd_bus_get_intr_count,	bcma_get_intr_count),
628 	DEVMETHOD(bhnd_bus_get_core_ivec,	bcma_get_core_ivec),
629 
630 	DEVMETHOD_END
631 };
632 
633 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
634 MODULE_VERSION(bcma, 1);
635 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);
636