Hi folks. I'm new to network driver code. I've managed to get a working bwfm with the mentioned patches, though I haven't done a full release build yet. I've only tested an open network due to not having other choices, but I will soon test WPA. It seems to work for fetching files & pinging stuff.
I'm omitting the actual firmware from the diff because it's big and binary. Let me know what you think. I will commit this in a few days if nobody objects.
>From 4df83365c365371a15d2f6d837bd906add23bee0 Mon Sep 17 00:00:00 2001 From: coypu <[email protected]> Date: Fri, 4 May 2018 02:41:00 +0300 Subject: [PATCH 1/4] sync with openbsd bwfm adds a txcheck. set chip active/passive for more kinds of chips and add wrapper around detect chip RAM make bwfm_rx take an mbuf --- sys/dev/ic/bwfm.c | 333 ++++++++++++++++++++++++++++++----- sys/dev/ic/bwfmreg.h | 107 ++++++++++- sys/dev/ic/bwfmvar.h | 17 +- sys/dev/sdmmc/if_bwfm_sdio.c | 11 ++ sys/dev/usb/if_bwfm_usb.c | 50 +++++- 5 files changed, 466 insertions(+), 52 deletions(-) diff --git a/sys/dev/ic/bwfm.c b/sys/dev/ic/bwfm.c index 82bac1d2a4..8da9214833 100644 --- a/sys/dev/ic/bwfm.c +++ b/sys/dev/ic/bwfm.c @@ -21,7 +21,6 @@ #include <sys/systm.h> #include <sys/buf.h> #include <sys/kernel.h> -#include <sys/malloc.h> #include <sys/device.h> #include <sys/queue.h> #include <sys/socket.h> @@ -85,9 +84,16 @@ void bwfm_chip_ai_reset(struct bwfm_softc *, struct bwfm_core *, void bwfm_chip_dmp_erom_scan(struct bwfm_softc *); int bwfm_chip_dmp_get_regaddr(struct bwfm_softc *, uint32_t *, uint32_t *, uint32_t *); +int bwfm_chip_cr4_set_active(struct bwfm_softc *, const uint32_t); void bwfm_chip_cr4_set_passive(struct bwfm_softc *); +int bwfm_chip_ca7_set_active(struct bwfm_softc *, const uint32_t); void bwfm_chip_ca7_set_passive(struct bwfm_softc *); +int bwfm_chip_cm3_set_active(struct bwfm_softc *); void bwfm_chip_cm3_set_passive(struct bwfm_softc *); +void bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_tcm_rambase(struct bwfm_softc *); int bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *, int, int, char *, size_t *); @@ -107,7 +113,7 @@ struct ieee80211_channel *bwfm_bss2chan(struct bwfm_softc *, struct bwfm_bss_inf void bwfm_scan(struct bwfm_softc *); void bwfm_connect(struct bwfm_softc *); -void bwfm_rx(struct bwfm_softc *, char *, size_t); +void bwfm_rx(struct bwfm_softc *, struct mbuf *); void bwfm_rx_event(struct bwfm_softc *, char *, size_t); void bwfm_scan_node(struct bwfm_softc *, struct bwfm_bss_info *, size_t); @@ -133,7 +139,7 @@ bwfm_attach(struct bwfm_softc *sc) char fw_version[BWFM_DCMD_SMLEN]; uint32_t bandlist[3]; uint32_t tmp; - int i, error; + int i, j, error; error = workqueue_create(&sc->sc_taskq, DEVNAME(sc), bwfm_task, sc, PRI_NONE, IPL_NET, 0); @@ -203,8 +209,8 @@ bwfm_attach(struct bwfm_softc *sc) ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; - for (i = 0; i < __arraycount(bwfm_2ghz_channels); i++) { - uint8_t chan = bwfm_2ghz_channels[i]; + for (j = 0; j < __arraycount(bwfm_2ghz_channels); j++) { + uint8_t chan = bwfm_2ghz_channels[j]; ic->ic_channels[chan].ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_2GHZ); ic->ic_channels[chan].ic_flags = @@ -215,8 +221,8 @@ bwfm_attach(struct bwfm_softc *sc) case BWFM_BAND_5G: ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a; - for (i = 0; i < __arraycount(bwfm_5ghz_channels); i++) { - uint8_t chan = bwfm_5ghz_channels[i]; + for (j = 0; j < __arraycount(bwfm_5ghz_channels); j++) { + uint8_t chan = bwfm_5ghz_channels[j]; ic->ic_channels[chan].ic_freq = ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); ic->ic_channels[chan].ic_flags = @@ -307,6 +313,12 @@ bwfm_start(struct ifnet *ifp) continue; } + if (sc->sc_bus_ops->bs_txcheck(sc)) { + IF_PREPEND(&ifp->if_snd, m); + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; @@ -848,24 +860,22 @@ bwfm_chip_attach(struct bwfm_softc *sc) return 1; } - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) - bwfm_chip_cr4_set_passive(sc); - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) - bwfm_chip_ca7_set_passive(sc); - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) - bwfm_chip_cm3_set_passive(sc); + bwfm_chip_set_passive(sc); if (sc->sc_buscore_ops->bc_reset) { sc->sc_buscore_ops->bc_reset(sc); - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) - bwfm_chip_cr4_set_passive(sc); - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) - bwfm_chip_ca7_set_passive(sc); - if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) - bwfm_chip_cm3_set_passive(sc); + bwfm_chip_set_passive(sc); } - /* TODO: get raminfo */ + if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4)) != NULL) { + bwfm_chip_tcm_ramsize(sc, core); + bwfm_chip_tcm_rambase(sc); + } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_SYS_MEM)) != NULL) { + bwfm_chip_sysmem_ramsize(sc, core); + bwfm_chip_tcm_rambase(sc); + } else if ((core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM)) != NULL) { + bwfm_chip_socram_ramsize(sc, core); + } core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_CHIPCOMMON); sc->sc_chip.ch_cc_caps = sc->sc_buscore_ops->bc_read(sc, @@ -1117,16 +1127,116 @@ bwfm_chip_dmp_get_regaddr(struct bwfm_softc *sc, uint32_t *erom, } /* Core configuration */ +int +bwfm_chip_set_active(struct bwfm_softc *sc, const uint32_t rstvec) +{ + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) + return bwfm_chip_cr4_set_active(sc, rstvec); + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) + return bwfm_chip_ca7_set_active(sc, rstvec); + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) + return bwfm_chip_cm3_set_active(sc); + return 1; +} + +void +bwfm_chip_set_passive(struct bwfm_softc *sc) +{ + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4) != NULL) { + bwfm_chip_cr4_set_passive(sc); + return; + } + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7) != NULL) { + bwfm_chip_ca7_set_passive(sc); + return; + } + if (bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3) != NULL) { + bwfm_chip_cm3_set_passive(sc); + return; + } +} + +int +bwfm_chip_cr4_set_active(struct bwfm_softc *sc, const uint32_t rstvec) +{ + struct bwfm_core *core; + + sc->sc_buscore_ops->bc_activate(sc, rstvec); + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4); + sc->sc_chip.ch_core_reset(sc, core, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0); + + return 0; +} + void bwfm_chip_cr4_set_passive(struct bwfm_softc *sc) { - panic("%s: CR4 not supported", DEVNAME(sc)); + struct bwfm_core *core; + uint32_t val; + + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CR4); + val = sc->sc_buscore_ops->bc_read(sc, + core->co_wrapbase + BWFM_AGENT_IOCTL); + sc->sc_chip.ch_core_reset(sc, core, + val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT); + + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211); + sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET | + BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, + BWFM_AGENT_D11_IOCTL_PHYCLOCKEN); +} + +int +bwfm_chip_ca7_set_active(struct bwfm_softc *sc, const uint32_t rstvec) +{ + struct bwfm_core *core; + + sc->sc_buscore_ops->bc_activate(sc, rstvec); + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7); + sc->sc_chip.ch_core_reset(sc, core, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, 0, 0); + + return 0; } void bwfm_chip_ca7_set_passive(struct bwfm_softc *sc) { - panic("%s: CA7 not supported", DEVNAME(sc)); + struct bwfm_core *core; + uint32_t val; + + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CA7); + val = sc->sc_buscore_ops->bc_read(sc, + core->co_wrapbase + BWFM_AGENT_IOCTL); + sc->sc_chip.ch_core_reset(sc, core, + val & BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT, + BWFM_AGENT_IOCTL_ARMCR4_CPUHALT); + + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_80211); + sc->sc_chip.ch_core_reset(sc, core, BWFM_AGENT_D11_IOCTL_PHYRESET | + BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, BWFM_AGENT_D11_IOCTL_PHYCLOCKEN, + BWFM_AGENT_D11_IOCTL_PHYCLOCKEN); +} + +int +bwfm_chip_cm3_set_active(struct bwfm_softc *sc) +{ + struct bwfm_core *core; + + core = bwfm_chip_get_core(sc, BWFM_AGENT_INTERNAL_MEM); + if (!sc->sc_chip.ch_core_isup(sc, core)) + return 1; + + sc->sc_buscore_ops->bc_activate(sc, 0); + + core = bwfm_chip_get_core(sc, BWFM_AGENT_CORE_ARM_CM3); + sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0); + + return 0; } void @@ -1151,6 +1261,153 @@ bwfm_chip_cm3_set_passive(struct bwfm_softc *sc) } } +/* RAM size helpers */ +void +bwfm_chip_socram_ramsize(struct bwfm_softc *sc, struct bwfm_core *core) +{ + uint32_t coreinfo, nb, lss, banksize, bankinfo; + uint32_t ramsize = 0, srsize = 0; + int i; + + if (!sc->sc_chip.ch_core_isup(sc, core)) + sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0); + + coreinfo = sc->sc_buscore_ops->bc_read(sc, + core->co_base + BWFM_SOCRAM_COREINFO); + nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK) + >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT; + + if (core->co_rev <= 7 || core->co_rev == 12) { + banksize = coreinfo & BWFM_SOCRAM_COREINFO_SRBSZ_MASK; + lss = (coreinfo & BWFM_SOCRAM_COREINFO_LSS_MASK) + >> BWFM_SOCRAM_COREINFO_LSS_SHIFT; + if (lss != 0) + nb--; + ramsize = nb * (1 << (banksize + BWFM_SOCRAM_COREINFO_SRBSZ_BASE)); + if (lss != 0) + ramsize += (1 << ((lss - 1) + BWFM_SOCRAM_COREINFO_SRBSZ_BASE)); + } else { + for (i = 0; i < nb; i++) { + sc->sc_buscore_ops->bc_write(sc, + core->co_base + BWFM_SOCRAM_BANKIDX, + (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM << + BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i); + bankinfo = sc->sc_buscore_ops->bc_read(sc, + core->co_base + BWFM_SOCRAM_BANKINFO); + banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1) + * BWFM_SOCRAM_BANKINFO_SZBASE; + ramsize += banksize; + if (bankinfo & BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK) + srsize += banksize; + } + } + + switch (sc->sc_chip.ch_chip) { + case BRCM_CC_4334_CHIP_ID: + if (sc->sc_chip.ch_chiprev < 2) + srsize = 32 * 1024; + break; + case BRCM_CC_43430_CHIP_ID: + srsize = 64 * 1024; + break; + default: + break; + } + + sc->sc_chip.ch_ramsize = ramsize; + sc->sc_chip.ch_srsize = srsize; +} + +void +bwfm_chip_sysmem_ramsize(struct bwfm_softc *sc, struct bwfm_core *core) +{ + uint32_t coreinfo, nb, banksize, bankinfo; + uint32_t ramsize = 0; + int i; + + if (!sc->sc_chip.ch_core_isup(sc, core)) + sc->sc_chip.ch_core_reset(sc, core, 0, 0, 0); + + coreinfo = sc->sc_buscore_ops->bc_read(sc, + core->co_base + BWFM_SOCRAM_COREINFO); + nb = (coreinfo & BWFM_SOCRAM_COREINFO_SRNB_MASK) + >> BWFM_SOCRAM_COREINFO_SRNB_SHIFT; + + for (i = 0; i < nb; i++) { + sc->sc_buscore_ops->bc_write(sc, + core->co_base + BWFM_SOCRAM_BANKIDX, + (BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM << + BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT) | i); + bankinfo = sc->sc_buscore_ops->bc_read(sc, + core->co_base + BWFM_SOCRAM_BANKINFO); + banksize = ((bankinfo & BWFM_SOCRAM_BANKINFO_SZMASK) + 1) + * BWFM_SOCRAM_BANKINFO_SZBASE; + ramsize += banksize; + } + + sc->sc_chip.ch_ramsize = ramsize; +} + +void +bwfm_chip_tcm_ramsize(struct bwfm_softc *sc, struct bwfm_core *core) +{ + uint32_t cap, nab, nbb, totb, bxinfo, ramsize = 0; + int i; + + cap = sc->sc_buscore_ops->bc_read(sc, core->co_base + BWFM_ARMCR4_CAP); + nab = (cap & BWFM_ARMCR4_CAP_TCBANB_MASK) >> BWFM_ARMCR4_CAP_TCBANB_SHIFT; + nbb = (cap & BWFM_ARMCR4_CAP_TCBBNB_MASK) >> BWFM_ARMCR4_CAP_TCBBNB_SHIFT; + totb = nab + nbb; + + for (i = 0; i < totb; i++) { + sc->sc_buscore_ops->bc_write(sc, + core->co_base + BWFM_ARMCR4_BANKIDX, i); + bxinfo = sc->sc_buscore_ops->bc_read(sc, + core->co_base + BWFM_ARMCR4_BANKINFO); + ramsize += ((bxinfo & BWFM_ARMCR4_BANKINFO_BSZ_MASK) + 1) * + BWFM_ARMCR4_BANKINFO_BSZ_MULT; + } + + sc->sc_chip.ch_ramsize = ramsize; +} + +void +bwfm_chip_tcm_rambase(struct bwfm_softc *sc) +{ + switch (sc->sc_chip.ch_chip) { + case BRCM_CC_4345_CHIP_ID: + sc->sc_chip.ch_rambase = 0x198000; + break; + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4350_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: + case BRCM_CC_4356_CHIP_ID: + case BRCM_CC_43567_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: + case BRCM_CC_43570_CHIP_ID: + case BRCM_CC_4358_CHIP_ID: + case BRCM_CC_4359_CHIP_ID: + case BRCM_CC_43602_CHIP_ID: + case BRCM_CC_4371_CHIP_ID: + sc->sc_chip.ch_rambase = 0x180000; + break; + case BRCM_CC_43465_CHIP_ID: + case BRCM_CC_43525_CHIP_ID: + case BRCM_CC_4365_CHIP_ID: + case BRCM_CC_4366_CHIP_ID: + sc->sc_chip.ch_rambase = 0x200000; + break; + case CY_CC_4373_CHIP_ID: + sc->sc_chip.ch_rambase = 0x160000; + break; + default: + printf("%s: unknown chip: %d\n", DEVNAME(sc), + sc->sc_chip.ch_chip); + break; + } +} + /* BCDC protocol implementation */ int bwfm_proto_bcdc_query_dcmd(struct bwfm_softc *sc, int ifidx, @@ -1504,44 +1761,26 @@ bwfm_connect(struct bwfm_softc *sc) } void -bwfm_rx(struct bwfm_softc *sc, char *buf, size_t len) +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 = (void *)buf; - struct mbuf *m; - char *mb; + struct bwfm_event *e = mtod(m, struct bwfm_event *); int s; - DPRINTF(("%s: buf %p len %lu\n", __func__, buf, len)); - - if (len >= sizeof(e->ehdr) && + 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, buf, len); - - if (__predict_false(len > MCLBYTES || len == 0)) + ntohs(e->hdr.usr_subtype) == BWFM_BRCM_SUBTYPE_EVENT) { + bwfm_rx_event(sc, mtod(m, char *), m->m_len); + m_freem(m); return; - MGETHDR(m, M_DONTWAIT, MT_DATA); - if (__predict_false(m == NULL)) - return; - if (len > MHLEN) { - MCLGET(m, M_DONTWAIT); - if (!(m->m_flags & M_EXT)) { - m_free(m); - return; - } } s = splnet(); if ((ifp->if_flags & IFF_RUNNING) != 0) { - mb = mtod(m, char *); - memcpy(mb, buf, len); - m->m_pkthdr.len = m->m_len = len; m_set_rcvif(m, ifp); - if_percpuq_enqueue(ifp->if_percpuq, m); } @@ -1627,7 +1866,7 @@ bwfm_rx_event(struct bwfm_softc *sc, char *buf, size_t len) if (ntohl(e->msg.status) == BWFM_E_STATUS_SUCCESS && ntohl(e->msg.reason) == 0) break; - + /* Link status has changed */ ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); break; diff --git a/sys/dev/ic/bwfmreg.h b/sys/dev/ic/bwfmreg.h index 22871ae06b..9ad133035f 100644 --- a/sys/dev/ic/bwfmreg.h +++ b/sys/dev/ic/bwfmreg.h @@ -1,5 +1,5 @@ /* $NetBSD: bwfmreg.h,v 1.2 2017/10/20 23:38:56 jmcneill Exp $ */ -/* $OpenBSD: bwfmreg.h,v 1.4 2017/10/16 22:27:16 patrick Exp $ */ +/* $OpenBSD: bwfmreg.h,v 1.16 2018/02/07 21:44:09 patrick Exp $ */ /* * Copyright (c) 2010-2016 Broadcom Corporation * Copyright (c) 2016,2017 Patrick Wildt <[email protected]> @@ -50,13 +50,24 @@ #define BWFM_CHIP_REG_CAPABILITIES_PMU 0x10000000 #define BWFM_CHIP_REG_CAPABILITIES_EXT 0x000000AC #define BWFM_CHIP_REG_CAPABILITIES_EXT_AOB_PRESENT 0x00000040 +#define BWFM_CHIP_REG_WATCHDOG 0x00000080 #define BWFM_CHIP_REG_EROMPTR 0x000000FC +#define BWFM_CHIP_REG_SR_CAPABILITY 0x00000500 +#define BWFM_CHIP_REG_SR_CONTROL0 0x00000504 +#define BWFM_CHIP_REG_SR_CONTROL1 0x00000508 #define BWFM_CHIP_REG_PMUCONTROL 0x00000600 #define BWFM_CHIP_REG_PMUCONTROL_RES_MASK 0x00006000 #define BWFM_CHIP_REG_PMUCONTROL_RES_SHIFT 13 #define BWFM_CHIP_REG_PMUCONTROL_RES_RELOAD 0x2 #define BWFM_CHIP_REG_PMUCAPABILITIES 0x00000604 #define BWFM_CHIP_REG_PMUCAPABILITIES_REV_MASK 0x000000ff +#define BWFM_CHIP_REG_PMUCAPABILITIES_EXT 0x0000064C +#define BWFM_CHIP_REG_PMUCAPABILITIES_SR_SUPP (1 << 1) +#define BWFM_CHIP_REG_CHIPCONTROL_ADDR 0x00000650 +#define BWFM_CHIP_REG_CHIPCONTROL_DATA 0x00000654 +#define BWFM_CHIP_REG_RETENTION_CTL 0x00000670 +#define BWFM_CHIP_REG_RETENTION_CTL_MACPHY_DIS (1 << 26) +#define BWFM_CHIP_REG_RETENTION_CTL_LOGIC_DIS (1 << 27) /* Agent registers */ #define BWFM_AGENT_IOCTL 0x0408 @@ -65,6 +76,7 @@ #define BWFM_AGENT_IOCTL_CORE_BITS 0x3FFC #define BWFM_AGENT_IOCTL_PME_EN 0x4000 #define BWFM_AGENT_IOCTL_BIST_EN 0x8000 +#define BWFM_AGENT_IOCTL_ARMCR4_CPUHALT 0x0020 #define BWFM_AGENT_RESET_CTL 0x0800 #define BWFM_AGENT_RESET_CTL_RESET 0x0001 @@ -75,16 +87,46 @@ #define BWFM_AGENT_CORE_PMU 0x827 #define BWFM_AGENT_CORE_SDIO_DEV 0x829 #define BWFM_AGENT_CORE_ARM_CM3 0x82A +#define BWFM_AGENT_CORE_PCIE2 0x83C #define BWFM_AGENT_CORE_ARM_CR4 0x83E #define BWFM_AGENT_CORE_ARM_CA7 0x847 +#define BWFM_AGENT_SYS_MEM 0x849 /* Specific Core Bits */ #define BWFM_AGENT_ARMCR4_IOCTL_CPUHALT 0x0020 #define BWFM_AGENT_D11_IOCTL_PHYCLOCKEN 0x0004 #define BWFM_AGENT_D11_IOCTL_PHYRESET 0x0008 +/* CR4 registers */ +#define BWFM_ARMCR4_CAP 0x0004 +#define BWFM_ARMCR4_CAP_TCBANB_MASK 0xf +#define BWFM_ARMCR4_CAP_TCBANB_SHIFT 0 +#define BWFM_ARMCR4_CAP_TCBBNB_MASK 0xf0 +#define BWFM_ARMCR4_CAP_TCBBNB_SHIFT 4 +#define BWFM_ARMCR4_BANKIDX 0x0040 +#define BWFM_ARMCR4_BANKINFO 0x0044 +#define BWFM_ARMCR4_BANKINFO_BSZ_MASK 0x3f +#define BWFM_ARMCR4_BANKINFO_BSZ_MULT 8192 +#define BWFM_ARMCR4_BANKPDA 0x004C + /* SOCRAM registers */ +#define BWFM_SOCRAM_COREINFO 0x0000 +#define BWFM_SOCRAM_COREINFO_SRBSZ_BASE 14 +#define BWFM_SOCRAM_COREINFO_SRBSZ_MASK 0xf +#define BWFM_SOCRAM_COREINFO_SRBSZ_SHIFT 0 +#define BWFM_SOCRAM_COREINFO_SRNB_MASK 0xf0 +#define BWFM_SOCRAM_COREINFO_SRNB_SHIFT 4 +#define BWFM_SOCRAM_COREINFO_LSS_MASK 0xf00000 +#define BWFM_SOCRAM_COREINFO_LSS_SHIFT 20 #define BWFM_SOCRAM_BANKIDX 0x0010 +#define BWFM_SOCRAM_BANKIDX_MEMTYPE_RAM 0 +#define BWFM_SOCRAM_BANKIDX_MEMTYPE_ROM 1 +#define BWFM_SOCRAM_BANKIDX_MEMTYPE_DEVRAM 2 +#define BWFM_SOCRAM_BANKIDX_MEMTYPE_SHIFT 8 +#define BWFM_SOCRAM_BANKINFO 0x0040 +#define BWFM_SOCRAM_BANKINFO_SZBASE 8192 +#define BWFM_SOCRAM_BANKINFO_SZMASK 0x7f +#define BWFM_SOCRAM_BANKINFO_RETNTRAM_MASK 0x10000 #define BWFM_SOCRAM_BANKPDA 0x0044 /* SDPCMD registers */ @@ -140,6 +182,13 @@ #define BWFM_AUTH_OPEN 0 #define BWFM_AUTH_SHARED_KEY 1 #define BWFM_AUTH_AUTO 2 +#define BWFM_CRYPTO_ALGO_OFF 0 +#define BWFM_CRYPTO_ALGO_WEP1 1 +#define BWFM_CRYPTO_ALGO_TKIP 2 +#define BWFM_CRYPTO_ALGO_WEP128 3 +#define BWFM_CRYPTO_ALGO_AES_CCM 4 +#define BWFM_CRYPTO_ALGO_AES_RESERVED1 5 +#define BWFM_CRYPTO_ALGO_AES_RESERVED2 6 #define BWFM_MFP_NONE 0 #define BWFM_MFP_CAPABLE 1 #define BWFM_MFP_REQUIRED 2 @@ -156,6 +205,59 @@ #define BWFM_WSEC_TKIP (1 << 1) #define BWFM_WSEC_AES (1 << 2) +/* Channel Parameters */ +#define BWFM_CHANSPEC_CHAN_MASK 0xff +#define BWFM_CHANSPEC_CHAN_SHIFT 0 +#define BWFM_CHANSPEC_D11N_SB_L (0x1 << 8) /* control lower */ +#define BWFM_CHANSPEC_D11N_SB_U (0x2 << 8) /* control lower */ +#define BWFM_CHANSPEC_D11N_SB_N (0x3 << 8) /* none */ +#define BWFM_CHANSPEC_D11N_SB_MASK (0x3 << 8) +#define BWFM_CHANSPEC_D11N_SB_SHIFT 8 +#define BWFM_CHANSPEC_D11N_BW_10 (0x1 << 10) +#define BWFM_CHANSPEC_D11N_BW_20 (0x2 << 10) +#define BWFM_CHANSPEC_D11N_BW_40 (0x3 << 10) +#define BWFM_CHANSPEC_D11N_BW_MASK (0x3 << 10) +#define BWFM_CHANSPEC_D11N_BW_SHIFT 10 +#define BWFM_CHANSPEC_D11N_BND_5G (0x1 << 12) +#define BWFM_CHANSPEC_D11N_BND_2G (0x2 << 12) +#define BWFM_CHANSPEC_D11N_BND_MASK (0x3 << 12) +#define BWFM_CHANSPEC_D11N_BND_SHIFT 12 +#define BWFM_CHANSPEC_D11AC_SB_LLL (0x0 << 8) +#define BWFM_CHANSPEC_D11AC_SB_LLU (0x1 << 8) +#define BWFM_CHANSPEC_D11AC_SB_LUL (0x2 << 8) +#define BWFM_CHANSPEC_D11AC_SB_LUU (0x3 << 8) +#define BWFM_CHANSPEC_D11AC_SB_ULL (0x4 << 8) +#define BWFM_CHANSPEC_D11AC_SB_ULU (0x5 << 8) +#define BWFM_CHANSPEC_D11AC_SB_UUL (0x6 << 8) +#define BWFM_CHANSPEC_D11AC_SB_UUU (0x7 << 8) +#define BWFM_CHANSPEC_D11AC_SB_MASK (0x7 << 8) +#define BWFM_CHANSPEC_D11AC_SB_SHIFT 8 +#define BWFM_CHANSPEC_D11AC_BW_5 (0x0 << 11) +#define BWFM_CHANSPEC_D11AC_BW_10 (0x1 << 11) +#define BWFM_CHANSPEC_D11AC_BW_20 (0x2 << 11) +#define BWFM_CHANSPEC_D11AC_BW_40 (0x3 << 11) +#define BWFM_CHANSPEC_D11AC_BW_80 (0x4 << 11) +#define BWFM_CHANSPEC_D11AC_BW_160 (0x5 << 11) +#define BWFM_CHANSPEC_D11AC_BW_8080 (0x6 << 11) +#define BWFM_CHANSPEC_D11AC_BW_MASK (0x7 << 11) +#define BWFM_CHANSPEC_D11AC_BW_SHIFT 11 +#define BWFM_CHANSPEC_D11AC_BND_2G (0x0 << 14) +#define BWFM_CHANSPEC_D11AC_BND_3G (0x1 << 14) +#define BWFM_CHANSPEC_D11AC_BND_4G (0x2 << 14) +#define BWFM_CHANSPEC_D11AC_BND_5G (0x3 << 14) +#define BWFM_CHANSPEC_D11AC_BND_MASK (0x3 << 14) +#define BWFM_CHANSPEC_D11AC_BND_SHIFT 14 + +#define BWFM_BAND_AUTO 0 +#define BWFM_BAND_5G 1 +#define BWFM_BAND_2G 2 +#define BWFM_BAND_ALL 3 + +/* Power Modes */ +#define BWFM_PM_CAM 0 +#define BWFM_PM_PS 1 +#define BWFM_PM_FAST_PS 2 + /* DCMD commands */ #define BWFM_C_GET_VERSION 1 #define BWFM_C_UP 2 @@ -300,6 +402,9 @@ struct bwfm_scan_params { uint8_t bss_type; #define DOT11_BSSTYPE_ANY 2 uint8_t scan_type; +#define BWFM_SCANTYPE_ACTIVE 0 +#define BWFM_SCANTYPE_PASSIVE 1 +#define BWFM_SCANTYPE_DEFAULT 0xff uint32_t nprobes; uint32_t active_time; uint32_t passive_time; diff --git a/sys/dev/ic/bwfmvar.h b/sys/dev/ic/bwfmvar.h index 938337b2ff..939bd55c07 100644 --- a/sys/dev/ic/bwfmvar.h +++ b/sys/dev/ic/bwfmvar.h @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/pcq.h> + /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 #define BRCM_CC_43235_CHIP_ID 43235 @@ -33,7 +35,9 @@ #define BRCM_CC_4339_CHIP_ID 0x4339 #define BRCM_CC_43430_CHIP_ID 43430 #define BRCM_CC_4345_CHIP_ID 0x4345 +#define BRCM_CC_43465_CHIP_ID 43465 #define BRCM_CC_4350_CHIP_ID 0x4350 +#define BRCM_CC_43525_CHIP_ID 43525 #define BRCM_CC_4354_CHIP_ID 0x4354 #define BRCM_CC_4356_CHIP_ID 0x4356 #define BRCM_CC_43566_CHIP_ID 43566 @@ -46,6 +50,7 @@ #define BRCM_CC_4365_CHIP_ID 0x4365 #define BRCM_CC_4366_CHIP_ID 0x4366 #define BRCM_CC_4371_CHIP_ID 0x4371 +#define CY_CC_4373_CHIP_ID 0x4373 /* Defaults */ #define BWFM_DEFAULT_SCAN_CHANNEL_TIME 40 @@ -87,6 +92,7 @@ struct bwfm_chip { struct bwfm_bus_ops { void (*bs_init)(struct bwfm_softc *); void (*bs_stop)(struct bwfm_softc *); + int (*bs_txcheck)(struct bwfm_softc *); int (*bs_txdata)(struct bwfm_softc *, struct mbuf *); int (*bs_txctl)(struct bwfm_softc *, char *, size_t); int (*bs_rxctl)(struct bwfm_softc *, char *, size_t *); @@ -98,7 +104,7 @@ struct bwfm_buscore_ops { int (*bc_prepare)(struct bwfm_softc *); int (*bc_reset)(struct bwfm_softc *); int (*bc_setup)(struct bwfm_softc *); - void (*bc_activate)(struct bwfm_softc *, uint32_t); + void (*bc_activate)(struct bwfm_softc *, const uint32_t); }; struct bwfm_proto_ops { @@ -163,8 +169,15 @@ struct bwfm_softc { }; void bwfm_attach(struct bwfm_softc *); +void bwfm_chip_socram_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_sysmem_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_tcm_ramsize(struct bwfm_softc *, struct bwfm_core *); +void bwfm_chip_tcm_rambase(struct bwfm_softc *); +void bwfm_start(struct ifnet *); 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 *); 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 *, char *, size_t); +void bwfm_rx(struct bwfm_softc *, struct mbuf *m); diff --git a/sys/dev/sdmmc/if_bwfm_sdio.c b/sys/dev/sdmmc/if_bwfm_sdio.c index f31d1afc96..639ee98982 100644 --- a/sys/dev/sdmmc/if_bwfm_sdio.c +++ b/sys/dev/sdmmc/if_bwfm_sdio.c @@ -87,6 +87,7 @@ void bwfm_sdio_buscore_write(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 *); @@ -94,6 +95,7 @@ int bwfm_sdio_rxctl(struct bwfm_softc *, char *, size_t *); struct bwfm_bus_ops bwfm_sdio_bus_ops = { .bs_init = NULL, .bs_stop = NULL, + .bs_txcheck = bwfm_sdio_txcheck, .bs_txdata = bwfm_sdio_txdata, .bs_txctl = bwfm_sdio_txctl, .bs_rxctl = bwfm_sdio_rxctl, @@ -404,6 +406,15 @@ bwfm_sdio_buscore_activate(struct bwfm_softc *bwfm, uint32_t rstvec) #endif } +int +bwfm_sdio_txcheck(struct bwfm_softc *bwfm, struct mbuf *m) +{ + DPRINTFN(2, ("%s: %s\n", DEVNAME(sc), __func__)); + + return 0; +} + + int bwfm_sdio_txdata(struct bwfm_softc *bwfm, struct mbuf *m) { diff --git a/sys/dev/usb/if_bwfm_usb.c b/sys/dev/usb/if_bwfm_usb.c index 060e948513..10d4e24ed8 100644 --- a/sys/dev/usb/if_bwfm_usb.c +++ b/sys/dev/usb/if_bwfm_usb.c @@ -199,16 +199,19 @@ void bwfm_usb_free_rx_list(struct bwfm_usb_softc *); int bwfm_usb_alloc_tx_list(struct bwfm_usb_softc *); void bwfm_usb_free_tx_list(struct bwfm_usb_softc *); +int bwfm_usb_txcheck(struct bwfm_softc *); int bwfm_usb_txdata(struct bwfm_softc *, struct mbuf *); int bwfm_usb_txctl(struct bwfm_softc *, char *, size_t); int bwfm_usb_rxctl(struct bwfm_softc *, char *, size_t *); +struct mbuf * bwfm_usb_newbuf(void); void bwfm_usb_rxeof(struct usbd_xfer *, void *, usbd_status); void bwfm_usb_txeof(struct usbd_xfer *, void *, usbd_status); struct bwfm_bus_ops bwfm_usb_bus_ops = { .bs_init = NULL, .bs_stop = NULL, + .bs_txcheck = bwfm_usb_txcheck, .bs_txdata = bwfm_usb_txdata, .bs_txctl = bwfm_usb_txctl, .bs_rxctl = bwfm_usb_rxctl, @@ -435,6 +438,26 @@ bwfm_usb_attachhook(device_t self) } } +struct mbuf * +bwfm_usb_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); +} + void bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) { @@ -443,6 +466,7 @@ bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) struct bwfm_proto_bcdc_hdr *hdr; usbd_status error; uint32_t len, off; + struct mbuf *m; DPRINTFN(2, ("%s: %s status %s\n", DEVNAME(sc), __func__, usbd_errstr(status))); @@ -466,8 +490,14 @@ bwfm_usb_rxeof(struct usbd_xfer *xfer, void *priv, usbd_status status) len -= hdr->data_offset << 2; off += hdr->data_offset << 2; - mutex_enter(&sc->sc_rx_lock); - bwfm_rx(&sc->sc_sc, &data->buf[off], len); + m = bwfm_usb_newbuf(); + if (m == NULL) + goto resubmit; + + memcpy(mtod(m, char *), data->buf + off, len); + m->m_len = m->m_pkthdr.len = len; + mutex_enter(&sc->sc_rx_lock); /* XXX */ + bwfm_rx(&sc->sc_sc, m); mutex_exit(&sc->sc_rx_lock); resubmit: @@ -734,6 +764,22 @@ err: return 1; } +int +bwfm_usb_txcheck(struct bwfm_softc *bwfm) +{ + struct bwfm_usb_softc *sc = (void *)bwfm; + + mutex_enter(&sc->sc_tx_lock); + + if (TAILQ_EMPTY(&sc->sc_tx_free_list)) { + mutex_exit(&sc->sc_tx_lock); + return ENOBUFS; + } + + return 0; +} + + int bwfm_usb_txdata(struct bwfm_softc *bwfm, struct mbuf *m) { -- 2.17.0
>From 0a2e6220eb196a5bc76baa4f3f739c20368c3c17 Mon Sep 17 00:00:00 2001 From: coypu <[email protected]> Date: Fri, 4 May 2018 04:35:21 +0300 Subject: [PATCH 2/4] add bwfm pci support. tested on BCM43602. XXX is testing workqueue before destroy like so valid if (sc->flowring_wq != NULL) workqueue_destroy(sc->flowring_wq); --- sys/dev/pci/files.pci | 4 + sys/dev/pci/if_bwfm_pci.c | 2062 +++++++++++++++++++++++++++++++++++++ sys/dev/pci/if_bwfm_pci.h | 279 +++++ 3 files changed, 2345 insertions(+) create mode 100644 sys/dev/pci/if_bwfm_pci.c create mode 100644 sys/dev/pci/if_bwfm_pci.h diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci index 930a42c78a..fda5bd36bb 100644 --- a/sys/dev/pci/files.pci +++ b/sys/dev/pci/files.pci @@ -1073,6 +1073,10 @@ include "dev/pci/n8/files.n8" attach bwi at pci with bwi_pci file dev/pci/if_bwi_pci.c bwi_pci +# Broadcom FullMAC USB wireless adapter +attach bwfm at pci with bwfm_pci: firmload +file dev/pci/if_bwfm_pci.c bwfm_pci + # Marvell Serial-ATA Host Controller attach mvsata at pci with mvsata_pci file dev/pci/mvsata_pci.c mvsata_pci diff --git a/sys/dev/pci/if_bwfm_pci.c b/sys/dev/pci/if_bwfm_pci.c new file mode 100644 index 0000000000..edefd9f7c2 --- /dev/null +++ b/sys/dev/pci/if_bwfm_pci.c @@ -0,0 +1,2062 @@ +/* $NetBSD$ */ +/* $OpenBSD: if_bwfm_pci.c,v 1.18 2018/02/08 05:00:38 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2017 Patrick Wildt <[email protected]> + * + * 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/kmem.h> +#include <sys/device.h> +#include <sys/pool.h> +#include <sys/workqueue.h> +#include <sys/socket.h> + +#include <net/bpf.h> +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#include <netinet/in.h> + +#include <net80211/ieee80211_var.h> + +#include <dev/firmload.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/ic/bwfmvar.h> +#include <dev/ic/bwfmreg.h> +#include <dev/pci/if_bwfm_pci.h> + +#define BWFM_DMA_D2H_SCRATCH_BUF_LEN 8 +#define BWFM_DMA_D2H_RINGUPD_BUF_LEN 1024 +#define BWFM_DMA_H2D_IOCTL_BUF_LEN ETHER_MAX_LEN + +#define BWFM_NUM_TX_MSGRINGS 2 +#define BWFM_NUM_RX_MSGRINGS 3 + +#define BWFM_NUM_TX_PKTIDS 2048 +#define BWFM_NUM_RX_PKTIDS 1024 + +#define BWFM_NUM_TX_DESCS 1 +#define BWFM_NUM_RX_DESCS 1 + +#ifdef BWFM_DEBUG +#define DPRINTF(x) do { if (bwfm_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (bwfm_debug >= (n)) printf x; } while (0) +static int bwfm_debug = 2; +#else +#define DPRINTF(x) do { ; } while (0) +#define DPRINTFN(n, x) do { ; } while (0) +#endif + +#define DEVNAME(sc) device_xname((sc)->sc_sc.sc_dev) +#define letoh16 htole16 +#define letoh32 htole32 +#define nitems(x) __arraycount(x) + +enum ring_status { + RING_CLOSED, + RING_CLOSING, + RING_OPEN, + RING_OPENING, +}; + +struct bwfm_pci_msgring { + uint32_t w_idx_addr; + uint32_t r_idx_addr; + uint32_t w_ptr; + uint32_t r_ptr; + int nitem; + int itemsz; + enum ring_status status; + struct bwfm_pci_dmamem *ring; + struct mbuf *m; + + int fifo; + uint8_t mac[ETHER_ADDR_LEN]; +}; + +struct bwfm_pci_buf { + bus_dmamap_t bb_map; + struct mbuf *bb_m; +}; + +struct bwfm_pci_pkts { + struct bwfm_pci_buf *pkts; + uint32_t npkt; + int last; +}; + +struct if_rxring { + u_int rxr_total; + u_int rxr_inuse; +}; + +struct bwfm_cmd_flowring_create { + struct work wq_cookie; + struct bwfm_pci_softc *sc; + struct mbuf *m; + int flowid; + int prio; +}; + +struct bwfm_pci_softc { + struct bwfm_softc sc_sc; + pci_chipset_tag_t sc_pc; + pcitag_t sc_tag; + pcireg_t sc_id; + void *sc_ih; + pci_intr_handle_t *sc_pihp; + + bus_space_tag_t sc_reg_iot; + bus_space_handle_t sc_reg_ioh; + bus_size_t sc_reg_ios; + + bus_space_tag_t sc_tcm_iot; + bus_space_handle_t sc_tcm_ioh; + bus_size_t sc_tcm_ios; + + bus_dma_tag_t sc_dmat; + + uint32_t sc_shared_address; + uint32_t sc_shared_flags; + uint8_t sc_shared_version; + + uint8_t sc_dma_idx_sz; + struct bwfm_pci_dmamem *sc_dma_idx_buf; + size_t sc_dma_idx_bufsz; + + uint16_t sc_max_rxbufpost; + uint32_t sc_rx_dataoffset; + uint32_t sc_htod_mb_data_addr; + uint32_t sc_dtoh_mb_data_addr; + uint32_t sc_ring_info_addr; + + uint32_t sc_console_base_addr; + uint32_t sc_console_buf_addr; + uint32_t sc_console_buf_size; + uint32_t sc_console_readidx; + + struct pool sc_flowring_pool; + struct workqueue *flowring_wq; + + uint16_t sc_max_flowrings; + uint16_t sc_max_submissionrings; + uint16_t sc_max_completionrings; + + struct bwfm_pci_msgring sc_ctrl_submit; + struct bwfm_pci_msgring sc_rxpost_submit; + struct bwfm_pci_msgring sc_ctrl_complete; + struct bwfm_pci_msgring sc_tx_complete; + struct bwfm_pci_msgring sc_rx_complete; + struct bwfm_pci_msgring *sc_flowrings; + + struct bwfm_pci_dmamem *sc_scratch_buf; + struct bwfm_pci_dmamem *sc_ringupd_buf; + + struct bwfm_pci_dmamem *sc_ioctl_buf; + int sc_ioctl_reqid; + uint32_t sc_ioctl_resp_pktid; + uint32_t sc_ioctl_resp_ret_len; + uint32_t sc_ioctl_resp_status; + int sc_ioctl_poll; + + struct if_rxring sc_ioctl_ring; + struct if_rxring sc_event_ring; + struct if_rxring sc_rxbuf_ring; + + struct bwfm_pci_pkts sc_rx_pkts; + struct bwfm_pci_pkts sc_tx_pkts; + int sc_tx_pkts_full; +}; + +struct bwfm_pci_dmamem { + bus_dmamap_t bdm_map; + bus_dma_segment_t bdm_seg; + size_t bdm_size; + char * bdm_kva; +}; + +#define BWFM_PCI_DMA_MAP(_bdm) ((_bdm)->bdm_map) +#define BWFM_PCI_DMA_LEN(_bdm) ((_bdm)->bdm_size) +#define BWFM_PCI_DMA_DVA(_bdm) ((_bdm)->bdm_map->dm_segs[0].ds_addr) +#define BWFM_PCI_DMA_KVA(_bdm) ((_bdm)->bdm_kva) + +static u_int if_rxr_get(struct if_rxring *rxr, unsigned int max); +static void if_rxr_put(struct if_rxring *rxr, unsigned int n); +static void if_rxr_init(struct if_rxring *rxr, unsigned int lwm, unsigned int hwm); + +int bwfm_pci_match(device_t parent, cfdata_t match, void *aux); +void bwfm_pci_attachhook(device_t); +void bwfm_pci_attach(device_t, device_t, void *); +int bwfm_pci_detach(device_t, int); + +int bwfm_pci_intr(void *); +void bwfm_pci_intr_enable(struct bwfm_pci_softc *); +void bwfm_pci_intr_disable(struct bwfm_pci_softc *); +int bwfm_pci_load_microcode(struct bwfm_pci_softc *, const u_char *, + size_t); +void bwfm_pci_select_core(struct bwfm_pci_softc *, int ); + +struct bwfm_pci_dmamem * + bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *, bus_size_t, + bus_size_t); +void bwfm_pci_dmamem_free(struct bwfm_pci_softc *, struct bwfm_pci_dmamem *); +int bwfm_pci_pktid_avail(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *); +int bwfm_pci_pktid_new(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, struct mbuf *, + uint32_t *, paddr_t *); +struct mbuf * bwfm_pci_pktid_free(struct bwfm_pci_softc *, + struct bwfm_pci_pkts *, uint32_t); +void bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *, + struct if_rxring *, uint32_t); +void bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *); +void bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *); +int bwfm_pci_setup_ring(struct bwfm_pci_softc *, struct bwfm_pci_msgring *, + int, size_t, uint32_t, uint32_t, int, uint32_t, uint32_t *); +int bwfm_pci_setup_flowring(struct bwfm_pci_softc *, struct bwfm_pci_msgring *, + int, size_t); + +void bwfm_pci_ring_bell(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void * bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int, int *); +void * bwfm_pci_ring_read_avail(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int *); +void bwfm_pci_ring_read_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); +void bwfm_pci_ring_write_commit(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *, int); + +void bwfm_pci_ring_rx(struct bwfm_pci_softc *, + struct bwfm_pci_msgring *); +void bwfm_pci_msg_rx(struct bwfm_pci_softc *, void *); + +uint32_t bwfm_pci_buscore_read(struct bwfm_softc *, uint32_t); +void bwfm_pci_buscore_write(struct bwfm_softc *, uint32_t, + uint32_t); +int bwfm_pci_buscore_prepare(struct bwfm_softc *); +int bwfm_pci_buscore_reset(struct bwfm_softc *); +void bwfm_pci_buscore_activate(struct bwfm_softc *, const uint32_t); + +int bwfm_pci_flowring_lookup(struct bwfm_pci_softc *, + struct mbuf *); +void bwfm_pci_flowring_create(struct bwfm_pci_softc *, + struct mbuf *); +void bwfm_pci_flowring_create_cb(struct work *, void *); +void bwfm_pci_flowring_delete(struct bwfm_pci_softc *, int); + +void bwfm_pci_stop(struct bwfm_softc *); +int bwfm_pci_txcheck(struct bwfm_softc *); +int bwfm_pci_txdata(struct bwfm_softc *, struct mbuf *); + +#ifdef BWFM_DEBUG +void bwfm_pci_debug_console(struct bwfm_pci_softc *); +#endif + +int bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *, int, + int, char *, size_t *); +int bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *, int, + int, char *, size_t); + +struct bwfm_buscore_ops bwfm_pci_buscore_ops = { + .bc_read = bwfm_pci_buscore_read, + .bc_write = bwfm_pci_buscore_write, + .bc_prepare = bwfm_pci_buscore_prepare, + .bc_reset = bwfm_pci_buscore_reset, + .bc_setup = NULL, + .bc_activate = bwfm_pci_buscore_activate, +}; + +struct bwfm_bus_ops bwfm_pci_bus_ops = { + .bs_init = NULL, + .bs_stop = bwfm_pci_stop, + .bs_txcheck = bwfm_pci_txcheck, + .bs_txdata = bwfm_pci_txdata, + .bs_txctl = NULL, + .bs_rxctl = NULL, +}; + +struct bwfm_proto_ops bwfm_pci_msgbuf_ops = { + .proto_query_dcmd = bwfm_pci_msgbuf_query_dcmd, + .proto_set_dcmd = bwfm_pci_msgbuf_set_dcmd, +}; + + +CFATTACH_DECL_NEW(bwfm_pci, sizeof(struct bwfm_pci_softc), + bwfm_pci_match, bwfm_pci_attach, bwfm_pci_detach, NULL); + +static const struct bwfm_pci_matchid { + pci_vendor_id_t bwfm_vendor; + pci_product_id_t bwfm_product; +} bwfm_pci_devices[] = { + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM43602 }, + { PCI_VENDOR_BROADCOM, PCI_PRODUCT_BROADCOM_BCM4350 }, +}; + +static struct mbuf * +MCLGETI(struct bwfm_pci_softc *sc __unused, int how, + struct ifnet *ifp __unused, u_int size) +{ + struct mbuf *m; + + MGETHDR(m, how, MT_DATA); + if (m == NULL) + return NULL; + + MEXTMALLOC(m, size, how); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return NULL; + } + return m; +} + +int +bwfm_pci_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_BROADCOM) + return 0; + + for (size_t i = 0; i < __arraycount(bwfm_pci_devices); i++) + if (PCI_PRODUCT(pa->pa_id) == bwfm_pci_devices[i].bwfm_product) + return 1; + + return 0; +} + +void +bwfm_pci_attach(device_t parent, device_t self, void *aux) +{ + struct bwfm_pci_softc *sc = device_private(self); + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + const char *intrstr; + char intrbuf[PCI_INTRSTR_LEN]; + + sc->sc_sc.sc_dev = self; + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x00, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_reg_iot, &sc->sc_reg_ioh, + NULL, &sc->sc_reg_ios)) { + printf(": can't map bar0\n"); + return; + } + + if (pci_mapreg_map(pa, PCI_MAPREG_START + 0x08, + PCI_MAPREG_MEM_TYPE_64BIT, 0, &sc->sc_tcm_iot, &sc->sc_tcm_ioh, + NULL, &sc->sc_tcm_ios)) { + printf(": can't map bar1\n"); + goto bar0; + } + + sc->sc_pc = pa->pa_pc; + sc->sc_tag = pa->pa_tag; + sc->sc_id = pa->pa_id; + + if (pci_dma64_available(pa)) + sc->sc_dmat = pa->pa_dmat64; + else + sc->sc_dmat = pa->pa_dmat; + + /* Map and establish the interrupt. */ + if (pci_intr_alloc(pa, &sc->sc_pihp, NULL, 0) != 0) { + printf(": couldn't map interrupt\n"); + goto bar1; + } + intrstr = pci_intr_string(pa->pa_pc, sc->sc_pihp[0], intrbuf, sizeof(intrbuf)); + + sc->sc_ih = pci_intr_establish(pa->pa_pc, sc->sc_pihp[0], IPL_NET, + bwfm_pci_intr, sc); + if (sc->sc_ih == NULL) { + printf(": couldn't establish interrupt"); + if (intrstr != NULL) + printf(" at %s", intrstr); + printf("\n"); + goto bar1; + } + printf(": %s\n", intrstr); + + config_mountroot(self, bwfm_pci_attachhook); + return; + +bar1: + bus_space_unmap(sc->sc_tcm_iot, sc->sc_tcm_ioh, sc->sc_tcm_ios); +bar0: + bus_space_unmap(sc->sc_reg_iot, sc->sc_reg_ioh, sc->sc_reg_ios); +} + +void +bwfm_pci_attachhook(device_t self) +{ + struct bwfm_pci_softc *sc = device_private(self); + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_pci_ringinfo ringinfo; + const char *name = NULL; + firmware_handle_t fwh; + u_char *ucode; size_t size; + uint32_t d2h_w_idx_ptr, d2h_r_idx_ptr; + uint32_t h2d_w_idx_ptr, h2d_r_idx_ptr; + uint32_t idx_offset, reg; + int i; + int error; + + sc->sc_sc.sc_buscore_ops = &bwfm_pci_buscore_ops; + if (bwfm_chip_attach(&sc->sc_sc) != 0) { + printf("%s: cannot attach chip\n", DEVNAME(sc)); + return; + } + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, 0x4e0); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + + switch (bwfm->sc_chip.ch_chip) + { + case BRCM_CC_4350_CHIP_ID: + if (bwfm->sc_chip.ch_chiprev > 7) + name = "brcmfmac4350-pcie.bin"; + else + name = "brcmfmac4350c2-pcie.bin"; + break; + case BRCM_CC_43602_CHIP_ID: + name = "brcmfmac43602-pcie.bin"; + break; + default: + printf("%s: unknown firmware for chip %s\n", + DEVNAME(sc), bwfm->sc_chip.ch_name); + return; + } + + if (firmware_open("if_bwfm", name, &fwh) != 0) { + printf("%s: failed firmware_open of file %s\n", + DEVNAME(sc), name); + return; + } + size = firmware_get_size(fwh); + ucode = firmware_malloc(size); + if (ucode == NULL) { + printf("%s: failed to allocate firmware memory\n", + DEVNAME(sc)); + firmware_close(fwh); + return; + } + 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); + firmware_free(ucode, size); + return; + } + + /* Retrieve RAM size from firmware. */ + if (size >= BWFM_RAMSIZE + 8) { + uint32_t *ramsize = (uint32_t *)&ucode[BWFM_RAMSIZE]; + if (letoh32(ramsize[0]) == BWFM_RAMSIZE_MAGIC) + bwfm->sc_chip.ch_ramsize = letoh32(ramsize[1]); + } + + if (bwfm_pci_load_microcode(sc, ucode, size) != 0) { + printf("%s: could not load microcode\n", + DEVNAME(sc)); + kmem_free(ucode, size); + return; + } + + firmware_free(ucode, size); + + sc->sc_shared_flags = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_INFO); + sc->sc_shared_version = sc->sc_shared_flags; + if (sc->sc_shared_version > BWFM_SHARED_INFO_MAX_VERSION || + sc->sc_shared_version < BWFM_SHARED_INFO_MIN_VERSION) { + printf("%s: PCIe version %d unsupported\n", + DEVNAME(sc), sc->sc_shared_version); + return; + } + + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_INDEX) { + if (sc->sc_shared_flags & BWFM_SHARED_INFO_DMA_2B_IDX) + sc->sc_dma_idx_sz = sizeof(uint16_t); + else + sc->sc_dma_idx_sz = sizeof(uint32_t); + } + + /* Maximum RX data buffers in the ring. */ + sc->sc_max_rxbufpost = bus_space_read_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_MAX_RXBUFPOST); + if (sc->sc_max_rxbufpost == 0) + sc->sc_max_rxbufpost = BWFM_SHARED_MAX_RXBUFPOST_DEFAULT; + + /* Alternative offset of data in a packet */ + sc->sc_rx_dataoffset = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RX_DATAOFFSET); + + /* For Power Management */ + sc->sc_htod_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_HTOD_MB_DATA_ADDR); + sc->sc_dtoh_mb_data_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DTOH_MB_DATA_ADDR); + + /* Ring information */ + sc->sc_ring_info_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_RING_INFO_ADDR); + + /* Firmware's "dmesg" */ + sc->sc_console_base_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_CONSOLE_ADDR); + sc->sc_console_buf_addr = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFADDR); + sc->sc_console_buf_size = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_BUFSIZE); + + /* Read ring information. */ + bus_space_read_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + + if (sc->sc_shared_version >= 6) { + sc->sc_max_submissionrings = le16toh(ringinfo.max_submissionrings); + sc->sc_max_flowrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_completionrings = le16toh(ringinfo.max_completionrings); + } else { + sc->sc_max_submissionrings = le16toh(ringinfo.max_flowrings); + sc->sc_max_flowrings = sc->sc_max_submissionrings - + BWFM_NUM_TX_MSGRINGS; + sc->sc_max_completionrings = BWFM_NUM_RX_MSGRINGS; + } + + if (sc->sc_dma_idx_sz == 0) { + d2h_w_idx_ptr = letoh32(ringinfo.d2h_w_idx_ptr); + d2h_r_idx_ptr = letoh32(ringinfo.d2h_r_idx_ptr); + h2d_w_idx_ptr = letoh32(ringinfo.h2d_w_idx_ptr); + h2d_r_idx_ptr = letoh32(ringinfo.h2d_r_idx_ptr); + idx_offset = sizeof(uint32_t); + } else { + uint64_t address; + + /* Each TX/RX Ring has a Read and Write Ptr */ + sc->sc_dma_idx_bufsz = (sc->sc_max_submissionrings + + sc->sc_max_completionrings) * sc->sc_dma_idx_sz * 2; + sc->sc_dma_idx_buf = bwfm_pci_dmamem_alloc(sc, + sc->sc_dma_idx_bufsz, 8); + if (sc->sc_dma_idx_buf == NULL) { + /* XXX: Fallback to TCM? */ + printf("%s: cannot allocate idx buf\n", + DEVNAME(sc)); + return; + } + + idx_offset = sc->sc_dma_idx_sz; + h2d_w_idx_ptr = 0; + address = BWFM_PCI_DMA_DVA(sc->sc_dma_idx_buf); + ringinfo.h2d_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_w_idx_hostaddr_high = + htole32(address >> 32); + + h2d_r_idx_ptr = h2d_w_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.h2d_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.h2d_r_idx_hostaddr_high = + htole32(address >> 32); + + d2h_w_idx_ptr = h2d_r_idx_ptr + + sc->sc_max_submissionrings * idx_offset; + address += sc->sc_max_submissionrings * idx_offset; + ringinfo.d2h_w_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_w_idx_hostaddr_high = + htole32(address >> 32); + + d2h_r_idx_ptr = d2h_w_idx_ptr + + sc->sc_max_completionrings * idx_offset; + address += sc->sc_max_completionrings * idx_offset; + ringinfo.d2h_r_idx_hostaddr_low = + htole32(address & 0xffffffff); + ringinfo.d2h_r_idx_hostaddr_high = + htole32(address >> 32); + + bus_space_write_region_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_ring_info_addr, (void *)&ringinfo, sizeof(ringinfo)); + } + + uint32_t ring_mem_ptr = letoh32(ringinfo.ringmem); + /* TX ctrl ring: Send ctrl buffers, send IOCTLs */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_submit, 64, 40, + h2d_w_idx_ptr, h2d_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* TX rxpost ring: Send clean data mbufs for RX */ + if (bwfm_pci_setup_ring(sc, &sc->sc_rxpost_submit, 512, 32, + h2d_w_idx_ptr, h2d_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + /* RX completion rings: recv our filled buffers back */ + if (bwfm_pci_setup_ring(sc, &sc->sc_ctrl_complete, 64, 24, + d2h_w_idx_ptr, d2h_r_idx_ptr, 0, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_tx_complete, 1024, 16, + d2h_w_idx_ptr, d2h_r_idx_ptr, 1, idx_offset, + &ring_mem_ptr)) + goto cleanup; + if (bwfm_pci_setup_ring(sc, &sc->sc_rx_complete, 512, 32, + d2h_w_idx_ptr, d2h_r_idx_ptr, 2, idx_offset, + &ring_mem_ptr)) + goto cleanup; + + /* Dynamic TX rings for actual data */ + sc->sc_flowrings = kmem_zalloc(sc->sc_max_flowrings * + sizeof(struct bwfm_pci_msgring), KM_SLEEP); + for (i = 0; i < sc->sc_max_flowrings; i++) { + struct bwfm_pci_msgring *ring = &sc->sc_flowrings[i]; + ring->w_idx_addr = h2d_w_idx_ptr + (i + 2) * idx_offset; + ring->r_idx_addr = h2d_r_idx_ptr + (i + 2) * idx_offset; + } + + pool_init(&sc->sc_flowring_pool, sizeof(struct bwfm_cmd_flowring_create), + 0, 0, 0, "bwfmpl", NULL, IPL_NET); + + /* Scratch and ring update buffers for firmware */ + if ((sc->sc_scratch_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_SCRATCH_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_scratch_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_SCRATCH_LEN, + BWFM_DMA_D2H_SCRATCH_BUF_LEN); + + if ((sc->sc_ringupd_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_D2H_RINGUPD_BUF_LEN, 8)) == NULL) + goto cleanup; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_LOW, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH, + BWFM_PCI_DMA_DVA(sc->sc_ringupd_buf) >> 32); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_shared_address + BWFM_SHARED_DMA_RINGUPD_LEN, + BWFM_DMA_D2H_RINGUPD_BUF_LEN); + + if ((sc->sc_ioctl_buf = bwfm_pci_dmamem_alloc(sc, + BWFM_DMA_H2D_IOCTL_BUF_LEN, 8)) == NULL) + goto cleanup; + + if (workqueue_create(&sc->flowring_wq, "bwfmflow", + bwfm_pci_flowring_create_cb, sc, PRI_SOFTNET, IPL_NET, 0)) + goto cleanup; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + bwfm_pci_intr_enable(sc); + + /* Maps RX mbufs to a packet id and back. */ + sc->sc_rx_pkts.npkt = BWFM_NUM_RX_PKTIDS; + sc->sc_rx_pkts.pkts = kmem_zalloc(BWFM_NUM_RX_PKTIDS * + sizeof(struct bwfm_pci_buf), KM_SLEEP); + for (i = 0; i < BWFM_NUM_RX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_RX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_rx_pkts.pkts[i].bb_map); + + /* Maps TX mbufs to a packet id and back. */ + sc->sc_tx_pkts.npkt = BWFM_NUM_TX_PKTIDS; + sc->sc_tx_pkts.pkts = kmem_zalloc(BWFM_NUM_TX_PKTIDS + * sizeof(struct bwfm_pci_buf), KM_SLEEP); + for (i = 0; i < BWFM_NUM_TX_PKTIDS; i++) + bus_dmamap_create(sc->sc_dmat, MSGBUF_MAX_PKT_SIZE, + BWFM_NUM_TX_DESCS, MSGBUF_MAX_PKT_SIZE, 0, BUS_DMA_WAITOK, + &sc->sc_tx_pkts.pkts[i].bb_map); + + /* + * For whatever reason, could also be a bug somewhere in this + * driver, the firmware needs a bunch of RX buffers otherwise + * it won't send any RX complete messages. 64 buffers don't + * suffice, but 128 buffers are enough. + */ + if_rxr_init(&sc->sc_rxbuf_ring, 128, sc->sc_max_rxbufpost); + if_rxr_init(&sc->sc_ioctl_ring, 8, 8); + if_rxr_init(&sc->sc_event_ring, 8, 8); + bwfm_pci_fill_rx_rings(sc); + + +#ifdef BWFM_DEBUG + sc->sc_console_readidx = 0; + bwfm_pci_debug_console(sc); +#endif + + sc->sc_ioctl_poll = 1; + sc->sc_sc.sc_bus_ops = &bwfm_pci_bus_ops; + sc->sc_sc.sc_proto_ops = &bwfm_pci_msgbuf_ops; + bwfm_attach(&sc->sc_sc); + sc->sc_ioctl_poll = 0; + return; + +cleanup: + if (sc->flowring_wq != NULL) + workqueue_destroy(sc->flowring_wq); + if (sc->sc_ih != NULL) { + pci_intr_disestablish(sc->sc_pc, sc->sc_ih); + pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); + } + if (sc->sc_ioctl_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + if (sc->sc_ringupd_buf) + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + if (sc->sc_scratch_buf) + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + if (sc->sc_rx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + if (sc->sc_tx_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + if (sc->sc_ctrl_complete.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + if (sc->sc_rxpost_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + if (sc->sc_ctrl_submit.ring) + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + if (sc->sc_dma_idx_buf) + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); +} + +int +bwfm_pci_load_microcode(struct bwfm_pci_softc *sc, const u_char *ucode, size_t size) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + uint32_t shared; + int i; + + if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) { + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_ARM_CR4); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 5); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKIDX, 7); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_ARMCR4REG_BANKPDA, 0); + } + + for (i = 0; i < size; i++) + bus_space_write_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + i, ucode[i]); + + /* Firmware replaces this with a pointer once up. */ + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4, 0); + + /* TODO: restore NVRAM */ + + /* Load reset vector from firmware and kickstart core. */ + if (bwfm->sc_chip.ch_chip == BRCM_CC_43602_CHIP_ID) { + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_INTERNAL_MEM); + bwfm->sc_chip.ch_core_reset(bwfm, core, 0, 0, 0); + } + bwfm_chip_set_active(bwfm, *(const uint32_t *)ucode); + + for (i = 0; i < 40; i++) { + delay(50 * 1000); + shared = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + bwfm->sc_chip.ch_rambase + bwfm->sc_chip.ch_ramsize - 4); + if (shared) + break; + } + if (!shared) { + printf("%s: firmware did not come up\n", DEVNAME(sc)); + return 1; + } + + sc->sc_shared_address = shared; + return 0; +} + +int +bwfm_pci_detach(device_t self, int flags) +{ + struct bwfm_pci_softc *sc = device_private(self); + + bwfm_detach(&sc->sc_sc, flags); + + /* FIXME: free RX buffers */ + /* FIXME: free TX buffers */ + /* FIXME: free more memory */ + + kmem_free(sc->sc_flowrings, sc->sc_max_flowrings + * sizeof(struct bwfm_pci_msgring)); + pool_destroy(&sc->sc_flowring_pool); + + workqueue_destroy(sc->flowring_wq); + pci_intr_disestablish(sc->sc_pc, sc->sc_ih); + pci_intr_release(sc->sc_pc, sc->sc_pihp, 1); + bwfm_pci_dmamem_free(sc, sc->sc_ioctl_buf); + bwfm_pci_dmamem_free(sc, sc->sc_ringupd_buf); + bwfm_pci_dmamem_free(sc, sc->sc_scratch_buf); + bwfm_pci_dmamem_free(sc, sc->sc_rx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_tx_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_complete.ring); + bwfm_pci_dmamem_free(sc, sc->sc_rxpost_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_ctrl_submit.ring); + bwfm_pci_dmamem_free(sc, sc->sc_dma_idx_buf); + return 0; +} + +/* DMA code */ +struct bwfm_pci_dmamem * +bwfm_pci_dmamem_alloc(struct bwfm_pci_softc *sc, bus_size_t size, bus_size_t align) +{ + struct bwfm_pci_dmamem *bdm; + int nsegs; + + bdm = kmem_zalloc(sizeof(*bdm), KM_SLEEP); + bdm->bdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &bdm->bdm_map) != 0) + goto bdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &bdm->bdm_seg, 1, + &nsegs, BUS_DMA_WAITOK) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &bdm->bdm_seg, nsegs, size, + (void **) &bdm->bdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, bdm->bdm_map, bdm->bdm_kva, size, + NULL, BUS_DMA_WAITOK) != 0) + goto unmap; + + bzero(bdm->bdm_kva, size); + + return (bdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); +bdmfree: + kmem_free(bdm, sizeof(*bdm)); + + return (NULL); +} + +void +bwfm_pci_dmamem_free(struct bwfm_pci_softc *sc, struct bwfm_pci_dmamem *bdm) +{ + bus_dmamem_unmap(sc->sc_dmat, bdm->bdm_kva, bdm->bdm_size); + bus_dmamem_free(sc->sc_dmat, &bdm->bdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, bdm->bdm_map); + kmem_free(bdm, sizeof(*bdm)); +} + +/* + * We need a simple mapping from a packet ID to mbufs, because when + * a transfer completed, we only know the ID so we have to look up + * the memory for the ID. This simply looks for an empty slot. + */ +int +bwfm_pci_pktid_avail(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts) +{ + int i, idx; + + idx = pkts->last + 1; + for (i = 0; i < pkts->npkt; i++) { + if (idx == pkts->npkt) + idx = 0; + if (pkts->pkts[idx].bb_m == NULL) + return 0; + idx++; + } + return ENOBUFS; +} + +int +bwfm_pci_pktid_new(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + struct mbuf *m, uint32_t *pktid, paddr_t *paddr) +{ + int i, idx; + + idx = pkts->last + 1; + for (i = 0; i < pkts->npkt; i++) { + if (idx == pkts->npkt) + idx = 0; + if (pkts->pkts[idx].bb_m == NULL) { + if (bus_dmamap_load_mbuf(sc->sc_dmat, + pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) != 0) { + if (m_defrag(m, M_DONTWAIT)) + return EFBIG; + if (bus_dmamap_load_mbuf(sc->sc_dmat, + pkts->pkts[idx].bb_map, m, BUS_DMA_NOWAIT) != 0) + return EFBIG; + } + pkts->last = idx; + pkts->pkts[idx].bb_m = m; + *pktid = idx; + *paddr = pkts->pkts[idx].bb_map->dm_segs[0].ds_addr; + return 0; + } + idx++; + } + return ENOBUFS; +} + +struct mbuf * +bwfm_pci_pktid_free(struct bwfm_pci_softc *sc, struct bwfm_pci_pkts *pkts, + uint32_t pktid) +{ + struct mbuf *m; + + if (pktid >= pkts->npkt || pkts->pkts[pktid].bb_m == NULL) + return NULL; + bus_dmamap_unload(sc->sc_dmat, pkts->pkts[pktid].bb_map); + m = pkts->pkts[pktid].bb_m; + pkts->pkts[pktid].bb_m = NULL; + return m; +} + +void +bwfm_pci_fill_rx_rings(struct bwfm_pci_softc *sc) +{ + bwfm_pci_fill_rx_buf_ring(sc); + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_ioctl_ring, + MSGBUF_TYPE_IOCTLRESP_BUF_POST); + bwfm_pci_fill_rx_ioctl_ring(sc, &sc->sc_event_ring, + MSGBUF_TYPE_EVENT_BUF_POST); +} + +void +bwfm_pci_fill_rx_ioctl_ring(struct bwfm_pci_softc *sc, struct if_rxring *rxring, + uint32_t msgtype) +{ + struct msgbuf_rx_ioctl_resp_or_event *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(rxring, 8); slots > 0; slots--) { + if (bwfm_pci_pktid_avail(sc, &sc->sc_rx_pkts)) + break; + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_ctrl_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = msgtype; + req->msg.request_id = htole32(pktid); + req->host_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->host_buf_addr.high_addr = htole32(paddr >> 32); + req->host_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + } + if_rxr_put(rxring, slots); + splx(s); +} + +void +bwfm_pci_fill_rx_buf_ring(struct bwfm_pci_softc *sc) +{ + struct msgbuf_rx_bufpost *req; + struct mbuf *m; + uint32_t pktid; + paddr_t paddr; + int s, slots; + + s = splnet(); + for (slots = if_rxr_get(&sc->sc_rxbuf_ring, sc->sc_max_rxbufpost); + slots > 0; slots--) { + if (bwfm_pci_pktid_avail(sc, &sc->sc_rx_pkts)) + break; + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_rxpost_submit); + if (req == NULL) + break; + m = MCLGETI(NULL, M_DONTWAIT, NULL, MSGBUF_MAX_PKT_SIZE); + if (m == NULL) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + break; + } + m->m_len = m->m_pkthdr.len = MSGBUF_MAX_PKT_SIZE; + if (bwfm_pci_pktid_new(sc, &sc->sc_rx_pkts, m, &pktid, &paddr)) { + bwfm_pci_ring_write_cancel(sc, &sc->sc_rxpost_submit, 1); + m_freem(m); + break; + } + memset(req, 0, sizeof(*req)); + req->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; + req->msg.request_id = htole32(pktid); + req->data_buf_len = htole16(MSGBUF_MAX_PKT_SIZE); + req->data_buf_addr.high_addr = htole32(paddr >> 32); + req->data_buf_addr.low_addr = htole32(paddr & 0xffffffff); + bwfm_pci_ring_write_commit(sc, &sc->sc_rxpost_submit); + } + if_rxr_put(&sc->sc_rxbuf_ring, slots); + splx(s); +} + +int +bwfm_pci_setup_ring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring, + int nitem, size_t itemsz, uint32_t w_idx, uint32_t r_idx, + int idx, uint32_t idx_off, uint32_t *ring_mem) +{ + ring->w_idx_addr = w_idx + idx * idx_off; + ring->r_idx_addr = r_idx + idx * idx_off; + ring->nitem = nitem; + ring->itemsz = itemsz; + bwfm_pci_ring_write_rptr(sc, ring); + bwfm_pci_ring_write_wptr(sc, ring); + + ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8); + if (ring->ring == NULL) + return ENOMEM; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_LOW, + BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff); + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MEM_BASE_ADDR_HIGH, + BWFM_PCI_DMA_DVA(ring->ring) >> 32); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_MAX_ITEM, nitem); + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + *ring_mem + BWFM_RING_LEN_ITEMS, itemsz); + *ring_mem = *ring_mem + BWFM_RING_MEM_SZ; + return 0; +} + +int +bwfm_pci_setup_flowring(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring, + int nitem, size_t itemsz) +{ + ring->w_ptr = 0; + ring->r_ptr = 0; + ring->nitem = nitem; + ring->itemsz = itemsz; + bwfm_pci_ring_write_rptr(sc, ring); + bwfm_pci_ring_write_wptr(sc, ring); + + ring->ring = bwfm_pci_dmamem_alloc(sc, nitem * itemsz, 8); + if (ring->ring == NULL) + return ENOMEM; + return 0; +} + +/* Ring helpers */ +void +bwfm_pci_ring_bell(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_H2D_MAILBOX, 1); +} + +void +bwfm_pci_ring_update_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->r_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->r_idx_addr); + } else { + ring->r_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr); + } +} + +static u_int +if_rxr_get(struct if_rxring *rxr, unsigned int max) +{ + u_int taken = MIN(max, (rxr->rxr_total - rxr->rxr_inuse)); + + KASSERTMSG(rxr->rxr_inuse + taken <= rxr->rxr_total, + "rxr->rxr_inuse: %d\n" + "taken: %d\n" + "rxr->rxr_total: %d\n", + rxr->rxr_inuse, taken, rxr->rxr_total); + rxr->rxr_inuse += taken; + + return taken; +} + +static void +if_rxr_put(struct if_rxring *rxr, unsigned int n) +{ + KASSERTMSG(rxr->rxr_inuse >= n, + "rxr->rxr_inuse: %d\n" + "n: %d\n" + "rxr->rxr_total: %d\n", + rxr->rxr_inuse, n, rxr->rxr_total); + + rxr->rxr_inuse -= n; +} + +static void +if_rxr_init(struct if_rxring *rxr, unsigned int lwm __unused, unsigned int hwm) +{ + (void) lwm; + + rxr->rxr_total = hwm; + rxr->rxr_inuse = 0; +} + +void +bwfm_pci_ring_update_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + ring->w_ptr = bus_space_read_2(sc->sc_tcm_iot, + sc->sc_tcm_ioh, ring->w_idx_addr); + } else { + ring->w_ptr = *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr); + } +} + +void +bwfm_pci_ring_write_rptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->r_idx_addr, ring->r_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->r_idx_addr) = ring->r_ptr; + } +} + +void +bwfm_pci_ring_write_wptr(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + if (sc->sc_dma_idx_sz == 0) { + bus_space_write_2(sc->sc_tcm_iot, sc->sc_tcm_ioh, + ring->w_idx_addr, ring->w_ptr); + } else { + *(uint16_t *)(BWFM_PCI_DMA_KVA(sc->sc_dma_idx_buf) + + ring->w_idx_addr) = ring->w_ptr; + } +} + +/* + * Retrieve a free descriptor to put new stuff in, but don't commit + * to it yet so we can rollback later if any error occurs. + */ +void * +bwfm_pci_ring_write_reserve(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + ring->w_ptr += 1; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +void * +bwfm_pci_ring_write_reserve_multi(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int count, int *avail) +{ + int available; + char *ret; + + bwfm_pci_ring_update_rptr(sc, ring); + + if (ring->r_ptr > ring->w_ptr) + available = ring->r_ptr - ring->w_ptr; + else + available = ring->r_ptr + (ring->nitem - ring->w_ptr); + + if (available < 1) + return NULL; + + ret = BWFM_PCI_DMA_KVA(ring->ring) + (ring->w_ptr * ring->itemsz); + *avail = min(count, available - 1); + if (*avail + ring->w_ptr > ring->nitem) + *avail = ring->nitem - ring->w_ptr; + ring->w_ptr += *avail; + if (ring->w_ptr == ring->nitem) + ring->w_ptr = 0; + return ret; +} + +/* + * Read number of descriptors available (submitted by the firmware) + * and retrieve pointer to first descriptor. + */ +void * +bwfm_pci_ring_read_avail(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int *avail) +{ + bwfm_pci_ring_update_wptr(sc, ring); + + if (ring->w_ptr >= ring->r_ptr) + *avail = ring->w_ptr - ring->r_ptr; + else + *avail = ring->nitem - ring->r_ptr; + + if (*avail == 0) + return NULL; + + return BWFM_PCI_DMA_KVA(ring->ring) + (ring->r_ptr * ring->itemsz); +} + +/* + * Let firmware know we read N descriptors. + */ +void +bwfm_pci_ring_read_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + ring->r_ptr += nitem; + if (ring->r_ptr == ring->nitem) + ring->r_ptr = 0; + bwfm_pci_ring_write_rptr(sc, ring); +} + +/* + * Let firmware know that we submitted some descriptors. + */ +void +bwfm_pci_ring_write_commit(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring) +{ + bwfm_pci_ring_write_wptr(sc, ring); + bwfm_pci_ring_bell(sc, ring); +} + +/* + * Rollback N descriptors in case we don't actually want + * to commit to it. + */ +void +bwfm_pci_ring_write_cancel(struct bwfm_pci_softc *sc, + struct bwfm_pci_msgring *ring, int nitem) +{ + if (ring->w_ptr == 0) + ring->w_ptr = ring->nitem - nitem; + else + ring->w_ptr -= nitem; +} + +/* + * Foreach written descriptor on the ring, pass the descriptor to + * a message handler and let the firmware know we handled it. + */ +void +bwfm_pci_ring_rx(struct bwfm_pci_softc *sc, struct bwfm_pci_msgring *ring) +{ + char *buf; + int avail, processed; + +again: + buf = bwfm_pci_ring_read_avail(sc, ring, &avail); + if (buf == NULL) + return; + + processed = 0; + while (avail) { + bwfm_pci_msg_rx(sc, buf + sc->sc_rx_dataoffset); + buf += ring->itemsz; + processed++; + if (processed == 48) { + bwfm_pci_ring_read_commit(sc, ring, processed); + processed = 0; + } + avail--; + } + if (processed) + bwfm_pci_ring_read_commit(sc, ring, processed); + if (ring->r_ptr == 0) + goto again; +} + +void +bwfm_pci_msg_rx(struct bwfm_pci_softc *sc, void *buf) +{ + struct ifnet *ifp = sc->sc_sc.sc_ic.ic_ifp; + struct msgbuf_ioctl_resp_hdr *resp; + struct msgbuf_tx_status *tx; + struct msgbuf_rx_complete *rx; + struct msgbuf_rx_event *event; + struct msgbuf_common_hdr *msg; + struct msgbuf_flowring_create_resp *fcr; + struct msgbuf_flowring_delete_resp *fdr; + struct bwfm_pci_msgring *ring; + struct mbuf *m; + int flowid; + + msg = (struct msgbuf_common_hdr *)buf; + switch (msg->msgtype) + { + case MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT: + fcr = (struct msgbuf_flowring_create_resp *)buf; + flowid = letoh16(fcr->compl_hdr.flow_ring_id); + if (flowid < 2) + break; + flowid -= 2; + if (flowid >= sc->sc_max_flowrings) + break; + ring = &sc->sc_flowrings[flowid]; + if (ring->status != RING_OPENING) + break; + if (fcr->compl_hdr.status) { + printf("%s: failed to open flowring %d\n", + DEVNAME(sc), flowid); + ring->status = RING_CLOSED; + if (ring->m) { + m_freem(ring->m); + ring->m = NULL; + } + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_start(ifp); + break; + } + ring->status = RING_OPEN; + if (ring->m != NULL) { + m = ring->m; + ring->m = NULL; + if (bwfm_pci_txdata(&sc->sc_sc, m)) + m_freem(ring->m); + } + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_start(ifp); + break; + case MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT: + fdr = (struct msgbuf_flowring_delete_resp *)buf; + flowid = letoh16(fdr->compl_hdr.flow_ring_id); + if (flowid < 2) + break; + flowid -= 2; + if (flowid >= sc->sc_max_flowrings) + break; + ring = &sc->sc_flowrings[flowid]; + if (ring->status != RING_CLOSING) + break; + if (fdr->compl_hdr.status) { + printf("%s: failed to delete flowring %d\n", + DEVNAME(sc), flowid); + break; + } + bwfm_pci_dmamem_free(sc, ring->ring); + ring->status = RING_CLOSED; + break; + case MSGBUF_TYPE_IOCTLPTR_REQ_ACK: + break; + case MSGBUF_TYPE_IOCTL_CMPLT: + resp = (struct msgbuf_ioctl_resp_hdr *)buf; + sc->sc_ioctl_resp_pktid = letoh32(resp->msg.request_id); + sc->sc_ioctl_resp_ret_len = letoh16(resp->resp_len); + sc->sc_ioctl_resp_status = letoh16(resp->compl_hdr.status); + if_rxr_put(&sc->sc_ioctl_ring, 1); + bwfm_pci_fill_rx_rings(sc); + wakeup(&sc->sc_ioctl_buf); + break; + case MSGBUF_TYPE_WL_EVENT: + event = (struct msgbuf_rx_event *)buf; + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, + letoh32(event->msg.request_id)); + if (m == NULL) + break; + m_adj(m, sc->sc_rx_dataoffset); + m->m_len = m->m_pkthdr.len = letoh16(event->event_data_len); + bwfm_rx(&sc->sc_sc, m); + if_rxr_put(&sc->sc_event_ring, 1); + bwfm_pci_fill_rx_rings(sc); + break; + case MSGBUF_TYPE_TX_STATUS: + tx = (struct msgbuf_tx_status *)buf; + m = bwfm_pci_pktid_free(sc, &sc->sc_tx_pkts, + letoh32(tx->msg.request_id)); + if (m == NULL) + break; + m_freem(m); + if (sc->sc_tx_pkts_full) { + sc->sc_tx_pkts_full = 0; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_start(ifp); + } + break; + case MSGBUF_TYPE_RX_CMPLT: + rx = (struct msgbuf_rx_complete *)buf; + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, + letoh32(rx->msg.request_id)); + if (m == NULL) + break; + if (letoh16(rx->data_offset)) + m_adj(m, letoh16(rx->data_offset)); + else if (sc->sc_rx_dataoffset) + m_adj(m, sc->sc_rx_dataoffset); + m->m_len = m->m_pkthdr.len = letoh16(rx->data_len); + bwfm_rx(&sc->sc_sc, m); + if_rxr_put(&sc->sc_rxbuf_ring, 1); + bwfm_pci_fill_rx_rings(sc); + break; + default: + printf("%s: msgtype 0x%08x\n", __func__, msg->msgtype); + break; + } +} + +/* Bus core helpers */ +void +bwfm_pci_select_core(struct bwfm_pci_softc *sc, int id) +{ + struct bwfm_softc *bwfm = (void *)sc; + struct bwfm_core *core; + + core = bwfm_chip_get_core(bwfm, id); + if (core == NULL) { + printf("%s: could not find core to select", DEVNAME(sc)); + return; + } + + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); + if (pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW) != core->co_base) + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_BAR0_WINDOW, core->co_base); +} + +uint32_t +bwfm_pci_buscore_read(struct bwfm_softc *bwfm, uint32_t reg) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + return bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset); +} + +void +bwfm_pci_buscore_write(struct bwfm_softc *bwfm, uint32_t reg, uint32_t val) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + uint32_t page, offset; + + page = reg & ~(BWFM_PCI_BAR0_REG_SIZE - 1); + offset = reg & (BWFM_PCI_BAR0_REG_SIZE - 1); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_BAR0_WINDOW, page); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, offset, val); +} + +int +bwfm_pci_buscore_prepare(struct bwfm_softc *bwfm) +{ + return 0; +} + +int +bwfm_pci_buscore_reset(struct bwfm_softc *bwfm) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_core *core; + uint32_t reg; + int i; + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + reg = pci_conf_read(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL); + pci_conf_write(sc->sc_pc, sc->sc_tag, BWFM_PCI_CFGREG_LINK_STATUS_CTRL, + reg & ~BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_CHIPCOMMON); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_CHIP_REG_WATCHDOG, 4); + delay(100 * 1000); + + bwfm_pci_select_core(sc, BWFM_AGENT_CORE_PCIE2); + pci_conf_write(sc->sc_pc, sc->sc_tag, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL, reg); + + core = bwfm_chip_get_core(bwfm, BWFM_AGENT_CORE_PCIE2); + if (core->co_rev <= 13) { + uint16_t cfg_offset[] = { + BWFM_PCI_CFGREG_STATUS_CMD, + BWFM_PCI_CFGREG_PM_CSR, + BWFM_PCI_CFGREG_MSI_CAP, + BWFM_PCI_CFGREG_MSI_ADDR_L, + BWFM_PCI_CFGREG_MSI_ADDR_H, + BWFM_PCI_CFGREG_MSI_DATA, + BWFM_PCI_CFGREG_LINK_STATUS_CTRL2, + BWFM_PCI_CFGREG_RBAR_CTRL, + BWFM_PCI_CFGREG_PML1_SUB_CTRL1, + BWFM_PCI_CFGREG_REG_BAR2_CONFIG, + BWFM_PCI_CFGREG_REG_BAR3_CONFIG, + }; + + for (i = 0; i < nitems(cfg_offset); i++) { + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGADDR, cfg_offset[i]); + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA); + DPRINTFN(3, ("%s: config offset 0x%04x, value 0x%04x\n", + DEVNAME(sc), cfg_offset[i], reg)); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_CONFIGDATA, reg); + } + } + + reg = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT); + if (reg != 0xffffffff) + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, reg); + + return 0; +} + +void +bwfm_pci_buscore_activate(struct bwfm_softc *bwfm, const uint32_t rstvec) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + bus_space_write_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, 0, rstvec); +} + +static int bwfm_pci_prio2fifo[8] = { + 1, /* best effort */ + 0, /* IPTOS_PREC_IMMEDIATE */ + 0, /* IPTOS_PREC_PRIORITY */ + 1, /* IPTOS_PREC_FLASH */ + 2, /* IPTOS_PREC_FLASHOVERRIDE */ + 2, /* IPTOS_PREC_CRITIC_ECP */ + 3, /* IPTOS_PREC_INTERNETCONTROL */ + 3, /* IPTOS_PREC_NETCONTROL */ +}; + +int +bwfm_pci_flowring_lookup(struct bwfm_pci_softc *sc, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_sc.sc_ic; + uint8_t *da = mtod(m, uint8_t *); + struct ether_header *eh; + int flowid, prio, fifo; + int i, found, ac; + + /* No QoS for EAPOL frames. */ + eh = mtod(m, struct ether_header *); + ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? + M_WME_GETAC(m) : WME_AC_BE; + + prio = ac; + fifo = bwfm_pci_prio2fifo[prio]; + + switch (ic->ic_opmode) + { + case IEEE80211_M_STA: + flowid = fifo; + break; +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_HOSTAP: + if (ETHER_IS_MULTICAST(da)) + da = __UNCONST(etherbroadcastaddr); + flowid = da[5] * 2 + fifo; + break; +#endif + default: + printf("%s: state not supported\n", DEVNAME(sc)); + return ENOBUFS; + } + + found = 0; + flowid = flowid % sc->sc_max_flowrings; + for (i = 0; i < sc->sc_max_flowrings; i++) { + if (ic->ic_opmode == IEEE80211_M_STA && + sc->sc_flowrings[flowid].status >= RING_OPEN && + sc->sc_flowrings[flowid].fifo == fifo) { + found = 1; + break; + } +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP && + sc->sc_flowrings[flowid].status >= RING_OPEN && + sc->sc_flowrings[flowid].fifo == fifo && + !memcmp(sc->sc_flowrings[flowid].mac, da, ETHER_ADDR_LEN)) { + found = 1; + break; + } +#endif + flowid = (flowid + 1) % sc->sc_max_flowrings; + } + + if (found) + return flowid; + + return -1; +} + +void +bwfm_pci_flowring_create(struct bwfm_pci_softc *sc, struct mbuf *m) +{ + struct ieee80211com *ic = &sc->sc_sc.sc_ic; + struct bwfm_cmd_flowring_create * cmd; + uint8_t *da = mtod(m, uint8_t *); + struct ether_header *eh; + struct bwfm_pci_msgring *ring; + int flowid, prio, fifo; + int i, found, ac; + + cmd = pool_get(&sc->sc_flowring_pool, PR_NOWAIT); + if (__predict_false(cmd == NULL)) + return; + + /* No QoS for EAPOL frames. */ + eh = mtod(m, struct ether_header *); + ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? + M_WME_GETAC(m) : WME_AC_BE; + + prio = ac; + fifo = bwfm_pci_prio2fifo[prio]; + + switch (ic->ic_opmode) + { + case IEEE80211_M_STA: + flowid = fifo; + break; +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_HOSTAP: + if (ETHER_IS_MULTICAST(da)) + da = __UNCONST(etherbroadcastaddr); + flowid = da[5] * 2 + fifo; + break; +#endif + default: + printf("%s: state not supported\n", DEVNAME(sc)); + return; + } + + found = 0; + flowid = flowid % sc->sc_max_flowrings; + for (i = 0; i < sc->sc_max_flowrings; i++) { + ring = &sc->sc_flowrings[flowid]; + if (ring->status == RING_CLOSED) { + ring->status = RING_OPENING; + found = 1; + break; + } + flowid = (flowid + 1) % sc->sc_max_flowrings; + } + + /* + * We cannot recover from that so far. Only a stop/init + * cycle can revive this if it ever happens at all. + */ + if (!found) { + printf("%s: no flowring available\n", DEVNAME(sc)); + return; + } + + cmd->sc = sc; + cmd->m = m; + cmd->prio = prio; + cmd->flowid = flowid; + workqueue_enqueue(sc->flowring_wq, &cmd->wq_cookie, NULL); +} + +void +bwfm_pci_flowring_create_cb(struct work *wk, void *arg) //(struct bwfm_softc *bwfm, void *arg) +{ + struct bwfm_cmd_flowring_create *cmd = container_of(wk, struct bwfm_cmd_flowring_create, wq_cookie); + struct bwfm_pci_softc *sc = cmd->sc; // (void *)bwfm; + struct ieee80211com *ic = &sc->sc_sc.sc_ic; + struct msgbuf_tx_flowring_create_req *req; + struct bwfm_pci_msgring *ring; + uint8_t *da, *sa; + + da = mtod(cmd->m, char *) + 0 * ETHER_ADDR_LEN; + sa = mtod(cmd->m, char *) + 1 * ETHER_ADDR_LEN; + + ring = &sc->sc_flowrings[cmd->flowid]; + if (ring->status != RING_OPENING) { + printf("%s: flowring not opening\n", DEVNAME(sc)); + return; + } + + if (bwfm_pci_setup_flowring(sc, ring, 512, 48)) { + printf("%s: cannot setup flowring\n", DEVNAME(sc)); + return; + } + + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) { + printf("%s: cannot reserve for flowring\n", DEVNAME(sc)); + return; + } + + ring->status = RING_OPENING; + ring->fifo = bwfm_pci_prio2fifo[cmd->prio]; + ring->m = cmd->m; + memcpy(ring->mac, da, ETHER_ADDR_LEN); +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP && ETHER_IS_MULTICAST(da)) + memcpy(ring->mac, etherbroadcastaddr, ETHER_ADDR_LEN); +#endif + + req->msg.msgtype = MSGBUF_TYPE_FLOW_RING_CREATE; + req->msg.ifidx = 0; + req->msg.request_id = 0; + req->tid = bwfm_pci_prio2fifo[cmd->prio]; + req->flow_ring_id = letoh16(cmd->flowid + 2); + memcpy(req->da, da, ETHER_ADDR_LEN); + memcpy(req->sa, sa, ETHER_ADDR_LEN); + req->flow_ring_addr.high_addr = + letoh32(BWFM_PCI_DMA_DVA(ring->ring) >> 32); + req->flow_ring_addr.low_addr = + letoh32(BWFM_PCI_DMA_DVA(ring->ring) & 0xffffffff); + req->max_items = letoh16(512); + req->len_item = letoh16(48); + + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + pool_put(&sc->sc_flowring_pool, cmd); +} + +void +bwfm_pci_flowring_delete(struct bwfm_pci_softc *sc, int flowid) +{ + struct msgbuf_tx_flowring_delete_req *req; + struct bwfm_pci_msgring *ring; + + ring = &sc->sc_flowrings[flowid]; + if (ring->status != RING_OPEN) { + printf("%s: flowring not open\n", DEVNAME(sc)); + return; + } + + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) { + printf("%s: cannot reserve for flowring\n", DEVNAME(sc)); + return; + } + + ring->status = RING_CLOSING; + + req->msg.msgtype = MSGBUF_TYPE_FLOW_RING_DELETE; + req->msg.ifidx = 0; + req->msg.request_id = 0; + req->flow_ring_id = letoh16(flowid + 2); + req->reason = 0; + + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); +} + +void +bwfm_pci_stop(struct bwfm_softc *bwfm) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_pci_msgring *ring; + int i; + + for (i = 0; i < sc->sc_max_flowrings; i++) { + ring = &sc->sc_flowrings[i]; + if (ring->status == RING_OPEN) + bwfm_pci_flowring_delete(sc, i); + } +} + +int +bwfm_pci_txcheck(struct bwfm_softc *bwfm) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_pci_msgring *ring; + int i; + + /* If we are transitioning, we cannot send. */ + for (i = 0; i < sc->sc_max_flowrings; i++) { + ring = &sc->sc_flowrings[i]; + if (ring->status == RING_OPENING) + return ENOBUFS; + } + + if (bwfm_pci_pktid_avail(sc, &sc->sc_tx_pkts)) { + sc->sc_tx_pkts_full = 1; + return ENOBUFS; + } + + return 0; +} + +int +bwfm_pci_txdata(struct bwfm_softc *bwfm, struct mbuf *m) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct bwfm_pci_msgring *ring; + struct msgbuf_tx_msghdr *tx; + uint32_t pktid; + paddr_t paddr; + struct ether_header *eh; + int flowid, ret, ac; + + flowid = bwfm_pci_flowring_lookup(sc, m); + if (flowid < 0) { + /* + * We cannot send the packet right now as there is + * no flowring yet. The flowring will be created + * asynchronously. While the ring is transitioning + * the TX check will tell the upper layers that we + * cannot send packets right now. When the flowring + * is created the queue will be restarted and this + * mbuf will be transmitted. + */ + bwfm_pci_flowring_create(sc, m); + return 0; + } + + ring = &sc->sc_flowrings[flowid]; + if (ring->status == RING_OPENING || + ring->status == RING_CLOSING) { + printf("%s: tried to use a flow that was " + "transitioning in status %d\n", + DEVNAME(sc), ring->status); + return ENOBUFS; + } + + tx = bwfm_pci_ring_write_reserve(sc, ring); + if (tx == NULL) + return ENOBUFS; + + /* No QoS for EAPOL frames. */ + eh = mtod(m, struct ether_header *); + ac = (eh->ether_type != htons(ETHERTYPE_PAE)) ? + M_WME_GETAC(m) : WME_AC_BE; + + memset(tx, 0, sizeof(*tx)); + tx->msg.msgtype = MSGBUF_TYPE_TX_POST; + tx->msg.ifidx = 0; + tx->flags = BWFM_MSGBUF_PKT_FLAGS_FRAME_802_3; + tx->flags |= ac << BWFM_MSGBUF_PKT_FLAGS_PRIO_SHIFT; + tx->seg_cnt = 1; + memcpy(tx->txhdr, mtod(m, char *), ETHER_HDR_LEN); + + ret = bwfm_pci_pktid_new(sc, &sc->sc_tx_pkts, m, &pktid, &paddr); + if (ret) { + if (ret == ENOBUFS) { + printf("%s: no pktid available for TX\n", + DEVNAME(sc)); + sc->sc_tx_pkts_full = 1; + } + bwfm_pci_ring_write_cancel(sc, ring, 1); + return ret; + } + paddr += ETHER_HDR_LEN; + + tx->msg.request_id = htole32(pktid); + tx->data_len = htole16(m->m_len - ETHER_HDR_LEN); + tx->data_buf_addr.high_addr = htole32(paddr >> 32); + tx->data_buf_addr.low_addr = htole32(paddr & 0xffffffff); + + bwfm_pci_ring_write_commit(sc, ring); + return 0; +} + +#ifdef BWFM_DEBUG +void +bwfm_pci_debug_console(struct bwfm_pci_softc *sc) +{ + uint32_t newidx = bus_space_read_4(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_base_addr + BWFM_CONSOLE_WRITEIDX); + + if (newidx != sc->sc_console_readidx) + DPRINTFN(3, ("BWFM CONSOLE: ")); + while (newidx != sc->sc_console_readidx) { + uint8_t ch = bus_space_read_1(sc->sc_tcm_iot, sc->sc_tcm_ioh, + sc->sc_console_buf_addr + 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 + +int +bwfm_pci_intr(void *v) +{ + struct bwfm_pci_softc *sc = (void *)v; + uint32_t status; + + if ((status = bus_space_read_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT)) == 0) + return 0; + + bwfm_pci_intr_disable(sc); + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXINT, status); + + if (status & (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1)) + printf("%s: handle MB data\n", __func__); + + if (status & BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB) { + bwfm_pci_ring_rx(sc, &sc->sc_rx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_tx_complete); + bwfm_pci_ring_rx(sc, &sc->sc_ctrl_complete); + } + +#ifdef BWFM_DEBUG + bwfm_pci_debug_console(sc); +#endif + + bwfm_pci_intr_enable(sc); + return 1; +} + +void +bwfm_pci_intr_enable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 | + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB); +} + +void +bwfm_pci_intr_disable(struct bwfm_pci_softc *sc) +{ + bus_space_write_4(sc->sc_reg_iot, sc->sc_reg_ioh, + BWFM_PCI_PCIE2REG_MAILBOXMASK, 0); +} + +/* Msgbuf protocol implementation */ +int +bwfm_pci_msgbuf_query_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t *len) +{ + struct bwfm_pci_softc *sc = (void *)bwfm; + struct msgbuf_ioctl_req_hdr *req; + struct mbuf *m; + size_t buflen; + int s; + + s = splnet(); + sc->sc_ioctl_resp_pktid = -1; + req = bwfm_pci_ring_write_reserve(sc, &sc->sc_ctrl_submit); + if (req == NULL) { + printf("%s: cannot reserve for write\n", DEVNAME(sc)); + splx(s); + return 1; + } + req->msg.msgtype = MSGBUF_TYPE_IOCTLPTR_REQ; + req->msg.ifidx = 0; + req->msg.flags = 0; + req->msg.request_id = htole32(MSGBUF_IOCTL_REQ_PKTID); + req->cmd = htole32(cmd); + req->output_buf_len = htole16(*len); + req->trans_id = htole16(sc->sc_ioctl_reqid++); + + buflen = min(*len, BWFM_DMA_H2D_IOCTL_BUF_LEN); + req->input_buf_len = htole16(buflen); + req->req_buf_addr.high_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) >> 32); + req->req_buf_addr.low_addr = + htole32((uint64_t)BWFM_PCI_DMA_DVA(sc->sc_ioctl_buf) & 0xffffffff); + if (buf) + memcpy(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), buf, buflen); + else + memset(BWFM_PCI_DMA_KVA(sc->sc_ioctl_buf), 0, buflen); + + bwfm_pci_ring_write_commit(sc, &sc->sc_ctrl_submit); + splx(s); + + if (tsleep(&sc->sc_ioctl_buf, PCATCH, "bwfm", hz)) { + printf("%s: timeout waiting for ioctl response\n", + DEVNAME(sc)); + return 1; + } + + m = bwfm_pci_pktid_free(sc, &sc->sc_rx_pkts, sc->sc_ioctl_resp_pktid); + if (m == NULL) + return 1; + + *len = min(buflen, sc->sc_ioctl_resp_ret_len); + if (buf) + memcpy(buf, mtod(m, char *), *len); + m_freem(m); + splx(s); + + return 0; +} + +int +bwfm_pci_msgbuf_set_dcmd(struct bwfm_softc *bwfm, int ifidx, + int cmd, char *buf, size_t len) +{ + return bwfm_pci_msgbuf_query_dcmd(bwfm, ifidx, cmd, buf, &len); +} diff --git a/sys/dev/pci/if_bwfm_pci.h b/sys/dev/pci/if_bwfm_pci.h new file mode 100644 index 0000000000..4c5da6fb2e --- /dev/null +++ b/sys/dev/pci/if_bwfm_pci.h @@ -0,0 +1,279 @@ +/* $OpenBSD: if_bwfm_pci.h,v 1.2 2018/01/05 23:30:16 patrick Exp $ */ +/* + * Copyright (c) 2010-2016 Broadcom Corporation + * Copyright (c) 2017 Patrick Wildt <[email protected]> + * + * 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_PCI_BAR0_WINDOW 0x80 +#define BWFM_PCI_BAR0_REG_SIZE 0x1000 + +#define BWFM_PCI_ARMCR4REG_BANKIDX 0x40 +#define BWFM_PCI_ARMCR4REG_BANKPDA 0x4C + +#define BWFM_PCI_PCIE2REG_INTMASK 0x24 +#define BWFM_PCI_PCIE2REG_MAILBOXINT 0x48 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK 0x4C +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_0 0x0100 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_FN0_1 0x0200 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0 0x10000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1 0x20000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0 0x40000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1 0x80000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0 0x100000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1 0x200000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0 0x400000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1 0x800000 +#define BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H_DB \ + (BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H0_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H1_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H2_DB1 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB0 | \ + BWFM_PCI_PCIE2REG_MAILBOXMASK_INT_D2H3_DB1) + +#define BWFM_PCI_PCIE2REG_CONFIGADDR 0x120 +#define BWFM_PCI_PCIE2REG_CONFIGDATA 0x124 +#define BWFM_PCI_PCIE2REG_H2D_MAILBOX 0x140 + +#define BWFM_PCI_CFGREG_STATUS_CMD 0x004 +#define BWFM_PCI_CFGREG_PM_CSR 0x04C +#define BWFM_PCI_CFGREG_MSI_CAP 0x058 +#define BWFM_PCI_CFGREG_MSI_ADDR_L 0x05C +#define BWFM_PCI_CFGREG_MSI_ADDR_H 0x060 +#define BWFM_PCI_CFGREG_MSI_DATA 0x064 +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL 0x0BC +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL_ASPM_ENAB 0x3 +#define BWFM_PCI_CFGREG_LINK_STATUS_CTRL2 0x0DC +#define BWFM_PCI_CFGREG_RBAR_CTRL 0x228 +#define BWFM_PCI_CFGREG_PML1_SUB_CTRL1 0x248 +#define BWFM_PCI_CFGREG_REG_BAR2_CONFIG 0x4E0 +#define BWFM_PCI_CFGREG_REG_BAR3_CONFIG 0x4F4 + +#define BWFM_RAMSIZE 0x6c +#define BWFM_RAMSIZE_MAGIC 0x534d4152 /* SMAR */ + +#define BWFM_SHARED_INFO 0x000 +#define BWFM_SHARED_INFO_MIN_VERSION 5 +#define BWFM_SHARED_INFO_MAX_VERSION 6 +#define BWFM_SHARED_INFO_VERSION_MASK 0x00FF +#define BWFM_SHARED_INFO_DMA_INDEX 0x10000 +#define BWFM_SHARED_INFO_DMA_2B_IDX 0x100000 +#define BWFM_SHARED_CONSOLE_ADDR 0x14 +#define BWFM_SHARED_MAX_RXBUFPOST 0x22 +#define BWFM_SHARED_MAX_RXBUFPOST_DEFAULT 255 +#define BWFM_SHARED_RX_DATAOFFSET 0x24 +#define BWFM_SHARED_HTOD_MB_DATA_ADDR 0x28 +#define BWFM_SHARED_DTOH_MB_DATA_ADDR 0x2c +#define BWFM_SHARED_RING_INFO_ADDR 0x30 +#define BWFM_SHARED_DMA_SCRATCH_LEN 0x34 +#define BWFM_SHARED_DMA_SCRATCH_ADDR_LOW 0x38 +#define BWFM_SHARED_DMA_SCRATCH_ADDR_HIGH 0x3c +#define BWFM_SHARED_DMA_RINGUPD_LEN 0x40 +#define BWFM_SHARED_DMA_RINGUPD_ADDR_LOW 0x44 +#define BWFM_SHARED_DMA_RINGUPD_ADDR_HIGH 0x48 + +#define BWFM_RING_MAX_ITEM 0x04 +#define BWFM_RING_LEN_ITEMS 0x06 +#define BWFM_RING_MEM_BASE_ADDR_LOW 0x08 +#define BWFM_RING_MEM_BASE_ADDR_HIGH 0x0c +#define BWFM_RING_MEM_SZ 16 + +#define BWFM_CONSOLE_BUFADDR 0x08 +#define BWFM_CONSOLE_BUFSIZE 0x0c +#define BWFM_CONSOLE_WRITEIDX 0x10 + +struct bwfm_pci_ringinfo { + uint32_t ringmem; + uint32_t h2d_w_idx_ptr; + uint32_t h2d_r_idx_ptr; + uint32_t d2h_w_idx_ptr; + uint32_t d2h_r_idx_ptr; + uint32_t h2d_w_idx_hostaddr_low; + uint32_t h2d_w_idx_hostaddr_high; + uint32_t h2d_r_idx_hostaddr_low; + uint32_t h2d_r_idx_hostaddr_high; + uint32_t d2h_w_idx_hostaddr_low; + uint32_t d2h_w_idx_hostaddr_high; + uint32_t d2h_r_idx_hostaddr_low; + uint32_t d2h_r_idx_hostaddr_high; + uint16_t max_flowrings; + uint16_t max_submissionrings; + uint16_t max_completionrings; +}; + +/* Msgbuf defines */ +#define MSGBUF_IOCTL_RESP_TIMEOUT 2000 /* msecs */ +#define MSGBUF_IOCTL_REQ_PKTID 0xFFFE +#define MSGBUF_MAX_PKT_SIZE 2048 + +#define MSGBUF_TYPE_GEN_STATUS 0x1 +#define MSGBUF_TYPE_RING_STATUS 0x2 +#define MSGBUF_TYPE_FLOW_RING_CREATE 0x3 +#define MSGBUF_TYPE_FLOW_RING_CREATE_CMPLT 0x4 +#define MSGBUF_TYPE_FLOW_RING_DELETE 0x5 +#define MSGBUF_TYPE_FLOW_RING_DELETE_CMPLT 0x6 +#define MSGBUF_TYPE_FLOW_RING_FLUSH 0x7 +#define MSGBUF_TYPE_FLOW_RING_FLUSH_CMPLT 0x8 +#define MSGBUF_TYPE_IOCTLPTR_REQ 0x9 +#define MSGBUF_TYPE_IOCTLPTR_REQ_ACK 0xA +#define MSGBUF_TYPE_IOCTLRESP_BUF_POST 0xB +#define MSGBUF_TYPE_IOCTL_CMPLT 0xC +#define MSGBUF_TYPE_EVENT_BUF_POST 0xD +#define MSGBUF_TYPE_WL_EVENT 0xE +#define MSGBUF_TYPE_TX_POST 0xF +#define MSGBUF_TYPE_TX_STATUS 0x10 +#define MSGBUF_TYPE_RXBUF_POST 0x11 +#define MSGBUF_TYPE_RX_CMPLT 0x12 +#define MSGBUF_TYPE_LPBK_DMAXFER 0x13 +#define MSGBUF_TYPE_LPBK_DMAXFER_CMPLT 0x14 + +struct msgbuf_common_hdr { + uint8_t msgtype; + uint8_t ifidx; + uint8_t flags; + uint8_t rsvd0; + uint32_t request_id; +}; + +struct msgbuf_buf_addr { + uint32_t low_addr; + uint32_t high_addr; +}; + +struct msgbuf_ioctl_req_hdr { + struct msgbuf_common_hdr msg; + uint32_t cmd; + uint16_t trans_id; + uint16_t input_buf_len; + uint16_t output_buf_len; + uint16_t rsvd0[3]; + struct msgbuf_buf_addr req_buf_addr; + uint32_t rsvd1[2]; +}; + +struct msgbuf_tx_msghdr { + struct msgbuf_common_hdr msg; + uint8_t txhdr[ETHER_HDR_LEN]; + uint8_t flags; +#define BWFM_MSGBUF_PKT_FLAGS_FRAME_802_3 (1 << 0) +#define BWFM_MSGBUF_PKT_FLAGS_PRIO_SHIFT 5 + uint8_t seg_cnt; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; + uint16_t metadata_buf_len; + uint16_t data_len; + uint32_t rsvd0; +}; + +struct msgbuf_rx_bufpost { + struct msgbuf_common_hdr msg; + uint16_t metadata_buf_len; + uint16_t data_buf_len; + uint32_t rsvd0; + struct msgbuf_buf_addr metadata_buf_addr; + struct msgbuf_buf_addr data_buf_addr; +}; + +struct msgbuf_rx_ioctl_resp_or_event { + struct msgbuf_common_hdr msg; + uint16_t host_buf_len; + uint16_t rsvd0[3]; + struct msgbuf_buf_addr host_buf_addr; + uint32_t rsvd1[4]; +}; + +struct msgbuf_completion_hdr { + uint16_t status; + uint16_t flow_ring_id; +}; + +struct msgbuf_rx_event { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t event_data_len; + uint16_t seqnum; + uint16_t rsvd0[4]; +}; + +struct msgbuf_ioctl_resp_hdr { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t resp_len; + uint16_t trans_id; + uint32_t cmd; + uint32_t rsvd0; +}; + +struct msgbuf_tx_status { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t metadata_len; + uint16_t tx_status; +}; + +struct msgbuf_rx_complete { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint16_t metadata_len; + uint16_t data_len; + uint16_t data_offset; + uint16_t flags; + uint32_t rx_status_0; + uint32_t rx_status_1; + uint32_t rsvd0; +}; + +struct msgbuf_tx_flowring_create_req { + struct msgbuf_common_hdr msg; + uint8_t da[ETHER_ADDR_LEN]; + uint8_t sa[ETHER_ADDR_LEN]; + uint8_t tid; + uint8_t if_flags; + uint16_t flow_ring_id; + uint8_t tc; + uint8_t priority; + uint16_t int_vector; + uint16_t max_items; + uint16_t len_item; + struct msgbuf_buf_addr flow_ring_addr; +}; + +struct msgbuf_tx_flowring_delete_req { + struct msgbuf_common_hdr msg; + uint16_t flow_ring_id; + uint16_t reason; + uint32_t rsvd0[7]; +}; + +struct msgbuf_flowring_create_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +}; + +struct msgbuf_flowring_delete_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +}; + +struct msgbuf_flowring_flush_resp { + struct msgbuf_common_hdr msg; + struct msgbuf_completion_hdr compl_hdr; + uint32_t rsvd0[3]; +}; -- 2.17.0
>From e3f220ed224933820d98c7a5bc296532f353565c Mon Sep 17 00:00:00 2001 From: coypu <[email protected]> Date: Fri, 4 May 2018 04:49:32 +0300 Subject: [PATCH 4/4] Update bwfm(4) to note PCI support and mention jmcneill. --- share/man/man4/bwfm.4 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/share/man/man4/bwfm.4 b/share/man/man4/bwfm.4 index c2ce635e20..6877ece7da 100644 --- a/share/man/man4/bwfm.4 +++ b/share/man/man4/bwfm.4 @@ -24,7 +24,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd October 26, 2017 +.Dd May 4, 2018 .Dt BWFM 4 .Os .Sh NAME @@ -32,6 +32,7 @@ .Nd Broadcom and Cypress wireless network driver .Sh SYNOPSIS .Cd "bwfm* at uhub? port ?" +.Cd "bwfm* at pci? dev ? function ?" .Sh DESCRIPTION The .Nm @@ -49,7 +50,10 @@ The .Nm driver was written by .An Patrick Wildt -.Aq Mt [email protected] . +.Aq Mt [email protected] +And +.An Jared D. Mcneill +.Aq Mt [email protected] . .Sh BUGS The firmware is outdated and contains known vulnerabilities. SDIO and PCI attachments are not supported yet. -- 2.17.0
Index: distrib/sets/lists/base/mi =================================================================== RCS file: /cvsroot/src/distrib/sets/lists/base/mi,v retrieving revision 1.1173 diff -u -r1.1173 mi --- distrib/sets/lists/base/mi 29 Mar 2018 04:19:54 -0000 1.1173 +++ distrib/sets/lists/base/mi 3 May 2018 15:53:20 -0000 @@ -145,6 +145,9 @@ ./libdata/firmware/if_bwfm/brcmfmac43236b.bin base-firmware-root ./libdata/firmware/if_bwfm/brcmfmac43242a.bin base-firmware-root ./libdata/firmware/if_bwfm/brcmfmac43569.bin base-firmware-root +./libdata/firmware/if_bwfm/brcmfmac4350-pcie.bin base-firmware-root +./libdata/firmware/if_bwfm/brcmfmac4350c2-pcie.bin base-firmware-root +./libdata/firmware/if_bwfm/brcmfmac43602-pcie.bin base-firmware-root ./libdata/firmware/if_ipw base-firmware-root ./libdata/firmware/if_ipw/LICENSE base-firmware-root ./libdata/firmware/if_ipw/ipw2100-1.2-i.fw base-firmware-root Index: external/broadcom/bwfm/Makefile =================================================================== RCS file: /cvsroot/src/external/broadcom/bwfm/Makefile,v retrieving revision 1.1 diff -u -r1.1 Makefile --- external/broadcom/bwfm/Makefile 21 Oct 2017 02:06:34 -0000 1.1 +++ external/broadcom/bwfm/Makefile 3 May 2018 15:53:21 -0000 @@ -7,6 +7,9 @@ FILES+= dist/brcmfmac43236b.bin FILES+= dist/brcmfmac43242a.bin FILES+= dist/brcmfmac43569.bin +FILES+= dist/brcmfmac4350-pcie.bin +FILES+= dist/brcmfmac4350c2-pcie.bin +FILES+= dist/brcmfmac43602-pcie.bin FILESDIR= /libdata/firmware/if_bwfm
