From 4310d6deb27da04d3fe079a0584edd557a764e21 Mon Sep 17 00:00:00 2001 From: Bernhard Schmidt Date: Thu, 10 May 2012 17:41:16 +0000 Subject: [PATCH] Add support for Ralink RT2800/RT3000 chipsets. Thanks to ray@, Sevan and Sergey Dyatko for feedback and testing! Obtained from: OpenBSD MFC after: 3 weeks --- sys/conf/files | 1 + sys/dev/ral/if_ral_pci.c | 52 +- sys/dev/ral/rt2860.c | 4103 ++++++++++++++++++++++++++++++++++++++ sys/dev/ral/rt2860reg.h | 1255 ++++++++++++ sys/dev/ral/rt2860var.h | 210 ++ sys/modules/ral/Makefile | 2 +- 6 files changed, 5619 insertions(+), 4 deletions(-) create mode 100644 sys/dev/ral/rt2860.c create mode 100644 sys/dev/ral/rt2860reg.h create mode 100644 sys/dev/ral/rt2860var.h diff --git a/sys/conf/files b/sys/conf/files index b1495ce6ccfc..a8c9891ad1d1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1759,6 +1759,7 @@ dev/puc/pucdata.c optional puc pci dev/quicc/quicc_core.c optional quicc dev/ral/rt2560.c optional ral dev/ral/rt2661.c optional ral +dev/ral/rt2860.c optional ral dev/ral/if_ral_pci.c optional ral pci rt2561fw.c optional rt2561fw | ralfw \ compile-with "${AWK} -f $S/tools/fw_stub.awk rt2561.fw:rt2561fw -mrt2561 -c${.TARGET}" \ diff --git a/sys/dev/ral/if_ral_pci.c b/sys/dev/ral/if_ral_pci.c index 68b662261f83..cd892b50d936 100644 --- a/sys/dev/ral/if_ral_pci.c +++ b/sys/dev/ral/if_ral_pci.c @@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include MODULE_DEPEND(ral, pci, 1, 1, 1); MODULE_DEPEND(ral, firmware, 1, 1, 1); @@ -70,11 +71,37 @@ struct ral_pci_ident { }; static const struct ral_pci_ident ral_pci_ids[] = { + { 0x1432, 0x7708, "Edimax RT2860" }, + { 0x1432, 0x7711, "Edimax RT3591" }, + { 0x1432, 0x7722, "Edimax RT3591" }, + { 0x1432, 0x7727, "Edimax RT2860" }, + { 0x1432, 0x7728, "Edimax RT2860" }, + { 0x1432, 0x7738, "Edimax RT2860" }, + { 0x1432, 0x7748, "Edimax RT2860" }, + { 0x1432, 0x7758, "Edimax RT2860" }, + { 0x1432, 0x7768, "Edimax RT2860" }, + { 0x1462, 0x891a, "MSI RT3090" }, { 0x1814, 0x0201, "Ralink Technology RT2560" }, { 0x1814, 0x0301, "Ralink Technology RT2561S" }, { 0x1814, 0x0302, "Ralink Technology RT2561" }, { 0x1814, 0x0401, "Ralink Technology RT2661" }, - + { 0x1814, 0x0601, "Ralink Technology RT2860" }, + { 0x1814, 0x0681, "Ralink Technology RT2890" }, + { 0x1814, 0x0701, "Ralink Technology RT2760" }, + { 0x1814, 0x0781, "Ralink Technology RT2790" }, + { 0x1814, 0x3060, "Ralink Technology RT3060" }, + { 0x1814, 0x3062, "Ralink Technology RT3062" }, + { 0x1814, 0x3090, "Ralink Technology RT3090" }, + { 0x1814, 0x3091, "Ralink Technology RT3091" }, + { 0x1814, 0x3092, "Ralink Technology RT3092" }, + { 0x1814, 0x3390, "Ralink Technology RT3390" }, + { 0x1814, 0x3562, "Ralink Technology RT3562" }, + { 0x1814, 0x3592, "Ralink Technology RT3592" }, + { 0x1814, 0x3593, "Ralink Technology RT3593" }, + { 0x1814, 0x5390, "Ralink Technology RT5390" }, + { 0x1814, 0x539a, "Ralink Technology RT5390" }, + { 0x1814, 0x539f, "Ralink Technology RT5390" }, + { 0x1a3b, 0x1059, "AWT RT2890" }, { 0, 0, NULL } }; @@ -101,12 +128,20 @@ static struct ral_opns { rt2661_suspend, rt2661_resume, rt2661_intr +}, ral_rt2860_opns = { + rt2860_attach, + rt2860_detach, + rt2860_shutdown, + rt2860_suspend, + rt2860_resume, + rt2860_intr }; struct ral_pci_softc { union { struct rt2560_softc sc_rt2560; struct rt2661_softc sc_rt2661; + struct rt2860_softc sc_rt2860; } u; struct ral_opns *sc_opns; @@ -180,8 +215,19 @@ ral_pci_attach(device_t dev) /* enable bus-mastering */ pci_enable_busmaster(dev); - psc->sc_opns = (pci_get_device(dev) == 0x0201) ? &ral_rt2560_opns : - &ral_rt2661_opns; + switch (pci_get_device(dev)) { + case 0x0201: + psc->sc_opns = &ral_rt2560_opns; + break; + case 0x0301: + case 0x0302: + case 0x0401: + psc->sc_opns = &ral_rt2661_opns; + break; + default: + psc->sc_opns = &ral_rt2860_opns; + break; + } psc->mem_rid = RAL_PCI_BAR0; psc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &psc->mem_rid, diff --git a/sys/dev/ral/rt2860.c b/sys/dev/ral/rt2860.c new file mode 100644 index 000000000000..4a5aa259c0e6 --- /dev/null +++ b/sys/dev/ral/rt2860.c @@ -0,0 +1,4103 @@ +/*- + * Copyright (c) 2007-2010 Damien Bergamini + * Copyright (c) 2012 Bernhard Schmidt + * + * 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. + * + * $OpenBSD: rt2860.c,v 1.65 2010/10/23 14:24:54 damien Exp $ + */ + +#include +__FBSDID("$FreeBSD$"); + +/*- + * Ralink Technology RT2860/RT3090/RT3390/RT3562 chipset driver + * http://www.ralinktech.com/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define RAL_DEBUG +#ifdef RAL_DEBUG +#define DPRINTF(x) do { if (sc->sc_debug > 0) printf x; } while (0) +#define DPRINTFN(n, x) do { if (sc->sc_debug >= (n)) printf x; } while (0) +#else +#define DPRINTF(x) +#define DPRINTFN(n, x) +#endif + +static struct ieee80211vap *rt2860_vap_create(struct ieee80211com *, + const char [IFNAMSIZ], int, enum ieee80211_opmode, + int, const uint8_t [IEEE80211_ADDR_LEN], + const uint8_t [IEEE80211_ADDR_LEN]); +static void rt2860_vap_delete(struct ieee80211vap *); +static void rt2860_dma_map_addr(void *, bus_dma_segment_t *, int, int); +static int rt2860_alloc_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +static void rt2860_reset_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +static void rt2860_free_tx_ring(struct rt2860_softc *, + struct rt2860_tx_ring *); +static int rt2860_alloc_tx_pool(struct rt2860_softc *); +static void rt2860_free_tx_pool(struct rt2860_softc *); +static int rt2860_alloc_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +static void rt2860_reset_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +static void rt2860_free_rx_ring(struct rt2860_softc *, + struct rt2860_rx_ring *); +static void rt2860_updatestats(struct rt2860_softc *); +static void rt2860_newassoc(struct ieee80211_node *, int); +static void rt2860_node_free(struct ieee80211_node *); +#ifdef IEEE80211_HT +static int rt2860_ampdu_rx_start(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +static void rt2860_ampdu_rx_stop(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +#endif +static int rt2860_newstate(struct ieee80211vap *, enum ieee80211_state, + int); +static uint16_t rt3090_efuse_read_2(struct rt2860_softc *, uint16_t); +static uint16_t rt2860_eeprom_read_2(struct rt2860_softc *, uint16_t); +static void rt2860_intr_coherent(struct rt2860_softc *); +static void rt2860_drain_stats_fifo(struct rt2860_softc *); +static void rt2860_tx_intr(struct rt2860_softc *, int); +static void rt2860_rx_intr(struct rt2860_softc *); +static void rt2860_tbtt_intr(struct rt2860_softc *); +static void rt2860_gp_intr(struct rt2860_softc *); +static int rt2860_tx(struct rt2860_softc *, struct mbuf *, + struct ieee80211_node *); +static int rt2860_raw_xmit(struct ieee80211_node *, struct mbuf *, + const struct ieee80211_bpf_params *); +static int rt2860_tx_raw(struct rt2860_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *params); +static void rt2860_start(struct ifnet *); +static void rt2860_start_locked(struct ifnet *); +static void rt2860_watchdog(void *); +static int rt2860_ioctl(struct ifnet *, u_long, caddr_t); +static void rt2860_mcu_bbp_write(struct rt2860_softc *, uint8_t, uint8_t); +static uint8_t rt2860_mcu_bbp_read(struct rt2860_softc *, uint8_t); +static void rt2860_rf_write(struct rt2860_softc *, uint8_t, uint32_t); +static uint8_t rt3090_rf_read(struct rt2860_softc *, uint8_t); +static void rt3090_rf_write(struct rt2860_softc *, uint8_t, uint8_t); +static int rt2860_mcu_cmd(struct rt2860_softc *, uint8_t, uint16_t, int); +static void rt2860_enable_mrr(struct rt2860_softc *); +static void rt2860_set_txpreamble(struct rt2860_softc *); +static void rt2860_set_basicrates(struct rt2860_softc *, + const struct ieee80211_rateset *); +static void rt2860_scan_start(struct ieee80211com *); +static void rt2860_scan_end(struct ieee80211com *); +static void rt2860_set_channel(struct ieee80211com *); +static void rt2860_select_chan_group(struct rt2860_softc *, int); +static void rt2860_set_chan(struct rt2860_softc *, u_int); +static void rt3090_set_chan(struct rt2860_softc *, u_int); +static int rt3090_rf_init(struct rt2860_softc *); +static void rt3090_rf_wakeup(struct rt2860_softc *); +static int rt3090_filter_calib(struct rt2860_softc *, uint8_t, uint8_t, + uint8_t *); +static void rt3090_rf_setup(struct rt2860_softc *); +static void rt2860_set_leds(struct rt2860_softc *, uint16_t); +static void rt2860_set_gp_timer(struct rt2860_softc *, int); +static void rt2860_set_bssid(struct rt2860_softc *, const uint8_t *); +static void rt2860_set_macaddr(struct rt2860_softc *, const uint8_t *); +static void rt2860_update_promisc(struct ifnet *); +static void rt2860_updateslot(struct ifnet *); +static void rt2860_updateprot(struct ifnet *); +static int rt2860_updateedca(struct ieee80211com *); +#ifdef HW_CRYPTO +static int rt2860_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +static void rt2860_delete_key(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_key *); +#endif +static int8_t rt2860_rssi2dbm(struct rt2860_softc *, uint8_t, uint8_t); +static const char *rt2860_get_rf(uint8_t); +static int rt2860_read_eeprom(struct rt2860_softc *, + uint8_t macaddr[IEEE80211_ADDR_LEN]); +static int rt2860_bbp_init(struct rt2860_softc *); +static int rt2860_txrx_enable(struct rt2860_softc *); +static void rt2860_init(void *); +static void rt2860_init_locked(struct rt2860_softc *); +static void rt2860_stop(void *); +static void rt2860_stop_locked(struct rt2860_softc *); +static int rt2860_load_microcode(struct rt2860_softc *); +#ifdef NOT_YET +static void rt2860_calib(struct rt2860_softc *); +#endif +static void rt3090_set_rx_antenna(struct rt2860_softc *, int); +static void rt2860_switch_chan(struct rt2860_softc *, + struct ieee80211_channel *); +static int rt2860_setup_beacon(struct rt2860_softc *, + struct ieee80211vap *); +static 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 +}; + +struct { + uint8_t n, r, k; +} rt3090_freqs[] = { + RT3070_RF3052 +}; + +static const struct { + uint8_t reg; + uint8_t val; +} rt3090_def_rf[] = { + RT3070_DEF_RF +}; + +int +rt2860_attach(device_t dev, int id) +{ + struct rt2860_softc *sc = device_get_softc(dev); + struct ieee80211com *ic; + struct ifnet *ifp; + uint32_t tmp; + int error, ntries, qid; + uint8_t bands; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + + sc->sc_dev = dev; + sc->sc_debug = 0; + + ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); + if (ifp == NULL) { + device_printf(sc->sc_dev, "can not if_alloc()\n"); + return ENOMEM; + } + ic = ifp->if_l2com; + + mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, + MTX_DEF | MTX_RECURSE); + + callout_init_mtx(&sc->watchdog_ch, &sc->sc_mtx, 0); + + /* wait for NIC to initialize */ + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT2860_ASIC_VER_ID); + if (tmp != 0 && tmp != 0xffffffff) + break; + DELAY(10); + } + if (ntries == 100) { + device_printf(sc->sc_dev, + "timeout waiting for NIC to initialize\n"); + error = EIO; + goto fail1; + } + sc->mac_ver = tmp >> 16; + sc->mac_rev = tmp & 0xffff; + + if (sc->mac_ver != 0x2860 && + (id == 0x0681 || id == 0x0781 || id == 0x1059)) + sc->sc_flags |= RT2860_ADVANCED_PS; + + /* retrieve RF rev. no and various other things from EEPROM */ + rt2860_read_eeprom(sc, macaddr); + if (bootverbose) { + device_printf(sc->sc_dev, "MAC/BBP RT%X (rev 0x%04X), " + "RF %s (MIMO %dT%dR), address %6D\n", + sc->mac_ver, sc->mac_rev, rt2860_get_rf(sc->rf_rev), + sc->ntxchains, sc->nrxchains, macaddr, ":"); + } + + /* + * 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) { + device_printf(sc->sc_dev, + "could not allocate Tx ring %d\n", qid); + goto fail2; + } + } + + if ((error = rt2860_alloc_rx_ring(sc, &sc->rxq)) != 0) { + device_printf(sc->sc_dev, "could not allocate Rx ring\n"); + goto fail2; + } + + if ((error = rt2860_alloc_tx_pool(sc)) != 0) { + device_printf(sc->sc_dev, "could not allocate Tx pool\n"); + goto fail3; + } + + /* mgmt ring is broken on RT2860C, use EDCA AC VO ring instead */ + sc->mgtqid = (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) ? + WME_AC_VO : 5; + + ifp->if_softc = sc; + if_initname(ifp, device_get_name(dev), device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = rt2860_init; + ifp->if_ioctl = rt2860_ioctl; + ifp->if_start = rt2860_start; + IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); + ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; + IFQ_SET_READY(&ifp->if_snd); + + ic->ic_ifp = ifp; + ic->ic_opmode = IEEE80211_M_STA; + ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ + + /* set device capabilities */ + ic->ic_caps = + IEEE80211_C_STA /* station mode */ + | IEEE80211_C_IBSS /* ibss, nee adhoc, mode */ + | IEEE80211_C_HOSTAP /* hostap mode */ + | IEEE80211_C_MONITOR /* monitor mode */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_WDS /* 4-address traffic works */ + | IEEE80211_C_MBSS /* mesh point link mode */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ + | IEEE80211_C_SHSLOT /* short slot time supported */ + | IEEE80211_C_WPA /* capable of WPA1+WPA2 */ +#if 0 + | IEEE80211_C_BGSCAN /* capable of bg scanning */ +#endif + | IEEE80211_C_WME /* 802.11e */ + ; + + bands = 0; + setbit(&bands, IEEE80211_MODE_11B); + setbit(&bands, IEEE80211_MODE_11G); + if (sc->rf_rev == RT2860_RF_2750 || sc->rf_rev == RT2860_RF_2850) + setbit(&bands, IEEE80211_MODE_11A); + ieee80211_init_channels(ic, NULL, &bands); + + ieee80211_ifattach(ic, macaddr); + + ic->ic_wme.wme_update = rt2860_updateedca; + ic->ic_scan_start = rt2860_scan_start; + ic->ic_scan_end = rt2860_scan_end; + ic->ic_set_channel = rt2860_set_channel; + ic->ic_updateslot = rt2860_updateslot; + ic->ic_update_promisc = rt2860_update_promisc; + ic->ic_raw_xmit = rt2860_raw_xmit; + sc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = rt2860_node_free; + ic->ic_newassoc = rt2860_newassoc; + + ic->ic_vap_create = rt2860_vap_create; + ic->ic_vap_delete = rt2860_vap_delete; + + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + RT2860_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + RT2860_RX_RADIOTAP_PRESENT); + +#ifdef RAL_DEBUG + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, 0, "debug msgs"); +#endif + if (bootverbose) + ieee80211_announce(ic); + + return 0; + +fail3: rt2860_free_rx_ring(sc, &sc->rxq); +fail2: while (--qid >= 0) + rt2860_free_tx_ring(sc, &sc->txq[qid]); +fail1: mtx_destroy(&sc->sc_mtx); + if_free(ifp); + return error; +} + +int +rt2860_detach(void *xsc) +{ + struct rt2860_softc *sc = xsc; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + int qid; + + RAL_LOCK(sc); + rt2860_stop_locked(sc); + RAL_UNLOCK(sc); + + ieee80211_ifdetach(ic); + + 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); + + if_free(ifp); + + mtx_destroy(&sc->sc_mtx); + + return 0; +} + +void +rt2860_shutdown(void *xsc) +{ + struct rt2860_softc *sc = xsc; + + rt2860_stop(sc); +} + +void +rt2860_suspend(void *xsc) +{ + struct rt2860_softc *sc = xsc; + + rt2860_stop(sc); +} + +void +rt2860_resume(void *xsc) +{ + struct rt2860_softc *sc = xsc; + struct ifnet *ifp = sc->sc_ifp; + + if (ifp->if_flags & IFF_UP) + rt2860_init(sc); +} + +static struct ieee80211vap * +rt2860_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_vap *rvp; + struct ieee80211vap *vap; + + switch (opmode) { + case IEEE80211_M_STA: + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_MONITOR: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_MBSS: + /* XXXRP: TBD */ + if (!TAILQ_EMPTY(&ic->ic_vaps)) { + if_printf(ifp, "only 1 vap supported\n"); + return NULL; + } + if (opmode == IEEE80211_M_STA) + flags |= IEEE80211_CLONE_NOBEACONS; + break; + case IEEE80211_M_WDS: + if (TAILQ_EMPTY(&ic->ic_vaps) || + ic->ic_opmode != IEEE80211_M_HOSTAP) { + if_printf(ifp, "wds only supported in ap mode\n"); + return NULL; + } + /* + * Silently remove any request for a unique + * bssid; WDS vap's always share the local + * mac address. + */ + flags &= ~IEEE80211_CLONE_BSSID; + break; + default: + if_printf(ifp, "unknown opmode %d\n", opmode); + return NULL; + } + rvp = malloc(sizeof(struct rt2860_vap), M_80211_VAP, M_NOWAIT | M_ZERO); + if (rvp == NULL) + return NULL; + vap = &rvp->ral_vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + + /* override state transition machine */ + rvp->ral_newstate = vap->iv_newstate; + vap->iv_newstate = rt2860_newstate; +#if 0 + vap->iv_update_beacon = rt2860_beacon_update; +#endif + + /* HW supports up to 255 STAs (0-254) in HostAP and IBSS modes */ + vap->iv_max_aid = min(IEEE80211_AID_MAX, RT2860_WCID_MAX); + + ieee80211_ratectl_init(vap); + /* complete setup */ + ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); + if (TAILQ_FIRST(&ic->ic_vaps) == vap) + ic->ic_opmode = opmode; + return vap; +} + +static void +rt2860_vap_delete(struct ieee80211vap *vap) +{ + struct rt2860_vap *rvp = RT2860_VAP(vap); + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + free(rvp, M_80211_VAP); +} + +static void +rt2860_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + if (error != 0) + return; + + KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg)); + + *(bus_addr_t *)arg = segs[0].ds_addr; +} + + +static int +rt2860_alloc_tx_ring(struct rt2860_softc *sc, struct rt2860_tx_ring *ring) +{ + int size, error; + + size = RT2860_TX_RING_COUNT * sizeof (struct rt2860_txd); + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 16, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &ring->desc_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create desc DMA map\n"); + goto fail; + } + + error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->txd, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->txd, + size, rt2860_dma_map_addr, &ring->paddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load desc DMA map\n"); + goto fail; + } + + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); + + 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++) { + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + if (data->m != NULL) { + bus_dmamap_sync(sc->txwi_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->txwi_dmat, data->map); + m_freem(data->m); + data->m = NULL; + } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[i] = NULL; + } + + 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(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dmat, ring->desc_map); + bus_dmamem_free(ring->desc_dmat, ring->txd, ring->desc_map); + } + if (ring->desc_dmat != NULL) + bus_dma_tag_destroy(ring->desc_dmat); + + for (i = 0; i < RT2860_TX_RING_COUNT; i++) { + if ((data = ring->data[i]) == NULL) + continue; /* nothing mapped in this slot */ + + if (data->m != NULL) { + bus_dmamap_sync(sc->txwi_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->txwi_dmat, data->map); + m_freem(data->m); + } + if (data->ni != NULL) + ieee80211_free_node(data->ni); + + 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) +{ + caddr_t vaddr; + bus_addr_t paddr; + int i, size, error; + + size = RT2860_TX_POOL_COUNT * RT2860_TXWI_DMASZ; + + /* init data_pool early in case of failure.. */ + SLIST_INIT(&sc->data_pool); + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &sc->txwi_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create txwi DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(sc->txwi_dmat, (void **)&sc->txwi_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &sc->txwi_map); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(sc->txwi_dmat, sc->txwi_map, + sc->txwi_vaddr, size, rt2860_dma_map_addr, &paddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load txwi DMA map\n"); + goto fail; + } + + bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map, BUS_DMASYNC_PREWRITE); + + vaddr = sc->txwi_vaddr; + for (i = 0; i < RT2860_TX_POOL_COUNT; i++) { + struct rt2860_tx_data *data = &sc->data[i]; + + error = bus_dmamap_create(sc->txwi_dmat, 0, &data->map); + if (error != 0) { + device_printf(sc->sc_dev, "could not create DMA map\n"); + goto fail; + } + data->txwi = (struct rt2860_txwi *)vaddr; + data->paddr = paddr; + vaddr += RT2860_TXWI_DMASZ; + paddr += RT2860_TXWI_DMASZ; + + 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_vaddr != NULL) { + bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->txwi_dmat, sc->txwi_map); + bus_dmamem_free(sc->txwi_dmat, sc->txwi_vaddr, sc->txwi_map); + } + if (sc->txwi_dmat != NULL) + bus_dma_tag_destroy(sc->txwi_dmat); + + while (!SLIST_EMPTY(&sc->data_pool)) { + struct rt2860_tx_data *data; + data = SLIST_FIRST(&sc->data_pool); + bus_dmamap_destroy(sc->txwi_dmat, data->map); + SLIST_REMOVE_HEAD(&sc->data_pool, next); + } +} + +int +rt2860_alloc_rx_ring(struct rt2860_softc *sc, struct rt2860_rx_ring *ring) +{ + bus_addr_t physaddr; + int i, size, error; + + size = RT2860_RX_RING_COUNT * sizeof (struct rt2860_rxd); + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 16, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + size, 1, size, 0, NULL, NULL, &ring->desc_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create desc DMA tag\n"); + goto fail; + } + + error = bus_dmamem_alloc(ring->desc_dmat, (void **)&ring->rxd, + BUS_DMA_NOWAIT | BUS_DMA_ZERO, &ring->desc_map); + if (error != 0) { + device_printf(sc->sc_dev, "could not allocate DMA memory\n"); + goto fail; + } + + error = bus_dmamap_load(ring->desc_dmat, ring->desc_map, ring->rxd, + size, rt2860_dma_map_addr, &ring->paddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, "could not load desc DMA map\n"); + goto fail; + } + + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, + 1, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); + if (error != 0) { + device_printf(sc->sc_dev, "could not create data DMA tag\n"); + goto fail; + } + + 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(ring->data_dmat, 0, &data->map); + if (error != 0) { + device_printf(sc->sc_dev, "could not create DMA map\n"); + goto fail; + } + + data->m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (data->m == NULL) { + device_printf(sc->sc_dev, + "could not allocate rx mbuf\n"); + error = ENOMEM; + goto fail; + } + + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), MCLBYTES, rt2860_dma_map_addr, + &physaddr, 0); + if (error != 0) { + device_printf(sc->sc_dev, + "could not load rx buf DMA map"); + goto fail; + } + + rxd->sdp0 = htole32(physaddr); + rxd->sdl0 = htole16(MCLBYTES); + } + + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, 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(ring->desc_dmat, ring->desc_map, 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(ring->desc_dmat, ring->desc_map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->desc_dmat, ring->desc_map); + bus_dmamem_free(ring->desc_dmat, ring->rxd, ring->desc_map); + } + if (ring->desc_dmat != NULL) + bus_dma_tag_destroy(ring->desc_dmat); + + for (i = 0; i < RT2860_RX_RING_COUNT; i++) { + struct rt2860_rx_data *data = &ring->data[i]; + + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); + } + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); + } + if (ring->data_dmat != NULL) + bus_dma_tag_destroy(ring->data_dmat); +} + +static void +rt2860_updatestats(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + + /* + * In IBSS or HostAP modes (when the hardware sends beacons), the + * MAC can run into a livelock and start sending CTS-to-self frames + * like crazy if protection is enabled. Fortunately, we can detect + * when such a situation occurs and reset the MAC. + */ + if (ic->ic_curmode != IEEE80211_M_STA) { + /* check if we're in a livelock situation.. */ + uint32_t tmp = RAL_READ(sc, RT2860_DEBUG); + if ((tmp & (1 << 29)) && (tmp & (1 << 7 | 1 << 5))) { + /* ..and reset MAC/BBP for a while.. */ + DPRINTF(("CTS-to-self livelock detected\n")); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_SRST); + RAL_BARRIER_WRITE(sc); + DELAY(1); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, + RT2860_MAC_RX_EN | RT2860_MAC_TX_EN); + } + } +} + +static void +rt2860_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211com *ic = ni->ni_ic; + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + uint8_t wcid; + + wcid = IEEE80211_AID(ni->ni_associd); + if (isnew && ni->ni_associd != 0) { + sc->wcid2ni[wcid] = ni; + + /* init WCID table entry */ + RAL_WRITE_REGION_1(sc, RT2860_WCID_ENTRY(wcid), + ni->ni_macaddr, IEEE80211_ADDR_LEN); + } + DPRINTF(("new assoc isnew=%d addr=%s WCID=%d\n", + isnew, ether_sprintf(ni->ni_macaddr), wcid)); +} + +static void +rt2860_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + uint8_t wcid; + + if (ni->ni_associd != 0) { + wcid = IEEE80211_AID(ni->ni_associd); + + /* clear Rx WCID search table entry */ + RAL_SET_REGION_4(sc, RT2860_WCID_ENTRY(wcid), 0, 2); + } + sc->sc_node_free(ni); +} + +#ifdef IEEE80211_HT +static int +rt2860_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct rt2860_softc *sc = ic->ic_softc; + uint8_t wcid = ((struct rt2860_node *)ni)->wcid; + uint32_t tmp; + + /* update BA session mask */ + tmp = RAL_READ(sc, RT2860_WCID_ENTRY(wcid) + 4); + tmp |= (1 << tid) << 16; + RAL_WRITE(sc, RT2860_WCID_ENTRY(wcid) + 4, tmp); + return 0; +} + +static void +rt2860_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct rt2860_softc *sc = ic->ic_softc; + uint8_t wcid = ((struct rt2860_node *)ni)->wcid; + uint32_t tmp; + + /* update BA session mask */ + tmp = RAL_READ(sc, RT2860_WCID_ENTRY(wcid) + 4); + tmp &= ~((1 << tid) << 16); + RAL_WRITE(sc, RT2860_WCID_ENTRY(wcid) + 4, tmp); +} +#endif + +int +rt2860_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +{ + struct rt2860_vap *rvp = RT2860_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + uint32_t tmp; + int error; + + if (vap->iv_state == IEEE80211_S_RUN) { + /* turn link LED off */ + rt2860_set_leds(sc, RT2860_LED_RADIO); + } + + if (nstate == IEEE80211_S_INIT && vap->iv_state == 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)); + } + + rt2860_set_gp_timer(sc, 0); + + error = rvp->ral_newstate(vap, nstate, arg); + if (error != 0) + return (error); + + if (nstate == IEEE80211_S_RUN) { + struct ieee80211_node *ni = vap->iv_bss; + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_enable_mrr(sc); + rt2860_set_txpreamble(sc); + rt2860_set_basicrates(sc, &ni->ni_rates); + rt2860_set_bssid(sc, ni->ni_bssid); + } + + if (vap->iv_opmode == IEEE80211_M_HOSTAP || + vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_MBSS) { + error = rt2860_setup_beacon(sc, vap); + if (error != 0) + return error; + } + + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + rt2860_enable_tsf_sync(sc); + rt2860_set_gp_timer(sc, 500); + } + + /* turn link LED on */ + rt2860_set_leds(sc, RT2860_LED_RADIO | + (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ? + RT2860_LED_LINK_2GHZ : RT2860_LED_LINK_5GHZ)); + } + return error; +} + +/* Read 16-bit from eFUSE ROM (>=RT3071 only.) */ +static uint16_t +rt3090_efuse_read_2(struct rt2860_softc *sc, uint16_t addr) +{ + uint32_t tmp; + uint16_t reg; + int ntries; + + addr *= 2; + /*- + * Read one 16-byte block into registers EFUSE_DATA[0-3]: + * DATA0: F E D C + * DATA1: B A 9 8 + * DATA2: 7 6 5 4 + * DATA3: 3 2 1 0 + */ + tmp = RAL_READ(sc, RT3070_EFUSE_CTRL); + tmp &= ~(RT3070_EFSROM_MODE_MASK | RT3070_EFSROM_AIN_MASK); + tmp |= (addr & ~0xf) << RT3070_EFSROM_AIN_SHIFT | RT3070_EFSROM_KICK; + RAL_WRITE(sc, RT3070_EFUSE_CTRL, tmp); + for (ntries = 0; ntries < 500; ntries++) { + tmp = RAL_READ(sc, RT3070_EFUSE_CTRL); + if (!(tmp & RT3070_EFSROM_KICK)) + break; + DELAY(2); + } + if (ntries == 500) + return 0xffff; + + if ((tmp & RT3070_EFUSE_AOUT_MASK) == RT3070_EFUSE_AOUT_MASK) + return 0xffff; /* address not found */ + + /* determine to which 32-bit register our 16-bit word belongs */ + reg = RT3070_EFUSE_DATA3 - (addr & 0xc); + tmp = RAL_READ(sc, reg); + + return (addr & 2) ? tmp >> 16 : tmp & 0xffff; +} + +/* + * Read 16 bits at address 'addr' from the serial EEPROM (either 93C46, + * 93C66 or 93C86). + */ +static uint16_t +rt2860_eeprom_read_2(struct rt2860_softc *sc, uint16_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; +} + +static __inline uint16_t +rt2860_srom_read(struct rt2860_softc *sc, uint8_t addr) +{ + /* either eFUSE ROM or EEPROM */ + return sc->sc_srom_read(sc, addr); +} + +static void +rt2860_intr_coherent(struct rt2860_softc *sc) +{ + uint32_t tmp; + + /* DMA finds data coherent event when checking the DDONE bit */ + + DPRINTF(("Tx/Rx Coherent interrupt\n")); + + /* restart DMA engine */ + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + tmp &= ~(RT2860_TX_WB_DDONE | RT2860_RX_DMA_EN | RT2860_TX_DMA_EN); + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + (void)rt2860_txrx_enable(sc); +} + +static void +rt2860_drain_stats_fifo(struct rt2860_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_node *ni; + uint32_t stat; + int retrycnt; + uint8_t wcid, mcs, pid; + + /* 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 >> RT2860_TXQ_WCID_SHIFT) & 0xff; + ni = sc->wcid2ni[wcid]; + + /* if no ACK was requested, no feedback is available */ + if (!(stat & RT2860_TXQ_ACKREQ) || wcid == 0xff || ni == NULL) + continue; + + /* update per-STA AMRR stats */ + if (stat & RT2860_TXQ_OK) { + /* + * Check if there were retries, ie if the Tx success + * rate is different from the requested rate. Note + * that it works only because we do not allow rate + * fallback from OFDM to CCK. + */ + mcs = (stat >> RT2860_TXQ_MCS_SHIFT) & 0x7f; + pid = (stat >> RT2860_TXQ_PID_SHIFT) & 0xf; + if (mcs + 1 != pid) + retrycnt = 1; + else + retrycnt = 0; + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); + } else { + ieee80211_ratectl_tx_complete(ni->ni_vap, ni, + IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); + ifp->if_oerrors++; + } + } +} + +static void +rt2860_tx_intr(struct rt2860_softc *sc, int qid) +{ + struct ifnet *ifp = sc->sc_ifp; + 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_tx_data *data = ring->data[ring->next]; + + if (data != NULL) { + bus_dmamap_sync(sc->txwi_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->txwi_dmat, data->map); + if (data->m->m_flags & M_TXCB) { + ieee80211_process_callback(data->ni, data->m, + 0); + } + m_freem(data->m); + ieee80211_free_node(data->ni); + data->m = NULL; + data->ni = NULL; + + SLIST_INSERT_HEAD(&sc->data_pool, data, next); + ring->data[ring->next] = NULL; + + ifp->if_opackets++; + } + ring->queued--; + ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; + } + + sc->sc_tx_timer = 0; + if (ring->queued < RT2860_TX_RING_COUNT) + sc->qfullmsk &= ~(1 << qid); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + rt2860_start_locked(ifp); +} + +/* + * Return the Rx chain with the highest RSSI for a given frame. + */ +static __inline 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; +} + +static void +rt2860_rx_intr(struct rt2860_softc *sc) +{ + struct rt2860_rx_radiotap_header *tap; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct mbuf *m, *m1; + bus_addr_t physaddr; + uint32_t hw; + uint16_t phy; + uint8_t ant; + int8_t rssi, nf; + int error; + + hw = RAL_READ(sc, RT2860_FS_DRX_IDX) & 0xfff; + while (sc->rxq.cur != hw) { + 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->rxq.desc_dmat, sc->rxq.desc_map, + BUS_DMASYNC_POSTREAD); + + if (__predict_false(!(rxd->sdl0 & htole16(RT2860_RX_DDONE)))) { + DPRINTF(("RXD DDONE bit not set!\n")); + break; /* should not happen */ + } + + if (__predict_false(rxd->flags & + htole32(RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { + ifp->if_ierrors++; + goto skip; + } + +#ifdef HW_CRYPTO + if (__predict_false(rxd->flags & htole32(RT2860_RX_MICERR))) { + /* report MIC failures to net80211 for TKIP */ + ic->ic_stats.is_rx_locmicfail++; + ieee80211_michael_mic_failure(ic, 0/* XXX */); + ifp->if_ierrors++; + goto skip; + } +#endif + + m1 = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR); + if (__predict_false(m1 == NULL)) { + ifp->if_ierrors++; + goto skip; + } + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + bus_dmamap_unload(sc->rxq.data_dmat, data->map); + + error = bus_dmamap_load(sc->rxq.data_dmat, data->map, + mtod(m1, void *), MCLBYTES, rt2860_dma_map_addr, + &physaddr, 0); + if (__predict_false(error != 0)) { + m_freem(m1); + + /* try to reload the old mbuf */ + error = bus_dmamap_load(sc->rxq.data_dmat, data->map, + mtod(data->m, void *), MCLBYTES, + rt2860_dma_map_addr, &physaddr, 0); + if (__predict_false(error != 0)) { + panic("%s: could not load old rx mbuf", + device_get_name(sc->sc_dev)); + } + /* physical address may have changed */ + rxd->sdp0 = htole32(physaddr); + ifp->if_ierrors++; + goto skip; + } + + /* + * New mbuf successfully loaded, update Rx ring and continue + * processing. + */ + m = data->m; + data->m = m1; + rxd->sdp0 = htole32(physaddr); + + 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 = le16toh(rxwi->len) & 0xfff; + + wh = mtod(m, struct ieee80211_frame *); +#ifdef HW_CRYPTO + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + /* frame is decrypted by hardware */ + wh->i_fc[1] &= ~IEEE80211_FC1_WEP; + } +#endif + + /* HW may insert 2 padding bytes after 802.11 header */ + if (rxd->flags & htole32(RT2860_RX_L2PAD)) { + u_int hdrlen = ieee80211_hdrsize(wh); + ovbcopy(wh, (caddr_t)wh + 2, hdrlen); + m->m_data += 2; + wh = mtod(m, struct ieee80211_frame *); + } + + ant = rt2860_maxrssi_chain(sc, rxwi); + rssi = rt2860_rssi2dbm(sc, rxwi->rssi[ant], ant); + nf = RT2860_NOISE_FLOOR; + + if (ieee80211_radiotap_active(ic)) { + tap = &sc->sc_rxtap; + tap->wr_flags = 0; + tap->wr_antenna = ant; + tap->wr_antsignal = nf + rssi; + tap->wr_antnoise = nf; + /* in case it can't be found below */ + tap->wr_rate = 2; + phy = le16toh(rxwi->phy); + switch (phy & RT2860_PHY_MODE) { + case RT2860_PHY_CCK: + switch ((phy & RT2860_PHY_MCS) & ~RT2860_PHY_SHPRE) { + case 0: tap->wr_rate = 2; break; + case 1: tap->wr_rate = 4; break; + case 2: tap->wr_rate = 11; break; + case 3: tap->wr_rate = 22; break; + } + if (phy & RT2860_PHY_SHPRE) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + break; + case RT2860_PHY_OFDM: + switch (phy & RT2860_PHY_MCS) { + case 0: tap->wr_rate = 12; break; + case 1: tap->wr_rate = 18; break; + case 2: tap->wr_rate = 24; break; + case 3: tap->wr_rate = 36; break; + case 4: tap->wr_rate = 48; break; + case 5: tap->wr_rate = 72; break; + case 6: tap->wr_rate = 96; break; + case 7: tap->wr_rate = 108; break; + } + break; + } + } + + RAL_UNLOCK(sc); + wh = mtod(m, struct ieee80211_frame *); + + /* send the frame to the 802.11 layer */ + ni = ieee80211_find_rxnode(ic, + (struct ieee80211_frame_min *)wh); + if (ni != NULL) { + (void)ieee80211_input(ni, m, rssi - nf, nf); + ieee80211_free_node(ni); + } else + (void)ieee80211_input_all(ic, m, rssi - nf, nf); + + RAL_LOCK(sc); + +skip: rxd->sdl0 &= ~htole16(RT2860_RX_DDONE); + + bus_dmamap_sync(sc->rxq.desc_dmat, sc->rxq.desc_map, + 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); +} + +static void +rt2860_tbtt_intr(struct rt2860_softc *sc) +{ +#if 0 + struct ieee80211com *ic = &sc->sc_ic; + +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + /* one less beacon until next DTIM */ + if (ic->ic_dtim_count == 0) + ic->ic_dtim_count = ic->ic_dtim_period - 1; + else + ic->ic_dtim_count--; + + /* update dynamic parts of beacon */ + rt2860_setup_beacon(sc); + + /* flush buffered multicast frames */ + if (ic->ic_dtim_count == 0) + ieee80211_notify_dtim(ic); + } +#endif + /* check if protection mode has changed */ + if ((sc->sc_ic_flags ^ ic->ic_flags) & IEEE80211_F_USEPROT) { + rt2860_updateprot(ic); + sc->sc_ic_flags = ic->ic_flags; + } +#endif +} + +static void +rt2860_gp_intr(struct rt2860_softc *sc) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + DPRINTFN(2, ("GP timeout state=%d\n", vap->iv_state)); + + if (vap->iv_state == IEEE80211_S_RUN) + rt2860_updatestats(sc); +} + +void +rt2860_intr(void *arg) +{ + struct rt2860_softc *sc = arg; + uint32_t r; + + RAL_LOCK(sc); + + r = RAL_READ(sc, RT2860_INT_STATUS); + if (__predict_false(r == 0xffffffff)) { + RAL_UNLOCK(sc); + return; /* device likely went away */ + } + if (r == 0) { + RAL_UNLOCK(sc); + return; /* not for us */ + } + + /* acknowledge interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, r); + + if (r & RT2860_TX_RX_COHERENT) + rt2860_intr_coherent(sc); + + if (r & RT2860_MAC_INT_2) /* TX status */ + 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_0) /* TBTT */ + rt2860_tbtt_intr(sc); + + if (r & RT2860_MAC_INT_3) /* Auto wakeup */ + /* TBD wakeup */; + + if (r & RT2860_MAC_INT_4) /* GP timer */ + rt2860_gp_intr(sc); + + RAL_UNLOCK(sc); +} + +static int +rt2860_tx(struct rt2860_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = ni->ni_vap; + struct rt2860_tx_ring *ring; + struct rt2860_tx_data *data; + struct rt2860_txd *txd; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + const struct ieee80211_txparam *tp; + struct ieee80211_key *k; + struct mbuf *m1; + bus_dma_segment_t segs[RT2860_MAX_SCATTER]; + bus_dma_segment_t *seg; + u_int hdrlen; + uint16_t qos, dur; + uint8_t type, qsel, mcs, pid, tid, qid; + int i, nsegs, ntxds, pad, rate, ridx, error; + + /* the data pool contains at least one element, pick the first */ + data = SLIST_FIRST(&sc->data_pool); + + wh = mtod(m, struct ieee80211_frame *); + + if (wh->i_fc[1] & IEEE80211_FC1_WEP) { + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + return ENOBUFS; + } + + /* packet header may have moved, reset our local pointer */ + wh = mtod(m, struct ieee80211_frame *); + } + + hdrlen = ieee80211_anyhdrsize(wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { + rate = tp->mcastrate; + } else if (m->m_flags & M_EAPOL) { + rate = tp->mgmtrate; + } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { + rate = tp->ucastrate; + } else { + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + rate &= IEEE80211_RATE_VAL; + + qid = M_WME_GETAC(m); + if (IEEE80211_QOS_HAS_SEQ(wh)) { + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + } else { + qos = 0; + tid = 0; + } + ring = &sc->txq[qid]; + ridx = ic->ic_rt->rateCodeToIndex[rate]; + + /* get MCS code from rate index */ + mcs = rt2860_rates[ridx].mcs; + + /* setup TX Wireless Information */ + txwi = data->txwi; + txwi->flags = 0; + /* let HW generate seq numbers for non-QoS frames */ + txwi->xflags = qos ? 0 : RT2860_TX_NSEQ; + if (type == IEEE80211_FC0_TYPE_DATA) + txwi->wcid = IEEE80211_AID(ni->ni_associd); + else + txwi->wcid = 0xff; + txwi->len = htole16(m->m_pkthdr.len); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + txwi->phy = htole16(RT2860_PHY_CCK); + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; + } else + txwi->phy = htole16(RT2860_PHY_OFDM); + txwi->phy |= htole16(mcs); + + /* + * We store the MCS code into the driver-private PacketID field. + * The PacketID is latched into TX_STAT_FIFO when Tx completes so + * that we know at which initial rate the frame was transmitted. + * We add 1 to the MCS code because setting the PacketID field to + * 0 means that we don't want feedback in TX_STAT_FIFO. + */ + pid = (mcs + 1) & 0xf; + txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (m->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold || + ((ic->ic_flags & IEEE80211_F_USEPROT) && + rt2860_rates[ridx].phy == IEEE80211_T_OFDM))) + txwi->txop = RT2860_TX_TXOP_HT; + else + txwi->txop = RT2860_TX_TXOP_BACKOFF; + + if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && + (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK)) { + txwi->xflags |= RT2860_TX_ACK; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + dur = rt2860_rates[ridx].sp_ack_dur; + else + dur = rt2860_rates[ridx].lp_ack_dur; + *(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; + + if (ieee80211_radiotap_active_vap(vap)) { + struct rt2860_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + if (mcs & RT2860_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + ieee80211_radiotap_tx(vap, m); + } + + pad = (hdrlen + 3) & ~3; + + /* copy and trim 802.11 header */ + memcpy(txwi + 1, wh, hdrlen); + m_adj(m, hdrlen); + + error = bus_dmamap_load_mbuf_sg(sc->txwi_dmat, data->map, m, segs, + &nsegs, 0); + if (__predict_false(error != 0 && error != EFBIG)) { + device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", + error); + m_freem(m); + return error; + } + if (__predict_true(error == 0)) { + /* determine how many TXDs are required */ + ntxds = 1 + (nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* not enough free TXDs, force mbuf defrag */ + bus_dmamap_unload(sc->txwi_dmat, data->map); + error = EFBIG; + } + } + if (__predict_false(error != 0)) { + m1 = m_defrag(m, M_DONTWAIT); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "could not defragment mbuf\n"); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(sc->txwi_dmat, data->map, m, + segs, &nsegs, 0); + if (__predict_false(error != 0)) { + device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", + error); + m_freem(m); + return error; + } + + /* determine how many TXDs are now required */ + ntxds = 1 + (nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* this is a hopeless case, drop the mbuf! */ + bus_dmamap_unload(sc->txwi_dmat, data->map); + m_freem(m); + return ENOBUFS; + } + } + + qsel = (qid < WME_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(sizeof (struct rt2860_txwi) + pad); + txd->flags = qsel; + + /* setup payload segments */ + seg = &segs[0]; + for (i = nsegs; i >= 2; i -= 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 (i > 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 = m; + data->ni = ni; + ring->data[ring->cur] = data; + + bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->txwi_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); + + DPRINTFN(4, ("sending frame qid=%d wcid=%d nsegs=%d ridx=%d\n", + qid, txwi->wcid, nsegs, ridx)); + + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + ring->queued += ntxds; + if (ring->queued >= RT2860_TX_RING_COUNT) + sc->qfullmsk |= 1 << qid; + + /* kick Tx */ + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); + + return 0; +} + +static int +rt2860_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_softc *sc = ifp->if_softc; + int error; + + RAL_LOCK(sc); + + /* prevent management frames from being sent if we're not ready */ + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + RAL_UNLOCK(sc); + m_freem(m); + ieee80211_free_node(ni); + return ENETDOWN; + } + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + error = rt2860_tx(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + error = rt2860_tx_raw(sc, m, ni, params); + } + if (error != 0) { + /* NB: m is reclaimed on tx failure */ + ieee80211_free_node(ni); + ifp->if_oerrors++; + } + sc->sc_tx_timer = 5; + RAL_UNLOCK(sc); + return error; +} + +static int +rt2860_tx_raw(struct rt2860_softc *sc, struct mbuf *m, + struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = ni->ni_vap; + struct rt2860_tx_ring *ring; + struct rt2860_tx_data *data; + struct rt2860_txd *txd; + struct rt2860_txwi *txwi; + struct ieee80211_frame *wh; + struct mbuf *m1; + bus_dma_segment_t segs[RT2860_MAX_SCATTER]; + bus_dma_segment_t *seg; + u_int hdrlen; + uint16_t dur; + uint8_t type, qsel, mcs, pid, tid, qid; + int i, nsegs, ntxds, pad, rate, ridx, error; + + /* the data pool contains at least one element, pick the first */ + data = SLIST_FIRST(&sc->data_pool); + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_hdrsize(wh); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + /* Choose a TX rate index. */ + rate = params->ibp_rate0; + ridx = ic->ic_rt->rateCodeToIndex[rate]; + if (ridx == (uint8_t)-1) { + /* XXX fall back to mcast/mgmt rate? */ + m_freem(m); + return EINVAL; + } + + qid = params->ibp_pri & 3; + tid = 0; + ring = &sc->txq[qid]; + + /* get MCS code from rate index */ + mcs = rt2860_rates[ridx].mcs; + + /* setup TX Wireless Information */ + txwi = data->txwi; + txwi->flags = 0; + /* let HW generate seq numbers for non-QoS frames */ + txwi->xflags = params->ibp_pri & 3 ? 0 : RT2860_TX_NSEQ; + txwi->wcid = 0xff; + txwi->len = htole16(m->m_pkthdr.len); + if (rt2860_rates[ridx].phy == IEEE80211_T_DS) { + txwi->phy = htole16(RT2860_PHY_CCK); + if (ridx != RT2860_RIDX_CCK1 && + (ic->ic_flags & IEEE80211_F_SHPREAMBLE)) + mcs |= RT2860_PHY_SHPRE; + } else + txwi->phy = htole16(RT2860_PHY_OFDM); + txwi->phy |= htole16(mcs); + + /* + * We store the MCS code into the driver-private PacketID field. + * The PacketID is latched into TX_STAT_FIFO when Tx completes so + * that we know at which initial rate the frame was transmitted. + * We add 1 to the MCS code because setting the PacketID field to + * 0 means that we don't want feedback in TX_STAT_FIFO. + */ + pid = (mcs + 1) & 0xf; + txwi->len |= htole16(pid << RT2860_TX_PID_SHIFT); + + /* check if RTS/CTS or CTS-to-self protection is required */ + if (params->ibp_flags & IEEE80211_BPF_RTS || + params->ibp_flags & IEEE80211_BPF_CTS) + txwi->txop = RT2860_TX_TXOP_HT; + else + txwi->txop = RT2860_TX_TXOP_BACKOFF; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) { + txwi->xflags |= RT2860_TX_ACK; + + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + dur = rt2860_rates[ridx].sp_ack_dur; + else + dur = rt2860_rates[ridx].lp_ack_dur; + *(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; + + if (ieee80211_radiotap_active_vap(vap)) { + struct rt2860_tx_radiotap_header *tap = &sc->sc_txtap; + + tap->wt_flags = 0; + tap->wt_rate = rate; + if (mcs & RT2860_PHY_SHPRE) + tap->wt_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + + ieee80211_radiotap_tx(vap, m); + } + + pad = (hdrlen + 3) & ~3; + + /* copy and trim 802.11 header */ + memcpy(txwi + 1, wh, hdrlen); + m_adj(m, hdrlen); + + error = bus_dmamap_load_mbuf_sg(sc->txwi_dmat, data->map, m, segs, + &nsegs, 0); + if (__predict_false(error != 0 && error != EFBIG)) { + device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", + error); + m_freem(m); + return error; + } + if (__predict_true(error == 0)) { + /* determine how many TXDs are required */ + ntxds = 1 + (nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* not enough free TXDs, force mbuf defrag */ + bus_dmamap_unload(sc->txwi_dmat, data->map); + error = EFBIG; + } + } + if (__predict_false(error != 0)) { + m1 = m_defrag(m, M_DONTWAIT); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "could not defragment mbuf\n"); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(sc->txwi_dmat, data->map, m, + segs, &nsegs, 0); + if (__predict_false(error != 0)) { + device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", + error); + m_freem(m); + return error; + } + + /* determine how many TXDs are now required */ + ntxds = 1 + (nsegs / 2); + + if (ring->queued + ntxds >= RT2860_TX_RING_COUNT) { + /* this is a hopeless case, drop the mbuf! */ + bus_dmamap_unload(sc->txwi_dmat, data->map); + m_freem(m); + return ENOBUFS; + } + } + + qsel = (qid < WME_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(sizeof (struct rt2860_txwi) + pad); + txd->flags = qsel; + + /* setup payload segments */ + seg = &segs[0]; + for (i = nsegs; i >= 2; i -= 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 (i > 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 = m; + data->ni = ni; + ring->data[ring->cur] = data; + + bus_dmamap_sync(sc->txwi_dmat, sc->txwi_map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(sc->txwi_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_PREWRITE); + + DPRINTFN(4, ("sending frame qid=%d wcid=%d nsegs=%d ridx=%d\n", + qid, txwi->wcid, nsegs, ridx)); + + ring->cur = (ring->cur + 1) % RT2860_TX_RING_COUNT; + ring->queued += ntxds; + if (ring->queued >= RT2860_TX_RING_COUNT) + sc->qfullmsk |= 1 << qid; + + /* kick Tx */ + RAL_WRITE(sc, RT2860_TX_CTX_IDX(qid), ring->cur); + + return 0; +} + +static void +rt2860_start(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + + RAL_LOCK(sc); + rt2860_start_locked(ifp); + RAL_UNLOCK(sc); +} + +static void +rt2860_start_locked(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211_node *ni; + struct mbuf *m; + + RAL_LOCK_ASSERT(sc); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) + return; + + for (;;) { + if (SLIST_EMPTY(&sc->data_pool) || sc->qfullmsk != 0) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (rt2860_tx(sc, m, ni) != 0) { + ieee80211_free_node(ni); + ifp->if_oerrors++; + continue; + } + sc->sc_tx_timer = 5; + } +} + +static void +rt2860_watchdog(void *arg) +{ + struct rt2860_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + RAL_LOCK_ASSERT(sc); + + KASSERT(ifp->if_drv_flags & IFF_DRV_RUNNING, ("not running")); + + if (sc->sc_invalid) /* card ejected */ + return; + + if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { + if_printf(ifp, "device timeout\n"); + rt2860_stop_locked(sc); + rt2860_init_locked(sc); + ifp->if_oerrors++; + return; + } + callout_reset(&sc->watchdog_ch, hz, rt2860_watchdog, sc); +} + +static int +rt2860_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + struct ifreq *ifr = (struct ifreq *)data; + int error = 0, startall = 0; + + switch (cmd) { + case SIOCSIFFLAGS: + RAL_LOCK(sc); + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { + rt2860_init_locked(sc); + startall = 1; + } else + rt2860_update_promisc(ifp); + } else { + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + rt2860_stop_locked(sc); + } + RAL_UNLOCK(sc); + if (startall) + ieee80211_start_all(ic); + break; + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); + break; + case SIOCSIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; + default: + error = EINVAL; + break; + } + 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) { + device_printf(sc->sc_dev, + "could not write to BBP through MCU\n"); + return; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | reg << 8 | val); + RAL_BARRIER_WRITE(sc); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0, 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) { + device_printf(sc->sc_dev, + "could not read from BBP through MCU\n"); + return 0; + } + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, RT2860_BBP_RW_PARALLEL | + RT2860_BBP_CSR_KICK | RT2860_BBP_CSR_READ | reg << 8); + RAL_BARRIER_WRITE(sc); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_BBP, 0, 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); + } + device_printf(sc->sc_dev, "could not read from BBP through MCU\n"); + + return 0; +} + +/* + * Write to one of the 4 programmable 24-bit RF registers. + */ +static 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) { + device_printf(sc->sc_dev, "could not write to RF\n"); + 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); +} + +static uint8_t +rt3090_rf_read(struct rt2860_softc *sc, uint8_t reg) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT3070_RF_CSR_CFG) & RT3070_RF_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read RF register\n"); + return 0xff; + } + tmp = RT3070_RF_KICK | reg << 8; + RAL_WRITE(sc, RT3070_RF_CSR_CFG, tmp); + + for (ntries = 0; ntries < 100; ntries++) { + tmp = RAL_READ(sc, RT3070_RF_CSR_CFG); + if (!(tmp & RT3070_RF_KICK)) + break; + DELAY(1); + } + if (ntries == 100) { + device_printf(sc->sc_dev, "could not read RF register\n"); + return 0xff; + } + return tmp & 0xff; +} + +void +rt3090_rf_write(struct rt2860_softc *sc, uint8_t reg, uint8_t val) +{ + uint32_t tmp; + int ntries; + + for (ntries = 0; ntries < 10; ntries++) { + if (!(RAL_READ(sc, RT3070_RF_CSR_CFG) & RT3070_RF_KICK)) + break; + DELAY(10); + } + if (ntries == 10) { + device_printf(sc->sc_dev, "could not write to RF\n"); + return; + } + + tmp = RT3070_RF_WRITE | RT3070_RF_KICK | reg << 8 | val; + RAL_WRITE(sc, RT3070_RF_CSR_CFG, tmp); +} + +/* + * Send a command to the 8051 microcontroller unit. + */ +int +rt2860_mcu_cmd(struct rt2860_softc *sc, uint8_t cmd, uint16_t arg, int wait) +{ + int slot, ntries; + uint32_t tmp; + uint8_t cid; + + for (ntries = 0; ntries < 100; ntries++) { + if (!(RAL_READ(sc, RT2860_H2M_MAILBOX) & RT2860_H2M_BUSY)) + break; + DELAY(2); + } + if (ntries == 100) + return EIO; + + cid = wait ? cmd : RT2860_TOKEN_NO_INTR; + RAL_WRITE(sc, RT2860_H2M_MAILBOX, RT2860_H2M_BUSY | cid << 16 | arg); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_HOST_CMD, cmd); + + if (!wait) + return 0; + /* wait for the command to complete */ + for (ntries = 0; ntries < 200; ntries++) { + tmp = RAL_READ(sc, RT2860_H2M_MAILBOX_CID); + /* find the command slot */ + for (slot = 0; slot < 4; slot++, tmp >>= 8) + if ((tmp & 0xff) == cid) + break; + if (slot < 4) + break; + DELAY(100); + } + if (ntries == 200) { + /* clear command and status */ + RAL_WRITE(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); + RAL_WRITE(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); + return ETIMEDOUT; + } + /* get command status (1 means success) */ + tmp = RAL_READ(sc, RT2860_H2M_MAILBOX_STATUS); + tmp = (tmp >> (slot * 8)) & 0xff; + DPRINTF(("MCU command=0x%02x slot=%d status=0x%02x\n", + cmd, slot, tmp)); + /* clear command and status */ + RAL_WRITE(sc, RT2860_H2M_MAILBOX_STATUS, 0xffffffff); + RAL_WRITE(sc, RT2860_H2M_MAILBOX_CID, 0xffffffff); + return (tmp == 1) ? 0 : EIO; +} + +static 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 +} + +static void +rt2860_set_txpreamble(struct rt2860_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_AUTO_RSP_CFG); + tmp &= ~RT2860_CCK_SHORT_EN; + if (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, + const struct ieee80211_rateset *rs) +{ +#define RV(r) ((r) & IEEE80211_RATE_VAL) + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t mask = 0; + uint8_t rate; + int i; + + for (i = 0; i < rs->rs_nrates; i++) { + rate = rs->rs_rates[i]; + + if (!(rate & IEEE80211_RATE_BASIC)) + continue; + + mask |= 1 << ic->ic_rt->rateCodeToIndex[RV(rate)]; + } + + RAL_WRITE(sc, RT2860_LEGACY_BASIC_RATE, mask); +#undef RV +} + +static void +rt2860_scan_start(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_softc *sc = ifp->if_softc; + uint32_t tmp; + + 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)); + rt2860_set_gp_timer(sc, 0); +} + +static void +rt2860_scan_end(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap->iv_state == IEEE80211_S_RUN) { + rt2860_enable_tsf_sync(sc); + rt2860_set_gp_timer(sc, 500); + } +} + +static void +rt2860_set_channel(struct ieee80211com *ic) +{ + struct ifnet *ifp = ic->ic_ifp; + struct rt2860_softc *sc = ifp->if_softc; + + RAL_LOCK(sc); + rt2860_switch_chan(sc, ic->ic_curchan); + RAL_UNLOCK(sc); +} + +static void +rt2860_select_chan_group(struct rt2860_softc *sc, int group) +{ + uint32_t tmp; + uint8_t agc; + + 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, 0x00); + + if (group == 0) { + if (sc->ext_2ghz_lna) { + rt2860_mcu_bbp_write(sc, 82, 0x62); + rt2860_mcu_bbp_write(sc, 75, 0x46); + } else { + rt2860_mcu_bbp_write(sc, 82, 0x84); + rt2860_mcu_bbp_write(sc, 75, 0x50); + } + } else { + if (sc->ext_5ghz_lna) { + rt2860_mcu_bbp_write(sc, 82, 0xf2); + rt2860_mcu_bbp_write(sc, 75, 0x46); + } else { + rt2860_mcu_bbp_write(sc, 82, 0xf2); + rt2860_mcu_bbp_write(sc, 75, 0x50); + } + } + + 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 | RT2860_LNA_PE0_EN; + if (sc->nrxchains > 1) + tmp |= RT2860_LNA_PE1_EN; + if (sc->mac_ver == 0x3593 && sc->nrxchains > 2) + tmp |= RT3593_LNA_PE2_EN; + if (group == 0) { /* 2GHz */ + tmp |= RT2860_PA_PE_G0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_G1_EN; + if (sc->mac_ver == 0x3593 && sc->ntxchains > 2) + tmp |= RT3593_PA_PE_G2_EN; + } else { /* 5GHz */ + tmp |= RT2860_PA_PE_A0_EN; + if (sc->ntxchains > 1) + tmp |= RT2860_PA_PE_A1_EN; + if (sc->mac_ver == 0x3593 && sc->ntxchains > 2) + tmp |= RT3593_PA_PE_A2_EN; + } + RAL_WRITE(sc, RT2860_TX_PIN_CFG, tmp); + + if (sc->mac_ver == 0x3593) { + tmp = RAL_READ(sc, RT2860_GPIO_CTRL); + if (sc->sc_flags & RT2860_PCIE) { + tmp &= ~0x01010000; + if (group == 0) + tmp |= 0x00010000; + } else { + tmp &= ~0x00008080; + if (group == 0) + tmp |= 0x00000080; + } + tmp = (tmp & ~0x00001000) | 0x00000010; + RAL_WRITE(sc, RT2860_GPIO_CTRL, tmp); + } + + /* set initial AGC value */ + if (group == 0) { /* 2GHz band */ + if (sc->mac_ver >= 0x3071) + agc = 0x1c + sc->lna[0] * 2; + else + agc = 0x2e + sc->lna[0]; + } else { /* 5GHz band */ + agc = 0x32 + (sc->lna[group] * 5) / 3; + } + rt2860_mcu_bbp_write(sc, 66, agc); + + DELAY(1000); +} + +static void +rt2860_set_chan(struct rt2860_softc *sc, u_int chan) +{ + const struct rfprog *rfprog = rt2860_rf2850; + uint32_t r2, r3, r4; + int8_t txpow1, txpow2; + u_int i; + + /* 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 */ + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + if (chan > 14) { + if (txpow1 >= 0) + txpow1 = txpow1 << 1 | 1; + else + txpow1 = (7 + txpow1) << 1; + if (txpow2 >= 0) + txpow2 = txpow2 << 1 | 1; + else + txpow2 = (7 + txpow2) << 1; + } + r3 = rfprog[i].r3 | txpow1 << 7; + r4 = rfprog[i].r4 | sc->freq << 13 | txpow2 << 4; + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3); + rt2860_rf_write(sc, RT2860_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3 | 1); + rt2860_rf_write(sc, RT2860_RF4, r4); + + DELAY(200); + + rt2860_rf_write(sc, RT2860_RF1, rfprog[i].r1); + rt2860_rf_write(sc, RT2860_RF2, r2); + rt2860_rf_write(sc, RT2860_RF3, r3); + rt2860_rf_write(sc, RT2860_RF4, r4); +} + +static void +rt3090_set_chan(struct rt2860_softc *sc, u_int chan) +{ + int8_t txpow1, txpow2; + uint8_t rf; + int i; + + /* RT3090 is 2GHz only */ + KASSERT(chan >= 1 && chan <= 14, ("chan %d not support", chan)); + + /* find the settings for this channel (we know it exists) */ + for (i = 0; rt2860_rf2850[i].chan != chan; i++); + + /* use Tx power values from EEPROM */ + txpow1 = sc->txpow1[i]; + txpow2 = sc->txpow2[i]; + + rt3090_rf_write(sc, 2, rt3090_freqs[i].n); + rf = rt3090_rf_read(sc, 3); + rf = (rf & ~0x0f) | rt3090_freqs[i].k; + rt3090_rf_write(sc, 3, rf); + rf = rt3090_rf_read(sc, 6); + rf = (rf & ~0x03) | rt3090_freqs[i].r; + rt3090_rf_write(sc, 6, rf); + + /* set Tx0 power */ + rf = rt3090_rf_read(sc, 12); + rf = (rf & ~0x1f) | txpow1; + rt3090_rf_write(sc, 12, rf); + + /* set Tx1 power */ + rf = rt3090_rf_read(sc, 13); + rf = (rf & ~0x1f) | txpow2; + rt3090_rf_write(sc, 13, rf); + + rf = rt3090_rf_read(sc, 1); + rf &= ~0xfc; + if (sc->ntxchains == 1) + rf |= RT3070_TX1_PD | RT3070_TX2_PD; + else if (sc->ntxchains == 2) + rf |= RT3070_TX2_PD; + if (sc->nrxchains == 1) + rf |= RT3070_RX1_PD | RT3070_RX2_PD; + else if (sc->nrxchains == 2) + rf |= RT3070_RX2_PD; + rt3090_rf_write(sc, 1, rf); + + /* set RF offset */ + rf = rt3090_rf_read(sc, 23); + rf = (rf & ~0x7f) | sc->freq; + rt3090_rf_write(sc, 23, rf); + + /* program RF filter */ + rf = rt3090_rf_read(sc, 24); /* Tx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + rt3090_rf_write(sc, 24, rf); + rf = rt3090_rf_read(sc, 31); /* Rx */ + rf = (rf & ~0x3f) | sc->rf24_20mhz; + rt3090_rf_write(sc, 31, rf); + + /* enable RF tuning */ + rf = rt3090_rf_read(sc, 7); + rt3090_rf_write(sc, 7, rf | RT3070_TUNE); +} + +static int +rt3090_rf_init(struct rt2860_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + uint32_t tmp; + uint8_t rf, bbp; + int i; + + rf = rt3090_rf_read(sc, 30); + /* toggle RF R30 bit 7 */ + rt3090_rf_write(sc, 30, rf | 0x80); + DELAY(1000); + rt3090_rf_write(sc, 30, rf & ~0x80); + + tmp = RAL_READ(sc, RT3070_LDO_CFG0); + tmp &= ~0x1f000000; + if (sc->patch_dac && sc->mac_rev < 0x0211) + tmp |= 0x0d000000; /* 1.35V */ + else + tmp |= 0x01000000; /* 1.2V */ + RAL_WRITE(sc, RT3070_LDO_CFG0, tmp); + + /* patch LNA_PE_G1 */ + tmp = RAL_READ(sc, RT3070_GPIO_SWITCH); + RAL_WRITE(sc, RT3070_GPIO_SWITCH, tmp & ~0x20); + + /* initialize RF registers to default value */ + for (i = 0; i < N(rt3090_def_rf); i++) { + rt3090_rf_write(sc, rt3090_def_rf[i].reg, + rt3090_def_rf[i].val); + } + + /* select 20MHz bandwidth */ + rt3090_rf_write(sc, 31, 0x14); + + rf = rt3090_rf_read(sc, 6); + rt3090_rf_write(sc, 6, rf | 0x40); + + if (sc->mac_ver != 0x3593) { + /* calibrate filter for 20MHz bandwidth */ + sc->rf24_20mhz = 0x1f; /* default value */ + rt3090_filter_calib(sc, 0x07, 0x16, &sc->rf24_20mhz); + + /* select 40MHz bandwidth */ + bbp = rt2860_mcu_bbp_read(sc, 4); + rt2860_mcu_bbp_write(sc, 4, (bbp & ~0x08) | 0x10); + rf = rt3090_rf_read(sc, 31); + rt3090_rf_write(sc, 31, rf | 0x20); + + /* calibrate filter for 40MHz bandwidth */ + sc->rf24_40mhz = 0x2f; /* default value */ + rt3090_filter_calib(sc, 0x27, 0x19, &sc->rf24_40mhz); + + /* go back to 20MHz bandwidth */ + bbp = rt2860_mcu_bbp_read(sc, 4); + rt2860_mcu_bbp_write(sc, 4, bbp & ~0x18); + } + if (sc->mac_rev < 0x0211) + rt3090_rf_write(sc, 27, 0x03); + + tmp = RAL_READ(sc, RT3070_OPT_14); + RAL_WRITE(sc, RT3070_OPT_14, tmp | 1); + + if (sc->rf_rev == RT3070_RF_3020) + rt3090_set_rx_antenna(sc, 0); + + bbp = rt2860_mcu_bbp_read(sc, 138); + if (sc->mac_ver == 0x3593) { + if (sc->ntxchains == 1) + bbp |= 0x60; /* turn off DAC1 and DAC2 */ + else if (sc->ntxchains == 2) + bbp |= 0x40; /* turn off DAC2 */ + if (sc->nrxchains == 1) + bbp &= ~0x06; /* turn off ADC1 and ADC2 */ + else if (sc->nrxchains == 2) + bbp &= ~0x04; /* turn off ADC2 */ + } else { + if (sc->ntxchains == 1) + bbp |= 0x20; /* turn off DAC1 */ + if (sc->nrxchains == 1) + bbp &= ~0x02; /* turn off ADC1 */ + } + rt2860_mcu_bbp_write(sc, 138, bbp); + + rf = rt3090_rf_read(sc, 1); + rf &= ~(RT3070_RX0_PD | RT3070_TX0_PD); + rf |= RT3070_RF_BLOCK | RT3070_RX1_PD | RT3070_TX1_PD; + rt3090_rf_write(sc, 1, rf); + + rf = rt3090_rf_read(sc, 15); + rt3090_rf_write(sc, 15, rf & ~RT3070_TX_LO2); + + rf = rt3090_rf_read(sc, 17); + rf &= ~RT3070_TX_LO1; + if (sc->mac_rev >= 0x0211 && !sc->ext_2ghz_lna) + rf |= 0x20; /* fix for long range Rx issue */ + if (sc->txmixgain_2ghz >= 2) + rf = (rf & ~0x7) | sc->txmixgain_2ghz; + rt3090_rf_write(sc, 17, rf); + + rf = rt3090_rf_read(sc, 20); + rt3090_rf_write(sc, 20, rf & ~RT3070_RX_LO1); + + rf = rt3090_rf_read(sc, 21); + rt3090_rf_write(sc, 21, rf & ~RT3070_RX_LO2); + + return 0; +#undef N +} + +void +rt3090_rf_wakeup(struct rt2860_softc *sc) +{ + uint32_t tmp; + uint8_t rf; + + if (sc->mac_ver == 0x3593) { + /* enable VCO */ + rf = rt3090_rf_read(sc, 1); + rt3090_rf_write(sc, 1, rf | RT3593_VCO); + + /* initiate VCO calibration */ + rf = rt3090_rf_read(sc, 3); + rt3090_rf_write(sc, 3, rf | RT3593_VCOCAL); + + /* enable VCO bias current control */ + rf = rt3090_rf_read(sc, 6); + rt3090_rf_write(sc, 6, rf | RT3593_VCO_IC); + + /* initiate res calibration */ + rf = rt3090_rf_read(sc, 2); + rt3090_rf_write(sc, 2, rf | RT3593_RESCAL); + + /* set reference current control to 0.33 mA */ + rf = rt3090_rf_read(sc, 22); + rf &= ~RT3593_CP_IC_MASK; + rf |= 1 << RT3593_CP_IC_SHIFT; + rt3090_rf_write(sc, 22, rf); + + /* enable RX CTB */ + rf = rt3090_rf_read(sc, 46); + rt3090_rf_write(sc, 46, rf | RT3593_RX_CTB); + + rf = rt3090_rf_read(sc, 20); + rf &= ~(RT3593_LDO_RF_VC_MASK | RT3593_LDO_PLL_VC_MASK); + rt3090_rf_write(sc, 20, rf); + } else { + /* enable RF block */ + rf = rt3090_rf_read(sc, 1); + rt3090_rf_write(sc, 1, rf | RT3070_RF_BLOCK); + + /* enable VCO bias current control */ + rf = rt3090_rf_read(sc, 7); + rt3090_rf_write(sc, 7, rf | 0x30); + + rf = rt3090_rf_read(sc, 9); + rt3090_rf_write(sc, 9, rf | 0x0e); + + /* enable RX CTB */ + rf = rt3090_rf_read(sc, 21); + rt3090_rf_write(sc, 21, rf | RT3070_RX_CTB); + + /* fix Tx to Rx IQ glitch by raising RF voltage */ + rf = rt3090_rf_read(sc, 27); + rf &= ~0x77; + if (sc->mac_rev < 0x0211) + rf |= 0x03; + rt3090_rf_write(sc, 27, rf); + } + if (sc->patch_dac && sc->mac_rev < 0x0211) { + tmp = RAL_READ(sc, RT3070_LDO_CFG0); + tmp = (tmp & ~0x1f000000) | 0x0d000000; + RAL_WRITE(sc, RT3070_LDO_CFG0, tmp); + } +} + +int +rt3090_filter_calib(struct rt2860_softc *sc, uint8_t init, uint8_t target, + uint8_t *val) +{ + uint8_t rf22, rf24; + uint8_t bbp55_pb, bbp55_sb, delta; + int ntries; + + /* program filter */ + rf24 = rt3090_rf_read(sc, 24); + rf24 = (rf24 & 0xc0) | init; /* initial filter value */ + rt3090_rf_write(sc, 24, rf24); + + /* enable baseband loopback mode */ + rf22 = rt3090_rf_read(sc, 22); + rt3090_rf_write(sc, 22, rf22 | RT3070_BB_LOOPBACK); + + /* set power and frequency of passband test tone */ + rt2860_mcu_bbp_write(sc, 24, 0x00); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + rt2860_mcu_bbp_write(sc, 25, 0x90); + DELAY(1000); + /* read received power */ + bbp55_pb = rt2860_mcu_bbp_read(sc, 55); + if (bbp55_pb != 0) + break; + } + if (ntries == 100) + return ETIMEDOUT; + + /* set power and frequency of stopband test tone */ + rt2860_mcu_bbp_write(sc, 24, 0x06); + for (ntries = 0; ntries < 100; ntries++) { + /* transmit test tone */ + rt2860_mcu_bbp_write(sc, 25, 0x90); + DELAY(1000); + /* read received power */ + bbp55_sb = rt2860_mcu_bbp_read(sc, 55); + + delta = bbp55_pb - bbp55_sb; + if (delta > target) + break; + + /* reprogram filter */ + rf24++; + rt3090_rf_write(sc, 24, rf24); + } + if (ntries < 100) { + if (rf24 != init) + rf24--; /* backtrack */ + *val = rf24; + rt3090_rf_write(sc, 24, rf24); + } + + /* restore initial state */ + rt2860_mcu_bbp_write(sc, 24, 0x00); + + /* disable baseband loopback mode */ + rf22 = rt3090_rf_read(sc, 22); + rt3090_rf_write(sc, 22, rf22 & ~RT3070_BB_LOOPBACK); + + return 0; +} + +static void +rt3090_rf_setup(struct rt2860_softc *sc) +{ + uint8_t bbp; + int i; + + if (sc->mac_rev >= 0x0211) { + /* enable DC filter */ + rt2860_mcu_bbp_write(sc, 103, 0xc0); + + /* improve power consumption */ + bbp = rt2860_mcu_bbp_read(sc, 31); + rt2860_mcu_bbp_write(sc, 31, bbp & ~0x03); + } + + RAL_WRITE(sc, RT2860_TX_SW_CFG1, 0); + if (sc->mac_rev < 0x0211) { + RAL_WRITE(sc, RT2860_TX_SW_CFG2, + sc->patch_dac ? 0x2c : 0x0f); + } else + RAL_WRITE(sc, RT2860_TX_SW_CFG2, 0); + + /* initialize RF registers from ROM */ + for (i = 0; i < 10; i++) { + if (sc->rf[i].reg == 0 || sc->rf[i].reg == 0xff) + continue; + rt3090_rf_write(sc, sc->rf[i].reg, sc->rf[i].val); + } +} + +static void +rt2860_set_leds(struct rt2860_softc *sc, uint16_t which) +{ + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LEDS, + which | (sc->leds & 0x7f), 0); +} + +/* + * Hardware has a general-purpose programmable timer interrupt that can + * periodically raise MAC_INT_4. + */ +static void +rt2860_set_gp_timer(struct rt2860_softc *sc, int ms) +{ + uint32_t tmp; + + /* disable GP timer before reprogramming it */ + tmp = RAL_READ(sc, RT2860_INT_TIMER_EN); + RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp & ~RT2860_GP_TIMER_EN); + + if (ms == 0) + return; + + tmp = RAL_READ(sc, RT2860_INT_TIMER_CFG); + ms *= 16; /* Unit: 64us */ + tmp = (tmp & 0xffff) | ms << RT2860_GP_TIMER_SHIFT; + RAL_WRITE(sc, RT2860_INT_TIMER_CFG, tmp); + + /* enable GP timer */ + tmp = RAL_READ(sc, RT2860_INT_TIMER_EN); + RAL_WRITE(sc, RT2860_INT_TIMER_EN, tmp | RT2860_GP_TIMER_EN); +} + +static 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); +} + +static 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 | 0xff << 16); +} + +static void +rt2860_updateslot(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + 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); +} + +static void +rt2860_updateprot(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + + tmp = RT2860_RTSTH_EN | RT2860_PROT_NAV_SHORT | RT2860_TXOP_ALLOW_ALL; + /* setup protection frame rate (MCS code) */ + tmp |= IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan) ? + rt2860_rates[RT2860_RIDX_OFDM6].mcs : + rt2860_rates[RT2860_RIDX_CCK11].mcs; + + /* CCK frames don't require protection */ + RAL_WRITE(sc, RT2860_CCK_PROT_CFG, tmp); + + if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + tmp |= RT2860_PROT_CTRL_RTS_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + tmp |= RT2860_PROT_CTRL_CTS; + } + RAL_WRITE(sc, RT2860_OFDM_PROT_CFG, tmp); +} + +static void +rt2860_update_promisc(struct ifnet *ifp) +{ + struct rt2860_softc *sc = ifp->if_softc; + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_RX_FILTR_CFG); + tmp &= ~RT2860_DROP_NOT_MYBSS; + if (!(ifp->if_flags & IFF_PROMISC)) + tmp |= RT2860_DROP_NOT_MYBSS; + RAL_WRITE(sc, RT2860_RX_FILTR_CFG, tmp); +} + +static int +rt2860_updateedca(struct ieee80211com *ic) +{ + struct rt2860_softc *sc = ic->ic_ifp->if_softc; + const struct wmeParams *wmep; + int aci; + + wmep = ic->ic_wme.wme_chanParams.cap_wmeParams; + + /* update MAC TX configuration registers */ + for (aci = 0; aci < WME_NUM_AC; aci++) { + RAL_WRITE(sc, RT2860_EDCA_AC_CFG(aci), + wmep[aci].wmep_logcwmax << 16 | + wmep[aci].wmep_logcwmin << 12 | + wmep[aci].wmep_aifsn << 8 | + wmep[aci].wmep_txopLimit); + } + + /* update SCH/DMA registers too */ + RAL_WRITE(sc, RT2860_WMM_AIFSN_CFG, + wmep[WME_AC_VO].wmep_aifsn << 12 | + wmep[WME_AC_VI].wmep_aifsn << 8 | + wmep[WME_AC_BK].wmep_aifsn << 4 | + wmep[WME_AC_BE].wmep_aifsn); + RAL_WRITE(sc, RT2860_WMM_CWMIN_CFG, + wmep[WME_AC_VO].wmep_logcwmin << 12 | + wmep[WME_AC_VI].wmep_logcwmin << 8 | + wmep[WME_AC_BK].wmep_logcwmin << 4 | + wmep[WME_AC_BE].wmep_logcwmin); + RAL_WRITE(sc, RT2860_WMM_CWMAX_CFG, + wmep[WME_AC_VO].wmep_logcwmax << 12 | + wmep[WME_AC_VI].wmep_logcwmax << 8 | + wmep[WME_AC_BK].wmep_logcwmax << 4 | + wmep[WME_AC_BE].wmep_logcwmax); + RAL_WRITE(sc, RT2860_WMM_TXOP0_CFG, + wmep[WME_AC_BK].wmep_txopLimit << 16 | + wmep[WME_AC_BE].wmep_txopLimit); + RAL_WRITE(sc, RT2860_WMM_TXOP1_CFG, + wmep[WME_AC_VO].wmep_txopLimit << 16 | + wmep[WME_AC_VI].wmep_txopLimit); + + return 0; +} + +#ifdef HW_CRYPTO +static int +rt2860_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct rt2860_softc *sc = ic->ic_softc; + bus_size_t base; + uint32_t attr; + uint8_t mode, wcid, iv[8]; + + /* defer setting of WEP keys until interface is brought up */ + if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING)) + return 0; + + /* map net80211 cipher to RT2860 security mode */ + switch (k->k_cipher) { + case IEEE80211_CIPHER_WEP40: + mode = RT2860_MODE_WEP40; + break; + case IEEE80211_CIPHER_WEP104: + mode = RT2860_MODE_WEP104; + break; + case IEEE80211_CIPHER_TKIP: + mode = RT2860_MODE_TKIP; + break; + case IEEE80211_CIPHER_CCMP: + mode = RT2860_MODE_AES_CCMP; + break; + default: + return EINVAL; + } + + if (k->k_flags & IEEE80211_KEY_GROUP) { + wcid = 0; /* NB: update WCID0 for group keys */ + base = RT2860_SKEY(0, k->k_id); + } else { + wcid = ((struct rt2860_node *)ni)->wcid; + base = RT2860_PKEY(wcid); + } + + if (k->k_cipher == IEEE80211_CIPHER_TKIP) { + RAL_WRITE_REGION_1(sc, base, k->k_key, 16); +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + RAL_WRITE_REGION_1(sc, base + 16, &k->k_key[16], 8); + RAL_WRITE_REGION_1(sc, base + 24, &k->k_key[24], 8); + } else +#endif + { + RAL_WRITE_REGION_1(sc, base + 16, &k->k_key[24], 8); + RAL_WRITE_REGION_1(sc, base + 24, &k->k_key[16], 8); + } + } else + RAL_WRITE_REGION_1(sc, base, k->k_key, k->k_len); + + if (!(k->k_flags & IEEE80211_KEY_GROUP) || + (k->k_flags & IEEE80211_KEY_TX)) { + /* set initial packet number in IV+EIV */ + if (k->k_cipher == IEEE80211_CIPHER_WEP40 || + k->k_cipher == IEEE80211_CIPHER_WEP104) { + uint32_t val = arc4random(); + /* skip weak IVs from Fluhrer/Mantin/Shamir */ + if (val >= 0x03ff00 && (val & 0xf8ff00) == 0x00ff00) + val += 0x000100; + iv[0] = val; + iv[1] = val >> 8; + iv[2] = val >> 16; + iv[3] = k->k_id << 6; + iv[4] = iv[5] = iv[6] = iv[7] = 0; + } else { + if (k->k_cipher == IEEE80211_CIPHER_TKIP) { + iv[0] = k->k_tsc >> 8; + iv[1] = (iv[0] | 0x20) & 0x7f; + iv[2] = k->k_tsc; + } else /* CCMP */ { + iv[0] = k->k_tsc; + iv[1] = k->k_tsc >> 8; + iv[2] = 0; + } + iv[3] = k->k_id << 6 | IEEE80211_WEP_EXTIV; + iv[4] = k->k_tsc >> 16; + iv[5] = k->k_tsc >> 24; + iv[6] = k->k_tsc >> 32; + iv[7] = k->k_tsc >> 40; + } + RAL_WRITE_REGION_1(sc, RT2860_IVEIV(wcid), iv, 8); + } + + if (k->k_flags & IEEE80211_KEY_GROUP) { + /* install group key */ + attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); + attr &= ~(0xf << (k->k_id * 4)); + attr |= mode << (k->k_id * 4); + RAL_WRITE(sc, RT2860_SKEY_MODE_0_7, attr); + } else { + /* install pairwise key */ + attr = RAL_READ(sc, RT2860_WCID_ATTR(wcid)); + attr = (attr & ~0xf) | (mode << 1) | RT2860_RX_PKEY_EN; + RAL_WRITE(sc, RT2860_WCID_ATTR(wcid), attr); + } + return 0; +} + +static void +rt2860_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct rt2860_softc *sc = ic->ic_softc; + uint32_t attr; + uint8_t wcid; + + if (k->k_flags & IEEE80211_KEY_GROUP) { + /* remove group key */ + attr = RAL_READ(sc, RT2860_SKEY_MODE_0_7); + attr &= ~(0xf << (k->k_id * 4)); + RAL_WRITE(sc, RT2860_SKEY_MODE_0_7, attr); + + } else { + /* remove pairwise key */ + wcid = ((struct rt2860_node *)ni)->wcid; + attr = RAL_READ(sc, RT2860_WCID_ATTR(wcid)); + attr &= ~0xf; + RAL_WRITE(sc, RT2860_WCID_ATTR(wcid), attr); + } +} +#endif + +static int8_t +rt2860_rssi2dbm(struct rt2860_softc *sc, uint8_t rssi, uint8_t rxchain) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211_channel *c = ic->ic_curchan; + 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; +} + +/* + * 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; +} + +static 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"; + case RT3070_RF_3020: return "RT3020"; + case RT3070_RF_2020: return "RT2020"; + case RT3070_RF_3021: return "RT3021"; + case RT3070_RF_3022: return "RT3022"; + case RT3070_RF_3052: return "RT3052"; + case RT3070_RF_3320: return "RT3320"; + case RT3070_RF_3053: return "RT3053"; + default: return "unknown"; + } +} + +static int +rt2860_read_eeprom(struct rt2860_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) +{ + int8_t delta_2ghz, delta_5ghz; + uint32_t tmp; + uint16_t val; + int ridx, ant, i; + + /* check whether the ROM is eFUSE ROM or EEPROM */ + sc->sc_srom_read = rt2860_eeprom_read_2; + if (sc->mac_ver >= 0x3071) { + tmp = RAL_READ(sc, RT3070_EFUSE_CTRL); + DPRINTF(("EFUSE_CTRL=0x%08x\n", tmp)); + if (tmp & RT3070_SEL_EFUSE) + sc->sc_srom_read = rt3090_efuse_read_2; + } + + /* read EEPROM version */ + val = rt2860_srom_read(sc, RT2860_EEPROM_VERSION); + DPRINTF(("EEPROM rev=%d, FAE=%d\n", val & 0xff, val >> 8)); + + /* read MAC address */ + val = rt2860_srom_read(sc, RT2860_EEPROM_MAC01); + macaddr[0] = val & 0xff; + macaddr[1] = val >> 8; + val = rt2860_srom_read(sc, RT2860_EEPROM_MAC23); + macaddr[2] = val & 0xff; + macaddr[3] = val >> 8; + val = rt2860_srom_read(sc, RT2860_EEPROM_MAC45); + macaddr[4] = val & 0xff; + macaddr[5] = val >> 8; + + /* read country code */ + val = rt2860_srom_read(sc, RT2860_EEPROM_COUNTRY); + DPRINTF(("EEPROM region code=0x%04x\n", val)); + + /* read vendor BBP settings */ + for (i = 0; i < 8; i++) { + val = rt2860_srom_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)); + } + if (sc->mac_ver >= 0x3071) { + /* read vendor RF settings */ + for (i = 0; i < 10; i++) { + val = rt2860_srom_read(sc, RT3071_EEPROM_RF_BASE + i); + sc->rf[i].val = val & 0xff; + sc->rf[i].reg = val >> 8; + DPRINTF(("RF%d=0x%02x\n", sc->rf[i].reg, + sc->rf[i].val)); + } + } + + /* read RF frequency offset from EEPROM */ + val = rt2860_srom_read(sc, RT2860_EEPROM_FREQ_LEDS); + sc->freq = ((val & 0xff) != 0xff) ? val & 0xff : 0; + DPRINTF(("EEPROM freq offset %d\n", sc->freq & 0xff)); + if ((val >> 8) != 0xff) { + /* read LEDs operating mode */ + sc->leds = val >> 8; + sc->led[0] = rt2860_srom_read(sc, RT2860_EEPROM_LED1); + sc->led[1] = rt2860_srom_read(sc, RT2860_EEPROM_LED2); + sc->led[2] = rt2860_srom_read(sc, RT2860_EEPROM_LED3); + } else { + /* broken EEPROM, use default settings */ + sc->leds = 0x01; + sc->led[0] = 0x5555; + sc->led[1] = 0x2221; + sc->led[2] = 0xa9f8; + } + DPRINTF(("EEPROM LED mode=0x%02x, LEDs=0x%04x/0x%04x/0x%04x\n", + sc->leds, sc->led[0], sc->led[1], sc->led[2])); + + /* read RF information */ + val = rt2860_srom_read(sc, RT2860_EEPROM_ANTENNA); + if (val == 0xffff) { + DPRINTF(("invalid EEPROM antenna info, using default\n")); + if (sc->mac_ver == 0x3593) { + /* default to RF3053 3T3R */ + sc->rf_rev = RT3070_RF_3053; + sc->ntxchains = 3; + sc->nrxchains = 3; + } else if (sc->mac_ver >= 0x3071) { + /* default to RF3020 1T1R */ + sc->rf_rev = RT3070_RF_3020; + sc->ntxchains = 1; + sc->nrxchains = 1; + } else { + /* default to RF2820 1T2R */ + 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_srom_read(sc, RT2860_EEPROM_CONFIG); + DPRINTF(("EEPROM CFG 0x%04x\n", val)); + /* check if driver should patch the DAC issue */ + if ((val >> 8) != 0xff) + sc->patch_dac = (val >> 15) & 1; + if ((val & 0xff) != 0xff) { + sc->ext_5ghz_lna = (val >> 3) & 1; + sc->ext_2ghz_lna = (val >> 2) & 1; + /* check if RF supports automatic Tx access gain control */ + sc->calib_2ghz = sc->calib_5ghz = 0; /* XXX (val >> 1) & 1 */; + /* check if we have a hardware radio switch */ + sc->rfswitch = val & 1; + } + if (sc->sc_flags & RT2860_ADVANCED_PS) { + /* read PCIe power save level */ + val = rt2860_srom_read(sc, RT2860_EEPROM_PCIE_PSLEVEL); + if ((val & 0xff) != 0xff) { + sc->pslevel = val & 0x3; + val = rt2860_srom_read(sc, RT2860_EEPROM_REV); + if ((val & 0xff80) != 0x9280) + sc->pslevel = MIN(sc->pslevel, 1); + DPRINTF(("EEPROM PCIe PS Level=%d\n", sc->pslevel)); + } + } + + /* read power settings for 2GHz channels */ + for (i = 0; i < 14; i += 2) { + val = rt2860_srom_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_srom_read(sc, + RT2860_EEPROM_PWR2GHZ_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 < 14; i++) { + if (sc->txpow1[i] < 0 || sc->txpow1[i] > 31) + sc->txpow1[i] = 5; + if (sc->txpow2[i] < 0 || sc->txpow2[i] > 31) + sc->txpow2[i] = 5; + DPRINTF(("chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[i].chan, sc->txpow1[i], sc->txpow2[i])); + } + /* read power settings for 5GHz channels */ + for (i = 0; i < 40; i += 2) { + val = rt2860_srom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE1 + i / 2); + sc->txpow1[i + 14] = (int8_t)(val & 0xff); + sc->txpow1[i + 15] = (int8_t)(val >> 8); + + val = rt2860_srom_read(sc, + RT2860_EEPROM_PWR5GHZ_BASE2 + i / 2); + sc->txpow2[i + 14] = (int8_t)(val & 0xff); + sc->txpow2[i + 15] = (int8_t)(val >> 8); + } + /* fix broken Tx power entries */ + for (i = 0; i < 40; i++) { + if (sc->txpow1[14 + i] < -7 || sc->txpow1[14 + i] > 15) + sc->txpow1[14 + i] = 5; + if (sc->txpow2[14 + i] < -7 || sc->txpow2[14 + i] > 15) + sc->txpow2[14 + i] = 5; + DPRINTF(("chan %d: power1=%d, power2=%d\n", + rt2860_rf2850[14 + i].chan, sc->txpow1[14 + i], + sc->txpow2[14 + i])); + } + + /* read Tx power compensation for each Tx rate */ + val = rt2860_srom_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_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2); + reg = val; + val = rt2860_srom_read(sc, RT2860_EEPROM_RPWR + ridx * 2 + 1); + reg |= (uint32_t)val << 16; + + 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_srom_read(sc, RT2860_EEPROM_TSSI1_2GHZ); + sc->tssi_2ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_2ghz[1] = val >> 8; /* [-3] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI2_2GHZ); + sc->tssi_2ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_2ghz[3] = val >> 8; /* [-1] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI3_2GHZ); + sc->tssi_2ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_2ghz[5] = val >> 8; /* [+1] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI4_2GHZ); + sc->tssi_2ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_2ghz[7] = val >> 8; /* [+3] */ + val = rt2860_srom_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_srom_read(sc, RT2860_EEPROM_TSSI1_5GHZ); + sc->tssi_5ghz[0] = val & 0xff; /* [-4] */ + sc->tssi_5ghz[1] = val >> 8; /* [-3] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI2_5GHZ); + sc->tssi_5ghz[2] = val & 0xff; /* [-2] */ + sc->tssi_5ghz[3] = val >> 8; /* [-1] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI3_5GHZ); + sc->tssi_5ghz[4] = val & 0xff; /* [+0] */ + sc->tssi_5ghz[5] = val >> 8; /* [+1] */ + val = rt2860_srom_read(sc, RT2860_EEPROM_TSSI4_5GHZ); + sc->tssi_5ghz[6] = val & 0xff; /* [+2] */ + sc->tssi_5ghz[7] = val >> 8; /* [+3] */ + val = rt2860_srom_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 RSSI offsets and LNA gains from EEPROM */ + val = rt2860_srom_read(sc, RT2860_EEPROM_RSSI1_2GHZ); + sc->rssi_2ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_2ghz[1] = val >> 8; /* Ant B */ + val = rt2860_srom_read(sc, RT2860_EEPROM_RSSI2_2GHZ); + if (sc->mac_ver >= 0x3071) { + /* + * On RT3090 chips (limited to 2 Rx chains), this ROM + * field contains the Tx mixer gain for the 2GHz band. + */ + if ((val & 0xff) != 0xff) + sc->txmixgain_2ghz = val & 0x7; + DPRINTF(("tx mixer gain=%u (2GHz)\n", sc->txmixgain_2ghz)); + } else + sc->rssi_2ghz[2] = val & 0xff; /* Ant C */ + sc->lna[2] = val >> 8; /* channel group 2 */ + + val = rt2860_srom_read(sc, RT2860_EEPROM_RSSI1_5GHZ); + sc->rssi_5ghz[0] = val & 0xff; /* Ant A */ + sc->rssi_5ghz[1] = val >> 8; /* Ant B */ + val = rt2860_srom_read(sc, RT2860_EEPROM_RSSI2_5GHZ); + sc->rssi_5ghz[2] = val & 0xff; /* Ant C */ + sc->lna[3] = val >> 8; /* channel group 3 */ + + val = rt2860_srom_read(sc, RT2860_EEPROM_LNA); + if (sc->mac_ver >= 0x3071) + sc->lna[0] = RT3090_DEF_LNA; + else /* channel group 0 */ + sc->lna[0] = val & 0xff; + 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; +} + +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) { + device_printf(sc->sc_dev, + "timeout waiting for BBP to wake up\n"); + 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 BBP84 for RT2860E */ + if (sc->mac_ver == 0x2860 && sc->mac_rev != 0x0101) + rt2860_mcu_bbp_write(sc, 84, 0x19); + + if (sc->mac_ver >= 0x3071) { + rt2860_mcu_bbp_write(sc, 79, 0x13); + rt2860_mcu_bbp_write(sc, 80, 0x05); + rt2860_mcu_bbp_write(sc, 81, 0x33); + } else if (sc->mac_ver == 0x2860 && sc->mac_rev == 0x0100) { + rt2860_mcu_bbp_write(sc, 69, 0x16); + rt2860_mcu_bbp_write(sc, 73, 0x12); + } + + return 0; +#undef N +} + +static int +rt2860_txrx_enable(struct rt2860_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + int ntries; + + /* enable Tx/Rx DMA engine */ + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_MAC_TX_EN); + RAL_BARRIER_READ_WRITE(sc); + 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) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + return ETIMEDOUT; + } + + DELAY(50); + + tmp |= 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; + if (ic->ic_opmode != IEEE80211_M_MONITOR) { + tmp |= RT2860_DROP_UC_NOME | RT2860_DROP_DUPL | + RT2860_DROP_CTS | RT2860_DROP_BA | RT2860_DROP_ACK | + RT2860_DROP_VER_ERR | RT2860_DROP_CTRL_RSV | + RT2860_DROP_CFACK | RT2860_DROP_CFEND; + 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); + + return 0; +} + +static void +rt2860_init(void *arg) +{ + struct rt2860_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + + RAL_LOCK(sc); + rt2860_init_locked(sc); + RAL_UNLOCK(sc); + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + ieee80211_start_all(ic); +} + +static void +rt2860_init_locked(struct rt2860_softc *sc) +{ +#define N(a) (sizeof (a) / sizeof ((a)[0])) + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + uint32_t tmp; + uint8_t bbp1, bbp3; + int i, qid, ridx, ntries, error; + + RAL_LOCK_ASSERT(sc); + + if (sc->rfswitch) { + /* hardware has a radio switch on GPIO pin 2 */ + if (!(RAL_READ(sc, RT2860_GPIO_CTRL) & (1 << 2))) { + device_printf(sc->sc_dev, + "radio is disabled by hardware switch\n"); +#ifdef notyet + rt2860_stop_locked(sc); + return; +#endif + } + } + RAL_WRITE(sc, RT2860_PWR_PIN_CFG, RT2860_IO_RA_PE); + + /* disable DMA */ + tmp = RAL_READ(sc, RT2860_WPDMA_GLO_CFG); + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* PBF hardware reset */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe1f); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + if ((error = rt2860_load_microcode(sc)) != 0) { + device_printf(sc->sc_dev, "could not load 8051 microcode\n"); + rt2860_stop_locked(sc); + return; + } + + rt2860_set_macaddr(sc, IF_LLADDR(ifp)); + + /* 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) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + rt2860_stop_locked(sc); + return; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* 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_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, 0xe00); + + RAL_WRITE(sc, RT2860_PWR_PIN_CFG, RT2860_IO_RA_PE | RT2860_IO_RF_PE); + + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, RT2860_BBP_HRST | RT2860_MAC_SRST); + RAL_BARRIER_WRITE(sc); + 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); + if (sc->mac_ver >= 0x3071) { + /* set delay of PA_PE assertion to 1us (unit of 0.25us) */ + RAL_WRITE(sc, RT2860_TX_SW_CFG0, + 4 << RT2860_DLY_PAPE_EN_SHIFT); + } + + if (!(RAL_READ(sc, RT2860_PCI_CFG) & RT2860_PCI_CFG_PCI)) { + sc->sc_flags |= RT2860_PCIE; + /* PCIe has different clock cycle count than PCI */ + tmp = RAL_READ(sc, RT2860_US_CYC_CNT); + tmp = (tmp & ~0xff) | 0x7d; + RAL_WRITE(sc, RT2860_US_CYC_CNT, tmp); + } + + /* 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) { + device_printf(sc->sc_dev, "timeout waiting for MAC\n"); + rt2860_stop_locked(sc); + return; + } + + /* clear Host to MCU mailbox */ + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, 0); + RAL_WRITE(sc, RT2860_H2M_MAILBOX, 0); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0, 0); + DELAY(1000); + + if ((error = rt2860_bbp_init(sc)) != 0) { + rt2860_stop_locked(sc); + return; + } + + /* 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, RT2860_SKEY_MODE_0_7, 0, 4); + + /* 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) { + device_printf(sc->sc_dev, "timeout waiting for DMA engine\n"); + rt2860_stop_locked(sc); + return; + } + tmp &= 0xff0; + RAL_WRITE(sc, RT2860_WPDMA_GLO_CFG, tmp); + + /* 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); + } + + /* select Main antenna for 1T1R devices */ + if (sc->rf_rev == RT3070_RF_2020 || + sc->rf_rev == RT3070_RF_3020 || + sc->rf_rev == RT3070_RF_3320) + rt3090_set_rx_antenna(sc, 0); + + /* send LEDs operating mode to microcontroller */ + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED1, sc->led[0], 0); + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED2, sc->led[1], 0); + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_LED3, sc->led[2], 0); + + if (sc->mac_ver >= 0x3071) + rt3090_rf_init(sc); + + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_SLEEP, 0x02ff, 1); + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_WAKEUP, 0, 1); + + if (sc->mac_ver >= 0x3071) + rt3090_rf_wakeup(sc); + + /* 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); + + /* disable non-existing Tx chains */ + bbp1 = rt2860_mcu_bbp_read(sc, 1); + if (sc->ntxchains == 1) + bbp1 = (bbp1 & ~(1 << 3 | 1 << 4)); + else if (sc->mac_ver == 0x3593 && sc->ntxchains == 2) + bbp1 = (bbp1 & ~(1 << 4)) | 1 << 3; + else if (sc->mac_ver == 0x3593 && sc->ntxchains == 3) + bbp1 = (bbp1 & ~(1 << 3)) | 1 << 4; + rt2860_mcu_bbp_write(sc, 1, bbp1); + + if (sc->mac_ver >= 0x3071) + rt3090_rf_setup(sc); + + /* select default channel */ + rt2860_switch_chan(sc, ic->ic_curchan); + + /* reset RF from MCU */ + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_RFRESET, 0, 0); + + /* set RTS threshold */ + tmp = RAL_READ(sc, RT2860_TX_RTS_CFG); + tmp &= ~0xffff00; + tmp |= IEEE80211_RTS_DEFAULT << 8; + RAL_WRITE(sc, RT2860_TX_RTS_CFG, tmp); + + /* setup initial protection mode */ + rt2860_updateprot(ifp); + + /* turn radio LED on */ + rt2860_set_leds(sc, RT2860_LED_RADIO); + + /* enable Tx/Rx DMA engine */ + if ((error = rt2860_txrx_enable(sc)) != 0) { + rt2860_stop_locked(sc); + return; + } + + /* clear pending interrupts */ + RAL_WRITE(sc, RT2860_INT_STATUS, 0xffffffff); + /* enable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0x3fffc); + + if (sc->sc_flags & RT2860_ADVANCED_PS) + rt2860_mcu_cmd(sc, RT2860_MCU_CMD_PSLEVEL, sc->pslevel, 0); + + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + + callout_reset(&sc->watchdog_ch, hz, rt2860_watchdog, sc); +#undef N +} + +static void +rt2860_stop(void *arg) +{ + struct rt2860_softc *sc = arg; + + RAL_LOCK(sc); + rt2860_stop_locked(sc); + RAL_UNLOCK(sc); +} + +static void +rt2860_stop_locked(struct rt2860_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + uint32_t tmp; + int qid; + + if (ifp->if_drv_flags & IFF_DRV_RUNNING) + rt2860_set_leds(sc, 0); /* turn all LEDs off */ + + callout_stop(&sc->watchdog_ch); + sc->sc_tx_timer = 0; + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + + /* disable interrupts */ + RAL_WRITE(sc, RT2860_INT_MASK, 0); + + /* disable GP timer */ + rt2860_set_gp_timer(sc, 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_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_MAC_SYS_CTRL, 0); + + /* reset Tx and Rx rings (and reclaim TXWIs) */ + sc->qfullmsk = 0; + for (qid = 0; qid < 6; qid++) + rt2860_reset_tx_ring(sc, &sc->txq[qid]); + rt2860_reset_rx_ring(sc, &sc->rxq); +} + +int +rt2860_load_microcode(struct rt2860_softc *sc) +{ + const struct firmware *fp; + int ntries, error; + + RAL_LOCK_ASSERT(sc); + + RAL_UNLOCK(sc); + fp = firmware_get("rt2860fw"); + RAL_LOCK(sc); + if (fp == NULL) { + device_printf(sc->sc_dev, + "unable to receive rt2860fw firmware image\n"); + return EINVAL; + } + + /* 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, fp->data, fp->datasize); + /* kick microcontroller unit */ + RAL_WRITE(sc, RT2860_SYS_CTRL, 0); + RAL_BARRIER_WRITE(sc); + RAL_WRITE(sc, RT2860_SYS_CTRL, RT2860_MCU_RESET); + + RAL_WRITE(sc, RT2860_H2M_BBPAGENT, 0); + RAL_WRITE(sc, RT2860_H2M_MAILBOX, 0); + + /* wait until microcontroller is ready */ + RAL_BARRIER_READ_WRITE(sc); + for (ntries = 0; ntries < 1000; ntries++) { + if (RAL_READ(sc, RT2860_SYS_CTRL) & RT2860_MCU_READY) + break; + DELAY(1000); + } + if (ntries == 1000) { + device_printf(sc->sc_dev, + "timeout waiting for MCU to initialize\n"); + error = ETIMEDOUT; + } else + error = 0; + + firmware_put(fp, FIRMWARE_UNLOAD); + return error; +} + +/* + * This function is called periodically to adjust Tx power based on + * temperature variation. + */ +#ifdef NOT_YET +static 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)); + } +} +#endif + +static void +rt3090_set_rx_antenna(struct rt2860_softc *sc, int aux) +{ + uint32_t tmp; + + if (aux) { + tmp = RAL_READ(sc, RT2860_PCI_EECTRL); + RAL_WRITE(sc, RT2860_PCI_EECTRL, tmp & ~RT2860_C); + tmp = RAL_READ(sc, RT2860_GPIO_CTRL); + RAL_WRITE(sc, RT2860_GPIO_CTRL, (tmp & ~0x0808) | 0x08); + } else { + tmp = RAL_READ(sc, RT2860_PCI_EECTRL); + RAL_WRITE(sc, RT2860_PCI_EECTRL, tmp | RT2860_C); + tmp = RAL_READ(sc, RT2860_GPIO_CTRL); + RAL_WRITE(sc, RT2860_GPIO_CTRL, tmp & ~0x0808); + } +} + +static void +rt2860_switch_chan(struct rt2860_softc *sc, struct ieee80211_channel *c) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + u_int chan, group; + + chan = ieee80211_chan2ieee(ic, c); + if (chan == 0 || chan == IEEE80211_CHAN_ANY) + return; + + if (sc->mac_ver >= 0x3071) + rt3090_set_chan(sc, chan); + else + rt2860_set_chan(sc, chan); + + /* 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); +} + +static int +rt2860_setup_beacon(struct rt2860_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_beacon_offsets bo; + struct rt2860_txwi txwi; + struct mbuf *m; + int ridx; + + if ((m = ieee80211_beacon_alloc(vap->iv_bss, &bo)) == 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 */ + ridx = IEEE80211_IS_CHAN_5GHZ(ic->ic_bsschan) ? + RT2860_RIDX_OFDM6 : RT2860_RIDX_CCK1; + txwi.phy = htole16(rt2860_rates[ridx].mcs); + if (rt2860_rates[ridx].phy == IEEE80211_T_OFDM) + txwi.phy |= htole16(RT2860_PHY_OFDM); + txwi.txop = RT2860_TX_TXOP_HT; + txwi.flags = RT2860_TX_TS; + txwi.xflags = RT2860_TX_NSEQ; + + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0), + (uint8_t *)&txwi, sizeof txwi); + RAL_WRITE_REGION_1(sc, RT2860_BCN_BASE(0) + sizeof txwi, + mtod(m, uint8_t *), m->m_pkthdr.len); + + m_freem(m); + + return 0; +} + +static void +rt2860_enable_tsf_sync(struct rt2860_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t tmp; + + tmp = RAL_READ(sc, RT2860_BCN_TIME_CFG); + + tmp &= ~0x1fffff; + tmp |= vap->iv_bss->ni_intval * 16; + tmp |= RT2860_TSF_TIMER_EN | RT2860_TBTT_TIMER_EN; + if (vap->iv_opmode == IEEE80211_M_STA) { + /* + * Local TSF is always updated with remote TSF on beacon + * reception. + */ + tmp |= 1 << RT2860_TSF_SYNC_MODE_SHIFT; + } + else if (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_MBSS) { + 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 (vap->iv_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); +} diff --git a/sys/dev/ral/rt2860reg.h b/sys/dev/ral/rt2860reg.h new file mode 100644 index 000000000000..06c275d47f0b --- /dev/null +++ b/sys/dev/ral/rt2860reg.h @@ -0,0 +1,1255 @@ +/*- + * Copyright (c) 2007 Damien Bergamini + * Copyright (c) 2012 Bernhard Schmidt + * + * 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. + * + * $OpenBSD: rt2860reg.h,v 1.30 2010/05/10 18:17:10 damien Exp $ + * $FreeBSD$ + */ + +#define RT2860_NOISE_FLOOR -95 + +/* 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 + +#define RT3090_AUX_CTRL 0x010c + +#define RT3070_OPT_14 0x0114 + +/* 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_USB_DMA_CFG 0x02a0 /* RT2870 only */ +#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 + +/* RT3070 registers */ +#define RT3070_RF_CSR_CFG 0x0500 +#define RT3070_EFUSE_CTRL 0x0580 +#define RT3070_EFUSE_DATA0 0x0590 +#define RT3070_EFUSE_DATA1 0x0594 +#define RT3070_EFUSE_DATA2 0x0598 +#define RT3070_EFUSE_DATA3 0x059c +#define RT3090_OSC_CTRL 0x05a4 +#define RT3070_LDO_CFG0 0x05d4 +#define RT3070_GPIO_SWITCH 0x05dc + +/* 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 + +/* undocumented registers */ +#define RT2860_DEBUG 0x10f4 + +/* 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 + +/* RX WCID search table */ +#define RT2860_WCID_ENTRY(wcid) (0x1800 + (wcid) * 8) + +#define RT2860_FW_BASE 0x2000 +#define RT2870_FW_BASE 0x3000 + +/* Pair-wise key table */ +#define RT2860_PKEY(wcid) (0x4000 + (wcid) * 32) + +/* IV/EIV table */ +#define RT2860_IVEIV(wcid) (0x6000 + (wcid) * 8) + +/* WCID attribute table */ +#define RT2860_WCID_ATTR(wcid) (0x6800 + (wcid) * 4) + +/* Shared Key Table */ +#define RT2860_SKEY(vap, kidx) (0x6c00 + (vap) * 128 + (kidx) * 32) + +/* Shared Key Mode */ +#define RT2860_SKEY_MODE_0_7 0x7000 +#define RT2860_SKEY_MODE_8_15 0x7004 +#define RT2860_SKEY_MODE_16_23 0x7008 +#define RT2860_SKEY_MODE_24_31 0x700c + +/* Shared Memory between MCU and host */ +#define RT2860_H2M_MAILBOX 0x7010 +#define RT2860_H2M_MAILBOX_CID 0x7014 +#define RT2860_H2M_MAILBOX_STATUS 0x701c +#define RT2860_H2M_BBPAGENT 0x7028 +#define RT2860_BCN_BASE(vap) (0x7800 + (vap) * 512) + + +/* possible flags for RT2860_PCI_CFG */ +#define RT2860_PCI_CFG_USB (1 << 17) +#define RT2860_PCI_CFG_PCI (1 << 16) + +/* 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 USB_DMA_CFG */ +#define RT2860_USB_TX_BUSY (1 << 31) +#define RT2860_USB_RX_BUSY (1 << 30) +#define RT2860_USB_EPOUT_VLD_SHIFT 24 +#define RT2860_USB_TX_EN (1 << 23) +#define RT2860_USB_RX_EN (1 << 22) +#define RT2860_USB_RX_AGG_EN (1 << 21) +#define RT2860_USB_TXOP_HALT (1 << 20) +#define RT2860_USB_TX_CLEAR (1 << 19) +#define RT2860_USB_PHY_WD_EN (1 << 16) +#define RT2860_USB_PHY_MAN_RST (1 << 15) +#define RT2860_USB_RX_AGG_LMT(x) ((x) << 8) /* in unit of 1KB */ +#define RT2860_USB_RX_AGG_TO(x) ((x) & 0xff) /* in unit of 33ns */ + +/* 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_SLEEP 0x30 +#define RT2860_MCU_CMD_WAKEUP 0x31 +#define RT2860_MCU_CMD_LEDS 0x50 +#define RT2860_MCU_CMD_LED_RSSI 0x51 +#define RT2860_MCU_CMD_LED1 0x52 +#define RT2860_MCU_CMD_LED2 0x53 +#define RT2860_MCU_CMD_LED3 0x54 +#define RT2860_MCU_CMD_RFRESET 0x72 +#define RT2860_MCU_CMD_ANTSEL 0x73 +#define RT2860_MCU_CMD_BBP 0x80 +#define RT2860_MCU_CMD_PSLEVEL 0x83 + +/* 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 TXRXQ_PCNT */ +#define RT2860_RX0Q_PCNT_MASK 0xff000000 +#define RT2860_TX2Q_PCNT_MASK 0x00ff0000 +#define RT2860_TX1Q_PCNT_MASK 0x0000ff00 +#define RT2860_TX0Q_PCNT_MASK 0x000000ff + +/* 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 RF_CSR_CFG */ +#define RT3070_RF_KICK (1 << 17) +#define RT3070_RF_WRITE (1 << 16) + +/* possible flags for register EFUSE_CTRL */ +#define RT3070_SEL_EFUSE (1 << 31) +#define RT3070_EFSROM_KICK (1 << 30) +#define RT3070_EFSROM_AIN_MASK 0x03ff0000 +#define RT3070_EFSROM_AIN_SHIFT 16 +#define RT3070_EFSROM_MODE_MASK 0x000000c0 +#define RT3070_EFUSE_AOUT_MASK 0x0000003f + +/* 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 RT3593_LNA_PE_G2_POL (1 << 31) +#define RT3593_LNA_PE_A2_POL (1 << 30) +#define RT3593_LNA_PE_G2_EN (1 << 29) +#define RT3593_LNA_PE_A2_EN (1 << 28) +#define RT3593_LNA_PE2_EN (RT3593_LNA_PE_A2_EN | RT3593_LNA_PE_G2_EN) +#define RT3593_PA_PE_G2_POL (1 << 27) +#define RT3593_PA_PE_A2_POL (1 << 26) +#define RT3593_PA_PE_G2_EN (1 << 25) +#define RT3593_PA_PE_A2_EN (1 << 24) +#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_PE1_EN (RT2860_LNA_PE_A1_EN | RT2860_LNA_PE_G1_EN) +#define RT2860_LNA_PE_G0_EN (1 << 9) +#define RT2860_LNA_PE_A0_EN (1 << 8) +#define RT2860_LNA_PE0_EN (RT2860_LNA_PE_A0_EN | RT2860_LNA_PE_G0_EN) +#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_TXOP_ALLOW_ALL (0x3f << 20) +#define RT2860_PROT_NAV_SHORT (1 << 18) +#define RT2860_PROT_NAV_LONG (2 << 18) +#define RT2860_PROT_CTRL_RTS_CTS (1 << 16) +#define RT2860_PROT_CTRL_CTS (2 << 16) + +/* 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_MCS_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 WCID_ATTR */ +#define RT2860_MODE_NOSEC 0 +#define RT2860_MODE_WEP40 1 +#define RT2860_MODE_WEP104 2 +#define RT2860_MODE_TKIP 3 +#define RT2860_MODE_AES_CCMP 4 +#define RT2860_MODE_CKIP40 5 +#define RT2860_MODE_CKIP104 6 +#define RT2860_MODE_CKIP128 7 +#define RT2860_RX_PKEY_EN (1 << 0) + +/* possible flags for register H2M_MAILBOX */ +#define RT2860_H2M_BUSY (1 << 24) +#define RT2860_TOKEN_NO_INTR 0xff + + +/* possible flags for MCU command RT2860_MCU_CMD_LEDS */ +#define RT2860_LED_RADIO (1 << 13) +#define RT2860_LED_LINK_2GHZ (1 << 14) +#define RT2860_LED_LINK_5GHZ (1 << 15) + + +/* possible flags for RT3020 RF register 1 */ +#define RT3070_RF_BLOCK (1 << 0) +#define RT3070_RX0_PD (1 << 2) +#define RT3070_TX0_PD (1 << 3) +#define RT3070_RX1_PD (1 << 4) +#define RT3070_TX1_PD (1 << 5) +#define RT3070_RX2_PD (1 << 6) +#define RT3070_TX2_PD (1 << 7) + +/* possible flags for RT3020 RF register 7 */ +#define RT3070_TUNE (1 << 0) + +/* possible flags for RT3020 RF register 15 */ +#define RT3070_TX_LO2 (1 << 3) + +/* possible flags for RT3020 RF register 17 */ +#define RT3070_TX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 20 */ +#define RT3070_RX_LO1 (1 << 3) + +/* possible flags for RT3020 RF register 21 */ +#define RT3070_RX_LO2 (1 << 3) +#define RT3070_RX_CTB (1 << 7) + +/* possible flags for RT3020 RF register 22 */ +#define RT3070_BB_LOOPBACK (1 << 0) + +/* possible flags for RT3053 RF register 1 */ +#define RT3593_VCO (1 << 0) + +/* possible flags for RT3053 RF register 2 */ +#define RT3593_RESCAL (1 << 7) + +/* possible flags for RT3053 RF register 3 */ +#define RT3593_VCOCAL (1 << 7) + +/* possible flags for RT3053 RF register 6 */ +#define RT3593_VCO_IC (1 << 6) + +/* possible flags for RT3053 RF register 20 */ +#define RT3593_LDO_PLL_VC_MASK 0x0e +#define RT3593_LDO_RF_VC_MASK 0xe0 + +/* possible flags for RT3053 RF register 22 */ +#define RT3593_CP_IC_MASK 0xe0 +#define RT3593_CP_IC_SHIFT 5 + +/* possible flags for RT3053 RF register 46 */ +#define RT3593_RX_CTB (1 << 5) + +#define RT3090_DEF_LNA 10 + +/* RT2860 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; + +/* RT2870 TX descriptor */ +struct rt2870_txd { + uint16_t len; + uint8_t pad; + uint8_t flags; +} __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_PHY_MODE 0xc000 +#define RT2860_PHY_CCK (0 << 14) +#define RT2860_PHY_OFDM (1 << 14) +#define RT2860_PHY_HT (2 << 14) +#define RT2860_PHY_HT_GF (3 << 14) +#define RT2860_PHY_SGI (1 << 8) +#define RT2860_PHY_BW40 (1 << 7) +#define RT2860_PHY_MCS 0x7f +#define RT2860_PHY_SHPRE (1 << 3) + + 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; +} __packed; + +/* RT2860 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; + +/* RT2870 RX descriptor */ +struct rt2870_rxd { + /* single 32-bit field */ + uint32_t flags; +} __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; + + +/* first DMA segment contains TXWI + 802.11 header + 32-bit padding */ +#define RT2860_TXWI_DMASZ \ + (sizeof (struct rt2860_txwi) + \ + sizeof (struct ieee80211_frame) + 6 + \ + sizeof (uint16_t)) + +#define RT2860_RF1 0 +#define RT2860_RF2 2 +#define RT2860_RF3 1 +#define RT2860_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 RT3070_RF_3020 5 /* 1T1R */ +#define RT3070_RF_2020 6 /* b/g */ +#define RT3070_RF_3021 7 /* 1T2R */ +#define RT3070_RF_3022 8 /* 2T2R */ +#define RT3070_RF_3052 9 /* dual-band 2T2R */ +#define RT3070_RF_3320 11 /* 1T1R */ +#define RT3070_RF_3053 13 /* dual-band 3T3R */ + +/* USB commands for RT2870 only */ +#define RT2870_RESET 1 +#define RT2870_WRITE_2 2 +#define RT2870_WRITE_REGION_1 6 +#define RT2870_READ_REGION_1 7 +#define RT2870_EEPROM_READ 9 + +#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_PCIE_PSLEVEL 0x11 +#define RT2860_EEPROM_REV 0x12 +#define RT2860_EEPROM_ANTENNA 0x1a +#define RT2860_EEPROM_CONFIG 0x1b +#define RT2860_EEPROM_COUNTRY 0x1c +#define RT2860_EEPROM_FREQ_LEDS 0x1d +#define RT2860_EEPROM_LED1 0x1e +#define RT2860_EEPROM_LED2 0x1f +#define RT2860_EEPROM_LED3 0x20 +#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 +#define RT3071_EEPROM_RF_BASE 0x82 + +#define RT2860_RIDX_CCK1 0 +#define RT2860_RIDX_CCK11 3 +#define RT2860_RIDX_OFDM6 4 +#define RT2860_RIDX_MAX 11 +static const struct rt2860_rate { + uint8_t rate; + uint8_t mcs; + enum ieee80211_phytype phy; + uint8_t ctl_ridx; + uint16_t sp_ack_dur; + uint16_t lp_ack_dur; +} rt2860_rates[] = { + { 2, 0, IEEE80211_T_DS, 0, 314, 314 }, + { 4, 1, IEEE80211_T_DS, 1, 258, 162 }, + { 11, 2, IEEE80211_T_DS, 2, 223, 127 }, + { 22, 3, IEEE80211_T_DS, 3, 213, 117 }, + { 12, 0, IEEE80211_T_OFDM, 4, 60, 60 }, + { 18, 1, IEEE80211_T_OFDM, 4, 52, 52 }, + { 24, 2, IEEE80211_T_OFDM, 6, 48, 48 }, + { 36, 3, IEEE80211_T_OFDM, 6, 44, 44 }, + { 48, 4, IEEE80211_T_OFDM, 8, 44, 44 }, + { 72, 5, IEEE80211_T_OFDM, 8, 40, 40 }, + { 96, 6, IEEE80211_T_OFDM, 8, 40, 40 }, + { 108, 7, IEEE80211_T_OFDM, 8, 40, 40 } +}; + +/* + * 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_BARRIER_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ + BUS_SPACE_BARRIER_WRITE) + +#define RAL_BARRIER_READ_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ + 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)); \ + RAL_BARRIER_READ_WRITE((sc)); \ + 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, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { 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, 0x47d01f0f }, \ + { 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, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + +/* XXX only a few registers differ from above, try to merge? */ +#define RT2870_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, 0x00000000 }, \ + { RT2860_TX_SW_CFG1, 0x00080606 }, \ + { RT2860_TX_LINK_CFG, 0x00001020 }, \ + { RT2860_TX_TIMEOUT_CFG, 0x000a2090 }, \ + { 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, 0x47d01f0f }, \ + { RT2860_AUTO_RSP_CFG, 0x00000013 }, \ + { RT2860_CCK_PROT_CFG, 0x05740003 }, \ + { RT2860_OFDM_PROT_CFG, 0x05740003 }, \ + { RT2860_PBF_CFG, 0x00f40006 }, \ + { RT2860_WPDMA_GLO_CFG, 0x00000030 }, \ + { RT2860_GF20_PROT_CFG, 0x01744004 }, \ + { RT2860_GF40_PROT_CFG, 0x03f44084 }, \ + { RT2860_MM20_PROT_CFG, 0x01744004 }, \ + { RT2860_MM40_PROT_CFG, 0x03f44084 }, \ + { RT2860_TXOP_CTRL_CFG, 0x0000583f }, \ + { RT2860_TXOP_HLDR_ET, 0x00000002 }, \ + { RT2860_TX_RTS_CFG, 0x00092b20 }, \ + { RT2860_EXP_ACK_TIME, 0x002400ca }, \ + { RT2860_XIFS_TIME_CFG, 0x33a41010 }, \ + { RT2860_PWR_PIN_CFG, 0x00000003 } + +/* + * Default values for BBP registers; values taken from the reference driver. + */ +#define RT2860_DEF_BBP \ + { 65, 0x2c }, \ + { 66, 0x38 }, \ + { 69, 0x12 }, \ + { 70, 0x0a }, \ + { 73, 0x10 }, \ + { 81, 0x37 }, \ + { 82, 0x62 }, \ + { 83, 0x6a }, \ + { 84, 0x99 }, \ + { 86, 0x00 }, \ + { 91, 0x04 }, \ + { 92, 0x00 }, \ + { 103, 0x00 }, \ + { 105, 0x05 }, \ + { 106, 0x35 } + +/* + * 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, 0x1701ac, 0x15e014, 0x001404 }, \ + { 104, 0x100bb2, 0x1701ac, 0x15e014, 0x001408 }, \ + { 108, 0x100bb3, 0x17028c, 0x15e014, 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, 0x001406 }, \ + { 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 }, \ + { 167, 0x100bb1, 0x1300f4, 0x05e014, 0x001407 }, \ + { 169, 0x100bb1, 0x1300f4, 0x05e014, 0x001409 }, \ + { 171, 0x100bb1, 0x1300f5, 0x05e014, 0x001401 }, \ + { 173, 0x100bb1, 0x1300f5, 0x05e014, 0x001403 } + +#define RT3070_RF3052 \ + { 0xf1, 2, 2 }, \ + { 0xf1, 2, 7 }, \ + { 0xf2, 2, 2 }, \ + { 0xf2, 2, 7 }, \ + { 0xf3, 2, 2 }, \ + { 0xf3, 2, 7 }, \ + { 0xf4, 2, 2 }, \ + { 0xf4, 2, 7 }, \ + { 0xf5, 2, 2 }, \ + { 0xf5, 2, 7 }, \ + { 0xf6, 2, 2 }, \ + { 0xf6, 2, 7 }, \ + { 0xf7, 2, 2 }, \ + { 0xf8, 2, 4 }, \ + { 0x56, 0, 4 }, \ + { 0x56, 0, 6 }, \ + { 0x56, 0, 8 }, \ + { 0x57, 0, 0 }, \ + { 0x57, 0, 2 }, \ + { 0x57, 0, 4 }, \ + { 0x57, 0, 8 }, \ + { 0x57, 0, 10 }, \ + { 0x58, 0, 0 }, \ + { 0x58, 0, 4 }, \ + { 0x58, 0, 6 }, \ + { 0x58, 0, 8 }, \ + { 0x5b, 0, 8 }, \ + { 0x5b, 0, 10 }, \ + { 0x5c, 0, 0 }, \ + { 0x5c, 0, 4 }, \ + { 0x5c, 0, 6 }, \ + { 0x5c, 0, 8 }, \ + { 0x5d, 0, 0 }, \ + { 0x5d, 0, 2 }, \ + { 0x5d, 0, 4 }, \ + { 0x5d, 0, 8 }, \ + { 0x5d, 0, 10 }, \ + { 0x5e, 0, 0 }, \ + { 0x5e, 0, 4 }, \ + { 0x5e, 0, 6 }, \ + { 0x5e, 0, 8 }, \ + { 0x5f, 0, 0 }, \ + { 0x5f, 0, 9 }, \ + { 0x5f, 0, 11 }, \ + { 0x60, 0, 1 }, \ + { 0x60, 0, 5 }, \ + { 0x60, 0, 7 }, \ + { 0x60, 0, 9 }, \ + { 0x61, 0, 1 }, \ + { 0x61, 0, 3 }, \ + { 0x61, 0, 5 }, \ + { 0x61, 0, 7 }, \ + { 0x61, 0, 9 } + +#define RT3070_DEF_RF \ + { 4, 0x40 }, \ + { 5, 0x03 }, \ + { 6, 0x02 }, \ + { 7, 0x70 }, \ + { 9, 0x0f }, \ + { 10, 0x41 }, \ + { 11, 0x21 }, \ + { 12, 0x7b }, \ + { 14, 0x90 }, \ + { 15, 0x58 }, \ + { 16, 0xb3 }, \ + { 17, 0x92 }, \ + { 18, 0x2c }, \ + { 19, 0x02 }, \ + { 20, 0xba }, \ + { 21, 0xdb }, \ + { 24, 0x16 }, \ + { 25, 0x01 }, \ + { 29, 0x1f } + +#define RT3572_DEF_RF \ + { 0, 0x70 }, \ + { 1, 0x81 }, \ + { 2, 0xf1 }, \ + { 3, 0x02 }, \ + { 4, 0x4c }, \ + { 5, 0x05 }, \ + { 6, 0x4a }, \ + { 7, 0xd8 }, \ + { 9, 0xc3 }, \ + { 10, 0xf1 }, \ + { 11, 0xb9 }, \ + { 12, 0x70 }, \ + { 13, 0x65 }, \ + { 14, 0xa0 }, \ + { 15, 0x53 }, \ + { 16, 0x4c }, \ + { 17, 0x23 }, \ + { 18, 0xac }, \ + { 19, 0x93 }, \ + { 20, 0xb3 }, \ + { 21, 0xd0 }, \ + { 22, 0x00 }, \ + { 23, 0x3c }, \ + { 24, 0x16 }, \ + { 25, 0x15 }, \ + { 26, 0x85 }, \ + { 27, 0x00 }, \ + { 28, 0x00 }, \ + { 29, 0x9b }, \ + { 30, 0x09 }, \ + { 31, 0x10 } diff --git a/sys/dev/ral/rt2860var.h b/sys/dev/ral/rt2860var.h new file mode 100644 index 000000000000..28a3d59b8263 --- /dev/null +++ b/sys/dev/ral/rt2860var.h @@ -0,0 +1,210 @@ +/*- + * Copyright (c) 2007 Damien Bergamini + * Copyright (c) 2012 Bernhard Schmidt + * + * 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. + * + * $OpenBSD: rt2860var.h,v 1.20 2010/09/07 16:21:42 deraadt Exp $ + * $FreeBSD$ + */ + +#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_rx_radiotap_header { + struct ieee80211_radiotap_header wr_ihdr; + uint64_t wr_tsf; + uint8_t wr_flags; + uint8_t wr_rate; + uint16_t wr_chan_freq; + uint16_t wr_chan_flags; + uint8_t wr_antenna; + int8_t wr_antsignal; + int8_t wr_antnoise; +} __packed; + +#define RT2860_RX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_TSFT) | \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ + (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) + +struct rt2860_tx_radiotap_header { + struct ieee80211_radiotap_header wt_ihdr; + uint8_t wt_flags; + uint8_t wt_rate; + uint16_t wt_chan_freq; + uint16_t wt_chan_flags; +} __packed; + +#define RT2860_TX_RADIOTAP_PRESENT \ + ((1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL)) + +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_dma_tag_t desc_dmat; + bus_dmamap_t desc_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_dma_tag_t desc_dmat; + bus_dmamap_t desc_map; + bus_dma_tag_t data_dmat; + bus_dma_segment_t seg; + unsigned int cur; /* must be unsigned */ + struct rt2860_rx_data data[RT2860_RX_RING_COUNT]; +}; + +struct rt2860_node { + struct ieee80211_node ni; + uint8_t wcid; + uint8_t ridx[IEEE80211_RATE_MAXSIZE]; + uint8_t ctl_ridx[IEEE80211_RATE_MAXSIZE]; +}; + +struct rt2860_vap { + struct ieee80211vap ral_vap; + + int (*ral_newstate)(struct ieee80211vap *, + enum ieee80211_state, int); +}; +#define RT2860_VAP(vap) ((struct rt2860_vap *)(vap)) + +struct rt2860_softc { + struct ifnet *sc_ifp; + device_t sc_dev; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + + struct mtx sc_mtx; + + struct callout watchdog_ch; + + int sc_invalid; + int sc_debug; +/* + * The same in both up to here + * ------------------------------------------------ + */ + + uint16_t (*sc_srom_read)(struct rt2860_softc *, + uint16_t); + void (*sc_node_free)(struct ieee80211_node *); + + int sc_flags; +#define RT2860_ENABLED (1 << 0) +#define RT2860_ADVANCED_PS (1 << 1) +#define RT2860_PCIE (1 << 2) + + struct ieee80211_node *wcid2ni[RT2860_WCID_MAX]; + + 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_dma_tag_t txwi_dmat; + bus_dmamap_t txwi_map; + bus_dma_segment_t txwi_seg; + caddr_t txwi_vaddr; + + int sc_tx_timer; + int mgtqid; + uint8_t qfullmsk; + + uint16_t mac_ver; + uint16_t mac_rev; + uint8_t rf_rev; + uint8_t freq; + uint8_t ntxchains; + uint8_t nrxchains; + uint8_t pslevel; + int8_t txpow1[54]; + int8_t txpow2[54]; + int8_t rssi_2ghz[3]; + int8_t rssi_5ghz[3]; + uint8_t lna[4]; + uint8_t rf24_20mhz; + uint8_t rf24_40mhz; + uint8_t patch_dac; + uint8_t rfswitch; + uint8_t ext_2ghz_lna; + uint8_t ext_5ghz_lna; + uint8_t calib_2ghz; + uint8_t calib_5ghz; + uint8_t txmixgain_2ghz; + uint8_t txmixgain_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], rf[10]; + uint8_t leds; + uint16_t led[3]; + uint32_t txpow20mhz[5]; + uint32_t txpow40mhz_2ghz[5]; + uint32_t txpow40mhz_5ghz[5]; + + struct rt2860_rx_radiotap_header sc_rxtap; + int sc_rxtap_len; + struct rt2860_tx_radiotap_header sc_txtap; + int sc_txtap_len; +}; + +int rt2860_attach(device_t, int); +int rt2860_detach(void *); +void rt2860_shutdown(void *); +void rt2860_suspend(void *); +void rt2860_resume(void *); +void rt2860_intr(void *); + +#define RAL_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define RAL_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +#define RAL_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) diff --git a/sys/modules/ral/Makefile b/sys/modules/ral/Makefile index 6a3b269f5bb3..cc5d3421dbde 100644 --- a/sys/modules/ral/Makefile +++ b/sys/modules/ral/Makefile @@ -3,7 +3,7 @@ .PATH: ${.CURDIR}/../../dev/ral KMOD= if_ral -SRCS= rt2560.c rt2661.c if_ral_pci.c +SRCS= rt2560.c rt2661.c rt2860.c if_ral_pci.c SRCS+= device_if.h bus_if.h pci_if.h .include