Module Name: src Committed By: martin Date: Sun Feb 2 14:30:00 UTC 2025
Modified Files: src/share/man/man4 [netbsd-10]: bwi.4 src/sys/arch/evbppc/conf [netbsd-10]: WII src/sys/dev/ic [netbsd-10]: bwi.c bwireg.h bwivar.h src/sys/dev/sdmmc [netbsd-10]: files.sdmmc sdmmc_cis.c sdmmcvar.h Added Files: src/sys/dev/sdmmc [netbsd-10]: if_bwi_sdio.c Log Message: Pull up following revision(s) (requested by jmcneill in ticket #1041): sys/dev/ic/bwi.c: revision 1.40 share/man/man4/bwi.4: revision 1.15 sys/dev/sdmmc/if_bwi_sdio.c: revision 1.1 sys/dev/ic/bwi.c: revision 1.41 sys/dev/sdmmc/sdmmcvar.h: revision 1.38 sys/dev/ic/bwireg.h: revision 1.5 sys/dev/ic/bwi.c: revision 1.39 sys/dev/ic/bwivar.h: revision 1.11 sys/dev/sdmmc/sdmmc_cis.c: revision 1.10 sys/dev/sdmmc/files.sdmmc: revision 1.6 sys/arch/evbppc/conf/WII: revision 1.7 bwi: Remove unnecessary pcivar.h include sdmmc: Capture lan_nid and expose sdmmc_cisptr LAN NID contains the MAC address for networking adapters. Device drivers may want to processor vendor specific tuple codes, so expose sdmmc_cisptr to help this. bwi(4): Add support for Nintendo Wii WLAN. Adapt the bwi(4) driver to support SDIO attachment and driving TX/RX using PIO instead of DMA since the latter is not supported on SDIO busses. fix uninitialized To generate a diff of this commit: cvs rdiff -u -r1.13 -r1.13.36.1 src/share/man/man4/bwi.4 cvs rdiff -u -r1.4.2.4 -r1.4.2.5 src/sys/arch/evbppc/conf/WII cvs rdiff -u -r1.38 -r1.38.10.1 src/sys/dev/ic/bwi.c cvs rdiff -u -r1.4 -r1.4.20.1 src/sys/dev/ic/bwireg.h cvs rdiff -u -r1.10 -r1.10.46.1 src/sys/dev/ic/bwivar.h cvs rdiff -u -r1.5 -r1.5.36.1 src/sys/dev/sdmmc/files.sdmmc cvs rdiff -u -r0 -r1.1.2.2 src/sys/dev/sdmmc/if_bwi_sdio.c cvs rdiff -u -r1.8.26.1 -r1.8.26.2 src/sys/dev/sdmmc/sdmmc_cis.c cvs rdiff -u -r1.36.18.1 -r1.36.18.2 src/sys/dev/sdmmc/sdmmcvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/share/man/man4/bwi.4 diff -u src/share/man/man4/bwi.4:1.13 src/share/man/man4/bwi.4:1.13.36.1 --- src/share/man/man4/bwi.4:1.13 Tue Mar 18 18:20:39 2014 +++ src/share/man/man4/bwi.4 Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -.\" $NetBSD: bwi.4,v 1.13 2014/03/18 18:20:39 riastradh Exp $ +.\" $NetBSD: bwi.4,v 1.13.36.1 2025/02/02 14:29:59 martin Exp $ .\" .\" Copyright (c) 2007 The DragonFly Project. All rights reserved. .\" @@ -31,7 +31,7 @@ .\" .\" $DragonFly: src/share/man/man4/bwi.4,v 1.10 2008/07/26 16:25:40 swildner Exp $ .\" -.Dd April 25, 2012 +.Dd January 18, 2025 .Dt BWI 4 .Os .Sh NAME @@ -40,6 +40,7 @@ .Sh SYNOPSIS .Cd "bwi* at pci? dev ? function ?" .Cd "bwi* at cardbus? function ?" +.Cd "bwi* at sdmmc?" .Sh DESCRIPTION The .Nm @@ -73,6 +74,7 @@ driver: .Pp .Bl -column -offset 6n -compact "Apple AirPort Extreme" "BCM4318" "Mini PCI" "b/g" .It Em "Card Chip Bus Standard" +.It Apple AirPort Extreme b/g .It Buffalo WLI-CB-G54 BCM4306 CardBus b/g .It Buffalo WLI3-CB-G54L BCM4318 CardBus b/g .It Buffalo WLI-PCI-G54S BCM4306 PCI b/g @@ -80,7 +82,7 @@ driver: .It Dell Wireless 1470 BCM4318 Mini PCI b/g .It Dell Truemobile 1400 BCM4309 Mini PCI b/g .It Dell Latitude D505 BCM4306 PCI b/g -.It Apple AirPort Extreme b/g +.It Nintendo Wii WLAN BCM4318 SDIO b/g .El .Sh FILES The firmware for the adapter is not shipped with Index: src/sys/arch/evbppc/conf/WII diff -u src/sys/arch/evbppc/conf/WII:1.4.2.4 src/sys/arch/evbppc/conf/WII:1.4.2.5 --- src/sys/arch/evbppc/conf/WII:1.4.2.4 Wed Oct 2 12:28:15 2024 +++ src/sys/arch/evbppc/conf/WII Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -# $NetBSD: WII,v 1.4.2.4 2024/10/02 12:28:15 martin Exp $ +# $NetBSD: WII,v 1.4.2.5 2025/02/02 14:29:59 martin Exp $ # # Nintendo Wii # @@ -155,6 +155,7 @@ sdhc0 at hollywood0 addr 0x0d070000 irq sdhc1 at hollywood0 addr 0x0d080000 irq 8 # SDIO/BT sdmmc* at sdmmcbus? ld* at sdmmc? +bwi* at sdmmc? # WLAN include "dev/usb/usbdevices.config" include "dev/bluetooth/bluetoothdevices.config" Index: src/sys/dev/ic/bwi.c diff -u src/sys/dev/ic/bwi.c:1.38 src/sys/dev/ic/bwi.c:1.38.10.1 --- src/sys/dev/ic/bwi.c:1.38 Wed Jun 16 00:21:18 2021 +++ src/sys/dev/ic/bwi.c Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: bwi.c,v 1.38 2021/06/16 00:21:18 riastradh Exp $ */ +/* $NetBSD: bwi.c,v 1.38.10.1 2025/02/02 14:29:59 martin Exp $ */ /* $OpenBSD: bwi.c,v 1.74 2008/02/25 21:13:30 mglocker Exp $ */ /* @@ -48,7 +48,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bwi.c,v 1.38 2021/06/16 00:21:18 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bwi.c,v 1.38.10.1 2025/02/02 14:29:59 martin Exp $"); #include <sys/param.h> #include <sys/callout.h> @@ -62,6 +62,10 @@ __KERNEL_RCSID(0, "$NetBSD: bwi.c,v 1.38 #include <sys/systm.h> #include <sys/bus.h> #include <sys/intr.h> +#include <sys/pool.h> +#include <sys/workqueue.h> +#include <sys/mutex.h> +#include <sys/kmem.h> #include <machine/endian.h> @@ -88,7 +92,7 @@ int bwi_debug = 0; #define DPRINTF(sc, dbg, fmt, ...) \ do { \ if ((sc)->sc_debug & (dbg)) \ - aprint_debug_dev((sc)->sc_dev, fmt, ##__VA_ARGS__); \ + device_printf((sc)->sc_dev, fmt, ##__VA_ARGS__); \ } while (0) #else /* !BWI_DEBUG */ @@ -99,7 +103,6 @@ do { \ /* XXX temporary porting goop */ #include <dev/pci/pcireg.h> -#include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> /* XXX does not belong here */ @@ -310,13 +313,18 @@ static void bwi_watchdog(struct ifnet * static void bwi_stop(struct ifnet *, int); static void bwi_newstate_begin(struct bwi_softc *, enum ieee80211_state); static int bwi_newstate(struct ieee80211com *, enum ieee80211_state, int); +static int bwi_newstate_sdio(struct ieee80211com *, enum ieee80211_state, + int); static int bwi_media_change(struct ifnet *); +static void bwi_task(struct work *, void *); /* [TRC: XXX amrr] */ static void bwi_iter_func(void *, struct ieee80211_node *); static void bwi_amrr_timeout(void *); static void bwi_newassoc(struct ieee80211_node *, int); static struct ieee80211_node * bwi_node_alloc(struct ieee80211_node_table *); +static int bwi_pio_alloc(struct bwi_softc *); +static void bwi_pio_free(struct bwi_softc *); static int bwi_dma_alloc(struct bwi_softc *); static void bwi_dma_free(struct bwi_softc *); static void bwi_ring_data_free(struct bwi_ring_data *, struct bwi_softc *); @@ -329,6 +337,13 @@ static int bwi_dma_mbuf_create(struct b static void bwi_dma_mbuf_destroy(struct bwi_softc *, int, int); static void bwi_enable_intrs(struct bwi_softc *, uint32_t); static void bwi_disable_intrs(struct bwi_softc *, uint32_t); +static int bwi_init_tx_ring_pio(struct bwi_softc *, int); +static int bwi_init_rx_ring_pio(struct bwi_softc *); +static int bwi_init_txstats_pio(struct bwi_softc *); +static void bwi_setup_rx_desc_pio(struct bwi_softc *, int, bus_addr_t, + int); +static void bwi_setup_tx_desc_pio(struct bwi_softc *, + struct bwi_ring_data *, int, bus_addr_t, int); static int bwi_init_tx_ring32(struct bwi_softc *, int); static void bwi_init_rxdesc_ring32(struct bwi_softc *, uint32_t, bus_addr_t, int, int); @@ -349,8 +364,12 @@ static void bwi_set_addr_filter(struct static int bwi_set_chan(struct bwi_softc *, struct ieee80211_channel *); static void bwi_next_scan(void *); static int bwi_rxeof(struct bwi_softc *, int); +static int bwi_rxeof_pio(struct bwi_softc *); static int bwi_rxeof32(struct bwi_softc *); static int bwi_rxeof64(struct bwi_softc *); +static void bwi_free_txstats_pio(struct bwi_softc *); +static void bwi_free_rx_ring_pio(struct bwi_softc *); +static void bwi_free_tx_ring_pio(struct bwi_softc *, int); static void bwi_reset_rx_ring32(struct bwi_softc *, uint32_t); static void bwi_free_txstats32(struct bwi_softc *); static void bwi_free_rx_ring32(struct bwi_softc *); @@ -370,8 +389,10 @@ static void bwi_ds_plcp_header(struct i static void bwi_plcp_header(void *, int, uint8_t); static int bwi_encap(struct bwi_softc *, int, struct mbuf *, struct ieee80211_node **, int); +static void bwi_start_tx_pio(struct bwi_softc *, uint32_t, int); static void bwi_start_tx32(struct bwi_softc *, uint32_t, int); static void bwi_start_tx64(struct bwi_softc *, uint32_t, int); +static void bwi_txeof_status_pio(struct bwi_softc *); static void bwi_txeof_status32(struct bwi_softc *); static void bwi_txeof_status64(struct bwi_softc *); static void _bwi_txeof(struct bwi_softc *, uint16_t); @@ -391,6 +412,8 @@ static void bwi_regwin_disable(struct b uint32_t); static void bwi_set_bssid(struct bwi_softc *, const uint8_t *); static void bwi_updateslot(struct ifnet *); +static void bwi_updateslot_sdio(struct ifnet *); +static void bwi_do_calibrate(struct bwi_softc *); static void bwi_calibrate(void *); static int bwi_calc_rssi(struct bwi_softc *, const struct bwi_rxbuf_hdr *); @@ -690,30 +713,13 @@ err: /* CODE */ -int -bwi_intr(void *arg) -{ - struct bwi_softc *sc = arg; - struct ifnet *ifp = &sc->sc_if; - - if (!device_is_active(sc->sc_dev) || - (ifp->if_flags & IFF_RUNNING) == 0) - return (0); - - /* Disable all interrupts */ - bwi_disable_intrs(sc, BWI_ALL_INTRS); - - softint_schedule(sc->sc_soft_ih); - return (1); -} - static void bwi_softintr(void *arg) { struct bwi_softc *sc = arg; struct bwi_mac *mac; struct ifnet *ifp = &sc->sc_if; - uint32_t intr_status; + uint32_t intr_status, intr_mask; uint32_t txrx_intr_status[BWI_TXRX_NRING]; int i, s, txrx_error, tx = 0, rx_data = -1; @@ -729,11 +735,19 @@ bwi_softintr(void *arg) if (intr_status == 0xffffffff) /* Not for us */ goto out; - intr_status &= CSR_READ_4(sc, BWI_MAC_INTR_MASK); - if (intr_status == 0) /* Nothing is interesting */ + if (BWI_IS_SDIO(sc)) { + intr_mask = 0xffffffff; + } else { + /* XXX FIXME */ + intr_mask = CSR_READ_4(sc, BWI_MAC_INTR_MASK); + } + DPRINTF(sc, BWI_DBG_INTR, + "intr status 0x%08x mask 0x%08x -> 0x%08x\n", + intr_status, intr_mask, intr_status & intr_mask); + intr_status &= intr_mask; + if (intr_status == 0) { /* Nothing is interesting */ goto out; - - DPRINTF(sc, BWI_DBG_INTR, "intr status 0x%08x\n", intr_status); + } KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC); mac = (struct bwi_mac *)sc->sc_cur_regwin; @@ -802,8 +816,9 @@ bwi_softintr(void *arg) if (intr_status & BWI_INTR_NOISE) aprint_normal_dev(sc->sc_dev, "intr noise\n"); - if (txrx_intr_status[0] & BWI_TXRX_INTR_RX) + if (txrx_intr_status[0] & BWI_TXRX_INTR_RX) { rx_data = (sc->sc_rxeof)(sc); + } if (txrx_intr_status[3] & BWI_TXRX_INTR_RX) { (sc->sc_txeof_status)(sc); @@ -815,7 +830,8 @@ bwi_softintr(void *arg) tx = 1; } - if (sc->sc_blink_led != NULL && sc->sc_led_blink) { + if (sc->sc_blink_led != NULL && sc->sc_led_blink && + !BWI_IS_SDIO(sc)) { int evt = BWI_LED_EVENT_NONE; if (tx && rx_data > 0) { @@ -842,6 +858,28 @@ out: } int +bwi_intr(void *arg) +{ + struct bwi_softc *sc = arg; + struct ifnet *ifp = &sc->sc_if; + + if (!device_is_active(sc->sc_dev) || + (ifp->if_flags & IFF_RUNNING) == 0) + return (0); + + /* Disable all interrupts */ + bwi_disable_intrs(sc, BWI_ALL_INTRS); + + if (BWI_IS_SDIO(sc)) { + bwi_softintr(sc); + } else { + softint_schedule(sc->sc_soft_ih); + } + + return (1); +} + +int bwi_attach(struct bwi_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; @@ -853,10 +891,25 @@ bwi_attach(struct bwi_softc *sc) /* [TRC: XXX Is this necessary?] */ s = splnet(); - sc->sc_soft_ih = softint_establish(SOFTINT_NET, bwi_softintr, sc); - if (sc->sc_soft_ih == NULL) { - error = ENXIO; - goto fail; + if (BWI_IS_SDIO(sc)) { + error = workqueue_create(&sc->sc_taskq, + device_xname(sc->sc_dev), bwi_task, sc, PRI_NONE, + IPL_NET, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "failed to create workqueue\n"); + goto fail; + } + sc->sc_freetask = pool_cache_init(sizeof(struct bwi_task), + 0, 0, 0, "bwitask", NULL, IPL_NET, NULL, NULL, NULL); + pool_cache_prime(sc->sc_freetask, BWI_TASK_COUNT); + } else { + sc->sc_soft_ih = softint_establish(SOFTINT_NET, bwi_softintr, + sc); + if (sc->sc_soft_ih == NULL) { + error = ENXIO; + goto fail; + } } /* @@ -945,7 +998,11 @@ bwi_attach(struct bwi_softc *sc) bwi_bbp_power_off(sc); - error = bwi_dma_alloc(sc); + if (BWI_IS_PIO(sc)) { + error = bwi_pio_alloc(sc); + } else { + error = bwi_dma_alloc(sc); + } if (error) goto fail; @@ -1019,7 +1076,8 @@ bwi_attach(struct bwi_softc *sc) ic->ic_state = IEEE80211_S_INIT; ic->ic_opmode = IEEE80211_M_STA; - ic->ic_updateslot = bwi_updateslot; + ic->ic_updateslot = BWI_IS_SDIO(sc) ? + bwi_updateslot_sdio : bwi_updateslot; if_initialize(ifp); ieee80211_ifattach(ic); @@ -1030,7 +1088,7 @@ bwi_attach(struct bwi_softc *sc) /* ic->ic_flags_ext |= IEEE80211_FEXT_SWBMISS; */ sc->sc_newstate = ic->ic_newstate; - ic->ic_newstate = bwi_newstate; + ic->ic_newstate = BWI_IS_SDIO(sc) ? bwi_newstate_sdio : bwi_newstate; /* [TRC: XXX amrr] */ ic->ic_newassoc = bwi_newassoc; ic->ic_node_alloc = bwi_node_alloc; @@ -1085,7 +1143,17 @@ bwi_detach(struct bwi_softc *sc) splx(s); - bwi_dma_free(sc); + if (BWI_IS_PIO(sc)) { + bwi_pio_free(sc); + if (sc->sc_taskq != NULL) { + workqueue_destroy(sc->sc_taskq); + } + if (sc->sc_freetask != NULL) { + pool_cache_destroy(sc->sc_freetask); + } + } else { + bwi_dma_free(sc); + } } /* MAC */ @@ -1251,6 +1319,12 @@ bwi_mac_lateattach(struct bwi_mac *mac) bwi_rf_off(mac); CSR_WRITE_2(mac->mac_sc, BWI_BBP_ATTEN, BWI_BBP_ATTEN_MAGIC); + + if (BWI_IS_PIO(mac->mac_sc)) { + /* Initialize RX padding data offset */ + MOBJ_WRITE_2(mac, BWI_COMM_MOBJ, BWI_COMM_MOBJ_RXPADOFF, 0); + } + bwi_regwin_disable(mac->mac_sc, &mac->mac_regwin, 0); return (0); @@ -1527,7 +1601,7 @@ bwi_mac_test(struct bwi_mac *mac) MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL1); val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0); if (val != TEST_VAL1) { - aprint_error_dev(sc->sc_dev, "TEST1 failed\n"); + aprint_error_dev(sc->sc_dev, "TEST1 failed [0x%08x]\n", val); return (ENXIO); } @@ -1535,7 +1609,7 @@ bwi_mac_test(struct bwi_mac *mac) MOBJ_WRITE_4(mac, BWI_COMM_MOBJ, 0, TEST_VAL2); val = MOBJ_READ_4(mac, BWI_COMM_MOBJ, 0); if (val != TEST_VAL2) { - aprint_error_dev(sc->sc_dev, "TEST2 failed\n"); + aprint_error_dev(sc->sc_dev, "TEST2 failed [0x%08x]\n", val); return (ENXIO); } @@ -2552,6 +2626,12 @@ bwi_mac_get_property(struct bwi_mac *mac * Byte swap */ val = CSR_READ_4(sc, BWI_MAC_STATUS); + if (BWI_IS_PIO(sc) && (val & BWI_MAC_STATUS_BSWAP)) { + DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "disable byte swap\n"); + val &= ~BWI_MAC_STATUS_BSWAP; + CSR_WRITE_4(sc, BWI_MAC_STATUS, val); + val = CSR_READ_4(sc, BWI_MAC_STATUS); + } if (val & BWI_MAC_STATUS_BSWAP) { DPRINTF(sc, BWI_DBG_MAC | BWI_DBG_ATTACH, "need byte swap\n"); mac->mac_flags |= BWI_MAC_F_BSWAP; @@ -6419,6 +6499,10 @@ bwi_power_on(struct bwi_softc *sc, int w DPRINTF(sc, BWI_DBG_MISC, "%s\n", __func__); + if (BWI_IS_SDIO(sc)) { + return; + } + gpio_in = (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_IN); if (gpio_in & BWI_PCIM_GPIO_PWR_ON) goto back; @@ -6460,6 +6544,10 @@ bwi_power_off(struct bwi_softc *sc, int DPRINTF(sc, BWI_DBG_MISC, "%s\n", __func__); + if (BWI_IS_SDIO(sc)) { + return (0); + } + (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_IN); /* dummy read */ gpio_out = (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_OUT); gpio_en = (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_ENABLE); @@ -6901,8 +6989,13 @@ bwi_bus_init(struct bwi_softc *sc, struc bus = &sc->sc_bus_regwin; KASSERT(sc->sc_cur_regwin == &mac->mac_regwin); + if (BWI_IS_SDIO(sc)) { + sc->sc_flags |= BWI_F_BUS_INITED; + return (0); + } + /* - * Tell bus to generate requested interrupts + * Tell bus to generate requested interrupts (PCI and Cardbus only). */ if (bus->rw_rev < 6 && bus->rw_type == BWI_REGWIN_T_BUSPCI) { /* @@ -7038,7 +7131,8 @@ bwi_get_clock_freq(struct bwi_softc *sc, src = -1; div = 0; if (com->rw_rev < 6) { - val = (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_OUT); + val = BWI_IS_SDIO(sc) ? + 0 : (sc->sc_conf_read)(sc, BWI_PCIR_GPIO_OUT); if (val & BWI_PCIM_GPIO_OUT_CLKSRC) { src = BWI_CLKSRC_PCI; div = 64; @@ -7250,12 +7344,12 @@ bwi_init_statechg(struct bwi_softc *sc, if (error) goto back; - /* Enable intrs */ - bwi_enable_intrs(sc, BWI_INIT_INTRS); - ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; + /* Enable intrs */ + bwi_enable_intrs(sc, BWI_INIT_INTRS); + if (statechg) { if (ic->ic_opmode != IEEE80211_M_MONITOR) { /* [TRC: XXX OpenBSD omits this conditional.] */ @@ -7274,6 +7368,8 @@ back: else /* [TRC: XXX DragonFlyBD uses ifp->if_start(ifp).] */ bwi_start(ifp); + + DPRINTF(sc, BWI_DBG_MISC, "%s done\n", __func__); } static int @@ -7503,6 +7599,24 @@ bwi_start(struct ifnet *ifp) if (trans) sc->sc_tx_timer = 5; ifp->if_timer = 1; + + if (BWI_IS_PIO(sc)) { + mutex_enter(&sc->sc_pio_txlock); + if (!STAILQ_EMPTY(&sc->sc_pio_txpend)) { + struct bwi_task *t; + + t = pool_cache_get(sc->sc_freetask, PR_NOWAIT); + if (t == NULL) { + device_printf(sc->sc_dev, "no free tasks\n"); + } else { + t->t_ic = &sc->sc_ic; + t->t_cmd = BWI_TASK_TX; + workqueue_enqueue(sc->sc_taskq, &t->t_work, + NULL); + } + } + mutex_exit(&sc->sc_pio_txlock); + } } static void @@ -7549,6 +7663,8 @@ bwi_stop(struct ifnet *ifp, int state_ch KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC); mac = (struct bwi_mac *)sc->sc_cur_regwin; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + bwi_disable_intrs(sc, BWI_ALL_INTRS); CSR_READ_4(sc, BWI_MAC_INTR_MASK); bwi_mac_stop(mac); @@ -7668,16 +7784,178 @@ back: } static int +bwi_newstate_sdio(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct bwi_softc *sc = ic->ic_ifp->if_softc; + struct bwi_task *t; + + t = pool_cache_get(sc->sc_freetask, PR_NOWAIT); + if (t == NULL) { + device_printf(sc->sc_dev, "no free tasks\n"); + return EIO; + } + + t->t_ic = ic; + t->t_cmd = BWI_TASK_NEWSTATE; + t->t_newstate.state = nstate; + t->t_newstate.arg = arg; + workqueue_enqueue(sc->sc_taskq, &t->t_work, NULL); + + return 0; +} + +static inline bool +bwi_tx_fifo_pkt_valid(struct bwi_softc *sc, u_int len) +{ + return len >= sizeof(uint32_t) && len <= sc->sc_pio_fifolen; +} + +static inline bool +bwi_tx_fifo_avail(struct bwi_softc *sc, u_int len) +{ + return len <= sc->sc_pio_fifoavail; +} + +static void +bwi_tx_frame_data_pio(struct bwi_softc *sc, u_int idx, uint32_t *ctl, + uint8_t *buf, u_int buflen) +{ + size_t count; + + *ctl |= BWI_PIO_TXCTL_VALID_BYTES(4); + CSR_WRITE_4(sc, BWI_PIO_TXCTL(idx), *ctl); + + count = buflen / sizeof(uint32_t); + CSR_WRITE_MULTI_4(sc, BWI_PIO_TXDATA(idx), (uint32_t *)buf, count); + buf += count * sizeof(uint32_t); + buflen -= count * sizeof(uint32_t); + if (buflen != 0) { + uint32_t data = 0; + + *ctl &= ~BWI_PIO_TXCTL_VALID; + *ctl |= BWI_PIO_TXCTL_VALID_BYTES(buflen); + CSR_WRITE_4(sc, BWI_PIO_TXCTL(idx), *ctl); + + memcpy(&data, buf, buflen); + CSR_WRITE_MULTI_4(sc, BWI_PIO_TXDATA(idx), &data, 1); + } +} + +static void +bwi_tx_frame_pio(struct bwi_softc *sc, struct bwi_txbuf *tb) +{ + struct mbuf *m = tb->tb_mbuf; + const u_int idx = BWI_TX_DATA_RING; + uint32_t ctl; + uint8_t *txbuf = sc->sc_pio_databuf; + u_int pktlen = m_length(m); + + m_copydata(m, 0, pktlen, txbuf); + + ctl = CSR_READ_4(sc, BWI_PIO_TXCTL(idx)); + ctl |= BWI_PIO_TXCTL_FREADY; + ctl &= ~BWI_PIO_TXCTL_EOF; + + bwi_tx_frame_data_pio(sc, idx, &ctl, txbuf, pktlen); + + ctl |= BWI_PIO_TXCTL_EOF; + CSR_WRITE_4(sc, BWI_PIO_TXCTL(idx), ctl); +} + +static void +bwi_tx_pending(struct bwi_softc *sc) +{ + struct ifnet *ifp = &sc->sc_if; + struct bwi_txbuf *tb; + + mutex_enter(&sc->sc_pio_txlock); + while ((tb = STAILQ_FIRST(&sc->sc_pio_txpend)) != NULL) { + const u_int pktlen = m_length(tb->tb_mbuf); + + if (!bwi_tx_fifo_pkt_valid(sc, pktlen)) { + device_printf(sc->sc_dev, + "dropping large packet (%u bytes)\n", pktlen); + + STAILQ_REMOVE_HEAD(&sc->sc_pio_txpend, tb_entry); + if_statinc(ifp, if_oerrors); + m_freem(tb->tb_mbuf); + tb->tb_mbuf = NULL; + continue; + } + + if (!bwi_tx_fifo_avail(sc, pktlen)) { + break; + } + + STAILQ_REMOVE_HEAD(&sc->sc_pio_txpend, tb_entry); + + sc->sc_pio_fifoavail -= roundup(pktlen, 4); + mutex_exit(&sc->sc_pio_txlock); + + bwi_tx_frame_pio(sc, tb); + + mutex_enter(&sc->sc_pio_txlock); + } + mutex_exit(&sc->sc_pio_txlock); +} + +static void +bwi_task(struct work *wk, void *arg) +{ + struct bwi_task *t = (struct bwi_task *)wk; + struct ieee80211com *ic = t->t_ic; + struct bwi_softc *sc = ic->ic_ifp->if_softc; + + switch (t->t_cmd) { + case BWI_TASK_NEWSTATE: + bwi_newstate(ic, t->t_newstate.state, t->t_newstate.arg); + break; + case BWI_TASK_UPDATESLOT: + bwi_updateslot(ic->ic_ifp); + break; + case BWI_TASK_TX: + bwi_tx_pending(sc); + break; + case BWI_TASK_INIT: + bwi_init(ic->ic_ifp); + break; + case BWI_TASK_CALIBRATE: + bwi_do_calibrate(sc); + break; + default: + panic("bwi: unknown task command %d", t->t_cmd); + } + + pool_cache_put(sc->sc_freetask, t); +} + +static int bwi_media_change(struct ifnet *ifp) { + struct bwi_softc *sc = ifp->if_softc; int error; error = ieee80211_media_change(ifp); if (error != ENETRESET) return (error); - if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) - bwi_init(ifp); + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) { + if (BWI_IS_SDIO(sc)) { + struct bwi_task *t; + + t = pool_cache_get(sc->sc_freetask, PR_NOWAIT); + if (t == NULL) { + device_printf(sc->sc_dev, "no free tasks\n"); + return (ENOBUFS); + } + + t->t_ic = &sc->sc_ic; + t->t_cmd = BWI_TASK_INIT; + workqueue_enqueue(sc->sc_taskq, &t->t_work, NULL); + } else { + bwi_init(ifp); + } + } return (0); } @@ -7742,6 +8020,64 @@ bwi_node_alloc(struct ieee80211_node_tab /* [TRC: XXX amrr end] */ static int +bwi_pio_alloc(struct bwi_softc *sc) +{ + struct bwi_mac *mac = &sc->sc_mac[0]; + int i, j, has_txstats; + + KASSERT(BWI_IS_PIO(sc)); + + if (mac->mac_rev < 8) { + aprint_error_dev(sc->sc_dev, + "driver does not support MAC rev %u in PIO mode\n", + mac->mac_rev); + return EINVAL; + } + + has_txstats = (mac->mac_flags & BWI_MAC_F_HAS_TXSTATS) != 0; + + sc->sc_init_tx_ring = bwi_init_tx_ring_pio; + sc->sc_free_tx_ring = bwi_free_tx_ring_pio; + sc->sc_init_rx_ring = bwi_init_rx_ring_pio; + sc->sc_free_rx_ring = bwi_free_rx_ring_pio; + sc->sc_setup_rxdesc = bwi_setup_rx_desc_pio; + sc->sc_setup_txdesc = bwi_setup_tx_desc_pio; + sc->sc_rxeof = bwi_rxeof_pio; + sc->sc_start_tx = bwi_start_tx_pio; + if (has_txstats) { + sc->sc_init_txstats = bwi_init_txstats_pio; + sc->sc_free_txstats = bwi_free_txstats_pio; + sc->sc_txeof_status = bwi_txeof_status_pio; + } + + mutex_init(&sc->sc_pio_txlock, MUTEX_DEFAULT, IPL_NET); + STAILQ_INIT(&sc->sc_pio_txpend); + + sc->sc_pio_fifolen = 2000 - 80; + sc->sc_pio_fifoavail = sc->sc_pio_fifolen; + + sc->sc_pio_databuf = kmem_alloc(sc->sc_pio_fifolen, KM_SLEEP); + + for (i = 0; i < BWI_TX_NRING; ++i) { + struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[i]; + for (j = 0; j < BWI_TX_NDESC; ++j) { + tbd->tbd_buf[j].tb_data = tbd; + } + } + + return 0; +} + +static void +bwi_pio_free(struct bwi_softc *sc) +{ + KASSERT(BWI_IS_PIO(sc)); + + kmem_free(sc->sc_pio_databuf, sc->sc_pio_fifolen); + mutex_destroy(&sc->sc_pio_txlock); +} + +static int bwi_dma_alloc(struct bwi_softc *sc) { int error, i, has_txstats; @@ -7750,6 +8086,8 @@ bwi_dma_alloc(struct bwi_softc *sc) bus_size_t tx_ring_sz, rx_ring_sz, desc_sz = 0; uint32_t txrx_ctrl_step = 0; + KASSERT(!BWI_IS_PIO(sc)); + has_txstats = 0; for (i = 0; i < sc->sc_nmac; ++i) { if (sc->sc_mac[i].mac_flags & BWI_MAC_F_HAS_TXSTATS) { @@ -7865,6 +8203,8 @@ bwi_dma_free(struct bwi_softc *sc) { int i; + KASSERT(!BWI_IS_PIO(sc)); + for (i = 0; i < BWI_TX_NRING; ++i) bwi_ring_data_free(&sc->sc_tx_rdata[i], sc); @@ -8140,16 +8480,64 @@ bwi_dma_mbuf_destroy(struct bwi_softc *s static void bwi_enable_intrs(struct bwi_softc *sc, uint32_t enable_intrs) { + DPRINTF(sc, BWI_DBG_INTR, "enable_intrs 0x%08x\n", enable_intrs); CSR_SETBITS_4(sc, BWI_MAC_INTR_MASK, enable_intrs); } static void bwi_disable_intrs(struct bwi_softc *sc, uint32_t disable_intrs) { + DPRINTF(sc, BWI_DBG_INTR, "disable_intrs 0x%08x\n", disable_intrs); CSR_CLRBITS_4(sc, BWI_MAC_INTR_MASK, disable_intrs); } static int +bwi_init_tx_ring_pio(struct bwi_softc *sc, int ring_idx) +{ + return (0); +} + +static int +bwi_init_rx_ring_pio(struct bwi_softc *sc) +{ + uint32_t ctrl_base = BWI_TXRX_CTRL_BASE; + uint32_t val; + int error; + + val = CSR_READ_4(sc, ctrl_base + BWI_RX32_CTRL); + val |= BWI_TXRX32_CTRL_ENABLE | + BWI_RX32_CTRL_DIRECT_FIFO; + CSR_WRITE_4(sc, ctrl_base + BWI_RX32_CTRL, val); + + error = bwi_newbuf(sc, 0, 1); + if (error) { + aprint_error_dev(sc->sc_dev, + "can't allocate RX buffer\n"); + return (error); + } + + return (0); +} + +static int +bwi_init_txstats_pio(struct bwi_softc *sc) +{ + return (0); +} + +static void +bwi_setup_rx_desc_pio(struct bwi_softc *sc, int buf_idx, bus_addr_t paddr, + int buf_len) +{ +} + +static void +bwi_setup_tx_desc_pio(struct bwi_softc *sc, struct bwi_ring_data *rd, + int buf_idx, bus_addr_t paddr, int buf_len) +{ +} + +static int bwi_init_tx_ring32(struct bwi_softc *sc, int ring_idx) { struct bwi_ring_data *rd; @@ -8320,7 +8708,7 @@ bwi_newbuf(struct bwi_softc *sc, int buf bus_dmamap_t map; bus_addr_t paddr; struct mbuf *m; - int error; + int error = 0; KASSERT(buf_idx < BWI_RX_NDESC); @@ -8343,35 +8731,39 @@ bwi_newbuf(struct bwi_softc *sc, int buf } m->m_len = m->m_pkthdr.len = MCLBYTES; - /* - * Try to load RX buf into temporary DMA map - */ - error = bus_dmamap_load_mbuf(sc->sc_dmat, rbd->rbd_tmp_dmap, m, - init ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); - if (error) { - m_freem(m); - + if (!BWI_IS_PIO(sc)) { /* - * See the comment above + * Try to load RX buf into temporary DMA map */ - if (init) - return error; - else - goto back; - } + error = bus_dmamap_load_mbuf(sc->sc_dmat, rbd->rbd_tmp_dmap, m, + init ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT); + if (error) { + m_freem(m); - if (!init) - bus_dmamap_unload(sc->sc_dmat, rxbuf->rb_dmap); + /* + * See the comment above + */ + if (init) + return error; + else + goto back; + } + + if (!init) + bus_dmamap_unload(sc->sc_dmat, rxbuf->rb_dmap); + } rxbuf->rb_mbuf = m; - /* - * Swap RX buf's DMA map with the loaded temporary one - */ - map = rxbuf->rb_dmap; - rxbuf->rb_dmap = rbd->rbd_tmp_dmap; - rbd->rbd_tmp_dmap = map; - paddr = rxbuf->rb_dmap->dm_segs[0].ds_addr; - rxbuf->rb_paddr = paddr; + if (!BWI_IS_PIO(sc)) { + /* + * Swap RX buf's DMA map with the loaded temporary one + */ + map = rxbuf->rb_dmap; + rxbuf->rb_dmap = rbd->rbd_tmp_dmap; + rbd->rbd_tmp_dmap = map; + paddr = rxbuf->rb_dmap->dm_segs[0].ds_addr; + rxbuf->rb_paddr = paddr; + } back: /* @@ -8379,8 +8771,10 @@ back: */ hdr = mtod(rxbuf->rb_mbuf, struct bwi_rxbuf_hdr *); memset(hdr, 0, sizeof(*hdr)); - bus_dmamap_sync(sc->sc_dmat, rxbuf->rb_dmap, 0, - rxbuf->rb_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + if (!BWI_IS_PIO(sc)) { + bus_dmamap_sync(sc->sc_dmat, rxbuf->rb_dmap, 0, + rxbuf->rb_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + } /* * Setup RX buf descriptor @@ -8558,6 +8952,184 @@ next: return (rx_data); } +static void +bwi_rx_frame_data_pio(struct bwi_softc *sc, struct bwi_rxbuf_hdr *hdr, int qid) +{ + struct bwi_rxbuf_data *rbd = &sc->sc_rx_bdata; + struct bwi_rxbuf *rb = &rbd->rbd_buf[0]; + struct ieee80211_frame_min *wh; + struct ieee80211_node *ni; + struct ifnet *ifp = &sc->sc_if; + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf *m; + const void *plcp; + uint16_t flags2; + int buflen, hdr_extra, rssi, type, rate; + int s; + + m = rb->rb_mbuf; + + if (bwi_newbuf(sc, 0, 0)) { + device_printf(sc->sc_dev, "couldn't create mbuf\n"); + if_statinc(ifp, if_ierrors); + return; + } + + flags2 = le16toh(hdr->rxh_flags2); + + hdr_extra = 0; + if (flags2 & BWI_RXH_F2_TYPE2FRAME) + hdr_extra = 2; + + buflen = le16toh(hdr->rxh_buflen); + + /* Read the packet data into the mbuf */ + CSR_READ_MULTI_4(sc, BWI_PIO_RXDATA(qid), mtod(m, uint32_t *), + buflen / sizeof(uint32_t)); + if (buflen & 0x3) { + uint8_t data[4]; + uint8_t *ppkt; + u_int resid; + u_int n; + + resid = buflen & 0x3; + ppkt = mtod(m, uint8_t *) + (buflen & ~0x3); + CSR_READ_MULTI_4(sc, BWI_PIO_RXDATA(qid), (uint32_t *)&data, 1); + for (n = 0; n < resid; n++, ppkt++) { + *ppkt = data[n]; + } + } + + plcp = mtod(m, uint8_t *) + hdr_extra; + rssi = bwi_calc_rssi(sc, hdr); + + m_set_rcvif(m, ifp); + m->m_len = m->m_pkthdr.len = buflen + hdr_extra; + m_adj(m, hdr_extra + 6); + + if (htole16(hdr->rxh_flags1) & BWI_RXH_F1_OFDM) + rate = bwi_ofdm_plcp2rate(plcp); + else + rate = bwi_ds_plcp2rate(plcp); + + s = splnet(); + + /* RX radio tap */ + if (sc->sc_drvbpf != NULL) { + struct mbuf mb; + struct bwi_rx_radiotap_hdr *tap = &sc->sc_rxtap; + + tap->wr_tsf = hdr->rxh_tsf; + tap->wr_flags = 0; + tap->wr_rate = rate; + tap->wr_chan_freq = + htole16(ic->ic_bss->ni_chan->ic_freq); + tap->wr_chan_flags = + htole16(ic->ic_bss->ni_chan->ic_flags); + tap->wr_antsignal = rssi; + tap->wr_antnoise = BWI_NOISE_FLOOR; + + mb.m_data = (void *)tap; + mb.m_len = sc->sc_rxtap_len; + mb.m_next = m; + mb.m_nextpkt = NULL; + mb.m_owner = NULL; + mb.m_type = 0; + mb.m_flags = 0; + bpf_mtap3(sc->sc_drvbpf, &mb, BPF_D_IN); + } + + m_adj(m, -IEEE80211_CRC_LEN); + + wh = mtod(m, struct ieee80211_frame_min *); + ni = ieee80211_find_rxnode(ic, wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + ieee80211_input(ic, m, ni, hdr->rxh_rssi, + le16toh(hdr->rxh_tsf)); + + ieee80211_free_node(ni); + + if (type == IEEE80211_FC0_TYPE_DATA) { + sc->sc_rx_rate = rate; + } + + splx(s); +} + +static int +bwi_rx_frame_pio(struct bwi_softc *sc) +{ + struct bwi_rxbuf_hdr rxh; + struct ifnet *ifp = &sc->sc_if; + const u_int qid = 0; + const size_t pio_hdrlen = 20; + uint32_t val; + uint16_t pktlen; + uint16_t flags2; + u_int n; + + /* Check for frame ready bit and acknowledge it */ + val = CSR_READ_4(sc, BWI_PIO_RXCTL(qid)); + if ((val & BWI_PIO_RXCTL_FRAMERDY) == 0) { + return 0; + } + CSR_WRITE_4(sc, BWI_PIO_RXCTL(qid), BWI_PIO_RXCTL_FRAMERDY); + + /* Wait for up to 100us for data ready */ + for (n = 10; n > 0; n--) { + val = CSR_READ_4(sc, BWI_PIO_RXCTL(qid)); + if ((val & BWI_PIO_RXCTL_DATARDY) != 0) { + break; + } + delay(10); + } + if (n == 0) { + device_printf(sc->sc_dev, "RX timeout\n"); + if_statinc(ifp, if_ierrors); + goto ack; + } + + /* Read 20 bytes of the packet RX header from the FIFO... */ + CSR_READ_MULTI_4(sc, BWI_PIO_RXDATA(qid), (uint32_t *)&rxh, + pio_hdrlen / sizeof(uint32_t)); + /* ... and zero the rest of it. */ + memset(((uint8_t *)&rxh) + pio_hdrlen, 0, sizeof(rxh) - pio_hdrlen); + + /* Validate packet */ + pktlen = le16toh(rxh.rxh_buflen); + if (pktlen > 0x700 || pktlen == 0) { + device_printf(sc->sc_dev, "RX error (length %#x)\n", pktlen); + if_statinc(ifp, if_ierrors); + goto ack; + } + flags2 = le16toh(rxh.rxh_flags2); + if ((flags2 & BWI_RXH_F2_INVALID) != 0) { + device_printf(sc->sc_dev, "RX frame invalid\n"); + if_statinc(ifp, if_ierrors); + goto ack; + } + + /* Process frame data */ + bwi_rx_frame_data_pio(sc, &rxh, qid); + +ack: + CSR_WRITE_4(sc, BWI_PIO_RXCTL(qid), BWI_PIO_RXCTL_DATARDY); + return 1; +} + +static int +bwi_rxeof_pio(struct bwi_softc *sc) +{ + unsigned npkt = 0; + + while (bwi_rx_frame_pio(sc) && npkt++ < 1000) { + preempt_point(); + } + + return (npkt != 0); +} + static int bwi_rxeof32(struct bwi_softc *sc) { @@ -8611,12 +9183,28 @@ bwi_reset_rx_ring32(struct bwi_softc *sc } static void +bwi_free_txstats_pio(struct bwi_softc *sc) +{ +} + +static void bwi_free_txstats32(struct bwi_softc *sc) { bwi_reset_rx_ring32(sc, sc->sc_txstats->stats_ctrl_base); } static void +bwi_free_rx_ring_pio(struct bwi_softc *sc) +{ +} + +static void +bwi_free_tx_ring_pio(struct bwi_softc *sc, int ring_idx) +{ + bwi_free_tx_ring32(sc, ring_idx); +} + +static void bwi_free_rx_ring32(struct bwi_softc *sc) { struct bwi_ring_data *rd = &sc->sc_rx_rdata; @@ -8685,7 +9273,9 @@ bwi_free_tx_ring32(struct bwi_softc *sc, struct bwi_txbuf *tb = &tbd->tbd_buf[i]; if (tb->tb_mbuf != NULL) { - bus_dmamap_unload(sc->sc_dmat, tb->tb_dmap); + if (!BWI_IS_PIO(sc)) { + bus_dmamap_unload(sc->sc_dmat, tb->tb_dmap); + } m_freem(tb->tb_mbuf); tb->tb_mbuf = NULL; } @@ -9165,18 +9755,24 @@ bwi_encap(struct bwi_softc *sc, int idx, hdr = NULL; wh = NULL; - /* DMA load */ - error = bus_dmamap_load_mbuf(sc->sc_dmat, tb->tb_dmap, m, - BUS_DMA_NOWAIT); - if (error && error != EFBIG) { - aprint_error_dev(sc->sc_dev, "can't load TX buffer (1) %d\n", - error); - goto back; + if (BWI_IS_PIO(sc)) { + error = 0; + } else { + /* DMA load */ + error = bus_dmamap_load_mbuf(sc->sc_dmat, tb->tb_dmap, m, + BUS_DMA_NOWAIT); + if (error && error != EFBIG) { + aprint_error_dev(sc->sc_dev, + "can't load TX buffer (1) %d\n", error); + goto back; + } } if (error) { /* error == EFBIG */ struct mbuf *m_new; + KASSERT(!BWI_IS_PIO(sc)); + error = 0; MGETHDR(m_new, M_DONTWAIT, MT_DATA); @@ -9217,8 +9813,10 @@ bwi_encap(struct bwi_softc *sc, int idx, } error = 0; - bus_dmamap_sync(sc->sc_dmat, tb->tb_dmap, 0, - tb->tb_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + if (!BWI_IS_PIO(sc)) { + bus_dmamap_sync(sc->sc_dmat, tb->tb_dmap, 0, + tb->tb_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + } if (mgt_pkt || mcast_pkt) { /* Don't involve mcast/mgt packets into TX rate control */ @@ -9245,11 +9843,13 @@ bwi_encap(struct bwi_softc *sc, int idx, DPRINTF(sc, BWI_DBG_TX, "idx %d, pkt_len %d, buflen %d\n", idx, pkt_len, m->m_pkthdr.len); - /* Setup TX descriptor */ - paddr = tb->tb_dmap->dm_segs[0].ds_addr; - (sc->sc_setup_txdesc)(sc, rd, idx, paddr, m->m_pkthdr.len); - bus_dmamap_sync(sc->sc_dmat, rd->rdata_dmap, 0, - rd->rdata_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + if (!BWI_IS_PIO(sc)) { + /* Setup TX descriptor */ + paddr = tb->tb_dmap->dm_segs[0].ds_addr; + (sc->sc_setup_txdesc)(sc, rd, idx, paddr, m->m_pkthdr.len); + bus_dmamap_sync(sc->sc_dmat, rd->rdata_dmap, 0, + rd->rdata_dmap->dm_mapsize, BUS_DMASYNC_PREWRITE); + } /* Kick start */ (sc->sc_start_tx)(sc, rd->rdata_txrx_ctrl, idx); @@ -9261,6 +9861,17 @@ back: } static void +bwi_start_tx_pio(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) +{ + struct bwi_txbuf_data *tbd = &sc->sc_tx_bdata[BWI_TX_DATA_RING]; + struct bwi_txbuf *tb = &tbd->tbd_buf[idx]; + + mutex_enter(&sc->sc_pio_txlock); + STAILQ_INSERT_TAIL(&sc->sc_pio_txpend, tb, tb_entry); + mutex_exit(&sc->sc_pio_txlock); +} + +static void bwi_start_tx32(struct bwi_softc *sc, uint32_t tx_ctrl, int idx) { idx = (idx + 1) % BWI_TX_NDESC; @@ -9275,6 +9886,12 @@ bwi_start_tx64(struct bwi_softc *sc, uin } static void +bwi_txeof_status_pio(struct bwi_softc *sc) +{ + /* TODO: PIO */ +} + +static void bwi_txeof_status32(struct bwi_softc *sc) { struct ifnet *ifp = &sc->sc_if; @@ -9331,7 +9948,15 @@ _bwi_txeof(struct bwi_softc *sc, uint16_ tb = &tbd->tbd_buf[buf_idx]; - bus_dmamap_unload(sc->sc_dmat, tb->tb_dmap); + if (BWI_IS_PIO(sc)) { + mutex_enter(&sc->sc_pio_txlock); + const u_int pktlen = m_length(tb->tb_mbuf); + sc->sc_pio_fifoavail += roundup(pktlen, sizeof(uint32_t)); + KASSERT(sc->sc_pio_fifoavail <= sc->sc_pio_fifolen); + mutex_exit(&sc->sc_pio_txlock); + } else { + bus_dmamap_unload(sc->sc_dmat, tb->tb_dmap); + } m_freem(tb->tb_mbuf); tb->tb_mbuf = NULL; @@ -9352,8 +9977,10 @@ bwi_txeof_status(struct bwi_softc *sc, i struct bwi_txstats_data *st = sc->sc_txstats; int idx; - bus_dmamap_sync(sc->sc_dmat, st->stats_dmap, 0, - st->stats_dmap->dm_mapsize, BUS_DMASYNC_POSTREAD); + if (!BWI_IS_PIO(sc)) { + bus_dmamap_sync(sc->sc_dmat, st->stats_dmap, 0, + st->stats_dmap->dm_mapsize, BUS_DMASYNC_POSTREAD); + } idx = st->stats_idx; while (idx != end_idx) { @@ -9693,6 +10320,41 @@ bwi_updateslot(struct ifnet *ifp) } static void +bwi_updateslot_sdio(struct ifnet *ifp) +{ + struct bwi_softc *sc = ifp->if_softc; + struct bwi_task *t; + + t = pool_cache_get(sc->sc_freetask, PR_NOWAIT); + if (t == NULL) { + device_printf(sc->sc_dev, "no free tasks\n"); + return; + } + + t->t_ic = &sc->sc_ic; + t->t_cmd = BWI_TASK_UPDATESLOT; + workqueue_enqueue(sc->sc_taskq, &t->t_work, NULL); +} + +static void +bwi_do_calibrate(struct bwi_softc *sc) +{ + struct bwi_mac *mac; + struct ieee80211com *ic = &sc->sc_ic; + + KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC); + mac = (struct bwi_mac *)sc->sc_cur_regwin; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + bwi_mac_calibrate_txpower(mac, sc->sc_txpwrcb_type); + sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; + } + + /* XXX 15 seconds */ + callout_schedule(&sc->sc_calib_ch, hz * 15); +} + +static void bwi_calibrate(void *xsc) { struct bwi_softc *sc = xsc; @@ -9702,18 +10364,22 @@ bwi_calibrate(void *xsc) s = splnet(); if (ic->ic_state == IEEE80211_S_RUN) { - struct bwi_mac *mac; - - KASSERT(sc->sc_cur_regwin->rw_type == BWI_REGWIN_T_MAC); - mac = (struct bwi_mac *)sc->sc_cur_regwin; + if (BWI_IS_SDIO(sc)) { + struct bwi_task *t; - if (ic->ic_opmode != IEEE80211_M_MONITOR) { - bwi_mac_calibrate_txpower(mac, sc->sc_txpwrcb_type); - sc->sc_txpwrcb_type = BWI_TXPWR_CALIB; + t = pool_cache_get(sc->sc_freetask, PR_NOWAIT); + if (t == NULL) { + device_printf(sc->sc_dev, "no free tasks\n"); + callout_schedule(&sc->sc_calib_ch, hz * 15); + } else { + t->t_ic = &sc->sc_ic; + t->t_cmd = BWI_TASK_CALIBRATE; + workqueue_enqueue(sc->sc_taskq, &t->t_work, + NULL); + } + } else { + bwi_do_calibrate(sc); } - - /* XXX 15 seconds */ - callout_schedule(&sc->sc_calib_ch, hz * 15); } splx(s); Index: src/sys/dev/ic/bwireg.h diff -u src/sys/dev/ic/bwireg.h:1.4 src/sys/dev/ic/bwireg.h:1.4.20.1 --- src/sys/dev/ic/bwireg.h:1.4 Mon May 18 05:47:54 2020 +++ src/sys/dev/ic/bwireg.h Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: bwireg.h,v 1.4 2020/05/18 05:47:54 msaitoh Exp $ */ +/* $NetBSD: bwireg.h,v 1.4.20.1 2025/02/02 14:29:59 martin Exp $ */ /* $OpenBSD: bwireg.h,v 1.7 2007/11/17 16:50:02 mglocker Exp $ */ /* @@ -150,6 +150,18 @@ #define BWI_TXRX_INTR_STATUS(i) (BWI_TXRX_INTR_STATUS_BASE + ((i) * 8)) #define BWI_TXRX_INTR_MASK(i) (BWI_TXRX_INTR_MASK_BASE + ((i) * 8)) +#define BWI_PIO_TXCTL(qid) (0x0000300 + (qid) * 0x10 + 0x0) +#define BWI_PIO_TXCTL_VALID (0xf << 0) +#define BWI_PIO_TXCTL_VALID_BYTES(n) ((1 << (n)) - 1) +#define BWI_PIO_TXCTL_EOF (1 << 4) +#define BWI_PIO_TXCTL_FREADY (1 << 7) +#define BWI_PIO_TXDATA(qid) (0x0000300 + (qid) * 0x10 + 0x4) + +#define BWI_PIO_RXCTL(qid) (0x0000300 + (qid) * 0x10 + 0x8) +#define BWI_PIO_RXCTL_FRAMERDY (1 << 0) +#define BWI_PIO_RXCTL_DATARDY (1 << 1) +#define BWI_PIO_RXDATA(qid) (0x0000300 + (qid) * 0x10 + 0xc) + #define BWI_MAC_STATUS 0x00000120 #define BWI_MAC_STATUS_ENABLE (1 << 0) #define BWI_MAC_STATUS_UCODE_START (1 << 1) @@ -196,6 +208,7 @@ #define BWI_COMM_MOBJ_SLOTTIME 0x10 #define BWI_COMM_MOBJ_MACREV 0x16 #define BWI_COMM_MOBJ_TX_ACK 0x22 +#define BWI_COMM_MOBJ_RXPADOFF 0x34 #define BWI_COMM_MOBJ_UCODE_STATE 0x40 #define BWI_COMM_MOBJ_SHRETRY_FB 0x44 #define BWI_COMM_MOBJ_LGRETEY_FB 0x46 @@ -252,6 +265,7 @@ #define BWI_TX32_STATUS_STATE_STOPPED 3 #define BWI_RX32_CTRL 0x10 #define BWI_RX32_CTRL_HDRSZ_MASK 0x00fe +#define BWI_RX32_CTRL_DIRECT_FIFO 0x0100 #define BWI_RX32_RINGINFO 0x14 #define BWI_RX32_INDEX 0x18 #define BWI_RX32_STATUS 0x1c Index: src/sys/dev/ic/bwivar.h diff -u src/sys/dev/ic/bwivar.h:1.10 src/sys/dev/ic/bwivar.h:1.10.46.1 --- src/sys/dev/ic/bwivar.h:1.10 Thu Feb 2 10:05:35 2017 +++ src/sys/dev/ic/bwivar.h Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: bwivar.h,v 1.10 2017/02/02 10:05:35 nonaka Exp $ */ +/* $NetBSD: bwivar.h,v 1.10.46.1 2025/02/02 14:29:59 martin Exp $ */ /* $OpenBSD: bwivar.h,v 1.23 2008/02/25 20:36:54 mglocker Exp $ */ /* @@ -78,14 +78,18 @@ enum bwi_txpwrcb_type { ((hdr) + sizeof(struct ieee80211_frame_ack) + IEEE80211_CRC_LEN) #define CSR_READ_4(sc, reg) \ - bus_space_read_4((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg)) + _bwi_read_4(sc, reg) #define CSR_READ_2(sc, reg) \ - bus_space_read_2((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg)) + _bwi_read_2(sc, reg) +#define CSR_READ_MULTI_4(sc, reg, datap, count) \ + _bwi_read_multi_4(sc, reg, datap, count) #define CSR_WRITE_4(sc, reg, val) \ - bus_space_write_4((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg), (val)) + _bwi_write_4(sc, reg, val) #define CSR_WRITE_2(sc, reg, val) \ - bus_space_write_2((sc)->sc_mem_bt, (sc)->sc_mem_bh, (reg), (val)) + _bwi_write_2(sc, reg, val) +#define CSR_WRITE_MULTI_4(sc, reg, datap, count) \ + _bwi_write_multi_4(sc, reg, datap, count) #define CSR_SETBITS_4(sc, reg, bits) \ CSR_WRITE_4((sc), (reg), CSR_READ_4((sc), (reg)) | (bits)) @@ -102,6 +106,9 @@ enum bwi_txpwrcb_type { #define CSR_CLRBITS_2(sc, reg, bits) \ CSR_WRITE_2((sc), (reg), CSR_READ_2((sc), (reg)) & ~(bits)) +struct pool_cache; +struct workqueue; + struct bwi_desc32 { /* Little endian */ uint32_t ctrl; @@ -145,6 +152,7 @@ struct bwi_rxbuf_hdr { #define BWI_RXH_F1_OFDM (1 << 0) #define BWI_RXH_F2_TYPE2FRAME (1 << 2) +#define BWI_RXH_F2_INVALID (1 << 0) #define BWI_RXH_F3_BCM2050_RSSI (1 << 10) @@ -207,12 +215,17 @@ struct bwi_ring_data { void *rdata_desc; }; +struct bwi_txbuf_data; + struct bwi_txbuf { struct mbuf *tb_mbuf; bus_dmamap_t tb_dmap; struct ieee80211_node *tb_ni; int tb_rate_idx[2]; + + struct bwi_txbuf_data *tb_data; + STAILQ_ENTRY(bwi_txbuf) tb_entry; }; struct bwi_txbuf_data { @@ -522,6 +535,28 @@ struct bwi_node { struct ieee80211_amrr_node amn; }; +enum bwi_task_cmd { + BWI_TASK_NEWSTATE, + BWI_TASK_UPDATESLOT, + BWI_TASK_TX, + BWI_TASK_INIT, + BWI_TASK_CALIBRATE, +}; + +#define BWI_TASK_COUNT 64 + +struct bwi_task { + struct work t_work; + struct ieee80211com *t_ic; + enum bwi_task_cmd t_cmd; + union { + struct { + enum ieee80211_state state; + int arg; + } t_newstate; + }; +}; + struct bwi_softc { device_t sc_dev; struct ethercom sc_ec; @@ -627,6 +662,24 @@ struct bwi_softc { void (*sc_conf_write)(void *, uint32_t, uint32_t); uint32_t (*sc_conf_read)(void *, uint32_t); + void (*sc_reg_write_2)(void *, uint32_t, uint16_t); + uint16_t (*sc_reg_read_2)(void *, uint32_t); + void (*sc_reg_write_4)(void *, uint32_t, uint32_t); + uint32_t (*sc_reg_read_4)(void *, uint32_t); + + void (*sc_reg_write_multi_4)(void *, uint32_t, + const uint32_t *, size_t); + void (*sc_reg_read_multi_4)(void *, uint32_t, + uint32_t *, size_t); + + struct pool_cache *sc_freetask; + struct workqueue *sc_taskq; + uint8_t *sc_pio_databuf; + kmutex_t sc_pio_txlock; + STAILQ_HEAD(, bwi_txbuf) sc_pio_txpend; + size_t sc_pio_fifolen; + size_t sc_pio_fifoavail; + struct sysctllog *sc_sysctllog; /* Sysctl variables */ @@ -654,8 +707,77 @@ struct bwi_softc { int sc_txtap_len; }; +static inline void +_bwi_read_multi_4(struct bwi_softc *sc, bus_size_t reg, uint32_t *datap, + bus_size_t count) +{ + if (sc->sc_reg_read_multi_4 != NULL) { + return sc->sc_reg_read_multi_4(sc, reg, datap, count); + } else { + return bus_space_read_multi_4(sc->sc_mem_bt, sc->sc_mem_bh, + reg, datap, count); + } +} + +static inline uint16_t +_bwi_read_2(struct bwi_softc *sc, bus_size_t reg) +{ + if (sc->sc_reg_read_2 != NULL) { + return sc->sc_reg_read_2(sc, reg); + } else { + return bus_space_read_2(sc->sc_mem_bt, sc->sc_mem_bh, reg); + } +} + +static inline uint32_t +_bwi_read_4(struct bwi_softc *sc, bus_size_t reg) +{ + if (sc->sc_reg_read_4 != NULL) { + return sc->sc_reg_read_4(sc, reg); + } else { + return bus_space_read_4(sc->sc_mem_bt, sc->sc_mem_bh, reg); + } +} + +static inline void +_bwi_write_multi_4(struct bwi_softc *sc, bus_size_t reg, const uint32_t *datap, + bus_size_t count) +{ + if (sc->sc_reg_read_multi_4 != NULL) { + return sc->sc_reg_write_multi_4(sc, reg, datap, count); + } else { + return bus_space_write_multi_4(sc->sc_mem_bt, sc->sc_mem_bh, + reg, datap, count); + } +} + +static inline void +_bwi_write_2(struct bwi_softc *sc, bus_size_t reg, uint16_t val) +{ + if (sc->sc_reg_write_2 != NULL) { + sc->sc_reg_write_2(sc, reg, val); + } else { + bus_space_write_2(sc->sc_mem_bt, sc->sc_mem_bh, reg, val); + } +} + +static inline void +_bwi_write_4(struct bwi_softc *sc, bus_size_t reg, uint32_t val) +{ + if (sc->sc_reg_write_4 != NULL) { + sc->sc_reg_write_4(sc, reg, val); + } else { + bus_space_write_4(sc->sc_mem_bt, sc->sc_mem_bh, reg, val); + } +} + #define BWI_F_BUS_INITED 0x1 #define BWI_F_PROMISC 0x2 +#define BWI_F_SDIO 0x4 +#define BWI_F_PIO 0x8 + +#define BWI_IS_SDIO(sc) ISSET((sc)->sc_flags, BWI_F_SDIO) +#define BWI_IS_PIO(sc) ISSET((sc)->sc_flags, BWI_F_PIO) #define BWI_DBG_MAC 0x00000001 #define BWI_DBG_RF 0x00000002 Index: src/sys/dev/sdmmc/files.sdmmc diff -u src/sys/dev/sdmmc/files.sdmmc:1.5 src/sys/dev/sdmmc/files.sdmmc:1.5.36.1 --- src/sys/dev/sdmmc/files.sdmmc:1.5 Tue Nov 7 16:30:32 2017 +++ src/sys/dev/sdmmc/files.sdmmc Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -# $NetBSD: files.sdmmc,v 1.5 2017/11/07 16:30:32 khorben Exp $ +# $NetBSD: files.sdmmc,v 1.5.36.1 2025/02/02 14:29:59 martin Exp $ # $OpenBSD: files.sdmmc,v 1.2 2006/06/01 21:53:41 uwe Exp $ # # Config file and device description for machine-independent SD/MMC code. @@ -25,3 +25,7 @@ file dev/sdmmc/sbt.c sbt # Broadcom FullMAC SDIO wireless adapter attach bwfm at sdmmc with bwfm_sdio file dev/sdmmc/if_bwfm_sdio.c bwfm_sdio + +# Broadcom SoftMAC SDIO wireless driver +attach bwi at sdmmc with bwi_sdio +file dev/sdmmc/if_bwi_sdio.c bwi_sdio Index: src/sys/dev/sdmmc/sdmmc_cis.c diff -u src/sys/dev/sdmmc/sdmmc_cis.c:1.8.26.1 src/sys/dev/sdmmc/sdmmc_cis.c:1.8.26.2 --- src/sys/dev/sdmmc/sdmmc_cis.c:1.8.26.1 Mon Oct 14 17:47:00 2024 +++ src/sys/dev/sdmmc/sdmmc_cis.c Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_cis.c,v 1.8.26.1 2024/10/14 17:47:00 martin Exp $ */ +/* $NetBSD: sdmmc_cis.c,v 1.8.26.2 2025/02/02 14:29:59 martin Exp $ */ /* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */ /* @@ -20,7 +20,7 @@ /* Routines to decode the Card Information Structure of SD I/O cards */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.8.26.1 2024/10/14 17:47:00 martin Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c,v 1.8.26.2 2025/02/02 14:29:59 martin Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -41,7 +41,6 @@ __KERNEL_RCSID(0, "$NetBSD: sdmmc_cis.c, #define DPRINTF(s) /**/ #endif -static uint32_t sdmmc_cisptr(struct sdmmc_function *); static void decode_funce_common(struct sdmmc_function *, struct sdmmc_cis *, int, uint32_t); static void decode_funce_function(struct sdmmc_function *, struct sdmmc_cis *, @@ -49,7 +48,7 @@ static void decode_funce_function(struct static void decode_vers_1(struct sdmmc_function *, struct sdmmc_cis *, int, uint32_t); -static uint32_t +uint32_t sdmmc_cisptr(struct sdmmc_function *sf) { uint32_t cisptr = 0; @@ -108,7 +107,6 @@ decode_funce_lan_nid(struct sdmmc_functi { struct sdmmc_function *sf0 = sf->sc->sc_fn0; device_t dev = sf->sc->sc_dev; - uint8_t mac[6] __unused; int i; if (tpllen != 8) { @@ -117,13 +115,19 @@ decode_funce_lan_nid(struct sdmmc_functi return; } + if (sdmmc_io_read_1(sf0, reg++) != 6) { + aprint_error_dev(dev, + "CISTPL_FUNCE(lan_nid) invalid\n"); + } + for (i = 0; i < 6; i++) { - mac[i] = sdmmc_io_read_1(sf0, reg++); + cis->lan_nid[i] = sdmmc_io_read_1(sf0, reg++); } DPRINTF( ("CISTPL_FUNCE: LAN_NID=%02x:%02x:%02x:%02x:%02x:%02x\n", - mac[0], mac[1], mac[2], mac[3], mac[4], mac[5])); + cis->lan_nid[0], cis->lan_nid[1], cis->lan_nid[2], + cis->lan_nid[3], cis->lan_nid[4], cis->lan_nid[5])); } static void Index: src/sys/dev/sdmmc/sdmmcvar.h diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.36.18.1 src/sys/dev/sdmmc/sdmmcvar.h:1.36.18.2 --- src/sys/dev/sdmmc/sdmmcvar.h:1.36.18.1 Sat Oct 26 15:26:43 2024 +++ src/sys/dev/sdmmc/sdmmcvar.h Sun Feb 2 14:29:59 2025 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcvar.h,v 1.36.18.1 2024/10/26 15:26:43 martin Exp $ */ +/* $NetBSD: sdmmcvar.h,v 1.36.18.2 2025/02/02 14:29:59 martin Exp $ */ /* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -187,6 +187,7 @@ struct sdmmc_cis { u_char cis1_minor; char cis1_info_buf[256]; char *cis1_info[4]; + uint8_t lan_nid[6]; }; /* @@ -393,6 +394,7 @@ int sdmmc_io_function_enable(struct sdmm void sdmmc_io_function_disable(struct sdmmc_function *); int sdmmc_io_function_abort(struct sdmmc_function *); +uint32_t sdmmc_cisptr(struct sdmmc_function *); int sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *); void sdmmc_print_cis(struct sdmmc_function *); void sdmmc_check_cis_quirks(struct sdmmc_function *); Added files: Index: src/sys/dev/sdmmc/if_bwi_sdio.c diff -u /dev/null src/sys/dev/sdmmc/if_bwi_sdio.c:1.1.2.2 --- /dev/null Sun Feb 2 14:30:00 2025 +++ src/sys/dev/sdmmc/if_bwi_sdio.c Sun Feb 2 14:29:59 2025 @@ -0,0 +1,453 @@ +/* $NetBSD: if_bwi_sdio.c,v 1.1.2.2 2025/02/02 14:29:59 martin Exp $ */ + +/*- + * Copyright (c) 2025 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(0, "$NetBSD: if_bwi_sdio.c,v 1.1.2.2 2025/02/02 14:29:59 martin Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/mutex.h> +#include <sys/systm.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_node.h> +#include <net80211/ieee80211_amrr.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_var.h> + +#include <dev/ic/bwireg.h> +#include <dev/ic/bwivar.h> + +#include <dev/pcmcia/pcmciareg.h> + +#include <dev/sdmmc/sdmmcdevs.h> +#include <dev/sdmmc/sdmmcvar.h> + +#define BWI_SDIO_FUNC1_SBADDRLOW 0x1000a +#define BWI_SDIO_FUNC1_SBADDRMID 0x1000b +#define BWI_SDIO_FUNC1_SBADDRHI 0x1000c + +#define BWI_CISTPL_VENDOR 0x80 +#define BWI_VENDOR_SROMREV 0 +#define BWI_VENDOR_ID 1 +#define BWI_VENDOR_BOARDREV 2 +#define BWI_VENDOR_PA 3 +#define BWI_VENDOR_OEMNAME 4 +#define BWI_VENDOR_CCODE 5 +#define BWI_VENDOR_ANTENNA 6 +#define BWI_VENDOR_ANTGAIN 7 +#define BWI_VENDOR_BFLAGS 8 +#define BWI_VENDOR_LEDS 9 + +#define BWI_SDIO_REG_OFFSET(ssc, reg) \ + ((reg) | ((ssc)->sc_sel_regwin & 0x7000)) + +#define BWI_SDIO_REG_32BIT_ACCESS 0x8000 + +static const struct bwi_sdio_product { + uint16_t vendor; + uint16_t product; +} bwi_sdio_products[] = { + { SDMMC_VENDOR_BROADCOM, SDMMC_PRODUCT_BROADCOM_NINTENDO_WII }, +}; + +struct bwi_sdio_sprom { + uint16_t pa_params[3]; + uint16_t board_vendor; + uint16_t card_flags; + uint8_t srom_rev; + uint8_t board_rev; + uint8_t idle_tssi; + uint8_t max_txpwr; + uint8_t country_code; + uint8_t ant_avail; + uint8_t ant_gain; + uint8_t gpio[4]; +}; + +struct bwi_sdio_softc { + struct bwi_softc sc_base; + + struct sdmmc_function *sc_sf; + struct bwi_sdio_sprom sc_sprom; + uint32_t sc_sel_regwin; + kmutex_t sc_lock; +}; + +static int bwi_sdio_match(device_t, cfdata_t, void *); +static void bwi_sdio_attach(device_t, device_t, void *); + +static void bwi_sdio_parse_cis(struct bwi_sdio_softc *); + +static int bwi_sdio_intr(void *); + +static void bwi_sdio_conf_write(void *, uint32_t, uint32_t); +static uint32_t bwi_sdio_conf_read(void *, uint32_t); +static void bwi_sdio_reg_write_2(void *, uint32_t, uint16_t); +static uint16_t bwi_sdio_reg_read_2(void *, uint32_t); +static void bwi_sdio_reg_write_4(void *, uint32_t, uint32_t); +static uint32_t bwi_sdio_reg_read_4(void *, uint32_t); +static void bwi_sdio_reg_write_multi_4(void *, uint32_t, const uint32_t *, + size_t); +static void bwi_sdio_reg_read_multi_4(void *, uint32_t, uint32_t *, + size_t); + +CFATTACH_DECL_NEW(bwi_sdio, sizeof(struct bwi_sdio_softc), + bwi_sdio_match, bwi_sdio_attach, NULL, NULL); + +static int +bwi_sdio_match(device_t parent, cfdata_t cf, void *aux) +{ + struct sdmmc_attach_args * const saa = aux; + struct sdmmc_function *sf = saa->sf; + struct sdmmc_cis *cis; + u_int n; + + if (sf == NULL) { + return 0; + } + cis = &sf->sc->sc_fn0->cis; + + for (n = 0; n < __arraycount(bwi_sdio_products); n++) { + const struct bwi_sdio_product *bsp = &bwi_sdio_products[n]; + + if (bsp->vendor == cis->manufacturer && + bsp->product == cis->product) { + return 1; + } + } + + return 0; +} + +static void +bwi_sdio_attach(device_t parent, device_t self, void *aux) +{ + struct bwi_sdio_softc * const ssc = device_private(self); + struct bwi_softc * const sc = &ssc->sc_base; + struct sdmmc_attach_args * const saa = aux; + struct sdmmc_function *sf = saa->sf; + struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis; + int error; + void *ih; + + aprint_naive("\n"); + aprint_normal(": Broadcom Wireless\n"); + + sc->sc_dev = self; + sc->sc_flags = BWI_F_SDIO | BWI_F_PIO; + sc->sc_conf_write = bwi_sdio_conf_write; + sc->sc_conf_read = bwi_sdio_conf_read; + sc->sc_reg_write_multi_4 = bwi_sdio_reg_write_multi_4; + sc->sc_reg_read_multi_4 = bwi_sdio_reg_read_multi_4; + sc->sc_reg_write_2 = bwi_sdio_reg_write_2; + sc->sc_reg_read_2 = bwi_sdio_reg_read_2; + sc->sc_reg_write_4 = bwi_sdio_reg_write_4; + sc->sc_reg_read_4 = bwi_sdio_reg_read_4; + sc->sc_pci_revid = 0; /* XXX can this come from CIS? */ + sc->sc_pci_did = cis->product; + sc->sc_pci_subvid = cis->manufacturer; + sc->sc_pci_subdid = cis->product; + + ssc->sc_sf = sf; + mutex_init(&ssc->sc_lock, MUTEX_DEFAULT, IPL_NONE); + + sdmmc_io_set_blocklen(ssc->sc_sf, 64); + if (sdmmc_io_function_enable(ssc->sc_sf) != 0) { + aprint_error_dev(self, "couldn't enable function\n"); + return; + } + + bwi_sdio_parse_cis(ssc); + + ih = sdmmc_intr_establish(parent, bwi_sdio_intr, ssc, + device_xname(self)); + if (ih == NULL) { + aprint_error_dev(self, "couldn't establish interrupt\n"); + return; + } + + error = bwi_attach(sc); + if (error != 0) { + sdmmc_intr_disestablish(ih); + return; + } + + sdmmc_intr_enable(ssc->sc_sf); +} + +static void +bwi_sdio_parse_cis(struct bwi_sdio_softc *ssc) +{ + struct sdmmc_function *sf0 = ssc->sc_sf->sc->sc_fn0; + struct bwi_sdio_sprom *sprom = &ssc->sc_sprom; + uint32_t reg; + uint8_t tplcode, tpllen; + + reg = sdmmc_cisptr(ssc->sc_sf); + for (;;) { + tplcode = sdmmc_io_read_1(sf0, reg++); + if (tplcode == PCMCIA_CISTPL_NULL) { + continue; + } + tpllen = sdmmc_io_read_1(sf0, reg++); + if (tplcode == PCMCIA_CISTPL_END || tpllen == 0) { + break; + } + if (tplcode != BWI_CISTPL_VENDOR) { + reg += tpllen; + continue; + } + + switch (sdmmc_io_read_1(sf0, reg)) { + case BWI_VENDOR_SROMREV: + sprom->srom_rev = sdmmc_io_read_1(sf0, reg + 1); + break; + case BWI_VENDOR_ID: + sprom->board_vendor = + sdmmc_io_read_1(sf0, reg + 1) | + ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); + break; + case BWI_VENDOR_BOARDREV: + sprom->board_rev = + sdmmc_io_read_1(sf0, reg + 1); + break; + case BWI_VENDOR_PA: + sprom->pa_params[0] = + sdmmc_io_read_1(sf0, reg + 1) | + ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); + sprom->pa_params[1] = + sdmmc_io_read_1(sf0, reg + 3) | + ((uint16_t)sdmmc_io_read_1(sf0, reg + 4) << 8); + sprom->pa_params[2] = + sdmmc_io_read_1(sf0, reg + 5) | + ((uint16_t)sdmmc_io_read_1(sf0, reg + 6) << 8); + sprom->idle_tssi = + sdmmc_io_read_1(sf0, reg + 7); + sprom->max_txpwr = + sdmmc_io_read_1(sf0, reg + 8); + break; + case BWI_VENDOR_CCODE: + sprom->country_code = + sdmmc_io_read_1(sf0, reg + 1); + break; + case BWI_VENDOR_ANTGAIN: + sprom->ant_gain = sdmmc_io_read_1(sf0, reg + 1); + break; + case BWI_VENDOR_BFLAGS: + sprom->card_flags = + sdmmc_io_read_1(sf0, reg + 1) | + ((uint16_t)sdmmc_io_read_1(sf0, reg + 2) << 8); + break; + case BWI_VENDOR_LEDS: + sprom->gpio[0] = sdmmc_io_read_1(sf0, reg + 1); + sprom->gpio[1] = sdmmc_io_read_1(sf0, reg + 2); + sprom->gpio[2] = sdmmc_io_read_1(sf0, reg + 3); + sprom->gpio[3] = sdmmc_io_read_1(sf0, reg + 4); + break; + } + + reg += tpllen; + } +} + +static int +bwi_sdio_intr(void *priv) +{ + struct bwi_sdio_softc * const ssc = priv; + + bwi_intr(&ssc->sc_base); + + return 1; +} + +static void +bwi_sdio_conf_write(void *priv, uint32_t reg, uint32_t val) +{ + struct bwi_sdio_softc * const ssc = priv; + + KASSERT(reg == BWI_PCIR_SEL_REGWIN); + + mutex_enter(&ssc->sc_lock); + if (reg == BWI_PCIR_SEL_REGWIN && ssc->sc_sel_regwin != val) { + sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRLOW, + (val >> 8) & 0x80); + sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRMID, + (val >> 16) & 0xff); + sdmmc_io_write_1(ssc->sc_sf, BWI_SDIO_FUNC1_SBADDRHI, + (val >> 24) & 0xff); + ssc->sc_sel_regwin = val; + } + mutex_exit(&ssc->sc_lock); +} + +static uint32_t +bwi_sdio_conf_read(void *priv, uint32_t reg) +{ + struct bwi_sdio_softc * const ssc = priv; + + KASSERT(reg == BWI_PCIR_SEL_REGWIN); + + if (reg == BWI_PCIR_SEL_REGWIN) { + return ssc->sc_sel_regwin; + } else { + return 0; + } +} + +static void +bwi_sdio_reg_write_multi_4(void *priv, uint32_t reg, const uint32_t *datap, + size_t count) +{ + struct bwi_sdio_softc * const ssc = priv; + + mutex_enter(&ssc->sc_lock); + sdmmc_io_write_multi_1(ssc->sc_sf, + BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, + (uint8_t *)__UNCONST(datap), count * sizeof(uint32_t)); + mutex_exit(&ssc->sc_lock); +} + +static void +bwi_sdio_reg_read_multi_4(void *priv, uint32_t reg, uint32_t *datap, + size_t count) +{ + struct bwi_sdio_softc * const ssc = priv; + + mutex_enter(&ssc->sc_lock); + sdmmc_io_read_multi_1(ssc->sc_sf, + BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, + (uint8_t *)datap, count * sizeof(uint32_t)); + mutex_exit(&ssc->sc_lock); +} + +static void +bwi_sdio_reg_write_2(void *priv, uint32_t reg, uint16_t val) +{ + struct bwi_sdio_softc * const ssc = priv; + + val = htole16(val); + + mutex_enter(&ssc->sc_lock); + sdmmc_io_write_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg), val); + mutex_exit(&ssc->sc_lock); +} + +static uint16_t +bwi_sdio_reg_read_sprom(struct bwi_sdio_softc *ssc, uint32_t reg) +{ + struct bwi_sdio_sprom *sprom = &ssc->sc_sprom; + struct sdmmc_cis *cis = &ssc->sc_sf->cis; + + switch (reg) { + case BWI_SPROM_11BG_EADDR ... BWI_SPROM_11BG_EADDR + 4: + return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11BG_EADDR]; + case BWI_SPROM_11A_EADDR ... BWI_SPROM_11A_EADDR + 4: + return *(uint16_t *)&cis->lan_nid[reg - BWI_SPROM_11A_EADDR]; + case BWI_SPROM_CARD_INFO: + return (uint16_t)sprom->country_code << 8; + case BWI_SPROM_PA_PARAM_11BG ... BWI_SPROM_PA_PARAM_11BG + 4: + return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11BG) / 2]; + case BWI_SPROM_PA_PARAM_11A ... BWI_SPROM_PA_PARAM_11A + 4: + return sprom->pa_params[(reg - BWI_SPROM_PA_PARAM_11A) / 2]; + case BWI_SPROM_GPIO01: + return sprom->gpio[0] | ((uint16_t)sprom->gpio[1] << 8); + case BWI_SPROM_GPIO23: + return sprom->gpio[2] | ((uint16_t)sprom->gpio[3] << 8); + case BWI_SPROM_MAX_TXPWR: + return sprom->max_txpwr | ((uint16_t)sprom->max_txpwr << 8); + case BWI_SPROM_IDLE_TSSI: + return sprom->idle_tssi | ((uint16_t)sprom->idle_tssi << 8); + case BWI_SPROM_CARD_FLAGS: + return sprom->card_flags; + case BWI_SPROM_ANT_GAIN: + return sprom->ant_gain | ((uint16_t)sprom->ant_gain << 8); + default: + return 0xffff; + } +} + +static uint16_t +bwi_sdio_reg_read_2(void *priv, uint32_t reg) +{ + struct bwi_sdio_softc * const ssc = priv; + uint16_t val; + + /* Emulate SPROM reads */ + if (reg >= BWI_SPROM_START && + reg <= BWI_SPROM_START + BWI_SPROM_ANT_GAIN) { + return bwi_sdio_reg_read_sprom(ssc, reg - BWI_SPROM_START); + } + + mutex_enter(&ssc->sc_lock); + val = sdmmc_io_read_2(ssc->sc_sf, BWI_SDIO_REG_OFFSET(ssc, reg)); + mutex_exit(&ssc->sc_lock); + + val = le16toh(val); + + return val; +} + +static void +bwi_sdio_reg_write_4(void *priv, uint32_t reg, uint32_t val) +{ + struct bwi_sdio_softc * const ssc = priv; + + val = htole32(val); + + mutex_enter(&ssc->sc_lock); + sdmmc_io_write_4(ssc->sc_sf, + BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS, val); + /* SDIO cards require a read after a 32-bit write */ + sdmmc_io_read_4(ssc->sc_sf, 0); + mutex_exit(&ssc->sc_lock); +} + +static uint32_t +bwi_sdio_reg_read_4(void *priv, uint32_t reg) +{ + struct bwi_sdio_softc * const ssc = priv; + uint32_t val; + + mutex_enter(&ssc->sc_lock); + val = sdmmc_io_read_4(ssc->sc_sf, + BWI_SDIO_REG_OFFSET(ssc, reg) | BWI_SDIO_REG_32BIT_ACCESS); + mutex_exit(&ssc->sc_lock); + + val = le32toh(val); + + return val; +}