Module Name: src Committed By: martin Date: Tue Feb 25 18:40:44 UTC 2020
Modified Files: src/sys/dev/fdt [netbsd-9]: dwcmmc_fdt.c src/sys/dev/ic [netbsd-9]: bwfm.c bwfmreg.h bwfmvar.h dwc_mmc.c dwc_mmc_reg.h dwc_mmc_var.h src/sys/dev/pcmcia [netbsd-9]: pcmciareg.h src/sys/dev/sdmmc [netbsd-9]: if_bwfm_sdio.c sdhc.c sdmmc.c sdmmc_cis.c sdmmc_io.c sdmmc_ioreg.h sdmmc_mem.c sdmmcdevs sdmmcvar.h src/sys/net [netbsd-9]: if_media.h Added Files: src/sys/dev/sdmmc [netbsd-9]: if_bwfm_sdio.h Log Message: Pull up following revision(s) (requested by mrg in ticket #717): sys/dev/fdt/dwcmmc_fdt.c 1.11 sys/dev/ic/bwfm.c 1.15-1.18 sys/dev/ic/bwfmreg.h 1.4-1.6 sys/dev/ic/bwfmvar.h 1.4,1.5 sys/dev/ic/dwc_mmc.c 1.21,1.22 sys/dev/ic/dwc_mmc_reg.h 1.8,1.9,1.12,1.13 sys/dev/pcmcia/pcmciareg.h 1.11 sys/dev/sdmmc/if_bwfm_sdio.c 1.4,1.6-1.12 sys/dev/sdmmc/if_bwfm_sdio.h 1.1,1.2 sys/dev/sdmmc/sdhc.c 1.105,1.106 sys/dev/sdmmc/sdmmc.c 1.37,1.39 sys/dev/sdmmc/sdmmc_cis.c 1.6,1.8 sys/dev/sdmmc/sdmmc_io.c 1.15-1.19 sys/dev/sdmmc/sdmmc_ioreg.h 1.4,1.5 sys/dev/sdmmc/sdmmc_mem.c 1.69-1.71 sys/dev/sdmmc/sdmmcdevs 1.5-1.8 sys/dev/sdmmc/sdmmcvar.h 1.31,1.33,1.34 sys/net/if_media.h 1.66 Add Broadcom devices - Fix typo - add PCMCIA_CISTPL_SDIO definition. - >From OpenBSD: - move event handling to workqueue - check for save/restore capability - Tag work queue as MPsafe and increase length. - Juse use bpf_mtap(), the 802.11 encapsulation is handled by firmware. - >From OpenBSD: - support block length per function - add functions to read/write regions - Decode (but not use) SDIO tuple in CIS. - Fix locking. - Add more SDIO defines (partially from version 3.0). - >From OpenBSD: - All the missing pieces (firmware load, chip setup, protocol handling) TX queue and interrupt handling via sdmmc_task. - Fix locking. - Fix packet parsing. - Add parser for original firmware config files. - tagging work queue as MPSAFE was premature. Revert. - SD_IO_RW_EXTENDED is a data transfer command, so set ADTC flag instead of AC Use correct function to verify if a task has been queued. Avoids race that can corrupt the task queue. - More register definitions. - Add IFM_IEEE80211_VHT subtype, IFM_IEEE80211_11AC operating mode, and missing descriptions - If firmware is connected in HT or VHT mode, report it to SIOCGIFMEDIA - white space police. Skip setting power when the voltage doesn't change. Also increase some timeouts. - Add and use sdmmc_pause to avoid long-term busy waits. - Add sdio abort function. - Additional error messages. - Print parameters for SDIO devices. - Minor cosmetics. - Simplyfy sdmmc_io_set_blocklen function signature by dropping the extra softc pointer. Aligns with OpenBSD. - Missing commit for sdio abort function. - More code from OpenBSD - no need to splnet() when enqueing packets - explicit structure padding - make internal functions static - also prepare for GPIO interrupts. - Avoid warnings for tautological shifts as sole conditional. - Follow the Linux driver an use the FDT "compatible" property to build a filename for the nvram config file, fall back to the standard filename. E.g. [Caching 123 nodes and 1093 properties] compatible 73696e6f 766f6970 2c627069 2d6d322d "sinovoip,bpi-m2- 0010: 7a65726f 00...... ........ ........ zero" 0015: 616c6c77 696e6e65 722c7375 6e38692d "allwinner,sun8i- 0025: 68322d70 6c757300 ........ ........ h2-plus" interrupt-parent 00000001 ........ ........ ........ .... model 42616e61 6e612050 69204250 492d4d32 "Banana Pi BPI-M2 0010: 2d5a6572 6f00.... ........ ........ -Zero" name 00...... ........ ........ ........ "" serial-number 30326330 30303432 65636431 36376566 02c00042ecd167ef 0010: 00...... ........ ........ ........ . -rw-r--r-- 1 root wheel 875 Nov 2 12:06 brcmfmac43430-sdio.AP6212.txt lrwxr-xr-x 1 root wheel 29 Dec 30 16:19 brcmfmac43430-sdio.sinovoip,bpi-m2-zero.txt -> brcmfmac43430-sdio.AP6212.txt -rw-r--r-- 1 root wheel 874 Jun 30 2019 brcmfmac43430-sdio.raspberrypi,3-model-b.txt -rw-r--r-- 1 root wheel 1864 Jun 30 2019 brcmfmac43455-sdio.raspberrypi,3-model-b-plus.txt lrwxr-xr-x 1 root wheel 29 Dec 30 11:24 brcmfmac43455-sdio.raspberrypi,4-model-b-plus.txt -> brcmfmac43455-sdio.raspberrypi,3-model-b-plus.txt - Add product ID for Broadcom BCM43455 - Use correct firmware for BCM43456 - size check was backwards. - Be less noisy for some commands. - Fix DWC_MMC_INT_SDIO_INT bit - dwc_mmc fixes: - Rockchip uses a different SDIO int bit, so take this into consideration - Avoid unnecessary resets and always wait for resets to complete - kpause instead of delay while holding spinlock - Do not attempt autostop for SD_IO_RW_EXTENDED commands - Allow for sub-blklen byte counts for single block transfers - More SDIO stability and performance fixes To generate a diff of this commit: cvs rdiff -u -r1.8.2.1 -r1.8.2.2 src/sys/dev/fdt/dwcmmc_fdt.c cvs rdiff -u -r1.14 -r1.14.6.1 src/sys/dev/ic/bwfm.c cvs rdiff -u -r1.3 -r1.3.10.1 src/sys/dev/ic/bwfmreg.h cvs rdiff -u -r1.3 -r1.3.6.1 src/sys/dev/ic/bwfmvar.h cvs rdiff -u -r1.17.2.1 -r1.17.2.2 src/sys/dev/ic/dwc_mmc.c cvs rdiff -u -r1.7 -r1.7.8.1 src/sys/dev/ic/dwc_mmc_reg.h cvs rdiff -u -r1.8.2.1 -r1.8.2.2 src/sys/dev/ic/dwc_mmc_var.h cvs rdiff -u -r1.10 -r1.10.170.1 src/sys/dev/pcmcia/pcmciareg.h cvs rdiff -u -r1.3 -r1.3.8.1 src/sys/dev/sdmmc/if_bwfm_sdio.c cvs rdiff -u -r0 -r1.2.4.2 src/sys/dev/sdmmc/if_bwfm_sdio.h cvs rdiff -u -r1.103 -r1.103.2.1 src/sys/dev/sdmmc/sdhc.c cvs rdiff -u -r1.36 -r1.36.4.1 src/sys/dev/sdmmc/sdmmc.c cvs rdiff -u -r1.5 -r1.5.10.1 src/sys/dev/sdmmc/sdmmc_cis.c cvs rdiff -u -r1.14 -r1.14.4.1 src/sys/dev/sdmmc/sdmmc_io.c cvs rdiff -u -r1.3 -r1.3.2.1 src/sys/dev/sdmmc/sdmmc_ioreg.h cvs rdiff -u -r1.68 -r1.68.2.1 src/sys/dev/sdmmc/sdmmc_mem.c cvs rdiff -u -r1.4 -r1.4.4.1 src/sys/dev/sdmmc/sdmmcdevs cvs rdiff -u -r1.30 -r1.30.4.1 src/sys/dev/sdmmc/sdmmcvar.h cvs rdiff -u -r1.65 -r1.65.2.1 src/sys/net/if_media.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/fdt/dwcmmc_fdt.c diff -u src/sys/dev/fdt/dwcmmc_fdt.c:1.8.2.1 src/sys/dev/fdt/dwcmmc_fdt.c:1.8.2.2 --- src/sys/dev/fdt/dwcmmc_fdt.c:1.8.2.1 Tue Jan 21 10:39:58 2020 +++ src/sys/dev/fdt/dwcmmc_fdt.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dwcmmc_fdt.c,v 1.8.2.1 2020/01/21 10:39:58 martin Exp $ */ +/* $NetBSD: dwcmmc_fdt.c,v 1.8.2.2 2020/02/25 18:40:43 martin Exp $ */ /*- * Copyright (c) 2015-2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dwcmmc_fdt.c,v 1.8.2.1 2020/01/21 10:39:58 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dwcmmc_fdt.c,v 1.8.2.2 2020/02/25 18:40:43 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -56,12 +56,14 @@ static int dwcmmc_fdt_signal_voltage(str struct dwcmmc_fdt_config { u_int ciu_div; u_int flags; + uint32_t intr_cardmask; }; static const struct dwcmmc_fdt_config dwcmmc_rk3288_config = { .ciu_div = 2, .flags = DWC_MMC_F_USE_HOLD_REG | DWC_MMC_F_DMA, + .intr_cardmask = __BIT(24), }; static const struct of_compat_data compat_data[] = { @@ -156,6 +158,7 @@ dwcmmc_fdt_attach(device_t parent, devic sc->sc_bus_width = 4; sc->sc_fifo_depth = fifo_depth; + sc->sc_intr_cardmask = esc->sc_conf->intr_cardmask; sc->sc_ciu_div = esc->sc_conf->ciu_div; sc->sc_flags = esc->sc_conf->flags; sc->sc_pre_power_on = dwcmmc_fdt_pre_power_on; Index: src/sys/dev/ic/bwfm.c diff -u src/sys/dev/ic/bwfm.c:1.14 src/sys/dev/ic/bwfm.c:1.14.6.1 --- src/sys/dev/ic/bwfm.c:1.14 Sun Sep 2 19:46:53 2018 +++ src/sys/dev/ic/bwfm.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: bwfm.c,v 1.14 2018/09/02 19:46:53 maya Exp $ */ +/* $NetBSD: bwfm.c,v 1.14.6.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: bwfm.c,v 1.5 2017/10/16 22:27:16 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation @@ -112,9 +112,11 @@ int bwfm_fwvar_var_set_int(struct bwfm_ struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc *, struct bwfm_bss_info *); void bwfm_scan(struct bwfm_softc *); void bwfm_connect(struct bwfm_softc *); +void bwfm_get_sta_info(struct bwfm_softc *, struct ifmediareq *); void bwfm_rx(struct bwfm_softc *, struct mbuf *); -void bwfm_rx_event(struct bwfm_softc *, char *, size_t); +void bwfm_rx_event(struct bwfm_softc *, struct mbuf *); +void bwfm_rx_event_cb(struct bwfm_softc *, struct mbuf *); void bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t); uint8_t bwfm_2ghz_channels[] = { @@ -304,9 +306,6 @@ bwfm_start(struct ifnet *ifp) /* TODO: return if no link? */ for (;;) { - struct ieee80211_node *ni; - struct ether_header *eh; - /* Discard management packets (fw handles this for us) */ IF_DEQUEUE(&ic->ic_mgtq, m); if (m != NULL) { @@ -323,36 +322,19 @@ bwfm_start(struct ifnet *ifp) if (m == NULL) break; - eh = mtod(m, struct ether_header *); - ni = ieee80211_find_txnode(ic, eh->ether_dhost); - if (ni == NULL) { - ifp->if_oerrors++; - m_freem(m); - continue; - } - - if (ieee80211_classify(ic, m, ni) != 0) { - ifp->if_oerrors++; - m_freem(m); - ieee80211_free_node(ni); - continue; - } - error = sc->sc_bus_ops->bs_txdata(sc, &m); if (error == ENOBUFS) { IF_PREPEND(&ifp->if_snd, m); ifp->if_flags |= IFF_OACTIVE; break; } - if (error != 0) { ifp->if_oerrors++; m_freem(m); - if (ni != NULL) - ieee80211_free_node(ni); - } else { - bpf_mtap3(ic->ic_rawbpf, m, BPF_D_OUT); + continue; } + + bpf_mtap(ifp, m, BPF_D_OUT); } } @@ -363,6 +345,7 @@ bwfm_init(struct ifnet *ifp) struct ieee80211com *ic = &sc->sc_ic; uint8_t evmask[BWFM_EVENT_MASK_LEN]; struct bwfm_join_pref_params join_pref[2]; + int pm; if (bwfm_fwvar_var_set_int(sc, "mpc", 1)) { printf("%s: could not set mpc\n", DEVNAME(sc)); @@ -388,10 +371,31 @@ bwfm_init(struct ifnet *ifp) #define ENABLE_EVENT(e) evmask[(e) / 8] |= 1 << ((e) % 8) /* Events used to drive the state machine */ - ENABLE_EVENT(BWFM_E_ASSOC); - ENABLE_EVENT(BWFM_E_ESCAN_RESULT); - ENABLE_EVENT(BWFM_E_SET_SSID); - ENABLE_EVENT(BWFM_E_LINK); + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + ENABLE_EVENT(BWFM_E_IF); + ENABLE_EVENT(BWFM_E_LINK); + ENABLE_EVENT(BWFM_E_AUTH); + ENABLE_EVENT(BWFM_E_ASSOC); + ENABLE_EVENT(BWFM_E_DEAUTH); + ENABLE_EVENT(BWFM_E_DISASSOC); + ENABLE_EVENT(BWFM_E_SET_SSID); + ENABLE_EVENT(BWFM_E_ESCAN_RESULT); + break; +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_HOSTAP: + ENABLE_EVENT(BWFM_E_AUTH_IND); + ENABLE_EVENT(BWFM_E_ASSOC_IND); + ENABLE_EVENT(BWFM_E_REASSOC_IND); + ENABLE_EVENT(BWFM_E_DEAUTH_IND); + ENABLE_EVENT(BWFM_E_DISASSOC_IND); + ENABLE_EVENT(BWFM_E_ESCAN_RESULT); + ENABLE_EVENT(BWFM_E_ESCAN_RESULT); + break; +#endif + default: + break; + } #undef ENABLE_EVENT #ifdef BWFM_DEBUG @@ -419,7 +423,16 @@ bwfm_init(struct ifnet *ifp) return EIO; } - if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 2)) { + /* + * Use CAM (constantly awake) when we are running as AP + * otherwise use fast power saving. + */ + pm = BWFM_PM_FAST_PS; +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + pm = BWFM_PM_CAM; +#endif + if (bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, pm)) { printf("%s: could not set power\n", DEVNAME(sc)); return EIO; } @@ -466,15 +479,25 @@ bwfm_stop(struct ifnet *ifp, int disable { struct bwfm_softc *sc = ifp->if_softc; struct ieee80211com *ic = &sc->sc_ic; + struct bwfm_join_params join; sc->sc_tx_timer = 0; ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + memset(&join, 0, sizeof(join)); + bwfm_fwvar_cmd_set_data(sc, BWFM_C_SET_SSID, &join, sizeof(join)); bwfm_fwvar_cmd_set_int(sc, BWFM_C_DOWN, 1); bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, 0); + bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_AP, 0); + bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_INFRA, 0); + bwfm_fwvar_cmd_set_int(sc, BWFM_C_UP, 1); + bwfm_fwvar_cmd_set_int(sc, BWFM_C_SET_PM, BWFM_PM_FAST_PS); ieee80211_new_state(ic, IEEE80211_S_INIT, -1); + + if (sc->sc_bus_ops->bs_stop) + sc->sc_bus_ops->bs_stop(sc); } void @@ -531,6 +554,12 @@ bwfm_ioctl(struct ifnet *ifp, u_long cmd } break; + case SIOCGIFMEDIA: + error = ieee80211_ioctl(ic, cmd, data); + if (error == 0 && ic->ic_state == IEEE80211_S_RUN) + bwfm_get_sta_info(sc, (struct ifmediareq *)data); + break; + default: error = ieee80211_ioctl(ic, cmd, data); } @@ -618,7 +647,7 @@ bwfm_key_set_cb(struct bwfm_softc *sc, s wsec_key.len = htole32(wk->wk_keylen); memcpy(wsec_key.data, wk->wk_key, sizeof(wsec_key.data)); if (!ext_key) - wsec_key.flags = htole32(BWFM_PRIMARY_KEY); + wsec_key.flags = htole32(BWFM_WSEC_PRIMARY_KEY); switch (wk->wk_cipher->ic_cipher) { case IEEE80211_CIPHER_WEP: @@ -682,7 +711,7 @@ bwfm_key_delete_cb(struct bwfm_softc *sc memset(&wsec_key, 0, sizeof(wsec_key)); wsec_key.index = htole32(wk->wk_keyix); - wsec_key.flags = htole32(BWFM_PRIMARY_KEY); + wsec_key.flags = htole32(BWFM_WSEC_PRIMARY_KEY); if (bwfm_fwvar_var_set_data(sc, "wsec_key", &wsec_key, sizeof(wsec_key))) return; @@ -770,6 +799,9 @@ bwfm_task(struct work *wk, void *arg) case BWFM_TASK_KEY_DELETE: bwfm_key_delete_cb(sc, &t->t_key); break; + case BWFM_TASK_RX_EVENT: + bwfm_rx_event_cb(sc, t->t_mbuf); + break; default: panic("bwfm: unknown task command %d", t->t_cmd); } @@ -1261,6 +1293,52 @@ bwfm_chip_cm3_set_passive(struct bwfm_so } } +int +bwfm_chip_sr_capable(struct bwfm_softc *sc) +{ + struct bwfm_core *core; + uint32_t reg; + + if (sc->sc_chip.ch_pmurev < 17) + return 0; + + switch (sc->sc_chip.ch_chip) { + case BRCM_CC_4345_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: + core = bwfm_chip_get_pmu(sc); + sc->sc_buscore_ops->bc_write(sc, core->co_base + + BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3); + reg = sc->sc_buscore_ops->bc_read(sc, core->co_base + + BWFM_CHIP_REG_CHIPCONTROL_DATA); + return (reg & (1 << 2)) != 0; + case BRCM_CC_43241_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + core = bwfm_chip_get_pmu(sc); + sc->sc_buscore_ops->bc_write(sc, core->co_base + + BWFM_CHIP_REG_CHIPCONTROL_ADDR, 3); + reg = sc->sc_buscore_ops->bc_read(sc, core->co_base + + BWFM_CHIP_REG_CHIPCONTROL_DATA); + return reg != 0; + case BRCM_CC_43430_CHIP_ID: + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON); + reg = sc->sc_buscore_ops->bc_read(sc, core->co_base + + BWFM_CHIP_REG_SR_CONTROL1); + return reg != 0; + default: + core = bwfm_chip_get_pmu(sc); + reg = sc->sc_buscore_ops->bc_read(sc, core->co_base + + BWFM_CHIP_REG_PMUCAPABILITIES_EXT); + if ((reg & BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP) == 0) + return 0; + reg = sc->sc_buscore_ops->bc_read(sc, core->co_base + + BWFM_CHIP_REG_RETENTION_CTL); + return (reg & (BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS | + BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS)) == 0; + } +} + /* RAM size helpers */ void bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core) @@ -1415,10 +1493,10 @@ bwfm_proto_bcdc_query_dcmd(struct bwfm_s { struct bwfm_proto_bcdc_dcmd *dcmd; size_t size = sizeof(dcmd->hdr) + *len; - static int reqid = 0; + int reqid; int ret = 1; - reqid++; + reqid = sc->sc_bcdc_reqid++; dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP); if (*len > sizeof(dcmd->buf)) @@ -1455,8 +1533,6 @@ bwfm_proto_bcdc_query_dcmd(struct bwfm_s } if (buf) { - if (size > *len) - size = *len; if (size < *len) *len = size; memcpy(buf, dcmd->buf, *len); @@ -1477,10 +1553,9 @@ bwfm_proto_bcdc_set_dcmd(struct bwfm_sof { struct bwfm_proto_bcdc_dcmd *dcmd; size_t size = sizeof(dcmd->hdr) + len; - int reqid = 0; - int ret = 1; + int ret = 1, reqid; - reqid++; + reqid = sc->sc_bcdc_reqid++; dcmd = kmem_zalloc(sizeof(*dcmd), KM_SLEEP); if (len > sizeof(dcmd->buf)) @@ -1761,50 +1836,107 @@ bwfm_connect(struct bwfm_softc *sc) } void +bwfm_get_sta_info(struct bwfm_softc *sc, struct ifmediareq *ifmr) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + struct bwfm_sta_info sta; + uint32_t flags, txrate; + + memset(&sta, 0, sizeof(sta)); + memcpy(&sta, ni->ni_macaddr, sizeof(ni->ni_macaddr)); + + if (bwfm_fwvar_var_get_data(sc, "sta_info", &sta, sizeof(sta))) + return; + + if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, sta.ea)) + return; + + if (le16toh(sta.ver) < 4) + return; + + flags = le32toh(sta.flags); + if ((flags & BWFM_STA_SCBSTATS) == 0) + return; + + txrate = le32toh(sta.tx_rate); + if (txrate == 0xffffffff) + return; + + if ((flags & BWFM_STA_VHT_CAP) != 0) { + ifmr->ifm_active &= ~IFM_TMASK; + ifmr->ifm_active |= IFM_IEEE80211_VHT; + ifmr->ifm_active &= ~IFM_MMASK; + ifmr->ifm_active |= IFM_IEEE80211_11AC; + } else if ((flags & BWFM_STA_N_CAP) != 0) { + ifmr->ifm_active &= ~IFM_TMASK; + ifmr->ifm_active |= IFM_IEEE80211_MCS; + ifmr->ifm_active &= ~IFM_MMASK; + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) + ifmr->ifm_active |= IFM_IEEE80211_11NG; + else + ifmr->ifm_active |= IFM_IEEE80211_11NA; + } +} + +void bwfm_rx(struct bwfm_softc *sc, struct mbuf *m) { struct ieee80211com *ic = &sc->sc_ic; struct ifnet *ifp = ic->ic_ifp; struct bwfm_event *e = mtod(m, struct bwfm_event *); - int s; if (m->m_len >= sizeof(e->ehdr) && ntohs(e->ehdr.ether_type) == BWFM_ETHERTYPE_LINK_CTL && memcmp(BWFM_BRCM_OUI, e->hdr.oui, sizeof(e->hdr.oui)) == 0 && ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) { - bwfm_rx_event(sc, mtod(m, char *), m->m_len); - m_freem(m); + bwfm_rx_event(sc, m); + // m_freem(m); return; } - s = splnet(); + m_set_rcvif(m, ifp); + if_percpuq_enqueue(ifp->if_percpuq, m); +} - if ((ifp->if_flags & IFF_RUNNING) != 0) { - m_set_rcvif(m, ifp); - if_percpuq_enqueue(ifp->if_percpuq, m); +void +bwfm_rx_event(struct bwfm_softc *sc, struct mbuf *m) +{ + struct bwfm_task *t; + + t = pcq_get(sc->sc_freetask); + if (t == NULL) { + m_freem(m); + printf("%s: no free tasks\n", DEVNAME(sc)); + return; } - splx(s); + t->t_cmd = BWFM_TASK_RX_EVENT; + t->t_mbuf = m; + workqueue_enqueue(sc->sc_taskq, (struct work*)t, NULL); } void -bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) +bwfm_rx_event_cb(struct bwfm_softc *sc, struct mbuf *m) { struct ieee80211com *ic = &sc->sc_ic; - struct bwfm_event *e = (void *)buf; + struct bwfm_event *e = mtod(m, void *); + size_t len = m->m_len; int s; - DPRINTF(("%s: buf %p len %lu datalen %u code %u status %u" - " reason %u\n", __func__, buf, len, ntohl(e->msg.datalen), + DPRINTF(("%s: event %p len %lu datalen %u code %u status %u" + " reason %u\n", __func__, e, len, ntohl(e->msg.datalen), ntohl(e->msg.event_type), ntohl(e->msg.status), ntohl(e->msg.reason))); - if (ntohl(e->msg.event_type) >= BWFM_E_LAST) + if (ntohl(e->msg.event_type) >= BWFM_E_LAST) { + m_freem(m); return; + } switch (ntohl(e->msg.event_type)) { case BWFM_E_ESCAN_RESULT: { - struct bwfm_escan_results *res = (void *)(buf + sizeof(*e)); + struct bwfm_escan_results *res = (void *)&e[1]; struct bwfm_bss_info *bss; int i; if (ntohl(e->msg.status) != BWFM_E_STATUS_PARTIAL) { @@ -1817,11 +1949,13 @@ bwfm_rx_event(struct bwfm_softc *sc, cha } len -= sizeof(*e); if (len < sizeof(*res) || len < le32toh(res->buflen)) { + m_freem(m); printf("%s: results too small\n", DEVNAME(sc)); return; } len -= sizeof(*res); if (len < le16toh(res->bss_count) * sizeof(struct bwfm_bss_info)) { + m_freem(m); printf("%s: results too small\n", DEVNAME(sc)); return; } @@ -1874,6 +2008,8 @@ bwfm_rx_event(struct bwfm_softc *sc, cha default: break; } + + m_freem(m); } void Index: src/sys/dev/ic/bwfmreg.h diff -u src/sys/dev/ic/bwfmreg.h:1.3 src/sys/dev/ic/bwfmreg.h:1.3.10.1 --- src/sys/dev/ic/bwfmreg.h:1.3 Fri May 11 07:41:11 2018 +++ src/sys/dev/ic/bwfmreg.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: bwfmreg.h,v 1.3 2018/05/11 07:41:11 maya Exp $ */ +/* $NetBSD: bwfmreg.h,v 1.3.10.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: bwfmreg.h,v 1.16 2018/02/07 21:44:09 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation @@ -372,25 +372,161 @@ struct bwfm_bss_info { uint16_t capability; uint8_t ssid_len; uint8_t ssid[BWFM_MAX_SSID_LEN]; + uint8_t pad0; uint32_t nrates; uint8_t rates[16]; uint16_t chanspec; uint16_t atim_window; uint8_t dtim_period; + uint8_t pad1; uint16_t rssi; uint8_t phy_noise; uint8_t n_cap; + uint16_t pad2; uint32_t nbss_cap; uint8_t ctl_ch; + uint8_t pad3[3]; uint32_t reserved32[1]; uint8_t flags; uint8_t reserved[3]; uint8_t basic_mcs[BWFM_MCSSET_LEN]; uint16_t ie_offset; + uint16_t pad4; uint32_t ie_length; uint16_t snr; }; +#define BWFM_MAXRATES_IN_SET BWFM_MCSSET_LEN +#define BWFM_ANT_MAX 4 +#define BWFM_VHT_CAP_MCS_MAP_NSS_MAX 8 +#define BWFM_HE_CAP_MCS_MAP_NSS_MAX BWFM_VHT_CAP_MCS_MAP_NSS_MAX + +struct bwfm_sta_rateset_v5 { + uint32_t count; + /* rates in 500kbps units w/hi bit set if basic */ + uint8_t rates[BWFM_MAXRATES_IN_SET]; + uint8_t mcs[BWFM_MCSSET_LEN]; + uint16_t vht_mcs[BWFM_VHT_CAP_MCS_MAP_NSS_MAX]; +}; + +struct bwfm_sta_rateset_v7 { + uint16_t version; + uint16_t len; + uint32_t count; + /* rates in 500kbps units w/hi bit set if basic */ + uint8_t rates[BWFM_MAXRATES_IN_SET]; + uint8_t mcs[BWFM_MCSSET_LEN]; + uint16_t vht_mcs[BWFM_VHT_CAP_MCS_MAP_NSS_MAX]; + uint16_t he_mcs[BWFM_HE_CAP_MCS_MAP_NSS_MAX]; +}; + +struct bwfm_sta_info { + uint16_t ver; + uint16_t len; + uint16_t cap; /* sta's advertised capabilities */ + + uint32_t flags; +#define BWFM_STA_BRCM 0x00000001 /* Running a Broadcom driver */ +#define BWFM_STA_WME 0x00000002 /* WMM association */ +#define BWFM_STA_NONERP 0x00000004 /* No ERP */ +#define BWFM_STA_AUTHE 0x00000008 /* Authenticated */ +#define BWFM_STA_ASSOC 0x00000010 /* Associated */ +#define BWFM_STA_AUTHO 0x00000020 /* Authorized */ +#define BWFM_STA_WDS 0x00000040 /* Wireless Distribution System */ +#define BWFM_STA_WDS_LINKUP 0x00000080 /* WDS traffic/probes flowing */ +#define BWFM_STA_PS 0x00000100 /* STA in power save mode, says AP */ +#define BWFM_STA_APSD_BE 0x00000200 /* APSD for AC_BE default enabled */ +#define BWFM_STA_APSD_BK 0x00000400 /* APSD for AC_BK default enabled */ +#define BWFM_STA_APSD_VI 0x00000800 /* APSD for AC_VI default enabled */ +#define BWFM_STA_APSD_VO 0x00001000 /* APSD for AC_VO default enabled */ +#define BWFM_STA_N_CAP 0x00002000 /* STA 802.11n capable */ +#define BWFM_STA_SCBSTATS 0x00004000 /* Per STA debug stats */ +#define BWFM_STA_AMPDU_CAP 0x00008000 /* STA AMPDU capable */ +#define BWFM_STA_AMSDU_CAP 0x00010000 /* STA AMSDU capable */ +#define BWFM_STA_MIMO_PS 0x00020000 /* mimo ps mode is enabled */ +#define BWFM_STA_MIMO_RTS 0x00040000 /* send rts in mimo ps mode */ +#define BWFM_STA_RIFS_CAP 0x00080000 /* rifs enabled */ +#define BWFM_STA_VHT_CAP 0x00100000 /* STA VHT(11ac) capable */ +#define BWFM_STA_WPS 0x00200000 /* WPS state */ +#define BWFM_STA_DWDS_CAP 0x01000000 /* DWDS CAP */ +#define BWFM_STA_DWDS 0x02000000 /* DWDS active */ + + uint32_t idle; /* time since data pkt rx'd from sta */ + uint8_t ea[ETHER_ADDR_LEN]; + uint32_t count; /* # rates in this set */ + uint8_t rates[BWFM_MAXRATES_IN_SET]; /* rates in 500kbps units */ + /* w/hi bit set if basic */ + uint32_t in; /* seconds elapsed since associated */ + uint32_t listen_interval_inms; /* Min Listen interval in ms for STA */ + + /* Fields valid for ver >= 3 */ + uint32_t tx_pkts; /* # of packets transmitted */ + uint32_t tx_failures; /* # of packets failed */ + uint32_t rx_ucast_pkts; /* # of unicast packets received */ + uint32_t rx_mcast_pkts; /* # of multicast packets received */ + uint32_t tx_rate; /* Rate of last successful tx frame, in bps */ + uint32_t rx_rate; /* Rate of last successful rx frame, in bps */ + uint32_t rx_decrypt_succeeds; /* # of packet decrypted successfully */ + uint32_t rx_decrypt_failures; /* # of packet decrypted failed */ + + /* Fields valid for ver >= 4 */ + uint32_t tx_tot_pkts; /* # of tx pkts (ucast + mcast) */ + uint32_t rx_tot_pkts; /* # of data packets recvd (uni + mcast) */ + uint32_t tx_mcast_pkts; /* # of mcast pkts txed */ + uint64_t tx_tot_bytes; /* data bytes txed (ucast + mcast) */ + uint64_t rx_tot_bytes; /* data bytes recvd (ucast + mcast) */ + uint64_t tx_ucast_bytes; /* data bytes txed (ucast) */ + uint64_t tx_mcast_bytes; /* # data bytes txed (mcast) */ + uint64_t rx_ucast_bytes; /* data bytes recvd (ucast) */ + uint64_t rx_mcast_bytes; /* data bytes recvd (mcast) */ + int8_t rssi[BWFM_ANT_MAX]; /* per antenna rssi */ + int8_t nf[BWFM_ANT_MAX]; /* per antenna noise floor */ + uint16_t aid; /* association ID */ + uint16_t ht_capabilities; /* advertised ht caps */ + uint16_t vht_flags; /* converted vht flags */ + uint32_t tx_pkts_retry_cnt; /* # of frames where a retry was + * exhausted. + */ + uint32_t tx_pkts_retry_exhausted; /* # of user frames where a retry + * was exhausted + */ + int8_t rx_lastpkt_rssi[BWFM_ANT_MAX]; /* Per antenna RSSI of last + * received data frame. + */ + /* TX WLAN retry/failure statistics: + * Separated for host requested frames and locally generated frames. + * Include unicast frame only where the retries/failures can be counted. + */ + uint32_t tx_pkts_total; /* # user frames sent successfully */ + uint32_t tx_pkts_retries; /* # user frames retries */ + uint32_t tx_pkts_fw_total; /* # FW generated sent successfully */ + uint32_t tx_pkts_fw_retries; /* # retries for FW generated frames */ + uint32_t tx_pkts_fw_retry_exhausted; /* # FW generated where a retry + * was exhausted + */ + uint32_t rx_pkts_retried; /* # rx with retry bit set */ + uint32_t tx_rate_fallback; /* lowest fallback TX rate */ + + union { + struct { + struct bwfm_sta_rateset_v5 rateset_adv; + } v5; + + struct { + uint32_t rx_dur_total; /* user RX duration (estimate) */ + uint16_t chanspec; + uint16_t pad_1; + struct bwfm_sta_rateset_v7 rateset_adv; + uint16_t wpauth; /* authentication type */ + uint8_t algo; /* crypto alogorithm */ + uint8_t pad_2; + uint32_t tx_rspec;/* Rate of last successful tx frame */ + uint32_t rx_rspec;/* Rate of last successful rx frame */ + uint32_t wnm_cap; + } v7; + }; +}; + struct bwfm_ssid { uint32_t len; uint8_t ssid[BWFM_MAX_SSID_LEN]; @@ -441,6 +577,7 @@ struct bwfm_escan_results { struct bwfm_assoc_params { uint8_t bssid[ETHER_ADDR_LEN]; + uint16_t pad; uint32_t chanspec_num; uint16_t chanspec_list[]; }; @@ -468,6 +605,7 @@ struct bwfm_join_params { struct bwfm_join_scan_params { uint8_t scan_type; + uint8_t pad[3]; uint32_t nprobes; uint32_t active_time; uint32_t passive_time; @@ -502,15 +640,18 @@ struct bwfm_wsec_key { #define BWFM_CRYPTO_ALGO_AES_RESERVED1 5 #define BWFM_CRYPTO_ALGO_AES_RESERVED2 6 uint32_t flags; -#define BWFM_PRIMARY_KEY (1 << 1) +#define BWFM_WSEC_PRIMARY_KEY (1 << 1) +#define BWFM_PRIMARY_KEY BWFM_WSEC_PRIMARY_KEY uint32_t pad_2[3]; uint32_t iv_initialized; uint32_t pad_3; + /* Rx IV */ struct { uint32_t hi; uint16_t lo; + uint16_t pad_4; } rxiv; - uint32_t pad_4[2]; + uint32_t pad_5[2]; uint8_t ea[IEEE80211_ADDR_LEN]; }; @@ -622,7 +763,7 @@ struct bwfm_ethhdr { #define BWFM_BRCM_OUI "\x00\x10\x18" uint16_t usr_subtype; #define BWFM_BRCM_SUBTYPE_EVENT 1 -}; +} __packed; struct bwfm_event_msg { uint16_t version; @@ -636,7 +777,7 @@ struct bwfm_event_msg { char ifname[IFNAMSIZ]; uint8_t ifidx; uint8_t bsscfgidx; -}; +} __packed; struct bwfm_event { struct ether_header ehdr; Index: src/sys/dev/ic/bwfmvar.h diff -u src/sys/dev/ic/bwfmvar.h:1.3 src/sys/dev/ic/bwfmvar.h:1.3.6.1 --- src/sys/dev/ic/bwfmvar.h:1.3 Sat Sep 1 22:01:03 2018 +++ src/sys/dev/ic/bwfmvar.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: bwfmvar.h,v 1.3 2018/09/01 22:01:03 riastradh Exp $ */ +/* $NetBSD: bwfmvar.h,v 1.3.6.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: bwfmvar.h,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation @@ -57,7 +57,7 @@ #define BWFM_DEFAULT_SCAN_UNASSOC_TIME 40 #define BWFM_DEFAULT_SCAN_PASSIVE_TIME 120 -#define BWFM_TASK_COUNT 32 +#define BWFM_TASK_COUNT 256 struct bwfm_softc; @@ -119,6 +119,7 @@ enum bwfm_task_cmd { BWFM_TASK_NEWSTATE, BWFM_TASK_KEY_SET, BWFM_TASK_KEY_DELETE, + BWFM_TASK_RX_EVENT, }; struct bwfm_cmd_newstate { @@ -138,9 +139,11 @@ struct bwfm_task { union { struct bwfm_cmd_newstate newstate; struct bwfm_cmd_key key; + struct mbuf *mbuf; } t_u; #define t_newstate t_u.newstate #define t_key t_u.key +#define t_mbuf t_u.mbuf }; struct bwfm_softc { @@ -166,6 +169,8 @@ struct bwfm_softc { int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int); + + int sc_bcdc_reqid; }; void bwfm_attach(struct bwfm_softc *); @@ -178,6 +183,7 @@ int bwfm_detach(struct bwfm_softc *, int int bwfm_chip_attach(struct bwfm_softc *); int bwfm_chip_set_active(struct bwfm_softc *, uint32_t); void bwfm_chip_set_passive(struct bwfm_softc *); +int bwfm_chip_sr_capable(struct bwfm_softc *); struct bwfm_core *bwfm_chip_get_core(struct bwfm_softc *, int); struct bwfm_core *bwfm_chip_get_pmu(struct bwfm_softc *); void bwfm_rx(struct bwfm_softc *, struct mbuf *m); Index: src/sys/dev/ic/dwc_mmc.c diff -u src/sys/dev/ic/dwc_mmc.c:1.17.2.1 src/sys/dev/ic/dwc_mmc.c:1.17.2.2 --- src/sys/dev/ic/dwc_mmc.c:1.17.2.1 Tue Jan 21 10:39:58 2020 +++ src/sys/dev/ic/dwc_mmc.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc.c,v 1.17.2.1 2020/01/21 10:39:58 martin Exp $ */ +/* $NetBSD: dwc_mmc.c,v 1.17.2.2 2020/02/25 18:40:43 martin Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.17.2.1 2020/01/21 10:39:58 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v 1.17.2.2 2020/02/25 18:40:43 martin Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -35,6 +35,7 @@ __KERNEL_RCSID(0, "$NetBSD: dwc_mmc.c,v #include <sys/intr.h> #include <sys/systm.h> #include <sys/kernel.h> +#include <sys/proc.h> #include <dev/sdmmc/sdmmcvar.h> #include <dev/sdmmc/sdmmcchip.h> @@ -296,6 +297,11 @@ dwc_mmc_write_protect(sdmmc_chipset_hand static int dwc_mmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) { + struct dwc_mmc_softc *sc = sch; + + if (ocr == 0) + sc->sc_card_inited = false; + return 0; } @@ -532,7 +538,6 @@ dwc_mmc_dma_prepare(struct dwc_mmc_softc val |= DWC_MMC_GCTRL_DMARESET; MMC_WRITE(sc, DWC_MMC_GCTRL, val); - MMC_WRITE(sc, DWC_MMC_DMAC, DWC_MMC_DMAC_SOFTRESET); if (cmd->c_flags & SCF_CMD_READ) val = DWC_MMC_IDST_RECEIVE_INT; else @@ -573,6 +578,7 @@ dwc_mmc_exec_command(sdmmc_chipset_handl uint32_t cmdval = DWC_MMC_CMD_START; int retry, error; uint32_t imask; + u_int reg; #ifdef DWC_MMC_DEBUG aprint_normal_dev(sc->sc_dev, @@ -581,24 +587,41 @@ dwc_mmc_exec_command(sdmmc_chipset_handl cmd->c_blklen); #endif - mutex_enter(&sc->sc_intr_lock); + mutex_enter(&sc->sc_lock); if (sc->sc_curcmd != NULL) { device_printf(sc->sc_dev, "WARNING: driver submitted a command while the controller was busy\n"); cmd->c_error = EBUSY; SET(cmd->c_flags, SCF_ITSDONE); - mutex_exit(&sc->sc_intr_lock); + mutex_exit(&sc->sc_lock); return; } sc->sc_curcmd = cmd; MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); + if (!sc->sc_card_inited) { + cmdval |= DWC_MMC_CMD_SEND_INIT_SEQ; + sc->sc_card_inited = true; + } + if (ISSET(sc->sc_flags, DWC_MMC_F_USE_HOLD_REG)) cmdval |= DWC_MMC_CMD_USE_HOLD_REG; - if (cmd->c_opcode == 0) - cmdval |= DWC_MMC_CMD_SEND_INIT_SEQ; + switch (cmd->c_opcode) { + case SD_IO_RW_DIRECT: + reg = (cmd->c_arg >> SD_ARG_CMD52_REG_SHIFT) & + SD_ARG_CMD52_REG_MASK; + if (reg != 0x6) /* func abort / card reset */ + break; + /* FALLTHROUGH */ + case MMC_GO_IDLE_STATE: + case MMC_STOP_TRANSMISSION: + case MMC_INACTIVE_STATE: + cmdval |= DWC_MMC_CMD_STOP_ABORT_CMD; + break; + } + if (cmd->c_flags & SCF_RSP_PRESENT) cmdval |= DWC_MMC_CMD_RSP_EXP; if (cmd->c_flags & SCF_RSP_136) @@ -611,6 +634,14 @@ dwc_mmc_exec_command(sdmmc_chipset_handl if (cmd->c_datalen > 0) { unsigned int nblks; + MMC_WRITE(sc, DWC_MMC_GCTRL, + MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET); + for (retry = 0; retry < 100000; retry++) { + if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET)) + break; + delay(1); + } + cmdval |= DWC_MMC_CMD_DATA_EXP | DWC_MMC_CMD_WAIT_PRE_OVER; if (!ISSET(cmd->c_flags, SCF_CMD_READ)) { cmdval |= DWC_MMC_CMD_WRITE; @@ -620,15 +651,22 @@ dwc_mmc_exec_command(sdmmc_chipset_handl if (nblks == 0 || (cmd->c_datalen % cmd->c_blklen) != 0) ++nblks; - if (nblks > 1) { + if (nblks > 1 && cmd->c_opcode != SD_IO_RW_EXTENDED) { cmdval |= DWC_MMC_CMD_SEND_AUTO_STOP; imask |= DWC_MMC_INT_AUTO_CMD_DONE; } else { imask |= DWC_MMC_INT_DATA_OVER; } + MMC_WRITE(sc, DWC_MMC_TIMEOUT, 0xffffffff); MMC_WRITE(sc, DWC_MMC_BLKSZ, cmd->c_blklen); - MMC_WRITE(sc, DWC_MMC_BYTECNT, nblks * cmd->c_blklen); + MMC_WRITE(sc, DWC_MMC_BYTECNT, + nblks > 1 ? nblks * cmd->c_blklen : cmd->c_datalen); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + MMC_WRITE(sc, DWC_MMC_CARDTHRCTL, + __SHIFTIN(cmd->c_blklen, DWC_MMC_CARDTHRCTL_RDTHR) | + DWC_MMC_CARDTHRCTL_RDTHREN); + } } MMC_WRITE(sc, DWC_MMC_IMASK, imask | sc->sc_intr_card); @@ -656,6 +694,16 @@ dwc_mmc_exec_command(sdmmc_chipset_handl } sc->sc_wait_cmd = true; + if ((cmdval & DWC_MMC_CMD_WAIT_PRE_OVER) != 0) { + for (retry = 0; retry < 10000; retry++) { + if (!(MMC_READ(sc, DWC_MMC_STATUS) & DWC_MMC_STATUS_CARD_DATA_BUSY)) + break; + delay(1); + } + } + + mutex_enter(&sc->sc_intr_lock); + MMC_WRITE(sc, DWC_MMC_CMD, cmdval | cmd->c_opcode); if (sc->sc_wait_dma) @@ -673,6 +721,8 @@ dwc_mmc_exec_command(sdmmc_chipset_handl } } + mutex_exit(&sc->sc_intr_lock); + if (cmd->c_error == 0 && cmd->c_datalen > 0) dwc_mmc_dma_complete(sc, cmd); @@ -705,26 +755,21 @@ done: MMC_WRITE(sc, DWC_MMC_IDIE, 0); MMC_WRITE(sc, DWC_MMC_RINT, 0x7fff); MMC_WRITE(sc, DWC_MMC_IDST, 0xffffffff); - sc->sc_curcmd = NULL; - mutex_exit(&sc->sc_intr_lock); if (cmd->c_error) { #ifdef DWC_MMC_DEBUG aprint_error_dev(sc->sc_dev, "i/o error %d\n", cmd->c_error); #endif - MMC_WRITE(sc, DWC_MMC_GCTRL, - MMC_READ(sc, DWC_MMC_GCTRL) | - DWC_MMC_GCTRL_DMARESET | DWC_MMC_GCTRL_FIFORESET); - for (retry = 0; retry < 1000; retry++) { - if (!(MMC_READ(sc, DWC_MMC_GCTRL) & DWC_MMC_GCTRL_RESET)) + MMC_WRITE(sc, DWC_MMC_DMAC, DWC_MMC_DMAC_SOFTRESET); + for (retry = 0; retry < 100; retry++) { + if (!(MMC_READ(sc, DWC_MMC_DMAC) & DWC_MMC_DMAC_SOFTRESET)) break; - delay(10); + kpause("dwcmmcrst", false, uimax(mstohz(1), 1), &sc->sc_lock); } - dwc_mmc_update_clock(sc); } - MMC_WRITE(sc, DWC_MMC_GCTRL, - MMC_READ(sc, DWC_MMC_GCTRL) | DWC_MMC_GCTRL_FIFORESET); + sc->sc_curcmd = NULL; + mutex_exit(&sc->sc_lock); } static void @@ -736,10 +781,10 @@ dwc_mmc_card_enable_intr(sdmmc_chipset_h mutex_enter(&sc->sc_intr_lock); imask = MMC_READ(sc, DWC_MMC_IMASK); if (enable) - imask |= DWC_MMC_INT_SDIO_INT; + imask |= sc->sc_intr_cardmask; else - imask &= ~DWC_MMC_INT_SDIO_INT; - sc->sc_intr_card = imask & DWC_MMC_INT_SDIO_INT; + imask &= ~sc->sc_intr_cardmask; + sc->sc_intr_card = imask & sc->sc_intr_cardmask; MMC_WRITE(sc, DWC_MMC_IMASK, imask); mutex_exit(&sc->sc_intr_lock); } @@ -776,6 +821,10 @@ dwc_mmc_init(struct dwc_mmc_softc *sc) sc->sc_fifo_depth = __SHIFTOUT(val, DWC_MMC_FIFOTH_RX_WMARK) + 1; } + if (sc->sc_intr_cardmask == 0) + sc->sc_intr_cardmask = DWC_MMC_INT_SDIO_INT(0); + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO); cv_init(&sc->sc_intr_cv, "dwcmmcirq"); @@ -815,9 +864,9 @@ dwc_mmc_intr(void *priv) #endif /* Handle SDIO card interrupt */ - if ((mint & DWC_MMC_INT_SDIO_INT) != 0) { + if ((mint & sc->sc_intr_cardmask) != 0) { imask = MMC_READ(sc, DWC_MMC_IMASK); - MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~DWC_MMC_INT_SDIO_INT); + MMC_WRITE(sc, DWC_MMC_IMASK, imask & ~sc->sc_intr_cardmask); sdmmc_card_intr(sc->sc_sdmmc_dev); } Index: src/sys/dev/ic/dwc_mmc_reg.h diff -u src/sys/dev/ic/dwc_mmc_reg.h:1.7 src/sys/dev/ic/dwc_mmc_reg.h:1.7.8.1 --- src/sys/dev/ic/dwc_mmc_reg.h:1.7 Sat Jun 16 00:15:40 2018 +++ src/sys/dev/ic/dwc_mmc_reg.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc_reg.h,v 1.7 2018/06/16 00:15:40 jmcneill Exp $ */ +/* $NetBSD: dwc_mmc_reg.h,v 1.7.8.1 2020/02/25 18:40:43 martin Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -67,6 +67,7 @@ #define DWC_MMC_IDIE 0x0090 #define DWC_MMC_DSCADDR 0x0094 #define DWC_MMC_BUFADDR 0x0098 +#define DWC_MMC_CARDTHRCTL 0x0100 #define DWC_MMC_GCTRL_USE_INTERNAL_DMAC __BIT(25) #define DWC_MMC_GCTRL_SEND_AUTO_STOP_CCSD __BIT(10) @@ -107,7 +108,7 @@ #define DWC_MMC_INT_CARD_REMOVE __BIT(31) #define DWC_MMC_INT_CARD_INSERT __BIT(30) -#define DWC_MMC_INT_SDIO_INT __BIT(16) +#define DWC_MMC_INT_SDIO_INT(n) __BIT(16 + (n)) #define DWC_MMC_INT_END_BIT_ERR __BIT(15) #define DWC_MMC_INT_AUTO_CMD_DONE __BIT(14) #define DWC_MMC_INT_START_BIT_ERR __BIT(13) @@ -167,6 +168,9 @@ #define DWC_MMC_IDST_COMPLETE \ (DWC_MMC_IDST_RECEIVE_INT | DWC_MMC_IDST_TRANSMIT_INT) +#define DWC_MMC_CARDTHRCTL_RDTHR __BITS(27,16) +#define DWC_MMC_CARDTHRCTL_RDTHREN __BIT(0) + struct dwc_mmc_idma_desc { uint32_t dma_config; #define DWC_MMC_IDMA_CONFIG_DIC __BIT(1) Index: src/sys/dev/ic/dwc_mmc_var.h diff -u src/sys/dev/ic/dwc_mmc_var.h:1.8.2.1 src/sys/dev/ic/dwc_mmc_var.h:1.8.2.2 --- src/sys/dev/ic/dwc_mmc_var.h:1.8.2.1 Tue Jan 21 10:39:58 2020 +++ src/sys/dev/ic/dwc_mmc_var.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: dwc_mmc_var.h,v 1.8.2.1 2020/01/21 10:39:58 martin Exp $ */ +/* $NetBSD: dwc_mmc_var.h,v 1.8.2.2 2020/02/25 18:40:43 martin Exp $ */ /*- * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca> @@ -44,8 +44,10 @@ struct dwc_mmc_softc { uint32_t sc_fifo_depth; u_int sc_clock_freq; u_int sc_bus_width; + bool sc_card_inited; void *sc_ih; + kmutex_t sc_lock; kmutex_t sc_intr_lock; kcondvar_t sc_intr_cv; @@ -69,6 +71,7 @@ struct dwc_mmc_softc { size_t sc_dmabounce_buflen; uint32_t sc_intr_card; + uint32_t sc_intr_cardmask; struct sdmmc_command *sc_curcmd; bool sc_wait_dma; bool sc_wait_cmd; Index: src/sys/dev/pcmcia/pcmciareg.h diff -u src/sys/dev/pcmcia/pcmciareg.h:1.10 src/sys/dev/pcmcia/pcmciareg.h:1.10.170.1 --- src/sys/dev/pcmcia/pcmciareg.h:1.10 Sun Dec 11 12:23:23 2005 +++ src/sys/dev/pcmcia/pcmciareg.h Tue Feb 25 18:40:44 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: pcmciareg.h,v 1.10 2005/12/11 12:23:23 christos Exp $ */ +/* $NetBSD: pcmciareg.h,v 1.10.170.1 2020/02/25 18:40:44 martin Exp $ */ /* * Copyright (c) 1997 Marc Horowitz. All rights reserved. @@ -202,6 +202,7 @@ /* #define PCMCIA_CISTPL_RESERVED 0x80-0x8F */ #define PCMCIA_CISTPL_SPCL 0x90 +#define PCMCIA_CISTPL_SDIO 0x91 /* #define PCMCIA_CISTPL_RESERVED 0x90-0xFE */ /* Index: src/sys/dev/sdmmc/if_bwfm_sdio.c diff -u src/sys/dev/sdmmc/if_bwfm_sdio.c:1.3 src/sys/dev/sdmmc/if_bwfm_sdio.c:1.3.8.1 --- src/sys/dev/sdmmc/if_bwfm_sdio.c:1.3 Fri May 11 07:41:11 2018 +++ src/sys/dev/sdmmc/if_bwfm_sdio.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: if_bwfm_sdio.c,v 1.3 2018/05/11 07:41:11 maya Exp $ */ +/* $NetBSD: if_bwfm_sdio.c,v 1.3.8.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: if_bwfm_sdio.c,v 1.1 2017/10/11 17:19:50 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation @@ -20,14 +20,13 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/buf.h> +#include <sys/endian.h> #include <sys/kernel.h> #include <sys/malloc.h> #include <sys/device.h> #include <sys/queue.h> #include <sys/socket.h> #include <sys/mutex.h> -#include <sys/workqueue.h> -#include <sys/pcq.h> #include <net/bpf.h> #include <net/if.h> @@ -37,20 +36,19 @@ #include <netinet/in.h> +#include <dev/ofw/openfirm.h> +#include <dev/fdt/fdtvar.h> + +#include <dev/firmload.h> + #include <net80211/ieee80211_var.h> +#include <dev/sdmmc/sdmmcdevs.h> #include <dev/sdmmc/sdmmcvar.h> #include <dev/ic/bwfmvar.h> #include <dev/ic/bwfmreg.h> - -#define BWFM_SDIO_CCCR_BRCM_CARDCAP 0xf0 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT 0x02 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT 0x04 -#define BWFM_SDIO_CCCR_BRCM_CARDCAP_CMD_NODEC 0x08 -#define BWFM_SDIO_CCCR_BRCM_CARDCTRL 0xf1 -#define BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET 0x02 -#define BWFM_SDIO_CCCR_BRCM_SEPINT 0xf2 +#include <dev/sdmmc/if_bwfm_sdio.h> #ifdef BWFM_DEBUG #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) @@ -63,34 +61,132 @@ static int bwfm_debug = 2; #define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) +enum bwfm_sdio_clkstate { + CLK_NONE, + CLK_SDONLY, + CLK_PENDING, + CLK_AVAIL +}; + struct bwfm_sdio_softc { - struct bwfm_softc sc_sc; + struct bwfm_softc sc_sc; + kmutex_t sc_lock; + kmutex_t sc_intr_lock; + + bool sc_bwfm_attached; + struct sdmmc_function **sc_sf; - uint32_t sc_bar0; + size_t sc_sf_size; + + uint32_t sc_bar0; + enum bwfm_sdio_clkstate sc_clkstate; + bool sc_sr_enabled; + bool sc_alp_only; + bool sc_sleeping; + bool sc_rxskip; + + struct sdmmc_task sc_task; + bool sc_task_queued; + + uint8_t sc_tx_seq; + uint8_t sc_tx_max_seq; + int sc_tx_count; + MBUFQ_HEAD() sc_tx_queue; + + struct mbuf *sc_rxctl_queue; + kcondvar_t sc_rxctl_cv; + + void *sc_ih; + struct bwfm_core *sc_cc; + + char *sc_bounce_buf; + size_t sc_bounce_size; + + uint32_t sc_console_addr; + char *sc_console_buf; + size_t sc_console_buf_size; + uint32_t sc_console_readidx; + + int sc_phandle; + void *sc_fdtih; }; -int bwfm_sdio_match(device_t, cfdata_t, void *); -void bwfm_sdio_attach(device_t, struct device *, void *); -int bwfm_sdio_detach(device_t, int); - -void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); -uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); -uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); -void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, +static int bwfm_sdio_match(device_t, cfdata_t, void *); +static void bwfm_sdio_attach(device_t, device_t, void *); +static int bwfm_sdio_detach(device_t, int); +static void bwfm_sdio_attachhook(device_t); +static int bwfm_fdt_find_phandle(device_t, device_t); +static const char *bwfm_fdt_get_model(void); + +static void bwfm_sdio_backplane(struct bwfm_sdio_softc *, uint32_t); +static uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *, uint32_t); +static uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *, uint32_t); +static void bwfm_sdio_write_1(struct bwfm_sdio_softc *, uint32_t, uint8_t); -void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, +static void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, + uint32_t); + +static uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); +static void bwfm_sdio_dev_write(struct bwfm_sdio_softc *, uint32_t, uint32_t); -uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); -void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, +static uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); +static void bwfm_sdio_buscore_write(struct bwfm_softc *, uint32_t, uint32_t); -int bwfm_sdio_buscore_prepare(struct bwfm_softc *); -void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); +static int bwfm_sdio_buscore_prepare(struct bwfm_softc *); +static void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); + +static int bwfm_sdio_buf_read(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); +static int bwfm_sdio_buf_write(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); + +static struct mbuf *bwfm_sdio_newbuf(void); +static void bwfm_qput(struct mbuf **, struct mbuf *); +static struct mbuf *bwfm_qget(struct mbuf **); + +static uint32_t bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *, + uint32_t, char *, size_t, int); +static uint32_t bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *, + char *, size_t, int); + +static int bwfm_sdio_intr1(void *, const char *); +static int bwfm_sdio_intr(void *); +static void bwfm_sdio_task(void *); +static void bwfm_sdio_task1(struct bwfm_sdio_softc *); + +static int bwfm_nvram_convert(u_char *, size_t, size_t *); +static int bwfm_sdio_load_microcode(struct bwfm_sdio_softc *, + u_char *, size_t, u_char *, size_t); +static void bwfm_sdio_clkctl(struct bwfm_sdio_softc *, + enum bwfm_sdio_clkstate, bool); +static void bwfm_sdio_htclk(struct bwfm_sdio_softc *, bool, bool); -int bwfm_sdio_txcheck(struct bwfm_softc *); -int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf *); -int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); -int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); +#ifdef notyet +static int bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *, bool, bool); +#endif +static void bwfm_sdio_drivestrength(struct bwfm_sdio_softc *, unsigned); +static void bwfm_sdio_readshared(struct bwfm_sdio_softc *); + +static int bwfm_sdio_txcheck(struct bwfm_softc *); +static int bwfm_sdio_txdata(struct bwfm_softc *, struct mbuf **); +static int bwfm_sdio_txctl(struct bwfm_softc *, char *, size_t); +static int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); + +static int bwfm_sdio_tx_ok(struct bwfm_sdio_softc *); +static void bwfm_sdio_tx_frames(struct bwfm_sdio_softc *); +static void bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *, + struct mbuf *); +static void bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *, + struct mbuf *); + +static void bwfm_sdio_rx_frames(struct bwfm_sdio_softc *); +static void bwfm_sdio_rx_glom(struct bwfm_sdio_softc *, + uint16_t *, int, uint16_t *); + +#ifdef BWFM_DEBUG +static void bwfm_sdio_debug_console(struct bwfm_sdio_softc *); +#endif struct bwfm_bus_ops bwfm_sdio_bus_ops = { .bs_init = NULL, @@ -113,21 +209,64 @@ struct bwfm_buscore_ops bwfm_sdio_buscor CFATTACH_DECL_NEW(bwfm_sdio, sizeof(struct bwfm_sdio_softc), bwfm_sdio_match, bwfm_sdio_attach, bwfm_sdio_detach, NULL); -int +static const struct bwfm_sdio_product { + uint32_t manufacturer; + uint32_t product; + const char *cisinfo[4]; +} bwfm_sdio_products[] = { + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM4330, + SDMMC_CIS_BROADCOM_BCM4330 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM4334, + SDMMC_CIS_BROADCOM_BCM4334 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM43143, + SDMMC_CIS_BROADCOM_BCM43143 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM43430, + SDMMC_CIS_BROADCOM_BCM43430 + }, + { + SDMMC_VENDOR_BROADCOM, + SDMMC_PRODUCT_BROADCOM_BCM43455, + SDMMC_CIS_BROADCOM_BCM43455 + }, +}; + +static const char *compatible[] = { + "brcm,bcm4329-fmac", + NULL +}; + +static int bwfm_sdio_match(device_t parent, cfdata_t match, void *aux) { struct sdmmc_attach_args *saa = aux; struct sdmmc_function *sf = saa->sf; struct sdmmc_cis *cis; + const struct bwfm_sdio_product *bsp; + int i; /* Not SDIO. */ if (sf == NULL) return 0; - /* Look for Broadcom 433[04]. */ cis = &sf->sc->sc_fn0->cis; - if (cis->manufacturer != 0x02d0 || (cis->product != 0x4330 && - cis->product != 0x4334)) + for (i = 0; i < __arraycount(bwfm_sdio_products); ++i) { + bsp = &bwfm_sdio_products[i]; + if (cis->manufacturer == bsp->manufacturer && + cis->product == bsp->product) + break; + } + if (i >= __arraycount(bwfm_sdio_products)) return 0; /* We need both functions, but ... */ @@ -141,39 +280,54 @@ bwfm_sdio_match(device_t parent, cfdata_ return 1; } -void +static void bwfm_sdio_attach(device_t parent, device_t self, void *aux) { struct bwfm_sdio_softc *sc = device_private(self); struct sdmmc_attach_args *saa = aux; struct sdmmc_function *sf = saa->sf; struct bwfm_core *core; + uint32_t reg; + + sc->sc_sc.sc_dev = self; aprint_naive("\n"); + aprint_normal("\n"); - sc->sc_sf = malloc((sf->sc->sc_function_count + 1) * - sizeof(struct sdmmc_function *), M_DEVBUF, M_WAITOK); + sc->sc_phandle = bwfm_fdt_find_phandle(self, parent); + + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + cv_init(&sc->sc_rxctl_cv, "bwfmctl"); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); + + sdmmc_init_task(&sc->sc_task, bwfm_sdio_task, sc); + sc->sc_task_queued = false; + + sc->sc_bounce_size = 64 * 1024; + sc->sc_bounce_buf = kmem_alloc(sc->sc_bounce_size, KM_SLEEP); + sc->sc_tx_seq = 0xff; + MBUFQ_INIT(&sc->sc_tx_queue); + sc->sc_rxctl_queue = NULL; + + sc->sc_sf_size = (sf->sc->sc_function_count + 1) + * sizeof(struct sdmmc_function *); + sc->sc_sf = kmem_zalloc(sc->sc_sf_size, KM_SLEEP); /* Copy all function pointers. */ SIMPLEQ_FOREACH(sf, &saa->sf->sc->sf_head, sf_list) { sc->sc_sf[sf->number] = sf; } - sf = saa->sf; - /* - * TODO: set block size to 64 for func 1, 512 for func 2. - * We might need to work on the SDMMC stack to be able to set - * a block size per function. Currently the IO code uses the - * SDHC controller's maximum block length. - */ + sdmmc_io_set_blocklen(sc->sc_sf[1], 64); + sdmmc_io_set_blocklen(sc->sc_sf[2], 512); /* Enable Function 1. */ if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { - aprint_error_dev(self, "cannot enable function 1\n"); - goto err; + printf("%s: cannot enable function 1\n", DEVNAME(sc)); + return; } - DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), + DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), bwfm_sdio_read_4(sc, 0x18000000))); /* Force PLL off */ @@ -184,14 +338,30 @@ bwfm_sdio_attach(device_t parent, device sc->sc_sc.sc_buscore_ops = &bwfm_sdio_buscore_ops; if (bwfm_chip_attach(&sc->sc_sc) != 0) { aprint_error_dev(self, "cannot attach chip\n"); - goto err; + return; } - /* TODO: drive strength */ + sc->sc_cc = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_CHIPCOMMON); + if (sc->sc_cc == NULL) { + aprint_error_dev(self, "cannot find chipcommon core\n"); + return; + } - bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL, - bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_BRCM_CARDCTRL) | - BWFM_SDIO_CCCR_BRCM_CARDCTRL_WLANRESET); + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + if (core->co_rev >= 12) { + reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR); + if ((reg & BWFM_SDIO_FUNC1_SLEEPCSR_KSO) == 0) { + reg |= BWFM_SDIO_FUNC1_SLEEPCSR_KSO; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SLEEPCSR, reg); + } + } + + /* Default, override from "brcm,drive-strength" */ + bwfm_sdio_drivestrength(sc, 6); + + bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCTRL, + bwfm_sdio_read_1(sc, BWFM_SDIO_CCCR_CARDCTRL) | + BWFM_SDIO_CCCR_CARDCTRL_WLANRESET); core = bwfm_chip_get_pmu(&sc->sc_sc); bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_PMUCONTROL, @@ -199,36 +369,320 @@ bwfm_sdio_attach(device_t parent, device (BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD << BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT)); + sdmmc_io_function_disable(sc->sc_sf[2]); + + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); + sc->sc_clkstate = CLK_SDONLY; + + config_mountroot(self, bwfm_sdio_attachhook); +} + +static void +bwfm_sdio_attachhook(device_t self) +{ + struct bwfm_sdio_softc *sc = device_private(self); + struct bwfm_softc *bwfm = &sc->sc_sc; + firmware_handle_t fwh; + const char *name, *nvname, *model; + char *nvnamebuf; + u_char *ucode, *nvram; + size_t size, nvlen, nvsize; + uint32_t reg, clk; + int error; + + DPRINTF(("%s: chip 0x%08x rev %u\n", DEVNAME(sc), + bwfm->sc_chip.ch_chip, bwfm->sc_chip.ch_chiprev)); + switch (bwfm->sc_chip.ch_chip) { + case BRCM_CC_4330_CHIP_ID: + name = "brcmfmac4330-sdio.bin"; + nvname = "brcmfmac4330-sdio.txt"; + break; + case BRCM_CC_4334_CHIP_ID: + name = "brcmfmac4334-sdio.bin"; + nvname = "brcmfmac4334-sdio.txt"; + break; + case BRCM_CC_4345_CHIP_ID: + if ((0x200 & __BIT(bwfm->sc_chip.ch_chiprev)) != 0) { + name = "brcmfmac43456-sdio.bin"; + nvname = "brcmfmac43456-sdio.txt"; + } else { + name = "brcmfmac43455-sdio.bin"; + nvname = "brcmfmac43455-sdio.txt"; + } + break; + case BRCM_CC_43340_CHIP_ID: + name = "brcmfmac43340-sdio.bin"; + nvname = "brcmfmac43340-sdio.txt"; + break; + case BRCM_CC_4335_CHIP_ID: + if (bwfm->sc_chip.ch_chiprev < 2) { + name = "brcmfmac4335-sdio.bin"; + nvname = "brcmfmac4335-sdio.txt"; + } else { + name = "brcmfmac4339-sdio.bin"; + nvname = "brcmfmac4339-sdio.txt"; + bwfm->sc_chip.ch_chip = BRCM_CC_4339_CHIP_ID; + } + break; + case BRCM_CC_4339_CHIP_ID: + name = "brcmfmac4339-sdio.bin"; + nvname = "brcmfmac4339-sdio.txt"; + break; + case BRCM_CC_43430_CHIP_ID: + if (bwfm->sc_chip.ch_chiprev == 0) { + name = "brcmfmac43430a0-sdio.bin"; + nvname = "brcmfmac43430a0-sdio.txt"; + } else { + name = "brcmfmac43430-sdio.bin"; + nvname = "brcmfmac43430-sdio.txt"; + } + break; + case BRCM_CC_4356_CHIP_ID: + name = "brcmfmac4356-sdio.bin"; + nvname = "brcmfmac4356-sdio.txt"; + break; + default: + printf("%s: unknown firmware for chip %s\n", + DEVNAME(sc), bwfm->sc_chip.ch_name); + goto err; + } + + /* compute a model specific filename for the NV config */ + nvnamebuf = NULL; + model = bwfm_fdt_get_model(); + if (model != NULL) { + /* assume nvname ends in ".txt" */ + nvnamebuf = kmem_asprintf("%.*s.%s.txt", + (int)(strlen(nvname) - 4), + nvname, model); + } + + aprint_verbose_dev(self, "Firmware %s\n", name); + aprint_verbose_dev(self, "Default Config %s\n", nvname); + if (nvnamebuf != NULL) + aprint_verbose_dev(self, "Model Config %s\n", nvnamebuf); + + if (firmware_open("if_bwfm", name, &fwh) != 0) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + goto err; + } + size = firmware_get_size(fwh); + ucode = firmware_malloc(size); + if (ucode == NULL) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + firmware_close(fwh); + goto err; + } + error = firmware_read(fwh, 0, ucode, size); + firmware_close(fwh); + if (error != 0) { + printf("%s: failed to read firmware (error %d)\n", + DEVNAME(sc), error); + goto err1; + } + + if ((nvnamebuf == NULL || firmware_open("if_bwfm", nvnamebuf, &fwh) != 0) + && firmware_open("if_bwfm", nvname, &fwh) != 0) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), nvname); + goto err1; + } + nvlen = firmware_get_size(fwh); + nvram = firmware_malloc(nvlen); + if (nvram == NULL) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + firmware_close(fwh); + goto err1; + } + error = firmware_read(fwh, 0, nvram, nvlen); + firmware_close(fwh); + if (error != 0) { + printf("%s: failed to read firmware (error %d)\n", + DEVNAME(sc), error); + goto err2; + } + + if (bwfm_nvram_convert(nvram, nvlen, &nvsize)) { + printf("%s: failed to convert nvram\n", DEVNAME(sc)); + goto err2; + } + + sc->sc_alp_only = true; + if (bwfm_sdio_load_microcode(sc, ucode, size, nvram, nvsize) != 0) { + printf("%s: could not load microcode\n", + DEVNAME(sc)); + goto err2; + } + sc->sc_alp_only = false; + + firmware_free(nvram, nvlen); + firmware_free(ucode, size); + if (nvnamebuf != NULL) + kmem_free(nvnamebuf, strlen(nvnamebuf)+1); + + sdmmc_pause(hztoms(1)*1000, NULL); + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + if (sc->sc_clkstate != CLK_AVAIL) { + printf("%s: could not access clock\n", + DEVNAME(sc)); + goto err; + } + + clk = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, + clk | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); + + bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOXDATA, + SDPCM_PROT_VERSION << SDPCM_PROT_VERSION_SHIFT); + if (sdmmc_io_function_enable(sc->sc_sf[2])) { + printf("%s: cannot enable function 2\n", DEVNAME(sc)); + goto err; + } + +// bwfm_sdio_dev_write(sc, SDPCMD_HOSTINTMASK, +// SDPCMD_INTSTATUS_HMB_SW_MASK | SDPCMD_INTSTATUS_CHIPACTIVE); + bwfm_sdio_dev_write(sc, SDPCMD_HOSTINTMASK, 0xffffffff); + bwfm_sdio_write_1(sc, BWFM_SDIO_WATERMARK, 8); + + if (bwfm_chip_sr_capable(bwfm)) { + reg = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL); + reg |= BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_WAKEUPCTRL, reg); + bwfm_sdio_write_1(sc, BWFM_SDIO_CCCR_CARDCAP, + BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT | + BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT); + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, + BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT); + sc->sc_sr_enabled = 1; + } else { + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, clk); + } + +#ifdef notyet + if (sc->sc_phandle >= 0) { + sc->sc_fdtih = fdtbus_intr_establish(sc->sc_phandle, + 0, IPL_SDMMC, IST_LEVEL, bwfm_sdio_intr, sc); + } +#endif + if (sc->sc_fdtih != NULL) { + aprint_normal_dev(self, "enabling GPIO interrupt\n"); + } else { + sc->sc_ih = sdmmc_intr_establish(device_parent(self), + bwfm_sdio_intr, sc, DEVNAME(sc)); + } + + if (sc->sc_ih == NULL && sc->sc_fdtih == NULL) { + aprint_error_dev(self, "could not establish interrupt\n"); + bwfm_sdio_clkctl(sc, CLK_NONE, false); + return; + } + sdmmc_intr_enable(sc->sc_sf[1]); + + sdmmc_pause(100000, NULL); + sc->sc_sc.sc_bus_ops = &bwfm_sdio_bus_ops; sc->sc_sc.sc_proto_ops = &bwfm_proto_bcdc_ops; bwfm_attach(&sc->sc_sc); + sc->sc_bwfm_attached = true; return; +err2: + firmware_free(nvram, nvlen); +err1: + firmware_free(ucode, size); + if (nvnamebuf != NULL) + kmem_free(nvnamebuf, strlen(nvnamebuf)+1); err: - free(sc->sc_sf, M_DEVBUF); + return; } -int -bwfm_sdio_detach(struct device *self, int flags) +static int +bwfm_fdt_find_phandle(device_t self, device_t parent) +{ + prop_dictionary_t dict; + device_t dev; + const char *str; + int phandle; + + /* locate in FDT */ + dict = device_properties(self); + if (prop_dictionary_get_cstring_nocopy(dict, "fdt-path", &str)) { + /* search in FDT */ + phandle = OF_finddevice(str); + } else { + + /* parent parent is sdhc controller */ + dev = device_parent(parent); + if (dev == NULL) + return -1; + /* locate in FDT */ + dict = device_properties(dev); + if (!prop_dictionary_get_cstring_nocopy(dict, "fdt-path", &str)) + return -1; + + /* are we the only FDT child ? */ + phandle = OF_child(OF_finddevice(str)); + } + + if (!of_match_compatible(phandle, compatible)) + return -1; + + return phandle; +} + +static const char * +bwfm_fdt_get_model(void) +{ + int phandle; + + phandle = OF_finddevice("/"); + return fdtbus_get_string_index(phandle, "compatible", 0); +} + +static int +bwfm_sdio_detach(device_t self, int flags) { struct bwfm_sdio_softc *sc = (struct bwfm_sdio_softc *)self; - bwfm_detach(&sc->sc_sc, flags); +#ifdef BWFM_DEBUG + bwfm_sdio_debug_console(sc); +#endif + + if (sc->sc_ih || sc->sc_fdtih) { + sdmmc_intr_disable(sc->sc_sf[1]); + if (sc->sc_ih) + sdmmc_intr_disestablish(sc->sc_ih); + if (sc->sc_fdtih) + fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_fdtih); + } + if (sc->sc_bwfm_attached) + bwfm_detach(&sc->sc_sc, flags); + + kmem_free(sc->sc_sf, sc->sc_sf_size); + kmem_free(sc->sc_bounce_buf, sc->sc_bounce_size); - free(sc->sc_sf, M_DEVBUF); + mutex_destroy(&sc->sc_intr_lock); + cv_destroy(&sc->sc_rxctl_cv); + mutex_destroy(&sc->sc_lock); return 0; } -void -bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t bar0) +static void +bwfm_sdio_backplane(struct bwfm_sdio_softc *sc, uint32_t addr) { + uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; + if (sc->sc_bar0 == bar0) return; bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRLOW, - (bar0 >> 8) & 0x80); + (bar0 >> 8) & 0xff); bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRMID, (bar0 >> 16) & 0xff); bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SBADDRHIGH, @@ -236,7 +690,7 @@ bwfm_sdio_backplane(struct bwfm_sdio_sof sc->sc_bar0 = bar0; } -uint8_t +static uint8_t bwfm_sdio_read_1(struct bwfm_sdio_softc *sc, uint32_t addr) { struct sdmmc_function *sf; @@ -257,14 +711,13 @@ bwfm_sdio_read_1(struct bwfm_sdio_softc return rv; } -uint32_t +static uint32_t bwfm_sdio_read_4(struct bwfm_sdio_softc *sc, uint32_t addr) { struct sdmmc_function *sf; - uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; uint32_t rv; - bwfm_sdio_backplane(sc, bar0); + bwfm_sdio_backplane(sc, addr); addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; @@ -284,7 +737,7 @@ bwfm_sdio_read_4(struct bwfm_sdio_softc return rv; } -void +static void bwfm_sdio_write_1(struct bwfm_sdio_softc *sc, uint32_t addr, uint8_t data) { struct sdmmc_function *sf; @@ -303,13 +756,12 @@ bwfm_sdio_write_1(struct bwfm_sdio_softc sdmmc_io_write_1(sf, addr, data); } -void +static void bwfm_sdio_write_4(struct bwfm_sdio_softc *sc, uint32_t addr, uint32_t data) { struct sdmmc_function *sf; - uint32_t bar0 = addr & ~BWFM_SDIO_SB_OFT_ADDR_MASK; - bwfm_sdio_backplane(sc, bar0); + bwfm_sdio_backplane(sc, addr); addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; @@ -328,31 +780,163 @@ bwfm_sdio_write_4(struct bwfm_sdio_softc sdmmc_io_write_4(sf, addr, data); } -uint32_t +static int +bwfm_sdio_buf_read(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, + uint32_t reg, char *data, size_t size) +{ + int err; + + KASSERT(((vaddr_t)data & 0x3) == 0); + KASSERT((size & 0x3) == 0); + + if (sf == sc->sc_sf[1]) + err = sdmmc_io_read_region_1(sf, reg, data, size); + else + err = sdmmc_io_read_multi_1(sf, reg, data, size); + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +static int +bwfm_sdio_buf_write(struct bwfm_sdio_softc *sc, struct sdmmc_function *sf, + uint32_t reg, char *data, size_t size) +{ + int err; + + KASSERT(((vaddr_t)data & 0x3) == 0); + KASSERT((size & 0x3) == 0); + + err = sdmmc_io_write_region_1(sf, reg, data, size); + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +static uint32_t +bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *sc, uint32_t reg, + char *data, size_t left, int write) +{ + uint32_t sbaddr, sdaddr, off; + size_t size; + int err; + + err = off = 0; + while (left > 0) { + sbaddr = reg + off; + bwfm_sdio_backplane(sc, sbaddr); + + sdaddr = sbaddr & BWFM_SDIO_SB_OFT_ADDR_MASK; + size = ulmin(left, (BWFM_SDIO_SB_OFT_ADDR_PAGE - sdaddr)); + sdaddr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + if (write) { + memcpy(sc->sc_bounce_buf, data + off, size); + if (roundup(size, 4) != size) + memset(sc->sc_bounce_buf + size, 0, + roundup(size, 4) - size); + err = bwfm_sdio_buf_write(sc, sc->sc_sf[1], sdaddr, + sc->sc_bounce_buf, roundup(size, 4)); + } else { + err = bwfm_sdio_buf_read(sc, sc->sc_sf[1], sdaddr, + sc->sc_bounce_buf, roundup(size, 4)); + memcpy(data + off, sc->sc_bounce_buf, size); + } + if (err) + break; + + off += size; + left -= size; + } + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +static uint32_t +bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *sc, + char *data, size_t size, int write) +{ + uint32_t addr; + int err; + + addr = sc->sc_cc->co_base; + bwfm_sdio_backplane(sc, addr); + + addr &= BWFM_SDIO_SB_OFT_ADDR_MASK; + addr |= BWFM_SDIO_SB_ACCESS_2_4B_FLAG; + + if (write) + err = bwfm_sdio_buf_write(sc, sc->sc_sf[2], addr, data, size); + else + err = bwfm_sdio_buf_read(sc, sc->sc_sf[2], addr, data, size); + + if (err) + printf("%s: error %d\n", __func__, err); + + return err; +} + +static uint32_t +bwfm_sdio_dev_read(struct bwfm_sdio_softc *sc, uint32_t reg) +{ + struct bwfm_core *core; + uint32_t val; + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + val = bwfm_sdio_read_4(sc, core->co_base + reg); + /* TODO: Workaround for 4335/4339 */ + + return val; +} + +static void +bwfm_sdio_dev_write(struct bwfm_sdio_softc *sc, uint32_t reg, uint32_t val) +{ + struct bwfm_core *core; + + core = bwfm_chip_get_core(&sc->sc_sc, BWFM_AGENT_CORE_SDIO_DEV); + bwfm_sdio_write_4(sc, core->co_base + reg, val); +} + +static uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) { struct bwfm_sdio_softc *sc = (void *)bwfm; uint32_t val; + mutex_enter(&sc->sc_lock); val = bwfm_sdio_read_4(sc, reg); /* TODO: Workaround for 4335/4339 */ + mutex_exit(&sc->sc_lock); return val; } -void +static void bwfm_sdio_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) { struct bwfm_sdio_softc *sc = (void *)bwfm; + + mutex_enter(&sc->sc_lock); bwfm_sdio_write_4(sc, reg, val); + mutex_exit(&sc->sc_lock); } -int +static int bwfm_sdio_buscore_prepare(struct bwfm_softc *bwfm) { struct bwfm_sdio_softc *sc = (void *)bwfm; uint8_t clkval, clkset, clkmask; - int i; + int i, error = 0; + + mutex_enter(&sc->sc_lock); clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ | BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF; @@ -365,7 +949,8 @@ bwfm_sdio_buscore_prepare(struct bwfm_so if ((clkval & ~clkmask) != clkset) { printf("%s: wrote 0x%02x read 0x%02x\n", DEVNAME(sc), clkset, clkval); - return 1; + error = 1; + goto done; } for (i = 1000; i > 0; i--) { @@ -377,7 +962,8 @@ bwfm_sdio_buscore_prepare(struct bwfm_so if (i == 0) { printf("%s: timeout on ALPAV wait, clkval 0x%02x\n", DEVNAME(sc), clkval); - return 1; + error = 1; + goto done; } clkset = BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF | @@ -387,10 +973,13 @@ bwfm_sdio_buscore_prepare(struct bwfm_so bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_SDIOPULLUP, 0); - return 0; +done: + mutex_exit(&sc->sc_lock); + + return error; } -void +static void bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) { struct bwfm_sdio_softc *sc = (void *)bwfm; @@ -400,56 +989,1090 @@ bwfm_sdio_buscore_activate(struct bwfm_s bwfm_sdio_buscore_write(&sc->sc_sc, core->co_base + BWFM_SDPCMD_INTSTATUS, 0xFFFFFFFF); -#if notyet + mutex_enter(&sc->sc_lock); if (rstvec) - bwfm_sdio_ram_write(&sc->sc_sc, 0, &rstvec, sizeof(rstvec)); -#endif + bwfm_sdio_ram_read_write(sc, 0, (char *)&rstvec, + sizeof(rstvec), 1); + mutex_exit(&sc->sc_lock); } -int -bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m) +static struct mbuf * +bwfm_sdio_newbuf(void) { - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + struct mbuf *m; - return 0; + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return NULL; + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return NULL; + } + + m->m_len = m->m_pkthdr.len = MCLBYTES; + return m; } +static struct mbuf * +bwfm_qget(struct mbuf **q) +{ + struct mbuf *m = NULL; -int -bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) + if (*q != NULL) { + m = *q; + *q = m->m_next; + m->m_next = NULL; + } + + return m; +} + +static void +bwfm_qput(struct mbuf **q, struct mbuf *m) +{ + + if (*q == NULL) + *q = m; + else + m_cat(*q, m); +} + +static int +bwfm_sdio_txcheck(struct bwfm_softc *bwfm) { -#ifdef BWFM_DEBUG struct bwfm_sdio_softc *sc = (void *)bwfm; -#endif - int ret = 1; + int error = 0; + + mutex_enter(&sc->sc_lock); + if (sc->sc_tx_count >= 64) + error = ENOBUFS; + mutex_exit(&sc->sc_lock); + + return error; +} + + +static int +bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf **mp) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + + if (sc->sc_tx_count >= 64) { + printf("%s: tx count limit reached\n",DEVNAME(sc)); + return ENOBUFS; + } - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + mutex_enter(&sc->sc_lock); + sc->sc_tx_count++; + MBUFQ_ENQUEUE(&sc->sc_tx_queue, *mp); + mutex_exit(&sc->sc_lock); - return ret; + bwfm_sdio_intr1(sc, "sdio_txdata"); + + return 0; } -int +static int bwfm_sdio_txctl(struct bwfm_softc *bwfm, char *buf, size_t len) { -#ifdef BWFM_DEBUG struct bwfm_sdio_softc *sc = (void *)bwfm; + struct mbuf *m; + + KASSERT(len <= MCLBYTES); + + MGET(m, M_DONTWAIT, MT_CONTROL); + if (m == NULL) + goto fail; + if (len > MLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + goto fail; + } + } + memcpy(mtod(m, char *), buf, len); + m->m_len = len; + + mutex_enter(&sc->sc_lock); + MBUFQ_ENQUEUE(&sc->sc_tx_queue, m); + mutex_exit(&sc->sc_lock); + + bwfm_sdio_intr1(sc, "sdio_txctl"); + + return 0; + +fail: + return ENOBUFS; +} + +static int +bwfm_nvram_convert(u_char *buf, size_t len, size_t *newlenp) +{ + u_char *src, *dst, *end = buf + len; + bool skip = false; + size_t count = 0, pad; + uint32_t token; + + for (src = buf, dst = buf; src != end; ++src) { + if (*src == '\n') { + if (count > 0) + *dst++ = '\0'; + count = 0; + skip = false; + continue; + } + if (skip) + continue; + if (*src == '#' && count == 0) { + skip = true; + continue; + } + if (*src == '\r') + continue; + *dst++ = *src; + ++count; + } + + count = dst - buf; + pad = roundup(count + 1, 4) - count; + + if (count + pad + sizeof(token) > len) + return 1; + + memset(dst, 0, pad); + count += pad; + dst += pad; + + token = (count / 4) & 0xffff; + token |= ~token << 16; + token = htole32(token); + + memcpy(dst, &token, sizeof(token)); + count += sizeof(token); + + *newlenp = count ; + + return 0; +} + +static int +bwfm_sdio_load_microcode(struct bwfm_sdio_softc *sc, u_char *ucode, size_t size, + u_char *nvram, size_t nvlen) +{ + struct bwfm_softc *bwfm = &sc->sc_sc; + char *verify = NULL; + int err = 0; + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + + DPRINTF(("ucode %zu bytes to 0x%08lx\n", size, + (u_long)bwfm->sc_chip.ch_rambase)); + /* Upload firmware */ + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, + ucode, size, 1); + if (err) + goto out; + + /* Verify firmware */ + verify = kmem_zalloc(size, KM_SLEEP); + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase, + verify, size, 0); + if (err || memcmp(verify, ucode, size)) { + printf("%s: firmware verification failed\n", + DEVNAME(sc)); + kmem_free(verify, size); + goto out; + } + kmem_free(verify, size); + + DPRINTF(("nvram %zu bytes to 0x%08lx\n", nvlen, + (u_long)bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize + - nvlen)); + /* Upload nvram */ + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + + bwfm->sc_chip.ch_ramsize - nvlen, nvram, nvlen, 1); + if (err) + goto out; + + /* Verify nvram */ + verify = kmem_zalloc(nvlen, KM_SLEEP); + err = bwfm_sdio_ram_read_write(sc, bwfm->sc_chip.ch_rambase + + bwfm->sc_chip.ch_ramsize - nvlen, verify, nvlen, 0); + if (err || memcmp(verify, nvram, nvlen)) { + printf("%s: nvram verification failed\n", + DEVNAME(sc)); + kmem_free(verify, nvlen); + goto out; + } + kmem_free(verify, nvlen); + + DPRINTF(("Reset core 0x%08x\n", *(uint32_t *)ucode)); + /* Load reset vector from firmware and kickstart core. */ + bwfm_chip_set_active(bwfm, *(uint32_t *)ucode); + +out: + bwfm_sdio_clkctl(sc, CLK_SDONLY, false); + return err; +} + +static void +bwfm_sdio_clkctl(struct bwfm_sdio_softc *sc, enum bwfm_sdio_clkstate newstate, + bool pendok) +{ + enum bwfm_sdio_clkstate oldstate; + + oldstate = sc->sc_clkstate; + if (oldstate == newstate) + return; + + switch (newstate) { + case CLK_AVAIL: + if (oldstate == CLK_NONE) + sc->sc_clkstate = CLK_SDONLY; /* XXX */ + bwfm_sdio_htclk(sc, true, pendok); + break; + case CLK_SDONLY: + if (oldstate == CLK_NONE) + sc->sc_clkstate = newstate; + else if (oldstate == CLK_AVAIL) + bwfm_sdio_htclk(sc, false, false); + else + printf("%s: clkctl %d -> %d\n", DEVNAME(sc), + sc->sc_clkstate, newstate); + break; + case CLK_NONE: + if (oldstate == CLK_AVAIL) + bwfm_sdio_htclk(sc, false, false); + sc->sc_clkstate = newstate; + break; + default: + break; + } + + DPRINTF(("%s: %d -> %d = %d\n", DEVNAME(sc), oldstate, newstate, + sc->sc_clkstate)); +} + +static void +bwfm_sdio_htclk(struct bwfm_sdio_softc *sc, bool on, bool pendok) +{ + uint32_t clkctl, devctl, req; + int i; + + if (sc->sc_sr_enabled) { + if (on) + sc->sc_clkstate = CLK_AVAIL; + else + sc->sc_clkstate = CLK_SDONLY; + return; + } + + if (on) { + if (sc->sc_alp_only) + req = BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ; + else + req = BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, req); + + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only) + && pendok) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl |= BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + sc->sc_clkstate = CLK_PENDING; + return; + } else if (sc->sc_clkstate == CLK_PENDING) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + } + + for (i = 0; i < 50; i++) { + if (BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, + sc->sc_alp_only)) + break; + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR +); + sdmmc_pause(100000, NULL); + } + if (i >= 50) { + printf("%s: HT avail timeout\n", DEVNAME(sc)); + return; + } + + sc->sc_clkstate = CLK_AVAIL; + } else { + if (sc->sc_clkstate == CLK_PENDING) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + } + sc->sc_clkstate = CLK_SDONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, 0); + } +} + +struct bwfm_sdio_dstab { + uint8_t milli; + uint8_t val; +}; + +static struct bwfm_sdio_dstab pmu11_1v8[] = { + {32, 0x6}, + {26, 0x7}, + {22, 0x4}, + {16, 0x5}, + {12, 0x2}, + {8, 0x3}, + {4, 0x0}, + {0, 0x1} +}, pmu13_1v8[] = { + {6, 0x7}, + {5, 0x6}, + {4, 0x5}, + {3, 0x4}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}, pmu17_1v8[] = { + {3, 0x3}, + {2, 0x2}, + {1, 0x1}, + {0, 0x0} +}, pmu17_3v3[] = { + {16, 0x7}, + {12, 0x5}, + {8, 0x3}, + {4, 0x1}, + {0, 0x0} +}; + +static void +bwfm_sdio_drivestrength(struct bwfm_sdio_softc *sc, unsigned milli) +{ + struct bwfm_softc *bwfm = &sc->sc_sc; + struct bwfm_core *core; + struct bwfm_sdio_dstab *tab; + uint32_t tmp, mask; + unsigned i; + + if ((bwfm->sc_chip.ch_cc_caps & BWFM_CHIP_REG_CAPABILITIES_PMU) == 0) + return; + + switch (bwfm->sc_chip.ch_chip) { + case BRCM_CC_4330_CHIP_ID: + tab = pmu11_1v8; + mask = __BITS(11,13); + break; + case BRCM_CC_4334_CHIP_ID: + tab = pmu17_1v8; + mask = __BITS(11,12); + break; + case BRCM_CC_43143_CHIP_ID: + tab = pmu17_3v3; + mask = __BITS(0,3); + break; + case BRCM_CC_43362_CHIP_ID: + tab = pmu13_1v8; + mask = __BITS(11,13); + break; + default: + return; + } + + for (i=0; tab[i].milli != 0; ++i) { + if (milli >= tab[i].milli) + break; + } + if (tab[i].milli == 0) + return; + + core = bwfm_chip_get_pmu(&sc->sc_sc); + tmp = bwfm_sdio_read_4(sc, core->co_base + BWFM_CHIP_REG_CHIPCONTROL_ADDR); + tmp &= mask; + tmp |= __SHIFTIN(tab[i].val, mask); + bwfm_sdio_write_4(sc, core->co_base + BWFM_CHIP_REG_CHIPCONTROL_ADDR, tmp); +} + + +#if notyet +static int +bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *sc, bool sleep, bool pendok) +{ + uint32_t clkctl; + + if (sc->sleeping == sleep) + return 0; + + if (sc->sc_sr_enabled) { + if (sleep) { + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if ((clkctl & BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK) == 0) + bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ); + } + /* kso_ctrl(sc, sleep) */ + } + + if (sleep) { + if (!sc->sc_sr_enabled) + bwfm_sdio_clkctl(sc, CLK_NONE, pendok); + } else { + bwfm_sdio_clkctl(sc, CLK_AVAIL, pendok); + } + + sc->sleeping = sleep; + + return 0; +} #endif - int ret = 1; - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); +static void +bwfm_sdio_readshared(struct bwfm_sdio_softc *sc) +{ + struct bwfm_softc *bwfm = &sc->sc_sc; + struct bwfm_sdio_sdpcm sdpcm; + uint32_t addr, shaddr; + int err; + + bwfm_sdio_clkctl(sc, CLK_AVAIL, false); + if (sc->sc_clkstate != CLK_AVAIL) + return; + + shaddr = bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4; + if (!bwfm->sc_chip.ch_rambase && sc->sc_sr_enabled) + shaddr -= bwfm->sc_chip.ch_srsize; + + err = bwfm_sdio_ram_read_write(sc, shaddr, (char *)&addr, + sizeof(addr), 0); + if (err) + return; + + addr = le32toh(addr); + if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) + return; + + err = bwfm_sdio_ram_read_write(sc, addr, (char *)&sdpcm, + sizeof(sdpcm), 0); + if (err) + return; + + sc->sc_console_addr = le32toh(sdpcm.console_addr); +} + +static int +bwfm_sdio_intr1(void *v, const char *name) +{ + struct bwfm_sdio_softc *sc = (void *)v; + + DPRINTF(("%s: %s\n", DEVNAME(sc), name)); - return ret; + mutex_enter(&sc->sc_intr_lock); + if (!sdmmc_task_pending(&sc->sc_task)) + sdmmc_add_task(sc->sc_sf[1]->sc, &sc->sc_task); + sc->sc_task_queued = true; + mutex_exit(&sc->sc_intr_lock); + return 1; } -int -bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len) +static int +bwfm_sdio_intr(void *v) { + return bwfm_sdio_intr1(v, "sdio_intr"); +} + +static void +bwfm_sdio_task(void *v) +{ + struct bwfm_sdio_softc *sc = (void *)v; #ifdef BWFM_DEBUG - struct bwfm_sdio_softc *sc = (void *)bwfm; + unsigned count = 0; +#endif + + mutex_enter(&sc->sc_intr_lock); + while (sc->sc_task_queued) { +#ifdef BWFM_DEBUG + ++count; +#endif + sc->sc_task_queued = false; + mutex_exit(&sc->sc_intr_lock); + + mutex_enter(&sc->sc_lock); + bwfm_sdio_task1(sc); +#ifdef BWFM_DEBUG + bwfm_sdio_debug_console(sc); #endif - int ret = 1; + mutex_exit(&sc->sc_lock); + + mutex_enter(&sc->sc_intr_lock); + } + mutex_exit(&sc->sc_intr_lock); + +#ifdef BWFM_DEBUG + if (count > 1) + DPRINTF(("%s: finished %u tasks\n", DEVNAME(sc), count)); +#endif +} + +static void +bwfm_sdio_task1(struct bwfm_sdio_softc *sc) +{ + uint32_t clkctl, devctl, intstat, hostint; + bool dorecv, dosend; + + if (!sc->sc_sr_enabled && sc->sc_clkstate == CLK_PENDING) { + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR); + if (BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(clkctl)) { + devctl = bwfm_sdio_read_1(sc, BWFM_SDIO_DEVICE_CTL); + devctl &= ~BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY; + bwfm_sdio_write_1(sc, BWFM_SDIO_DEVICE_CTL, devctl); + sc->sc_clkstate = CLK_AVAIL; + } + } + + dorecv = dosend = sc->sc_clkstate == CLK_AVAIL; + + intstat = bwfm_sdio_dev_read(sc, BWFM_SDPCMD_INTSTATUS); + DPRINTF(("%s: intstat 0x%" PRIx32 "\n", DEVNAME(sc), intstat)); + intstat &= (SDPCMD_INTSTATUS_HMB_SW_MASK|SDPCMD_INTSTATUS_CHIPACTIVE); + if (intstat) + bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, intstat); + + if (intstat & SDPCMD_INTSTATUS_CHIPACTIVE) + printf("%s: CHIPACTIVE\n", DEVNAME(sc)); + + if (intstat & SDPCMD_INTSTATUS_HMB_HOST_INT) { + hostint = bwfm_sdio_dev_read(sc, SDPCMD_TOHOSTMAILBOXDATA); + DPRINTF(("%s: hostint 0x%" PRIx32 "\n", DEVNAME(sc), hostint)); + bwfm_sdio_dev_write(sc, SDPCMD_TOSBMAILBOX, + SDPCMD_TOSBMAILBOX_INT_ACK); + if (hostint & SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED) + sc->sc_rxskip = false; + if (hostint & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY || + hostint & SDPCMD_TOHOSTMAILBOXDATA_FWREADY) + bwfm_sdio_readshared(sc); + } + + if (intstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) { + /* ignore receive indications while recovering */ + if (dorecv && !sc->sc_rxskip) { + DPRINTF(("%s: recv\n", DEVNAME(sc))); + bwfm_sdio_rx_frames(sc); + } + } + + if (intstat & SDPCMD_INTSTATUS_HMB_FC_STATE) + dosend = false; + + if (intstat & SDPCMD_INTSTATUS_HMB_FC_CHANGE) { + if (dosend) { + intstat = bwfm_sdio_dev_read(sc, BWFM_SDPCMD_INTSTATUS); + DPRINTF(("%s: intstat2 0x%" PRIx32 "\n", DEVNAME(sc), intstat)); + if (intstat & (SDPCMD_INTSTATUS_HMB_FC_STATE | SDPCMD_INTSTATUS_HMB_FC_CHANGE)) + dosend = false; + } + } + +if (!dosend && MBUFQ_FIRST(&sc->sc_tx_queue)) printf("%s: flowctl\n", DEVNAME(sc)); + if (dosend && MBUFQ_FIRST(&sc->sc_tx_queue)) { + DPRINTF(("%s: xmit\n", DEVNAME(sc))); + bwfm_sdio_tx_frames(sc); + } +} + +static int +bwfm_sdio_tx_ok(struct bwfm_sdio_softc *sc) +{ + return (uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) != 0 && + ((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq) & 0x80) == 0; +} + +static void +bwfm_sdio_tx_frames(struct bwfm_sdio_softc *sc) +{ + struct mbuf *m; + struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp; + bool ifstart = false; + int i; + + if (!bwfm_sdio_tx_ok(sc)) + return; + + i = uimin((uint8_t)(sc->sc_tx_max_seq - sc->sc_tx_seq), 32); + while (i--) { + MBUFQ_DEQUEUE(&sc->sc_tx_queue, m); + if (m == NULL) + break; + + if (m->m_type == MT_CONTROL) + bwfm_sdio_tx_ctrlframe(sc, m); + else { + bwfm_sdio_tx_dataframe(sc, m); + ifp->if_opackets++; + ifstart = true; + } + + m_freem(m); + } + + if (ifstart) { + ifp->if_flags &= ~IFF_OACTIVE; + if_schedule_deferred_start(ifp); + } +} + +static void +bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *sc, struct mbuf *m) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + m->m_len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_CONTROL; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + m_copydata(m, 0, m->m_len, &swhdr[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, + roundup(len, roundto) - len); + + bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + roundup(len, roundto), 1); +} + +static void +bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *sc, struct mbuf *m) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + size_t len, roundto; + + len = sizeof(*hwhdr) + sizeof(*swhdr) + sizeof(*bcdc) + + m->m_pkthdr.len; + + /* Zero-pad to either block-size or 4-byte alignment. */ + if (len > 512 && (len % 512) != 0) + roundto = 512; + else + roundto = 4; + + KASSERT(roundup(len, roundto) <= sc->sc_bounce_size); + + hwhdr = (void *)sc->sc_bounce_buf; + hwhdr->frmlen = htole16(len); + hwhdr->cksum = htole16(~len); + + swhdr = (void *)&hwhdr[1]; + swhdr->seqnr = sc->sc_tx_seq++; + swhdr->chanflag = BWFM_SDIO_SWHDR_CHANNEL_DATA; + swhdr->nextlen = 0; + swhdr->dataoff = sizeof(*hwhdr) + sizeof(*swhdr); + swhdr->maxseqnr = 0; + + bcdc = (void *)&swhdr[1]; + bcdc->data_offset = 0; + bcdc->priority = WME_AC_BE; + bcdc->flags = BWFM_BCDC_FLAG_VER(BWFM_BCDC_FLAG_PROTO_VER); + bcdc->flags2 = 0; + + m_copydata(m, 0, m->m_pkthdr.len, &bcdc[1]); + + if (roundup(len, roundto) != len) + memset(sc->sc_bounce_buf + len, 0, + roundup(len, roundto) - len); + + bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + roundup(len, roundto), 1); + + sc->sc_tx_count--; +} + +static int +bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *lenp) +{ + struct bwfm_sdio_softc *sc = (void *)bwfm; + struct mbuf *m; + int err = 0; + + mutex_enter(&sc->sc_lock); + while ((m = bwfm_qget(&sc->sc_rxctl_queue)) == NULL) { + err = cv_timedwait(&sc->sc_rxctl_cv, &sc->sc_lock, + mstohz(5000)); + if (err == EWOULDBLOCK) + break; + } + mutex_exit(&sc->sc_lock); + + if (err) + return 1; - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + if (m->m_len > *lenp) { + m_freem(m); + return 1; + } + + *lenp = m->m_len; + m_copydata(m, 0, m->m_len, buf); + m_freem(m); + return 0; +} + +static void +bwfm_sdio_rx_frames(struct bwfm_sdio_softc *sc) +{ + struct bwfm_sdio_hwhdr *hwhdr; + struct bwfm_sdio_swhdr *swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + uint16_t *sublen, nextlen = 0; + struct mbuf *m; + size_t flen, off, hoff; + char *data; + int nsub; + size_t subsize; + + hwhdr = (struct bwfm_sdio_hwhdr *)sc->sc_bounce_buf; + swhdr = (struct bwfm_sdio_swhdr *)&hwhdr[1]; + data = (char *)&swhdr[1]; + + for (;;) { + /* If we know the next size, just read ahead. */ + if (nextlen) { + if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + nextlen, 0)) + break; + nextlen = 0; + } else { + if (bwfm_sdio_frame_read_write(sc, sc->sc_bounce_buf, + sizeof(*hwhdr) + sizeof(*swhdr), 0)) + break; + } + + hwhdr->frmlen = le16toh(hwhdr->frmlen); + hwhdr->cksum = le16toh(hwhdr->cksum); + + if (hwhdr->frmlen == 0 && hwhdr->cksum == 0) { + break; + } + + if ((hwhdr->frmlen ^ hwhdr->cksum) != 0xffff) { + printf("%s: checksum error\n", DEVNAME(sc)); + break; + } + + if (hwhdr->frmlen < sizeof(*hwhdr) + sizeof(*swhdr)) { + printf("%s: length error\n", DEVNAME(sc)); + break; + } + + if (nextlen && hwhdr->frmlen > nextlen) { + printf("%s: read ahead length error (%u > %u)\n", + DEVNAME(sc), hwhdr->frmlen, nextlen); + break; + } + + sc->sc_tx_max_seq = swhdr->maxseqnr; + + flen = hwhdr->frmlen - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (flen == 0) { + nextlen = swhdr->nextlen << 4; + continue; + } + + if (!nextlen) { + KASSERT(roundup(flen, 4) <= sc->sc_bounce_size - + (sizeof(*hwhdr) + sizeof(*swhdr))); + if (bwfm_sdio_frame_read_write(sc, data, + roundup(flen, 4), 0)) { + printf("%s: I/O error roundup(%zu, 4) bytes\n", + DEVNAME(sc), flen); + break; + } + } + + if (swhdr->dataoff < (sizeof(*hwhdr) + sizeof(*swhdr))) { + printf("%s: data offset %u in header\n", + DEVNAME(sc), swhdr->dataoff); + break; + } + + off = swhdr->dataoff - (sizeof(*hwhdr) + sizeof(*swhdr)); + if (off > flen) { + printf("%s: offset %zu beyond end %zu\n", + DEVNAME(sc), off, flen); + break; + } + + switch (swhdr->chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { + case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: + m = bwfm_sdio_newbuf(); + if (m == NULL) + break; + if (flen - off > m->m_len) { + printf("%s: ctl bigger than anticipated\n", + DEVNAME(sc)); + m_freem(m); + break; + } + m->m_len = m->m_pkthdr.len = flen - off; + memcpy(mtod(m, char *), data + off, flen - off); + bwfm_qput(&sc->sc_rxctl_queue, m); + cv_broadcast(&sc->sc_rxctl_cv); + nextlen = swhdr->nextlen << 4; + break; + case BWFM_SDIO_SWHDR_CHANNEL_EVENT: + case BWFM_SDIO_SWHDR_CHANNEL_DATA: + m = bwfm_sdio_newbuf(); + if (m == NULL) + break; + if (flen - off > m->m_len) { + printf("%s: frame bigger than anticipated\n", + DEVNAME(sc)); + m_freem(m); + break; + } + m->m_len = m->m_pkthdr.len = flen - off; + memcpy(mtod(m, char *), data + off, flen - off); + bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); + hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); + if (m->m_len < hoff) { + printf("%s: short bcdc packet %d < %zu\n", + DEVNAME(sc), m->m_len, hoff); + m_freem(m); + break; + } + m_adj(m, hoff); + bwfm_rx(&sc->sc_sc, m); + nextlen = swhdr->nextlen << 4; + break; + case BWFM_SDIO_SWHDR_CHANNEL_GLOM: + if ((flen % sizeof(uint16_t)) != 0) { + printf("%s: odd length (%zu) glom table\n", + DEVNAME(sc), flen); + break; + } + nsub = flen / sizeof(uint16_t); + subsize = nsub * sizeof(uint16_t); + sublen = NULL; + nextlen = 0; + if (subsize > 0) + sublen = kmem_zalloc(subsize, KM_NOSLEEP); + if (sublen != NULL) { + memcpy(sublen, data, subsize); + bwfm_sdio_rx_glom(sc, sublen, nsub, &nextlen); + kmem_free(sublen, subsize); + } + break; + default: + printf("%s: unknown channel\n", DEVNAME(sc)); + break; + } + } +} + +static void +bwfm_sdio_rx_glom(struct bwfm_sdio_softc *sc, uint16_t *sublen, int nsub, + uint16_t *nextlen) +{ + struct bwfm_sdio_hwhdr hwhdr; + struct bwfm_sdio_swhdr swhdr; + struct bwfm_proto_bcdc_hdr *bcdc; + struct mbuf *m, *m0; + size_t flen, off, hoff; + int i; + + if (nsub == 0) + return; - return ret; + m0 = NULL; + for (i = 0; i < nsub; i++) { + m = bwfm_sdio_newbuf(); + if (m == NULL) { + m_freem(m0); + return; + } + bwfm_qput(&m0, m); + if (le16toh(sublen[i]) > m->m_len) { + m_freem(m0); + printf("%s: header larger than mbuf\n", DEVNAME(sc)); + return; + } + if (bwfm_sdio_frame_read_write(sc, mtod(m, char *), + le16toh(sublen[i]), 0)) { + m_freem(m0); + printf("%s: frame I/O error\n", DEVNAME(sc)); + return; + } + m->m_len = m->m_pkthdr.len = le16toh(sublen[i]); + } + + if (m0->m_len >= sizeof(hwhdr) + sizeof(swhdr)) { + m_copydata(m0, 0, sizeof(hwhdr), &hwhdr); + m_copydata(m0, sizeof(hwhdr), sizeof(swhdr), &swhdr); + + /* TODO: Verify actual superframe header */ + + /* remove superframe header */ + if (m0->m_len >= swhdr.dataoff) + m_adj(m0, swhdr.dataoff); + } + + *nextlen = 0; + while ((m = bwfm_qget(&m0)) != NULL) { + if (m->m_len < sizeof(hwhdr) + sizeof(swhdr)) { + printf("%s: tiny mbuf %d < %zu\n", DEVNAME(sc), + m->m_len, sizeof(hwhdr) + sizeof(swhdr)); + goto drop; + } + + m_copydata(m, 0, sizeof(hwhdr), &hwhdr); + m_copydata(m, sizeof(hwhdr), sizeof(swhdr), &swhdr); + + hwhdr.frmlen = le16toh(hwhdr.frmlen); + hwhdr.cksum = le16toh(hwhdr.cksum); + + if (hwhdr.frmlen == 0 && hwhdr.cksum == 0) + goto drop; + + if ((hwhdr.frmlen ^ hwhdr.cksum) != 0xffff) { + printf("%s: checksum error\n", DEVNAME(sc)); + goto drop; + } + + + if (hwhdr.frmlen < sizeof(hwhdr) + sizeof(swhdr)) { + printf("%s: length error\n", DEVNAME(sc)); + goto drop; + } + + flen = hwhdr.frmlen - (sizeof(hwhdr) + sizeof(swhdr)); + if (flen == 0) + goto drop; + + if (hwhdr.frmlen > m->m_len) { + printf("%s: short mbuf %d < %zu\n", + DEVNAME(sc),m->m_len,flen); + goto drop; + } + + if (swhdr.dataoff < (sizeof(hwhdr) + sizeof(swhdr))) { + printf("%s: data offset %u in header\n", + DEVNAME(sc), swhdr.dataoff); + goto drop; + } + + off = swhdr.dataoff - (sizeof(hwhdr) + sizeof(swhdr)); + if (off > flen) { + printf("%s: offset %zu beyond end %zu\n", + DEVNAME(sc), off, flen); + goto drop; + } + + m_adj(m, (int)hwhdr.frmlen - m->m_len); + *nextlen = swhdr.nextlen << 4; + + switch (swhdr.chanflag & BWFM_SDIO_SWHDR_CHANNEL_MASK) { + case BWFM_SDIO_SWHDR_CHANNEL_CONTROL: + printf("%s: control channel not allowed in glom\n", + DEVNAME(sc)); + goto drop; + case BWFM_SDIO_SWHDR_CHANNEL_EVENT: + case BWFM_SDIO_SWHDR_CHANNEL_DATA: + m_adj(m, swhdr.dataoff); + bcdc = mtod(m, struct bwfm_proto_bcdc_hdr *); + hoff = sizeof(*bcdc) + ((size_t)bcdc->data_offset << 2); + if (m->m_len < hoff) { + printf("%s: short bcdc packet %d < %zu\n", + DEVNAME(sc), m->m_len, hoff); + m_freem(m); + break; + } + m_adj(m, hoff); + bwfm_rx(&sc->sc_sc, m); + break; + case BWFM_SDIO_SWHDR_CHANNEL_GLOM: + printf("%s: glom not allowed in glom\n", + DEVNAME(sc)); + goto drop; + default: + printf("%s: unknown channel\n", DEVNAME(sc)); + goto drop; + } + + continue; +drop: + printf("rx dropped %p len %d\n",mtod(m, char *),m->m_pkthdr.len); + m_free(m); + break; + } } + +#ifdef BWFM_DEBUG +static void +bwfm_sdio_debug_console(struct bwfm_sdio_softc *sc) +{ + struct bwfm_sdio_console c; + uint32_t newidx; + int err; + + if (!sc->sc_console_addr) + return; + + err = bwfm_sdio_ram_read_write(sc, sc->sc_console_addr, + (char *)&c, sizeof(c), 0); + if (err) + return; + + c.log_buf = le32toh(c.log_buf); + c.log_bufsz = le32toh(c.log_bufsz); + c.log_idx = le32toh(c.log_idx); + + if (sc->sc_console_buf == NULL) { + sc->sc_console_buf = malloc(c.log_bufsz, M_DEVBUF, + M_WAITOK|M_ZERO); + sc->sc_console_buf_size = c.log_bufsz; + } + + newidx = c.log_idx; + if (newidx >= sc->sc_console_buf_size) + return; + + err = bwfm_sdio_ram_read_write(sc, c.log_buf, sc->sc_console_buf, + sc->sc_console_buf_size, 0); + if (err) + return; + + if (newidx != sc->sc_console_readidx) + DPRINTFN(3, ("BWFM CONSOLE: ")); + while (newidx != sc->sc_console_readidx) { + uint8_t ch = sc->sc_console_buf[sc->sc_console_readidx]; + sc->sc_console_readidx++; + if (sc->sc_console_readidx == sc->sc_console_buf_size) + sc->sc_console_readidx = 0; + if (ch == '\r') + continue; + DPRINTFN(3, ("%c", ch)); + } +} +#endif Index: src/sys/dev/sdmmc/sdhc.c diff -u src/sys/dev/sdmmc/sdhc.c:1.103 src/sys/dev/sdmmc/sdhc.c:1.103.2.1 --- src/sys/dev/sdmmc/sdhc.c:1.103 Wed Jul 3 23:10:08 2019 +++ src/sys/dev/sdmmc/sdhc.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdhc.c,v 1.103 2019/07/03 23:10:08 jmcneill Exp $ */ +/* $NetBSD: sdhc.c,v 1.103.2.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $ */ /* @@ -23,7 +23,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.103 2019/07/03 23:10:08 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.103.2.1 2020/02/25 18:40:43 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -98,6 +98,8 @@ struct sdhc_host { bus_dmamap_t adma_map; bus_dma_segment_t adma_segs[1]; void *adma2; + + uint8_t vdd; /* last vdd setting */ }; #define HDEVNAME(hp) (device_xname((hp)->sc->sc_dev)) @@ -156,23 +158,29 @@ hwrite2(struct sdhc_host *hp, bus_size_t } } +static void +hwrite4(struct sdhc_host *hp, bus_size_t o, uint32_t val) +{ + + bus_space_write_4(hp->iot, hp->ioh, o, val); +} + #define HWRITE1(hp, reg, val) hwrite1(hp, reg, val) #define HWRITE2(hp, reg, val) hwrite2(hp, reg, val) -#define HWRITE4(hp, reg, val) \ - bus_space_write_4((hp)->iot, (hp)->ioh, (reg), (val)) +#define HWRITE4(hp, reg, val) hwrite4(hp, reg, val) #define HCLR1(hp, reg, bits) \ - do if (bits) HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)); while (0) + do if ((bits) != 0) HWRITE1((hp), (reg), HREAD1((hp), (reg)) & ~(bits)); while (0) #define HCLR2(hp, reg, bits) \ - do if (bits) HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)); while (0) + do if ((bits) != 0) HWRITE2((hp), (reg), HREAD2((hp), (reg)) & ~(bits)); while (0) #define HCLR4(hp, reg, bits) \ - do if (bits) HWRITE4((hp), (reg), HREAD4((hp), (reg)) & ~(bits)); while (0) + do if ((bits) != 0) HWRITE4((hp), (reg), HREAD4((hp), (reg)) & ~(bits)); while (0) #define HSET1(hp, reg, bits) \ - do if (bits) HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)); while (0) + do if ((bits) != 0) HWRITE1((hp), (reg), HREAD1((hp), (reg)) | (bits)); while (0) #define HSET2(hp, reg, bits) \ - do if (bits) HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)); while (0) + do if ((bits) != 0) HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)); while (0) #define HSET4(hp, reg, bits) \ - do if (bits) HWRITE4((hp), (reg), HREAD4((hp), (reg)) | (bits)); while (0) + do if ((bits) != 0) HWRITE4((hp), (reg), HREAD4((hp), (reg)) | (bits)); while (0) static int sdhc_host_reset(sdmmc_chipset_handle_t); static int sdhc_host_reset1(sdmmc_chipset_handle_t); @@ -782,6 +790,9 @@ sdhc_host_reset1(sdmmc_chipset_handle_t HWRITE2(hp, SDHC_NINTR_SIGNAL_EN, 0); } + /* Let sdhc_bus_power restore power */ + hp->vdd = 0; + /* * Reset the entire host controller and wait up to 100ms for * the controller to clear the reset bit. @@ -896,6 +907,7 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc int error = 0; const uint32_t pcmask = ~(SDHC_BUS_POWER | (SDHC_VOLTAGE_MASK << SDHC_VOLTAGE_SHIFT)); + uint32_t reg; mutex_enter(&hp->intr_lock); @@ -903,8 +915,10 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc * Disable bus power before voltage change. */ if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_32BIT_ACCESS) - && !ISSET(hp->sc->sc_flags, SDHC_FLAG_NO_PWR0)) + && !ISSET(hp->sc->sc_flags, SDHC_FLAG_NO_PWR0)) { + hp->vdd = 0; HWRITE1(hp, SDHC_POWER_CTL, 0); + } /* If power is disabled, reset the host and return now. */ if (ocr == 0) { @@ -929,6 +943,12 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc goto out; } + /* + * Did voltage change ? + */ + if (vdd == hp->vdd) + goto out; + if (!ISSET(hp->sc->sc_flags, SDHC_FLAG_ENHANCED)) { /* * Enable bus power. Wait at least 1 ms (or 74 clocks) plus @@ -939,13 +959,14 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc HWRITE1(hp, SDHC_POWER_CTL, (vdd << SDHC_VOLTAGE_SHIFT) | SDHC_BUS_POWER); } else { - HWRITE1(hp, SDHC_POWER_CTL, - HREAD1(hp, SDHC_POWER_CTL) & pcmask); + reg = HREAD1(hp, SDHC_POWER_CTL) & pcmask; + HWRITE1(hp, SDHC_POWER_CTL, reg); sdmmc_delay(1); - HWRITE1(hp, SDHC_POWER_CTL, - (vdd << SDHC_VOLTAGE_SHIFT)); + reg |= (vdd << SDHC_VOLTAGE_SHIFT); + HWRITE1(hp, SDHC_POWER_CTL, reg); sdmmc_delay(1); - HSET1(hp, SDHC_POWER_CTL, SDHC_BUS_POWER); + reg |= SDHC_BUS_POWER; + HWRITE1(hp, SDHC_POWER_CTL, reg); sdmmc_delay(10000); } @@ -960,6 +981,9 @@ sdhc_bus_power(sdmmc_chipset_handle_t sc } } + /* power successfully changed */ + hp->vdd = vdd; + out: mutex_exit(&hp->intr_lock); @@ -1516,7 +1540,7 @@ sdhc_wait_state(struct sdhc_host *hp, ui uint32_t state; int timeout; - for (timeout = 10000; timeout > 0; timeout--) { + for (timeout = 100000; timeout > 0; timeout--) { if (((state = HREAD4(hp, SDHC_PRESENT_STATE)) & mask) == value) return 0; sdmmc_delay(10); @@ -1581,7 +1605,7 @@ sdhc_exec_command(sdmmc_chipset_handle_t * is marked done for any other reason. */ probing = (cmd->c_flags & SCF_TOUT_OK) != 0; - if (!sdhc_wait_intr(hp, SDHC_COMMAND_COMPLETE, SDHC_COMMAND_TIMEOUT, probing)) { + if (!sdhc_wait_intr(hp, SDHC_COMMAND_COMPLETE, SDHC_COMMAND_TIMEOUT*3, probing)) { DPRINTF(1,("%s: timeout for command\n", __func__)); sdmmc_delay(50); cmd->c_error = ETIMEDOUT; Index: src/sys/dev/sdmmc/sdmmc.c diff -u src/sys/dev/sdmmc/sdmmc.c:1.36 src/sys/dev/sdmmc/sdmmc.c:1.36.4.1 --- src/sys/dev/sdmmc/sdmmc.c:1.36 Tue Nov 6 16:01:38 2018 +++ src/sys/dev/sdmmc/sdmmc.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc.c,v 1.36 2018/11/06 16:01:38 jmcneill Exp $ */ +/* $NetBSD: sdmmc.c,v 1.36.4.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmc.c,v 1.18 2009/01/09 10:58:38 jsg Exp $ */ /* @@ -49,7 +49,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.36 2018/11/06 16:01:38 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.36.4.1 2020/02/25 18:40:43 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -567,7 +567,7 @@ sdmmc_enable(struct sdmmc_softc *sc) } /* XXX wait for card to power up */ - sdmmc_delay(100000); + sdmmc_pause(100000, NULL); if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { /* Initialize SD I/O card function(s). */ @@ -656,6 +656,7 @@ sdmmc_function_alloc(struct sdmmc_softc sf->cis.product = SDMMC_PRODUCT_INVALID; sf->cis.function = SDMMC_FUNCTION_INVALID; sf->width = 1; + sf->blklen = sdmmc_chip_host_maxblklen(sc->sc_sct, sc->sc_sch); if (ISSET(sc->sc_flags, SMF_MEM_MODE) && ISSET(sc->sc_caps, SMC_CAPS_DMA) && @@ -788,6 +789,17 @@ sdmmc_delay(u_int usecs) delay(usecs); } +void +sdmmc_pause(u_int usecs, kmutex_t *lock) +{ + unsigned ticks = mstohz(usecs/1000); + + if (cold || ticks < 1) + delay(usecs); + else + kpause("sdmmcdelay", false, ticks, lock); +} + int sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_function *sf, struct sdmmc_command *cmd) { @@ -908,7 +920,7 @@ sdmmc_set_relative_addr(struct sdmmc_sof /* Don't lock */ if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { - aprint_error_dev(sc->sc_dev, + device_printf(sc->sc_dev, "sdmmc_set_relative_addr: SMC_CAPS_SPI_MODE set"); return EIO; } @@ -941,7 +953,7 @@ sdmmc_select_card(struct sdmmc_softc *sc /* Don't lock */ if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { - aprint_error_dev(sc->sc_dev, + device_printf(sc->sc_dev, "sdmmc_select_card: SMC_CAPS_SPI_MODE set"); return EIO; } @@ -960,6 +972,11 @@ sdmmc_select_card(struct sdmmc_softc *sc if (error == 0 || sf == NULL) sc->sc_card = sf; + if (error) { + device_printf(sc->sc_dev, + "sdmmc_select_card: error %d", error); + } + return error; } Index: src/sys/dev/sdmmc/sdmmc_cis.c diff -u src/sys/dev/sdmmc/sdmmc_cis.c:1.5 src/sys/dev/sdmmc/sdmmc_cis.c:1.5.10.1 --- src/sys/dev/sdmmc/sdmmc_cis.c:1.5 Sun Jan 28 14:34:06 2018 +++ src/sys/dev/sdmmc/sdmmc_cis.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_cis.c,v 1.5 2018/01/28 14:34:06 jmcneill Exp $ */ +/* $NetBSD: sdmmc_cis.c,v 1.5.10.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */ /* @@ -20,7 +20,7 @@ /* Routines to decode the Card Information Structure of SD I/O cards */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.5 2018/01/28 14:34:06 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.5.10.1 2020/02/25 18:40:43 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -130,6 +130,7 @@ decode_funce_function(struct sdmmc_funct max_blk_size = sdmmc_io_read_1(sf0, reg + 11); max_blk_size |= sdmmc_io_read_1(sf0, reg + 12) << 8; +device_printf(dev, "MAX_BLK_SIZE%d = %d\n", sf->number, max_blk_size); DPRINTF(("CISTPL_FUNCE: MAX_BLK_SIZE=0x%x\n", max_blk_size)); } @@ -202,7 +203,7 @@ sdmmc_read_cis(struct sdmmc_function *sf } #ifdef SDMMCCISDEBUG - { + { int i; /* print the tuple */ @@ -259,6 +260,11 @@ sdmmc_read_cis(struct sdmmc_function *sf reg += tpllen; break; + case PCMCIA_CISTPL_SDIO: + aprint_normal_dev(dev, "SDIO function\n"); + reg += tpllen; + break; + default: /* * Tuple codes between 80h-8Fh are vendor unique. @@ -312,7 +318,7 @@ sdmmc_check_cis_quirks(struct sdmmc_func if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC && sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) { /* This card lacks the VERS_1 tuple. */ - static const char cis1_info[] = + static const char cis1_info[] = "Spectec\0SDIO WLAN Card\0SDW-820\0\0"; sf->cis.cis1_major = 0x01; Index: src/sys/dev/sdmmc/sdmmc_io.c diff -u src/sys/dev/sdmmc/sdmmc_io.c:1.14 src/sys/dev/sdmmc/sdmmc_io.c:1.14.4.1 --- src/sys/dev/sdmmc/sdmmc_io.c:1.14 Sun Oct 14 17:37:40 2018 +++ src/sys/dev/sdmmc/sdmmc_io.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_io.c,v 1.14 2018/10/14 17:37:40 jdolecek Exp $ */ +/* $NetBSD: sdmmc_io.c,v 1.14.4.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmc_io.c,v 1.10 2007/09/17 01:33:33 krw Exp $ */ /* @@ -20,7 +20,7 @@ /* Routines for SD I/O cards. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_io.c,v 1.14 2018/10/14 17:37:40 jdolecek Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_io.c,v 1.14.4.1 2020/02/25 18:40:43 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -52,7 +52,7 @@ struct sdmmc_intr_handler { }; static int sdmmc_io_rw_direct(struct sdmmc_softc *, - struct sdmmc_function *, int, u_char *, int); + struct sdmmc_function *, int, u_char *, int, bool); static int sdmmc_io_rw_extended(struct sdmmc_softc *, struct sdmmc_function *, int, u_char *, int, int); #if 0 @@ -194,6 +194,8 @@ sdmmc_io_init(struct sdmmc_softc *sc, st SDMMC_LOCK(sc); + sf->blklen = sdmmc_chip_host_maxblklen(sc->sc_sct, sc->sc_sch); + if (sf->number == 0) { reg = sdmmc_io_read_1(sf, SD_IO_CCCR_CAPABILITY); if (!(reg & CCCR_CAPS_LSC) || (reg & CCCR_CAPS_4BLS)) { @@ -228,7 +230,7 @@ sdmmc_io_init(struct sdmmc_softc *sc, st sf->csd.tran_speed = 50000; /* 50MHz */ /* Wait 400KHz x 8 clock */ - delay(1); + sdmmc_delay(20); } if (sc->sc_busclk > sf->csd.tran_speed) sc->sc_busclk = sf->csd.tran_speed; @@ -238,6 +240,15 @@ sdmmc_io_init(struct sdmmc_softc *sc, st if (error) aprint_error_dev(sc->sc_dev, "can't change bus clock\n"); + + aprint_normal_dev(sc->sc_dev, "%u-bit width,", sf->width); + if ((sc->sc_busclk / 1000) != 0) + aprint_normal(" %u.%03u MHz\n", + sc->sc_busclk / 1000, sc->sc_busclk % 1000); + else + aprint_normal(" %u KHz\n", sc->sc_busclk % 1000); + + } else { reg = sdmmc_io_read_1(sf0, SD_IO_FBR(sf->number) + 0x000); sf->interface = FBR_STD_FUNC_IF_CODE(reg); @@ -330,7 +341,7 @@ sdmmc_io_function_disable(struct sdmmc_f static int sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf, - int reg, u_char *datap, int arg) + int reg, u_char *datap, int arg, bool toutok) { struct sdmmc_command cmd; int error; @@ -353,9 +364,19 @@ sdmmc_io_rw_direct(struct sdmmc_softc *s cmd.c_opcode = SD_IO_RW_DIRECT; cmd.c_arg = arg; cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5; + if (toutok) + cmd.c_flags |= SCF_TOUT_OK; error = sdmmc_mmc_command(sc, &cmd); - *datap = SD_R5_DATA(cmd.c_resp); + if (error == 0) + *datap = SD_R5_DATA(cmd.c_resp); + + if (error && error != ETIMEDOUT) { + device_printf(sc->sc_dev, + "direct I/O error %d, r=%d p=%p %s\n", + error, reg, datap, + ISSET(arg, SD_ARG_CMD53_WRITE) ? "write" : "read"); + } return error; } @@ -392,16 +413,23 @@ sdmmc_io_rw_extended(struct sdmmc_softc memset(&cmd, 0, sizeof cmd); cmd.c_opcode = SD_IO_RW_EXTENDED; cmd.c_arg = arg; - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5; + cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R5; cmd.c_data = datap; cmd.c_datalen = datalen; - cmd.c_blklen = MIN(datalen, - sdmmc_chip_host_maxblklen(sc->sc_sct,sc->sc_sch)); + cmd.c_blklen = MIN(datalen, sf->blklen); + if (!ISSET(arg, SD_ARG_CMD53_WRITE)) cmd.c_flags |= SCF_CMD_READ; error = sdmmc_mmc_command(sc, &cmd); + if (error) { + device_printf(sc->sc_dev, + "extended I/O error %d, r=%d p=%p l=%d %s\n", + error, reg, datap, datalen, + ISSET(arg, SD_ARG_CMD53_WRITE) ? "write" : "read"); + } + return error; } @@ -413,7 +441,7 @@ sdmmc_io_read_1(struct sdmmc_function *s /* Don't lock */ (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data, - SD_ARG_CMD52_READ); + SD_ARG_CMD52_READ, false); return data; } @@ -424,7 +452,7 @@ sdmmc_io_write_1(struct sdmmc_function * /* Don't lock */ (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data, - SD_ARG_CMD52_WRITE); + SD_ARG_CMD52_WRITE, false); } uint16_t @@ -476,21 +504,26 @@ int sdmmc_io_read_multi_1(struct sdmmc_function *sf, int reg, u_char *data, int datalen) { - int error; + int blocks, bytes, error = 0; /* Don't lock */ - while (datalen > SD_ARG_CMD53_LENGTH_MAX) { + while (datalen >= sf->blklen) { + //blocks = imin(datalen / sf->blklen, + // SD_ARG_CMD53_LENGTH_MAX); + blocks = 1; + bytes = blocks * sf->blklen; error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, - SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_READ); + bytes, SD_ARG_CMD53_READ); if (error) goto error; - data += SD_ARG_CMD53_LENGTH_MAX; - datalen -= SD_ARG_CMD53_LENGTH_MAX; + data += bytes; + datalen -= bytes; } - error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, - SD_ARG_CMD53_READ); + if (datalen) + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, + SD_ARG_CMD53_READ); error: return error; } @@ -499,21 +532,85 @@ int sdmmc_io_write_multi_1(struct sdmmc_function *sf, int reg, u_char *data, int datalen) { - int error; + int blocks, bytes, error = 0; + + /* Don't lock */ + + while (datalen >= sf->blklen) { + //blocks = imin(datalen / sf->blklen, + // SD_ARG_CMD53_LENGTH_MAX); + blocks = 1; + bytes = blocks * sf->blklen; + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, + bytes, SD_ARG_CMD53_WRITE); + if (error) + goto error; + data += bytes; + datalen -= bytes; + } + + if (datalen) + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, + SD_ARG_CMD53_WRITE); +error: + return error; +} + + +int +sdmmc_io_read_region_1(struct sdmmc_function *sf, int reg, u_char *data, + int datalen) +{ + int blocks, bytes, error = 0; + + /* Don't lock */ + + while (datalen >= sf->blklen) { + //blocks = imin(datalen / sf->blklen, + // SD_ARG_CMD53_LENGTH_MAX); + blocks = 1; + bytes = blocks * sf->blklen; + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, + bytes, SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT); + if (error) + goto error; + reg += bytes; + data += bytes; + datalen -= bytes; + } + + if (datalen) + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, + SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT); +error: + return error; +} + +int +sdmmc_io_write_region_1(struct sdmmc_function *sf, int reg, u_char *data, + int datalen) +{ + int blocks, bytes, error = 0; /* Don't lock */ - while (datalen > SD_ARG_CMD53_LENGTH_MAX) { + while (datalen >= sf->blklen) { + //blocks = imin(datalen / sf->blklen, + // SD_ARG_CMD53_LENGTH_MAX); + blocks = 1; + bytes = blocks * sf->blklen; error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, - SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_WRITE); + bytes, SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT); if (error) goto error; - data += SD_ARG_CMD53_LENGTH_MAX; - datalen -= SD_ARG_CMD53_LENGTH_MAX; + reg += bytes; + data += bytes; + datalen -= bytes; } - error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, - SD_ARG_CMD53_WRITE); + if (datalen) + error = sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen, + SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT); error: return error; } @@ -527,11 +624,23 @@ sdmmc_io_xchg(struct sdmmc_softc *sc, st /* Don't lock */ return sdmmc_io_rw_direct(sc, sf, reg, datap, - SD_ARG_CMD52_WRITE|SD_ARG_CMD52_EXCHANGE); + SD_ARG_CMD52_WRITE|SD_ARG_CMD52_EXCHANGE, false); } #endif /* + * Abort I/O function of the card + */ +int +sdmmc_io_function_abort(struct sdmmc_function *sf) +{ + u_char data = CCCR_CTL_AS(sf->number); + + return sdmmc_io_rw_direct(sf->sc, NULL, SD_IO_CCCR_CTL, &data, + SD_ARG_CMD52_WRITE, true); +} + +/* * Reset the I/O functions of the card. */ static void @@ -539,8 +648,9 @@ sdmmc_io_reset(struct sdmmc_softc *sc) { u_char data = CCCR_CTL_RES; - if (sdmmc_io_rw_direct(sc, NULL, SD_IO_CCCR_CTL, &data, SD_ARG_CMD52_WRITE) == 0) - sdmmc_delay(100000); + if (sdmmc_io_rw_direct(sc, NULL, SD_IO_CCCR_CTL, &data, + SD_ARG_CMD52_WRITE, true) == 0) + sdmmc_pause(100000, NULL); /* XXX SDMMC_LOCK */ } /* @@ -575,7 +685,7 @@ sdmmc_io_send_op_cond(struct sdmmc_softc break; error = ETIMEDOUT; - sdmmc_delay(10000); + sdmmc_pause(10000, NULL); } if (error == 0 && ocrp != NULL) *ocrp = MMC_R4(cmd.c_resp); @@ -597,11 +707,9 @@ sdmmc_intr_enable(struct sdmmc_function uint8_t reg; SDMMC_LOCK(sc); - mutex_enter(&sc->sc_intr_task_mtx); reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_INTEN); reg |= 1 << sf->number; sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_INTEN, reg); - mutex_exit(&sc->sc_intr_task_mtx); SDMMC_UNLOCK(sc); } @@ -613,11 +721,9 @@ sdmmc_intr_disable(struct sdmmc_function uint8_t reg; SDMMC_LOCK(sc); - mutex_enter(&sc->sc_intr_task_mtx); reg = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_INTEN); reg &= ~(1 << sf->number); sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_INTEN, reg); - mutex_exit(&sc->sc_intr_task_mtx); SDMMC_UNLOCK(sc); } @@ -719,3 +825,32 @@ sdmmc_intr_task(void *arg) sdmmc_chip_card_intr_ack(sc->sc_sct, sc->sc_sch); } + +int +sdmmc_io_set_blocklen(struct sdmmc_function *sf, + int blklen) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_function *sf0 = sc->sc_fn0; + int error = EINVAL; + + SDMMC_LOCK(sc); + + if (blklen <= 0 || + blklen > sdmmc_chip_host_maxblklen(sc->sc_sct, sc->sc_sch)) + goto err; + + sdmmc_io_write_1(sf0, SD_IO_FBR(sf->number) + + SD_IO_FBR_BLOCKLEN, blklen & 0xff); + sdmmc_io_write_1(sf0, SD_IO_FBR(sf->number) + + SD_IO_FBR_BLOCKLEN + 1, (blklen >> 8) & 0xff); + + sf->blklen = blklen; + error = 0; + +err: + SDMMC_UNLOCK(sc); + + return error; +} + Index: src/sys/dev/sdmmc/sdmmc_ioreg.h diff -u src/sys/dev/sdmmc/sdmmc_ioreg.h:1.3 src/sys/dev/sdmmc/sdmmc_ioreg.h:1.3.2.1 --- src/sys/dev/sdmmc/sdmmc_ioreg.h:1.3 Wed Jul 24 05:45:42 2019 +++ src/sys/dev/sdmmc/sdmmc_ioreg.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_ioreg.h,v 1.3 2019/07/24 05:45:42 msaitoh Exp $ */ +/* $NetBSD: sdmmc_ioreg.h,v 1.3.2.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmc_ioreg.h,v 1.4 2007/06/02 01:48:37 uwe Exp $ */ /* @@ -48,7 +48,7 @@ #define SD_ARG_CMD53_REG_MASK 0x1ffff #define SD_ARG_CMD53_LENGTH_SHIFT 0 #define SD_ARG_CMD53_LENGTH_MASK 0x1ff -#define SD_ARG_CMD53_LENGTH_MAX 64 /* XXX should be 511? */ +#define SD_ARG_CMD53_LENGTH_MAX 64 /* 48-bit response decoding (32 bits w/o CRC) */ #define MMC_R4(resp) ((resp)[0]) @@ -68,16 +68,19 @@ #define CCCR_CCCR_REV_1_00 0 #define CCCR_CCCR_REV_1_10 1 #define CCCR_CCCR_REV_1_20 2 +#define CCCR_CCCR_REV_3_00 3 #define SD_IO_CCCR_SDIO_REV(r) (((r) >> 4) & 0xf) #define CCCR_SDIO_REV_1_00 0 #define CCCR_SDIO_REV_1_10 1 #define CCCR_SDIO_REV_1_20 2 /* (unreleased) */ #define CCCR_SDIO_REV_2_00 3 +#define CCCR_SDIO_REV_3_00 4 #define SD_IO_CCCR_SPEC_REV 0x01 #define SD_IO_CCCR_SD_PHYS_SPEC_VER(r) ((r) & 0xf) #define CCCR_SD_PHYS_SPEC_VER_1_01 0 #define CCCR_SD_PHYS_SPEC_VER_1_10 1 #define CCCR_SD_PHYS_SPEC_VER_2_00 2 +#define CCCR_SD_PHYS_SPEC_VER_3_00 3 #define SD_IO_CCCR_FN_ENABLE 0x02 #define SD_IO_CCCR_FN_IOREADY 0x03 #define SD_IO_CCCR_FN_INTEN 0x04 @@ -85,9 +88,13 @@ #define SD_IO_CCCR_FN_INTPENDING 0x05 #define SD_IO_CCCR_CTL 0x06 #define CCCR_CTL_RES (1<<3) +#define CCCR_CTL_AS(x) ((x) & 0x7) #define SD_IO_CCCR_BUS_WIDTH 0x07 #define CCCR_BUS_WIDTH_4 (2<<0) #define CCCR_BUS_WIDTH_1 (0<<0) +#define CCCR_BUS_ECSI (1<<5) +#define CCCR_BUS_SCSI (1<<6) +#define CCCR_BUS_NOCD (1<<7) #define SD_IO_CCCR_CAPABILITY 0x08 #define CCCR_CAPS_SDC (1<<0) #define CCCR_CAPS_SMB (1<<1) /* Multi-Block support */ @@ -100,6 +107,8 @@ #define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */ #define SD_IO_CCCR_BUS_SUSPEND 0x0c #define SD_IO_CCCR_FUNC_SELECT 0x0d +#define SD_IO_CCCR_EXEC_FLAGS 0x0e +#define SD_IO_CCCR_READY_FLAGS 0x0f #define CCCR_FUNC_FS(r) ((r) & 0xf) #define CCCR_FUNC_FS_FN(fn) ((fn) & 0x7) #define CCCR_FUNC_FS_MEM 8 @@ -110,12 +119,36 @@ #define SD_IO_CCCR_HIGH_SPEED 0x13 #define CCCR_HIGH_SPEED_SHS (1<<0) /* Support High-Speed */ #define CCCR_HIGH_SPEED_EHS (1<<1) /* Enable High-Speed */ +#define CCCR_HIGH_SPEED_SDR50 (2<<1) +#define CCCR_HIGH_SPEED_SDR104 (3<<1) +#define CCCR_HIGH_SPEED_DDR50 (4<<1) +#define SD_IO_CCCR_UHS 0x14 +#define CCCR_UHS_SDR50 (1<<0) +#define CCCR_UHS_SDR104 (1<<1) +#define CCCR_UHS_DDR50 (1<<2) +#define SD_IO_DRIVE_STRENGTH 0x15 +#define CCCR_DRIVE_SDTA (1<<0) +#define CCCR_DRIVE_SDTC (1<<1) +#define CCCR_DRIVE_SDTD (1<<2) /* Function Basic Registers (FBR) */ #define SD_IO_FBR_START 0x00100 #define SD_IO_FBR_SIZE 0x100 #define SD_IO_FBR(func) ((((func) - 1) * SD_IO_FBR_SIZE) + SD_IO_FBR_START) + +/* FBR offsets */ +#define SD_IO_FBR_BASIC 0x00 #define FBR_STD_FUNC_IF_CODE(v) ((v) & 0x0f) +#define FBR_STD_FUNC_CSA(v) ((v) & 0x40) /* supports CSA */ +#define FBR_STD_FUNC_CSAE(v) ((v) & 0x80) /* enable CSA */ +#define SD_IO_FBR_EXT 0x01 +#define SD_IO_FBR_PWR 0x02 +#define FBR_PWR_SPS (1<<0) /* support power selection */ +#define FBR_PWR_EPS (1<<1) /* enable low power selection */ +#define SD_IO_FBR_CIS 0x09 /* 0x109-0x10b */ +#define SD_IO_FBR_CSA 0x0c /* 0x10c-0x10e */ +#define SD_IO_FBR_DATA 0x0f +#define SD_IO_FBR_BLOCKLEN 0x10 /* 0x110-0x111 */ /* Card Information Structure (CIS) */ #define SD_IO_CIS_START 0x01000 @@ -131,5 +164,6 @@ #define SD_IO_SFIC_PHS 0x6 #define SD_IO_SFIC_WLAN 0x7 #define SD_IO_SFIC_ATA 0x8 /* Embedded SDIO-ATA */ +#define SD_IO_SFIC_EXTENDED 0xf /* See next byte */ #endif /* _SDMMC_IOREG_H_ */ Index: src/sys/dev/sdmmc/sdmmc_mem.c diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.68 src/sys/dev/sdmmc/sdmmc_mem.c:1.68.2.1 --- src/sys/dev/sdmmc/sdmmc_mem.c:1.68 Thu Jun 6 20:50:46 2019 +++ src/sys/dev/sdmmc/sdmmc_mem.c Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_mem.c,v 1.68 2019/06/06 20:50:46 jmcneill Exp $ */ +/* $NetBSD: sdmmc_mem.c,v 1.68.2.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -45,7 +45,7 @@ /* Routines for SD/MMC memory cards. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.68 2019/06/06 20:50:46 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.68.2.1 2020/02/25 18:40:43 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -651,7 +651,7 @@ sdmmc_mem_send_op_cond(struct sdmmc_soft } error = ETIMEDOUT; - sdmmc_delay(10000); + sdmmc_pause(10000, NULL); } if (ocrp != NULL) { if (error == 0 && @@ -676,7 +676,7 @@ sdmmc_mem_send_if_cond(struct sdmmc_soft memset(&cmd, 0, sizeof(cmd)); cmd.c_arg = ocr; - cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R7 | SCF_RSP_SPI_R7; + cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R7 | SCF_RSP_SPI_R7 | SCF_TOUT_OK; cmd.c_opcode = SD_SEND_IF_COND; error = sdmmc_mmc_command(sc, &cmd); @@ -771,7 +771,7 @@ sdmmc_mem_execute_tuning(struct sdmmc_so break; case 208000: timing = SDMMC_TIMING_UHS_SDR104; - break; + break; default: return 0; } @@ -1057,7 +1057,7 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s } /* - * HS_TIMING must be set to “0x1” before setting BUS_WIDTH + * HS_TIMING must be set to 0x1 before setting BUS_WIDTH * for dual data rate operation */ if (ddr && Index: src/sys/dev/sdmmc/sdmmcdevs diff -u src/sys/dev/sdmmc/sdmmcdevs:1.4 src/sys/dev/sdmmc/sdmmcdevs:1.4.4.1 --- src/sys/dev/sdmmc/sdmmcdevs:1.4 Sat Dec 29 04:58:52 2018 +++ src/sys/dev/sdmmc/sdmmcdevs Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ - $NetBSD: sdmmcdevs,v 1.4 2018/12/29 04:58:52 thorpej Exp $ + $NetBSD: sdmmcdevs,v 1.4.4.1 2020/02/25 18:40:43 martin Exp $ /* $OpenBSD: sdmmcdevs,v 1.8 2007/05/11 17:16:16 mglocker Exp $ */ /* @@ -25,6 +25,7 @@ vendor TOSHIBA 0x0098 Toshiba vendor SOCKETCOM 0x0104 Socket Communications, Inc. vendor REALTEK 0x024c Realtek vendor ATHEROS 0x0271 Atheros +vendor BROADCOM 0x02d0 Broadcom vendor SYCHIP 0x02db SyChip Inc. vendor SPECTEC 0x02fe Spectec Computer Co., Ltd vendor MEDIATEK 0x037a MediaTek Inc. @@ -39,12 +40,27 @@ vendor ABOCOM 0x13d1 AboCom Systems, I /* AboCom Systems, Inc. */ product ABOCOM SDW11G 0xac02 SDW11G -/* Atheros */ +/* Atheros */ product ATHEROS AR6001_8 0x0108 AR6001 product ATHEROS AR6001_9 0x0109 AR6001 product ATHEROS AR6001_a 0x010a AR6001 product ATHEROS AR6001_b 0x010b AR6001 +/* Broadcom */ +product BROADCOM BCM4324 0x4324 BCM 4324 +product BROADCOM BCM4329 0x4329 BCM 4329 +product BROADCOM BCM4330 0x4330 BCM 4330 +product BROADCOM BCM4334 0x4334 BCM 4334 +product BROADCOM BCM4339 0x4339 BCM 4339 +product BROADCOM BCM4345 0x4345 BCM 4345 +product BROADCOM BCM4354 0x4354 BCM 4354 +product BROADCOM BCM43143 0xa887 BCM 43143 +product BROADCOM BCM43340 0xa94c BCM 43140 +product BROADCOM BCM43341 0xa94d BCM 43141 +product BROADCOM BCM43362 0xa962 BCM 43362 +product BROADCOM BCM43430 0xa9a6 BCM 43430 +product BROADCOM BCM43455 0xa9bf BCM 43455 + /* C-guys, Inc. */ product CGUYS TIACX100 0x0001 TI ACX100 SD-Link11b WiFi Card product CGUYS SDFMRADIO2 0x0005 C-guys SD FM Radio 2 Index: src/sys/dev/sdmmc/sdmmcvar.h diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.30 src/sys/dev/sdmmc/sdmmcvar.h:1.30.4.1 --- src/sys/dev/sdmmc/sdmmcvar.h:1.30 Mon Feb 25 19:28:00 2019 +++ src/sys/dev/sdmmc/sdmmcvar.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcvar.h,v 1.30 2019/02/25 19:28:00 jmcneill Exp $ */ +/* $NetBSD: sdmmcvar.h,v 1.30.4.1 2020/02/25 18:40:43 martin Exp $ */ /* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -185,6 +185,7 @@ struct sdmmc_function { uint16_t rca; /* relative card address */ int interface; /* SD/MMC:0, SDIO:standard interface */ int width; /* bus width */ + u_int blklen; /* block length */ int flags; #define SFF_ERROR 0x0001 /* function is poo; ignore it */ #define SFF_SDHC 0x0002 /* SD High Capacity card */ @@ -356,16 +357,20 @@ void sdmmc_dump_data(const char *, void int sdmmc_io_enable(struct sdmmc_softc *); void sdmmc_io_scan(struct sdmmc_softc *); int sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_io_set_blocklen(struct sdmmc_function *, int); uint8_t sdmmc_io_read_1(struct sdmmc_function *, int); uint16_t sdmmc_io_read_2(struct sdmmc_function *, int); uint32_t sdmmc_io_read_4(struct sdmmc_function *, int); int sdmmc_io_read_multi_1(struct sdmmc_function *, int, u_char *, int); +int sdmmc_io_read_region_1(struct sdmmc_function *, int, u_char *, int); void sdmmc_io_write_1(struct sdmmc_function *, int, uint8_t); void sdmmc_io_write_2(struct sdmmc_function *, int, uint16_t); void sdmmc_io_write_4(struct sdmmc_function *, int, uint32_t); int sdmmc_io_write_multi_1(struct sdmmc_function *, int, u_char *, int); +int sdmmc_io_write_region_1(struct sdmmc_function *, int, u_char *, int); int sdmmc_io_function_enable(struct sdmmc_function *); void sdmmc_io_function_disable(struct sdmmc_function *); +int sdmmc_io_function_abort(struct sdmmc_function *); int sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *); void sdmmc_print_cis(struct sdmmc_function *); @@ -385,4 +390,6 @@ int sdmmc_mem_write_block(struct sdmmc_f int sdmmc_mem_discard(struct sdmmc_function *, uint32_t, uint32_t); int sdmmc_mem_flush_cache(struct sdmmc_function *, bool); +void sdmmc_pause(u_int, kmutex_t *); + #endif /* _SDMMCVAR_H_ */ Index: src/sys/net/if_media.h diff -u src/sys/net/if_media.h:1.65 src/sys/net/if_media.h:1.65.2.1 --- src/sys/net/if_media.h:1.65 Fri May 17 07:37:12 2019 +++ src/sys/net/if_media.h Tue Feb 25 18:40:43 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: if_media.h,v 1.65 2019/05/17 07:37:12 msaitoh Exp $ */ +/* $NetBSD: if_media.h,v 1.65.2.1 2020/02/25 18:40:43 martin Exp $ */ /*- * Copyright (c) 1998, 2000, 2001 The NetBSD Foundation, Inc. @@ -373,6 +373,7 @@ #define IFM_IEEE80211_OFDM27 23 /* OFDM 27Mbps */ /* NB: not enough bits to express MCS fully */ #define IFM_IEEE80211_MCS 24 /* HT MCS rate */ +#define IFM_IEEE80211_VHT 25 /* VHT MCS rate */ /* IFM_OMASK bits */ #define IFM_IEEE80211_ADHOC 0x00000100 /* Operate in Adhoc mode */ @@ -390,6 +391,7 @@ #define IFM_IEEE80211_FH 0x00040000 /* 2 GHz, GFSK mode */ #define IFM_IEEE80211_11NA 0x00050000 /* 5Ghz, HT mode */ #define IFM_IEEE80211_11NG 0x00060000 /* 2Ghz, HT mode */ +#define IFM_IEEE80211_11AC 0x00070000 /* 2Ghz/5Ghz, VHT mode */ /* @@ -628,6 +630,8 @@ struct ifmedia_description { { IFM_IEEE80211 | IFM_IEEE80211_OFDM3, "OFDM/3Mbps" }, \ { IFM_IEEE80211 | IFM_IEEE80211_OFDM4, "OFDM/4.5Mbps" }, \ { IFM_IEEE80211 | IFM_IEEE80211_OFDM27, "OFDM/27Mbps" }, \ + { IFM_IEEE80211 | IFM_IEEE80211_MCS, "HT" }, \ + { IFM_IEEE80211 | IFM_IEEE80211_VHT, "VHT" }, \ \ { 0, NULL }, \ } @@ -641,6 +645,7 @@ struct ifmedia_description { { IFM_IEEE80211 | IFM_IEEE80211_FH, "fh" }, \ { IFM_IEEE80211 | IFM_IEEE80211_11NA, "11na" }, \ { IFM_IEEE80211 | IFM_IEEE80211_11NG, "11ng" }, \ + { IFM_IEEE80211 | IFM_IEEE80211_11AC, "11ac" }, \ { 0, NULL }, \ } Added files: Index: src/sys/dev/sdmmc/if_bwfm_sdio.h diff -u /dev/null src/sys/dev/sdmmc/if_bwfm_sdio.h:1.2.4.2 --- /dev/null Tue Feb 25 18:40:44 2020 +++ src/sys/dev/sdmmc/if_bwfm_sdio.h Tue Feb 25 18:40:43 2020 @@ -0,0 +1,209 @@ +/* $OpenBSD: if_bwfm_sdio.h,v 1.2 2018/05/19 10:43:10 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2018 Patrick Wildt <patr...@blueri.se> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Registers */ +#define BWFM_SDIO_CCCR_CARDCAP 0xf0 +#define BWFM_SDIO_CCCR_CARDCAP_CMD14_SUPPORT (1 << 1) +#define BWFM_SDIO_CCCR_CARDCAP_CMD14_EXT (1 << 2) +#define BWFM_SDIO_CCCR_CARDCAP_CMD_NODEC (1 << 3) +#define BWFM_SDIO_CCCR_CARDCTRL 0xf1 +#define BWFM_SDIO_CCCR_CARDCTRL_WLANRESET (1 << 1) +#define BWFM_SDIO_CCCR_SEPINT 0xf2 +#define BWFM_SDIO_CCCR_SEPINT_MASK 0x01 +#define BWFM_SDIO_CCCR_SEPINT_OE (1 << 1) +#define BWFM_SDIO_CCCR_SEPINT_ACT_HI (1 << 2) + +#define BWFM_SDIO_WATERMARK 0x10008 +#define BWFM_SDIO_DEVICE_CTL 0x10009 +#define BWFM_SDIO_DEVICE_CTL_SETBUSY 0x01 +#define BWFM_SDIO_DEVICE_CTL_SPI_INTR_SYNC 0x02 +#define BWFM_SDIO_DEVICE_CTL_CA_INT_ONLY 0x04 +#define BWFM_SDIO_DEVICE_CTL_PADS_ISO 0x08 +#define BWFM_SDIO_DEVICE_CTL_SB_RST_CTL 0x30 +#define BWFM_SDIO_DEVICE_CTL_RST_CORECTL 0x00 +#define BWFM_SDIO_DEVICE_CTL_RST_BPRESET 0x10 +#define BWFM_SDIO_DEVICE_CTL_RST_NOBPRESET 0x20 +#define BWFM_SDIO_FUNC1_SBADDRLOW 0x1000A +#define BWFM_SDIO_FUNC1_SBADDRMID 0x1000B +#define BWFM_SDIO_FUNC1_SBADDRHIGH 0x1000C +#define BWFM_SDIO_FUNC1_FRAMECTRL 0x1000D +#define BWFM_SDIO_FUNC1_FRAMECTRL_RF_TERM (1 << 0) +#define BWFM_SDIO_FUNC1_FRAMECTRL_WF_TERM (1 << 1) +#define BWFM_SDIO_FUNC1_FRAMECTRL_CRC4WOOS (1 << 2) +#define BWFM_SDIO_FUNC1_FRAMECTRL_ABORTALL (1 << 3) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR 0x1000E +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ALP 0x01 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HT 0x02 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_ILP 0x04 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL_REQ 0x08 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL_REQ 0x10 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_FORCE_HW_CLKREQ_OFF 0x20 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL 0x40 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL 0x80 +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_CSR_MASK 0x1F +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_HT_AVAIL | \ + BWFM_SDIO_FUNC1_CHIPCLKCSR_ALP_AVAIL) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) \ + ((regval) & BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval) \ + (((regval) & BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) == BWFM_SDIO_FUNC1_CHIPCLKCSR_AVBITS) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPONLY(regval) \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) && \ + !BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval)) +#define BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(regval, alponly) \ + (BWFM_SDIO_FUNC1_CHIPCLKCSR_ALPAV(regval) && \ + (alponly ? 1 : BWFM_SDIO_FUNC1_CHIPCLKCSR_HTAV(regval))) +#define BWFM_SDIO_FUNC1_SDIOPULLUP 0x1000F +#define BWFM_SDIO_FUNC1_WFRAMEBCLO 0x10019 +#define BWFM_SDIO_FUNC1_WFRAMEBCHI 0x1001A +#define BWFM_SDIO_FUNC1_RFRAMEBCLO 0x1001B +#define BWFM_SDIO_FUNC1_RFRAMEBCHI 0x1001C +#define BWFM_SDIO_FUNC1_MESBUSYCTRL 0x1001D +#define BWFM_SDIO_FUNC1_WAKEUPCTRL 0x1001E +#define BWFM_SDIO_FUNC1_WAKEUPCTRL_HTWAIT (1 << 1) +#define BWFM_SDIO_FUNC1_SLEEPCSR 0x1001F +#define BWFM_SDIO_FUNC1_SLEEPCSR_KSO (1 << 0) +#define BWFM_SDIO_FUNC1_SLEEPCSR_DEVON (1 << 2) + +#define BWFM_SDIO_SB_OFT_ADDR_PAGE 0x08000 +#define BWFM_SDIO_SB_OFT_ADDR_MASK 0x07FFF +#define BWFM_SDIO_SB_ACCESS_2_4B_FLAG 0x08000 + +/* Protocol defines */ +#define SDPCM_PROT_VERSION 4 +#define SDPCM_PROT_VERSION_SHIFT 16 +#define SDPCM_PROT_VERSION_MASK 0x00ff0000 +#define SDPCM_SHARED_VERSION 0x0003 +#define SDPCM_SHARED_VERSION_MASK 0x00FF +#define SDPCM_SHARED_ASSERT_BUILT 0x0100 +#define SDPCM_SHARED_ASSERT 0x0200 +#define SDPCM_SHARED_TRAP 0x0400 + +#define SDPCMD_INTSTATUS 0x020 +#define SDPCMD_INTSTATUS_SMB_SW0 (1 << 0) /* To SB Mail S/W interrupt 0 */ +#define SDPCMD_INTSTATUS_SMB_SW1 (1 << 1) /* To SB Mail S/W interrupt 1 */ +#define SDPCMD_INTSTATUS_SMB_SW2 (1 << 2) /* To SB Mail S/W interrupt 2 */ +#define SDPCMD_INTSTATUS_SMB_SW3 (1 << 3) /* To SB Mail S/W interrupt 3 */ +#define SDPCMD_INTSTATUS_SMB_SW_MASK 0x0000000f /* To SB Mail S/W interrupts mask */ +#define SDPCMD_INTSTATUS_SMB_SW_SHIFT 0 /* To SB Mail S/W interrupts shift */ +#define SDPCMD_INTSTATUS_HMB_SW0 (1 << 4) /* To Host Mail S/W interrupt 0 */ +#define SDPCMD_INTSTATUS_HMB_SW1 (1 << 5) /* To Host Mail S/W interrupt 1 */ +#define SDPCMD_INTSTATUS_HMB_SW2 (1 << 6) /* To Host Mail S/W interrupt 2 */ +#define SDPCMD_INTSTATUS_HMB_SW3 (1 << 7) /* To Host Mail S/W interrupt 3 */ +#define SDPCMD_INTSTATUS_HMB_FC_STATE SDPCMD_INTSTATUS_HMB_SW0 +#define SDPCMD_INTSTATUS_HMB_FC_CHANGE SDPCMD_INTSTATUS_HMB_SW1 +#define SDPCMD_INTSTATUS_HMB_FRAME_IND SDPCMD_INTSTATUS_HMB_SW2 +#define SDPCMD_INTSTATUS_HMB_HOST_INT SDPCMD_INTSTATUS_HMB_SW3 +#define SDPCMD_INTSTATUS_HMB_SW_MASK 0x000000f0 /* To Host Mail S/W interrupts mask */ +#define SDPCMD_INTSTATUS_HMB_SW_SHIFT 4 /* To Host Mail S/W interrupts shift */ +#define SDPCMD_INTSTATUS_WR_OOSYNC (1 << 8) /* Write Frame Out Of Sync */ +#define SDPCMD_INTSTATUS_RD_OOSYNC (1 << 9) /* Read Frame Out Of Sync */ +#define SDPCMD_INTSTATUS_PC (1 << 10)/* descriptor error */ +#define SDPCMD_INTSTATUS_PD (1 << 11)/* data error */ +#define SDPCMD_INTSTATUS_DE (1 << 12)/* Descriptor protocol Error */ +#define SDPCMD_INTSTATUS_RU (1 << 13)/* Receive descriptor Underflow */ +#define SDPCMD_INTSTATUS_RO (1 << 14)/* Receive fifo Overflow */ +#define SDPCMD_INTSTATUS_XU (1 << 15)/* Transmit fifo Underflow */ +#define SDPCMD_INTSTATUS_RI (1 << 16)/* Receive Interrupt */ +#define SDPCMD_INTSTATUS_BUSPWR (1 << 17)/* SDIO Bus Power Change (rev 9) */ +#define SDPCMD_INTSTATUS_XMTDATA_AVAIL (1 << 23)/* bits in fifo */ +#define SDPCMD_INTSTATUS_XI (1 << 24)/* Transmit Interrupt */ +#define SDPCMD_INTSTATUS_RF_TERM (1 << 25)/* Read Frame Terminate */ +#define SDPCMD_INTSTATUS_WF_TERM (1 << 26)/* Write Frame Terminate */ +#define SDPCMD_INTSTATUS_PCMCIA_XU (1 << 27)/* PCMCIA Transmit FIFO Underflow */ +#define SDPCMD_INTSTATUS_SBINT (1 << 28)/* sbintstatus Interrupt */ +#define SDPCMD_INTSTATUS_CHIPACTIVE (1 << 29)/* chip from doze to active state */ +#define SDPCMD_INTSTATUS_SRESET (1 << 30)/* CCCR RES interrupt */ +#define SDPCMD_INTSTATUS_IOE2 (1U << 31)/* CCCR IOE2 Bit Changed */ +#define SDPCMD_INTSTATUS_ERRORS (SDPCMD_INTSTATUS_PC | \ + SDPCMD_INTSTATUS_PD | \ + SDPCMD_INTSTATUS_DE | \ + SDPCMD_INTSTATUS_RU | \ + SDPCMD_INTSTATUS_RO | \ + SDPCMD_INTSTATUS_XU) +#define SDPCMD_INTSTATUS_DMA (SDPCMD_INTSTATUS_RI | \ + SDPCMD_INTSTATUS_XI | \ + SDPCMD_INTSTATUS_ERRORS) +#define SDPCMD_HOSTINTMASK 0x024 +#define SDPCMD_INTMASK 0x028 +#define SDPCMD_SBINTSTATUS 0x02c +#define SDPCMD_SBINTMASK 0x030 +#define SDPCMD_FUNCTINTMASK 0x034 +#define SDPCMD_TOSBMAILBOX 0x040 +#define SDPCMD_TOSBMAILBOX_NAK (1 << 0) +#define SDPCMD_TOSBMAILBOX_INT_ACK (1 << 1) +#define SDPCMD_TOSBMAILBOX_USE_OOB (1 << 2) +#define SDPCMD_TOSBMAILBOX_DEV_INT (1 << 3) +#define SDPCMD_TOHOSTMAILBOX 0x044 +#define SDPCMD_TOSBMAILBOXDATA 0x048 +#define SDPCMD_TOHOSTMAILBOXDATA 0x04C +#define SDPCMD_TOHOSTMAILBOXDATA_NAKHANDLED (1 << 0) +#define SDPCMD_TOHOSTMAILBOXDATA_DEVREADY (1 << 1) +#define SDPCMD_TOHOSTMAILBOXDATA_FC (1 << 2) +#define SDPCMD_TOHOSTMAILBOXDATA_FWREADY (1 << 3) +#define SDPCMD_TOHOSTMAILBOXDATA_FWHALT (1 << 4) + +struct bwfm_sdio_hwhdr { + uint16_t frmlen; + uint16_t cksum; +}; + +struct bwfm_sdio_hwexthdr { + uint16_t pktlen; + uint8_t res0; + uint8_t flags; + uint16_t res1; + uint16_t padlen; +}; + +struct bwfm_sdio_swhdr { + uint8_t seqnr; + uint8_t chanflag; /* channel + flag */ +#define BWFM_SDIO_SWHDR_CHANNEL_CONTROL 0x00 +#define BWFM_SDIO_SWHDR_CHANNEL_EVENT 0x01 +#define BWFM_SDIO_SWHDR_CHANNEL_DATA 0x02 +#define BWFM_SDIO_SWHDR_CHANNEL_GLOM 0x03 +#define BWFM_SDIO_SWHDR_CHANNEL_TEST 0x0F +#define BWFM_SDIO_SWHDR_CHANNEL_MASK 0x0F + uint8_t nextlen; + uint8_t dataoff; + uint8_t flowctl; + uint8_t maxseqnr; + uint16_t res0; +}; + +struct bwfm_sdio_sdpcm { + uint32_t flags; + uint32_t trap_addr; + uint32_t assert_exp_addr; + uint32_t assert_file_addr; + uint32_t assert_line; + uint32_t console_addr; + uint32_t msgtrace_addr; + uint8_t tag[32]; + uint32_t brpt_addr; +}; + +struct bwfm_sdio_console { + uint32_t vcons_in; + uint32_t vcons_out; + uint32_t log_buf; + uint32_t log_bufsz; + uint32_t log_idx; +};