2 * Copyright (c) 2015 Landon Fuller <landon@landonf.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/module.h>
38 #include <sys/systm.h>
40 #include <machine/bus.h>
42 #include <dev/bhnd/cores/pmu/bhnd_pmu.h>
46 #include "bcma_eromreg.h"
47 #include "bcma_eromvar.h"
51 /* RID used when allocating EROM table */
52 #define BCMA_EROM_RID 0
54 static bhnd_erom_class_t *
55 bcma_get_erom_class(driver_t *driver)
57 return (&bcma_erom_parser);
61 bcma_probe(device_t dev)
63 device_set_desc(dev, "BCMA BHND bus");
64 return (BUS_PROBE_DEFAULT);
68 * Default bcma(4) bus driver implementation of DEVICE_ATTACH().
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.
75 bcma_attach(device_t dev)
79 /* Enumerate children */
80 if ((error = bcma_add_children(dev))) {
81 device_delete_children(dev);
89 bcma_detach(device_t dev)
91 return (bhnd_generic_detach(dev));
95 bcma_add_child(device_t dev, u_int order, const char *name, int unit)
97 struct bcma_devinfo *dinfo;
100 child = device_add_child_ordered(dev, order, name, unit);
104 if ((dinfo = bcma_alloc_dinfo(dev)) == NULL) {
105 device_delete_child(dev, child);
109 device_set_ivars(child, dinfo);
115 bcma_child_deleted(device_t dev, device_t child)
117 struct bhnd_softc *sc;
118 struct bcma_devinfo *dinfo;
120 sc = device_get_softc(dev);
122 /* Call required bhnd(4) implementation */
123 bhnd_generic_child_deleted(dev, child);
125 /* Free bcma device info */
126 if ((dinfo = device_get_ivars(child)) != NULL)
127 bcma_free_dinfo(dev, dinfo);
129 device_set_ivars(child, NULL);
133 bcma_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
135 const struct bcma_devinfo *dinfo;
136 const struct bhnd_core_info *ci;
138 dinfo = device_get_ivars(child);
139 ci = &dinfo->corecfg->core_info;
142 case BHND_IVAR_VENDOR:
143 *result = ci->vendor;
145 case BHND_IVAR_DEVICE:
146 *result = ci->device;
148 case BHND_IVAR_HWREV:
151 case BHND_IVAR_DEVICE_CLASS:
152 *result = bhnd_core_class(ci);
154 case BHND_IVAR_VENDOR_NAME:
155 *result = (uintptr_t) bhnd_vendor_name(ci->vendor);
157 case BHND_IVAR_DEVICE_NAME:
158 *result = (uintptr_t) bhnd_core_name(ci);
160 case BHND_IVAR_CORE_INDEX:
161 *result = ci->core_idx;
163 case BHND_IVAR_CORE_UNIT:
166 case BHND_IVAR_PMU_INFO:
167 *result = (uintptr_t) dinfo->pmu_info;
175 bcma_write_ivar(device_t dev, device_t child, int index, uintptr_t value)
177 struct bcma_devinfo *dinfo;
179 dinfo = device_get_ivars(child);
182 case BHND_IVAR_VENDOR:
183 case BHND_IVAR_DEVICE:
184 case BHND_IVAR_HWREV:
185 case BHND_IVAR_DEVICE_CLASS:
186 case BHND_IVAR_VENDOR_NAME:
187 case BHND_IVAR_DEVICE_NAME:
188 case BHND_IVAR_CORE_INDEX:
189 case BHND_IVAR_CORE_UNIT:
191 case BHND_IVAR_PMU_INFO:
192 dinfo->pmu_info = (struct bhnd_core_pmu_info *) value;
199 static struct resource_list *
200 bcma_get_resource_list(device_t dev, device_t child)
202 struct bcma_devinfo *dinfo = device_get_ivars(child);
203 return (&dinfo->resources);
207 bcma_read_iost(device_t dev, device_t child, uint16_t *iost)
212 if ((error = bhnd_read_config(child, BCMA_DMP_IOSTATUS, &value, 4)))
215 /* Return only the bottom 16 bits */
216 *iost = (value & BCMA_DMP_IOST_MASK);
221 bcma_read_ioctl(device_t dev, device_t child, uint16_t *ioctl)
226 if ((error = bhnd_read_config(child, BCMA_DMP_IOCTRL, &value, 4)))
229 /* Return only the bottom 16 bits */
230 *ioctl = (value & BCMA_DMP_IOCTRL_MASK);
235 bcma_write_ioctl(device_t dev, device_t child, uint16_t value, uint16_t mask)
237 struct bcma_devinfo *dinfo;
238 struct bhnd_resource *r;
241 if (device_get_parent(child) != dev)
244 dinfo = device_get_ivars(child);
245 if ((r = dinfo->res_agent) == NULL)
248 /* Write new value */
249 ioctl = bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
250 ioctl &= ~(BCMA_DMP_IOCTRL_MASK & mask);
251 ioctl |= (value & mask);
253 bhnd_bus_write_4(r, BCMA_DMP_IOCTRL, ioctl);
255 /* Perform read-back and wait for completion */
256 bhnd_bus_read_4(r, BCMA_DMP_IOCTRL);
263 bcma_is_hw_suspended(device_t dev, device_t child)
269 /* Is core held in RESET? */
270 error = bhnd_read_config(child, BCMA_DMP_RESETCTRL, &rst, 4);
272 device_printf(child, "error reading HW reset state: %d\n",
277 if (rst & BMCA_DMP_RC_RESET)
280 /* Is core clocked? */
281 error = bhnd_read_ioctl(child, &ioctl);
283 device_printf(child, "error reading HW ioctl register: %d\n",
288 if (!(ioctl & BHND_IOCTL_CLK_EN))
295 bcma_reset_hw(device_t dev, device_t child, uint16_t ioctl)
297 struct bcma_devinfo *dinfo;
298 struct bhnd_core_pmu_info *pm;
299 struct bhnd_resource *r;
302 if (device_get_parent(child) != dev)
305 dinfo = device_get_ivars(child);
306 pm = dinfo->pmu_info;
308 /* We require exclusive control over BHND_IOCTL_CLK_EN and
309 * BHND_IOCTL_CLK_FORCE. */
310 if (ioctl & (BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE))
313 /* Can't suspend the core without access to the agent registers */
314 if ((r = dinfo->res_agent) == NULL)
317 /* Place core into known RESET state */
318 if ((error = BHND_BUS_SUSPEND_HW(dev, child)))
322 * Leaving the core in reset:
323 * - Set the caller's IOCTL flags
325 * - Force clock distribution to ensure propagation throughout the
328 error = bhnd_write_ioctl(child,
329 ioctl | BHND_IOCTL_CLK_EN | BHND_IOCTL_CLK_FORCE, UINT16_MAX);
333 /* Bring the core out of reset */
334 if ((error = bcma_dmp_write_reset(child, dinfo, 0x0)))
337 /* Disable forced clock gating (leaving clock enabled) */
338 error = bhnd_write_ioctl(child, 0x0, BHND_IOCTL_CLK_FORCE);
346 bcma_suspend_hw(device_t dev, device_t child)
348 struct bcma_devinfo *dinfo;
349 struct bhnd_core_pmu_info *pm;
350 struct bhnd_resource *r;
354 if (device_get_parent(child) != dev)
357 dinfo = device_get_ivars(child);
358 pm = dinfo->pmu_info;
360 /* Can't suspend the core without access to the agent registers */
361 if ((r = dinfo->res_agent) == NULL)
364 /* Wait for any pending reset operations to clear */
365 if ((error = bcma_dmp_wait_reset(child, dinfo)))
368 /* Already in reset? */
369 rst = bhnd_bus_read_4(r, BCMA_DMP_RESETCTRL);
370 if (rst & BMCA_DMP_RC_RESET)
373 /* Put core into reset */
374 if ((error = bcma_dmp_write_reset(child, dinfo, BMCA_DMP_RC_RESET)))
377 /* Clear core flags */
378 if ((error = bhnd_write_ioctl(child, 0x0, UINT16_MAX)))
381 /* Inform PMU that all outstanding request state should be discarded */
383 if ((error = BHND_PMU_CORE_RELEASE(pm->pm_pmu, pm)))
391 bcma_read_config(device_t dev, device_t child, bus_size_t offset, void *value,
394 struct bcma_devinfo *dinfo;
395 struct bhnd_resource *r;
397 /* Must be a directly attached child core */
398 if (device_get_parent(child) != dev)
401 /* Fetch the agent registers */
402 dinfo = device_get_ivars(child);
403 if ((r = dinfo->res_agent) == NULL)
407 if (offset > rman_get_size(r->res))
410 if (rman_get_size(r->res) - offset < width)
415 *((uint8_t *)value) = bhnd_bus_read_1(r, offset);
418 *((uint16_t *)value) = bhnd_bus_read_2(r, offset);
421 *((uint32_t *)value) = bhnd_bus_read_4(r, offset);
429 bcma_write_config(device_t dev, device_t child, bus_size_t offset,
430 const void *value, u_int width)
432 struct bcma_devinfo *dinfo;
433 struct bhnd_resource *r;
435 /* Must be a directly attached child core */
436 if (device_get_parent(child) != dev)
439 /* Fetch the agent registers */
440 dinfo = device_get_ivars(child);
441 if ((r = dinfo->res_agent) == NULL)
445 if (offset > rman_get_size(r->res))
448 if (rman_get_size(r->res) - offset < width)
453 bhnd_bus_write_1(r, offset, *(const uint8_t *)value);
456 bhnd_bus_write_2(r, offset, *(const uint16_t *)value);
459 bhnd_bus_write_4(r, offset, *(const uint32_t *)value);
467 bcma_get_port_count(device_t dev, device_t child, bhnd_port_type type)
469 struct bcma_devinfo *dinfo;
471 /* delegate non-bus-attached devices to our parent */
472 if (device_get_parent(child) != dev)
473 return (BHND_BUS_GET_PORT_COUNT(device_get_parent(dev), child,
476 dinfo = device_get_ivars(child);
478 case BHND_PORT_DEVICE:
479 return (dinfo->corecfg->num_dev_ports);
480 case BHND_PORT_BRIDGE:
481 return (dinfo->corecfg->num_bridge_ports);
482 case BHND_PORT_AGENT:
483 return (dinfo->corecfg->num_wrapper_ports);
485 device_printf(dev, "%s: unknown type (%d)\n",
493 bcma_get_region_count(device_t dev, device_t child, bhnd_port_type type,
496 struct bcma_devinfo *dinfo;
497 struct bcma_sport_list *ports;
498 struct bcma_sport *port;
500 /* delegate non-bus-attached devices to our parent */
501 if (device_get_parent(child) != dev)
502 return (BHND_BUS_GET_REGION_COUNT(device_get_parent(dev), child,
505 dinfo = device_get_ivars(child);
506 ports = bcma_corecfg_get_port_list(dinfo->corecfg, type);
508 STAILQ_FOREACH(port, ports, sp_link) {
509 if (port->sp_num == port_num)
510 return (port->sp_num_maps);
518 bcma_get_port_rid(device_t dev, device_t child, bhnd_port_type port_type,
519 u_int port_num, u_int region_num)
521 struct bcma_devinfo *dinfo;
522 struct bcma_map *map;
523 struct bcma_sport_list *ports;
524 struct bcma_sport *port;
526 dinfo = device_get_ivars(child);
527 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
529 STAILQ_FOREACH(port, ports, sp_link) {
530 if (port->sp_num != port_num)
533 STAILQ_FOREACH(map, &port->sp_maps, m_link)
534 if (map->m_region_num == region_num)
542 bcma_decode_port_rid(device_t dev, device_t child, int type, int rid,
543 bhnd_port_type *port_type, u_int *port_num, u_int *region_num)
545 struct bcma_devinfo *dinfo;
546 struct bcma_map *map;
547 struct bcma_sport_list *ports;
548 struct bcma_sport *port;
550 dinfo = device_get_ivars(child);
552 /* Ports are always memory mapped */
553 if (type != SYS_RES_MEMORY)
556 /* Starting with the most likely device list, search all three port
558 bhnd_port_type types[] = {
564 for (int i = 0; i < nitems(types); i++) {
565 ports = bcma_corecfg_get_port_list(dinfo->corecfg, types[i]);
567 STAILQ_FOREACH(port, ports, sp_link) {
568 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
569 if (map->m_rid != rid)
572 *port_type = port->sp_type;
573 *port_num = port->sp_num;
574 *region_num = map->m_region_num;
584 bcma_get_region_addr(device_t dev, device_t child, bhnd_port_type port_type,
585 u_int port_num, u_int region_num, bhnd_addr_t *addr, bhnd_size_t *size)
587 struct bcma_devinfo *dinfo;
588 struct bcma_map *map;
589 struct bcma_sport_list *ports;
590 struct bcma_sport *port;
592 dinfo = device_get_ivars(child);
593 ports = bcma_corecfg_get_port_list(dinfo->corecfg, port_type);
595 /* Search the port list */
596 STAILQ_FOREACH(port, ports, sp_link) {
597 if (port->sp_num != port_num)
600 STAILQ_FOREACH(map, &port->sp_maps, m_link) {
601 if (map->m_region_num != region_num)
615 * Default bcma(4) bus driver implementation of BHND_BUS_GET_INTR_COUNT().
617 * This implementation consults @p child's agent register block,
618 * returning the number of interrupt output lines routed to @p child.
621 bcma_get_intr_count(device_t dev, device_t child)
623 struct bcma_devinfo *dinfo;
624 uint32_t dmpcfg, oobw;
626 dinfo = device_get_ivars(child);
628 /* Agent block must be mapped */
629 if (dinfo->res_agent == NULL)
632 /* Agent must support OOB */
633 dmpcfg = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_CONFIG);
634 if (!BCMA_DMP_GET_FLAG(dmpcfg, BCMA_DMP_CFG_OOB))
637 /* Return OOB width as interrupt count */
638 oobw = bhnd_bus_read_4(dinfo->res_agent,
639 BCMA_DMP_OOB_OUTWIDTH(BCMA_OOB_BANK_INTR));
640 if (oobw > BCMA_OOB_NUM_SEL) {
641 device_printf(dev, "ignoring invalid OOBOUTWIDTH for core %u: "
642 "%#x\n", BCMA_DINFO_COREIDX(dinfo), oobw);
650 * Default bcma(4) bus driver implementation of BHND_BUS_GET_CORE_IVEC().
652 * This implementation consults @p child's agent register block,
653 * returning the interrupt output line routed to @p child, at OOB selector
657 bcma_get_core_ivec(device_t dev, device_t child, u_int intr, uint32_t *ivec)
659 struct bcma_devinfo *dinfo;
662 dinfo = device_get_ivars(child);
664 /* Interrupt ID must be valid. */
665 if (intr >= bcma_get_intr_count(dev, child))
668 /* Fetch OOBSEL busline value */
669 KASSERT(dinfo->res_agent != NULL, ("missing agent registers"));
670 oobsel = bhnd_bus_read_4(dinfo->res_agent, BCMA_DMP_OOBSELOUT(
671 BCMA_OOB_BANK_INTR, intr));
672 *ivec = (oobsel >> BCMA_DMP_OOBSEL_SHIFT(intr)) &
673 BCMA_DMP_OOBSEL_BUSLINE_MASK;
679 * Scan the device enumeration ROM table, adding all valid discovered cores to
682 * @param bus The bcma bus.
685 bcma_add_children(device_t bus)
688 struct bcma_erom *bcma_erom;
689 const struct bhnd_chipid *cid;
690 struct bcma_corecfg *corecfg;
691 struct bcma_devinfo *dinfo;
695 cid = BHND_BUS_GET_CHIPID(bus, bus);
698 /* Allocate our EROM parser */
699 erom = bhnd_erom_alloc(&bcma_erom_parser, cid, bus, BCMA_EROM_RID);
704 bcma_erom = (struct bcma_erom *)erom;
705 while ((error = bcma_erom_next_corecfg(bcma_erom, &corecfg)) == 0) {
708 /* Add the child device */
709 child = BUS_ADD_CHILD(bus, 0, NULL, -1);
715 /* Initialize device ivars */
716 dinfo = device_get_ivars(child);
717 if ((error = bcma_init_dinfo(bus, dinfo, corecfg)))
720 /* The dinfo instance now owns the corecfg value */
723 /* Allocate device's agent registers, if any */
724 if ((error = bcma_dinfo_alloc_agent(bus, child, dinfo)))
727 /* Assign interrupts */
728 nintr = bhnd_get_intr_count(child);
729 for (int rid = 0; rid < nintr; rid++) {
730 error = BHND_BUS_ASSIGN_INTR(bus, child, rid);
732 device_printf(bus, "failed to assign interrupt "
733 "%d to core %u: %d\n", rid,
734 BCMA_DINFO_COREIDX(dinfo), error);
738 /* If pins are floating or the hardware is otherwise
739 * unpopulated, the device shouldn't be used. */
740 if (bhnd_is_hw_disabled(child))
741 device_disable(child);
743 /* Issue bus callback for fully initialized child. */
744 BHND_BUS_CHILD_ADDED(bus, child);
747 /* EOF while parsing cores is expected */
752 bhnd_erom_free(erom);
755 bcma_free_corecfg(corecfg);
758 device_delete_children(bus);
764 static device_method_t bcma_methods[] = {
765 /* Device interface */
766 DEVMETHOD(device_probe, bcma_probe),
767 DEVMETHOD(device_attach, bcma_attach),
768 DEVMETHOD(device_detach, bcma_detach),
771 DEVMETHOD(bus_add_child, bcma_add_child),
772 DEVMETHOD(bus_child_deleted, bcma_child_deleted),
773 DEVMETHOD(bus_read_ivar, bcma_read_ivar),
774 DEVMETHOD(bus_write_ivar, bcma_write_ivar),
775 DEVMETHOD(bus_get_resource_list, bcma_get_resource_list),
778 DEVMETHOD(bhnd_bus_get_erom_class, bcma_get_erom_class),
779 DEVMETHOD(bhnd_bus_read_ioctl, bcma_read_ioctl),
780 DEVMETHOD(bhnd_bus_write_ioctl, bcma_write_ioctl),
781 DEVMETHOD(bhnd_bus_read_iost, bcma_read_iost),
782 DEVMETHOD(bhnd_bus_is_hw_suspended, bcma_is_hw_suspended),
783 DEVMETHOD(bhnd_bus_reset_hw, bcma_reset_hw),
784 DEVMETHOD(bhnd_bus_suspend_hw, bcma_suspend_hw),
785 DEVMETHOD(bhnd_bus_read_config, bcma_read_config),
786 DEVMETHOD(bhnd_bus_write_config, bcma_write_config),
787 DEVMETHOD(bhnd_bus_get_port_count, bcma_get_port_count),
788 DEVMETHOD(bhnd_bus_get_region_count, bcma_get_region_count),
789 DEVMETHOD(bhnd_bus_get_port_rid, bcma_get_port_rid),
790 DEVMETHOD(bhnd_bus_decode_port_rid, bcma_decode_port_rid),
791 DEVMETHOD(bhnd_bus_get_region_addr, bcma_get_region_addr),
792 DEVMETHOD(bhnd_bus_get_intr_count, bcma_get_intr_count),
793 DEVMETHOD(bhnd_bus_get_core_ivec, bcma_get_core_ivec),
798 DEFINE_CLASS_1(bhnd, bcma_driver, bcma_methods, sizeof(struct bcma_softc), bhnd_driver);
799 MODULE_VERSION(bcma, 1);
800 MODULE_DEPEND(bcma, bhnd, 1, 1, 1);