2 * Copyright (c) 2015-2016 Landon Fuller <landon@landonf.org>
3 * Copyright (c) 2017 The FreeBSD Foundation
6 * Portions of this software were developed by Landon Fuller
7 * under sponsorship from the FreeBSD Foundation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer,
14 * without modification.
15 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
16 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
17 * redistribution must be conditioned upon including a substantially
18 * similar Disclaimer requirement for further binary redistribution.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
24 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
25 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
26 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGES.
34 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD$");
37 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/systm.h>
44 #include <machine/bus.h>
46 #include <dev/bhnd/cores/pmu/bhnd_pmu.h>
50 #include "bcma_eromreg.h"
51 #include "bcma_eromvar.h"
55 /* RID used when allocating EROM table */
56 #define BCMA_EROM_RID 0
58 static bhnd_erom_class_t *
59 bcma_get_erom_class(driver_t *driver)
61 return (&bcma_erom_parser);
65 bcma_probe(device_t dev)
67 device_set_desc(dev, "BCMA BHND bus");
68 return (BUS_PROBE_DEFAULT);
72 * Default bcma(4) bus driver implementation of DEVICE_ATTACH().
74 * This implementation initializes internal bcma(4) state and performs
75 * bus enumeration, and must be called by subclassing drivers in
76 * DEVICE_ATTACH() before any other bus methods.
79 bcma_attach(device_t dev)
83 /* Enumerate children */
84 if ((error = bcma_add_children(dev))) {
85 device_delete_children(dev);
93 bcma_detach(device_t dev)
95 return (bhnd_generic_detach(dev));
99 bcma_add_child(device_t dev, u_int order, const char *name, int unit)
101 struct bcma_devinfo *dinfo;
104 child = device_add_child_ordered(dev, order, name, unit);
108 if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
109 device_delete_child(dev, child);
113 device_set_ivars(child, dinfo);
119 bcma_child_deleted(device_t dev, device_t child)
121 struct bhnd_softc *sc;
122 struct bcma_devinfo *dinfo;
124 sc = device_get_softc(dev);
126 /* Call required bhnd(4) implementation */
127 bhnd_generic_child_deleted(dev, child);
129 /* Free bcma device info */
130 if ((dinfo = device_get_ivars(child)) != NULL)
131 bcma_free_dinfo(dev, child, dinfo);
133 device_set_ivars(child, NULL);
137 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
139 const struct bcma_devinfo *dinfo;
140 const struct bhnd_core_info *ci;
142 dinfo = device_get_ivars(child);
143 ci = &dinfo->corecfg->core_info;
146 case BHND_IVAR_VENDOR:
147 *result = ci->vendor;
149 case BHND_IVAR_DEVICE:
150 *result = ci->device;
152 case BHND_IVAR_HWREV:
155 case BHND_IVAR_DEVICE_CLASS:
156 *result = bhnd_core_class(ci);
158 case BHND_IVAR_VENDOR_NAME:
159 *result = (uintptr_t) bhnd_vendor_name(ci->vendor);
161 case BHND_IVAR_DEVICE_NAME:
162 *result = (uintptr_t) bhnd_core_name(ci);
164 case BHND_IVAR_CORE_INDEX:
165 *result = ci->core_idx;
167 case BHND_IVAR_CORE_UNIT:
170 case BHND_IVAR_PMU_INFO:
171 *result = (uintptr_t) dinfo->pmu_info;
179 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
181 struct bcma_devinfo *dinfo;
183 dinfo = device_get_ivars(child);
186 case BHND_IVAR_VENDOR:
187 case BHND_IVAR_DEVICE:
188 case BHND_IVAR_HWREV:
189 case BHND_IVAR_DEVICE_CLASS:
190 case BHND_IVAR_VENDOR_NAME:
191 case BHND_IVAR_DEVICE_NAME:
192 case BHND_IVAR_CORE_INDEX:
193 case BHND_IVAR_CORE_UNIT:
195 case BHND_IVAR_PMU_INFO:
196 dinfo->pmu_info = (void *)value;
203 static struct resource_list *
204 bcma_get_resource_list(device_t dev, device_t child)
206 struct bcma_devinfo *dinfo = device_get_ivars(child);
207 return (&dinfo->resources);
211 bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
216 if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
219 /* Return only the bottom 16 bits */
220 *iost = (value & BCMA_DMP_IOST_MASK);
225 bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
230 if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
233 /* Return only the bottom 16 bits */
234 *ioctl = (value & BCMA_DMP_IOCTRL_MASK);
239 bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
241 struct bcma_devinfo *dinfo;
242 struct bhnd_resource *r;
245 if (device_get_parent(child) != dev)
248 dinfo = device_get_ivars(child);
249 if ((r = dinfo->res_agent) == NULL)
252 /* Write new value */
253 ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
254 ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
255 ioctl |= (value & mask);
257 bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
259 /* Perform read-back and wait for completion */
260 bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
267 bcma_is_hw_suspended(device_t dev, device_t child)
273 /* Is core held in RESET? */
274 error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
276 device_printf(child, "error reading HW reset state: %d\n",
281 if (rst & BCMA_DMP_RC_RESET)
284 /* Is core clocked? */
285 error = bhnd_read_ioctl(child, &ioctl);
287 device_printf(child, "error reading HW ioctl register: %d\n",
292 if (!(ioctl & BHND_IOCTL_CLK_EN))
299 bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl,
300 uint16_t reset_ioctl)
302 struct bcma_devinfo *dinfo;
303 struct bhnd_resource *r;
307 if (device_get_parent(child) != dev)
310 dinfo = device_get_ivars(child);
312 /* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
313 clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
314 if (ioctl & clkflags)
317 /* Can't suspend the core without access to the agent registers */
318 if ((r = dinfo->res_agent) == NULL)
321 /* Place core into known RESET state */
322 if ((error = bhnd_suspend_hw(child, reset_ioctl)))
326 * Leaving the core in reset:
327 * - Set the caller's IOCTL flags
329 * - Force clock distribution to ensure propagation throughout the
332 if ((error = bhnd_write_ioctl(child, ioctl | clkflags, UINT16_MAX)))
335 /* Bring the core out of reset */
336 if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
339 /* Disable forced clock gating (leaving clock enabled) */
340 error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
348 bcma_suspend_hw(device_t dev, device_t child, uint16_t ioctl)
350 struct bcma_devinfo *dinfo;
351 struct bhnd_resource *r;
355 if (device_get_parent(child) != dev)
358 dinfo = device_get_ivars(child);
360 /* We require exclusive control over BHND_IOCTL_CLK_(EN|FORCE) */
361 clkflags = BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE;
362 if (ioctl & clkflags)
365 /* Can't suspend the core without access to the agent registers */
366 if ((r = dinfo->res_agent) == NULL)
369 /* Wait for any pending reset operations to clear */
370 if ((error = bcma_dmp_wait_reset(child, dinfo)))
373 /* Put core into reset (if not already in reset) */
374 if ((error = bcma_dmp_write_reset(child, dinfo, BCMA_DMP_RC_RESET)))
377 /* Write core flags (and clear CLK_EN/CLK_FORCE) */
378 if ((error = bhnd_write_ioctl(child, ioctl, ~clkflags)))
385 bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
388 struct bcma_devinfo *dinfo;
389 struct bhnd_resource *r;
391 /* Must be a directly attached child core */
392 if (device_get_parent(child) != dev)
395 /* Fetch the agent registers */
396 dinfo = device_get_ivars(child);
397 if ((r = dinfo->res_agent) == NULL)
401 if (offset > rman_get_size(r->res))
404 if (rman_get_size(r->res) - offset < width)
409 *((uint8_t *)value) = bhnd_bus_read_1(r, offset);
412 *((uint16_t *)value) = bhnd_bus_read_2(r, offset);
415 *((uint32_t *)value) = bhnd_bus_read_4(r, offset);
423 bcma_write_config(device_t dev, device_t child, bus_size_t offset,
424 const void *value, u_int width)
426 struct bcma_devinfo *dinfo;
427 struct bhnd_resource *r;
429 /* Must be a directly attached child core */
430 if (device_get_parent(child) != dev)
433 /* Fetch the agent registers */
434 dinfo = device_get_ivars(child);
435 if ((r = dinfo->res_agent) == NULL)
439 if (offset > rman_get_size(r->res))
442 if (rman_get_size(r->res) - offset < width)
447 bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
450 bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
453 bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
461 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
463 struct bcma_devinfo *dinfo;
465 /* delegate non-bus-attached devices to our parent */
466 if (device_get_parent(child) != dev)
467 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
470 dinfo = device_get_ivars(child);
472 case BHND_PORT_DEVICE:
473 return (dinfo->corecfg->num_dev_ports);
474 case BHND_PORT_BRIDGE:
475 return (dinfo->corecfg->num_bridge_ports);
476 case BHND_PORT_AGENT:
477 return (dinfo->corecfg->num_wrapper_ports);
479 device_printf(dev, "%s: unknown type (%d)\n",
487 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
490 struct bcma_devinfo *dinfo;
491 struct bcma_sport_list *ports;
492 struct bcma_sport *port;
494 /* delegate non-bus-attached devices to our parent */
495 if (device_get_parent(child) != dev)
496 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
499 dinfo = device_get_ivars(child);
500 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
502 STAILQ_FOREACH(port, ports, sp_link) {
503 if (port->sp_num == port_num)
504 return (port->sp_num_maps);
512 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
513 u_int port_num, u_int region_num)
515 struct bcma_devinfo *dinfo;
516 struct bcma_map *map;
517 struct bcma_sport_list *ports;
518 struct bcma_sport *port;
520 dinfo = device_get_ivars(child);
521 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
523 STAILQ_FOREACH(port, ports, sp_link) {
524 if (port->sp_num != port_num)
527 STAILQ_FOREACH(map, &port->sp_maps, m_link)
528 if (map->m_region_num == region_num)
536 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
537 bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
539 struct bcma_devinfo *dinfo;
540 struct bcma_map *map;
541 struct bcma_sport_list *ports;
542 struct bcma_sport *port;
544 dinfo = device_get_ivars(child);
546 /* Ports are always memory mapped */
547 if (type != SYS_RES_MEMORY)
550 /* Starting with the most likely device list, search all three port
552 bhnd_port_type types[] = {
558 for (int i = 0; i < nitems(types); i++) {
559 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
561 STAILQ_FOREACH(port, ports, sp_link) {
562 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
563 if (map->m_rid != rid)
566 *port_type = port->sp_type;
567 *port_num = port->sp_num;
568 *region_num = map->m_region_num;
578 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
579 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
581 struct bcma_devinfo *dinfo;
582 struct bcma_map *map;
583 struct bcma_sport_list *ports;
584 struct bcma_sport *port;
586 dinfo = device_get_ivars(child);
587 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
589 /* Search the port list */
590 STAILQ_FOREACH(port, ports, sp_link) {
591 if (port->sp_num != port_num)
594 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
595 if (map->m_region_num != region_num)
609 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
612 bcma_get_intr_count(device_t dev, device_t child)
614 struct bcma_devinfo *dinfo;
616 /* delegate non-bus-attached devices to our parent */
617 if (device_get_parent(child) != dev)
618 return (BHND_BUS_GET_INTR_COUNT(device_get_parent(dev), child));
620 dinfo = device_get_ivars(child);
621 return (dinfo->num_intrs);
625 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_IVEC().
628 bcma_get_intr_ivec(device_t dev, device_t child, u_int intr, u_int *ivec)
630 struct bcma_devinfo *dinfo;
631 struct bcma_intr *desc;
633 /* delegate non-bus-attached devices to our parent */
634 if (device_get_parent(child) != dev) {
635 return (BHND_BUS_GET_INTR_IVEC(device_get_parent(dev), child,
639 dinfo = device_get_ivars(child);
641 STAILQ_FOREACH(desc, &dinfo->intrs, i_link) {
642 if (desc->i_sel == intr) {
643 *ivec = desc->i_busline;
653 * Scan the device enumeration ROM table, adding all valid discovered cores to
656 * @param bus The bcma bus.
659 bcma_add_children(device_t bus)
662 struct bcma_erom *bcma_erom;
663 struct bhnd_erom_io *eio;
664 const struct bhnd_chipid *cid;
665 struct bcma_corecfg *corecfg;
666 struct bcma_devinfo *dinfo;
670 cid = BHND_BUS_GET_CHIPID(bus, bus);
673 /* Allocate our EROM parser */
674 eio = bhnd_erom_iores_new(bus, BCMA_EROM_RID);
675 erom = bhnd_erom_alloc(&bcma_erom_parser, cid, eio);
677 bhnd_erom_io_fini(eio);
682 bcma_erom = (struct bcma_erom *)erom;
683 while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
684 /* Add the child device */
685 child = BUS_ADD_CHILD(bus, 0, NULL, -1);
691 /* Initialize device ivars */
692 dinfo = device_get_ivars(child);
693 if ((error = bcma_init_dinfo(bus, child, dinfo, corecfg)))
696 /* The dinfo instance now owns the corecfg value */
699 /* If pins are floating or the hardware is otherwise
700 * unpopulated, the device shouldn't be used. */
701 if (bhnd_is_hw_disabled(child))
702 device_disable(child);
704 /* Issue bus callback for fully initialized child. */
705 BHND_BUS_CHILD_ADDED(bus, child);
708 /* EOF while parsing cores is expected */
713 bhnd_erom_free(erom);
716 bcma_free_corecfg(corecfg);
719 device_delete_children(bus);
725 static device_method_t bcma_methods[] = {
726 /* Device interface */
727 DEVMETHOD(device_probe, bcma_probe),
728 DEVMETHOD(device_attach, bcma_attach),
729 DEVMETHOD(device_detach, bcma_detach),
732 DEVMETHOD(bus_add_child, bcma_add_child),
733 DEVMETHOD(bus_child_deleted, bcma_child_deleted),
734 DEVMETHOD(bus_read_ivar, bcma_read_ivar),
735 DEVMETHOD(bus_write_ivar, bcma_write_ivar),
736 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list),
739 DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class),
740 DEVMETHOD(bhnd_bus_read_ioctl, bcma_read_ioctl),
741 DEVMETHOD(bhnd_bus_write_ioctl, bcma_write_ioctl),
742 DEVMETHOD(bhnd_bus_read_iost, bcma_read_iost),
743 DEVMETHOD(bhnd_bus_is_hw_suspended, bcma_is_hw_suspended),
744 DEVMETHOD(bhnd_bus_reset_hw, bcma_reset_hw),
745 DEVMETHOD(bhnd_bus_suspend_hw, bcma_suspend_hw),
746 DEVMETHOD(bhnd_bus_read_config, bcma_read_config),
747 DEVMETHOD(bhnd_bus_write_config, bcma_write_config),
748 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count),
749 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count),
750 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid),
751 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid),
752 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr),
753 DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count),
754 DEVMETHOD(bhnd_bus_get_intr_ivec, bcma_get_intr_ivec),
759 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
760 MODULE_VERSION(bcma, 1);
761 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);