/** * Return the backplane clock speed in Hz. * * @param sc driver instance state. */ uint32_t bhnd_pwrctl_getclk_speed(struct bhnd_pwrctl_softc *sc) { const struct bhnd_chipid *cid; struct chipc_caps *ccaps; bus_size_t creg; uint32_t n, m; uint32_t rate; PWRCTL_LOCK_ASSERT(sc, MA_OWNED); cid = bhnd_get_chipid(sc->chipc_dev); ccaps = BHND_CHIPC_GET_CAPS(sc->chipc_dev); n = bhnd_bus_read_4(sc->res, CHIPC_CLKC_N); /* Get M register offset */ creg = bhnd_pwrctl_si_clkreg_m(cid, ccaps->pll_type, &rate); if (creg == 0) /* fixed rate */ return (rate); /* calculate rate */ m = bhnd_bus_read_4(sc->res, creg); return (bhnd_pwrctl_si_clock_rate(cid, ccaps->pll_type, n, m)); }
/* return the ILP (slowclock) min or max frequency */ static uint32_t bhnd_pwrctl_slowclk_freq(struct bhnd_pwrctl_softc *sc, bool max_freq) { bhnd_clksrc slowclk; uint32_t div; uint32_t hz; slowclk = bhnd_pwrctl_slowclk_src(sc); /* Determine clock divisor */ if (PWRCTL_QUIRK(sc, PCICLK_CTL)) { if (slowclk == BHND_CLKSRC_PCI) div = 64; else div = 32; } else if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { div = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); div = CHIPC_GET_BITS(div, CHIPC_SCC_CD); div *= 4; } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { if (max_freq) { div = 1; } else { div = bhnd_bus_read_4(sc->res, CHIPC_SYS_CLK_CTL); div = CHIPC_GET_BITS(div, CHIPC_SYCC_CD); div = 4 * (div + 1); } } else { device_printf(sc->dev, "unknown device type\n"); return (0); } /* Determine clock frequency */ switch (slowclk) { case BHND_CLKSRC_LPO: hz = max_freq ? CHIPC_LPOMAXFREQ : CHIPC_LPOMINFREQ; break; case BHND_CLKSRC_XTAL: hz = max_freq ? CHIPC_XTALMAXFREQ : CHIPC_XTALMINFREQ; break; case BHND_CLKSRC_PCI: hz = max_freq ? CHIPC_PCIMAXFREQ : CHIPC_PCIMINFREQ; break; default: device_printf(sc->dev, "unknown slowclk source %#x\n", slowclk); return (0); } return (hz / div); }
/* return the slow clock source */ static bhnd_clksrc bhnd_pwrctl_slowclk_src(struct bhnd_pwrctl_softc *sc) { uint32_t clkreg; uint32_t clksrc; /* Fetch clock source */ if (PWRCTL_QUIRK(sc, PCICLK_CTL)) { return (bhnd_pwrctl_get_clksrc(sc->chipc_dev, BHND_CLOCK_ILP)); } else if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { clkreg = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); clksrc = clkreg & CHIPC_SCC_SS_MASK; } else { /* Instaclock */ clksrc = CHIPC_SCC_SS_XTAL; } /* Map to bhnd_clksrc */ switch (clksrc) { case CHIPC_SCC_SS_PCI: return (BHND_CLKSRC_PCI); case CHIPC_SCC_SS_LPO: return (BHND_CLKSRC_LPO); case CHIPC_SCC_SS_XTAL: return (BHND_CLKSRC_XTAL); default: return (BHND_CLKSRC_UNKNOWN); } }
/** * If required by this device, revert any GPIO/pin configuration applied * to allow SPROM access. * * @param sc chipc driver state. */ static void chipc_disable_sprom_pins(struct chipc_softc *sc) { uint32_t cctrl; /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return; CHIPC_LOCK_ASSERT(sc, MA_OWNED); KASSERT(sc->sprom_refcnt != 0, ("sprom pins already disabled")); KASSERT(sc->sprom_refcnt == 1, ("sprom pins in use")); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); /* 4331 devices */ if (CHIPC_QUIRK(sc, 4331_EXTPA_MUX_SPROM)) { cctrl |= CHIPC_CCTRL4331_EXTPA_EN; if (CHIPC_QUIRK(sc, 4331_GPIO2_5_MUX_SPROM)) cctrl |= CHIPC_CCTRL4331_EXTPA_ON_GPIO2_5; if (CHIPC_QUIRK(sc, 4331_EXTPA2_MUX_SPROM)) cctrl |= CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); return; } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } }
/** * Determine the NVRAM data source for this device. * * The SPROM, OTP, and flash capability flags must be fully populated in * @p caps. * * @param sc chipc driver state. * @param caps capability flags to be used to derive NVRAM configuration. */ static bhnd_nvram_src chipc_find_nvram_src(struct chipc_softc *sc, struct chipc_caps *caps) { uint32_t otp_st, srom_ctrl; /* * We check for hardware presence in order of precedence. For example, * SPROM is is always used in preference to internal OTP if found. */ if (CHIPC_QUIRK(sc, SUPPORTS_SPROM) && caps->sprom) { srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); if (srom_ctrl & CHIPC_SRC_PRESENT) return (BHND_NVRAM_SRC_SPROM); } /* Check for programmed OTP H/W subregion (contains SROM data) */ if (CHIPC_QUIRK(sc, SUPPORTS_OTP) && caps->otp_size > 0) { /* TODO: need access to HND-OTP device */ if (!CHIPC_QUIRK(sc, OTP_HND)) { device_printf(sc->dev, "NVRAM unavailable: unsupported OTP controller.\n"); return (BHND_NVRAM_SRC_UNKNOWN); } otp_st = bhnd_bus_read_4(sc->core, CHIPC_OTPST); if (otp_st & CHIPC_OTPS_GUP_HW) return (BHND_NVRAM_SRC_OTP); } /* Check for flash */ if (caps->flash_type != CHIPC_FLASH_NONE) return (BHND_NVRAM_SRC_FLASH); /* No NVRAM hardware capability declared */ return (BHND_NVRAM_SRC_UNKNOWN); }
static void chipc_write_chipctrl(device_t dev, uint32_t value, uint32_t mask) { struct chipc_softc *sc; uint32_t cctrl; sc = device_get_softc(dev); CHIPC_LOCK(sc); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); cctrl = (cctrl & ~mask) | (value | mask); bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); CHIPC_UNLOCK(sc); }
/* return the value suitable for writing to the dot11 core * FAST_PWRUP_DELAY register */ uint16_t bhnd_pwrctl_fast_pwrup_delay(struct bhnd_pwrctl_softc *sc) { uint32_t pll_on_delay, slowminfreq; uint16_t fpdelay; fpdelay = 0; slowminfreq = bhnd_pwrctl_slowclk_freq(sc, false); pll_on_delay = bhnd_bus_read_4(sc->res, CHIPC_PLL_ON_DELAY) + 2; pll_on_delay *= 1000000; pll_on_delay += (slowminfreq - 1); fpdelay = pll_on_delay / slowminfreq; return (fpdelay); }
/** * Determine the preferred NVRAM data source. */ static bhnd_nvram_src_t chipc_nvram_src(device_t dev) { struct chipc_softc *sc; uint32_t srom_ctrl; sc = device_get_softc(dev); /* Very early devices always included a SPROM */ if (CHIPC_QUIRK(sc, ALWAYS_HAS_SPROM)) return (BHND_NVRAM_SRC_SPROM); /* Most other early devices require checking ChipStatus flags */ if (CHIPC_QUIRK(sc, SPROM_CHECK_CHIPST)) return (chipc_nvram_src_chipst(sc)); /* * Later chipset revisions standardized the NVRAM capability flags and * register interfaces. * * We check for hardware presence in order of precedence. For example, * SPROM is is always used in preference to internal OTP if found. */ if (CHIPC_CAP(sc, CAP_SPROM)) { srom_ctrl = bhnd_bus_read_4(sc->core, CHIPC_SPROM_CTRL); if (srom_ctrl & CHIPC_SRC_PRESENT) return (BHND_NVRAM_SRC_SPROM); } /* Check for OTP */ if (CHIPC_CAP(sc, CAP_OTP_SIZE)) return (BHND_NVRAM_SRC_OTP); /* * Finally, Northstar chipsets (and possibly other chipsets?) support * external NAND flash. */ if (CHIPC_QUIRK(sc, SUPPORTS_NFLASH) && CHIPC_CAP(sc, CAP_NFLASH)) return (BHND_NVRAM_SRC_NFLASH); /* No NVRAM hardware capability declared */ return (BHND_NVRAM_SRC_NONE); }
/** * Read a 32-bit value from @p offset relative to the base address of * the given @p core_idx. * * @param io EROM I/O context. * @param core_idx Core index. * @param offset Core register offset. */ static uint32_t siba_eio_read_4(struct siba_erom_io *io, u_int core_idx, bus_size_t offset) { bus_size_t core_offset; /* Sanity check core index and offset */ if (core_idx >= io->ncores) panic("core index %u out of range (ncores=%u)", core_idx, io->ncores); if (offset > SIBA_CORE_SIZE - sizeof(uint32_t)) panic("invalid core offset %#jx", (uintmax_t)offset); /* Perform read */ core_offset = io->offset + SIBA_CORE_OFFSET(core_idx) + offset; if (io->res != NULL) return (bhnd_bus_read_4(io->res, core_offset)); else return (bus_space_read_4(io->bst, io->bsh, core_offset)); }
/** * If required by this device, enable access to the SPROM. * * @param sc chipc driver state. */ static int chipc_enable_sprom_pins(struct chipc_softc *sc) { uint32_t cctrl; CHIPC_LOCK_ASSERT(sc, MA_OWNED); KASSERT(sc->sprom_refcnt == 0, ("sprom pins already enabled")); /* Nothing to do? */ if (!CHIPC_QUIRK(sc, MUX_SPROM)) return (0); /* Check whether bus is busy */ if (!chipc_should_enable_muxed_sprom(sc)) return (EBUSY); cctrl = bhnd_bus_read_4(sc->core, CHIPC_CHIPCTRL); /* 4331 devices */ if (CHIPC_QUIRK(sc, 4331_EXTPA_MUX_SPROM)) { cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN; if (CHIPC_QUIRK(sc, 4331_GPIO2_5_MUX_SPROM)) cctrl &= ~CHIPC_CCTRL4331_EXTPA_ON_GPIO2_5; if (CHIPC_QUIRK(sc, 4331_EXTPA2_MUX_SPROM)) cctrl &= ~CHIPC_CCTRL4331_EXTPA_EN2; bhnd_bus_write_4(sc->core, CHIPC_CHIPCTRL, cctrl); return (0); } /* 4360 devices */ if (CHIPC_QUIRK(sc, 4360_FEM_MUX_SPROM)) { /* Unimplemented */ } /* Refuse to proceed on unsupported devices with muxed SPROM pins */ device_printf(sc->dev, "muxed sprom lines on unrecognized device\n"); return (ENXIO); }
static int bhnd_pmu_chipc_probe(device_t dev) { struct bhnd_pmu_softc *sc; struct chipc_caps *ccaps; struct chipc_softc *chipc_sc; device_t chipc; char desc[34]; int error; uint32_t pcaps; uint8_t rev; sc = device_get_softc(dev); /* Look for chipc parent */ chipc = device_get_parent(dev); if (device_get_devclass(chipc) != devclass_find("bhnd_chipc")) return (ENXIO); /* Check the chipc PMU capability flag. */ ccaps = BHND_CHIPC_GET_CAPS(chipc); if (!ccaps->pmu) return (ENXIO); /* Delegate to common driver implementation */ if ((error = bhnd_pmu_probe(dev)) > 0) return (error); /* Fetch PMU capability flags */ chipc_sc = device_get_softc(chipc); pcaps = bhnd_bus_read_4(chipc_sc->core, BHND_PMU_CAP); /* Set description */ rev = BHND_PMU_GET_BITS(pcaps, BHND_PMU_CAP_REV); snprintf(desc, sizeof(desc), "Broadcom ChipCommon PMU, rev %hhu", rev); device_set_desc_copy(dev, desc); return (BUS_PROBE_NOWILDCARD); }
static int chipc_attach(device_t dev) { struct chipc_softc *sc; bhnd_addr_t enum_addr; uint32_t ccid_reg; uint8_t chip_type; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = bhnd_device_quirks(dev, chipc_devices, sizeof(chipc_devices[0])); /* Allocate bus resources */ memcpy(sc->rspec, chipc_rspec, sizeof(sc->rspec)); if ((error = bhnd_alloc_resources(dev, sc->rspec, sc->res))) return (error); sc->core = sc->res[0]; /* Fetch our chipset identification data */ ccid_reg = bhnd_bus_read_4(sc->core, CHIPC_ID); chip_type = CHIPC_GET_ATTR(ccid_reg, ID_BUS); switch (chip_type) { case BHND_CHIPTYPE_SIBA: /* enumeration space starts at the ChipCommon register base. */ enum_addr = rman_get_start(sc->core->res); break; case BHND_CHIPTYPE_BCMA: case BHND_CHIPTYPE_BCMA_ALT: enum_addr = bhnd_bus_read_4(sc->core, CHIPC_EROMPTR); break; default: device_printf(dev, "unsupported chip type %hhu\n", chip_type); error = ENODEV; goto cleanup; } sc->ccid = bhnd_parse_chipid(ccid_reg, enum_addr); /* Fetch capability and status register values */ sc->caps = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); sc->cst = bhnd_bus_read_4(sc->core, CHIPC_CHIPST); // TODO switch (bhnd_chipc_nvram_src(dev)) { case BHND_NVRAM_SRC_CIS: device_printf(dev, "NVRAM source: CIS\n"); break; case BHND_NVRAM_SRC_SPROM: device_printf(dev, "NVRAM source: SPROM\n"); break; case BHND_NVRAM_SRC_OTP: device_printf(dev, "NVRAM source: OTP\n"); break; case BHND_NVRAM_SRC_NFLASH: device_printf(dev, "NVRAM source: NFLASH\n"); break; case BHND_NVRAM_SRC_NONE: device_printf(dev, "NVRAM source: NONE\n"); break; } return (0); cleanup: bhnd_release_resources(dev, sc->rspec, sc->res); return (error); }
static uint32_t bhnd_pmu_read_4(bus_size_t reg, void *ctx) { struct bhnd_pmu_softc *sc = ctx; return (bhnd_bus_read_4(sc->res, reg)); }
/** * Default bhnd_pmu driver implementation of DEVICE_ATTACH(). * * @param dev PMU device. * @param res The PMU device registers. The driver will maintain a borrowed * reference to this resource for the lifetime of the device. */ int bhnd_pmu_attach(device_t dev, struct bhnd_resource *res) { struct bhnd_pmu_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; devclass_t bhnd_class; device_t core, bus; int error; sc = device_get_softc(dev); sc->dev = dev; sc->quirks = 0; sc->res = res; /* Fetch capability flags */ sc->caps = bhnd_bus_read_4(sc->res, BHND_PMU_CAP); /* Find the bus-attached core */ bhnd_class = devclass_find("bhnd"); core = sc->dev; while ((bus = device_get_parent(core)) != NULL) { if (device_get_devclass(bus) == bhnd_class) break; core = bus; } if (core == NULL) { device_printf(sc->dev, "bhnd bus not found\n"); return (ENXIO); } /* Fetch chip and board info */ sc->cid = *bhnd_get_chipid(core); if ((error = bhnd_read_board_info(core, &sc->board))) { device_printf(sc->dev, "error fetching board info: %d\n", error); return (ENXIO); } /* Locate ChipCommon device */ sc->chipc_dev = bhnd_find_child(bus, BHND_DEVCLASS_CC, 0); if (sc->chipc_dev == NULL) { device_printf(sc->dev, "chipcommon device not found\n"); return (ENXIO); } /* Initialize query state */ error = bhnd_pmu_query_init(&sc->query, dev, sc->cid, &bhnd_pmu_res_io, sc); if (error) return (error); sc->io = sc->query.io; sc->io_ctx = sc->query.io_ctx; BPMU_LOCK_INIT(sc); /* Set quirk flags */ switch (sc->cid.chip_id) { case BHND_CHIPID_BCM4328: case BHND_CHIPID_BCM5354: /* HTAVAIL/ALPAVAIL are bitswapped in CLKCTL */ sc->quirks |= BPMU_QUIRK_CLKCTL_CCS0; break; default: break; } /* Initialize PMU */ if ((error = bhnd_pmu_init(sc))) { device_printf(sc->dev, "PMU init failed: %d\n", error); goto failed; } /* Set up sysctl nodes */ ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "bus_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, bhnd_pmu_sysctl_bus_freq, "IU", "Bus clock frequency"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "cpu_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, bhnd_pmu_sysctl_cpu_freq, "IU", "CPU clock frequency"); SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "mem_freq", CTLTYPE_UINT | CTLFLAG_RD, sc, 0, bhnd_pmu_sysctl_mem_freq, "IU", "Memory clock frequency"); return (0); failed: BPMU_LOCK_DESTROY(sc); bhnd_pmu_query_fini(&sc->query); return (error); }
/** * Distribute @p clock on backplane. * * @param sc Driver instance state. * @param clock Clock to enable. * * @retval 0 success * @retval ENODEV If @p clock is unsupported, or if the device does not * support dynamic clock control. */ int bhnd_pwrctl_setclk(struct bhnd_pwrctl_softc *sc, bhnd_clock clock) { uint32_t scc; PWRCTL_LOCK_ASSERT(sc, MA_OWNED); /* Is dynamic clock control supported? */ if (PWRCTL_QUIRK(sc, FIXED_CLK)) return (ENODEV); /* Chips with ccrev 10 are EOL and they don't have SYCC_HR used below */ if (bhnd_get_hwrev(sc->chipc_dev) == 10) return (ENODEV); scc = bhnd_bus_read_4(sc->res, CHIPC_PLL_SLOWCLK_CTL); switch (clock) { case BHND_CLOCK_HT: /* fast (pll) clock */ if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { scc &= ~(CHIPC_SCC_XC | CHIPC_SCC_FS | CHIPC_SCC_IP); scc |= CHIPC_SCC_IP; /* force xtal back on before clearing SCC_DYN_XTAL.. */ bhnd_pwrctl_ungate_clock(sc->chipc_dev, BHND_CLOCK_HT); } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { scc |= CHIPC_SYCC_HR; } else { return (ENODEV); } bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); DELAY(CHIPC_PLL_DELAY); break; case BHND_CLOCK_DYN: /* enable dynamic clock control */ if (PWRCTL_QUIRK(sc, SLOWCLK_CTL)) { scc &= ~(CHIPC_SCC_FS | CHIPC_SCC_IP | CHIPC_SCC_XC); if ((scc & CHIPC_SCC_SS_MASK) != CHIPC_SCC_SS_XTAL) scc |= CHIPC_SCC_XC; bhnd_bus_write_4(sc->res, CHIPC_PLL_SLOWCLK_CTL, scc); /* for dynamic control, we have to release our xtal_pu * "force on" */ if (scc & CHIPC_SCC_XC) { bhnd_pwrctl_gate_clock(sc->chipc_dev, BHND_CLOCK_HT); } } else if (PWRCTL_QUIRK(sc, INSTACLK_CTL)) { /* Instaclock */ scc &= ~CHIPC_SYCC_HR; bhnd_bus_write_4(sc->res, CHIPC_SYS_CLK_CTL, scc); } else { return (ENODEV); } break; default: return (ENODEV); } return (0); }
static uint32_t chipc_read_chipst(device_t dev) { struct chipc_softc *sc = device_get_softc(dev); return (bhnd_bus_read_4(sc->core, CHIPC_CHIPST)); }
/* Read and parse chipc capabilities */ static int chipc_read_caps(struct chipc_softc *sc, struct chipc_caps *caps) { uint32_t cap_reg; uint32_t cap_ext_reg; uint32_t regval; /* Fetch cap registers */ cap_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES); cap_ext_reg = 0; if (CHIPC_QUIRK(sc, SUPPORTS_CAP_EXT)) cap_ext_reg = bhnd_bus_read_4(sc->core, CHIPC_CAPABILITIES_EXT); /* Extract values */ caps->num_uarts = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_NUM_UART); caps->mipseb = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_MIPSEB); caps->uart_gpio = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_UARTGPIO); caps->uart_clock = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_UCLKSEL); caps->extbus_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_EXTBUS); caps->pwr_ctrl = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PWR_CTL); caps->jtag_master = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_JTAGP); caps->pll_type = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_PLL); caps->backplane_64 = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_BKPLN64); caps->boot_rom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ROM); caps->pmu = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_PMU); caps->eci = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_ECI); caps->sprom = CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_SPROM); caps->otp_size = CHIPC_GET_BITS(cap_reg, CHIPC_CAP_OTP_SIZE); caps->seci = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_SECI); caps->gsio = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_GSIO); caps->aob = CHIPC_GET_FLAG(cap_ext_reg, CHIPC_CAP2_AOB); /* Fetch OTP size for later IPX controller revisions */ if (CHIPC_QUIRK(sc, IPX_OTPL_SIZE)) { regval = bhnd_bus_read_4(sc->core, CHIPC_OTPLAYOUT); caps->otp_size = CHIPC_GET_BITS(regval, CHIPC_OTPL_SIZE); } /* Determine flash type and parameters */ caps->cfi_width = 0; switch (CHIPC_GET_BITS(cap_reg, CHIPC_CAP_FLASH)) { case CHIPC_CAP_SFLASH_ST: caps->flash_type = CHIPC_SFLASH_ST; break; case CHIPC_CAP_SFLASH_AT: caps->flash_type = CHIPC_SFLASH_AT; break; case CHIPC_CAP_NFLASH: /* unimplemented */ caps->flash_type = CHIPC_NFLASH; break; case CHIPC_CAP_PFLASH: caps->flash_type = CHIPC_PFLASH_CFI; /* determine cfi width */ regval = bhnd_bus_read_4(sc->core, CHIPC_FLASH_CFG); if (CHIPC_GET_FLAG(regval, CHIPC_FLASH_CFG_DS)) caps->cfi_width = 2; else caps->cfi_width = 1; break; case CHIPC_CAP_FLASH_NONE: caps->flash_type = CHIPC_FLASH_NONE; break; } /* Handle 4706_NFLASH fallback */ if (CHIPC_QUIRK(sc, 4706_NFLASH) && CHIPC_GET_FLAG(cap_reg, CHIPC_CAP_4706_NFLASH)) { caps->flash_type = CHIPC_NFLASH_4706; } /* Determine NVRAM source. Must occur after the SPROM/OTP/flash * capability flags have been populated. */ caps->nvram_src = chipc_find_nvram_src(sc, caps); /* Determine the SPROM offset within OTP (if any). SPROM-formatted * data is placed within the OTP general use region. */ caps->sprom_offset = 0; if (caps->nvram_src == BHND_NVRAM_SRC_OTP) { CHIPC_ASSERT_QUIRK(sc, OTP_IPX); /* Bit offset to GUP HW subregion containing SPROM data */ regval = bhnd_bus_read_4(sc->core, CHIPC_OTPLAYOUT); caps->sprom_offset = CHIPC_GET_BITS(regval, CHIPC_OTPL_GUP); /* Convert to bytes */ caps->sprom_offset /= 8; } return (0); }