Index: sys/dev/ic/bwfm.c =================================================================== RCS file: /cvsroot/src/sys/dev/ic/bwfm.c,v retrieving revision 1.14 diff -p -u -r1.14 bwfm.c --- sys/dev/ic/bwfm.c 2 Sep 2018 19:46:53 -0000 1.14 +++ sys/dev/ic/bwfm.c 31 Aug 2019 16:31:41 -0000 @@ -114,7 +114,8 @@ void bwfm_scan(struct bwfm_softc *); void bwfm_connect(struct bwfm_softc *); 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[] = { @@ -142,7 +143,7 @@ bwfm_attach(struct bwfm_softc *sc) int i, j, error; error = workqueue_create(&sc->sc_taskq, DEVNAME(sc), - bwfm_task, sc, PRI_NONE, IPL_NET, 0); + bwfm_task, sc, PRI_NONE, IPL_NET, WQ_MPSAFE); if (error != 0) { printf("%s: could not create workqueue\n", DEVNAME(sc)); return; @@ -304,9 +305,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 +321,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); } } @@ -770,6 +751,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 +1245,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) @@ -1455,8 +1485,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); @@ -1772,39 +1800,59 @@ bwfm_rx(struct bwfm_softc *sc, struct mb 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(); - if ((ifp->if_flags & IFF_RUNNING) != 0) { + //if ((ifp->if_flags & IFF_RUNNING) != 0) { m_set_rcvif(m, ifp); if_percpuq_enqueue(ifp->if_percpuq, m); - } + //} splx(s); } void -bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) +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; + } + + t->t_cmd = BWFM_TASK_RX_EVENT; + t->t_mbuf = m; + workqueue_enqueue(sc->sc_taskq, (struct work*)t, NULL); +} + +void +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 +1865,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 +1924,8 @@ bwfm_rx_event(struct bwfm_softc *sc, cha default: break; } + + m_freem(m); } void Index: sys/dev/ic/bwfmreg.h =================================================================== RCS file: /cvsroot/src/sys/dev/ic/bwfmreg.h,v retrieving revision 1.3 diff -p -u -r1.3 bwfmreg.h --- sys/dev/ic/bwfmreg.h 11 May 2018 07:41:11 -0000 1.3 +++ sys/dev/ic/bwfmreg.h 31 Aug 2019 16:31:41 -0000 @@ -644,3 +644,4 @@ struct bwfm_event { struct bwfm_ethhdr hdr; struct bwfm_event_msg msg; } __packed; + Index: sys/dev/ic/bwfmvar.h =================================================================== RCS file: /cvsroot/src/sys/dev/ic/bwfmvar.h,v retrieving revision 1.3 diff -p -u -r1.3 bwfmvar.h --- sys/dev/ic/bwfmvar.h 1 Sep 2018 22:01:03 -0000 1.3 +++ sys/dev/ic/bwfmvar.h 31 Aug 2019 16:31:41 -0000 @@ -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 { @@ -178,6 +181,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: sys/dev/sdmmc/if_bwfm_sdio.c =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/if_bwfm_sdio.c,v retrieving revision 1.3 diff -p -u -r1.3 if_bwfm_sdio.c --- sys/dev/sdmmc/if_bwfm_sdio.c 11 May 2018 07:41:11 -0000 1.3 +++ sys/dev/sdmmc/if_bwfm_sdio.c 31 Aug 2019 16:31:41 -0000 @@ -20,14 +20,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include @@ -37,20 +36,16 @@ #include +#include + #include +#include #include #include #include - -#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 #ifdef BWFM_DEBUG #define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) @@ -63,34 +58,122 @@ 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; + + 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 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, +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_attachhook(device_t); + +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, uint8_t); -void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, +void bwfm_sdio_write_4(struct bwfm_sdio_softc *, uint32_t, + uint32_t); + +uint32_t bwfm_sdio_dev_read(struct bwfm_sdio_softc *, uint32_t); +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, +uint32_t bwfm_sdio_buscore_read(struct bwfm_softc *, uint32_t); +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); +int bwfm_sdio_buscore_prepare(struct bwfm_softc *); +void bwfm_sdio_buscore_activate(struct bwfm_softc *, uint32_t); -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 *); +int bwfm_sdio_buf_read(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); +int bwfm_sdio_buf_write(struct bwfm_sdio_softc *, + struct sdmmc_function *, uint32_t, char *, size_t); + +struct mbuf *bwfm_sdio_newbuf(void); +void bwfm_qput(struct mbuf **, struct mbuf *); +struct mbuf *bwfm_qget(struct mbuf **); + +uint32_t bwfm_sdio_ram_read_write(struct bwfm_sdio_softc *, + uint32_t, char *, size_t, int); +uint32_t bwfm_sdio_frame_read_write(struct bwfm_sdio_softc *, + char *, size_t, int); + +int bwfm_sdio_intr(void *); +void bwfm_sdio_task(void *); +void bwfm_sdio_task1(struct bwfm_sdio_softc *); + +int bwfm_nvram_convert(u_char *, size_t, size_t *); +int bwfm_sdio_load_microcode(struct bwfm_sdio_softc *, + u_char *, size_t, u_char *, size_t); +void bwfm_sdio_clkctl(struct bwfm_sdio_softc *, + enum bwfm_sdio_clkstate, bool); +void bwfm_sdio_htclk(struct bwfm_sdio_softc *, bool, bool); + +int bwfm_sdio_bus_sleep(struct bwfm_sdio_softc *, bool, bool); +void bwfm_sdio_readshared(struct bwfm_sdio_softc *); + +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 *); + +int bwfm_sdio_tx_ok(struct bwfm_sdio_softc *); +void bwfm_sdio_tx_frames(struct bwfm_sdio_softc *); +void bwfm_sdio_tx_ctrlframe(struct bwfm_sdio_softc *, + struct mbuf *); +void bwfm_sdio_tx_dataframe(struct bwfm_sdio_softc *, + struct mbuf *); + +void bwfm_sdio_rx_frames(struct bwfm_sdio_softc *); +void bwfm_sdio_rx_glom(struct bwfm_sdio_softc *, + uint16_t *, int, uint16_t *); + +#ifdef BWFM_DEBUG +void bwfm_sdio_debug_console(struct bwfm_sdio_softc *); +#endif struct bwfm_bus_ops bwfm_sdio_bus_ops = { .bs_init = NULL, @@ -113,21 +196,54 @@ 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); +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 + }, +}; + 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 ... */ @@ -148,33 +264,46 @@ bwfm_sdio_attach(device_t parent, device 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); + 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]->sc, sc->sc_sf[1], 64); + sdmmc_io_set_blocklen(sc->sc_sf[2]->sc, 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; - } + /* Enable Function 1. */ + if (sdmmc_io_function_enable(sc->sc_sf[1]) != 0) { + printf("%s: cannot enable function 1\n", DEVNAME(sc)); + return; + } - DPRINTFN(2, ("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), - bwfm_sdio_read_4(sc, 0x18000000))); + DPRINTF(("%s: F1 signature read @0x18000000=%x\n", DEVNAME(sc), + bwfm_sdio_read_4(sc, 0x18000000))); /* Force PLL off */ bwfm_sdio_write_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR, @@ -184,14 +313,29 @@ 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; + } + + 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; + } + + 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); + } } /* TODO: drive strength */ - 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); + 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,14 +343,193 @@ 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); +} + +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; + 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: + 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"; + } + 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; + } + + 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 (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); + + 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_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); + } + + sc->sc_ih = sdmmc_intr_establish(sc->sc_sc.sc_dev->dv_parent, + bwfm_sdio_intr, sc, DEVNAME(sc)); + if (sc->sc_ih == 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]); + 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); err: - free(sc->sc_sf, M_DEVBUF); + return; } int @@ -214,21 +537,37 @@ bwfm_sdio_detach(struct device *self, in { 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) { + sdmmc_intr_disable(sc->sc_sf[1]); + sdmmc_intr_disestablish(sc->sc_ih); + } + if (sc->sc_bwfm_attached) + bwfm_detach(&sc->sc_sc, flags); - free(sc->sc_sf, M_DEVBUF); + kmem_free(sc->sc_sf, sc->sc_sf_size); + kmem_free(sc->sc_bounce_buf, sc->sc_bounce_size); + + 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) +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, @@ -261,10 +600,9 @@ 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; @@ -307,9 +645,8 @@ 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,14 +665,135 @@ bwfm_sdio_write_4(struct bwfm_sdio_softc sdmmc_io_write_4(sf, addr, data); } +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; +} + +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; +} + +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; + } + + return err; +} + +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); + + return err; +} + +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; +} + +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); +} + 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; } @@ -344,7 +802,10 @@ 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 @@ -352,7 +813,9 @@ bwfm_sdio_buscore_prepare(struct bwfm_so { 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 +828,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 +841,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,7 +852,10 @@ 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 @@ -400,56 +868,953 @@ 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); +} + +struct mbuf * +bwfm_sdio_newbuf(void) +{ + struct mbuf *m; + + 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; +} + +struct mbuf * +bwfm_qget(struct mbuf **q) +{ + struct mbuf *m = NULL; + + if (*q != NULL) { + m = *q; + *q = m->m_next; + m->m_next = NULL; + } + + return m; +} + +void +bwfm_qput(struct mbuf **q, struct mbuf *m) +{ + + if (*q == NULL) + *q = m; + else + m_cat(*q, m); } int -bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m) +bwfm_sdio_txcheck(struct bwfm_softc *bwfm) { - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + struct bwfm_sdio_softc *sc = (void *)bwfm; + int error = 0; - return 0; + mutex_enter(&sc->sc_lock); + if (sc->sc_tx_count >= 64) + error = ENOBUFS; + mutex_exit(&sc->sc_lock); + + return error; } int -bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf **mp) { -#ifdef BWFM_DEBUG struct bwfm_sdio_softc *sc = (void *)bwfm; -#endif - int ret = 1; - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + if (sc->sc_tx_count >= 64) { + printf("%s: tx count limit reached\n",DEVNAME(sc)); + return ENOBUFS; + } + + 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_intr(sc); + + return 0; } 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_intr(sc); + + return 0; + +fail: + return ENOBUFS; +} + +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; +} + +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; +} + +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)); +} + +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 < 1000; i++) { + if (BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, + sc->sc_alp_only)) + break; + clkctl = bwfm_sdio_read_1(sc, BWFM_SDIO_FUNC1_CHIPCLKCSR +); + delay(1000); + } + if (!BWFM_SDIO_FUNC1_CHIPCLKCSR_CLKAV(clkctl, sc->sc_alp_only)) +{ + 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); + } +} + +#if notyet +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__)); +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; - return ret; + 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); } int -bwfm_sdio_rxctl(struct bwfm_softc *bwfm, char *buf, size_t *len) +bwfm_sdio_intr(void *v) { + struct bwfm_sdio_softc *sc = (void *)v; + + DPRINTF(("%s: sdio_intr\n", DEVNAME(sc))); + + mutex_enter(&sc->sc_intr_lock); + if (!sc->sc_task_queued) { + sc->sc_task_queued = true; + sdmmc_add_task(sc->sc_sf[1]->sc, &sc->sc_task); + } + mutex_exit(&sc->sc_intr_lock); + return 1; +} + +void +bwfm_sdio_task(void *v) +{ + struct bwfm_sdio_softc *sc = (void *)v; + + mutex_enter(&sc->sc_intr_lock); + while (sc->sc_task_queued) { + sc->sc_task_queued = false; + mutex_exit(&sc->sc_intr_lock); + + mutex_enter(&sc->sc_lock); + bwfm_sdio_task1(sc); #ifdef BWFM_DEBUG - struct bwfm_sdio_softc *sc = (void *)bwfm; + 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); +} + +void +bwfm_sdio_task1(struct bwfm_sdio_softc *sc) +{ + uint32_t clkctl, devctl, intstat, hostint; + + 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; + } + } + + 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); + /* XXX fc state */ + if (intstat) + bwfm_sdio_dev_write(sc, BWFM_SDPCMD_INTSTATUS, intstat); + + 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) + intstat |= SDPCMD_INTSTATUS_HMB_FRAME_IND; + if (hostint & SDPCMD_TOHOSTMAILBOXDATA_DEVREADY || + hostint & SDPCMD_TOHOSTMAILBOXDATA_FWREADY) + bwfm_sdio_readshared(sc); + } + + /* FIXME: Might stall if we don't when not set. */ + if (intstat & SDPCMD_INTSTATUS_HMB_FRAME_IND) + bwfm_sdio_rx_frames(sc); + + if (MBUFQ_FIRST(&sc->sc_tx_queue)) + bwfm_sdio_tx_frames(sc); +} + +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; +} + +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); + } +} + +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); +} + +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--; +} + +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; + + 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; +} + +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; + + 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; + } 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; + } - DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + sc->sc_tx_max_seq = swhdr->maxseqnr; - return ret; + 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)) + 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); + sublen = kmem_zalloc(nsub * sizeof(uint16_t), KM_SLEEP); + memcpy(sublen, data, nsub * sizeof(uint16_t)); + bwfm_sdio_rx_glom(sc, sublen, nsub, &nextlen); + kmem_free(sublen, nsub * sizeof(uint16_t)); + break; + default: + printf("%s: unknown channel\n", DEVNAME(sc)); + break; + } + } } + +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; + + 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); + return; + } + if (bwfm_sdio_frame_read_write(sc, mtod(m, char *), + le16toh(sublen[i]), 0)) { + m_freem(m0); + 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); + } +} + +#ifdef BWFM_DEBUG +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: sys/dev/sdmmc/if_bwfm_sdio.h =================================================================== RCS file: sys/dev/sdmmc/if_bwfm_sdio.h diff -N sys/dev/sdmmc/if_bwfm_sdio.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/sdmmc/if_bwfm_sdio.h 31 Aug 2019 16:31:41 -0000 @@ -0,0 +1,199 @@ +/* $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 + * + * 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_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_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; +}; Index: sys/dev/sdmmc/sdmmc.c =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc.c,v retrieving revision 1.36 diff -p -u -r1.36 sdmmc.c --- sys/dev/sdmmc/sdmmc.c 6 Nov 2018 16:01:38 -0000 1.36 +++ sys/dev/sdmmc/sdmmc.c 31 Aug 2019 16:31:41 -0000 @@ -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) && Index: sys/dev/sdmmc/sdmmc_cis.c =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc_cis.c,v retrieving revision 1.5 diff -p -u -r1.5 sdmmc_cis.c --- sys/dev/sdmmc/sdmmc_cis.c 28 Jan 2018 14:34:06 -0000 1.5 +++ sys/dev/sdmmc/sdmmc_cis.c 31 Aug 2019 16:31:41 -0000 @@ -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)); } @@ -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. Index: sys/dev/sdmmc/sdmmc_io.c =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc_io.c,v retrieving revision 1.14 diff -p -u -r1.14 sdmmc_io.c --- sys/dev/sdmmc/sdmmc_io.c 14 Oct 2018 17:37:40 -0000 1.14 +++ sys/dev/sdmmc/sdmmc_io.c 31 Aug 2019 16:31:41 -0000 @@ -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)) { @@ -395,8 +397,8 @@ sdmmc_io_rw_extended(struct sdmmc_softc cmd.c_flags = SCF_CMD_AC | 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; @@ -476,21 +478,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 +506,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; } @@ -539,7 +610,8 @@ 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) + if (sdmmc_io_rw_direct(sc, NULL, SD_IO_CCCR_CTL, &data, + SD_ARG_CMD52_WRITE) == 0) sdmmc_delay(100000); } @@ -597,11 +669,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 +683,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 +787,30 @@ sdmmc_intr_task(void *arg) sdmmc_chip_card_intr_ack(sc->sc_sct, sc->sc_sch); } + +int +sdmmc_io_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf, + int blklen) +{ + 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: sys/dev/sdmmc/sdmmc_ioreg.h =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmc_ioreg.h,v retrieving revision 1.3 diff -p -u -r1.3 sdmmc_ioreg.h --- sys/dev/sdmmc/sdmmc_ioreg.h 24 Jul 2019 05:45:42 -0000 1.3 +++ sys/dev/sdmmc/sdmmc_ioreg.h 31 Aug 2019 16:31:41 -0000 @@ -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 @@ -88,6 +91,9 @@ #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 +106,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 +118,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 +163,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: sys/dev/sdmmc/sdmmcvar.h =================================================================== RCS file: /cvsroot/src/sys/dev/sdmmc/sdmmcvar.h,v retrieving revision 1.30 diff -p -u -r1.30 sdmmcvar.h --- sys/dev/sdmmc/sdmmcvar.h 25 Feb 2019 19:28:00 -0000 1.30 +++ sys/dev/sdmmc/sdmmcvar.h 31 Aug 2019 16:31:41 -0000 @@ -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,14 +357,18 @@ 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_softc *, 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 *);