diff options
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/dev/cardbus/if_ral_cardbus.c | 42 | ||||
-rw-r--r-- | sys/dev/ic/rt2860.c | 2697 | ||||
-rw-r--r-- | sys/dev/ic/rt2860reg.h | 869 | ||||
-rw-r--r-- | sys/dev/ic/rt2860var.h | 130 | ||||
-rw-r--r-- | sys/dev/pci/if_ral_pci.c | 48 |
6 files changed, 3766 insertions, 23 deletions
diff --git a/sys/conf/files b/sys/conf/files index 8dab37929c4..5c65b4ee3bf 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.413 2007/10/18 17:39:54 miod Exp $ +# $OpenBSD: files,v 1.414 2007/11/15 21:15:34 damien Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -369,6 +369,7 @@ file dev/ic/rtw.c rtw device ral: ether, ifnet, ifmedia, firmload, wlan file dev/ic/rt2560.c ral file dev/ic/rt2661.c ral +file dev/ic/rt2860.c ral # Texas Instruments ACX 802.11 device acx: ether, ifnet, ifmedia, firmload, wlan diff --git a/sys/dev/cardbus/if_ral_cardbus.c b/sys/dev/cardbus/if_ral_cardbus.c index 08fe03a4e9d..712102fea70 100644 --- a/sys/dev/cardbus/if_ral_cardbus.c +++ b/sys/dev/cardbus/if_ral_cardbus.c @@ -1,7 +1,7 @@ -/* $OpenBSD: if_ral_cardbus.c,v 1.9 2006/10/22 12:14:44 damien Exp $ */ +/* $OpenBSD: if_ral_cardbus.c,v 1.10 2007/11/15 21:15:34 damien Exp $ */ /*- - * Copyright (c) 2005, 2006 + * Copyright (c) 2005-2007 * Damien Bergamini <damien.bergamini@free.fr> * * Permission to use, copy, modify, and distribute this software for any @@ -18,7 +18,7 @@ */ /* - * CardBus front-end for the Ralink RT2560/RT2561/RT2561S/RT2661 driver. + * CardBus front-end for the Ralink RT2560/RT2561/RT2661/RT2860 driver. */ #include "bpfilter.h" @@ -49,6 +49,7 @@ #include <dev/ic/rt2560var.h> #include <dev/ic/rt2661var.h> +#include <dev/ic/rt2860var.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -70,12 +71,18 @@ static struct ral_opns { rt2661_attach, rt2661_detach, rt2661_intr + +}, ral_rt2860_opns = { + rt2860_attach, + rt2860_detach, + rt2860_intr }; struct ral_cardbus_softc { union { struct rt2560_softc sc_rt2560; struct rt2661_softc sc_rt2661; + struct rt2860_softc sc_rt2860; } u; #define sc_sc u.sc_rt2560 @@ -99,10 +106,13 @@ struct cfattach ral_cardbus_ca = { }; static const struct cardbus_matchid ral_cardbus_devices[] = { - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2560 }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561 }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561S }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2661 } + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2560 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561S }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2661 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_1 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_2 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_3 } }; int ral_cardbus_enable(struct rt2560_softc *); @@ -128,9 +138,21 @@ ral_cardbus_attach(struct device *parent, struct device *self, void *aux) bus_addr_t base; int error; - csc->sc_opns = - (CARDBUS_PRODUCT(ca->ca_id) == PCI_PRODUCT_RALINK_RT2560) ? - &ral_rt2560_opns : &ral_rt2661_opns; + switch (CARDBUS_PRODUCT(ca->ca_id)) { + case PCI_PRODUCT_RALINK_RT2560: + csc->sc_opns = &ral_rt2560_opns; + break; + case PCI_PRODUCT_RALINK_RT2561: + case PCI_PRODUCT_RALINK_RT2561S: + case PCI_PRODUCT_RALINK_RT2661: + csc->sc_opns = &ral_rt2661_opns; + break; + case PCI_PRODUCT_RALINK_RT2860_1: + case PCI_PRODUCT_RALINK_RT2860_2: + case PCI_PRODUCT_RALINK_RT2860_3: + csc->sc_opns = &ral_rt2860_opns; + break; + } sc->sc_dmat = ca->ca_dmat; csc->sc_ct = ct; diff --git a/sys/dev/ic/rt2860.c b/sys/dev/ic/rt2860.c new file mode 100644 index 00000000000..42245a7be3a --- /dev/null +++ b/sys/dev/ic/rt2860.c @@ -0,0 +1,2697 @@ +/* $OpenBSD: rt2860.c,v 1.1 2007/11/15 21:15:34 damien Exp $ */ + +/*- + * Copyright (c) 2007 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and 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. + */ + +/*- + * Ralink Technology RT2860 chipset driver + * http://www.ralinktech.com/ + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/mbuf.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/timeout.h> +#include <sys/conf.h> +#include <sys/device.h> + +#include <machine/bus.h> +#include <machine/endian.h> +#include <machine/intr.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_amrr.h> +#include <net80211/ieee80211_radiotap.h> + +#include <dev/ic/rt2860reg.h> +#include <dev/ic/rt2860var.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#define RAL_DEBUG + +#ifdef RAL_DEBUG +#define DPRINTF(x) do { if (rt2860_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (rt2860_debug >= (n)) printf x; } while (0) +int rt2860_debug = 1; +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +int rt2860_alloc_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +void rt2860_reset_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +void rt2860_free_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +int rt2860_alloc_tx_pool(struct rt2860_softc *); +void rt2860_free_tx_pool(struct rt2860_softc *); +int rt2860_alloc_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +void rt2860_reset_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +void rt2860_free_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +int rt2860_media_change(struct ifnet *); +void rt2860_next_scan(void *); +void rt2860_iter_func(void *, struct ieee80211_node *); +void rt2860_updatestats(void *); +void rt2860_newassoc(struct ieee80211com *, struct ieee80211_node *, + int); +int rt2860_newstate(struct ieee80211com *, enum ieee80211_state, + int); +uint16_t rt2860_eeprom_read(struct rt2860_softc *, uint8_t); +void rt2860_drain_stats_fifo(struct rt2860_softc *); +void rt2860_tx_intr(struct rt2860_softc *, int); +void rt2860_rx_intr(struct rt2860_softc *); +int rt2860_ack_rate(struct ieee80211com *, int); +uint16_t rt2860_txtime(int, int, uint32_t); +uint8_t rt2860_rate2mcs(struct rt2860_softc *, uint8_t); +int rt2860_tx_data(struct rt2860_softc *, struct mbuf *, + struct ieee80211_node *, int); +void rt2860_start(struct ifnet *); +void rt2860_watchdog(struct ifnet *); +int rt2860_ioctl(struct ifnet *, u_long, caddr_t); +void rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t); +uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t); +void rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t); +int rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t); +void rt2860_enable_mrr(struct rt2860_softc *); +void rt2860_set_txpreamble(struct rt2860_softc *); +void rt2860_set_basicrates(struct rt2860_softc *); +void rt2860_select_chan_group(struct rt2860_softc *, int); +void rt2860_set_chan(struct rt2860_softc *, + struct ieee80211_channel *); +void rt2860_set_bssid(struct rt2860_softc *, const uint8_t *); +void rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *); +void rt2860_updateslot(struct ieee80211com *); +void rt2860_updateedca(struct ieee80211com *); +int8_t rt2860_rssi2dbm(struct rt2860_softc *, uint8_t, uint8_t); +uint8_t rt2860_maxrssi_chain(struct rt2860_softc *, + const struct rt2860_rxwi *); +const char * rt2860_get_rf(uint8_t); +int rt2860_read_eeprom(struct rt2860_softc *); +int rt2860_bbp_init(struct rt2860_softc *); +int rt2860_init(struct ifnet *); +void rt2860_stop(struct ifnet *, int); +int rt2860_load_microcode(struct rt2860_softc *); +void rt2860_calib(struct rt2860_softc *); +int rt2860_setup_beacon(struct rt2860_softc *); +void rt2860_enable_tsf_sync(struct rt2860_softc *); + +static const struct { + uint32_t reg; + uint32_t val; +} rt2860_def_mac[] = { + RT2860_DEF_MAC +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt2860_def_bbp[] = { + RT2860_DEF_BBP +}; + +static const struct rfprog { + uint8_t chan; + uint32_t r1, r2, r3, r4; +} rt2860_rf2850[] = { + RT2860_RF2850 +}; + +int +rt2860_attach(void *xsc, int id) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct rt2860_softc *sc = xsc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int i, qid, ntries, error; + + sc->amrr.amrr_min_success_threshold = 1; + sc->amrr.amrr_max_success_threshold = 15; + timeout_set(&sc->amrr_to, rt2860_updatestats, sc); + timeout_set(&sc->scan_to, rt2860_next_scan, sc); + + /* wait for NIC to initialize */ + for (ntries = 0; ntries < 100; ntries++) { + sc->mac_rev = RAL_READ(sc, RT2860_ASIC_VER_ID); + if (sc->mac_rev != 0 && sc->mac_rev != 0xffffffff) + break; + DELAY(10); + } + if (ntries == 100) { + printf("%s: timeout waiting for NIC to initialize\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + /* retrieve RF rev. no and various other things from EEPROM */ + rt2860_read_eeprom(sc); + printf(", address %s\n", ether_sprintf(ic->ic_myaddr)); + printf("%s: MAC/BBP RT%X (rev 0x%04X), RF %s (%dT%dR)\n", + sc->sc_dev.dv_xname, sc->mac_rev >> 16, sc->mac_rev & 0xffff, + rt2860_get_rf(sc->rf_rev), sc->ntxchains, sc->nrxchains); + + /* + * Allocate Tx (4 EDCAs + HCCA + Mgt) and Rx rings. + */ + for (qid = 0; qid < 6; qid++) { + if ((error = rt2860_alloc_tx_ring(sc, &sc->txq[qid])) != 0) { + printf("%s: could not allocate Tx ring %d\n", + sc->sc_dev.dv_xname, qid); + goto fail1; + } + } + + if ((error = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) { + printf("%s: could not allocate Rx ring\n", + sc->sc_dev.dv_xname); + goto fail1; + } + + if ((error = rt2860_alloc_tx_pool(sc)) != 0) { + printf("%s: could not allocate Tx pool\n", + sc->sc_dev.dv_xname); + goto fail2; + } + + /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ + sc->mgtqid = (sc->mac_rev == 0x28600100) ? EDCA_AC_VO : 5; + + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ + ic->ic_state = IEEE80211_S_INIT; + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_IBSS | /* IBSS mode supported */ + IEEE80211_C_MONITOR | /* monitor mode supported */ + IEEE80211_C_HOSTAP | /* HostAP mode supported */ + IEEE80211_C_TXPMGT | /* tx power management */ + IEEE80211_C_SHPREAMBLE | /* short preamble supported */ + IEEE80211_C_SHSLOT | /* short slot time supported */ + IEEE80211_C_WEP; /* s/w WEP */ + + if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850) { + /* set supported .11a rates */ + ic->ic_sup_rates[IEEE80211_MODE_11A] = + ieee80211_std_rateset_11a; + + /* set supported .11a channels */ + for (i = 14; i < N(rt2860_rf2850); i++) { + uint8_t chan = rt2860_rf2850[i].chan; + ic->ic_channels[chan].ic_freq = + ieee80211_ieee2mhz(chan, IEEE80211_CHAN_5GHZ); + ic->ic_channels[chan].ic_flags = IEEE80211_CHAN_A; + } + } + + /* set supported .11b and .11g rates */ + ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; + ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; + + /* set supported .11b and .11g channels (1 through 14) */ + for (i = 1; i <= 14; i++) { + ic->ic_channels[i].ic_freq = + ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); + ic->ic_channels[i].ic_flags = + IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | + IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; + } + + /* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */ + ic->ic_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX); + + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = rt2860_init; + ifp->if_ioctl = rt2860_ioctl; + ifp->if_start = rt2860_start; + ifp->if_watchdog = rt2860_watchdog; + IFQ_SET_READY(&ifp->if_snd); + memcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + + if_attach(ifp); + ieee80211_ifattach(ifp); + ic->ic_newassoc = rt2860_newassoc; + ic->ic_updateslot = rt2860_updateslot; + ic->ic_updateedca = rt2860_updateedca; + + /* override state transition machine */ + sc->sc_newstate = ic->ic_newstate; + ic->ic_newstate = rt2860_newstate; + ieee80211_media_init(ifp, rt2860_media_change, ieee80211_media_status); + + return 0; + +fail2: rt2860_free_rx_ring(sc, &sc->rxq); +fail1: while (--qid >= 0) + rt2860_free_tx_ring(sc, &sc->txq[qid]); + return error; +#undef N +} + +int +rt2860_detach(void *xsc) +{ + struct rt2860_softc *sc = xsc; + struct ifnet *ifp = &sc->sc_ic.ic_if; + int qid; + + timeout_del(&sc->scan_to); + timeout_del(&sc->amrr_to); + + ieee80211_ifdetach(ifp); /* free all nodes */ + if_detach(ifp); + + for (qid = 0; qid < 6; qid++) + rt2860_free_tx_ring(sc, &sc->txq[qid]); + rt2860_free_rx_ring(sc, &sc->rxq); + rt2860_free_tx_pool(sc); + + return 0; +} + +int +rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) +{ + int nsegs, size, error; + + size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &ring->map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + /* Tx rings must be 4-DWORD aligned */ + error = bus_dmamem_alloc(sc->sc_dmat, size, 16, 0, &ring->seg, 1, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, size, + (caddr_t *)&ring->txd, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->txd, size, NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(ring->txd, 0, size); + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, size, BUS_DMASYNC_PREWRITE); + + ring->paddr = ring->map->dm_segs[0].ds_addr; + + return 0; + +fail: rt2860_free_tx_ring(sc, ring); + return error; +} + +void +rt2860_reset_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) +{ + struct rt2860_tx_data *data; + int i; + + for (i = 0; i < RT2860_TX_RING_COUNT; i++) { + ring->txd[i].sdl0 &= ~htole16(RT2860_TX_DDONE); + + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m= NULL; + data->ni = NULL; /* node already freed */ + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[i] = NULL; + } + + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + ring->queued = 0; + ring->cur = ring->next = 0; +} + +void +rt2860_free_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) +{ + struct rt2860_tx_data *data; + int i; + + if (ring->txd != NULL) { + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, + ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->txd, + RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + for (i = 0; i < RT2860_TX_RING_COUNT; i++) { + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + } +} + +/* + * Allocate a pool of TX Wireless Information blocks. + */ +int +rt2860_alloc_tx_pool(struct rt2860_softc *sc) +{ + int i, nsegs, size, error; + + size = RT2860_TX_POOL_COUNT * sizeof (struct rt2860_txwi); + + /* init data_pool early in case of failure.. */ + SLIST_INIT(&sc->data_pool); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &sc->txwi_map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, + &sc->txwi_seg, 1, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &sc->txwi_seg, nsegs, size, + (caddr_t *)&sc->txwi, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, sc->txwi_map, sc->txwi, size, + NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(sc->txwi, 0, size); + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, 0, size, + BUS_DMASYNC_PREWRITE); + + for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { + struct rt2860_tx_data *data = &sc->data[i]; + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, + RT2860_MAX_SCATTER, MCLBYTES, 0, BUS_DMA_NOWAIT, + &data->map); + if (error != 0) { + printf("%s: could not create DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + data->txwi = &sc->txwi[i]; + data->paddr = sc->txwi_map->dm_segs[0].ds_addr + + i * sizeof (struct rt2860_txwi); + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + } + + return 0; + +fail: rt2860_free_tx_pool(sc); + return error; +} + +void +rt2860_free_tx_pool(struct rt2860_softc *sc) +{ + if (sc->txwi != NULL) { + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, 0, + sc->txwi_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->txwi_map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->txwi, + RT2860_TX_POOL_COUNT * sizeof (struct rt2860_txwi)); + bus_dmamem_free(sc->sc_dmat, &sc->txwi_seg, 1); + } + if (sc->txwi_map != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->txwi_map); + + while (!SLIST_EMPTY(&sc->data_pool)) { + struct rt2860_tx_data *data; + data = SLIST_FIRST(&sc->data_pool); + bus_dmamap_destroy(sc->sc_dmat, data->map); + SLIST_REMOVE_HEAD(&sc->data_pool, next); + } +} + +int +rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) +{ + int i, nsegs, size, error; + + size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd); + + error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_NOWAIT, &ring->map); + if (error != 0) { + printf("%s: could not create DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + /* Rx ring must be 4-DWORD aligned */ + error = bus_dmamem_alloc(sc->sc_dmat, size, 16, 0, &ring->seg, 1, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not allocate DMA memory\n", + sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamem_map(sc->sc_dmat, &ring->seg, nsegs, size, + (caddr_t *)&ring->rxd, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map DMA memory\n", sc->sc_dev.dv_xname); + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, ring->map, ring->rxd, size, NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", sc->sc_dev.dv_xname); + goto fail; + } + + memset(ring->rxd, 0, size); + ring->paddr = ring->map->dm_segs[0].ds_addr; + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) { + struct rt2860_rx_data *data = &ring->data[i]; + struct rt2860_rxd *rxd = &ring->rxd[i]; + + error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, + 0, BUS_DMA_NOWAIT, &data->map); + if (error != 0) { + printf("%s: could not create DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + MGETHDR(data->m, M_DONTWAIT, MT_DATA); + if (data->m == NULL) { + printf("%s: could not allocate Rx mbuf\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + MCLGET(data->m, M_DONTWAIT); + if (!(data->m->m_flags & M_EXT)) { + printf("%s: could not allocate Rx mbuf cluster\n", + sc->sc_dev.dv_xname); + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not load DMA map\n", + sc->sc_dev.dv_xname); + goto fail; + } + + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + rxd->sdl0 = htole16(MCLBYTES); + } + + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, size, BUS_DMASYNC_PREWRITE); + + return 0; + +fail: rt2860_free_rx_ring(sc, ring); + return error; +} + +void +rt2860_reset_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) +{ + int i; + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) + ring->rxd[i].sdl0 &= ~htole16(RT2860_RX_DDONE); + + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + ring->cur = 0; +} + +void +rt2860_free_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) +{ + int i; + + if (ring->rxd != NULL) { + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, + ring->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, ring->map); + bus_dmamem_unmap(sc->sc_dmat, (caddr_t)ring->rxd, + RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd)); + bus_dmamem_free(sc->sc_dmat, &ring->seg, 1); + } + if (ring->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, ring->map); + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) { + struct rt2860_rx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(sc->sc_dmat, data->map); + } +} + +int +rt2860_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return error; + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING)) + rt2860_init(ifp); + + return 0; +} + +/* + * This function is called periodically (every 200ms) during scanning to + * switch from one channel to another. + */ +void +rt2860_next_scan(void *arg) +{ + struct rt2860_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + int s; + + s = splnet(); + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(ifp); + splx(s); +} + +void +rt2860_iter_func(void *arg, struct ieee80211_node *ni) +{ + struct rt2860_softc *sc = arg; + uint8_t wcid; + + wcid = RT2860_AID2WCID(ni->ni_associd); + ieee80211_amrr_choose(&sc->amrr, ni, &sc->amn[wcid]); +} + +void +rt2860_updatestats(void *arg) +{ + struct rt2860_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + int s; + + s = splnet(); + if (ic->ic_opmode == IEEE80211_M_STA) + rt2860_iter_func(sc, ic->ic_bss); + else + ieee80211_iterate_nodes(ic, rt2860_iter_func, arg); + splx(s); + + timeout_add(&sc->amrr_to, hz / 2); +} + +void +rt2860_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, int isnew) +{ + struct rt2860_softc *sc = ic->ic_softc; + uint8_t wcid; + int i; + + wcid = RT2860_AID2WCID(ni->ni_associd); + + /* init WCID table entry */ + RAL_WRITE_REGION_1(sc, RT2860_WCID_ENTRY(wcid), + ni->ni_macaddr, IEEE80211_ADDR_LEN); + + ieee80211_amrr_node_init(&sc->amrr, &sc->amn[wcid]); + + /* set rate to some reasonable initial value */ + for (i = ni->ni_rates.rs_nrates - 1; + i > 0 && (ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL) > 72; + i--); + ni->ni_txrate = i; + + DPRINTF(("new assoc addr=%s WCID=%d, initial rate=%d\n", + ether_sprintf(ni->ni_macaddr), wcid, + ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL)); +} + +int +rt2860_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) +{ + struct rt2860_softc *sc = ic->ic_if.if_softc; + enum ieee80211_state ostate; + uint32_t tmp; + + ostate = ic->ic_state; + timeout_del(&sc->scan_to); + timeout_del(&sc->amrr_to); + + switch (nstate) { + case IEEE80211_S_INIT: + if (ostate == IEEE80211_S_RUN) { + /* abort TSF synchronization */ + tmp = RAL_READ(sc, RT2860_BCN_TIME_CFG); + RAL_WRITE(sc, RT2860_BCN_TIME_CFG, + tmp & ~(RT2860_BCN_TX_EN | RT2860_TSF_TIMER_EN | + RT2860_TBTT_TIMER_EN)); + } + break; + + case IEEE80211_S_SCAN: + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + timeout_add(&sc->scan_to, hz / 5); + break; + + case IEEE80211_S_AUTH: + case IEEE80211_S_ASSOC: + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + break; + + case IEEE80211_S_RUN: + rt2860_set_chan(sc, ic->ic_bss->ni_chan); + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_updateslot(ic); + rt2860_enable_mrr(sc); + rt2860_set_txpreamble(sc); + rt2860_set_basicrates(sc); + rt2860_set_bssid(sc, ic->ic_bss->ni_bssid); + } + + if (ic->ic_opmode == IEEE80211_M_HOSTAP || + ic->ic_opmode == IEEE80211_M_IBSS) + rt2860_setup_beacon(sc); + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* fake a join to init the tx rate */ + rt2860_newassoc(ic, ic->ic_bss, 1); + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_enable_tsf_sync(sc); + timeout_add(&sc->amrr_to, hz / 2); + } + break; + } + + return sc->sc_newstate(ic, nstate, arg); +} + +/* + * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, + * 93C66 or 93C86). + */ +uint16_t +rt2860_eeprom_read(struct rt2860_softc *sc, uint8_t addr) +{ + uint32_t tmp; + uint16_t val; + int n; + + /* clock C once before the first command */ + RT2860_EEPROM_CTL(sc, 0); + + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + RT2860_EEPROM_CTL(sc, RT2860_S); + + /* write start bit (1) */ + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); + + /* write READ opcode (10) */ + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_D | RT2860_C); + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + + /* write address (A5-A0 or A7-A0) */ + n = ((RAL_READ(sc, RT2860_PCI_EECTRL) & 0x30) == 0) ? 5 : 7; + for (; n >= 0; n--) { + RT2860_EEPROM_CTL(sc, RT2860_S | + (((addr >> n) & 1) << RT2860_SHIFT_D)); + RT2860_EEPROM_CTL(sc, RT2860_S | + (((addr >> n) & 1) << RT2860_SHIFT_D) | RT2860_C); + } + + RT2860_EEPROM_CTL(sc, RT2860_S); + + /* read data Q15-Q0 */ + val = 0; + for (n = 15; n >= 0; n--) { + RT2860_EEPROM_CTL(sc, RT2860_S | RT2860_C); + tmp = RAL_READ(sc, RT2860_PCI_EECTRL); + val |= ((tmp & RT2860_Q) >> RT2860_SHIFT_Q) << n; + RT2860_EEPROM_CTL(sc, RT2860_S); + } + + RT2860_EEPROM_CTL(sc, 0); + + /* clear Chip Select and clock C */ + RT2860_EEPROM_CTL(sc, RT2860_S); + RT2860_EEPROM_CTL(sc, 0); + RT2860_EEPROM_CTL(sc, RT2860_C); + + return val; +} + +void +rt2860_drain_stats_fifo(struct rt2860_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ic.ic_if; + struct ieee80211_amrr_node *amn; + uint32_t stat; + uint8_t wcid; + + /* drain Tx status FIFO (maxsize = 16) */ + while ((stat = RAL_READ(sc, RT2860_TX_STAT_FIFO)) & RT2860_TXQ_VLD) { + DPRINTFN(4, ("tx stat 0x%08x\n", stat)); + + wcid = (stat >> 8) & 0xff; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff) + continue; + + /* update per-STA AMRR stats */ + amn = &sc->amn[wcid]; + amn->amn_txcnt++; + if (stat & RT2860_TXQ_OK) { + /* + * Check if there were retries, ie if the Tx success + * rate is different from the requested rate. + */ + if (((stat >> RT2860_TXQ_RATE_SHIFT) & 0x7f) != + ((stat >> RT2860_TXQ_PID_SHIFT) & 0xf)) + amn->amn_retrycnt++; + } else { + amn->amn_retrycnt++; + ifp->if_oerrors++; + } + } +} + +void +rt2860_tx_intr(struct rt2860_softc *sc, int qid) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct rt2860_tx_ring *ring = &sc->txq[qid]; + uint32_t hw; + + rt2860_drain_stats_fifo(sc); + + hw = RAL_READ(sc, RT2860_TX_DTX_IDX(qid)); + while (ring->next != hw) { + struct rt2860_txd *txd = &ring->txd[ring->next]; + struct rt2860_tx_data *data = ring->data[ring->next]; + + if (data != NULL) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(data->m); + data->m= NULL; + ieee80211_release_node(ic, data->ni); + data->ni = NULL; + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[ring->next] = NULL; + + ifp->if_opackets++; + } + + txd->sdl0 &= ~htole16(RT2860_TX_DDONE); + + bus_dmamap_sync(sc->sc_dmat, ring->map, + ring->next * sizeof (struct rt2860_txd), + sizeof (struct rt2860_txd), BUS_DMASYNC_PREWRITE); + + ring->queued--; + ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; + } + + sc->sc_tx_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + rt2860_start(ifp); +} + +void +rt2860_rx_intr(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m, *mnew; + u_int hdrlen; + uint8_t rssi; + int error; + + for (;;) { + struct rt2860_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct rt2860_rxd *rxd = &sc->rxq.rxd[sc->rxq.cur]; + struct rt2860_rxwi *rxwi; + + bus_dmamap_sync(sc->sc_dmat, sc->rxq.map, + sc->rxq.cur * sizeof (struct rt2860_rxd), + sizeof (struct rt2860_rxd), BUS_DMASYNC_POSTREAD); + + if (!(letoh16(rxd->sdl0) & RT2860_RX_DDONE)) + break; + + if (letoh32(rxd->flags) & + (RT2860_RX_CRCERR | RT2860_RX_ICVERR)) { + ifp->if_ierrors++; + goto skip; + } + + if (letoh32(rxd->flags) & RT2860_RX_MICERR) { + /* XXX report MIC failures to net80211 for TKIP */ + ifp->if_ierrors++; + goto skip; + } + + MGETHDR(mnew, M_DONTWAIT, MT_DATA); + if (mnew == NULL) { + ifp->if_ierrors++; + goto skip; + } + MCLGET(mnew, M_DONTWAIT); + if (!(mnew->m_flags & M_EXT)) { + m_freem(mnew); + ifp->if_ierrors++; + goto skip; + } + + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + data->map->dm_mapsize, BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->sc_dmat, data->map); + + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(mnew, void *), MCLBYTES, NULL, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(mnew); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->sc_dmat, data->map, + mtod(data->m, void *), MCLBYTES, NULL, + BUS_DMA_NOWAIT); + if (error != 0) { + panic("%s: could not load old rx mbuf", + sc->sc_dev.dv_xname); + } + /* physical address may have changed */ + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + ifp->if_ierrors++; + goto skip; + } + + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ + m = data->m; + data->m = mnew; + rxd->sdp0 = htole32(data->map->dm_segs[0].ds_addr); + + rxwi = mtod(m, struct rt2860_rxwi *); + + /* finalize mbuf */ + m->m_pkthdr.rcvif = ifp; + m->m_data = (caddr_t)(rxwi + 1); + m->m_pkthdr.len = m->m_len = letoh16(rxwi->len) & 0xfff; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + + /* HW may insert 2 padding bytes after 802.11 header */ + if (letoh32(rxd->flags) & RT2860_RX_L2PAD) { + ovbcopy(wh, (caddr_t)wh + 2, hdrlen); + m->m_data += 2; + wh = mtod(m, struct ieee80211_frame *); + } + + /* grab a reference to the source node */ + ni = ieee80211_find_rxnode(ic, wh); + + /* send the frame to the 802.11 layer */ + rssi = rxwi->rssi[rt2860_maxrssi_chain(sc, rxwi)]; + ieee80211_input(ifp, m, ni, rssi, 0); + + /* node is no longer needed */ + ieee80211_release_node(ic, ni); + +skip: rxd->sdl0 &= ~htole16(RT2860_RX_DDONE); + + bus_dmamap_sync(sc->sc_dmat, sc->rxq.map, + sc->rxq.cur * sizeof (struct rt2860_rxd), + sizeof (struct rt2860_rxd), BUS_DMASYNC_PREWRITE); + + sc->rxq.cur = (sc->rxq.cur + 1) % RT2860_RX_RING_COUNT; + } + + /* tell HW what we have processed */ + RAL_WRITE(sc, RT2860_RX_CALC_IDX, + (sc->rxq.cur - 1) % RT2860_RX_RING_COUNT); + + /* + * In HostAP mode, ieee80211_input() will enqueue packets in if_snd + * without calling if_start(). + */ + if (!IFQ_IS_EMPTY(&ifp->if_snd) && !(ifp->if_flags & IFF_OACTIVE)) + rt2860_start(ifp); +} + +int +rt2860_intr(void *arg) +{ + struct rt2860_softc *sc = arg; + uint32_t r; + + r = RAL_READ(sc, RT2860_INT_STATUS); + if (r == 0) + return 0; /* not for us */ + + /* acknowledge interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, r); + + if (r & RT2860_TX_COHERENT) + /* TBD */; + + if (r & RT2860_RX_COHERENT) + /* TBD */; + + if (r & RT2860_MAC_INT_2) + rt2860_drain_stats_fifo(sc); + + if (r & RT2860_TX_DONE_INT5) + rt2860_tx_intr(sc, 5); + + if (r & RT2860_RX_DONE_INT) + rt2860_rx_intr(sc); + + if (r & RT2860_TX_DONE_INT4) + rt2860_tx_intr(sc, 4); + + if (r & RT2860_TX_DONE_INT3) + rt2860_tx_intr(sc, 3); + + if (r & RT2860_TX_DONE_INT2) + rt2860_tx_intr(sc, 2); + + if (r & RT2860_TX_DONE_INT1) + rt2860_tx_intr(sc, 1); + + if (r & RT2860_TX_DONE_INT0) + rt2860_tx_intr(sc, 0); + + if (r & RT2860_MAC_INT_1) + /* TBD pre-TBTT */; + + if (r & RT2860_MAC_INT_0) + /* TBD TBTT */; + + if (r & RT2860_MAC_INT_3) + /* TBD wakeup */; + + return 1; +} + +/* quickly determine if a given rate is CCK or OFDM */ +#define RAL_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + +#define RAL_ACK_SIZE 14 /* 10 + 4(FCS) */ +#define RAL_SIFS_TIME 10 + +/* + * Return the expected ack rate for a frame transmitted at rate `rate'. + */ +int +rt2860_ack_rate(struct ieee80211com *ic, int rate) +{ + switch (rate) { + /* CCK rates */ + case 2: + return 2; + case 4: + case 11: + case 22: + return (ic->ic_curmode == IEEE80211_MODE_11B) ? 4 : rate; + + /* OFDM rates */ + case 12: + case 18: + return 12; + case 24: + case 36: + return 24; + case 48: + case 72: + case 96: + case 108: + return 48; + } + + /* default to 1Mbps */ + return 2; +} + +/* + * Compute the duration (in us) needed to transmit `len' bytes at rate `rate'. + * The function automatically determines the operating mode depending on the + * given rate. `flags' indicates whether short preamble is in use or not. + */ +uint16_t +rt2860_txtime(int len, int rate, uint32_t flags) +{ + uint16_t txtime; + + if (RAL_RATE_IS_OFDM(rate)) { + /* IEEE Std 802.11g-2003, pp. 44 */ + txtime = (8 + 4 * len + 3 + rate - 1) / rate; + txtime = 16 + 4 + 4 * txtime + 6; + } else { + /* IEEE Std 802.11b-1999, pp. 28 */ + txtime = (16 * len + rate - 1) / rate; + if (rate != 2 && (flags & IEEE80211_F_SHPREAMBLE)) + txtime += 72 + 24; + else + txtime += 144 + 48; + } + return txtime; +} + +uint8_t +rt2860_rate2mcs(struct rt2860_softc *sc, uint8_t rate) +{ + struct ieee80211com *ic = &sc->sc_ic; + + if (!RAL_RATE_IS_OFDM(rate)) { + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) { + switch (rate) { + case 2: return 0; + case 4: return 9; + case 11: return 10; + case 22: return 11; + } + } else { + switch (rate) { + case 2: return 0; + case 4: return 1; + case 11: return 2; + case 22: return 3; + } + } + } else { + switch (rate) { + case 12: return 0; + case 18: return 1; + case 24: return 2; + case 36: return 3; + case 48: return 4; + case 72: return 5; + case 96: return 6; + case 108: return 7; + } + } + return 0; /* shouldn't get there */ +} + +int +rt2860_tx_data(struct rt2860_softc *sc, struct mbuf *m0, + struct ieee80211_node *ni, int qid) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct rt2860_tx_ring *ring = &sc->txq[qid]; + struct rt2860_tx_data *data; + struct rt2860_txd *txd; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + bus_dma_segment_t *seg; + u_int hdrlen; + uint16_t dur; + uint8_t type, qsel, mcs; + int nsegs, ntxds, rate, error; + + /* the data pool contains at least one element, pick the first */ + data = SLIST_FIRST(&sc->data_pool); + + wh = mtod(m0, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* pickup a rate */ + if (IEEE80211_IS_MULTICAST(wh->i_addr1) || + type != IEEE80211_FC0_TYPE_DATA) + rate = ni->ni_rates.rs_rates[0]; + else if (ic->ic_fixed_rate != -1) + rate = ic->ic_sup_rates[ic->ic_curmode]. + rs_rates[ic->ic_fixed_rate]; + else + rate = ni->ni_rates.rs_rates[ni->ni_txrate]; + rate &= IEEE80211_RATE_VAL; + + /* get MCS code from rate */ + mcs = rt2860_rate2mcs(sc, rate); + + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + m0 = ieee80211_wep_crypt(ifp, m0, 1); + if (m0 == NULL) + return ENOBUFS; + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m0, struct ieee80211_frame *); + } + + /* setup TX Wireless Information */ + txwi = data->txwi; + memset(txwi, 0, sizeof (struct rt2860_txwi)); + txwi->wcid = (type == IEEE80211_FC0_TYPE_DATA) ? + RT2860_AID2WCID(ni->ni_associd) : 0xff; + txwi->len = htole16(m0->m_pkthdr.len); + txwi->len |= htole16(mcs << RT2860_TX_PID_SHIFT); + txwi->phy = htole16(mcs); + if (RAL_RATE_IS_OFDM(rate)) + txwi->phy |= htole16(RT2860_TX_OFDM); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (m0->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold || + ((ic->ic_flags & IEEE80211_F_USEPROT) && RAL_RATE_IS_OFDM(rate)))) + txwi->txop = RT2860_TX_TXOP_HT; + else + txwi->txop = RT2860_TX_TXOP_BACKOFF; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { + txwi->xflags |= RT2860_TX_ACK; + + dur = rt2860_txtime(RAL_ACK_SIZE, rt2860_ack_rate(ic, rate), + ic->ic_flags) + sc->sifs; + *(uint16_t *)wh->i_dur = htole16(dur); + } + /* ask MAC to insert timestamp into probe responses */ + if ((wh->i_fc[0] & + (IEEE80211_FC0_TYPE_MASK | IEEE80211_FC0_SUBTYPE_MASK)) == + (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_RESP)) + /* NOTE: beacons do not pass through tx_data() */ + txwi->flags |= RT2860_TX_TS; + + /* copy and trim 802.11 header */ + memcpy(&txwi->wh, wh, hdrlen); + m_adj(m0, hdrlen); + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, + BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + printf("%s: could not map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m0); + return error; + } + if (error == 0) { + /* determine how many TXDs are required */ + ntxds = 1 + (data->map->dm_nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* not enough free TXDs, force mbuf defrag */ + bus_dmamap_unload(sc->sc_dmat, data->map); + error = EFBIG; + } + } + if (error != 0) { /* too many fragments, linearize */ + struct mbuf *m1; + + MGETHDR(m1, M_DONTWAIT, MT_DATA); + if (m1 == NULL) { + m_freem(m0); + return ENOMEM; + } + M_DUP_PKTHDR(m1, m0); + if (m0->m_pkthdr.len > MHLEN) { + MCLGET(m1, M_DONTWAIT); + if (!(m1->m_flags & M_EXT)) { + m_freem(m0); + m_freem(m1); + return ENOMEM; + } + } + m_copydata(m0, 0, m0->m_pkthdr.len, mtod(m1, caddr_t)); + m1->m_len = m1->m_pkthdr.len; + m_freem(m0); + m0 = m1; + + error = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m0, + BUS_DMA_NOWAIT); + if (error != 0) { + printf("%s: could not map mbuf (error %d)\n", + sc->sc_dev.dv_xname, error); + m_freem(m0); + return error; + } + + /* determine how many TXDs are now required */ + ntxds = 1 + (data->map->dm_nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* this is an hopeless case, drop the mbuf! */ + bus_dmamap_unload(sc->sc_dmat, data->map); + m_freem(m0); + return ENOMEM; + } + } + + qsel = (qid < EDCA_NUM_AC) ? RT2860_TX_QSEL_EDCA : RT2860_TX_QSEL_MGMT; + + /* first segment is TXWI + 802.11 header */ + txd = &ring->txd[ring->cur]; + txd->sdp0 = htole32(data->paddr); + txd->sdl0 = htole16(16 + hdrlen); + txd->flags = qsel; + + /* setup payload segments */ + seg = data->map->dm_segs; + for (nsegs = data->map->dm_nsegs; nsegs >= 2; nsegs -= 2) { + txd->sdp1 = htole32(seg->ds_addr); + txd->sdl1 = htole16(seg->ds_len); + seg++; + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + /* grab a new Tx descriptor */ + txd = &ring->txd[ring->cur]; + txd->sdp0 = htole32(seg->ds_addr); + txd->sdl0 = htole16(seg->ds_len); + txd->flags = qsel; + seg++; + } + /* finalize last segment */ + if (nsegs > 0) { + txd->sdp1 = htole32(seg->ds_addr); + txd->sdl1 = htole16(seg->ds_len | RT2860_TX_LS1); + } else { + txd->sdl0 |= htole16(RT2860_TX_LS0); + txd->sdl1 = 0; + } + + /* remove from the free pool and link it into the SW Tx slot */ + SLIST_REMOVE_HEAD(&sc->data_pool, next); + data->m = m0; + data->ni = ni; + ring->data[ring->cur] = data; + + bus_dmamap_sync(sc->sc_dmat, sc->txwi_map, + (caddr_t)txwi - (caddr_t)sc->txwi, sizeof (struct rt2860_txwi), + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, data->map, 0, data->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->sc_dmat, ring->map, 0, ring->map->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + DPRINTFN(4, ("sending frame qid=%d wcid=%d nsegs=%d rate=%d\n", + qid, txwi->wcid, data->map->dm_nsegs, rate)); + + ring->queued += ntxds; + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + + /* kick Tx */ + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); + + return 0; +} + +void +rt2860_start(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m0; + + /* + * net80211 may still try to send management frames even if the + * IFF_RUNNING flag is not set... + */ + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + for (;;) { + IF_POLL(&ic->ic_mgtq, m0); + if (m0 != NULL) { + if (SLIST_EMPTY(&sc->data_pool) || + sc->txq[sc->mgtqid].queued >= + RT2860_TX_RING_COUNT) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IF_DEQUEUE(&ic->ic_mgtq, m0); + + ni = (struct ieee80211_node *)m0->m_pkthdr.rcvif; + m0->m_pkthdr.rcvif = NULL; +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); +#endif + if (rt2860_tx_data(sc, m0, ni, sc->mgtqid) != 0) + break; + + } else { + if (ic->ic_state != IEEE80211_S_RUN) + break; + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + if (SLIST_EMPTY(&sc->data_pool) || + sc->txq[EDCA_AC_BE].queued >= + RT2860_TX_RING_COUNT) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + IFQ_DEQUEUE(&ifp->if_snd, m0); +#if NBPFILTER > 0 + if (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); +#endif + m0 = ieee80211_encap(ifp, m0, &ni); + if (m0 == NULL) + continue; +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m0, BPF_DIRECTION_OUT); +#endif + if (rt2860_tx_data(sc, m0, ni, EDCA_AC_BE) != 0) { + if (ni != NULL) + ieee80211_release_node(ic, ni); + ifp->if_oerrors++; + break; + } + } + + sc->sc_tx_timer = 5; + ifp->if_timer = 1; + } +} + +void +rt2860_watchdog(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + + ifp->if_timer = 0; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", sc->sc_dev.dv_xname); + rt2860_init(ifp); + ifp->if_oerrors++; + return; + } + ifp->if_timer = 1; + } + + ieee80211_watchdog(ifp); +} + +int +rt2860_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifaddr *ifa; + struct ifreq *ifr; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&ic->ic_ac, ifa); +#endif + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + rt2860_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + rt2860_stop(ifp, 1); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ifr = (struct ifreq *)data; + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &ic->ic_ac) : + ether_delmulti(ifr, &ic->ic_ac); + + if (error == ENETRESET) + error = 0; + break; + + case SIOCS80211CHANNEL: + /* + * This allows for fast channel switching in monitor mode + * (used by kismet). In IBSS mode, we must explicitly reset + * the interface to generate a new beacon frame. + */ + error = ieee80211_ioctl(ifp, cmd, data); + if (error == ENETRESET && + ic->ic_opmode == IEEE80211_M_MONITOR) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) + rt2860_set_chan(sc, ic->ic_ibss_chan); + error = 0; + } + break; + + default: + error = ieee80211_ioctl(ifp, cmd, data); + } + + if (error == ENETRESET) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) + rt2860_init(ifp); + error = 0; + } + + splx(s); + + return error; +} + +/* + * Reading and writing from/to the BBP is different from RT2560 and RT2661. + * We access the BBP through the 8051 microcontroller unit which means that + * the microcode must be loaded first. + */ +void +rt2860_mcu_bbp_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) +{ + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not write to BBP through MCU\n", + sc->sc_dev.dv_xname); + return; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | reg << 8 | val); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); + DELAY(1000); +} + +uint8_t +rt2860_mcu_bbp_read(struct rt2860_softc *sc, uint8_t reg) +{ + uint32_t val; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_BBPAGENT) & RT2860_BBP_CSR_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not read from BBP through MCU\n", + sc->sc_dev.dv_xname); + return 0; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0); + DELAY(1000); + + for (ntries = 0; ntries < 100; ntries++) { + val = RAL_READ(sc, RT2860_H2M_BBPAGENT); + if (!(val & RT2860_BBP_CSR_KICK)) + return val & 0xff; + DELAY(1); + } + printf("%s: could not read from BBP through MCU\n", + sc->sc_dev.dv_xname); + + return 0; +} + +/* + * Write to one of the 4 programmable 24-bit RF registers. + */ +void +rt2860_rf_write(struct rt2860_softc *sc, uint8_t reg, uint32_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_RF_CSR_CFG0) & RT2860_RF_REG_CTRL)) + break; + DELAY(1); + } + if (ntries == 100) { + printf("%s: could not write to RF\n", sc->sc_dev.dv_xname); + return; + } + + /* RF registers are 24-bit on the RT2860 */ + tmp = RT2860_RF_REG_CTRL | 24 << RT2860_RF_REG_WIDTH_SHIFT | + (val & 0x3fffff) << 2 | (reg & 3); + RAL_WRITE(sc, RT2860_RF_CSR_CFG0, tmp); +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +int +rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg) +{ + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY)) + break; + DELAY(2); + } + if (ntries == 100) + return EIO; + + RAL_WRITE(sc, RT2860_H2M_MAILBOX, + RT2860_H2M_BUSY | RT2860_TOKEN_NO_INTR << 16 | arg); + RAL_WRITE(sc, RT2860_HOST_CMD, cmd); + + return 0; +} + +void +rt2860_enable_mrr(struct rt2860_softc *sc) +{ +#define CCK(mcs) (mcs) +#define OFDM(mcs) (1 << 3 | (mcs)) + RAL_WRITE(sc, RT2860_LG_FBK_CFG0, + OFDM(6) << 28 | /* 54->48 */ + OFDM(5) << 24 | /* 48->36 */ + OFDM(4) << 20 | /* 36->24 */ + OFDM(3) << 16 | /* 24->18 */ + OFDM(2) << 12 | /* 18->12 */ + OFDM(1) << 8 | /* 12-> 9 */ + OFDM(0) << 4 | /* 9-> 6 */ + OFDM(0)); /* 6-> 6 */ + + RAL_WRITE(sc, RT2860_LG_FBK_CFG1, + CCK(2) << 12 | /* 11->5.5 */ + CCK(1) << 8 | /* 5.5-> 2 */ + CCK(0) << 4 | /* 2-> 1 */ + CCK(0)); /* 1-> 1 */ +#undef OFDM +#undef CCK +} + +void +rt2860_set_txpreamble(struct rt2860_softc *sc) +{ + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_AUTO_RSP_CFG); + tmp &= ~RT2860_CCK_SHORT_EN; + if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) + tmp |= RT2860_CCK_SHORT_EN; + RAL_WRITE(sc, RT2860_AUTO_RSP_CFG, tmp); +} + +void +rt2860_set_basicrates(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* set basic rates mask */ + if (ic->ic_curmode == IEEE80211_MODE_11B) + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x003); + else if (ic->ic_curmode == IEEE80211_MODE_11A) + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x150); + else /* 11a */ + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, 0x15f); +} + +void +rt2860_select_chan_group(struct rt2860_softc *sc, int group) +{ + uint32_t tmp; + + rt2860_mcu_bbp_write(sc, 62, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 63, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 64, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 86, 0x37 - sc->lna[group]); + rt2860_mcu_bbp_write(sc, 82, (group == 0) ? 0x62 : 0xf2); + + tmp = RAL_READ(sc, RT2860_TX_BAND_CFG); + tmp &= ~(RT2860_5G_BAND_SEL_N | RT2860_5G_BAND_SEL_P); + tmp |= (group == 0) ? RT2860_5G_BAND_SEL_N : RT2860_5G_BAND_SEL_P; + RAL_WRITE(sc, RT2860_TX_BAND_CFG, tmp); + + /* enable appropriate Power Amplifiers and Low Noise Amplifiers */ + tmp = RT2860_RFTR_EN | RT2860_TRSW_EN; + if (group == 0) { /* 2GHz */ + tmp |= RT2860_PA_PE_G0_EN | RT2860_LNA_PE_G0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_G1_EN; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE_G1_EN; + } else { /* 5GHz */ + tmp |= RT2860_PA_PE_A0_EN | RT2860_LNA_PE_A0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_A1_EN; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE_A1_EN; + } + RAL_WRITE(sc, RT2860_TX_PIN_CFG, tmp); + + rt2860_mcu_bbp_write(sc, 66, 0x2e + sc->lna[group]); +} + +void +rt2860_set_chan(struct rt2860_softc *sc, struct ieee80211_channel *c) +{ + struct ieee80211com *ic = &sc->sc_ic; + const struct rfprog *rfprog = rt2860_rf2850; + uint32_t r2, r3, r4; + int8_t txpow1, txpow2; + u_int i, chan, group; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rfprog[i].chan != chan; i++); + + r2 = rfprog[i].r2; + if (sc->ntxchains == 1) + r2 |= 1 << 12; /* 1T: disable Tx chain 2 */ + if (sc->nrxchains == 1) + r2 |= 1 << 15 | 1 << 4; /* 1R: disable Rx chains 2 & 3 */ + else if (sc->nrxchains == 2) + r2 |= 1 << 4; /* 2R: disable Rx chain 3 */ + +#ifdef notyet + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; +#else + /* use default Tx power values */ + txpow1 = 0; txpow2 = 5; +#endif + if (IEEE80211_IS_CHAN_5GHZ(c)) { + txpow1 = txpow1 << 1 | 1; + txpow2 = txpow2 << 1 | 1; + } + r3 = rfprog[i].r3 | txpow1 << 7; + r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4; + + rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RAL_RF2, r2); + rt2860_rf_write(sc, RAL_RF3, r3); + rt2860_rf_write(sc, RAL_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RAL_RF2, r2); + rt2860_rf_write(sc, RAL_RF3, r3 | 1); + rt2860_rf_write(sc, RAL_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RAL_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RAL_RF2, r2); + rt2860_rf_write(sc, RAL_RF3, r3); + rt2860_rf_write(sc, RAL_RF4, r4); + + /* 802.11a uses a 16 microseconds short interframe space */ + sc->sifs = IEEE80211_IS_CHAN_5GHZ(c) ? 16 : 10; + + /* determine channel group */ + if (chan <= 14) + group = 0; + else if (chan <= 64) + group = 1; + else if (chan <= 128) + group = 2; + else + group = 3; + + /* XXX necessary only when group has changed! */ + rt2860_select_chan_group(sc, group); + + DELAY(1000); +} + +void +rt2860_set_bssid(struct rt2860_softc *sc, const uint8_t *bssid) +{ + RAL_WRITE(sc, RT2860_MAC_BSSID_DW0, + bssid[0] | bssid[1] << 8 | bssid[2] << 16 | bssid[3] << 24); + RAL_WRITE(sc, RT2860_MAC_BSSID_DW1, + bssid[4] | bssid[5] << 8); +} + +void +rt2860_set_macaddr(struct rt2860_softc *sc, const uint8_t *addr) +{ + RAL_WRITE(sc, RT2860_MAC_ADDR_DW0, + addr[0] | addr[1] << 8 | addr[2] << 16 | addr[3] << 24); + RAL_WRITE(sc, RT2860_MAC_ADDR_DW1, + addr[4] | addr[5] << 8); +} + +void +rt2860_updateslot(struct ieee80211com *ic) +{ + struct rt2860_softc *sc = ic->ic_softc; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_BKOFF_SLOT_CFG); + tmp &= ~0xff; + tmp |= (ic->ic_flags & IEEE80211_F_SHSLOT) ? 9 : 20; + RAL_WRITE(sc, RT2860_BKOFF_SLOT_CFG, tmp); +} + +#if 0 +void +rt2860_updateprot(struct ieee80211com *ic) +{ + /* set RTS rate and enable CTS-to-self protection if needed */ + RAL_WRITE(sc, RT2860_CCK_PROT_CFG, ); + RAL_WRITE(sc, RT2860_OFDM_PROT_CFG, ); +} +#endif + +void +rt2860_updateedca(struct ieee80211com *ic) +{ + struct rt2860_softc *sc = ic->ic_softc; + int aci; + + /* update MAC TX configuration registers */ + for (aci = 0; aci < EDCA_NUM_AC; aci++) { + RAL_WRITE(sc, RT2860_EDCA_AC_CFG(aci), + ic->ic_edca_ac[aci].ac_ecwmax << 16 | + ic->ic_edca_ac[aci].ac_ecwmin << 12 | + ic->ic_edca_ac[aci].ac_aifsn << 8 | + ic->ic_edca_ac[aci].ac_txoplimit); + } + + /* update SCH/DMA registers too */ + RAL_WRITE(sc, RT2860_WMM_AIFSN_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_aifsn << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_aifsn << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_aifsn << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_aifsn); + RAL_WRITE(sc, RT2860_WMM_CWMIN_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_ecwmin << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_ecwmin << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_ecwmin << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_ecwmin); + RAL_WRITE(sc, RT2860_WMM_CWMAX_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_ecwmax << 12 | + ic->ic_edca_ac[EDCA_AC_VI].ac_ecwmax << 8 | + ic->ic_edca_ac[EDCA_AC_BK].ac_ecwmax << 4 | + ic->ic_edca_ac[EDCA_AC_BE].ac_ecwmax); + RAL_WRITE(sc, RT2860_WMM_TXOP0_CFG, + ic->ic_edca_ac[EDCA_AC_BK].ac_txoplimit << 16 | + ic->ic_edca_ac[EDCA_AC_BE].ac_txoplimit); + RAL_WRITE(sc, RT2860_WMM_TXOP1_CFG, + ic->ic_edca_ac[EDCA_AC_VO].ac_txoplimit << 16 | + ic->ic_edca_ac[EDCA_AC_VI].ac_txoplimit); +} + +int8_t +rt2860_rssi2dbm(struct rt2860_softc *sc, uint8_t rssi, uint8_t rxchain) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c = ic->ic_bss->ni_chan; + int delta; + + if (IEEE80211_IS_CHAN_5GHZ(c)) { + u_int chan = ieee80211_chan2ieee(ic, c); + delta = sc->rssi_5ghz[rxchain]; + + /* determine channel group */ + if (chan <= 64) + delta -= sc->lna[1]; + else if (chan <= 128) + delta -= sc->lna[2]; + else + delta -= sc->lna[3]; + } else + delta = sc->rssi_2ghz[rxchain] - sc->lna[0]; + + return -12 - delta - rssi; +} + +/* + * Return the Rx chain with the highest RSSI for a given frame. + */ +uint8_t +rt2860_maxrssi_chain(struct rt2860_softc *sc, const struct rt2860_rxwi *rxwi) +{ + uint8_t rxchain = 0; + + if (sc->nrxchains > 1) + if (rxwi->rssi[1] > rxwi->rssi[rxchain]) + rxchain = 1; + if (sc->nrxchains > 2) + if (rxwi->rssi[2] > rxwi->rssi[rxchain]) + rxchain = 2; + + return rxchain; +} + +/* + * Add `delta' (signed) to each 4-bit sub-word of a 32-bit word. + * Used to adjust per-rate Tx power registers. + */ +static __inline uint32_t +b4inc(uint32_t b32, int8_t delta) +{ + int8_t i, b4; + + for (i = 0; i < 8; i++) { + b4 = b32 & 0xf; + b4 += delta; + if (b4 < 0) + b4 = 0; + else if (b4 > 0xf) + b4 = 0xf; + b32 = b32 >> 4 | b4 << 28; + } + return b32; +} + +const char * +rt2860_get_rf(uint8_t rev) +{ + switch (rev) { + case RT2860_RF_2820: return "RT2820"; + case RT2860_RF_2850: return "RT2850"; + case RT2860_RF_2720: return "RT2720"; + case RT2860_RF_2750: return "RT2750"; + default: return "unknown"; + } +} + +int +rt2860_read_eeprom(struct rt2860_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct ieee80211com *ic = &sc->sc_ic; + uint16_t val; + int8_t delta_2ghz, delta_5ghz; + int ridx, ant, i; + + /* read EEPROM version */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_VERSION); + DPRINTF(("EEPROM rev=%d, FAE=%d\n", val & 0xff, val >> 8)); + + /* read MAC address */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC01); + ic->ic_myaddr[0] = val & 0xff; + ic->ic_myaddr[1] = val >> 8; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC23); + ic->ic_myaddr[2] = val & 0xff; + ic->ic_myaddr[3] = val >> 8; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_MAC45); + ic->ic_myaddr[4] = val & 0xff; + ic->ic_myaddr[5] = val >> 8; + + /* read country code */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_COUNTRY); + DPRINTF(("EEPROM region code=0x%04x\n", val)); + + /* read default BBP settings */ + for (i = 0; i < 8; i++) { + val = rt2860_eeprom_read(sc, RT2860_EEPROM_BBP_BASE + i); + sc->bbp[i].val = val & 0xff; + sc->bbp[i].reg = val >> 8; + DPRINTF(("BBP%d=0x%02x\n", sc->bbp[i].reg, sc->bbp[i].val)); + } + + /* read RF information */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_ANTENNA); + if (val == 0xffff) { + /* broken EEPROM, default to RF2820 1T2R */ + DPRINTF(("invalid EEPROM antenna info, using default\n")); + sc->rf_rev = RT2860_RF_2820; + sc->ntxchains = 1; + sc->nrxchains = 2; + } else { + sc->rf_rev = (val >> 8) & 0xf; + sc->ntxchains = (val >> 4) & 0xf; + sc->nrxchains = val & 0xf; + } + DPRINTF(("EEPROM RF rev=0x%02x chains=%dT%dR\n", + sc->rf_rev, sc->ntxchains, sc->nrxchains)); + + /* check if RF supports automatic Tx access gain control */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_CONFIG); + DPRINTF(("EEPROM CFG 0x%04x\n", val)); + if ((val & 0xff) != 0xff) + sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; + + /* read power settings for 2GHz channels */ + for (i = 0; i < 14; i += 2) { + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR2GHZ_BASE1 + i / 2); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR2GHZ_BASE2 + i / 2); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + } + /* read power settings for 5GHz channels */ + for (; i < 50; i += 2) { + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2); + sc->txpow1[i + 0] = (int8_t)(val & 0xff); + sc->txpow1[i + 1] = (int8_t)(val >> 8); + + val = rt2860_eeprom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2); + sc->txpow2[i + 0] = (int8_t)(val & 0xff); + sc->txpow2[i + 1] = (int8_t)(val >> 8); + } + + /* fix broken Tx power entries */ + for (i = 0; i < N(rt2860_rf2850); i++) { + DPRINTF(("chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i])); + if (sc->txpow1[i] < -6 || sc->txpow1[i] > 36) { + DPRINTF(("out-of-range Tx power1 for chan %d: %d\n", + rt2860_rf2850[i].chan, sc->txpow1[i])); + sc->txpow1[i] = 5; + } + if (sc->txpow2[i] < -6 || sc->txpow2[i] > 36) { + DPRINTF(("out-of-range Tx power2 for chan %d: %d\n", + rt2860_rf2850[i].chan, sc->txpow2[i])); + sc->txpow2[i] = 5; + } + } + + /* read Tx power compensation for each Tx rate */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_DELTAPWR); + delta_2ghz = delta_5ghz = 0; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_2ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_2ghz = -delta_2ghz; + } + val >>= 8; + if ((val & 0xff) != 0xff && (val & 0x80)) { + delta_5ghz = val & 0xf; + if (!(val & 0x40)) /* negative number */ + delta_5ghz = -delta_5ghz; + } + DPRINTF(("power compensation=%d (2GHz), %d (5GHz)\n", + delta_2ghz, delta_5ghz)); + + for (ridx = 0; ridx < 5; ridx++) { + uint32_t reg; + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx); + reg = (uint32_t)val << 16; + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RPWR + ridx + 1); + reg |= val; + + sc->txpow20mhz[ridx] = reg; + sc->txpow40mhz_2ghz[ridx] = b4inc(reg, delta_2ghz); + sc->txpow40mhz_5ghz[ridx] = b4inc(reg, delta_5ghz); + + DPRINTF(("ridx %d: power 20MHz=0x%08x, 40MHz/2GHz=0x%08x, " + "40MHz/5GHz=0x%08x\n", ridx, sc->txpow20mhz[ridx], + sc->txpow40mhz_2ghz[ridx], sc->txpow40mhz_5ghz[ridx])); + } + + /* read factory-calibrated samples for temperature compensation */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_2GHZ); + sc->tssi_2ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_2ghz[1] = val >> 8; /* [-3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_2GHZ); + sc->tssi_2ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_2ghz[3] = val >> 8; /* [-1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_2GHZ); + sc->tssi_2ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_2ghz[5] = val >> 8; /* [+1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_2GHZ); + sc->tssi_2ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_2ghz[7] = val >> 8; /* [+3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_2GHZ); + sc->tssi_2ghz[8] = val & 0xff; /* [+4] */ + sc->step_2ghz = val >> 8; + DPRINTF(("TSSI 2GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x step=%d\n", sc->tssi_2ghz[0], sc->tssi_2ghz[1], + sc->tssi_2ghz[2], sc->tssi_2ghz[3], sc->tssi_2ghz[4], + sc->tssi_2ghz[5], sc->tssi_2ghz[6], sc->tssi_2ghz[7], + sc->tssi_2ghz[8], sc->step_2ghz)); + /* check that ref value is correct, otherwise disable calibration */ + if (sc->tssi_2ghz[4] == 0xff) + sc->calib_2ghz = 0; + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI1_5GHZ); + sc->tssi_5ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_5ghz[1] = val >> 8; /* [-3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI2_5GHZ); + sc->tssi_5ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_5ghz[3] = val >> 8; /* [-1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI3_5GHZ); + sc->tssi_5ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_5ghz[5] = val >> 8; /* [+1] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI4_5GHZ); + sc->tssi_5ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_5ghz[7] = val >> 8; /* [+3] */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_TSSI5_5GHZ); + sc->tssi_5ghz[8] = val & 0xff; /* [+4] */ + sc->step_5ghz = val >> 8; + DPRINTF(("TSSI 5GHz: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x " + "0x%02x 0x%02x step=%d\n", sc->tssi_5ghz[0], sc->tssi_5ghz[1], + sc->tssi_5ghz[2], sc->tssi_5ghz[3], sc->tssi_5ghz[4], + sc->tssi_5ghz[5], sc->tssi_5ghz[6], sc->tssi_5ghz[7], + sc->tssi_5ghz[8], sc->step_5ghz)); + /* check that ref value is correct, otherwise disable calibration */ + if (sc->tssi_5ghz[4] == 0xff) + sc->calib_5ghz = 0; + + /* read RF frequency offset from EEPROM */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_FREQ); + sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + DPRINTF(("EEPROM freq offset %d\n", sc->freq & 0xff)); + + /* read RSSI offsets and LNA gains from EEPROM */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_2GHZ); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_2GHZ); + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + sc->lna[2] = val >> 8; /* channel group 2 */ + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI1_5GHZ); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + val = rt2860_eeprom_read(sc, RT2860_EEPROM_RSSI2_5GHZ); + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + sc->lna[3] = val >> 8; /* channel group 3 */ + + val = rt2860_eeprom_read(sc, RT2860_EEPROM_LNA); + sc->lna[0] = val & 0xff; /* channel group 0 */ + sc->lna[1] = val >> 8; /* channel group 1 */ + + /* fix broken 5GHz LNA entries */ + if (sc->lna[2] == 0 || sc->lna[2] == 0xff) { + DPRINTF(("invalid LNA for channel group %d\n", 2)); + sc->lna[2] = sc->lna[1]; + } + if (sc->lna[3] == 0 || sc->lna[3] == 0xff) { + DPRINTF(("invalid LNA for channel group %d\n", 3)); + sc->lna[3] = sc->lna[1]; + } + + /* fix broken RSSI offset entries */ + for (ant = 0; ant < 3; ant++) { + if (sc->rssi_2ghz[ant] < -10 || sc->rssi_2ghz[ant] > 10) { + DPRINTF(("invalid RSSI%d offset: %d (2GHz)\n", + ant + 1, sc->rssi_2ghz[ant])); + sc->rssi_2ghz[ant] = 0; + } + if (sc->rssi_5ghz[ant] < -10 || sc->rssi_5ghz[ant] > 10) { + DPRINTF(("invalid RSSI%d offset: %d (5GHz)\n", + ant + 1, sc->rssi_5ghz[ant])); + sc->rssi_5ghz[ant] = 0; + } + } + + return 0; +#undef N +} + +int +rt2860_bbp_init(struct rt2860_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + int i, ntries; + + /* wait for BBP to wake up */ + for (ntries = 0; ntries < 20; ntries++) { + uint8_t bbp0 = rt2860_mcu_bbp_read(sc, 0); + if (bbp0 != 0 && bbp0 != 0xff) + break; + } + if (ntries == 20) { + printf("%s: timeout waiting for BBP to wake up\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + + /* initialize BBP registers to default values */ + for (i = 0; i < N(rt2860_def_bbp); i++) { + rt2860_mcu_bbp_write(sc, rt2860_def_bbp[i].reg, + rt2860_def_bbp[i].val); + } + + /* fix BBP69 and BBP73 for RT2860C */ + if (sc->mac_rev == 0x28600100) { + rt2860_mcu_bbp_write(sc, 69, 0x16); + rt2860_mcu_bbp_write(sc, 73, 0x12); + } + + return 0; +#undef N +} + +int +rt2860_init(struct ifnet *ifp) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + uint8_t bbp3; + int i, qid, ridx, ntries, error; + + /* for CardBus, power on the socket */ + if (!(sc->sc_flags & RT2860_ENABLED)) { + if (sc->sc_enable != NULL && (*sc->sc_enable)(sc) != 0) { + printf("%s: could not enable device\n", + sc->sc_dev.dv_xname); + return EIO; + } + sc->sc_flags |= RT2860_ENABLED; + } + + rt2860_stop(ifp, 0); + + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); + + RAL_WRITE(sc, RT2860_WPDMA_RST_IDX, 0xffffffff); + + /* PBF hardware reset */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + if (!(sc->sc_flags & RT2860_FWLOADED)) { + if ((error = rt2860_load_microcode(sc)) != 0) { + printf("%s: could not load microcode\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return error; + } + sc->sc_flags |= RT2860_FWLOADED; + } + + IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); + rt2860_set_macaddr(sc, ic->ic_myaddr); + + /* init Tx power for all Tx rates (from EEPROM) */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + RAL_WRITE(sc, RT2860_TX_PWR_CFG(ridx), sc->txpow20mhz[ridx]); + } + + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); + + /* reset Rx ring and all 6 Tx rings */ + RAL_WRITE(sc, RT2860_WPDMA_RST_IDX, 0x1003f); + + /* PBF hardware reset */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); + + for (i = 0; i < N(rt2860_def_mac); i++) + RAL_WRITE(sc, rt2860_def_mac[i].reg, rt2860_def_mac[i].val); + + /* wait while MAC is busy */ + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_MAC_STATUS_REG) & + (RT2860_RX_STATUS_BUSY | RT2860_TX_STATUS_BUSY))) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for MAC\n", sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + + if ((error = rt2860_bbp_init(sc)) != 0) { + rt2860_stop(ifp, 1); + return error; + } + + /* init Tx rings (4 EDCAs + HCCA + Mgt) */ + for (qid = 0; qid < 6; qid++) { + RAL_WRITE(sc, RT2860_TX_BASE_PTR(qid), sc->txq[qid].paddr); + RAL_WRITE(sc, RT2860_TX_MAX_CNT(qid), RT2860_TX_RING_COUNT); + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), 0); + } + + /* init Rx ring */ + RAL_WRITE(sc, RT2860_RX_BASE_PTR, sc->rxq.paddr); + RAL_WRITE(sc, RT2860_RX_MAX_CNT, RT2860_RX_RING_COUNT); + RAL_WRITE(sc, RT2860_RX_CALC_IDX, RT2860_RX_RING_COUNT - 1); + + /* setup maximum buffer sizes */ + RAL_WRITE(sc, RT2860_MAX_LEN_CFG, 1 << 12 | + (MCLBYTES - sizeof (struct rt2860_rxwi) - 2)); + + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 100) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp | RT2860_TX_WB_DDONE); + + /* disable interrupts mitigation */ + RAL_WRITE(sc, RT2860_DELAY_INT_CFG, 0); + + /* write vendor-specific BBP values (from EEPROM) */ + for (i = 0; i < 8; i++) { + if (sc->bbp[i].reg == 0 || sc->bbp[i].reg == 0xff) + continue; + rt2860_mcu_bbp_write(sc, sc->bbp[i].reg, sc->bbp[i].val); + } + + /* disable non-existing Rx chains */ + bbp3 = rt2860_mcu_bbp_read(sc, 3); + bbp3 &= ~(1 << 3 | 1 << 4); + if (sc->nrxchains == 2) + bbp3 |= 1 << 3; + else if (sc->nrxchains == 3) + bbp3 |= 1 << 4; + rt2860_mcu_bbp_write(sc, 3, bbp3); + + /* select default channel */ + ic->ic_bss->ni_chan = ic->ic_ibss_chan; + rt2860_set_chan(sc, ic->ic_ibss_chan); + + /* set RTS threshold */ + tmp = RAL_READ(sc, RT2860_TX_RTS_CFG); + tmp &= ~0xffff00; + tmp |= (ic->ic_rtsthreshold & 0xffff) << 8; + + /* enable Tx/Rx DMA engine */ + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); + for (ntries = 0; ntries < 200; ntries++) { + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + if ((tmp & (RT2860_TX_DMA_BUSY | RT2860_RX_DMA_BUSY)) == 0) + break; + DELAY(1000); + } + if (ntries == 200) { + printf("%s: timeout waiting for DMA engine\n", + sc->sc_dev.dv_xname); + rt2860_stop(ifp, 1); + return ETIMEDOUT; + } + + DELAY(50); + + tmp |= RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN | + RT2860_WPDMA_BT_SIZE64 << RT2860_WPDMA_BT_SIZE_SHIFT; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* set Rx filter */ + tmp = + RT2860_DROP_CRC_ERR | RT2860_DROP_PHY_ERR | RT2860_DROP_UC_NOME | + RT2860_DROP_VER_ERR | RT2860_DROP_DUPL | RT2860_DROP_CFACK | + RT2860_DROP_CFEND | RT2860_DROP_ACK | RT2860_DROP_CTS | + RT2860_DROP_BA | RT2860_DROP_CTRL_RSV; + if (ic->ic_opmode == IEEE80211_M_STA) + tmp |= RT2860_DROP_RTS | RT2860_DROP_PSPOLL; + RAL_WRITE(sc, RT2860_RX_FILTR_CFG, tmp); + + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + + /* clear pending interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, 0xffffffff); + /* enable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0x3fffc); + + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_flags |= IFF_RUNNING; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + + return 0; +#undef N +} + +void +rt2860_stop(struct ifnet *ifp, int disable) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + int qid; + + sc->sc_tx_timer = 0; + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); /* free all nodes */ + + /* clear RX WCID search table */ + RAL_SET_REGION_4(sc, RT2860_WCID_ENTRY(0), 0, 512); + /* clear pairwise key table */ + RAL_SET_REGION_4(sc, RT2860_PKEY(0), 0, 2048); + /* clear IV/EIV table */ + RAL_SET_REGION_4(sc, RT2860_IVEIV(0), 0, 512); + /* clear WCID attribute table */ + RAL_SET_REGION_4(sc, RT2860_WCID_ATTR(0), 0, 256); + /* clear shared key table */ + RAL_SET_REGION_4(sc, RT2860_SKEY(0, 0), 0, 8 * 32); + /* clear shared key mode */ + RAL_SET_REGION_4(sc, 0x7000, 0, 4); + + /* disable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0); + + /* disable Rx */ + tmp = RAL_READ(sc, RT2860_MAC_SYS_CTRL); + tmp &= ~(RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, tmp); + + /* reset adapter */ + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); + + /* reset Tx and Rx rings (and reclaim TXWIs) */ + for (qid = 0; qid < 6; qid++) + rt2860_reset_tx_ring(sc, &sc->txq[qid]); + rt2860_reset_rx_ring(sc, &sc->rxq); + + /* for CardBus, power down the socket */ + if (disable && sc->sc_disable != NULL) { + if (sc->sc_flags & RT2860_ENABLED) { + (*sc->sc_disable)(sc); + sc->sc_flags &= ~(RT2860_ENABLED | RT2860_FWLOADED); + } + } +} + +int +rt2860_load_microcode(struct rt2860_softc *sc) +{ + u_char *ucode; + size_t size; + int error, ntries; + + if ((error = loadfirmware("ral-rt2860", &ucode, &size)) != 0) { + printf("%s: error %d, could not read firmware file %s\n", + sc->sc_dev.dv_xname, error, "ral-rt2860"); + return error; + } + + /* set "host program ram write selection" bit */ + RAL_WRITE(sc, RT2860_SYS_CTRL, RT2860_HST_PM_SEL); + /* write microcode image */ + RAL_WRITE_REGION_1(sc, RT2860_FW_BASE, ucode, size); + /* kick microcontroller unit */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0); + RAL_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET); + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, 0); + RAL_WRITE(sc, RT2860_H2M_MAILBOX, 0); + + free(ucode, M_DEVBUF); + + /* wait until microcontroller is ready */ + for (ntries = 0; ntries < 1000; ntries++) { + if (RAL_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY) + break; + DELAY(1000); + } + if (ntries == 1000) { + printf("%s: timeout waiting for microcontroller unit\n", + sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + return 0; +} + +/* + * This function is called periodically to adjust Tx power based on + * temperature variation. + */ +void +rt2860_calib(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + const uint8_t *tssi; + uint8_t step, bbp49; + int8_t ridx, d; + + /* read current temperature */ + bbp49 = rt2860_mcu_bbp_read(sc, 49); + + if (IEEE80211_IS_CHAN_2GHZ(ic->ic_bss->ni_chan)) { + tssi = &sc->tssi_2ghz[4]; + step = sc->step_2ghz; + } else { + tssi = &sc->tssi_5ghz[4]; + step = sc->step_5ghz; + } + + if (bbp49 < tssi[0]) { /* lower than reference */ + /* use higher Tx power than default */ + for (d = 0; d > -4 && bbp49 <= tssi[d - 1]; d--); + } else if (bbp49 > tssi[0]) { /* greater than reference */ + /* use lower Tx power than default */ + for (d = 0; d < +4 && bbp49 >= tssi[d + 1]; d++); + } else { + /* use default Tx power */ + d = 0; + } + d *= step; + + DPRINTF(("BBP49=0x%02x, adjusting Tx power by %d\n", bbp49, d)); + + /* write adjusted Tx power values for each Tx rate */ + for (ridx = 0; ridx < 5; ridx++) { + if (sc->txpow20mhz[ridx] == 0xffffffff) + continue; + RAL_WRITE(sc, RT2860_TX_PWR_CFG(ridx), + b4inc(sc->txpow20mhz[ridx], d)); + } +} + +int +rt2860_setup_beacon(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct rt2860_txwi txwi; + struct mbuf *m; + int rate; + + if ((m = ieee80211_beacon_alloc(ic, ic->ic_bss)) == NULL) + return ENOBUFS; + + memset(&txwi, 0, sizeof txwi); + txwi.wcid = 0xff; + txwi.len = htole16(m->m_pkthdr.len); + /* send beacons at the lowest available rate */ + rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? 12 : 2; + txwi.phy = htole16(rt2860_rate2mcs(sc, rate)); + txwi.txop = RT2860_TX_TXOP_HT; + txwi.flags = RT2860_TX_TS; + + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0), &txwi, 16); + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0) + 16, + mtod(m, uint8_t *), m->m_pkthdr.len); + + m_freem(m); + + return 0; +} + +void +rt2860_enable_tsf_sync(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_BCN_TIME_CFG); + + tmp &= ~0x1fffff; + tmp |= ic->ic_bss->ni_intval * 16; + tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; + if (ic->ic_opmode == IEEE80211_M_STA) { + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_IBSS) { + tmp |= RT2860_BCN_TX_EN; + /* + * Local TSF is updated with remote TSF on beacon reception + * only if the remote TSF is greater than local TSF. + */ + tmp |= 2 << RT2860_TSF_SYNC_MODE_SHIFT; + } else if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + tmp |= RT2860_BCN_TX_EN; + /* SYNC with nobody */ + tmp |= 3 << RT2860_TSF_SYNC_MODE_SHIFT; + } + + RAL_WRITE(sc, RT2860_BCN_TIME_CFG, tmp); +} + +void +rt2860_shutdown(void *arg) +{ + struct rt2860_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ic.ic_if; + + rt2860_stop(ifp, 1); +} + diff --git a/sys/dev/ic/rt2860reg.h b/sys/dev/ic/rt2860reg.h new file mode 100644 index 00000000000..be1e5a500e0 --- /dev/null +++ b/sys/dev/ic/rt2860reg.h @@ -0,0 +1,869 @@ +/* $OpenBSD: rt2860reg.h,v 1.1 2007/11/15 21:15:34 damien Exp $ */ + +/*- + * Copyright (c) 2007 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and 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. + */ + +/* PCI registers */ +#define RT2860_PCI_CFG 0x0000 +#define RT2860_PCI_EECTRL 0x0004 +#define RT2860_PCI_MCUCTRL 0x0008 +#define RT2860_PCI_SYSCTRL 0x000c +#define RT2860_PCIE_JTAG 0x0010 + +/* SCH/DMA registers */ +#define RT2860_INT_STATUS 0x0200 +#define RT2860_INT_MASK 0x0204 +#define RT2860_WPDMA_GLO_CFG 0x0208 +#define RT2860_WPDMA_RST_IDX 0x020c +#define RT2860_DELAY_INT_CFG 0x0210 +#define RT2860_WMM_AIFSN_CFG 0x0214 +#define RT2860_WMM_CWMIN_CFG 0x0218 +#define RT2860_WMM_CWMAX_CFG 0x021c +#define RT2860_WMM_TXOP0_CFG 0x0220 +#define RT2860_WMM_TXOP1_CFG 0x0224 +#define RT2860_GPIO_CTRL 0x0228 +#define RT2860_MCU_CMD_REG 0x022c +#define RT2860_TX_BASE_PTR(qid) (0x0230 + (qid) * 16) +#define RT2860_TX_MAX_CNT(qid) (0x0234 + (qid) * 16) +#define RT2860_TX_CTX_IDX(qid) (0x0238 + (qid) * 16) +#define RT2860_TX_DTX_IDX(qid) (0x023c + (qid) * 16) +#define RT2860_RX_BASE_PTR 0x0290 +#define RT2860_RX_MAX_CNT 0x0294 +#define RT2860_RX_CALC_IDX 0x0298 +#define RT2860_FS_DRX_IDX 0x029c +#define RT2860_US_CYC_CNT 0x02a4 + +/* PBF registers */ +#define RT2860_SYS_CTRL 0x0400 +#define RT2860_HOST_CMD 0x0404 +#define RT2860_PBF_CFG 0x0408 +#define RT2860_MAX_PCNT 0x040c +#define RT2860_BUF_CTRL 0x0410 +#define RT2860_MCU_INT_STA 0x0414 +#define RT2860_MCU_INT_ENA 0x0418 +#define RT2860_TXQ_IO(qid) (0x041c + (qid) * 4) +#define RT2860_RX0Q_IO 0x0424 +#define RT2860_BCN_OFFSET0 0x042c +#define RT2860_BCN_OFFSET1 0x0430 +#define RT2860_TXRXQ_STA 0x0434 +#define RT2860_TXRXQ_PCNT 0x0438 +#define RT2860_PBF_DBG 0x043c +#define RT2860_CAP_CTRL 0x0440 + +/* MAC registers */ +#define RT2860_ASIC_VER_ID 0x1000 +#define RT2860_MAC_SYS_CTRL 0x1004 +#define RT2860_MAC_ADDR_DW0 0x1008 +#define RT2860_MAC_ADDR_DW1 0x100c +#define RT2860_MAC_BSSID_DW0 0x1010 +#define RT2860_MAC_BSSID_DW1 0x1014 +#define RT2860_MAX_LEN_CFG 0x1018 +#define RT2860_BBP_CSR_CFG 0x101c +#define RT2860_RF_CSR_CFG0 0x1020 +#define RT2860_RF_CSR_CFG1 0x1024 +#define RT2860_RF_CSR_CFG2 0x1028 +#define RT2860_LED_CFG 0x102c + +/* MAC Timing control registers */ +#define RT2860_XIFS_TIME_CFG 0x1100 +#define RT2860_BKOFF_SLOT_CFG 0x1104 +#define RT2860_NAV_TIME_CFG 0x1108 +#define RT2860_CH_TIME_CFG 0x110c +#define RT2860_PBF_LIFE_TIMER 0x1110 +#define RT2860_BCN_TIME_CFG 0x1114 +#define RT2860_TBTT_SYNC_CFG 0x1118 +#define RT2860_TSF_TIMER_DW0 0x111c +#define RT2860_TSF_TIMER_DW1 0x1120 +#define RT2860_TBTT_TIMER 0x1124 +#define RT2860_INT_TIMER_CFG 0x1128 +#define RT2860_INT_TIMER_EN 0x112c +#define RT2860_CH_IDLE_TIME 0x1130 + +/* MAC Power Save configuration registers */ +#define RT2860_MAC_STATUS_REG 0x1200 +#define RT2860_PWR_PIN_CFG 0x1204 +#define RT2860_AUTO_WAKEUP_CFG 0x1208 + +/* MAC TX configuration registers */ +#define RT2860_EDCA_AC_CFG(aci) (0x1300 + (aci) * 4) +#define RT2860_EDCA_TID_AC_MAP 0x1310 +#define RT2860_TX_PWR_CFG(ridx) (0x1314 + (ridx) * 4) +#define RT2860_TX_PIN_CFG 0x1328 +#define RT2860_TX_BAND_CFG 0x132c +#define RT2860_TX_SW_CFG0 0x1330 +#define RT2860_TX_SW_CFG1 0x1334 +#define RT2860_TX_SW_CFG2 0x1338 +#define RT2860_TXOP_THRES_CFG 0x133c +#define RT2860_TXOP_CTRL_CFG 0x1340 +#define RT2860_TX_RTS_CFG 0x1344 +#define RT2860_TX_TIMEOUT_CFG 0x1348 +#define RT2860_TX_RTY_CFG 0x134c +#define RT2860_TX_LINK_CFG 0x1350 +#define RT2860_HT_FBK_CFG0 0x1354 +#define RT2860_HT_FBK_CFG1 0x1358 +#define RT2860_LG_FBK_CFG0 0x135c +#define RT2860_LG_FBK_CFG1 0x1360 +#define RT2860_CCK_PROT_CFG 0x1364 +#define RT2860_OFDM_PROT_CFG 0x1368 +#define RT2860_MM20_PROT_CFG 0x136c +#define RT2860_MM40_PROT_CFG 0x1370 +#define RT2860_GF20_PROT_CFG 0x1374 +#define RT2860_GF40_PROT_CFG 0x1378 +#define RT2860_EXP_CTS_TIME 0x137c +#define RT2860_EXP_ACK_TIME 0x1380 + +/* MAC RX configuration registers */ +#define RT2860_RX_FILTR_CFG 0x1400 +#define RT2860_AUTO_RSP_CFG 0x1404 +#define RT2860_LEGACY_BASIC_RATE 0x1408 +#define RT2860_HT_BASIC_RATE 0x140c +#define RT2860_HT_CTRL_CFG 0x1410 +#define RT2860_SIFS_COST_CFG 0x1414 +#define RT2860_RX_PARSER_CFG 0x1418 + +/* MAC Security configuration registers */ +#define RT2860_TX_SEC_CNT0 0x1500 +#define RT2860_RX_SEC_CNT0 0x1504 +#define RT2860_CCMP_FC_MUTE 0x1508 + +/* MAC HCCA/PSMP configuration registers */ +#define RT2860_TXOP_HLDR_ADDR0 0x1600 +#define RT2860_TXOP_HLDR_ADDR1 0x1604 +#define RT2860_TXOP_HLDR_ET 0x1608 +#define RT2860_QOS_CFPOLL_RA_DW0 0x160c +#define RT2860_QOS_CFPOLL_A1_DW1 0x1610 +#define RT2860_QOS_CFPOLL_QC 0x1614 + +/* MAC Statistics Counters */ +#define RT2860_RX_STA_CNT0 0x1700 +#define RT2860_RX_STA_CNT1 0x1704 +#define RT2860_RX_STA_CNT2 0x1708 +#define RT2860_TX_STA_CNT0 0x170c +#define RT2860_TX_STA_CNT1 0x1710 +#define RT2860_TX_STA_CNT2 0x1714 +#define RT2860_TX_STAT_FIFO 0x1718 + +#define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8) +#define RT2860_FW_BASE 0x2000 +#define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32) +#define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8) +#define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 8) +#define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Memory between MCU and host */ +#define RT2860_H2M_MAILBOX 0x7010 +#define RT2860_H2M_BBPAGENT 0x7028 +#define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512) + + +/* possible flags for register RT2860_PCI_EECTRL */ +#define RT2860_C (1 << 0) +#define RT2860_S (1 << 1) +#define RT2860_D (1 << 2) +#define RT2860_SHIFT_D 2 +#define RT2860_Q (1 << 3) +#define RT2860_SHIFT_Q 3 + +/* possible flags for registers INT_STATUS/INT_MASK */ +#define RT2860_TX_COHERENT (1 << 17) +#define RT2860_RX_COHERENT (1 << 16) +#define RT2860_MAC_INT_4 (1 << 15) +#define RT2860_MAC_INT_3 (1 << 14) +#define RT2860_MAC_INT_2 (1 << 13) +#define RT2860_MAC_INT_1 (1 << 12) +#define RT2860_MAC_INT_0 (1 << 11) +#define RT2860_TX_RX_COHERENT (1 << 10) +#define RT2860_MCU_CMD_INT (1 << 9) +#define RT2860_TX_DONE_INT5 (1 << 8) +#define RT2860_TX_DONE_INT4 (1 << 7) +#define RT2860_TX_DONE_INT3 (1 << 6) +#define RT2860_TX_DONE_INT2 (1 << 5) +#define RT2860_TX_DONE_INT1 (1 << 4) +#define RT2860_TX_DONE_INT0 (1 << 3) +#define RT2860_RX_DONE_INT (1 << 2) +#define RT2860_TX_DLY_INT (1 << 1) +#define RT2860_RX_DLY_INT (1 << 0) + +/* possible flags for register WPDMA_GLO_CFG */ +#define RT2860_HDR_SEG_LEN_SHIFT 8 +#define RT2860_BIG_ENDIAN (1 << 7) +#define RT2860_TX_WB_DDONE (1 << 6) +#define RT2860_WPDMA_BT_SIZE_SHIFT 4 +#define RT2860_WPDMA_BT_SIZE16 0 +#define RT2860_WPDMA_BT_SIZE32 1 +#define RT2860_WPDMA_BT_SIZE64 2 +#define RT2860_WPDMA_BT_SIZE128 3 +#define RT2860_RX_DMA_BUSY (1 << 3) +#define RT2860_RX_DMA_EN (1 << 2) +#define RT2860_TX_DMA_BUSY (1 << 1) +#define RT2860_TX_DMA_EN (1 << 0) + +/* possible flags for register DELAY_INT_CFG */ +#define RT2860_TXDLY_INT_EN (1 << 31) +#define RT2860_TXMAX_PINT_SHIFT 24 +#define RT2860_TXMAX_PTIME_SHIFT 16 +#define RT2860_RXDLY_INT_EN (1 << 15) +#define RT2860_RXMAX_PINT_SHIFT 8 +#define RT2860_RXMAX_PTIME_SHIFT 0 + +/* possible flags for register GPIO_CTRL */ +#define RT2860_GPIO_D_SHIFT 8 +#define RT2860_GPIO_O_SHIFT 0 + +/* possible flags for register US_CYC_CNT */ +#define RT2860_TEST_EN (1 << 24) +#define RT2860_TEST_SEL_SHIFT 16 +#define RT2860_BT_MODE_EN (1 << 8) +#define RT2860_US_CYC_CNT_SHIFT 0 + +/* possible flags for register SYS_CTRL */ +#define RT2860_HST_PM_SEL (1 << 16) +#define RT2860_CAP_MODE (1 << 14) +#define RT2860_PME_OEN (1 << 13) +#define RT2860_CLKSELECT (1 << 12) +#define RT2860_PBF_CLK_EN (1 << 11) +#define RT2860_MAC_CLK_EN (1 << 10) +#define RT2860_DMA_CLK_EN (1 << 9) +#define RT2860_MCU_READY (1 << 7) +#define RT2860_ASY_RESET (1 << 4) +#define RT2860_PBF_RESET (1 << 3) +#define RT2860_MAC_RESET (1 << 2) +#define RT2860_DMA_RESET (1 << 1) +#define RT2860_MCU_RESET (1 << 0) + +/* possible values for register HOST_CMD */ +#define RT2860_MCU_CMD_BBP 0x80 + +/* possible flags for register PBF_CFG */ +#define RT2860_TX1Q_NUM_SHIFT 21 +#define RT2860_TX2Q_NUM_SHIFT 16 +#define RT2860_NULL0_MODE (1 << 15) +#define RT2860_NULL1_MODE (1 << 14) +#define RT2860_RX_DROP_MODE (1 << 13) +#define RT2860_TX0Q_MANUAL (1 << 12) +#define RT2860_TX1Q_MANUAL (1 << 11) +#define RT2860_TX2Q_MANUAL (1 << 10) +#define RT2860_RX0Q_MANUAL (1 << 9) +#define RT2860_HCCA_EN (1 << 8) +#define RT2860_TX0Q_EN (1 << 4) +#define RT2860_TX1Q_EN (1 << 3) +#define RT2860_TX2Q_EN (1 << 2) +#define RT2860_RX0Q_EN (1 << 1) + +/* possible flags for register BUF_CTRL */ +#define RT2860_WRITE_TXQ(qid) (1 << (11 - (qid))) +#define RT2860_NULL0_KICK (1 << 7) +#define RT2860_NULL1_KICK (1 << 6) +#define RT2860_BUF_RESET (1 << 5) +#define RT2860_READ_TXQ(qid) (1 << (3 - (qid)) +#define RT2860_READ_RX0Q (1 << 0) + +/* possible flags for registers MCU_INT_STA/MCU_INT_ENA */ +#define RT2860_MCU_MAC_INT_8 (1 << 24) +#define RT2860_MCU_MAC_INT_7 (1 << 23) +#define RT2860_MCU_MAC_INT_6 (1 << 22) +#define RT2860_MCU_MAC_INT_4 (1 << 20) +#define RT2860_MCU_MAC_INT_3 (1 << 19) +#define RT2860_MCU_MAC_INT_2 (1 << 18) +#define RT2860_MCU_MAC_INT_1 (1 << 17) +#define RT2860_MCU_MAC_INT_0 (1 << 16) +#define RT2860_DTX0_INT (1 << 11) +#define RT2860_DTX1_INT (1 << 10) +#define RT2860_DTX2_INT (1 << 9) +#define RT2860_DRX0_INT (1 << 8) +#define RT2860_HCMD_INT (1 << 7) +#define RT2860_N0TX_INT (1 << 6) +#define RT2860_N1TX_INT (1 << 5) +#define RT2860_BCNTX_INT (1 << 4) +#define RT2860_MTX0_INT (1 << 3) +#define RT2860_MTX1_INT (1 << 2) +#define RT2860_MTX2_INT (1 << 1) +#define RT2860_MRX0_INT (1 << 0) + +/* possible flags for register CAP_CTRL */ +#define RT2860_CAP_ADC_FEQ (1 << 31) +#define RT2860_CAP_START (1 << 30) +#define RT2860_MAN_TRIG (1 << 29) +#define RT2860_TRIG_OFFSET_SHIFT 16 +#define RT2860_START_ADDR_SHIFT 0 + +/* possible flags for register MAC_SYS_CTRL */ +#define RT2860_RX_TS_EN (1 << 7) +#define RT2860_WLAN_HALT_EN (1 << 6) +#define RT2860_PBF_LOOP_EN (1 << 5) +#define RT2860_CONT_TX_TEST (1 << 4) +#define RT2860_MAC_RX_EN (1 << 3) +#define RT2860_MAC_TX_EN (1 << 2) +#define RT2860_BBP_HRST (1 << 1) +#define RT2860_MAC_SRST (1 << 0) + +/* possible flags for register MAC_BSSID_DW1 */ +#define RT2860_MULTI_BCN_NUM_SHIFT 18 +#define RT2860_MULTI_BSSID_MODE_SHIFT 16 + +/* possible flags for register MAX_LEN_CFG */ +#define RT2860_MIN_MPDU_LEN_SHIFT 16 +#define RT2860_MAX_PSDU_LEN_SHIFT 12 +#define RT2860_MAX_PSDU_LEN8K 0 +#define RT2860_MAX_PSDU_LEN16K 1 +#define RT2860_MAX_PSDU_LEN32K 2 +#define RT2860_MAX_PSDU_LEN64K 3 +#define RT2860_MAX_MPDU_LEN_SHIFT 0 + +/* possible flags for registers BBP_CSR_CFG/H2M_BBPAGENT */ +#define RT2860_BBP_RW_PARALLEL (1 << 19) +#define RT2860_BBP_PAR_DUR_112_5 (1 << 18) +#define RT2860_BBP_CSR_KICK (1 << 17) +#define RT2860_BBP_CSR_READ (1 << 16) +#define RT2860_BBP_ADDR_SHIFT 8 +#define RT2860_BBP_DATA_SHIFT 0 + +/* possible flags for register RF_CSR_CFG0 */ +#define RT2860_RF_REG_CTRL (1 << 31) +#define RT2860_RF_LE_SEL1 (1 << 30) +#define RT2860_RF_LE_STBY (1 << 29) +#define RT2860_RF_REG_WIDTH_SHIFT 24 +#define RT2860_RF_REG_0_SHIFT 0 + +/* possible flags for register RF_CSR_CFG1 */ +#define RT2860_RF_DUR_5 (1 << 24) +#define RT2860_RF_REG_1_SHIFT 0 + +/* possible flags for register LED_CFG */ +#define RT2860_LED_POL (1 << 30) +#define RT2860_Y_LED_MODE_SHIFT 28 +#define RT2860_G_LED_MODE_SHIFT 26 +#define RT2860_R_LED_MODE_SHIFT 24 +#define RT2860_LED_MODE_OFF 0 +#define RT2860_LED_MODE_BLINK_TX 1 +#define RT2860_LED_MODE_SLOW_BLINK 2 +#define RT2860_LED_MODE_ON 3 +#define RT2860_SLOW_BLK_TIME_SHIFT 16 +#define RT2860_LED_OFF_TIME_SHIFT 8 +#define RT2860_LED_ON_TIME_SHIFT 0 + +/* possible flags for register XIFS_TIME_CFG */ +#define RT2860_BB_RXEND_EN (1 << 29) +#define RT2860_EIFS_TIME_SHIFT 20 +#define RT2860_OFDM_XIFS_TIME_SHIFT 16 +#define RT2860_OFDM_SIFS_TIME_SHIFT 8 +#define RT2860_CCK_SIFS_TIME_SHIFT 0 + +/* possible flags for register BKOFF_SLOT_CFG */ +#define RT2860_CC_DELAY_TIME_SHIFT 8 +#define RT2860_SLOT_TIME 0 + +/* possible flags for register NAV_TIME_CFG */ +#define RT2860_NAV_UPD (1 << 31) +#define RT2860_NAV_UPD_VAL_SHIFT 16 +#define RT2860_NAV_CLR_EN (1 << 15) +#define RT2860_NAV_TIMER_SHIFT 0 + +/* possible flags for register CH_TIME_CFG */ +#define RT2860_EIFS_AS_CH_BUSY (1 << 4) +#define RT2860_NAV_AS_CH_BUSY (1 << 3) +#define RT2860_RX_AS_CH_BUSY (1 << 2) +#define RT2860_TX_AS_CH_BUSY (1 << 1) +#define RT2860_CH_STA_TIMER_EN (1 << 0) + +/* possible values for register BCN_TIME_CFG */ +#define RT2860_TSF_INS_COMP_SHIFT 24 +#define RT2860_BCN_TX_EN (1 << 20) +#define RT2860_TBTT_TIMER_EN (1 << 19) +#define RT2860_TSF_SYNC_MODE_SHIFT 17 +#define RT2860_TSF_SYNC_MODE_DIS 0 +#define RT2860_TSF_SYNC_MODE_STA 1 +#define RT2860_TSF_SYNC_MODE_IBSS 2 +#define RT2860_TSF_SYNC_MODE_HOSTAP 3 +#define RT2860_TSF_TIMER_EN (1 << 16) +#define RT2860_BCN_INTVAL_SHIFT 0 + +/* possible flags for register TBTT_SYNC_CFG */ +#define RT2860_BCN_CWMIN_SHIFT 20 +#define RT2860_BCN_AIFSN_SHIFT 16 +#define RT2860_BCN_EXP_WIN_SHIFT 8 +#define RT2860_TBTT_ADJUST_SHIFT 0 + +/* possible flags for register INT_TIMER_CFG */ +#define RT2860_GP_TIMER_SHIFT 16 +#define RT2860_PRE_TBTT_TIMER_SHIFT 0 + +/* possible flags for register INT_TIMER_EN */ +#define RT2860_GP_TIMER_EN (1 << 1) +#define RT2860_PRE_TBTT_INT_EN (1 << 0) + +/* possible flags for register MAC_STATUS_REG */ +#define RT2860_RX_STATUS_BUSY (1 << 1) +#define RT2860_TX_STATUS_BUSY (1 << 0) + +/* possible flags for register PWR_PIN_CFG */ +#define RT2860_IO_ADDA_PD (1 << 3) +#define RT2860_IO_PLL_PD (1 << 2) +#define RT2860_IO_RA_PE (1 << 1) +#define RT2860_IO_RF_PE (1 << 0) + +/* possible flags for register AUTO_WAKEUP_CFG */ +#define RT2860_AUTO_WAKEUP_EN (1 << 15) +#define RT2860_SLEEP_TBTT_NUM_SHIFT 8 +#define RT2860_WAKEUP_LEAD_TIME_SHIFT 0 + +/* possible flags for register TX_PIN_CFG */ +#define RT2860_TRSW_POL (1 << 19) +#define RT2860_TRSW_EN (1 << 18) +#define RT2860_RFTR_POL (1 << 17) +#define RT2860_RFTR_EN (1 << 16) +#define RT2860_LNA_PE_G1_POL (1 << 15) +#define RT2860_LNA_PE_A1_POL (1 << 14) +#define RT2860_LNA_PE_G0_POL (1 << 13) +#define RT2860_LNA_PE_A0_POL (1 << 12) +#define RT2860_LNA_PE_G1_EN (1 << 11) +#define RT2860_LNA_PE_A1_EN (1 << 10) +#define RT2860_LNA_PE_G0_EN (1 << 9) +#define RT2860_LNA_PE_A0_EN (1 << 8) +#define RT2860_PA_PE_G1_POL (1 << 7) +#define RT2860_PA_PE_A1_POL (1 << 6) +#define RT2860_PA_PE_G0_POL (1 << 5) +#define RT2860_PA_PE_A0_POL (1 << 4) +#define RT2860_PA_PE_G1_EN (1 << 3) +#define RT2860_PA_PE_A1_EN (1 << 2) +#define RT2860_PA_PE_G0_EN (1 << 1) +#define RT2860_PA_PE_A0_EN (1 << 0) + +/* possible flags for register TX_BAND_CFG */ +#define RT2860_5G_BAND_SEL_N (1 << 2) +#define RT2860_5G_BAND_SEL_P (1 << 1) +#define RT2860_TX_BAND_SEL (1 << 0) + +/* possible flags for register TX_SW_CFG0 */ +#define RT2860_DLY_RFTR_EN_SHIFT 24 +#define RT2860_DLY_TRSW_EN_SHIFT 16 +#define RT2860_DLY_PAPE_EN_SHIFT 8 +#define RT2860_DLY_TXPE_EN_SHIFT 0 + +/* possible flags for register TX_SW_CFG1 */ +#define RT2860_DLY_RFTR_DIS_SHIFT 16 +#define RT2860_DLY_TRSW_DIS_SHIFT 8 +#define RT2860_DLY_PAPE_DIS SHIFT 0 + +/* possible flags for register TX_SW_CFG2 */ +#define RT2860_DLY_LNA_EN_SHIFT 24 +#define RT2860_DLY_LNA_DIS_SHIFT 16 +#define RT2860_DLY_DAC_EN_SHIFT 8 +#define RT2860_DLY_DAC_DIS_SHIFT 0 + +/* possible flags for register TXOP_THRES_CFG */ +#define RT2860_TXOP_REM_THRES_SHIFT 24 +#define RT2860_CF_END_THRES_SHIFT 16 +#define RT2860_RDG_IN_THRES 8 +#define RT2860_RDG_OUT_THRES 0 + +/* possible flags for register TXOP_CTRL_CFG */ +#define RT2860_EXT_CW_MIN_SHIFT 16 +#define RT2860_EXT_CCA_DLY_SHIFT 8 +#define RT2860_EXT_CCA_EN (1 << 7) +#define RT2860_LSIG_TXOP_EN (1 << 6) +#define RT2860_TXOP_TRUN_EN_MIMOPS (1 << 4) +#define RT2860_TXOP_TRUN_EN_TXOP (1 << 3) +#define RT2860_TXOP_TRUN_EN_RATE (1 << 2) +#define RT2860_TXOP_TRUN_EN_AC (1 << 1) +#define RT2860_TXOP_TRUN_EN_TIMEOUT (1 << 0) + +/* possible flags for register TX_RTS_CFG */ +#define RT2860_RTS_FBK_EN (1 << 24) +#define RT2860_RTS_THRES_SHIFT 8 +#define RT2860_RTS_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_TIMEOUT_CFG */ +#define RT2860_TXOP_TIMEOUT_SHIFT 16 +#define RT2860_RX_ACK_TIMEOUT_SHIFT 8 +#define RT2860_MPDU_LIFE_TIME_SHIFT 4 + +/* possible flags for register TX_RTY_CFG */ +#define RT2860_TX_AUTOFB_EN (1 << 30) +#define RT2860_AGG_RTY_MODE_TIMER (1 << 29) +#define RT2860_NAG_RTY_MODE_TIMER (1 << 28) +#define RT2860_LONG_RTY_THRES_SHIFT 16 +#define RT2860_LONG_RTY_LIMIT_SHIFT 8 +#define RT2860_SHORT_RTY_LIMIT_SHIFT 0 + +/* possible flags for register TX_LINK_CFG */ +#define RT2860_REMOTE_MFS_SHIFT 24 +#define RT2860_REMOTE_MFB_SHIFT 16 +#define RT2860_TX_CFACK_EN (1 << 12) +#define RT2860_TX_RDG_EN (1 << 11) +#define RT2860_TX_MRQ_EN (1 << 10) +#define RT2860_REMOTE_UMFS_EN (1 << 9) +#define RT2860_TX_MFB_EN (1 << 8) +#define RT2860_REMOTE_MFB_LT_SHIFT 0 + +/* possible flags for registers *_PROT_CFG */ +#define RT2860_RTSTH_EN (1 << 26) +#define RT2860_TXOP_ALLOW_GF40 (1 << 25) +#define RT2860_TXOP_ALLOW_GF20 (1 << 24) +#define RT2860_TXOP_ALLOW_MM40 (1 << 23) +#define RT2860_TXOP_ALLOW_MM20 (1 << 22) +#define RT2860_TXOP_ALLOW_OFDM (1 << 21) +#define RT2860_TXOP_ALLOW_CCK (1 << 20) +#define RT2860_PROT_NAV_SHIFT 18 +#define RT2860_PROT_NAV_NONE 0 +#define RT2860_PROT_NAV_SHORT 1 +#define RT2860_PROT_NAV_LONG 2 +#define RT2860_PROT_CTRL_SHIFT 16 +#define RT2860_PROT_CTRL_NONE 0 +#define RT2860_PROT_CTRL_RTS_CTS 1 +#define RT2860_PROT_CTRL_CTS 2 +#define RT2860_PROT_RATE_SHIFT 0 + +/* possible flags for registers EXP_{CTS,ACK}_TIME */ +#define RT2860_EXP_OFDM_TIME_SHIFT 16 +#define RT2860_EXP_CCK_TIME_SHIFT 0 + +/* possible flags for register RX_FILTR_CFG */ +#define RT2860_DROP_CTRL_RSV (1 << 16) +#define RT2860_DROP_BAR (1 << 15) +#define RT2860_DROP_BA (1 << 14) +#define RT2860_DROP_PSPOLL (1 << 13) +#define RT2860_DROP_RTS (1 << 12) +#define RT2860_DROP_CTS (1 << 11) +#define RT2860_DROP_ACK (1 << 10) +#define RT2860_DROP_CFEND (1 << 9) +#define RT2860_DROP_CFACK (1 << 8) +#define RT2860_DROP_DUPL (1 << 7) +#define RT2860_DROP_BC (1 << 6) +#define RT2860_DROP_MC (1 << 5) +#define RT2860_DROP_VER_ERR (1 << 4) +#define RT2860_DROP_NOT_MYBSS (1 << 3) +#define RT2860_DROP_UC_NOME (1 << 2) +#define RT2860_DROP_PHY_ERR (1 << 1) +#define RT2860_DROP_CRC_ERR (1 << 0) + +/* possible flags for register AUTO_RSP_CFG */ +#define RT2860_CTRL_PWR_BIT (1 << 7) +#define RT2860_BAC_ACK_POLICY (1 << 6) +#define RT2860_CCK_SHORT_EN (1 << 4) +#define RT2860_CTS_40M_REF_EN (1 << 3) +#define RT2860_CTS_40M_MODE_EN (1 << 2) +#define RT2860_BAC_ACKPOLICY_EN (1 << 1) +#define RT2860_AUTO_RSP_EN (1 << 0) + +/* possible flags for register SIFS_COST_CFG */ +#define RT2860_OFDM_SIFS_COST_SHIFT 8 +#define RT2860_CCK_SIFS_COST_SHIFT 0 + +/* possible flags for register TXOP_HLDR_ET */ +#define RT2860_TXOP_ETM1_EN (1 << 25) +#define RT2860_TXOP_ETM0_EN (1 << 24) +#define RT2860_TXOP_ETM_THRES_SHIFT 16 +#define RT2860_TXOP_ETO_EN (1 << 8) +#define RT2860_TXOP_ETO_THRES_SHIFT 1 +#define RT2860_PER_RX_RST_EN (1 << 0) + +/* possible flags for register TX_STAT_FIFO */ +#define RT2860_TXQ_RATE_SHIFT 16 +#define RT2860_TXQ_WCID_SHIFT 8 +#define RT2860_TXQ_ACKREQ (1 << 7) +#define RT2860_TXQ_AGG (1 << 6) +#define RT2860_TXQ_OK (1 << 5) +#define RT2860_TXQ_PID_SHIFT 1 +#define RT2860_TXQ_VLD (1 << 0) + +/* possible flags for register H2M_MAILBOX */ +#define RT2860_H2M_BUSY (1 << 24) +#define RT2860_TOKEN_NO_INTR 0xff + + +/* TX descriptor */ +struct rt2860_txd { + uint32_t sdp0; /* Segment Data Pointer 0 */ + uint16_t sdl1; /* Segment Data Length 1 */ +#define RT2860_TX_BURST (1 << 15) +#define RT2860_TX_LS1 (1 << 14) /* SDP1 is the last segment */ + + uint16_t sdl0; /* Segment Data Length 0 */ +#define RT2860_TX_DDONE (1 << 15) +#define RT2860_TX_LS0 (1 << 14) /* SDP0 is the last segment */ + + uint32_t sdp1; /* Segment Data Pointer 1 */ + uint8_t reserved[3]; + uint8_t flags; +#define RT2860_TX_QSEL_SHIFT 1 +#define RT2860_TX_QSEL_MGMT (0 << 1) +#define RT2860_TX_QSEL_HCCA (1 << 1) +#define RT2860_TX_QSEL_EDCA (2 << 1) +#define RT2860_TX_WIV (1 << 0) +} __packed; + +/* TX Wireless Information */ +struct rt2860_txwi { + uint8_t flags; +#define RT2860_TX_MPDU_DSITY_SHIFT 5 +#define RT2860_TX_AMPDU (1 << 4) +#define RT2860_TX_TS (1 << 3) +#define RT2860_TX_CFACK (1 << 2) +#define RT2860_TX_MMPS (1 << 1) +#define RT2860_TX_FRAG (1 << 0) + + uint8_t txop; +#define RT2860_TX_TXOP_HT 0 +#define RT2860_TX_TXOP_PIFS 1 +#define RT2860_TX_TXOP_SIFS 2 +#define RT2860_TX_TXOP_BACKOFF 3 + + uint16_t phy; +#define RT2860_TX_CCK (0 << 14) +#define RT2860_TX_OFDM (1 << 14) +#define RT2860_TX_HT (2 << 14) +#define RT2860_TX_HT_GF (3 << 14) +#define RT2860_TX_SGI (1 << 8) +#define RT2860_TX_BW (1 << 7) + + uint8_t xflags; +#define RT2860_TX_BAWINSIZE_SHIFT 2 +#define RT2860_TX_NSEQ (1 << 1) +#define RT2860_TX_ACK (1 << 0) + + uint8_t wcid; /* Wireless Client ID */ + uint16_t len; +#define RT2860_TX_PID_SHIFT 12 + + uint32_t iv; + uint32_t eiv; + + struct ieee80211_htframe wh; + uint16_t pad; +} __packed; + +/* RX descriptor */ +struct rt2860_rxd { + uint32_t sdp0; + uint16_t sdl1; /* unused */ + uint16_t sdl0; +#define RT2860_RX_DDONE (1 << 15) +#define RT2860_RX_LS0 (1 << 14) + + uint32_t sdp1; /* unused */ + uint32_t flags; +#define RT2860_RX_DEC (1 << 16) +#define RT2860_RX_AMPDU (1 << 15) +#define RT2860_RX_L2PAD (1 << 14) +#define RT2860_RX_RSSI (1 << 13) +#define RT2860_RX_HTC (1 << 12) +#define RT2860_RX_AMSDU (1 << 11) +#define RT2860_RX_MICERR (1 << 10) +#define RT2860_RX_ICVERR (1 << 9) +#define RT2860_RX_CRCERR (1 << 8) +#define RT2860_RX_MYBSS (1 << 7) +#define RT2860_RX_BC (1 << 6) +#define RT2860_RX_MC (1 << 5) +#define RT2860_RX_UC2ME (1 << 4) +#define RT2860_RX_FRAG (1 << 3) +#define RT2860_RX_NULL (1 << 2) +#define RT2860_RX_DATA (1 << 1) +#define RT2860_RX_BA (1 << 0) +} __packed; + +/* RX Wireless Information */ +struct rt2860_rxwi { + uint8_t wcid; + uint8_t keyidx; +#define RT2860_RX_UDF_SHIFT 5 +#define RT2860_RX_BSS_IDX_SHIFT 2 + + uint16_t len; +#define RT2860_RX_TID_SHIFT 12 + + uint16_t seq; + uint16_t phy; + uint8_t rssi[3]; + uint8_t reserved1; + uint8_t snr[2]; + uint16_t reserved2; +} __packed; + +#define RAL_RF1 0 +#define RAL_RF2 2 +#define RAL_RF3 1 +#define RAL_RF4 3 + +#define RT2860_RF_2820 1 /* 2T3R */ +#define RT2860_RF_2850 2 /* dual-band 2T3R */ +#define RT2860_RF_2720 3 /* 1T2R */ +#define RT2860_RF_2750 4 /* dual-band 1T2R */ + +#define RT2860_EEPROM_DELAY 1 /* minimum hold time (microsecond) */ + +#define RT2860_EEPROM_VERSION 0x01 +#define RT2860_EEPROM_MAC01 0x02 +#define RT2860_EEPROM_MAC23 0x03 +#define RT2860_EEPROM_MAC45 0x04 +#define RT2860_EEPROM_ANTENNA 0x1a +#define RT2860_EEPROM_CONFIG 0x1b +#define RT2860_EEPROM_COUNTRY 0x1c +#define RT2860_EEPROM_FREQ 0x1d +#define RT2860_EEPROM_LNA 0x22 +#define RT2860_EEPROM_RSSI1_2GHZ 0x23 +#define RT2860_EEPROM_RSSI2_2GHZ 0x24 +#define RT2860_EEPROM_RSSI1_5GHZ 0x25 +#define RT2860_EEPROM_RSSI2_5GHZ 0x26 +#define RT2860_EEPROM_DELTAPWR 0x28 +#define RT2860_EEPROM_PWR2GHZ_BASE1 0x29 +#define RT2860_EEPROM_PWR2GHZ_BASE2 0x30 +#define RT2860_EEPROM_TSSI1_2GHZ 0x37 +#define RT2860_EEPROM_TSSI2_2GHZ 0x38 +#define RT2860_EEPROM_TSSI3_2GHZ 0x39 +#define RT2860_EEPROM_TSSI4_2GHZ 0x3a +#define RT2860_EEPROM_TSSI5_2GHZ 0x3b +#define RT2860_EEPROM_PWR5GHZ_BASE1 0x3c +#define RT2860_EEPROM_PWR5GHZ_BASE2 0x53 +#define RT2860_EEPROM_TSSI1_5GHZ 0x6a +#define RT2860_EEPROM_TSSI2_5GHZ 0x6b +#define RT2860_EEPROM_TSSI3_5GHZ 0x6c +#define RT2860_EEPROM_TSSI4_5GHZ 0x6d +#define RT2860_EEPROM_TSSI5_5GHZ 0x6e +#define RT2860_EEPROM_RPWR 0x6f +#define RT2860_EEPROM_BBP_BASE 0x78 + + +/* + * Control and status registers access macros. + */ +#define RAL_READ(sc, reg) \ + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + +#define RAL_WRITE(sc, reg, val) \ + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +#define RAL_WRITE_1(sc, reg, val) \ + bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + +#define RAL_RW_BARRIER_1(sc, reg) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, (reg), 1, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + +#define RAL_WRITE_REGION_1(sc, offset, datap, count) \ + bus_space_write_region_1((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + +#define RAL_SET_REGION_4(sc, offset, val, count) \ + bus_space_set_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (val), (count)) + +/* + * EEPROM access macro + */ +#define RT2860_EEPROM_CTL(sc, val) do { \ + RAL_WRITE((sc), RT2860_PCI_EECTRL, (val)); \ + DELAY(RT2860_EEPROM_DELAY); \ +} while (/* CONSTCOND */0) + +/* + * Default values for MAC registers; values taken from the reference driver. + */ +#define RT2860_DEF_MAC \ + { RT2860_BCN_OFFSET0, 0xf8f0e8e0 }, \ + { RT2860_LEGACY_BASIC_RATE, 0x0000013f }, \ + { RT2860_HT_BASIC_RATE, 0x00008003 }, \ + { RT2860_MAC_SYS_CTRL, 0x00000000 }, \ + { RT2860_BKOFF_SLOT_CFG, 0x00000209 }, \ + { RT2860_TX_SW_CFG0, 0x00040a06 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x00182090 }, \ + { RT2860_LED_CFG, 0x7f031e46 }, \ + { RT2860_WMM_AIFSN_CFG, 0x00002273 }, \ + { RT2860_WMM_CWMIN_CFG, 0x00002344 }, \ + { RT2860_WMM_CWMAX_CFG, 0x000034aa }, \ + { RT2860_MAX_PCNT, 0x1f3fbf9f }, \ + { RT2860_TX_RTY_CFG, 0x6bb80408 }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f54084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x000024bf }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca } + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +#define RT2860_DEF_BBP \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 69, 0x12 }, \ + { 73, 0x10 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x99 }, \ + { 105, 0x01 } + +/* + * Default settings for RF registers; values derived from the reference driver. + */ +#define RT2860_RF2850 \ + { 1, 0x100bb3, 0x1301e1, 0x05a014, 0x001402 }, \ + { 2, 0x100bb3, 0x1301e1, 0x05a014, 0x001407 }, \ + { 3, 0x100bb3, 0x1301e2, 0x05a014, 0x001402 }, \ + { 4, 0x100bb3, 0x1301e2, 0x05a014, 0x001407 }, \ + { 5, 0x100bb3, 0x1301e3, 0x05a014, 0x001402 }, \ + { 6, 0x100bb3, 0x1301e3, 0x05a014, 0x001407 }, \ + { 7, 0x100bb3, 0x1301e4, 0x05a014, 0x001402 }, \ + { 8, 0x100bb3, 0x1301e4, 0x05a014, 0x001407 }, \ + { 9, 0x100bb3, 0x1301e5, 0x05a014, 0x001402 }, \ + { 10, 0x100bb3, 0x1301e5, 0x05a014, 0x001407 }, \ + { 11, 0x100bb3, 0x1301e6, 0x05a014, 0x001402 }, \ + { 12, 0x100bb3, 0x1301e6, 0x05a014, 0x001407 }, \ + { 13, 0x100bb3, 0x1301e7, 0x05a014, 0x001402 }, \ + { 14, 0x100bb3, 0x1301e8, 0x05a014, 0x001404 }, \ + { 36, 0x100bb3, 0x130266, 0x056014, 0x001408 }, \ + { 38, 0x100bb3, 0x130267, 0x056014, 0x001404 }, \ + { 40, 0x100bb2, 0x1301a0, 0x056014, 0x001400 }, \ + { 44, 0x100bb2, 0x1301a0, 0x056014, 0x001408 }, \ + { 46, 0x100bb2, 0x1301a1, 0x056014, 0x001402 }, \ + { 48, 0x100bb2, 0x1301a1, 0x056014, 0x001406 }, \ + { 52, 0x100bb2, 0x1301a2, 0x056014, 0x001404 }, \ + { 54, 0x100bb2, 0x1301a2, 0x056014, 0x001408 }, \ + { 56, 0x100bb2, 0x1301a3, 0x056014, 0x001402 }, \ + { 60, 0x100bb2, 0x1301a4, 0x056014, 0x001400 }, \ + { 62, 0x100bb2, 0x1301a4, 0x056014, 0x001404 }, \ + { 64, 0x100bb2, 0x1301a4, 0x056014, 0x001408 }, \ + { 100, 0x100bb2, 0x1301ac, 0x05e014, 0x001400 }, \ + { 102, 0x100bb2, 0x1301ac, 0x05e014, 0x001404 }, \ + { 104, 0x100bb2, 0x1301ac, 0x05e014, 0x001408 }, \ + { 108, 0x100bb3, 0x13028c, 0x05e014, 0x001404 }, \ + { 110, 0x100bb3, 0x13028d, 0x05e014, 0x001400 }, \ + { 112, 0x100bb3, 0x13028d, 0x05e014, 0x001406 }, \ + { 116, 0x100bb3, 0x13028e, 0x05e014, 0x001408 }, \ + { 118, 0x100bb3, 0x13028f, 0x05e014, 0x001404 }, \ + { 120, 0x100bb1, 0x1300e0, 0x05e014, 0x001400 }, \ + { 124, 0x100bb1, 0x1300e0, 0x05e014, 0x001404 }, \ + { 126, 0x100bb1, 0x1300e0, 0x05e014, 0x00140e }, \ + { 128, 0x100bb1, 0x1300e0, 0x05e014, 0x001408 }, \ + { 132, 0x100bb1, 0x1300e1, 0x05e014, 0x001402 }, \ + { 134, 0x100bb1, 0x1300e1, 0x05e014, 0x001404 }, \ + { 136, 0x100bb1, 0x1300e1, 0x05e014, 0x001406 }, \ + { 140, 0x100bb1, 0x1300e2, 0x05e014, 0x001400 }, \ + { 149, 0x100bb1, 0x1300e2, 0x05e014, 0x001409 }, \ + { 151, 0x100bb1, 0x1300e3, 0x05e014, 0x001401 }, \ + { 153, 0x100bb1, 0x1300e3, 0x05e014, 0x001403 }, \ + { 157, 0x100bb1, 0x1300e3, 0x05e014, 0x001407 }, \ + { 159, 0x100bb1, 0x1300e3, 0x05e014, 0x001409 }, \ + { 161, 0x100bb1, 0x1300e4, 0x05e014, 0x001401 }, \ + { 165, 0x100bb1, 0x1300e4, 0x05e014, 0x001405 } diff --git a/sys/dev/ic/rt2860var.h b/sys/dev/ic/rt2860var.h new file mode 100644 index 00000000000..f552158cc5d --- /dev/null +++ b/sys/dev/ic/rt2860var.h @@ -0,0 +1,130 @@ +/* $OpenBSD: rt2860var.h,v 1.1 2007/11/15 21:15:34 damien Exp $ */ + +/*- + * Copyright (c) 2007 + * Damien Bergamini <damien.bergamini@free.fr> + * + * Permission to use, copy, modify, and 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. + */ + +#define RT2860_TX_RING_COUNT 64 +#define RT2860_RX_RING_COUNT 128 +#define RT2860_TX_POOL_COUNT (RT2860_TX_RING_COUNT * 2) + +#define RT2860_MAX_SCATTER ((RT2860_TX_RING_COUNT * 2) - 1) + +/* HW supports up to 255 STAs */ +#define RT2860_WCID_MAX 254 +#define RT2860_AID2WCID(aid) ((aid) & 0xff) + +struct rt2860_tx_data { + struct rt2860_txwi *txwi; + struct mbuf *m; + struct ieee80211_node *ni; + bus_dmamap_t map; + bus_addr_t paddr; + SLIST_ENTRY(rt2860_tx_data) next; +}; + +struct rt2860_tx_ring { + struct rt2860_txd *txd; + bus_addr_t paddr; + bus_dmamap_t map; + bus_dma_segment_t seg; + struct rt2860_tx_data *data[RT2860_TX_RING_COUNT]; + int cur; + int next; + int queued; +}; + +struct rt2860_rx_data { + struct mbuf *m; + bus_dmamap_t map; +}; + +struct rt2860_rx_ring { + struct rt2860_rxd *rxd; + bus_addr_t paddr; + bus_dmamap_t map; + bus_dma_segment_t seg; + unsigned int cur; /* must be unsigned */ + struct rt2860_rx_data data[RT2860_RX_RING_COUNT]; +}; + +struct rt2860_softc { + struct device sc_dev; + + struct ieee80211com sc_ic; + int (*sc_newstate)(struct ieee80211com *, + enum ieee80211_state, int); + struct ieee80211_amrr amrr; + + int (*sc_enable)(struct rt2860_softc *); + void (*sc_disable)(struct rt2860_softc *); + void (*sc_power)(struct rt2860_softc *, int); + + bus_dma_tag_t sc_dmat; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + + struct timeout scan_to; + struct timeout amrr_to; + + int sc_flags; +#define RT2860_ENABLED (1 << 0) +#define RT2860_FWLOADED (1 << 1) + + struct rt2860_tx_ring txq[6]; + struct rt2860_rx_ring rxq; + + SLIST_HEAD(, rt2860_tx_data) data_pool; + struct rt2860_tx_data data[RT2860_TX_POOL_COUNT]; + bus_dmamap_t txwi_map; + bus_dma_segment_t txwi_seg; + struct rt2860_txwi *txwi; + + int sc_tx_timer; + int mgtqid; + int sifs; + + uint32_t mac_rev; + uint8_t rf_rev; + uint8_t freq; + uint8_t ntxchains; + uint8_t nrxchains; + int8_t txpow1[50]; + int8_t txpow2[50]; + int8_t rssi_2ghz[3]; + int8_t rssi_5ghz[3]; + uint8_t lna[4]; + uint8_t calib_2ghz; + uint8_t calib_5ghz; + uint8_t tssi_2ghz[9]; + uint8_t tssi_5ghz[9]; + uint8_t step_2ghz; + uint8_t step_5ghz; + struct { + uint8_t reg; + uint8_t val; + } bbp[8]; + uint32_t txpow20mhz[5]; + uint32_t txpow40mhz_2ghz[5]; + uint32_t txpow40mhz_5ghz[5]; + + struct ieee80211_amrr_node amn[RT2860_WCID_MAX + 1]; +}; + +int rt2860_attach(void *, int); +int rt2860_detach(void *); +int rt2860_intr(void *); +void rt2860_shutdown(void *); diff --git a/sys/dev/pci/if_ral_pci.c b/sys/dev/pci/if_ral_pci.c index af4b3a22663..dfefaa08dd8 100644 --- a/sys/dev/pci/if_ral_pci.c +++ b/sys/dev/pci/if_ral_pci.c @@ -1,7 +1,7 @@ -/* $OpenBSD: if_ral_pci.c,v 1.8 2006/11/18 20:44:40 grange Exp $ */ +/* $OpenBSD: if_ral_pci.c,v 1.9 2007/11/15 21:15:34 damien Exp $ */ /*- - * Copyright (c) 2005, 2006 + * Copyright (c) 2005-2007 * Damien Bergamini <damien.bergamini@free.fr> * * Permission to use, copy, modify, and distribute this software for any @@ -18,7 +18,7 @@ */ /* - * PCI front-end for the Ralink RT2560/RT2561/RT2561S/RT2661 driver. + * PCI front-end for the Ralink RT2560/RT2561/RT2661/RT2860 driver. */ #include "bpfilter.h" @@ -49,6 +49,7 @@ #include <dev/ic/rt2560var.h> #include <dev/ic/rt2661var.h> +#include <dev/ic/rt2860var.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> @@ -68,12 +69,18 @@ static struct ral_opns { rt2661_attach, rt2661_detach, rt2661_intr + +}, ral_rt2860_opns = { + rt2860_attach, + rt2860_detach, + rt2860_intr }; struct ral_pci_softc { union { struct rt2560_softc sc_rt2560; struct rt2661_softc sc_rt2661; + struct rt2860_softc sc_rt2860; } u; #define sc_sc u.sc_rt2560 @@ -97,10 +104,13 @@ struct cfattach ral_pci_ca = { }; const struct pci_matchid ral_pci_devices[] = { - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2560 }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561 }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561S }, - { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2661 } + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2560 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2561S }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2661 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_1 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_2 }, + { PCI_VENDOR_RALINK, PCI_PRODUCT_RALINK_RT2860_3 } }; int @@ -118,18 +128,32 @@ ral_pci_attach(struct device *parent, struct device *self, void *aux) struct pci_attach_args *pa = aux; const char *intrstr; pci_intr_handle_t ih; + pcireg_t memtype; int error; - psc->sc_opns = (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_RALINK_RT2560) ? - &ral_rt2560_opns : &ral_rt2661_opns; + switch (PCI_PRODUCT(pa->pa_id)) { + case PCI_PRODUCT_RALINK_RT2560: + psc->sc_opns = &ral_rt2560_opns; + break; + case PCI_PRODUCT_RALINK_RT2561: + case PCI_PRODUCT_RALINK_RT2561S: + case PCI_PRODUCT_RALINK_RT2661: + psc->sc_opns = &ral_rt2661_opns; + break; + case PCI_PRODUCT_RALINK_RT2860_1: + case PCI_PRODUCT_RALINK_RT2860_2: + case PCI_PRODUCT_RALINK_RT2860_3: + psc->sc_opns = &ral_rt2860_opns; + break; + } sc->sc_dmat = pa->pa_dmat; psc->sc_pc = pa->pa_pc; /* map control/status registers */ - error = pci_mapreg_map(pa, RAL_PCI_BAR0, PCI_MAPREG_TYPE_MEM | - PCI_MAPREG_MEM_TYPE_32BIT, 0, &sc->sc_st, &sc->sc_sh, NULL, - &psc->sc_mapsize, 0); + memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, RAL_PCI_BAR0); + error = pci_mapreg_map(pa, RAL_PCI_BAR0, memtype, 0, &sc->sc_st, + &sc->sc_sh, NULL, &psc->sc_mapsize, 0); if (error != 0) { printf(": could not map memory space\n"); return; |