1*479c151dSjsg /* $OpenBSD: rtwn.c,v 1.59 2024/09/20 02:00:46 jsg Exp $ */ 28b9da0f5Sstsp 38b9da0f5Sstsp /*- 48b9da0f5Sstsp * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 58b9da0f5Sstsp * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> 690540544Sjmatthew * Copyright (c) 2016 Nathanial Sloss <nathanialsloss@yahoo.com.au> 78b9da0f5Sstsp * 88b9da0f5Sstsp * Permission to use, copy, modify, and distribute this software for any 98b9da0f5Sstsp * purpose with or without fee is hereby granted, provided that the above 108b9da0f5Sstsp * copyright notice and this permission notice appear in all copies. 118b9da0f5Sstsp * 128b9da0f5Sstsp * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 138b9da0f5Sstsp * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 148b9da0f5Sstsp * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 158b9da0f5Sstsp * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 168b9da0f5Sstsp * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 178b9da0f5Sstsp * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 188b9da0f5Sstsp * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 198b9da0f5Sstsp */ 208b9da0f5Sstsp 218b9da0f5Sstsp /* 2234a2cadeSkevlo * Driver for Realtek 802.11b/g/n chipsets. 238b9da0f5Sstsp */ 248b9da0f5Sstsp 258b9da0f5Sstsp #include "bpfilter.h" 268b9da0f5Sstsp 278b9da0f5Sstsp #include <sys/param.h> 288b9da0f5Sstsp #include <sys/sockio.h> 298b9da0f5Sstsp #include <sys/mbuf.h> 308b9da0f5Sstsp #include <sys/kernel.h> 318b9da0f5Sstsp #include <sys/socket.h> 328b9da0f5Sstsp #include <sys/systm.h> 338b9da0f5Sstsp #include <sys/task.h> 348b9da0f5Sstsp #include <sys/timeout.h> 358b9da0f5Sstsp #include <sys/conf.h> 368b9da0f5Sstsp #include <sys/device.h> 378b9da0f5Sstsp #include <sys/endian.h> 388b9da0f5Sstsp 398b9da0f5Sstsp #include <machine/bus.h> 408b9da0f5Sstsp #include <machine/intr.h> 418b9da0f5Sstsp 428b9da0f5Sstsp #if NBPFILTER > 0 438b9da0f5Sstsp #include <net/bpf.h> 448b9da0f5Sstsp #endif 458b9da0f5Sstsp #include <net/if.h> 468b9da0f5Sstsp #include <net/if_dl.h> 478b9da0f5Sstsp #include <net/if_media.h> 488b9da0f5Sstsp 498b9da0f5Sstsp #include <netinet/in.h> 508b9da0f5Sstsp #include <netinet/if_ether.h> 518b9da0f5Sstsp 528b9da0f5Sstsp #include <net80211/ieee80211_var.h> 538b9da0f5Sstsp #include <net80211/ieee80211_radiotap.h> 548b9da0f5Sstsp 558b9da0f5Sstsp #include <dev/ic/r92creg.h> 568b9da0f5Sstsp #include <dev/ic/rtwnvar.h> 578b9da0f5Sstsp 5834a2cadeSkevlo #define RTWN_RIDX_CCK1 0 5934a2cadeSkevlo #define RTWN_RIDX_CCK2 1 6034a2cadeSkevlo #define RTWN_RIDX_CCK11 3 6134a2cadeSkevlo #define RTWN_RIDX_OFDM6 4 6234a2cadeSkevlo #define RTWN_RIDX_OFDM54 11 6334a2cadeSkevlo #define RTWN_RIDX_MCS0 12 6434a2cadeSkevlo #define RTWN_RIDX_MCS8 (RTWN_RIDX_MCS0 + 8) 6534a2cadeSkevlo #define RTWN_RIDX_MCS15 27 6634a2cadeSkevlo #define RTWN_RIDX_MAX 27 6734a2cadeSkevlo 6834a2cadeSkevlo #define RTWN_POWER_CCK1 0 6934a2cadeSkevlo #define RTWN_POWER_CCK2 1 7034a2cadeSkevlo #define RTWN_POWER_CCK55 2 7134a2cadeSkevlo #define RTWN_POWER_CCK11 3 7234a2cadeSkevlo #define RTWN_POWER_OFDM6 4 7334a2cadeSkevlo #define RTWN_POWER_OFDM9 5 7434a2cadeSkevlo #define RTWN_POWER_OFDM12 6 7534a2cadeSkevlo #define RTWN_POWER_OFDM18 7 7634a2cadeSkevlo #define RTWN_POWER_OFDM24 8 7734a2cadeSkevlo #define RTWN_POWER_OFDM36 9 7834a2cadeSkevlo #define RTWN_POWER_OFDM48 10 7934a2cadeSkevlo #define RTWN_POWER_OFDM54 11 8034a2cadeSkevlo #define RTWN_POWER_MCS(mcs) (12 + (mcs)) 8134a2cadeSkevlo #define RTWN_POWER_COUNT 28 8234a2cadeSkevlo 838b9da0f5Sstsp 848b9da0f5Sstsp #ifdef RTWN_DEBUG 858b9da0f5Sstsp #define DPRINTF(x) do { if (rtwn_debug) printf x; } while (0) 868b9da0f5Sstsp #define DPRINTFN(n, x) do { if (rtwn_debug >= (n)) printf x; } while (0) 878b9da0f5Sstsp int rtwn_debug = 0; 888b9da0f5Sstsp #else 898b9da0f5Sstsp #define DPRINTF(x) 908b9da0f5Sstsp #define DPRINTFN(n, x) 918b9da0f5Sstsp #endif 928b9da0f5Sstsp 933150bca2Sstsp /* Registers to save and restore during IQ calibration. */ 943150bca2Sstsp struct rtwn_iq_cal_regs { 953150bca2Sstsp uint32_t adda[16]; 963150bca2Sstsp uint8_t txpause; 973150bca2Sstsp uint8_t bcn_ctrl; 983150bca2Sstsp uint8_t bcn_ctrl1; 993150bca2Sstsp uint32_t gpio_muxcfg; 1003150bca2Sstsp uint32_t ofdm0_trxpathena; 1013150bca2Sstsp uint32_t ofdm0_trmuxpar; 10290540544Sjmatthew uint32_t fpga0_rfifacesw0; 1033150bca2Sstsp uint32_t fpga0_rfifacesw1; 10490540544Sjmatthew uint32_t fpga0_rfifaceoe0; 10590540544Sjmatthew uint32_t fpga0_rfifaceoe1; 10690540544Sjmatthew uint32_t config_ant_a; 10790540544Sjmatthew uint32_t config_ant_b; 10890540544Sjmatthew uint32_t cck0_afesetting; 1093150bca2Sstsp }; 1103150bca2Sstsp 1118b9da0f5Sstsp void rtwn_write_1(struct rtwn_softc *, uint16_t, uint8_t); 1128b9da0f5Sstsp void rtwn_write_2(struct rtwn_softc *, uint16_t, uint16_t); 1138b9da0f5Sstsp void rtwn_write_4(struct rtwn_softc *, uint16_t, uint32_t); 1148b9da0f5Sstsp uint8_t rtwn_read_1(struct rtwn_softc *, uint16_t); 1158b9da0f5Sstsp uint16_t rtwn_read_2(struct rtwn_softc *, uint16_t); 1168b9da0f5Sstsp uint32_t rtwn_read_4(struct rtwn_softc *, uint16_t); 1178b9da0f5Sstsp int rtwn_fw_cmd(struct rtwn_softc *, uint8_t, const void *, int); 118067851b1Skevlo void rtwn_rf_write(struct rtwn_softc *, int, uint16_t, uint32_t); 1198b9da0f5Sstsp uint32_t rtwn_rf_read(struct rtwn_softc *, int, uint8_t); 1208b9da0f5Sstsp void rtwn_cam_write(struct rtwn_softc *, uint32_t, uint32_t); 1218b9da0f5Sstsp uint8_t rtwn_efuse_read_1(struct rtwn_softc *, uint16_t); 12234a2cadeSkevlo void rtwn_efuse_read(struct rtwn_softc *, uint8_t *, size_t); 1235fda900fSstsp void rtwn_efuse_switch_power(struct rtwn_softc *); 12434a2cadeSkevlo int rtwn_read_chipid(struct rtwn_softc *); 125bedde684Skevlo void rtwn_read_rom(struct rtwn_softc *); 12634a2cadeSkevlo void rtwn_r92c_read_rom(struct rtwn_softc *); 12790540544Sjmatthew void rtwn_r92e_read_rom(struct rtwn_softc *); 1285fda900fSstsp void rtwn_r88e_read_rom(struct rtwn_softc *); 129067851b1Skevlo void rtwn_r88f_read_rom(struct rtwn_softc *); 130600882e8Sjmatthew void rtwn_r23a_read_rom(struct rtwn_softc *); 1318b9da0f5Sstsp int rtwn_media_change(struct ifnet *); 1328b9da0f5Sstsp int rtwn_ra_init(struct rtwn_softc *); 1335fda900fSstsp int rtwn_r92c_ra_init(struct rtwn_softc *, u_int8_t, u_int32_t, 1345fda900fSstsp int, uint32_t, int); 1355fda900fSstsp int rtwn_r88e_ra_init(struct rtwn_softc *, u_int8_t, u_int32_t, 1365fda900fSstsp int, uint32_t, int); 1378b9da0f5Sstsp void rtwn_tsf_sync_enable(struct rtwn_softc *); 1388b9da0f5Sstsp void rtwn_set_led(struct rtwn_softc *, int, int); 13956fbb35bSkevlo void rtwn_set_nettype(struct rtwn_softc *, enum ieee80211_opmode); 1406056a428Sstsp void rtwn_update_short_preamble(struct ieee80211com *); 14156fbb35bSkevlo void rtwn_r92c_update_short_preamble(struct rtwn_softc *); 14256fbb35bSkevlo void rtwn_r88e_update_short_preamble(struct rtwn_softc *); 1435fda900fSstsp int8_t rtwn_r88e_get_rssi(struct rtwn_softc *, int, void *); 144067851b1Skevlo int8_t rtwn_r88f_get_rssi(struct rtwn_softc *, int, void *); 1458b9da0f5Sstsp void rtwn_watchdog(struct ifnet *); 146bedde684Skevlo void rtwn_fw_reset(struct rtwn_softc *); 147bedde684Skevlo void rtwn_r92c_fw_reset(struct rtwn_softc *); 1485fda900fSstsp void rtwn_r88e_fw_reset(struct rtwn_softc *); 1498b9da0f5Sstsp int rtwn_load_firmware(struct rtwn_softc *); 1508b9da0f5Sstsp void rtwn_rf_init(struct rtwn_softc *); 1518b9da0f5Sstsp void rtwn_cam_init(struct rtwn_softc *); 1528b9da0f5Sstsp void rtwn_pa_bias_init(struct rtwn_softc *); 1538b9da0f5Sstsp void rtwn_rxfilter_init(struct rtwn_softc *); 1548b9da0f5Sstsp void rtwn_edca_init(struct rtwn_softc *); 1555fb81a6cSkevlo void rtwn_rate_fallback_init(struct rtwn_softc *); 15604cdc2f2Spatrick void rtwn_write_txpower(struct rtwn_softc *, int, uint16_t *); 157bedde684Skevlo void rtwn_get_txpower(struct rtwn_softc *sc, int, 158bedde684Skevlo struct ieee80211_channel *, struct ieee80211_channel *, 15904cdc2f2Spatrick uint16_t *); 16034a2cadeSkevlo void rtwn_r92c_get_txpower(struct rtwn_softc *, int, 1618b9da0f5Sstsp struct ieee80211_channel *, struct ieee80211_channel *, 16204cdc2f2Spatrick uint16_t *); 16390540544Sjmatthew void rtwn_r92e_get_txpower(struct rtwn_softc *, int, 16490540544Sjmatthew struct ieee80211_channel *, 16504cdc2f2Spatrick struct ieee80211_channel *, uint16_t *); 1665fda900fSstsp void rtwn_r88e_get_txpower(struct rtwn_softc *, int, 1675fda900fSstsp struct ieee80211_channel *, 16804cdc2f2Spatrick struct ieee80211_channel *, uint16_t *); 1698b9da0f5Sstsp void rtwn_set_txpower(struct rtwn_softc *, 1708b9da0f5Sstsp struct ieee80211_channel *, struct ieee80211_channel *); 1718b9da0f5Sstsp void rtwn_set_chan(struct rtwn_softc *, 1728b9da0f5Sstsp struct ieee80211_channel *, struct ieee80211_channel *); 173067851b1Skevlo int rtwn_chan2group(int); 1748b9da0f5Sstsp int rtwn_iq_calib_chain(struct rtwn_softc *, int, uint16_t[2], 1758b9da0f5Sstsp uint16_t[2]); 1768b9da0f5Sstsp void rtwn_iq_calib_run(struct rtwn_softc *, int, uint16_t[2][2], 1773150bca2Sstsp uint16_t rx[2][2], struct rtwn_iq_cal_regs *); 1788b9da0f5Sstsp int rtwn_iq_calib_compare_results(uint16_t[2][2], uint16_t[2][2], 1798b9da0f5Sstsp uint16_t[2][2], uint16_t[2][2], int); 1808b9da0f5Sstsp void rtwn_iq_calib_write_results(struct rtwn_softc *, uint16_t[2], 1818b9da0f5Sstsp uint16_t[2], int); 1828b9da0f5Sstsp void rtwn_iq_calib(struct rtwn_softc *); 1838b9da0f5Sstsp void rtwn_lc_calib(struct rtwn_softc *); 1848b9da0f5Sstsp void rtwn_temp_calib(struct rtwn_softc *); 18523340becSstsp void rtwn_enable_intr(struct rtwn_softc *); 18623340becSstsp void rtwn_disable_intr(struct rtwn_softc *); 1878b9da0f5Sstsp int rtwn_init(struct ifnet *); 1888b9da0f5Sstsp void rtwn_init_task(void *); 1898b9da0f5Sstsp void rtwn_stop(struct ifnet *); 1908b9da0f5Sstsp 1918b9da0f5Sstsp /* Aliases. */ 1928b9da0f5Sstsp #define rtwn_bb_write rtwn_write_4 1938b9da0f5Sstsp #define rtwn_bb_read rtwn_read_4 1948b9da0f5Sstsp 19534a2cadeSkevlo /* 19634a2cadeSkevlo * Macro to convert 4-bit signed integer to 8-bit signed integer. 19734a2cadeSkevlo */ 19834a2cadeSkevlo #define RTWN_SIGN4TO8(val) (((val) & 0x08) ? (val) | 0xf0 : (val)) 19934a2cadeSkevlo 200fde5a185Sstsp int 20134a2cadeSkevlo rtwn_attach(struct device *pdev, struct rtwn_softc *sc) 2028b9da0f5Sstsp { 2038b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 2048b9da0f5Sstsp struct ifnet *ifp = &ic->ic_if; 2058b9da0f5Sstsp int i, error; 2068b9da0f5Sstsp 2078b9da0f5Sstsp sc->sc_pdev = pdev; 2088b9da0f5Sstsp 2098b9da0f5Sstsp task_set(&sc->init_task, rtwn_init_task, sc); 2108b9da0f5Sstsp 21134a2cadeSkevlo error = rtwn_read_chipid(sc); 2128b9da0f5Sstsp if (error != 0) { 21323340becSstsp printf("%s: unsupported chip\n", sc->sc_pdev->dv_xname); 214799517fdSstsp return (ENXIO); 215799517fdSstsp } 2168b9da0f5Sstsp 2178b9da0f5Sstsp /* Determine number of Tx/Rx chains. */ 2188bca19d0Skevlo if (sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_92E)) { 2198b9da0f5Sstsp sc->ntxchains = (sc->chip & RTWN_CHIP_92C_1T2R) ? 1 : 2; 2208b9da0f5Sstsp sc->nrxchains = 2; 221c2c8be24Smestre } else { 2228b9da0f5Sstsp sc->ntxchains = 1; 2238b9da0f5Sstsp sc->nrxchains = 1; 2248b9da0f5Sstsp } 2255fda900fSstsp 2268b9da0f5Sstsp rtwn_read_rom(sc); 2278b9da0f5Sstsp 22823340becSstsp if (sc->chip & RTWN_CHIP_PCI) { 2298b9da0f5Sstsp printf("%s: MAC/BB RTL%s, RF 6052 %dT%dR, address %s\n", 2308b9da0f5Sstsp sc->sc_pdev->dv_xname, 2315fda900fSstsp (sc->chip & RTWN_CHIP_92C) ? "8192CE" : 232600882e8Sjmatthew (sc->chip & RTWN_CHIP_88E) ? "8188EE" : 233600882e8Sjmatthew (sc->chip & RTWN_CHIP_92E) ? "8192EE" : 234600882e8Sjmatthew (sc->chip & RTWN_CHIP_23A) ? "8723AE" : 235600882e8Sjmatthew (sc->chip & RTWN_CHIP_23B) ? "8723BE" : "8188CE", 2368b9da0f5Sstsp sc->ntxchains, sc->nrxchains, 2378b9da0f5Sstsp ether_sprintf(ic->ic_myaddr)); 23823340becSstsp } else if (sc->chip & RTWN_CHIP_USB) { 23923340becSstsp printf("%s: MAC/BB RTL%s, RF 6052 %dT%dR, address %s\n", 24023340becSstsp sc->sc_pdev->dv_xname, 24123340becSstsp (sc->chip & RTWN_CHIP_92C) ? "8192CU" : 24290540544Sjmatthew (sc->chip & RTWN_CHIP_92E) ? "8192EU" : 24323340becSstsp (sc->chip & RTWN_CHIP_88E) ? "8188EU" : 244067851b1Skevlo (sc->chip & RTWN_CHIP_88F) ? "8188FTV" : 24523340becSstsp (sc->board_type == R92C_BOARD_TYPE_HIGHPA) ? "8188RU" : 24623340becSstsp (sc->board_type == R92C_BOARD_TYPE_MINICARD) ? 24723340becSstsp "8188CE-VAU" : "8188CUS", 24823340becSstsp sc->ntxchains, sc->nrxchains, 24923340becSstsp ether_sprintf(ic->ic_myaddr)); 25023340becSstsp } else { 25123340becSstsp printf("%s: unsupported chip\n", sc->sc_pdev->dv_xname); 25223340becSstsp return (ENXIO); 25323340becSstsp } 2548b9da0f5Sstsp 2558b9da0f5Sstsp ic->ic_phytype = IEEE80211_T_OFDM; /* Not only, but not used. */ 2568b9da0f5Sstsp ic->ic_opmode = IEEE80211_M_STA; /* Default to BSS mode. */ 2578b9da0f5Sstsp ic->ic_state = IEEE80211_S_INIT; 2588b9da0f5Sstsp 2598b9da0f5Sstsp /* Set device capabilities. */ 2608b9da0f5Sstsp ic->ic_caps = 2618b9da0f5Sstsp IEEE80211_C_MONITOR | /* Monitor mode supported. */ 2628b9da0f5Sstsp IEEE80211_C_SHPREAMBLE | /* Short preamble supported. */ 2638b9da0f5Sstsp IEEE80211_C_SHSLOT | /* Short slot time supported. */ 2648b9da0f5Sstsp IEEE80211_C_WEP | /* WEP. */ 2658b9da0f5Sstsp IEEE80211_C_RSN; /* WPA/RSN. */ 2668b9da0f5Sstsp 2678b9da0f5Sstsp /* Set HT capabilities. */ 2688b9da0f5Sstsp ic->ic_htcaps = 2698b9da0f5Sstsp IEEE80211_HTCAP_CBW20_40 | 2708b9da0f5Sstsp IEEE80211_HTCAP_DSSSCCK40; 2718b9da0f5Sstsp /* Set supported HT rates. */ 2728b9da0f5Sstsp for (i = 0; i < sc->nrxchains; i++) 2738b9da0f5Sstsp ic->ic_sup_mcs[i] = 0xff; 2748b9da0f5Sstsp 2758b9da0f5Sstsp /* Set supported .11b and .11g rates. */ 2768b9da0f5Sstsp ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b; 2778b9da0f5Sstsp ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g; 2788b9da0f5Sstsp 2798b9da0f5Sstsp /* Set supported .11b and .11g channels (1 through 14). */ 2808b9da0f5Sstsp for (i = 1; i <= 14; i++) { 2818b9da0f5Sstsp ic->ic_channels[i].ic_freq = 2828b9da0f5Sstsp ieee80211_ieee2mhz(i, IEEE80211_CHAN_2GHZ); 2838b9da0f5Sstsp ic->ic_channels[i].ic_flags = 2848b9da0f5Sstsp IEEE80211_CHAN_CCK | IEEE80211_CHAN_OFDM | 2858b9da0f5Sstsp IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ; 2868b9da0f5Sstsp } 2878b9da0f5Sstsp 2888b9da0f5Sstsp #ifdef notyet 2898b9da0f5Sstsp /* 2908b9da0f5Sstsp * The number of STAs that we can support is limited by the number 2918b9da0f5Sstsp * of CAM entries used for hardware crypto. 2928b9da0f5Sstsp */ 2938b9da0f5Sstsp ic->ic_max_nnodes = R92C_CAM_ENTRY_COUNT - 4; 2948b9da0f5Sstsp if (ic->ic_max_nnodes > IEEE80211_CACHE_SIZE) 2958b9da0f5Sstsp ic->ic_max_nnodes = IEEE80211_CACHE_SIZE; 2968b9da0f5Sstsp #endif 2978b9da0f5Sstsp 2988b9da0f5Sstsp ifp->if_softc = sc; 2998b9da0f5Sstsp ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 3008b9da0f5Sstsp ifp->if_ioctl = rtwn_ioctl; 3018b9da0f5Sstsp ifp->if_start = rtwn_start; 3028b9da0f5Sstsp ifp->if_watchdog = rtwn_watchdog; 3038b9da0f5Sstsp memcpy(ifp->if_xname, sc->sc_pdev->dv_xname, IFNAMSIZ); 3048b9da0f5Sstsp 3058b9da0f5Sstsp if_attach(ifp); 3068b9da0f5Sstsp ieee80211_ifattach(ifp); 3076056a428Sstsp ic->ic_updateslot = rtwn_updateslot; 3088b9da0f5Sstsp ic->ic_updateedca = rtwn_updateedca; 3098b9da0f5Sstsp #ifdef notyet 3108b9da0f5Sstsp ic->ic_set_key = rtwn_set_key; 3118b9da0f5Sstsp ic->ic_delete_key = rtwn_delete_key; 3128b9da0f5Sstsp #endif 3138b9da0f5Sstsp /* Override state transition machine. */ 3148b9da0f5Sstsp sc->sc_newstate = ic->ic_newstate; 3158b9da0f5Sstsp ic->ic_newstate = rtwn_newstate; 3168b9da0f5Sstsp ieee80211_media_init(ifp, rtwn_media_change, ieee80211_media_status); 317fde5a185Sstsp 318fde5a185Sstsp return (0); 3198b9da0f5Sstsp } 3208b9da0f5Sstsp 3218b9da0f5Sstsp int 3228b9da0f5Sstsp rtwn_detach(struct rtwn_softc *sc, int flags) 3238b9da0f5Sstsp { 3248b9da0f5Sstsp struct ifnet *ifp = &sc->sc_ic.ic_if; 3258b9da0f5Sstsp int s; 3268b9da0f5Sstsp 3278b9da0f5Sstsp s = splnet(); 3288b9da0f5Sstsp 3298b9da0f5Sstsp task_del(systq, &sc->init_task); 3308b9da0f5Sstsp 3318b9da0f5Sstsp if (ifp->if_softc != NULL) { 3328b9da0f5Sstsp ieee80211_ifdetach(ifp); 3338b9da0f5Sstsp if_detach(ifp); 3348b9da0f5Sstsp } 3358b9da0f5Sstsp 3368b9da0f5Sstsp splx(s); 3378b9da0f5Sstsp 3388b9da0f5Sstsp return (0); 3398b9da0f5Sstsp } 3408b9da0f5Sstsp 3418b9da0f5Sstsp int 3428b9da0f5Sstsp rtwn_activate(struct rtwn_softc *sc, int act) 3438b9da0f5Sstsp { 3448b9da0f5Sstsp struct ifnet *ifp = &sc->sc_ic.ic_if; 3458b9da0f5Sstsp 3468b9da0f5Sstsp switch (act) { 347a4d0895aSstsp case DVACT_QUIESCE: /* rtwn_stop() may sleep */ 3488b9da0f5Sstsp if (ifp->if_flags & IFF_RUNNING) 3498b9da0f5Sstsp rtwn_stop(ifp); 3508b9da0f5Sstsp break; 3518b9da0f5Sstsp case DVACT_WAKEUP: 3528b9da0f5Sstsp rtwn_init_task(sc); 3538b9da0f5Sstsp break; 3548b9da0f5Sstsp } 3558b9da0f5Sstsp return (0); 3568b9da0f5Sstsp } 3578b9da0f5Sstsp 3588b9da0f5Sstsp void 3598b9da0f5Sstsp rtwn_write_1(struct rtwn_softc *sc, uint16_t addr, uint8_t val) 3608b9da0f5Sstsp { 3618b9da0f5Sstsp sc->sc_ops.write_1(sc->sc_ops.cookie, addr, val); 3628b9da0f5Sstsp } 3638b9da0f5Sstsp 3648b9da0f5Sstsp void 3658b9da0f5Sstsp rtwn_write_2(struct rtwn_softc *sc, uint16_t addr, uint16_t val) 3668b9da0f5Sstsp { 3678b9da0f5Sstsp sc->sc_ops.write_2(sc->sc_ops.cookie, addr, val); 3688b9da0f5Sstsp } 3698b9da0f5Sstsp 3708b9da0f5Sstsp void 3718b9da0f5Sstsp rtwn_write_4(struct rtwn_softc *sc, uint16_t addr, uint32_t val) 3728b9da0f5Sstsp { 3738b9da0f5Sstsp sc->sc_ops.write_4(sc->sc_ops.cookie, addr, val); 3748b9da0f5Sstsp } 3758b9da0f5Sstsp 3768b9da0f5Sstsp uint8_t 3778b9da0f5Sstsp rtwn_read_1(struct rtwn_softc *sc, uint16_t addr) 3788b9da0f5Sstsp { 3798b9da0f5Sstsp return sc->sc_ops.read_1(sc->sc_ops.cookie, addr); 3808b9da0f5Sstsp } 3818b9da0f5Sstsp 3828b9da0f5Sstsp uint16_t 3838b9da0f5Sstsp rtwn_read_2(struct rtwn_softc *sc, uint16_t addr) 3848b9da0f5Sstsp { 3858b9da0f5Sstsp return sc->sc_ops.read_2(sc->sc_ops.cookie, addr); 3868b9da0f5Sstsp } 3878b9da0f5Sstsp 3888b9da0f5Sstsp uint32_t 3898b9da0f5Sstsp rtwn_read_4(struct rtwn_softc *sc, uint16_t addr) 3908b9da0f5Sstsp { 3918b9da0f5Sstsp return sc->sc_ops.read_4(sc->sc_ops.cookie, addr); 3928b9da0f5Sstsp } 3938b9da0f5Sstsp 3948b9da0f5Sstsp int 3958b9da0f5Sstsp rtwn_fw_cmd(struct rtwn_softc *sc, uint8_t id, const void *buf, int len) 3968b9da0f5Sstsp { 3978b9da0f5Sstsp struct r92c_fw_cmd cmd; 3988b9da0f5Sstsp int ntries; 3998b9da0f5Sstsp 4008b9da0f5Sstsp /* Wait for current FW box to be empty. */ 4018b9da0f5Sstsp for (ntries = 0; ntries < 100; ntries++) { 4028b9da0f5Sstsp if (!(rtwn_read_1(sc, R92C_HMETFR) & (1 << sc->fwcur))) 4038b9da0f5Sstsp break; 40490540544Sjmatthew DELAY(10); 4058b9da0f5Sstsp } 4068b9da0f5Sstsp if (ntries == 100) { 4078b9da0f5Sstsp printf("%s: could not send firmware command %d\n", 4088b9da0f5Sstsp sc->sc_pdev->dv_xname, id); 4098b9da0f5Sstsp return (ETIMEDOUT); 4108b9da0f5Sstsp } 4118b9da0f5Sstsp memset(&cmd, 0, sizeof(cmd)); 4128b9da0f5Sstsp cmd.id = id; 4138b9da0f5Sstsp if (len > 3) 4148b9da0f5Sstsp cmd.id |= R92C_CMD_FLAG_EXT; 4158b9da0f5Sstsp KASSERT(len <= sizeof(cmd.msg)); 4168b9da0f5Sstsp memcpy(cmd.msg, buf, len); 4178b9da0f5Sstsp 4188b9da0f5Sstsp /* Write the first word last since that will trigger the FW. */ 41990540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) 42090540544Sjmatthew rtwn_write_2(sc, R88E_HMEBOX_EXT(sc->fwcur), 42190540544Sjmatthew *((uint8_t *)&cmd + 4)); 42290540544Sjmatthew else 42390540544Sjmatthew rtwn_write_2(sc, R92C_HMEBOX_EXT(sc->fwcur), 42490540544Sjmatthew *((uint8_t *)&cmd + 4)); 4258b9da0f5Sstsp rtwn_write_4(sc, R92C_HMEBOX(sc->fwcur), *((uint8_t *)&cmd + 0)); 4268b9da0f5Sstsp 4278b9da0f5Sstsp sc->fwcur = (sc->fwcur + 1) % R92C_H2C_NBOX; 4288b9da0f5Sstsp 42923340becSstsp if (sc->chip & RTWN_CHIP_PCI) { 4308b9da0f5Sstsp /* Give firmware some time for processing. */ 4318b9da0f5Sstsp DELAY(2000); 43223340becSstsp } 4338b9da0f5Sstsp 4348b9da0f5Sstsp return (0); 4358b9da0f5Sstsp } 4368b9da0f5Sstsp 4378b9da0f5Sstsp void 438067851b1Skevlo rtwn_rf_write(struct rtwn_softc *sc, int chain, uint16_t addr, uint32_t val) 4398b9da0f5Sstsp { 4405fda900fSstsp uint32_t param_addr; 4415fda900fSstsp 44290540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 44390540544Sjmatthew rtwn_write_4(sc, R92C_FPGA0_POWER_SAVE, 44490540544Sjmatthew rtwn_read_4(sc, R92C_FPGA0_POWER_SAVE) & ~0x20000); 44590540544Sjmatthew } 44690540544Sjmatthew 447067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) 4485fda900fSstsp param_addr = SM(R88E_LSSI_PARAM_ADDR, addr); 4495fda900fSstsp else 4505fda900fSstsp param_addr = SM(R92C_LSSI_PARAM_ADDR, addr); 4515fda900fSstsp 4528b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(chain), 4535fda900fSstsp param_addr | SM(R92C_LSSI_PARAM_DATA, val)); 45490540544Sjmatthew 45590540544Sjmatthew DELAY(1); 45690540544Sjmatthew 45790540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 45890540544Sjmatthew rtwn_write_4(sc, R92C_FPGA0_POWER_SAVE, 45990540544Sjmatthew rtwn_read_4(sc, R92C_FPGA0_POWER_SAVE) | 0x20000); 46090540544Sjmatthew } 4618b9da0f5Sstsp } 4628b9da0f5Sstsp 4638b9da0f5Sstsp uint32_t 4648b9da0f5Sstsp rtwn_rf_read(struct rtwn_softc *sc, int chain, uint8_t addr) 4658b9da0f5Sstsp { 4668b9da0f5Sstsp uint32_t reg[R92C_MAX_CHAINS], val; 4678b9da0f5Sstsp 4688b9da0f5Sstsp reg[0] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(0)); 4698b9da0f5Sstsp if (chain != 0) 4708b9da0f5Sstsp reg[chain] = rtwn_bb_read(sc, R92C_HSSI_PARAM2(chain)); 4718b9da0f5Sstsp 4728b9da0f5Sstsp rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), 4738b9da0f5Sstsp reg[0] & ~R92C_HSSI_PARAM2_READ_EDGE); 4748b9da0f5Sstsp DELAY(1000); 4758b9da0f5Sstsp 4768b9da0f5Sstsp rtwn_bb_write(sc, R92C_HSSI_PARAM2(chain), 4778b9da0f5Sstsp RW(reg[chain], R92C_HSSI_PARAM2_READ_ADDR, addr) | 4788b9da0f5Sstsp R92C_HSSI_PARAM2_READ_EDGE); 4798b9da0f5Sstsp DELAY(1000); 4808b9da0f5Sstsp 4815cc093eeSkevlo if (!(sc->chip & RTWN_CHIP_88E)) { 4828b9da0f5Sstsp rtwn_bb_write(sc, R92C_HSSI_PARAM2(0), 4838b9da0f5Sstsp reg[0] | R92C_HSSI_PARAM2_READ_EDGE); 4848b9da0f5Sstsp DELAY(1000); 4855cc093eeSkevlo } 4868b9da0f5Sstsp 4878b9da0f5Sstsp if (rtwn_bb_read(sc, R92C_HSSI_PARAM1(chain)) & R92C_HSSI_PARAM1_PI) 4888b9da0f5Sstsp val = rtwn_bb_read(sc, R92C_HSPI_READBACK(chain)); 4898b9da0f5Sstsp else 4908b9da0f5Sstsp val = rtwn_bb_read(sc, R92C_LSSI_READBACK(chain)); 4918b9da0f5Sstsp return (MS(val, R92C_LSSI_READBACK_DATA)); 4928b9da0f5Sstsp } 4938b9da0f5Sstsp 4948b9da0f5Sstsp void 4958b9da0f5Sstsp rtwn_cam_write(struct rtwn_softc *sc, uint32_t addr, uint32_t data) 4968b9da0f5Sstsp { 4978b9da0f5Sstsp rtwn_write_4(sc, R92C_CAMWRITE, data); 4988b9da0f5Sstsp rtwn_write_4(sc, R92C_CAMCMD, 4998b9da0f5Sstsp R92C_CAMCMD_POLLING | R92C_CAMCMD_WRITE | 5008b9da0f5Sstsp SM(R92C_CAMCMD_ADDR, addr)); 5018b9da0f5Sstsp } 5028b9da0f5Sstsp 5038b9da0f5Sstsp uint8_t 5048b9da0f5Sstsp rtwn_efuse_read_1(struct rtwn_softc *sc, uint16_t addr) 5058b9da0f5Sstsp { 5068b9da0f5Sstsp uint32_t reg; 5078b9da0f5Sstsp int ntries; 5088b9da0f5Sstsp 5098b9da0f5Sstsp reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); 5108b9da0f5Sstsp reg = RW(reg, R92C_EFUSE_CTRL_ADDR, addr); 5118b9da0f5Sstsp reg &= ~R92C_EFUSE_CTRL_VALID; 5128b9da0f5Sstsp rtwn_write_4(sc, R92C_EFUSE_CTRL, reg); 5138b9da0f5Sstsp /* Wait for read operation to complete. */ 5148b9da0f5Sstsp for (ntries = 0; ntries < 100; ntries++) { 5158b9da0f5Sstsp reg = rtwn_read_4(sc, R92C_EFUSE_CTRL); 5168b9da0f5Sstsp if (reg & R92C_EFUSE_CTRL_VALID) 5178b9da0f5Sstsp return (MS(reg, R92C_EFUSE_CTRL_DATA)); 5188b9da0f5Sstsp DELAY(5); 5198b9da0f5Sstsp } 5208b9da0f5Sstsp printf("%s: could not read efuse byte at address 0x%x\n", 5218b9da0f5Sstsp sc->sc_pdev->dv_xname, addr); 5228b9da0f5Sstsp return (0xff); 5238b9da0f5Sstsp } 5248b9da0f5Sstsp 5258b9da0f5Sstsp void 52634a2cadeSkevlo rtwn_efuse_read(struct rtwn_softc *sc, uint8_t *rom, size_t size) 5278b9da0f5Sstsp { 52834a2cadeSkevlo uint8_t off, msk, tmp; 5298b9da0f5Sstsp uint16_t addr = 0; 5308b9da0f5Sstsp uint32_t reg; 53134a2cadeSkevlo int i, len; 5328b9da0f5Sstsp 5339f1b7096Skevlo if (!(sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_88C))) 53423340becSstsp rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON); 5359f1b7096Skevlo 5365fda900fSstsp rtwn_efuse_switch_power(sc); 5375fda900fSstsp 538067851b1Skevlo /* Switch bank to 0 for wifi/bt later use. */ 539067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) { 540067851b1Skevlo reg = rtwn_read_4(sc, R92C_EFUSE_TEST); 541067851b1Skevlo reg = RW(reg, R92C_EFUSE_TEST_SEL, 0); 542067851b1Skevlo rtwn_write_4(sc, R92C_EFUSE_TEST, reg); 543067851b1Skevlo } 544067851b1Skevlo 54534a2cadeSkevlo memset(rom, 0xff, size); 54634a2cadeSkevlo len = (sc->chip & RTWN_CHIP_88E) ? 256 : 512; 54734a2cadeSkevlo while (addr < len) { 5488b9da0f5Sstsp reg = rtwn_efuse_read_1(sc, addr); 5498b9da0f5Sstsp if (reg == 0xff) 5508b9da0f5Sstsp break; 5518b9da0f5Sstsp addr++; 55234a2cadeSkevlo 55334a2cadeSkevlo /* Check for extended header. */ 55434a2cadeSkevlo if ((sc->sc_flags & RTWN_FLAG_EXT_HDR) && 55534a2cadeSkevlo (reg & 0x1f) == 0x0f) { 55634a2cadeSkevlo tmp = (reg & 0xe0) >> 5; 55734a2cadeSkevlo reg = rtwn_efuse_read_1(sc, addr); 558799140aeSstsp addr++; 55934a2cadeSkevlo if ((reg & 0x0f) != 0x0f) 56034a2cadeSkevlo off = ((reg & 0xf0) >> 1) | tmp; 561799140aeSstsp else 562799140aeSstsp continue; 56334a2cadeSkevlo } else 5648b9da0f5Sstsp off = reg >> 4; 5658b9da0f5Sstsp msk = reg & 0xf; 5668b9da0f5Sstsp for (i = 0; i < 4; i++) { 5678b9da0f5Sstsp if (msk & (1 << i)) 5688b9da0f5Sstsp continue; 569799140aeSstsp rom[off * 8 + i * 2 + 0] = rtwn_efuse_read_1(sc, addr); 5708b9da0f5Sstsp addr++; 571799140aeSstsp rom[off * 8 + i * 2 + 1] = rtwn_efuse_read_1(sc, addr); 5728b9da0f5Sstsp addr++; 5738b9da0f5Sstsp } 5748b9da0f5Sstsp } 5758b9da0f5Sstsp #ifdef RTWN_DEBUG 5768b9da0f5Sstsp if (rtwn_debug >= 2) { 5778b9da0f5Sstsp /* Dump ROM content. */ 5788b9da0f5Sstsp printf("\n"); 5791fa62374Sjsg for (i = 0; i < size; i++) 5808b9da0f5Sstsp printf("%02x:", rom[i]); 5818b9da0f5Sstsp printf("\n"); 5828b9da0f5Sstsp } 5838b9da0f5Sstsp #endif 5849f1b7096Skevlo if (!(sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_88C))) 58523340becSstsp rtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF); 5868b9da0f5Sstsp } 5878b9da0f5Sstsp 5885fda900fSstsp void 5895fda900fSstsp rtwn_efuse_switch_power(struct rtwn_softc *sc) 5905fda900fSstsp { 5915fda900fSstsp uint16_t reg; 5925fda900fSstsp 593067851b1Skevlo if (!(sc->chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))) { 5945fda900fSstsp reg = rtwn_read_2(sc, R92C_SYS_ISO_CTRL); 5955fda900fSstsp if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) { 5965fda900fSstsp rtwn_write_2(sc, R92C_SYS_ISO_CTRL, 5975fda900fSstsp reg | R92C_SYS_ISO_CTRL_PWC_EV12V); 5985fda900fSstsp } 5992d2a7e26Skevlo } 6005fda900fSstsp reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); 6015fda900fSstsp if (!(reg & R92C_SYS_FUNC_EN_ELDR)) { 6025fda900fSstsp rtwn_write_2(sc, R92C_SYS_FUNC_EN, 6035fda900fSstsp reg | R92C_SYS_FUNC_EN_ELDR); 6045fda900fSstsp } 6055fda900fSstsp reg = rtwn_read_2(sc, R92C_SYS_CLKR); 6065fda900fSstsp if ((reg & (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) != 6075fda900fSstsp (R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M)) { 6085fda900fSstsp rtwn_write_2(sc, R92C_SYS_CLKR, 6095fda900fSstsp reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M); 6105fda900fSstsp } 6115fda900fSstsp } 6125fda900fSstsp 6138b9da0f5Sstsp int 61434a2cadeSkevlo rtwn_read_chipid(struct rtwn_softc *sc) 6158b9da0f5Sstsp { 6168b9da0f5Sstsp uint32_t reg; 6178b9da0f5Sstsp 618067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) { 61934a2cadeSkevlo sc->sc_flags |= RTWN_FLAG_EXT_HDR; 6205fda900fSstsp return (0); 62123340becSstsp } 6225fda900fSstsp 6238b9da0f5Sstsp reg = rtwn_read_4(sc, R92C_SYS_CFG); 6248b9da0f5Sstsp if (reg & R92C_SYS_CFG_TRP_VAUX_EN) 6258b9da0f5Sstsp /* Unsupported test chip. */ 6268b9da0f5Sstsp return (EIO); 6278b9da0f5Sstsp 62834a2cadeSkevlo if ((sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_88C)) != 0) { 6298b9da0f5Sstsp if (reg & R92C_SYS_CFG_TYPE_92C) { 63034a2cadeSkevlo sc->chip &= ~RTWN_CHIP_88C; 6318b9da0f5Sstsp /* Check if it is a castrated 8192C. */ 6328b9da0f5Sstsp if (MS(rtwn_read_4(sc, R92C_HPON_FSM), 6338b9da0f5Sstsp R92C_HPON_FSM_CHIP_BONDING_ID) == 6348b9da0f5Sstsp R92C_HPON_FSM_CHIP_BONDING_ID_92C_1T2R) 6358b9da0f5Sstsp sc->chip |= RTWN_CHIP_92C_1T2R; 636799517fdSstsp } else 63734a2cadeSkevlo sc->chip &= ~RTWN_CHIP_92C; 638799517fdSstsp 6398b9da0f5Sstsp if (reg & R92C_SYS_CFG_VENDOR_UMC) { 6408b9da0f5Sstsp sc->chip |= RTWN_CHIP_UMC; 6418b9da0f5Sstsp if (MS(reg, R92C_SYS_CFG_CHIP_VER_RTL) == 0) 6428b9da0f5Sstsp sc->chip |= RTWN_CHIP_UMC_A_CUT; 6438b9da0f5Sstsp } 64423340becSstsp 6458b9da0f5Sstsp return (0); 646600882e8Sjmatthew } else if (sc->chip & RTWN_CHIP_23A) { 647600882e8Sjmatthew sc->sc_flags |= RTWN_FLAG_EXT_HDR; 648600882e8Sjmatthew 649600882e8Sjmatthew if ((reg & 0xf000) == 0) 650600882e8Sjmatthew sc->chip |= RTWN_CHIP_UMC_A_CUT; 651600882e8Sjmatthew return (0); 6528b9da0f5Sstsp } 6538b9da0f5Sstsp 65423340becSstsp return (ENXIO); /* unsupported chip */ 65523340becSstsp } 65623340becSstsp 6578b9da0f5Sstsp void 658bedde684Skevlo rtwn_read_rom(struct rtwn_softc *sc) 65934a2cadeSkevlo { 66034a2cadeSkevlo if (sc->chip & RTWN_CHIP_88E) 66134a2cadeSkevlo rtwn_r88e_read_rom(sc); 662067851b1Skevlo else if (sc->chip & RTWN_CHIP_88F) 663067851b1Skevlo rtwn_r88f_read_rom(sc); 66490540544Sjmatthew else if (sc->chip & RTWN_CHIP_92E) 66590540544Sjmatthew rtwn_r92e_read_rom(sc); 666600882e8Sjmatthew else if (sc->chip & RTWN_CHIP_23A) 667600882e8Sjmatthew rtwn_r23a_read_rom(sc); 66834a2cadeSkevlo else 66934a2cadeSkevlo rtwn_r92c_read_rom(sc); 67034a2cadeSkevlo } 67134a2cadeSkevlo 67234a2cadeSkevlo void 67334a2cadeSkevlo rtwn_r92c_read_rom(struct rtwn_softc *sc) 6748b9da0f5Sstsp { 6758b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 67634a2cadeSkevlo struct r92c_rom *rom = &sc->sc_r92c_rom; 6778b9da0f5Sstsp 6788b9da0f5Sstsp /* Read full ROM image. */ 67934a2cadeSkevlo rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r92c_rom, 68034a2cadeSkevlo sizeof(sc->sc_r92c_rom)); 6818b9da0f5Sstsp 6828b9da0f5Sstsp /* XXX Weird but this is what the vendor driver does. */ 6838b9da0f5Sstsp sc->pa_setting = rtwn_efuse_read_1(sc, 0x1fa); 6848b9da0f5Sstsp DPRINTF(("PA setting=0x%x\n", sc->pa_setting)); 6858b9da0f5Sstsp 6868b9da0f5Sstsp sc->board_type = MS(rom->rf_opt1, R92C_ROM_RF1_BOARD_TYPE); 6875fda900fSstsp DPRINTF(("board type=%d\n", sc->board_type)); 6888b9da0f5Sstsp 6898b9da0f5Sstsp sc->regulatory = MS(rom->rf_opt1, R92C_ROM_RF1_REGULATORY); 6908b9da0f5Sstsp DPRINTF(("regulatory type=%d\n", sc->regulatory)); 6918b9da0f5Sstsp 6928b9da0f5Sstsp IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr); 6938b9da0f5Sstsp } 6948b9da0f5Sstsp 6955fda900fSstsp void 69690540544Sjmatthew rtwn_r92e_read_rom(struct rtwn_softc *sc) 69790540544Sjmatthew { 69890540544Sjmatthew struct ieee80211com *ic = &sc->sc_ic; 69990540544Sjmatthew struct r92e_rom *rom = &sc->sc_r92e_rom; 70090540544Sjmatthew 70190540544Sjmatthew /* Read full ROM image. */ 70290540544Sjmatthew rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r92e_rom, 70390540544Sjmatthew sizeof(sc->sc_r92e_rom)); 70490540544Sjmatthew 70590540544Sjmatthew sc->crystal_cap = rom->xtal_k; 70690540544Sjmatthew DPRINTF(("crystal cap=0x%x\n", sc->crystal_cap)); 70790540544Sjmatthew 70890540544Sjmatthew sc->regulatory = MS(rom->rf_board_opt, R92C_ROM_RF1_REGULATORY); 70990540544Sjmatthew DPRINTF(("regulatory type=%d\n", sc->regulatory)); 71090540544Sjmatthew 71190540544Sjmatthew IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr); 71290540544Sjmatthew } 71390540544Sjmatthew 71490540544Sjmatthew void 7155fda900fSstsp rtwn_r88e_read_rom(struct rtwn_softc *sc) 7165fda900fSstsp { 7175fda900fSstsp struct ieee80211com *ic = &sc->sc_ic; 71834a2cadeSkevlo struct r88e_rom *rom = &sc->sc_r88e_rom; 7195fda900fSstsp 7205fda900fSstsp /* Read full ROM image. */ 72134a2cadeSkevlo rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r88e_rom, 722626433d9Skevlo sizeof(sc->sc_r88e_rom)); 7235fda900fSstsp 724d8e41173Sjmatthew sc->crystal_cap = (sc->chip & RTWN_CHIP_PCI) ? 0x20 : rom->xtal; 72534a2cadeSkevlo DPRINTF(("Crystal cap=0x%x\n", sc->crystal_cap)); 7265fda900fSstsp 72734a2cadeSkevlo sc->regulatory = MS(rom->rf_board_opt, R92C_ROM_RF1_REGULATORY); 72834a2cadeSkevlo DPRINTF(("regulatory type=%d\n", sc->regulatory)); 72934a2cadeSkevlo 730d8e41173Sjmatthew if (sc->chip & RTWN_CHIP_PCI) 731626433d9Skevlo IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->r88ee_rom.macaddr); 732d8e41173Sjmatthew else 733626433d9Skevlo IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->r88eu_rom.macaddr); 7345fda900fSstsp } 7355fda900fSstsp 736600882e8Sjmatthew void 737067851b1Skevlo rtwn_r88f_read_rom(struct rtwn_softc *sc) 738067851b1Skevlo { 739067851b1Skevlo struct ieee80211com *ic = &sc->sc_ic; 740067851b1Skevlo struct r88f_rom *rom = &sc->sc_r88f_rom; 741067851b1Skevlo 742067851b1Skevlo /* Read full ROM image. */ 743067851b1Skevlo rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r88f_rom, 744067851b1Skevlo sizeof(sc->sc_r88f_rom)); 745067851b1Skevlo 746067851b1Skevlo sc->crystal_cap = rom->xtal; 747067851b1Skevlo 748067851b1Skevlo IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr); 749067851b1Skevlo } 750067851b1Skevlo 751067851b1Skevlo void 752600882e8Sjmatthew rtwn_r23a_read_rom(struct rtwn_softc *sc) 753600882e8Sjmatthew { 754600882e8Sjmatthew struct ieee80211com *ic = &sc->sc_ic; 755600882e8Sjmatthew struct r23a_rom *rom = &sc->sc_r23a_rom; 756600882e8Sjmatthew 757600882e8Sjmatthew /* Read full ROM image. */ 758600882e8Sjmatthew rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r23a_rom, 759600882e8Sjmatthew sizeof(sc->sc_r23a_rom)); 760600882e8Sjmatthew 761600882e8Sjmatthew IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr); 762600882e8Sjmatthew } 763600882e8Sjmatthew 7648b9da0f5Sstsp int 7658b9da0f5Sstsp rtwn_media_change(struct ifnet *ifp) 7668b9da0f5Sstsp { 7678b9da0f5Sstsp int error; 7688b9da0f5Sstsp 7698b9da0f5Sstsp error = ieee80211_media_change(ifp); 7708b9da0f5Sstsp if (error != ENETRESET) 7718b9da0f5Sstsp return (error); 7728b9da0f5Sstsp 7738b9da0f5Sstsp if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 7748b9da0f5Sstsp (IFF_UP | IFF_RUNNING)) { 7758b9da0f5Sstsp rtwn_stop(ifp); 7762e342c84Skevlo error = rtwn_init(ifp); 7778b9da0f5Sstsp } 7782e342c84Skevlo return (error); 7798b9da0f5Sstsp } 7808b9da0f5Sstsp 7818b9da0f5Sstsp /* 7821f929dc7Sstsp * Initialize rate adaptation. 7838b9da0f5Sstsp */ 7848b9da0f5Sstsp int 7858b9da0f5Sstsp rtwn_ra_init(struct rtwn_softc *sc) 7868b9da0f5Sstsp { 7878b9da0f5Sstsp static const uint8_t map[] = 7888b9da0f5Sstsp { 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 }; 7898b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 7908b9da0f5Sstsp struct ieee80211_node *ni = ic->ic_bss; 7918b9da0f5Sstsp struct ieee80211_rateset *rs = &ni->ni_rates; 7928b9da0f5Sstsp uint32_t rates, basicrates; 7938b9da0f5Sstsp uint8_t mode; 794007f9c5dSjsg int maxrate, maxbasicrate, i, j; 795007f9c5dSjsg int error = 0; 7968b9da0f5Sstsp 7978b9da0f5Sstsp /* Get normal and basic rates mask. */ 7988b9da0f5Sstsp rates = basicrates = 0; 7998b9da0f5Sstsp maxrate = maxbasicrate = 0; 8008b9da0f5Sstsp for (i = 0; i < rs->rs_nrates; i++) { 8018b9da0f5Sstsp /* Convert 802.11 rate to HW rate index. */ 8028b9da0f5Sstsp for (j = 0; j < nitems(map); j++) 8038b9da0f5Sstsp if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == map[j]) 8048b9da0f5Sstsp break; 8058b9da0f5Sstsp if (j == nitems(map)) /* Unknown rate, skip. */ 8068b9da0f5Sstsp continue; 8078b9da0f5Sstsp rates |= 1 << j; 8088b9da0f5Sstsp if (j > maxrate) 8098b9da0f5Sstsp maxrate = j; 8108b9da0f5Sstsp if (rs->rs_rates[i] & IEEE80211_RATE_BASIC) { 8118b9da0f5Sstsp basicrates |= 1 << j; 8128b9da0f5Sstsp if (j > maxbasicrate) 8138b9da0f5Sstsp maxbasicrate = j; 8148b9da0f5Sstsp } 8158b9da0f5Sstsp } 8168b9da0f5Sstsp if (ic->ic_curmode == IEEE80211_MODE_11B) 8178b9da0f5Sstsp mode = R92C_RAID_11B; 8188b9da0f5Sstsp else 8198b9da0f5Sstsp mode = R92C_RAID_11BG; 8208b9da0f5Sstsp DPRINTF(("mode=0x%x rates=0x%08x, basicrates=0x%08x\n", 8218b9da0f5Sstsp mode, rates, basicrates)); 8228b9da0f5Sstsp 82323340becSstsp if (sc->chip & RTWN_CHIP_PCI) { 8245fda900fSstsp /* Configure Automatic Rate Fallback Register. */ 8255fda900fSstsp if (ic->ic_curmode == IEEE80211_MODE_11B) { 8265fda900fSstsp if (rates & 0x0c) 8271f929dc7Sstsp rtwn_write_4(sc, R92C_ARFR(0), rates & 0x05); 8285fda900fSstsp else 8291f929dc7Sstsp rtwn_write_4(sc, R92C_ARFR(0), rates & 0x07); 8305fda900fSstsp } else 8311f929dc7Sstsp rtwn_write_4(sc, R92C_ARFR(0), rates & 0x07f5); 83223340becSstsp } 8335fda900fSstsp 834067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) { 8355fda900fSstsp error = rtwn_r88e_ra_init(sc, mode, rates, maxrate, 8365fda900fSstsp basicrates, maxbasicrate); 837a087613eSstsp /* We use AMRR with this chip. Start with the lowest rate. */ 838a087613eSstsp ni->ni_txrate = 0; 839a087613eSstsp } else { 8401f929dc7Sstsp if (sc->chip & RTWN_CHIP_PCI) { 8411f929dc7Sstsp ni->ni_txrate = 0; /* AMRR will raise. */ 8421f929dc7Sstsp /* Set initial MRR rates. */ 8431f929dc7Sstsp rtwn_write_1(sc, 8441f929dc7Sstsp R92C_INIDATA_RATE_SEL(R92C_MACID_BC), maxbasicrate); 8451f929dc7Sstsp rtwn_write_1(sc, 8461f929dc7Sstsp R92C_INIDATA_RATE_SEL(R92C_MACID_BSS), 0); 8471f929dc7Sstsp } else { 8485fda900fSstsp error = rtwn_r92c_ra_init(sc, mode, rates, maxrate, 8495fda900fSstsp basicrates, maxbasicrate); 8501f929dc7Sstsp /* No AMRR support. Indicate highest supported rate. */ 8515fda900fSstsp ni->ni_txrate = rs->rs_nrates - 1; 852a087613eSstsp } 8531f929dc7Sstsp } 8545fda900fSstsp return (error); 8555fda900fSstsp } 8565fda900fSstsp 8571f929dc7Sstsp /* 8581f929dc7Sstsp * Initialize rate adaptation in firmware. 8591f929dc7Sstsp */ 860da5607f6Sjsg int 861da5607f6Sjsg rtwn_r92c_ra_init(struct rtwn_softc *sc, u_int8_t mode, u_int32_t rates, 8625fda900fSstsp int maxrate, uint32_t basicrates, int maxbasicrate) 8635fda900fSstsp { 8645fda900fSstsp struct r92c_fw_cmd_macid_cfg cmd; 8655fda900fSstsp int error; 8665fda900fSstsp 8678b9da0f5Sstsp /* Set rates mask for group addressed frames. */ 8688b9da0f5Sstsp cmd.macid = R92C_MACID_BC | R92C_MACID_VALID; 8698b9da0f5Sstsp cmd.mask = htole32(mode << 28 | basicrates); 8708b9da0f5Sstsp error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); 8718b9da0f5Sstsp if (error != 0) { 8728b9da0f5Sstsp printf("%s: could not add broadcast station\n", 8738b9da0f5Sstsp sc->sc_pdev->dv_xname); 8748b9da0f5Sstsp return (error); 8758b9da0f5Sstsp } 8768b9da0f5Sstsp /* Set initial MRR rate. */ 8778b9da0f5Sstsp DPRINTF(("maxbasicrate=%d\n", maxbasicrate)); 8788b9da0f5Sstsp rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(R92C_MACID_BC), 8798b9da0f5Sstsp maxbasicrate); 8808b9da0f5Sstsp 8818b9da0f5Sstsp /* Set rates mask for unicast frames. */ 8828b9da0f5Sstsp cmd.macid = R92C_MACID_BSS | R92C_MACID_VALID; 8838b9da0f5Sstsp cmd.mask = htole32(mode << 28 | rates); 8848b9da0f5Sstsp error = rtwn_fw_cmd(sc, R92C_CMD_MACID_CONFIG, &cmd, sizeof(cmd)); 8858b9da0f5Sstsp if (error != 0) { 8868b9da0f5Sstsp printf("%s: could not add BSS station\n", 8878b9da0f5Sstsp sc->sc_pdev->dv_xname); 8888b9da0f5Sstsp return (error); 8898b9da0f5Sstsp } 8908b9da0f5Sstsp /* Set initial MRR rate. */ 8918b9da0f5Sstsp DPRINTF(("maxrate=%d\n", maxrate)); 8928b9da0f5Sstsp rtwn_write_1(sc, R92C_INIDATA_RATE_SEL(R92C_MACID_BSS), 8938b9da0f5Sstsp maxrate); 8948b9da0f5Sstsp 8955fda900fSstsp return (0); 8965fda900fSstsp } 8978b9da0f5Sstsp 8985fda900fSstsp int 8995fda900fSstsp rtwn_r88e_ra_init(struct rtwn_softc *sc, u_int8_t mode, u_int32_t rates, 9005fda900fSstsp int maxrate, uint32_t basicrates, int maxbasicrate) 9015fda900fSstsp { 9025fda900fSstsp u_int32_t reg; 9035fda900fSstsp 9045fda900fSstsp rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, maxbasicrate); 9055fda900fSstsp 9065fda900fSstsp reg = rtwn_read_4(sc, R92C_RRSR); 9075fda900fSstsp reg = RW(reg, R92C_RRSR_RATE_BITMAP, rates); 9085fda900fSstsp rtwn_write_4(sc, R92C_RRSR, reg); 9095fda900fSstsp 9105fda900fSstsp /* 9115fda900fSstsp * Workaround for performance problems with firmware rate adaptation: 9125fda900fSstsp * If the AP only supports 11b rates, disable mixed B/G mode. 9135fda900fSstsp */ 9145fda900fSstsp if (mode != R92C_RAID_11B && maxrate <= 3 /* 11M */) 9155fda900fSstsp sc->sc_flags |= RTWN_FLAG_FORCE_RAID_11B; 9165fda900fSstsp 9178b9da0f5Sstsp return (0); 9188b9da0f5Sstsp } 9198b9da0f5Sstsp 9208b9da0f5Sstsp void 9218b9da0f5Sstsp rtwn_tsf_sync_enable(struct rtwn_softc *sc) 9228b9da0f5Sstsp { 9238b9da0f5Sstsp struct ieee80211_node *ni = sc->sc_ic.ic_bss; 9248b9da0f5Sstsp uint64_t tsf; 9258b9da0f5Sstsp 9268b9da0f5Sstsp /* Enable TSF synchronization. */ 9278b9da0f5Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, 9288b9da0f5Sstsp rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_DIS_TSF_UDT0); 9298b9da0f5Sstsp 9308b9da0f5Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, 9318b9da0f5Sstsp rtwn_read_1(sc, R92C_BCN_CTRL) & ~R92C_BCN_CTRL_EN_BCN); 9328b9da0f5Sstsp 9338b9da0f5Sstsp /* Set initial TSF. */ 93456fbb35bSkevlo memcpy(&tsf, ni->ni_tstamp, sizeof(tsf)); 9358b9da0f5Sstsp tsf = letoh64(tsf); 9368b9da0f5Sstsp tsf = tsf - (tsf % (ni->ni_intval * IEEE80211_DUR_TU)); 9378b9da0f5Sstsp tsf -= IEEE80211_DUR_TU; 9388b9da0f5Sstsp rtwn_write_4(sc, R92C_TSFTR + 0, tsf); 9398b9da0f5Sstsp rtwn_write_4(sc, R92C_TSFTR + 4, tsf >> 32); 9408b9da0f5Sstsp 9418b9da0f5Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, 9428b9da0f5Sstsp rtwn_read_1(sc, R92C_BCN_CTRL) | R92C_BCN_CTRL_EN_BCN); 9438b9da0f5Sstsp } 9448b9da0f5Sstsp 9458b9da0f5Sstsp void 9468b9da0f5Sstsp rtwn_set_led(struct rtwn_softc *sc, int led, int on) 9478b9da0f5Sstsp { 9488b9da0f5Sstsp uint8_t reg; 9498b9da0f5Sstsp 95023340becSstsp if (led != RTWN_LED_LINK) 95123340becSstsp return; /* not supported */ 95223340becSstsp 95323340becSstsp if (sc->chip & RTWN_CHIP_PCI) { 9548b9da0f5Sstsp reg = rtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; 9558b9da0f5Sstsp if (!on) 9568b9da0f5Sstsp reg |= R92C_LEDCFG2_DIS; 9578b9da0f5Sstsp else 9588b9da0f5Sstsp reg |= R92C_LEDCFG2_EN; 9598b9da0f5Sstsp rtwn_write_1(sc, R92C_LEDCFG2, reg); 96023340becSstsp } else if (sc->chip & RTWN_CHIP_USB) { 96190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 96290540544Sjmatthew rtwn_write_1(sc, 0x64, rtwn_read_1(sc, 0x64) & 0xfe); 96390540544Sjmatthew reg = rtwn_read_1(sc, R92C_LEDCFG1) & R92E_LEDSON; 96490540544Sjmatthew rtwn_write_1(sc, R92C_LEDCFG1, reg | 96590540544Sjmatthew (R92C_LEDCFG0_DIS << 1)); 96690540544Sjmatthew if (on) { 96790540544Sjmatthew reg = rtwn_read_1(sc, R92C_LEDCFG1) & 96890540544Sjmatthew R92E_LEDSON; 96990540544Sjmatthew rtwn_write_1(sc, R92C_LEDCFG1, reg); 97090540544Sjmatthew } 97190540544Sjmatthew } else if (sc->chip & RTWN_CHIP_88E) { 97223340becSstsp reg = rtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; 97323340becSstsp rtwn_write_1(sc, R92C_LEDCFG2, reg | R92C_LEDCFG2_EN); 97423340becSstsp if (!on) { 97523340becSstsp reg = rtwn_read_1(sc, R92C_LEDCFG2) & 0x90; 97623340becSstsp rtwn_write_1(sc, R92C_LEDCFG2, 97723340becSstsp reg | R92C_LEDCFG0_DIS); 97823340becSstsp rtwn_write_1(sc, R92C_MAC_PINMUX_CFG, 97923340becSstsp rtwn_read_1(sc, R92C_MAC_PINMUX_CFG) & 98023340becSstsp 0xfe); 9818b9da0f5Sstsp } 98223340becSstsp } else { 98323340becSstsp reg = rtwn_read_1(sc, R92C_LEDCFG0) & 0x70; 98423340becSstsp if (!on) 98523340becSstsp reg |= R92C_LEDCFG0_DIS; 98623340becSstsp rtwn_write_1(sc, R92C_LEDCFG0, reg); 98723340becSstsp } 98823340becSstsp } 98923340becSstsp sc->ledlink = on; /* Save LED state. */ 9908b9da0f5Sstsp } 9918b9da0f5Sstsp 9928b9da0f5Sstsp void 99356fbb35bSkevlo rtwn_set_nettype(struct rtwn_softc *sc, enum ieee80211_opmode opmode) 99456fbb35bSkevlo { 99556fbb35bSkevlo uint8_t msr; 99656fbb35bSkevlo 99756fbb35bSkevlo msr = rtwn_read_1(sc, R92C_MSR) & ~R92C_MSR_NETTYPE_MASK; 99856fbb35bSkevlo 99956fbb35bSkevlo switch (opmode) { 100056fbb35bSkevlo case IEEE80211_M_MONITOR: 100156fbb35bSkevlo msr |= R92C_MSR_NETTYPE_NOLINK; 100256fbb35bSkevlo break; 100356fbb35bSkevlo case IEEE80211_M_STA: 100456fbb35bSkevlo msr |= R92C_MSR_NETTYPE_INFRA; 100556fbb35bSkevlo break; 100656fbb35bSkevlo default: 100756fbb35bSkevlo break; 100856fbb35bSkevlo } 100956fbb35bSkevlo 101056fbb35bSkevlo rtwn_write_1(sc, R92C_MSR, msr); 101156fbb35bSkevlo } 101256fbb35bSkevlo 101356fbb35bSkevlo void 1014e9f037d1Sstsp rtwn_calib(struct rtwn_softc *sc) 10158b9da0f5Sstsp { 10168b9da0f5Sstsp 10178b9da0f5Sstsp if (sc->avg_pwdb != -1) { 101890540544Sjmatthew DPRINTFN(3, ("sending RSSI command avg=%d\n", sc->avg_pwdb)); 101990540544Sjmatthew 10208b9da0f5Sstsp /* Indicate Rx signal strength to FW for rate adaptation. */ 102190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 102290540544Sjmatthew struct r92e_fw_cmd_rssi cmd; 102390540544Sjmatthew 10248b9da0f5Sstsp memset(&cmd, 0, sizeof(cmd)); 10258b9da0f5Sstsp cmd.macid = 0; /* BSS. */ 10268b9da0f5Sstsp cmd.pwdb = sc->avg_pwdb; 102790540544Sjmatthew rtwn_fw_cmd(sc, R92E_CMD_RSSI_REPORT, &cmd, 102890540544Sjmatthew sizeof(cmd)); 102990540544Sjmatthew } else { 103090540544Sjmatthew struct r92c_fw_cmd_rssi cmd; 103190540544Sjmatthew 103290540544Sjmatthew memset(&cmd, 0, sizeof(cmd)); 103390540544Sjmatthew cmd.macid = 0; /* BSS. */ 103490540544Sjmatthew cmd.pwdb = sc->avg_pwdb; 103590540544Sjmatthew rtwn_fw_cmd(sc, R92C_CMD_RSSI_SETTING, &cmd, 103690540544Sjmatthew sizeof(cmd)); 103790540544Sjmatthew } 10388b9da0f5Sstsp } 10398b9da0f5Sstsp 10408b9da0f5Sstsp /* Do temperature compensation. */ 10418b9da0f5Sstsp rtwn_temp_calib(sc); 10428b9da0f5Sstsp 1043e9f037d1Sstsp sc->sc_ops.next_calib(sc->sc_ops.cookie); 10448b9da0f5Sstsp } 10458b9da0f5Sstsp 10468b9da0f5Sstsp void 1047e9f037d1Sstsp rtwn_next_scan(struct rtwn_softc *sc) 10488b9da0f5Sstsp { 1049e9f037d1Sstsp struct ieee80211com *ic = &sc->sc_ic; 1050e9f037d1Sstsp int s; 10518b9da0f5Sstsp 1052e9f037d1Sstsp s = splnet(); 1053e9f037d1Sstsp if (ic->ic_state == IEEE80211_S_SCAN) 1054e9f037d1Sstsp ieee80211_next_scan(&ic->ic_if); 1055e9f037d1Sstsp splx(s); 10568b9da0f5Sstsp } 10578b9da0f5Sstsp 10588b9da0f5Sstsp int 10598b9da0f5Sstsp rtwn_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 10608b9da0f5Sstsp { 10618b9da0f5Sstsp struct rtwn_softc *sc = ic->ic_softc; 10628b9da0f5Sstsp struct ieee80211_node *ni; 10638b9da0f5Sstsp enum ieee80211_state ostate; 10648b9da0f5Sstsp uint32_t reg; 106523340becSstsp int s, error; 10668b9da0f5Sstsp 10678b9da0f5Sstsp s = splnet(); 10688b9da0f5Sstsp ostate = ic->ic_state; 10698b9da0f5Sstsp 10708b9da0f5Sstsp if (nstate != ostate) 10718b9da0f5Sstsp DPRINTF(("newstate %s -> %s\n", 10728b9da0f5Sstsp ieee80211_state_name[ostate], 10738b9da0f5Sstsp ieee80211_state_name[nstate])); 10748b9da0f5Sstsp 10758b9da0f5Sstsp if (ostate == IEEE80211_S_RUN) { 10768b9da0f5Sstsp /* Stop calibration. */ 1077e9f037d1Sstsp sc->sc_ops.cancel_calib(sc->sc_ops.cookie); 10788b9da0f5Sstsp 10798b9da0f5Sstsp /* Turn link LED off. */ 10808b9da0f5Sstsp rtwn_set_led(sc, RTWN_LED_LINK, 0); 10818b9da0f5Sstsp 10828b9da0f5Sstsp /* Set media status to 'No Link'. */ 108356fbb35bSkevlo rtwn_set_nettype(sc, IEEE80211_M_MONITOR); 10848b9da0f5Sstsp 10858b9da0f5Sstsp /* Stop Rx of data frames. */ 10868b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP2, 0); 10878b9da0f5Sstsp 10888b9da0f5Sstsp /* Rest TSF. */ 10898b9da0f5Sstsp rtwn_write_1(sc, R92C_DUAL_TSF_RST, 0x03); 10908b9da0f5Sstsp 10918b9da0f5Sstsp /* Disable TSF synchronization. */ 10928b9da0f5Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, 10938b9da0f5Sstsp rtwn_read_1(sc, R92C_BCN_CTRL) | 10948b9da0f5Sstsp R92C_BCN_CTRL_DIS_TSF_UDT0); 10958b9da0f5Sstsp 10968b9da0f5Sstsp /* Reset EDCA parameters. */ 1097af1fd538Sstsp rtwn_edca_init(sc); 10985fda900fSstsp 10996056a428Sstsp rtwn_updateslot(ic); 11006056a428Sstsp rtwn_update_short_preamble(ic); 11016056a428Sstsp 11025fda900fSstsp /* Disable 11b-only AP workaround (see rtwn_r88e_ra_init). */ 11035fda900fSstsp sc->sc_flags &= ~RTWN_FLAG_FORCE_RAID_11B; 11048b9da0f5Sstsp } 11058b9da0f5Sstsp switch (nstate) { 11068b9da0f5Sstsp case IEEE80211_S_INIT: 11078b9da0f5Sstsp /* Turn link LED off. */ 11088b9da0f5Sstsp rtwn_set_led(sc, RTWN_LED_LINK, 0); 11098b9da0f5Sstsp break; 11108b9da0f5Sstsp case IEEE80211_S_SCAN: 11118b9da0f5Sstsp if (ostate != IEEE80211_S_SCAN) { 11128b9da0f5Sstsp /* Allow Rx from any BSSID. */ 11138b9da0f5Sstsp rtwn_write_4(sc, R92C_RCR, 11148b9da0f5Sstsp rtwn_read_4(sc, R92C_RCR) & 11158b9da0f5Sstsp ~(R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN)); 11168b9da0f5Sstsp 11178b9da0f5Sstsp /* Set gain for scanning. */ 11188b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); 11198b9da0f5Sstsp reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20); 11208b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); 11218b9da0f5Sstsp 112223340becSstsp if (!(sc->chip & RTWN_CHIP_88E)) { 11238b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); 11248b9da0f5Sstsp reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x20); 11258b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); 11268b9da0f5Sstsp } 112723340becSstsp } 11288b9da0f5Sstsp 11298b9da0f5Sstsp /* Make link LED blink during scan. */ 11308b9da0f5Sstsp rtwn_set_led(sc, RTWN_LED_LINK, !sc->ledlink); 11318b9da0f5Sstsp 11328b9da0f5Sstsp /* Pause AC Tx queues. */ 11338b9da0f5Sstsp rtwn_write_1(sc, R92C_TXPAUSE, 113456fbb35bSkevlo rtwn_read_1(sc, R92C_TXPAUSE) | R92C_TXPAUSE_AC_VO | 113556fbb35bSkevlo R92C_TXPAUSE_AC_VI | R92C_TXPAUSE_AC_BE | 113656fbb35bSkevlo R92C_TXPAUSE_AC_BK); 113723340becSstsp 11388b9da0f5Sstsp rtwn_set_chan(sc, ic->ic_bss->ni_chan, NULL); 1139e9f037d1Sstsp sc->sc_ops.next_scan(sc->sc_ops.cookie); 11408b9da0f5Sstsp break; 11418b9da0f5Sstsp 11428b9da0f5Sstsp case IEEE80211_S_AUTH: 11438b9da0f5Sstsp /* Set initial gain under link. */ 11448b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); 11458b9da0f5Sstsp reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32); 11468b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg); 11478b9da0f5Sstsp 114823340becSstsp if (!(sc->chip & RTWN_CHIP_88E)) { 11498b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); 11508b9da0f5Sstsp reg = RW(reg, R92C_OFDM0_AGCCORE1_GAIN, 0x32); 11518b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg); 115223340becSstsp } 11538b9da0f5Sstsp 11548b9da0f5Sstsp rtwn_set_chan(sc, ic->ic_bss->ni_chan, NULL); 11558b9da0f5Sstsp break; 11568b9da0f5Sstsp case IEEE80211_S_ASSOC: 11578b9da0f5Sstsp break; 11588b9da0f5Sstsp case IEEE80211_S_RUN: 11598b9da0f5Sstsp if (ic->ic_opmode == IEEE80211_M_MONITOR) { 11608b9da0f5Sstsp rtwn_set_chan(sc, ic->ic_ibss_chan, NULL); 11618b9da0f5Sstsp 11628b9da0f5Sstsp /* Enable Rx of data frames. */ 11638b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); 11648b9da0f5Sstsp 1165afbf2756Skevlo /* Enable Rx of control frames. */ 1166afbf2756Skevlo rtwn_write_2(sc, R92C_RXFLTMAP1, 0xffff); 1167afbf2756Skevlo 1168afbf2756Skevlo rtwn_write_4(sc, R92C_RCR, 1169afbf2756Skevlo rtwn_read_4(sc, R92C_RCR) | 1170afbf2756Skevlo R92C_RCR_AAP | R92C_RCR_ADF | R92C_RCR_ACF | 1171afbf2756Skevlo R92C_RCR_AMF); 1172afbf2756Skevlo 11738b9da0f5Sstsp /* Turn link LED on. */ 11748b9da0f5Sstsp rtwn_set_led(sc, RTWN_LED_LINK, 1); 11758b9da0f5Sstsp break; 11768b9da0f5Sstsp } 11778b9da0f5Sstsp ni = ic->ic_bss; 11788b9da0f5Sstsp 11798b9da0f5Sstsp /* Set media status to 'Associated'. */ 118056fbb35bSkevlo rtwn_set_nettype(sc, IEEE80211_M_STA); 11818b9da0f5Sstsp 11828b9da0f5Sstsp /* Set BSSID. */ 11838b9da0f5Sstsp rtwn_write_4(sc, R92C_BSSID + 0, LE_READ_4(&ni->ni_bssid[0])); 11848b9da0f5Sstsp rtwn_write_4(sc, R92C_BSSID + 4, LE_READ_2(&ni->ni_bssid[4])); 11858b9da0f5Sstsp 11868b9da0f5Sstsp if (ic->ic_curmode == IEEE80211_MODE_11B) 11878b9da0f5Sstsp rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 0); 11888b9da0f5Sstsp else /* 802.11b/g */ 11898b9da0f5Sstsp rtwn_write_1(sc, R92C_INIRTS_RATE_SEL, 3); 11908b9da0f5Sstsp 11916056a428Sstsp rtwn_updateslot(ic); 11926056a428Sstsp rtwn_update_short_preamble(ic); 11936056a428Sstsp 11948b9da0f5Sstsp /* Enable Rx of data frames. */ 11958b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); 11968b9da0f5Sstsp 11978b9da0f5Sstsp /* Flush all AC queues. */ 11984192ff9bSstsp rtwn_write_1(sc, R92C_TXPAUSE, 0x00); 11998b9da0f5Sstsp 12008b9da0f5Sstsp /* Set beacon interval. */ 12018b9da0f5Sstsp rtwn_write_2(sc, R92C_BCN_INTERVAL, ni->ni_intval); 12028b9da0f5Sstsp 12038b9da0f5Sstsp /* Allow Rx from our BSSID only. */ 12048b9da0f5Sstsp rtwn_write_4(sc, R92C_RCR, 12058b9da0f5Sstsp rtwn_read_4(sc, R92C_RCR) | 12068b9da0f5Sstsp R92C_RCR_CBSSID_DATA | R92C_RCR_CBSSID_BCN); 12078b9da0f5Sstsp 12088b9da0f5Sstsp /* Enable TSF synchronization. */ 12098b9da0f5Sstsp rtwn_tsf_sync_enable(sc); 12108b9da0f5Sstsp 12114b1a56afSjsg /* Initialize rate adaptation. */ 12128b9da0f5Sstsp rtwn_ra_init(sc); 1213af1fd538Sstsp 12148b9da0f5Sstsp /* Turn link LED on. */ 12158b9da0f5Sstsp rtwn_set_led(sc, RTWN_LED_LINK, 1); 12168b9da0f5Sstsp 12178b9da0f5Sstsp sc->avg_pwdb = -1; /* Reset average RSSI. */ 12188b9da0f5Sstsp /* Reset temperature calibration state machine. */ 12198b9da0f5Sstsp sc->thcal_state = 0; 12208b9da0f5Sstsp sc->thcal_lctemp = 0; 12218b9da0f5Sstsp /* Start periodic calibration. */ 1222e9f037d1Sstsp sc->sc_ops.next_calib(sc->sc_ops.cookie); 12238b9da0f5Sstsp break; 12248b9da0f5Sstsp } 122523340becSstsp 122623340becSstsp error = sc->sc_newstate(ic, nstate, arg); 12278b9da0f5Sstsp splx(s); 12288b9da0f5Sstsp 122923340becSstsp return (error); 12308b9da0f5Sstsp } 12318b9da0f5Sstsp 12328b9da0f5Sstsp void 12336056a428Sstsp rtwn_update_short_preamble(struct ieee80211com *ic) 12346056a428Sstsp { 12356056a428Sstsp struct rtwn_softc *sc = ic->ic_softc; 123656fbb35bSkevlo 123790540544Sjmatthew if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_92E)) 123856fbb35bSkevlo rtwn_r88e_update_short_preamble(sc); 123956fbb35bSkevlo else 124056fbb35bSkevlo rtwn_r92c_update_short_preamble(sc); 124156fbb35bSkevlo } 124256fbb35bSkevlo 124356fbb35bSkevlo void 124456fbb35bSkevlo rtwn_r92c_update_short_preamble(struct rtwn_softc *sc) 124556fbb35bSkevlo { 12466056a428Sstsp uint32_t reg; 12476056a428Sstsp 12486056a428Sstsp reg = rtwn_read_4(sc, R92C_RRSR); 124956fbb35bSkevlo if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) 12506056a428Sstsp reg |= R92C_RRSR_SHORT; 12516056a428Sstsp else 12526056a428Sstsp reg &= ~R92C_RRSR_SHORT; 12536056a428Sstsp rtwn_write_4(sc, R92C_RRSR, reg); 12546056a428Sstsp } 12556056a428Sstsp 12566056a428Sstsp void 125756fbb35bSkevlo rtwn_r88e_update_short_preamble(struct rtwn_softc *sc) 125856fbb35bSkevlo { 125956fbb35bSkevlo uint32_t reg; 126056fbb35bSkevlo 126156fbb35bSkevlo reg = rtwn_read_4(sc, R92C_WMAC_TRXPTCL_CTL); 126256fbb35bSkevlo if (sc->sc_ic.ic_flags & IEEE80211_F_SHPREAMBLE) 126356fbb35bSkevlo reg |= R92C_WMAC_TRXPTCL_CTL_SHORT; 126456fbb35bSkevlo else 126556fbb35bSkevlo reg &= ~R92C_WMAC_TRXPTCL_CTL_SHORT; 126656fbb35bSkevlo rtwn_write_4(sc, R92C_WMAC_TRXPTCL_CTL, reg); 126756fbb35bSkevlo } 126856fbb35bSkevlo 126956fbb35bSkevlo void 12706056a428Sstsp rtwn_updateslot(struct ieee80211com *ic) 12716056a428Sstsp { 12726056a428Sstsp struct rtwn_softc *sc = ic->ic_softc; 12736056a428Sstsp int s; 12746056a428Sstsp 12756056a428Sstsp s = splnet(); 12766056a428Sstsp if (ic->ic_flags & IEEE80211_F_SHSLOT) 1277e198220fSkevlo rtwn_write_1(sc, R92C_SLOT, IEEE80211_DUR_DS_SHSLOT); 12786056a428Sstsp else 12796056a428Sstsp rtwn_write_1(sc, R92C_SLOT, IEEE80211_DUR_DS_SLOT); 12806056a428Sstsp splx(s); 12816056a428Sstsp } 12826056a428Sstsp 12836056a428Sstsp void 12848b9da0f5Sstsp rtwn_updateedca(struct ieee80211com *ic) 12858b9da0f5Sstsp { 12868b9da0f5Sstsp struct rtwn_softc *sc = ic->ic_softc; 12878b9da0f5Sstsp const uint16_t aci2reg[EDCA_NUM_AC] = { 12888b9da0f5Sstsp R92C_EDCA_BE_PARAM, 12898b9da0f5Sstsp R92C_EDCA_BK_PARAM, 12908b9da0f5Sstsp R92C_EDCA_VI_PARAM, 12918b9da0f5Sstsp R92C_EDCA_VO_PARAM 12928b9da0f5Sstsp }; 12938b9da0f5Sstsp struct ieee80211_edca_ac_params *ac; 12948b9da0f5Sstsp int s, aci, aifs, slottime; 1295c8390491Sstsp uint8_t acm = 0; 12968b9da0f5Sstsp 1297af1fd538Sstsp if (ic->ic_flags & IEEE80211_F_SHSLOT) 1298e198220fSkevlo slottime = IEEE80211_DUR_DS_SHSLOT; 1299af1fd538Sstsp else 1300af1fd538Sstsp slottime = IEEE80211_DUR_DS_SLOT; 13018b9da0f5Sstsp s = splnet(); 13028b9da0f5Sstsp for (aci = 0; aci < EDCA_NUM_AC; aci++) { 13038b9da0f5Sstsp ac = &ic->ic_edca_ac[aci]; 13048b9da0f5Sstsp /* AIFS[AC] = AIFSN[AC] * aSlotTime + aSIFSTime. */ 1305af1fd538Sstsp aifs = ac->ac_aifsn * slottime + IEEE80211_DUR_DS_SIFS; 13068b9da0f5Sstsp rtwn_write_4(sc, aci2reg[aci], 13078b9da0f5Sstsp SM(R92C_EDCA_PARAM_TXOP, ac->ac_txoplimit) | 13088b9da0f5Sstsp SM(R92C_EDCA_PARAM_ECWMIN, ac->ac_ecwmin) | 13098b9da0f5Sstsp SM(R92C_EDCA_PARAM_ECWMAX, ac->ac_ecwmax) | 13108b9da0f5Sstsp SM(R92C_EDCA_PARAM_AIFS, aifs)); 1311c8390491Sstsp 1312c8390491Sstsp /* Is admission control mandatory for this queue? */ 1313c8390491Sstsp if (ac->ac_acm) { 1314c8390491Sstsp switch (aci) { 1315c8390491Sstsp case EDCA_AC_BE: 1316c8390491Sstsp acm |= R92C_ACMHW_BEQEN; 1317c8390491Sstsp break; 1318c8390491Sstsp case EDCA_AC_VI: 1319c8390491Sstsp acm |= R92C_ACMHW_VIQEN; 1320c8390491Sstsp break; 1321c8390491Sstsp case EDCA_AC_VO: 1322c8390491Sstsp acm |= R92C_ACMHW_VOQEN; 1323c8390491Sstsp break; 1324c8390491Sstsp default: 1325c8390491Sstsp break; 1326c8390491Sstsp } 1327c8390491Sstsp } 13288b9da0f5Sstsp } 13298b9da0f5Sstsp splx(s); 1330c8390491Sstsp 1331c8390491Sstsp /* Enable hardware admission control. */ 1332c8390491Sstsp rtwn_write_1(sc, R92C_ACMHWCTRL, R92C_ACMHW_HWEN | acm); 13338b9da0f5Sstsp } 13348b9da0f5Sstsp 13358b9da0f5Sstsp int 13368b9da0f5Sstsp rtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, 13378b9da0f5Sstsp struct ieee80211_key *k) 13388b9da0f5Sstsp { 13398b9da0f5Sstsp struct rtwn_softc *sc = ic->ic_softc; 13408b9da0f5Sstsp static const uint8_t etherzeroaddr[6] = { 0 }; 13418b9da0f5Sstsp const uint8_t *macaddr; 13428b9da0f5Sstsp uint8_t keybuf[16], algo; 13438b9da0f5Sstsp int i, entry; 13448b9da0f5Sstsp 13458b9da0f5Sstsp /* Defer setting of WEP keys until interface is brought up. */ 13468b9da0f5Sstsp if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != 13478b9da0f5Sstsp (IFF_UP | IFF_RUNNING)) 13488b9da0f5Sstsp return (0); 13498b9da0f5Sstsp 13508b9da0f5Sstsp /* Map net80211 cipher to HW crypto algorithm. */ 13518b9da0f5Sstsp switch (k->k_cipher) { 13528b9da0f5Sstsp case IEEE80211_CIPHER_WEP40: 13538b9da0f5Sstsp algo = R92C_CAM_ALGO_WEP40; 13548b9da0f5Sstsp break; 13558b9da0f5Sstsp case IEEE80211_CIPHER_WEP104: 13568b9da0f5Sstsp algo = R92C_CAM_ALGO_WEP104; 13578b9da0f5Sstsp break; 13588b9da0f5Sstsp case IEEE80211_CIPHER_TKIP: 13598b9da0f5Sstsp algo = R92C_CAM_ALGO_TKIP; 13608b9da0f5Sstsp break; 13618b9da0f5Sstsp case IEEE80211_CIPHER_CCMP: 13628b9da0f5Sstsp algo = R92C_CAM_ALGO_AES; 13638b9da0f5Sstsp break; 13648b9da0f5Sstsp default: 13658b9da0f5Sstsp /* Fallback to software crypto for other ciphers. */ 13668b9da0f5Sstsp return (ieee80211_set_key(ic, ni, k)); 13678b9da0f5Sstsp } 13688b9da0f5Sstsp if (k->k_flags & IEEE80211_KEY_GROUP) { 13698b9da0f5Sstsp macaddr = etherzeroaddr; 13708b9da0f5Sstsp entry = k->k_id; 13718b9da0f5Sstsp } else { 13728b9da0f5Sstsp macaddr = ic->ic_bss->ni_macaddr; 13738b9da0f5Sstsp entry = 4; 13748b9da0f5Sstsp } 13758b9da0f5Sstsp /* Write key. */ 13768b9da0f5Sstsp memset(keybuf, 0, sizeof(keybuf)); 13778b9da0f5Sstsp memcpy(keybuf, k->k_key, MIN(k->k_len, sizeof(keybuf))); 13788b9da0f5Sstsp for (i = 0; i < 4; i++) { 13798b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_KEY(entry, i), 13808b9da0f5Sstsp LE_READ_4(&keybuf[i * 4])); 13818b9da0f5Sstsp } 13828b9da0f5Sstsp /* Write CTL0 last since that will validate the CAM entry. */ 13838b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_CTL1(entry), 13848b9da0f5Sstsp LE_READ_4(&macaddr[2])); 13858b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_CTL0(entry), 13868b9da0f5Sstsp SM(R92C_CAM_ALGO, algo) | 13878b9da0f5Sstsp SM(R92C_CAM_KEYID, k->k_id) | 13888b9da0f5Sstsp SM(R92C_CAM_MACLO, LE_READ_2(&macaddr[0])) | 13898b9da0f5Sstsp R92C_CAM_VALID); 13908b9da0f5Sstsp 13918b9da0f5Sstsp return (0); 13928b9da0f5Sstsp } 13938b9da0f5Sstsp 13948b9da0f5Sstsp void 13958b9da0f5Sstsp rtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, 13968b9da0f5Sstsp struct ieee80211_key *k) 13978b9da0f5Sstsp { 13988b9da0f5Sstsp struct rtwn_softc *sc = ic->ic_softc; 13998b9da0f5Sstsp int i, entry; 14008b9da0f5Sstsp 14018b9da0f5Sstsp if (!(ic->ic_if.if_flags & IFF_RUNNING) || 14028b9da0f5Sstsp ic->ic_state != IEEE80211_S_RUN) 14038b9da0f5Sstsp return; /* Nothing to do. */ 14048b9da0f5Sstsp 14058b9da0f5Sstsp if (k->k_flags & IEEE80211_KEY_GROUP) 14068b9da0f5Sstsp entry = k->k_id; 14078b9da0f5Sstsp else 14088b9da0f5Sstsp entry = 4; 14098b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_CTL0(entry), 0); 14108b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_CTL1(entry), 0); 14118b9da0f5Sstsp /* Clear key. */ 14128b9da0f5Sstsp for (i = 0; i < 4; i++) 14138b9da0f5Sstsp rtwn_cam_write(sc, R92C_CAM_KEY(entry, i), 0); 14148b9da0f5Sstsp } 14158b9da0f5Sstsp 14168b9da0f5Sstsp void 14178b9da0f5Sstsp rtwn_update_avgrssi(struct rtwn_softc *sc, int rate, int8_t rssi) 14188b9da0f5Sstsp { 14198b9da0f5Sstsp int pwdb; 14208b9da0f5Sstsp 14218b9da0f5Sstsp /* Convert antenna signal to percentage. */ 14228b9da0f5Sstsp if (rssi <= -100 || rssi >= 20) 14238b9da0f5Sstsp pwdb = 0; 14248b9da0f5Sstsp else if (rssi >= 0) 14258b9da0f5Sstsp pwdb = 100; 14268b9da0f5Sstsp else 14278b9da0f5Sstsp pwdb = 100 + rssi; 1428fdfa064bSkevlo if (sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_88C)) { 14298b9da0f5Sstsp if (rate <= 3) { 14308b9da0f5Sstsp /* CCK gain is smaller than OFDM/MCS gain. */ 14318b9da0f5Sstsp pwdb += 6; 14328b9da0f5Sstsp if (pwdb > 100) 14338b9da0f5Sstsp pwdb = 100; 14348b9da0f5Sstsp if (pwdb <= 14) 14358b9da0f5Sstsp pwdb -= 4; 14368b9da0f5Sstsp else if (pwdb <= 26) 14378b9da0f5Sstsp pwdb -= 8; 14388b9da0f5Sstsp else if (pwdb <= 34) 14398b9da0f5Sstsp pwdb -= 6; 14408b9da0f5Sstsp else if (pwdb <= 42) 14418b9da0f5Sstsp pwdb -= 2; 14428b9da0f5Sstsp } 1443fdfa064bSkevlo } 14448b9da0f5Sstsp if (sc->avg_pwdb == -1) /* Init. */ 14458b9da0f5Sstsp sc->avg_pwdb = pwdb; 14468b9da0f5Sstsp else if (sc->avg_pwdb < pwdb) 14478b9da0f5Sstsp sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20) + 1; 14488b9da0f5Sstsp else 14498b9da0f5Sstsp sc->avg_pwdb = ((sc->avg_pwdb * 19 + pwdb) / 20); 14508b9da0f5Sstsp DPRINTFN(4, ("PWDB=%d EMA=%d\n", pwdb, sc->avg_pwdb)); 14518b9da0f5Sstsp } 14528b9da0f5Sstsp 14538b9da0f5Sstsp int8_t 14548b9da0f5Sstsp rtwn_get_rssi(struct rtwn_softc *sc, int rate, void *physt) 14558b9da0f5Sstsp { 14568b9da0f5Sstsp static const int8_t cckoff[] = { 16, -12, -26, -46 }; 14578b9da0f5Sstsp struct r92c_rx_phystat *phy; 14588b9da0f5Sstsp struct r92c_rx_cck *cck; 14598b9da0f5Sstsp uint8_t rpt; 14608b9da0f5Sstsp int8_t rssi; 14618b9da0f5Sstsp 146290540544Sjmatthew if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_92E)) 14635fda900fSstsp return rtwn_r88e_get_rssi(sc, rate, physt); 1464067851b1Skevlo else if (sc->chip & RTWN_CHIP_88E) 1465067851b1Skevlo return rtwn_r88f_get_rssi(sc, rate, physt); 14665fda900fSstsp 14678b9da0f5Sstsp if (rate <= 3) { 14688b9da0f5Sstsp cck = (struct r92c_rx_cck *)physt; 14698b9da0f5Sstsp if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) { 14708b9da0f5Sstsp rpt = (cck->agc_rpt >> 5) & 0x3; 14718b9da0f5Sstsp rssi = (cck->agc_rpt & 0x1f) << 1; 14728b9da0f5Sstsp } else { 14738b9da0f5Sstsp rpt = (cck->agc_rpt >> 6) & 0x3; 14748b9da0f5Sstsp rssi = cck->agc_rpt & 0x3e; 14758b9da0f5Sstsp } 14768b9da0f5Sstsp rssi = cckoff[rpt] - rssi; 14778b9da0f5Sstsp } else { /* OFDM/HT. */ 14788b9da0f5Sstsp phy = (struct r92c_rx_phystat *)physt; 14798b9da0f5Sstsp rssi = ((letoh32(phy->phydw1) >> 1) & 0x7f) - 110; 14808b9da0f5Sstsp } 14818b9da0f5Sstsp return (rssi); 14828b9da0f5Sstsp } 14838b9da0f5Sstsp 14845fda900fSstsp int8_t 14855fda900fSstsp rtwn_r88e_get_rssi(struct rtwn_softc *sc, int rate, void *physt) 14865fda900fSstsp { 1487fdfa064bSkevlo static const int8_t cckoff[] = { 20, 14, 10, -4, -16, -22, -38, -40 }; 1488fdfa064bSkevlo struct r88e_rx_phystat *phy; 1489fdfa064bSkevlo uint8_t rpt; 14905fda900fSstsp int8_t rssi; 14915fda900fSstsp 1492fdfa064bSkevlo phy = (struct r88e_rx_phystat *)physt; 1493fdfa064bSkevlo 14945fda900fSstsp if (rate <= 3) { 1495fdfa064bSkevlo rpt = (phy->agc_rpt >> 5) & 0x7; 1496fdfa064bSkevlo rssi = (phy->agc_rpt & 0x1f) << 1; 1497fdfa064bSkevlo if (sc->sc_flags & RTWN_FLAG_CCK_HIPWR) { 1498fdfa064bSkevlo if (rpt == 2) 1499fdfa064bSkevlo rssi -= 6; 15005fda900fSstsp } 1501fdfa064bSkevlo rssi = (phy->agc_rpt & 0x1f) > 27 ? -94 : cckoff[rpt] - rssi; 15025fda900fSstsp } else { /* OFDM/HT. */ 1503fdfa064bSkevlo rssi = ((le32toh(phy->sq_rpt) >> 1) & 0x7f) - 110; 15045fda900fSstsp } 15055fda900fSstsp return (rssi); 15065fda900fSstsp } 15075fda900fSstsp 1508067851b1Skevlo int8_t 1509067851b1Skevlo rtwn_r88f_get_rssi(struct rtwn_softc *sc, int rate, void *physt) 1510067851b1Skevlo { 1511067851b1Skevlo struct r88e_rx_phystat *phy; 1512067851b1Skevlo uint8_t lna_idx, vga_idx; 1513067851b1Skevlo int8_t rssi; 1514067851b1Skevlo 1515067851b1Skevlo phy = (struct r88e_rx_phystat *)physt; 1516067851b1Skevlo lna_idx = (phy->agc_rpt & 0xe0) >> 5; 1517067851b1Skevlo vga_idx = (phy->agc_rpt & 0x1f); 1518067851b1Skevlo rssi = -(2 * vga_idx); 1519067851b1Skevlo 1520067851b1Skevlo if (rate <= 3) { 1521067851b1Skevlo switch (lna_idx) { 1522067851b1Skevlo case 7: 1523067851b1Skevlo if (vga_idx > 27) 1524067851b1Skevlo rssi = -100; 1525067851b1Skevlo else 1526067851b1Skevlo rssi += -46; 1527067851b1Skevlo break; 1528067851b1Skevlo case 5: 1529067851b1Skevlo rssi += -32; 1530067851b1Skevlo break; 1531067851b1Skevlo case 3: 1532067851b1Skevlo rssi += -20; 1533067851b1Skevlo break; 1534067851b1Skevlo case 1: 1535067851b1Skevlo rssi += -6; 1536067851b1Skevlo break; 1537067851b1Skevlo default: 1538067851b1Skevlo rssi = 0; 1539067851b1Skevlo break; 1540067851b1Skevlo } 1541067851b1Skevlo } else { /* OFDM/HT. */ 1542067851b1Skevlo rssi = ((le32toh(phy->sq_rpt) >> 1) & 0x7f) - 110; 1543067851b1Skevlo } 1544067851b1Skevlo return (rssi); 1545067851b1Skevlo } 1546067851b1Skevlo 15478b9da0f5Sstsp void 15488b9da0f5Sstsp rtwn_start(struct ifnet *ifp) 15498b9da0f5Sstsp { 15508b9da0f5Sstsp struct rtwn_softc *sc = ifp->if_softc; 15518b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 15528b9da0f5Sstsp struct ieee80211_node *ni; 15538b9da0f5Sstsp struct mbuf *m; 15548b9da0f5Sstsp 15558b9da0f5Sstsp if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive(&ifp->if_snd)) 15568b9da0f5Sstsp return; 15578b9da0f5Sstsp 15588b9da0f5Sstsp for (;;) { 15598b9da0f5Sstsp if (sc->sc_ops.is_oactive(sc->sc_ops.cookie)) { 15608b9da0f5Sstsp ifq_set_oactive(&ifp->if_snd); 15618b9da0f5Sstsp break; 15628b9da0f5Sstsp } 15638b9da0f5Sstsp /* Send pending management frames first. */ 15648b9da0f5Sstsp m = mq_dequeue(&ic->ic_mgtq); 15658b9da0f5Sstsp if (m != NULL) { 15668b9da0f5Sstsp ni = m->m_pkthdr.ph_cookie; 15678b9da0f5Sstsp goto sendit; 15688b9da0f5Sstsp } 15698b9da0f5Sstsp if (ic->ic_state != IEEE80211_S_RUN) 15708b9da0f5Sstsp break; 15718b9da0f5Sstsp 15728b9da0f5Sstsp /* Encapsulate and send data frames. */ 157363bcfa73Spatrick m = ifq_dequeue(&ifp->if_snd); 15748b9da0f5Sstsp if (m == NULL) 15758b9da0f5Sstsp break; 15768b9da0f5Sstsp #if NBPFILTER > 0 15778b9da0f5Sstsp if (ifp->if_bpf != NULL) 15788b9da0f5Sstsp bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); 15798b9da0f5Sstsp #endif 15808b9da0f5Sstsp if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) 15818b9da0f5Sstsp continue; 15828b9da0f5Sstsp sendit: 15838b9da0f5Sstsp #if NBPFILTER > 0 15848b9da0f5Sstsp if (ic->ic_rawbpf != NULL) 15858b9da0f5Sstsp bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); 15868b9da0f5Sstsp #endif 15878b9da0f5Sstsp if (sc->sc_ops.tx(sc->sc_ops.cookie, m, ni) != 0) { 15888b9da0f5Sstsp ieee80211_release_node(ic, ni); 15898b9da0f5Sstsp ifp->if_oerrors++; 15908b9da0f5Sstsp continue; 15918b9da0f5Sstsp } 15928b9da0f5Sstsp 15938b9da0f5Sstsp sc->sc_tx_timer = 5; 15948b9da0f5Sstsp ifp->if_timer = 1; 15958b9da0f5Sstsp } 15968b9da0f5Sstsp } 15978b9da0f5Sstsp 15988b9da0f5Sstsp void 15998b9da0f5Sstsp rtwn_watchdog(struct ifnet *ifp) 16008b9da0f5Sstsp { 16018b9da0f5Sstsp struct rtwn_softc *sc = ifp->if_softc; 16028b9da0f5Sstsp 16038b9da0f5Sstsp ifp->if_timer = 0; 16048b9da0f5Sstsp 16058b9da0f5Sstsp if (sc->sc_tx_timer > 0) { 16068b9da0f5Sstsp if (--sc->sc_tx_timer == 0) { 16078b9da0f5Sstsp printf("%s: device timeout\n", sc->sc_pdev->dv_xname); 16088b9da0f5Sstsp task_add(systq, &sc->init_task); 16098b9da0f5Sstsp ifp->if_oerrors++; 16108b9da0f5Sstsp return; 16118b9da0f5Sstsp } 16128b9da0f5Sstsp ifp->if_timer = 1; 16138b9da0f5Sstsp } 16148b9da0f5Sstsp ieee80211_watchdog(ifp); 16158b9da0f5Sstsp } 16168b9da0f5Sstsp 16178b9da0f5Sstsp int 16188b9da0f5Sstsp rtwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 16198b9da0f5Sstsp { 16208b9da0f5Sstsp struct rtwn_softc *sc = ifp->if_softc; 16218b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 16228b9da0f5Sstsp int s, error = 0; 16238b9da0f5Sstsp 16248b9da0f5Sstsp s = splnet(); 16258b9da0f5Sstsp /* 16268b9da0f5Sstsp * Prevent processes from entering this function while another 16278b9da0f5Sstsp * process is tsleep'ing in it. 16288b9da0f5Sstsp */ 16298b9da0f5Sstsp while ((sc->sc_flags & RTWN_FLAG_BUSY) && error == 0) 163003604742Smpi error = tsleep_nsec(&sc->sc_flags, PCATCH, "rtwnioc", INFSLP); 16318b9da0f5Sstsp if (error != 0) { 16328b9da0f5Sstsp splx(s); 16338b9da0f5Sstsp return error; 16348b9da0f5Sstsp } 16358b9da0f5Sstsp sc->sc_flags |= RTWN_FLAG_BUSY; 16368b9da0f5Sstsp 16378b9da0f5Sstsp switch (cmd) { 16388b9da0f5Sstsp case SIOCSIFADDR: 16398b9da0f5Sstsp ifp->if_flags |= IFF_UP; 16408b9da0f5Sstsp /* FALLTHROUGH */ 16418b9da0f5Sstsp case SIOCSIFFLAGS: 16428b9da0f5Sstsp if (ifp->if_flags & IFF_UP) { 16438b9da0f5Sstsp if (!(ifp->if_flags & IFF_RUNNING)) 16448b9da0f5Sstsp rtwn_init(ifp); 16458b9da0f5Sstsp } else { 16468b9da0f5Sstsp if (ifp->if_flags & IFF_RUNNING) 16478b9da0f5Sstsp rtwn_stop(ifp); 16488b9da0f5Sstsp } 16498b9da0f5Sstsp break; 16508b9da0f5Sstsp case SIOCS80211CHANNEL: 16518b9da0f5Sstsp error = ieee80211_ioctl(ifp, cmd, data); 16528b9da0f5Sstsp if (error == ENETRESET && 16538b9da0f5Sstsp ic->ic_opmode == IEEE80211_M_MONITOR) { 16548b9da0f5Sstsp if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 16558b9da0f5Sstsp (IFF_UP | IFF_RUNNING)) 16568b9da0f5Sstsp rtwn_set_chan(sc, ic->ic_ibss_chan, NULL); 16578b9da0f5Sstsp error = 0; 16588b9da0f5Sstsp } 16598b9da0f5Sstsp break; 16608b9da0f5Sstsp default: 16618b9da0f5Sstsp error = ieee80211_ioctl(ifp, cmd, data); 16628b9da0f5Sstsp } 16638b9da0f5Sstsp 16648b9da0f5Sstsp if (error == ENETRESET) { 16658b9da0f5Sstsp if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == 16668b9da0f5Sstsp (IFF_UP | IFF_RUNNING)) { 16678b9da0f5Sstsp rtwn_stop(ifp); 16688b9da0f5Sstsp rtwn_init(ifp); 16698b9da0f5Sstsp } 16708b9da0f5Sstsp error = 0; 16718b9da0f5Sstsp } 16728b9da0f5Sstsp sc->sc_flags &= ~RTWN_FLAG_BUSY; 16738b9da0f5Sstsp wakeup(&sc->sc_flags); 16748b9da0f5Sstsp splx(s); 16758b9da0f5Sstsp 16768b9da0f5Sstsp return (error); 16778b9da0f5Sstsp } 16788b9da0f5Sstsp 16798b9da0f5Sstsp void 16808b9da0f5Sstsp rtwn_fw_reset(struct rtwn_softc *sc) 16818b9da0f5Sstsp { 1682067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) 1683bedde684Skevlo rtwn_r88e_fw_reset(sc); 1684bedde684Skevlo else 1685bedde684Skevlo rtwn_r92c_fw_reset(sc); 1686bedde684Skevlo } 1687bedde684Skevlo 1688bedde684Skevlo void 1689bedde684Skevlo rtwn_r92c_fw_reset(struct rtwn_softc *sc) 1690bedde684Skevlo { 16918b9da0f5Sstsp uint16_t reg; 16928b9da0f5Sstsp int ntries; 16938b9da0f5Sstsp 16948b9da0f5Sstsp /* Tell 8051 to reset itself. */ 16958b9da0f5Sstsp rtwn_write_1(sc, R92C_HMETFR + 3, 0x20); 16968b9da0f5Sstsp 16978b9da0f5Sstsp /* Wait until 8051 resets by itself. */ 16988b9da0f5Sstsp for (ntries = 0; ntries < 100; ntries++) { 16998b9da0f5Sstsp reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); 17008b9da0f5Sstsp if (!(reg & R92C_SYS_FUNC_EN_CPUEN)) 17018b9da0f5Sstsp goto sleep; 17028b9da0f5Sstsp DELAY(50); 17038b9da0f5Sstsp } 17048b9da0f5Sstsp /* Force 8051 reset. */ 17058b9da0f5Sstsp rtwn_write_2(sc, R92C_SYS_FUNC_EN, reg & ~R92C_SYS_FUNC_EN_CPUEN); 17068b9da0f5Sstsp sleep: 170723340becSstsp if (sc->chip & RTWN_CHIP_PCI) { 17088b9da0f5Sstsp /* 17098b9da0f5Sstsp * We must sleep for one second to let the firmware settle. 17108b9da0f5Sstsp * Accessing registers too early will hang the whole system. 17118b9da0f5Sstsp */ 17128544fed6Smpi tsleep_nsec(®, 0, "rtwnrst", SEC_TO_NSEC(1)); 17138b9da0f5Sstsp } 171423340becSstsp } 17158b9da0f5Sstsp 17165fda900fSstsp void 17175fda900fSstsp rtwn_r88e_fw_reset(struct rtwn_softc *sc) 17185fda900fSstsp { 1719bedde684Skevlo /* Reset MCU IO wrapper. */ 1720067851b1Skevlo if (!(sc->chip & RTWN_CHIP_88F)) { 17212d2a7e26Skevlo rtwn_write_1(sc, R92C_RSV_CTRL, 17222d2a7e26Skevlo rtwn_read_1(sc, R92C_RSV_CTRL) & ~R92C_RSV_CTRL_WLOCK_00); 1723067851b1Skevlo } 17242d2a7e26Skevlo if (sc->chip & RTWN_CHIP_88E) { 17252d2a7e26Skevlo rtwn_write_2(sc, R92C_RSV_CTRL, 17262d2a7e26Skevlo rtwn_read_2(sc, R92C_RSV_CTRL) & ~R88E_RSV_CTRL_MCU_RST); 17272d2a7e26Skevlo } else { 172890540544Sjmatthew rtwn_write_2(sc, R92C_RSV_CTRL, 172990540544Sjmatthew rtwn_read_2(sc, R92C_RSV_CTRL) & ~R88E_RSV_CTRL_MIO_EN); 17302d2a7e26Skevlo } 1731067851b1Skevlo rtwn_write_2(sc, R92C_SYS_FUNC_EN, 1732067851b1Skevlo rtwn_read_2(sc, R92C_SYS_FUNC_EN) & ~R92C_SYS_FUNC_EN_CPUEN); 173390540544Sjmatthew 1734bedde684Skevlo /* Enable MCU IO wrapper. */ 1735067851b1Skevlo if (!(sc->chip & RTWN_CHIP_88F)) { 17362d2a7e26Skevlo rtwn_write_1(sc, R92C_RSV_CTRL, 17372d2a7e26Skevlo rtwn_read_1(sc, R92C_RSV_CTRL) & ~R92C_RSV_CTRL_WLOCK_00); 1738067851b1Skevlo } 17392d2a7e26Skevlo if (sc->chip & RTWN_CHIP_88E) { 17402d2a7e26Skevlo rtwn_write_2(sc, R92C_RSV_CTRL, 17412d2a7e26Skevlo rtwn_read_2(sc, R92C_RSV_CTRL) | R88E_RSV_CTRL_MCU_RST); 17422d2a7e26Skevlo } else { 174390540544Sjmatthew rtwn_write_2(sc, R92C_RSV_CTRL, 174490540544Sjmatthew rtwn_read_2(sc, R92C_RSV_CTRL) | R88E_RSV_CTRL_MIO_EN); 17452d2a7e26Skevlo } 1746067851b1Skevlo rtwn_write_2(sc, R92C_SYS_FUNC_EN, 1747067851b1Skevlo rtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_CPUEN); 17485fda900fSstsp } 17495fda900fSstsp 17508b9da0f5Sstsp int 17518b9da0f5Sstsp rtwn_load_firmware(struct rtwn_softc *sc) 17528b9da0f5Sstsp { 17538b9da0f5Sstsp const struct r92c_fw_hdr *hdr; 17548b9da0f5Sstsp u_char *fw, *ptr; 17556a474672Sjca size_t len0, len; 17568b9da0f5Sstsp uint32_t reg; 17578b9da0f5Sstsp int mlen, ntries, page, error; 17588b9da0f5Sstsp 17598b9da0f5Sstsp /* Read firmware image from the filesystem. */ 17606a474672Sjca error = sc->sc_ops.load_firmware(sc->sc_ops.cookie, &fw, &len0); 17615fda900fSstsp if (error) 17628b9da0f5Sstsp return (error); 17636a474672Sjca len = len0; 17648b9da0f5Sstsp if (len < sizeof(*hdr)) { 17658b9da0f5Sstsp printf("%s: firmware too short\n", sc->sc_pdev->dv_xname); 17668b9da0f5Sstsp error = EINVAL; 17678b9da0f5Sstsp goto fail; 17688b9da0f5Sstsp } 17698b9da0f5Sstsp ptr = fw; 17708b9da0f5Sstsp hdr = (const struct r92c_fw_hdr *)ptr; 17718b9da0f5Sstsp /* Check if there is a valid FW header and skip it. */ 1772600882e8Sjmatthew if ((letoh16(hdr->signature) >> 4) == 0x230 || 1773600882e8Sjmatthew (letoh16(hdr->signature) >> 4) == 0x88c || 17745fda900fSstsp (letoh16(hdr->signature) >> 4) == 0x88e || 1775067851b1Skevlo (letoh16(hdr->signature) >> 4) == 0x88f || 177690540544Sjmatthew (letoh16(hdr->signature) >> 4) == 0x92c || 177790540544Sjmatthew (letoh16(hdr->signature) >> 4) == 0x92e) { 17788b9da0f5Sstsp DPRINTF(("FW V%d.%d %02d-%02d %02d:%02d\n", 17798b9da0f5Sstsp letoh16(hdr->version), letoh16(hdr->subversion), 17808b9da0f5Sstsp hdr->month, hdr->date, hdr->hour, hdr->minute)); 17818b9da0f5Sstsp ptr += sizeof(*hdr); 17828b9da0f5Sstsp len -= sizeof(*hdr); 17838b9da0f5Sstsp } 17848b9da0f5Sstsp 17855fda900fSstsp if (rtwn_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL) { 178623340becSstsp rtwn_write_1(sc, R92C_MCUFWDL, 0); 1787bedde684Skevlo rtwn_fw_reset(sc); 1788bedde684Skevlo } 1789bedde684Skevlo 1790067851b1Skevlo if ((sc->chip & RTWN_CHIP_PCI) || (sc->chip & RTWN_CHIP_88F)) { 1791bedde684Skevlo rtwn_write_2(sc, R92C_SYS_FUNC_EN, 1792bedde684Skevlo rtwn_read_2(sc, R92C_SYS_FUNC_EN) | R92C_SYS_FUNC_EN_CPUEN); 17935fda900fSstsp } 17948b9da0f5Sstsp 17958b9da0f5Sstsp /* Enable FW download. */ 17968b9da0f5Sstsp rtwn_write_1(sc, R92C_MCUFWDL, 17978b9da0f5Sstsp rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_EN); 1798bedde684Skevlo rtwn_write_4(sc, R92C_MCUFWDL, 1799bedde684Skevlo rtwn_read_4(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_ROM_DLEN); 18008b9da0f5Sstsp 18018b9da0f5Sstsp /* Reset the FWDL checksum. */ 18028b9da0f5Sstsp rtwn_write_1(sc, R92C_MCUFWDL, 18038b9da0f5Sstsp rtwn_read_1(sc, R92C_MCUFWDL) | R92C_MCUFWDL_CHKSUM_RPT); 18048b9da0f5Sstsp 180590540544Sjmatthew DELAY(50); 18068b9da0f5Sstsp for (page = 0; len > 0; page++) { 18078b9da0f5Sstsp mlen = MIN(len, R92C_FW_PAGE_SIZE); 180823340becSstsp error = sc->sc_ops.fw_loadpage(sc->sc_ops.cookie, page, ptr, 180923340becSstsp mlen); 18108b9da0f5Sstsp if (error != 0) { 18118b9da0f5Sstsp printf("%s: could not load firmware page %d\n", 18128b9da0f5Sstsp sc->sc_pdev->dv_xname, page); 18138b9da0f5Sstsp goto fail; 18148b9da0f5Sstsp } 18158b9da0f5Sstsp ptr += mlen; 18168b9da0f5Sstsp len -= mlen; 18178b9da0f5Sstsp } 18188b9da0f5Sstsp 18198b9da0f5Sstsp /* Wait for checksum report. */ 18208b9da0f5Sstsp for (ntries = 0; ntries < 1000; ntries++) { 18218b9da0f5Sstsp if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_CHKSUM_RPT) 18228b9da0f5Sstsp break; 18238b9da0f5Sstsp DELAY(5); 18248b9da0f5Sstsp } 18258b9da0f5Sstsp if (ntries == 1000) { 18268b9da0f5Sstsp printf("%s: timeout waiting for checksum report\n", 18278b9da0f5Sstsp sc->sc_pdev->dv_xname); 18288b9da0f5Sstsp error = ETIMEDOUT; 18298b9da0f5Sstsp goto fail; 18308b9da0f5Sstsp } 18318b9da0f5Sstsp 1832bedde684Skevlo /* Disable FW download. */ 1833bedde684Skevlo rtwn_write_1(sc, R92C_MCUFWDL, 1834bedde684Skevlo rtwn_read_1(sc, R92C_MCUFWDL) & ~R92C_MCUFWDL_EN); 18358bca19d0Skevlo 18368bca19d0Skevlo /* Reserved for fw extension. */ 1837067851b1Skevlo if (!(sc->chip & (RTWN_CHIP_88F | RTWN_CHIP_92E))) 1838bedde684Skevlo rtwn_write_1(sc, R92C_MCUFWDL + 1, 0); 1839bedde684Skevlo 18408b9da0f5Sstsp reg = rtwn_read_4(sc, R92C_MCUFWDL); 18418b9da0f5Sstsp reg = (reg & ~R92C_MCUFWDL_WINTINI_RDY) | R92C_MCUFWDL_RDY; 18428b9da0f5Sstsp rtwn_write_4(sc, R92C_MCUFWDL, reg); 1843462fe981Skevlo if (sc->chip & (RTWN_CHIP_92C | RTWN_CHIP_88C | RTWN_CHIP_23A)) { 1844bedde684Skevlo reg = rtwn_read_2(sc, R92C_SYS_FUNC_EN); 1845bedde684Skevlo rtwn_write_2(sc, R92C_SYS_FUNC_EN, 1846bedde684Skevlo reg & ~R92C_SYS_FUNC_EN_CPUEN); 1847bedde684Skevlo rtwn_write_2(sc, R92C_SYS_FUNC_EN, 1848bedde684Skevlo reg | R92C_SYS_FUNC_EN_CPUEN); 1849462fe981Skevlo } else 1850bedde684Skevlo rtwn_fw_reset(sc); 18518b9da0f5Sstsp /* Wait for firmware readiness. */ 18528b9da0f5Sstsp for (ntries = 0; ntries < 1000; ntries++) { 18538b9da0f5Sstsp if (rtwn_read_4(sc, R92C_MCUFWDL) & R92C_MCUFWDL_WINTINI_RDY) 18548b9da0f5Sstsp break; 18555fda900fSstsp DELAY(10); 18568b9da0f5Sstsp } 18578b9da0f5Sstsp if (ntries == 1000) { 18588b9da0f5Sstsp printf("%s: timeout waiting for firmware readiness\n", 18598b9da0f5Sstsp sc->sc_pdev->dv_xname); 18608b9da0f5Sstsp error = ETIMEDOUT; 18618b9da0f5Sstsp goto fail; 18628b9da0f5Sstsp } 18638b9da0f5Sstsp fail: 18646a474672Sjca free(fw, M_DEVBUF, len0); 1865067851b1Skevlo /* Init H2C command. */ 1866067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) 1867067851b1Skevlo rtwn_write_1(sc, R92C_HMETFR, 0xf); 18688b9da0f5Sstsp return (error); 18698b9da0f5Sstsp } 18708b9da0f5Sstsp 18718b9da0f5Sstsp void 18728b9da0f5Sstsp rtwn_rf_init(struct rtwn_softc *sc) 18738b9da0f5Sstsp { 18748b9da0f5Sstsp const struct r92c_rf_prog *prog; 18758b9da0f5Sstsp uint32_t reg, type; 18768b9da0f5Sstsp int i, j, idx, off; 18778b9da0f5Sstsp 18788b9da0f5Sstsp /* Select RF programming based on board type. */ 18795fda900fSstsp if (sc->chip & RTWN_CHIP_88E) 18805fda900fSstsp prog = rtl8188eu_rf_prog; 1881067851b1Skevlo else if (sc->chip & RTWN_CHIP_88F) 1882067851b1Skevlo prog = rtl8188ftv_rf_prog; 188390540544Sjmatthew else if (sc->chip & RTWN_CHIP_92E) 188490540544Sjmatthew prog = rtl8192e_rf_prog; 18855fda900fSstsp else if (!(sc->chip & RTWN_CHIP_92C)) { 18868b9da0f5Sstsp if (sc->board_type == R92C_BOARD_TYPE_MINICARD) 18878b9da0f5Sstsp prog = rtl8188ce_rf_prog; 18888b9da0f5Sstsp else if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) 18898b9da0f5Sstsp prog = rtl8188ru_rf_prog; 18908b9da0f5Sstsp else 18918b9da0f5Sstsp prog = rtl8188cu_rf_prog; 18928b9da0f5Sstsp } else 18938b9da0f5Sstsp prog = rtl8192ce_rf_prog; 18948b9da0f5Sstsp 18958b9da0f5Sstsp for (i = 0; i < sc->nrxchains; i++) { 18968b9da0f5Sstsp /* Save RF_ENV control type. */ 18978b9da0f5Sstsp idx = i / 2; 18988b9da0f5Sstsp off = (i % 2) * 16; 18998b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); 19008b9da0f5Sstsp type = (reg >> off) & 0x10; 19018b9da0f5Sstsp 19028b9da0f5Sstsp /* Set RF_ENV enable. */ 19038b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); 19048b9da0f5Sstsp reg |= 0x100000; 19058b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); 190690540544Sjmatthew DELAY(50); 19078b9da0f5Sstsp /* Set RF_ENV output high. */ 19088b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(i)); 19098b9da0f5Sstsp reg |= 0x10; 19108b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(i), reg); 191190540544Sjmatthew DELAY(50); 19128b9da0f5Sstsp /* Set address and data lengths of RF registers. */ 19138b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); 19148b9da0f5Sstsp reg &= ~R92C_HSSI_PARAM2_ADDR_LENGTH; 19158b9da0f5Sstsp rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); 191690540544Sjmatthew DELAY(50); 19178b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_HSSI_PARAM2(i)); 19188b9da0f5Sstsp reg &= ~R92C_HSSI_PARAM2_DATA_LENGTH; 19198b9da0f5Sstsp rtwn_bb_write(sc, R92C_HSSI_PARAM2(i), reg); 192090540544Sjmatthew DELAY(50); 19218b9da0f5Sstsp 19228b9da0f5Sstsp /* Write RF initialization values for this chain. */ 19238b9da0f5Sstsp for (j = 0; j < prog[i].count; j++) { 192490540544Sjmatthew switch (prog[i].regs[j]) { 192590540544Sjmatthew case 0xfe: 1926067851b1Skevlo case 0xffe: 192790540544Sjmatthew DELAY(50000); 192890540544Sjmatthew continue; 192990540544Sjmatthew case 0xfd: 193090540544Sjmatthew DELAY(5000); 193190540544Sjmatthew continue; 193290540544Sjmatthew case 0xfc: 193390540544Sjmatthew DELAY(1000); 193490540544Sjmatthew continue; 193590540544Sjmatthew case 0xfb: 19368b9da0f5Sstsp DELAY(50); 19378b9da0f5Sstsp continue; 193890540544Sjmatthew case 0xfa: 193990540544Sjmatthew DELAY(5); 194090540544Sjmatthew continue; 194190540544Sjmatthew case 0xf9: 194290540544Sjmatthew DELAY(1); 194390540544Sjmatthew continue; 19448b9da0f5Sstsp } 19458b9da0f5Sstsp rtwn_rf_write(sc, i, prog[i].regs[j], 19468b9da0f5Sstsp prog[i].vals[j]); 194790540544Sjmatthew DELAY(5); 19488b9da0f5Sstsp } 19498b9da0f5Sstsp 19508b9da0f5Sstsp /* Restore RF_ENV control type. */ 19518b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(idx)); 19528b9da0f5Sstsp reg &= ~(0x10 << off) | (type << off); 19538b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(idx), reg); 19548b9da0f5Sstsp 19558b9da0f5Sstsp /* Cache RF register CHNLBW. */ 19568b9da0f5Sstsp sc->rf_chnlbw[i] = rtwn_rf_read(sc, i, R92C_RF_CHNLBW); 19578b9da0f5Sstsp } 19588b9da0f5Sstsp 1959d8e41173Sjmatthew /* magic value for HP 8188EEs */ 1960d8e41173Sjmatthew if (sc->chip == (RTWN_CHIP_88E | RTWN_CHIP_PCI)) { 1961626433d9Skevlo struct r88e_rom *rom = &sc->sc_r88e_rom; 1962626433d9Skevlo if (rom->r88ee_rom.svid == 0x103c && 1963626433d9Skevlo rom->r88ee_rom.smid == 0x197d) 1964d8e41173Sjmatthew rtwn_rf_write(sc, 0, 0x52, 0x7e4bd); 1965d8e41173Sjmatthew } 1966d8e41173Sjmatthew 19678b9da0f5Sstsp if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) == 19688b9da0f5Sstsp RTWN_CHIP_UMC_A_CUT) { 19698b9da0f5Sstsp rtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255); 19708b9da0f5Sstsp rtwn_rf_write(sc, 0, R92C_RF_RX_G2, 0x50a00); 1971600882e8Sjmatthew } else if (sc->chip & RTWN_CHIP_23A) { 1972600882e8Sjmatthew rtwn_rf_write(sc, 0, 0x0C, 0x894ae); 1973600882e8Sjmatthew rtwn_rf_write(sc, 0, 0x0A, 0x1af31); 1974600882e8Sjmatthew rtwn_rf_write(sc, 0, R92C_RF_IPA, 0x8f425); 1975600882e8Sjmatthew rtwn_rf_write(sc, 0, R92C_RF_SYN_G(1), 0x4f200); 1976600882e8Sjmatthew rtwn_rf_write(sc, 0, R92C_RF_RCK1, 0x44053); 1977600882e8Sjmatthew rtwn_rf_write(sc, 0, R92C_RF_RCK2, 0x80201); 19788b9da0f5Sstsp } 19798b9da0f5Sstsp } 19808b9da0f5Sstsp 19818b9da0f5Sstsp void 19828b9da0f5Sstsp rtwn_cam_init(struct rtwn_softc *sc) 19838b9da0f5Sstsp { 19848b9da0f5Sstsp /* Invalidate all CAM entries. */ 19858b9da0f5Sstsp rtwn_write_4(sc, R92C_CAMCMD, 19868b9da0f5Sstsp R92C_CAMCMD_POLLING | R92C_CAMCMD_CLR); 19878b9da0f5Sstsp } 19888b9da0f5Sstsp 19898b9da0f5Sstsp void 19908b9da0f5Sstsp rtwn_pa_bias_init(struct rtwn_softc *sc) 19918b9da0f5Sstsp { 19928b9da0f5Sstsp uint8_t reg; 19938b9da0f5Sstsp int i; 19948b9da0f5Sstsp 19958b9da0f5Sstsp for (i = 0; i < sc->nrxchains; i++) { 19968b9da0f5Sstsp if (sc->pa_setting & (1 << i)) 19978b9da0f5Sstsp continue; 19988b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_IPA, 0x0f406); 19998b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_IPA, 0x4f406); 20008b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_IPA, 0x8f406); 20018b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_IPA, 0xcf406); 20028b9da0f5Sstsp } 20038b9da0f5Sstsp if (!(sc->pa_setting & 0x10)) { 20048b9da0f5Sstsp reg = rtwn_read_1(sc, 0x16); 20058b9da0f5Sstsp reg = (reg & ~0xf0) | 0x90; 20068b9da0f5Sstsp rtwn_write_1(sc, 0x16, reg); 20078b9da0f5Sstsp } 20088b9da0f5Sstsp } 20098b9da0f5Sstsp 20108b9da0f5Sstsp void 20118b9da0f5Sstsp rtwn_rxfilter_init(struct rtwn_softc *sc) 20128b9da0f5Sstsp { 20138b9da0f5Sstsp /* Initialize Rx filter. */ 20148b9da0f5Sstsp rtwn_write_4(sc, R92C_RCR, 20158b9da0f5Sstsp R92C_RCR_AAP | R92C_RCR_APM | R92C_RCR_AM | R92C_RCR_AB | 20168b9da0f5Sstsp R92C_RCR_APP_ICV | R92C_RCR_AMF | R92C_RCR_HTC_LOC_CTRL | 20178b9da0f5Sstsp R92C_RCR_APP_MIC | R92C_RCR_APP_PHYSTS); 20188b9da0f5Sstsp /* Accept all multicast frames. */ 20198b9da0f5Sstsp rtwn_write_4(sc, R92C_MAR + 0, 0xffffffff); 20208b9da0f5Sstsp rtwn_write_4(sc, R92C_MAR + 4, 0xffffffff); 20218b9da0f5Sstsp /* Accept all management frames. */ 20228b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP0, 0xffff); 20238b9da0f5Sstsp /* Reject all control frames. */ 20248b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP1, 0x0000); 20258b9da0f5Sstsp /* Accept all data frames. */ 20268b9da0f5Sstsp rtwn_write_2(sc, R92C_RXFLTMAP2, 0xffff); 20278b9da0f5Sstsp } 20288b9da0f5Sstsp 20298b9da0f5Sstsp void 20308b9da0f5Sstsp rtwn_edca_init(struct rtwn_softc *sc) 20318b9da0f5Sstsp { 2032af1fd538Sstsp struct ieee80211com *ic = &sc->sc_ic; 2033af1fd538Sstsp int mode, aci; 2034af1fd538Sstsp 2035af1fd538Sstsp /* Set SIFS; 0x10 = 16 usec (SIFS 11g), 0x0a = 10 usec (SIFS 11b) */ 203623340becSstsp rtwn_write_2(sc, R92C_SPEC_SIFS, 0x100a); 203723340becSstsp rtwn_write_2(sc, R92C_MAC_SPEC_SIFS, 0x100a); 203823340becSstsp rtwn_write_2(sc, R92C_SIFS_CCK, 0x100a); 203923340becSstsp rtwn_write_2(sc, R92C_SIFS_OFDM, 0x100a); 2040067851b1Skevlo if (!(sc->chip & RTWN_CHIP_88F)) { 2041af1fd538Sstsp rtwn_write_2(sc, R92C_RESP_SIFS_CCK, 0x100a); 2042af1fd538Sstsp rtwn_write_2(sc, R92C_RESP_SIFS_OFDM, 0x100a); 2043067851b1Skevlo } else { 2044067851b1Skevlo rtwn_write_2(sc, R92C_RESP_SIFS_CCK, 0x0808); 2045067851b1Skevlo rtwn_write_2(sc, R92C_RESP_SIFS_OFDM, 0x0a0a); 2046067851b1Skevlo } 2047af1fd538Sstsp 2048af1fd538Sstsp if (ic->ic_curmode == IEEE80211_MODE_AUTO) 2049af1fd538Sstsp mode = IEEE80211_MODE_11G; /* XXX */ 2050af1fd538Sstsp else 2051af1fd538Sstsp mode = ic->ic_curmode; 2052af1fd538Sstsp for (aci = 0; aci < EDCA_NUM_AC; aci++) 2053af1fd538Sstsp memcpy(&ic->ic_edca_ac[aci], &ieee80211_edca_table[mode][aci], 2054af1fd538Sstsp sizeof(struct ieee80211_edca_ac_params)); 2055af1fd538Sstsp rtwn_updateedca(ic); 2056af1fd538Sstsp 20575fb81a6cSkevlo if (sc->chip & RTWN_CHIP_PCI) { 20585fb81a6cSkevlo /* linux magic */ 20595fb81a6cSkevlo rtwn_write_4(sc, R92C_FAST_EDCA_CTRL, 0x086666); 20605fb81a6cSkevlo } 2061af1fd538Sstsp 2062af1fd538Sstsp rtwn_write_4(sc, R92C_EDCA_RANDOM_GEN, arc4random()); 20638b9da0f5Sstsp } 20648b9da0f5Sstsp 20658b9da0f5Sstsp void 20665fb81a6cSkevlo rtwn_rate_fallback_init(struct rtwn_softc *sc) 20675fb81a6cSkevlo { 206890540544Sjmatthew if (!(sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_92E))) { 20695fb81a6cSkevlo if (sc->chip & RTWN_CHIP_PCI) { 20705fb81a6cSkevlo rtwn_write_4(sc, R92C_DARFRC + 0, 0x01000000); 20715fb81a6cSkevlo rtwn_write_4(sc, R92C_DARFRC + 4, 0x07060504); 20725fb81a6cSkevlo rtwn_write_4(sc, R92C_RARFRC + 0, 0x01000000); 20735fb81a6cSkevlo rtwn_write_4(sc, R92C_RARFRC + 4, 0x07060504); 20745fb81a6cSkevlo } else if (sc->chip & RTWN_CHIP_USB) { 20755fb81a6cSkevlo rtwn_write_4(sc, R92C_DARFRC + 0, 0x00000000); 20765fb81a6cSkevlo rtwn_write_4(sc, R92C_DARFRC + 4, 0x10080404); 20775fb81a6cSkevlo rtwn_write_4(sc, R92C_RARFRC + 0, 0x04030201); 20785fb81a6cSkevlo rtwn_write_4(sc, R92C_RARFRC + 4, 0x08070605); 20795fb81a6cSkevlo } 20805fb81a6cSkevlo } 20815fb81a6cSkevlo } 20825fb81a6cSkevlo 20835fb81a6cSkevlo void 20848b9da0f5Sstsp rtwn_write_txpower(struct rtwn_softc *sc, int chain, 208534a2cadeSkevlo uint16_t power[RTWN_POWER_COUNT]) 20868b9da0f5Sstsp { 20878b9da0f5Sstsp uint32_t reg; 20888b9da0f5Sstsp 20898b9da0f5Sstsp /* Write per-CCK rate Tx power. */ 20908b9da0f5Sstsp if (chain == 0) { 20918b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_TXAGC_A_CCK1_MCS32); 209234a2cadeSkevlo reg = RW(reg, R92C_TXAGC_A_CCK1, power[RTWN_POWER_CCK1]); 20938b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_A_CCK1_MCS32, reg); 20948b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); 209534a2cadeSkevlo reg = RW(reg, R92C_TXAGC_A_CCK2, power[RTWN_POWER_CCK2]); 209634a2cadeSkevlo reg = RW(reg, R92C_TXAGC_A_CCK55, power[RTWN_POWER_CCK55]); 209734a2cadeSkevlo reg = RW(reg, R92C_TXAGC_A_CCK11, power[RTWN_POWER_CCK11]); 20988b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); 20998b9da0f5Sstsp } else { 21008b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK1_55_MCS32); 210134a2cadeSkevlo reg = RW(reg, R92C_TXAGC_B_CCK1, power[RTWN_POWER_CCK1]); 210234a2cadeSkevlo reg = RW(reg, R92C_TXAGC_B_CCK2, power[RTWN_POWER_CCK2]); 210334a2cadeSkevlo reg = RW(reg, R92C_TXAGC_B_CCK55, power[RTWN_POWER_CCK55]); 21048b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_B_CCK1_55_MCS32, reg); 21058b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_TXAGC_B_CCK11_A_CCK2_11); 210634a2cadeSkevlo reg = RW(reg, R92C_TXAGC_B_CCK11, power[RTWN_POWER_CCK11]); 21078b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_B_CCK11_A_CCK2_11, reg); 21088b9da0f5Sstsp } 21098b9da0f5Sstsp /* Write per-OFDM rate Tx power. */ 21108b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_RATE18_06(chain), 211134a2cadeSkevlo SM(R92C_TXAGC_RATE06, power[RTWN_POWER_OFDM6]) | 211234a2cadeSkevlo SM(R92C_TXAGC_RATE09, power[RTWN_POWER_OFDM9]) | 211334a2cadeSkevlo SM(R92C_TXAGC_RATE12, power[RTWN_POWER_OFDM12]) | 211434a2cadeSkevlo SM(R92C_TXAGC_RATE18, power[RTWN_POWER_OFDM18])); 21158b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_RATE54_24(chain), 211634a2cadeSkevlo SM(R92C_TXAGC_RATE24, power[RTWN_POWER_OFDM24]) | 211734a2cadeSkevlo SM(R92C_TXAGC_RATE36, power[RTWN_POWER_OFDM36]) | 211834a2cadeSkevlo SM(R92C_TXAGC_RATE48, power[RTWN_POWER_OFDM48]) | 211934a2cadeSkevlo SM(R92C_TXAGC_RATE54, power[RTWN_POWER_OFDM54])); 21208b9da0f5Sstsp /* Write per-MCS Tx power. */ 21218b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_MCS03_MCS00(chain), 212234a2cadeSkevlo SM(R92C_TXAGC_MCS00, power[RTWN_POWER_MCS( 0)]) | 212334a2cadeSkevlo SM(R92C_TXAGC_MCS01, power[RTWN_POWER_MCS( 1)]) | 212434a2cadeSkevlo SM(R92C_TXAGC_MCS02, power[RTWN_POWER_MCS( 2)]) | 212534a2cadeSkevlo SM(R92C_TXAGC_MCS03, power[RTWN_POWER_MCS( 3)])); 21268b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_MCS07_MCS04(chain), 212734a2cadeSkevlo SM(R92C_TXAGC_MCS04, power[RTWN_POWER_MCS( 4)]) | 212834a2cadeSkevlo SM(R92C_TXAGC_MCS05, power[RTWN_POWER_MCS( 5)]) | 212934a2cadeSkevlo SM(R92C_TXAGC_MCS06, power[RTWN_POWER_MCS( 6)]) | 213034a2cadeSkevlo SM(R92C_TXAGC_MCS07, power[RTWN_POWER_MCS( 7)])); 21312f5d27e0Skevlo if (sc->ntxchains > 1) { 21328b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_MCS11_MCS08(chain), 213334a2cadeSkevlo SM(R92C_TXAGC_MCS08, power[RTWN_POWER_MCS( 8)]) | 213434a2cadeSkevlo SM(R92C_TXAGC_MCS09, power[RTWN_POWER_MCS( 9)]) | 213534a2cadeSkevlo SM(R92C_TXAGC_MCS10, power[RTWN_POWER_MCS(10)]) | 213634a2cadeSkevlo SM(R92C_TXAGC_MCS11, power[RTWN_POWER_MCS(11)])); 21378b9da0f5Sstsp rtwn_bb_write(sc, R92C_TXAGC_MCS15_MCS12(chain), 213834a2cadeSkevlo SM(R92C_TXAGC_MCS12, power[RTWN_POWER_MCS(12)]) | 213934a2cadeSkevlo SM(R92C_TXAGC_MCS13, power[RTWN_POWER_MCS(13)]) | 214034a2cadeSkevlo SM(R92C_TXAGC_MCS14, power[RTWN_POWER_MCS(14)]) | 214134a2cadeSkevlo SM(R92C_TXAGC_MCS15, power[RTWN_POWER_MCS(15)])); 21428b9da0f5Sstsp } 214308300c66Skevlo } 21448b9da0f5Sstsp 21458b9da0f5Sstsp void 2146bedde684Skevlo rtwn_get_txpower(struct rtwn_softc *sc, int chain, struct ieee80211_channel *c, 214734a2cadeSkevlo struct ieee80211_channel *extc, uint16_t power[RTWN_POWER_COUNT]) 214834a2cadeSkevlo { 2149067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F)) 215034a2cadeSkevlo rtwn_r88e_get_txpower(sc, chain, c, extc, power); 215190540544Sjmatthew else if (sc->chip & RTWN_CHIP_92E) 215290540544Sjmatthew rtwn_r92e_get_txpower(sc, chain, c, extc, power); 215334a2cadeSkevlo else 215434a2cadeSkevlo rtwn_r92c_get_txpower(sc, chain, c, extc, power); 215534a2cadeSkevlo } 215634a2cadeSkevlo 215734a2cadeSkevlo void 215834a2cadeSkevlo rtwn_r92c_get_txpower(struct rtwn_softc *sc, int chain, 21598b9da0f5Sstsp struct ieee80211_channel *c, struct ieee80211_channel *extc, 216034a2cadeSkevlo uint16_t power[RTWN_POWER_COUNT]) 21618b9da0f5Sstsp { 21628b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 216334a2cadeSkevlo struct r92c_rom *rom = &sc->sc_r92c_rom; 21648b9da0f5Sstsp uint16_t cckpow, ofdmpow, htpow, diff, max; 21658b9da0f5Sstsp const struct r92c_txpwr *base; 21668b9da0f5Sstsp int ridx, chan, group; 21678b9da0f5Sstsp 21688b9da0f5Sstsp /* Determine channel group. */ 21698b9da0f5Sstsp chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ 21708b9da0f5Sstsp if (chan <= 3) 21718b9da0f5Sstsp group = 0; 21728b9da0f5Sstsp else if (chan <= 9) 21738b9da0f5Sstsp group = 1; 21748b9da0f5Sstsp else 21758b9da0f5Sstsp group = 2; 21768b9da0f5Sstsp 21778b9da0f5Sstsp /* Get original Tx power based on board type and RF chain. */ 21788b9da0f5Sstsp if (!(sc->chip & RTWN_CHIP_92C)) { 21798b9da0f5Sstsp if (sc->board_type == R92C_BOARD_TYPE_HIGHPA) 21808b9da0f5Sstsp base = &rtl8188ru_txagc[chain]; 21818b9da0f5Sstsp else 21828b9da0f5Sstsp base = &rtl8192cu_txagc[chain]; 21838b9da0f5Sstsp } else 21848b9da0f5Sstsp base = &rtl8192cu_txagc[chain]; 21858b9da0f5Sstsp 218634a2cadeSkevlo memset(power, 0, RTWN_POWER_COUNT * sizeof(power[0])); 21878b9da0f5Sstsp if (sc->regulatory == 0) { 218834a2cadeSkevlo for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) 21898b9da0f5Sstsp power[ridx] = base->pwr[0][ridx]; 21908b9da0f5Sstsp } 219134a2cadeSkevlo for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_MAX; ridx++) { 21928b9da0f5Sstsp if (sc->regulatory == 3) { 21938b9da0f5Sstsp power[ridx] = base->pwr[0][ridx]; 21948b9da0f5Sstsp /* Apply vendor limits. */ 21958b9da0f5Sstsp if (extc != NULL) 21968b9da0f5Sstsp max = rom->ht40_max_pwr[group]; 21978b9da0f5Sstsp else 21988b9da0f5Sstsp max = rom->ht20_max_pwr[group]; 21998b9da0f5Sstsp max = (max >> (chain * 4)) & 0xf; 22008b9da0f5Sstsp if (power[ridx] > max) 22018b9da0f5Sstsp power[ridx] = max; 22028b9da0f5Sstsp } else if (sc->regulatory == 1) { 22038b9da0f5Sstsp if (extc == NULL) 22048b9da0f5Sstsp power[ridx] = base->pwr[group][ridx]; 22058b9da0f5Sstsp } else if (sc->regulatory != 2) 22068b9da0f5Sstsp power[ridx] = base->pwr[0][ridx]; 22078b9da0f5Sstsp } 22088b9da0f5Sstsp 22098b9da0f5Sstsp /* Compute per-CCK rate Tx power. */ 22108b9da0f5Sstsp cckpow = rom->cck_tx_pwr[chain][group]; 221134a2cadeSkevlo for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { 22128b9da0f5Sstsp power[ridx] += cckpow; 22138b9da0f5Sstsp if (power[ridx] > R92C_MAX_TX_PWR) 22148b9da0f5Sstsp power[ridx] = R92C_MAX_TX_PWR; 22158b9da0f5Sstsp } 22168b9da0f5Sstsp 22178b9da0f5Sstsp htpow = rom->ht40_1s_tx_pwr[chain][group]; 22188b9da0f5Sstsp if (sc->ntxchains > 1) { 22198b9da0f5Sstsp /* Apply reduction for 2 spatial streams. */ 22208b9da0f5Sstsp diff = rom->ht40_2s_tx_pwr_diff[group]; 22218b9da0f5Sstsp diff = (diff >> (chain * 4)) & 0xf; 22228b9da0f5Sstsp htpow = (htpow > diff) ? htpow - diff : 0; 22238b9da0f5Sstsp } 22248b9da0f5Sstsp 22258b9da0f5Sstsp /* Compute per-OFDM rate Tx power. */ 22268b9da0f5Sstsp diff = rom->ofdm_tx_pwr_diff[group]; 22278b9da0f5Sstsp diff = (diff >> (chain * 4)) & 0xf; 22288b9da0f5Sstsp ofdmpow = htpow + diff; /* HT->OFDM correction. */ 222934a2cadeSkevlo for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) { 22308b9da0f5Sstsp power[ridx] += ofdmpow; 22318b9da0f5Sstsp if (power[ridx] > R92C_MAX_TX_PWR) 22328b9da0f5Sstsp power[ridx] = R92C_MAX_TX_PWR; 22338b9da0f5Sstsp } 22348b9da0f5Sstsp 22358b9da0f5Sstsp /* Compute per-MCS Tx power. */ 22368b9da0f5Sstsp if (extc == NULL) { 22378b9da0f5Sstsp diff = rom->ht20_tx_pwr_diff[group]; 22388b9da0f5Sstsp diff = (diff >> (chain * 4)) & 0xf; 22398b9da0f5Sstsp htpow += diff; /* HT40->HT20 correction. */ 22408b9da0f5Sstsp } 224134a2cadeSkevlo for (ridx = RTWN_RIDX_MCS0; ridx <= RTWN_RIDX_MCS15; ridx++) { 22428b9da0f5Sstsp power[ridx] += htpow; 22438b9da0f5Sstsp if (power[ridx] > R92C_MAX_TX_PWR) 22448b9da0f5Sstsp power[ridx] = R92C_MAX_TX_PWR; 22458b9da0f5Sstsp } 22468b9da0f5Sstsp #ifdef RTWN_DEBUG 22478b9da0f5Sstsp if (rtwn_debug >= 4) { 22488b9da0f5Sstsp /* Dump per-rate Tx power values. */ 22498b9da0f5Sstsp printf("Tx power for chain %d:\n", chain); 225034a2cadeSkevlo for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_MAX; ridx++) 22518b9da0f5Sstsp printf("Rate %d = %u\n", ridx, power[ridx]); 22528b9da0f5Sstsp } 22538b9da0f5Sstsp #endif 22548b9da0f5Sstsp } 22558b9da0f5Sstsp 22568b9da0f5Sstsp void 225790540544Sjmatthew rtwn_r92e_get_txpower(struct rtwn_softc *sc, int chain, 225890540544Sjmatthew struct ieee80211_channel *c, struct ieee80211_channel *extc, 225990540544Sjmatthew uint16_t power[RTWN_POWER_COUNT]) 226090540544Sjmatthew { 226190540544Sjmatthew struct ieee80211com *ic = &sc->sc_ic; 226290540544Sjmatthew struct r92e_rom *rom = &sc->sc_r92e_rom; 226390540544Sjmatthew struct r92e_tx_pwr *txpwr; 226433410555Skevlo uint8_t cckpow, htpow, htpow2s = 0, ofdmpow; 226590540544Sjmatthew int8_t diff; 226690540544Sjmatthew int ridx, chan, group; 226790540544Sjmatthew 226890540544Sjmatthew /* Determine channel group. */ 226990540544Sjmatthew chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ 2270067851b1Skevlo group = rtwn_chan2group(chan); 227190540544Sjmatthew 227290540544Sjmatthew memset(power, 0, RTWN_POWER_COUNT * sizeof(power[0])); 227390540544Sjmatthew 227490540544Sjmatthew if (chain == 0) 227590540544Sjmatthew txpwr = &rom->txpwr_a; 227690540544Sjmatthew else 227790540544Sjmatthew txpwr = &rom->txpwr_b; 227890540544Sjmatthew 227990540544Sjmatthew /* Compute per-CCK rate Tx power. */ 228090540544Sjmatthew cckpow = txpwr->cck_tx_pwr[group]; 228190540544Sjmatthew for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { 228290540544Sjmatthew power[ridx] = cckpow; 228390540544Sjmatthew if (power[ridx] > R92C_MAX_TX_PWR) 228490540544Sjmatthew power[ridx] = R92C_MAX_TX_PWR; 228590540544Sjmatthew } 228690540544Sjmatthew 228733410555Skevlo htpow = txpwr->ht40_tx_pwr[group]; 228833410555Skevlo 228990540544Sjmatthew /* Compute per-OFDM rate Tx power. */ 229090540544Sjmatthew diff = RTWN_SIGN4TO8(MS(txpwr->ht20_ofdm_tx_pwr_diff, 229190540544Sjmatthew R92E_ROM_TXPWR_OFDM_DIFF)); 229233410555Skevlo ofdmpow = htpow + diff; 229390540544Sjmatthew for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) { 229490540544Sjmatthew power[ridx] = ofdmpow; 229590540544Sjmatthew if (power[ridx] > R92C_MAX_TX_PWR) 229690540544Sjmatthew power[ridx] = R92C_MAX_TX_PWR; 229790540544Sjmatthew } 229890540544Sjmatthew 229933410555Skevlo /* Compute per-MCS Tx power. */ 230033410555Skevlo if (extc == NULL) { 230190540544Sjmatthew diff = RTWN_SIGN4TO8(MS(txpwr->ht20_ofdm_tx_pwr_diff, 230290540544Sjmatthew R92E_ROM_TXPWR_HT20_DIFF)); 230333410555Skevlo htpow += diff; 230433410555Skevlo if (sc->ntxchains > 1) { 230533410555Skevlo diff = RTWN_SIGN4TO8(MS( 230633410555Skevlo txpwr->pwr_diff[0].ht40_ht20_tx_pwr_diff, 230733410555Skevlo R92E_ROM_TXPWR_HT20_2S_DIFF)); 230833410555Skevlo htpow2s = htpow + diff; 230933410555Skevlo } 231033410555Skevlo } 231190540544Sjmatthew 2312c0ae0cefSkevlo for (ridx = RTWN_RIDX_MCS0; ridx <= RTWN_RIDX_MCS15; ridx++) { 231333410555Skevlo power[ridx] = (ridx < RTWN_RIDX_MCS8) ? htpow : htpow2s; 231490540544Sjmatthew if (power[ridx] > R92C_MAX_TX_PWR) 231590540544Sjmatthew power[ridx] = R92C_MAX_TX_PWR; 231690540544Sjmatthew } 231790540544Sjmatthew } 231890540544Sjmatthew 231990540544Sjmatthew void 23205fda900fSstsp rtwn_r88e_get_txpower(struct rtwn_softc *sc, int chain, 23215fda900fSstsp struct ieee80211_channel *c, struct ieee80211_channel *extc, 232234a2cadeSkevlo uint16_t power[RTWN_POWER_COUNT]) 23235fda900fSstsp { 23245fda900fSstsp struct ieee80211com *ic = &sc->sc_ic; 232534a2cadeSkevlo struct r88e_rom *rom = &sc->sc_r88e_rom; 232634a2cadeSkevlo uint8_t cckpow, htpow, ofdmpow; 232734a2cadeSkevlo int8_t diff; 23285fda900fSstsp int ridx, chan, group; 23295fda900fSstsp 23305fda900fSstsp /* Determine channel group. */ 23315fda900fSstsp chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ 2332067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) 2333067851b1Skevlo group = rtwn_chan2group(chan); 2334067851b1Skevlo else { 23355fda900fSstsp if (chan <= 2) 23365fda900fSstsp group = 0; 23375fda900fSstsp else if (chan <= 5) 23385fda900fSstsp group = 1; 23395fda900fSstsp else if (chan <= 8) 23405fda900fSstsp group = 2; 23415fda900fSstsp else if (chan <= 11) 23425fda900fSstsp group = 3; 23435fda900fSstsp else if (chan <= 13) 23445fda900fSstsp group = 4; 23455fda900fSstsp else 23465fda900fSstsp group = 5; 2347067851b1Skevlo } 23485fda900fSstsp 234934a2cadeSkevlo memset(power, 0, RTWN_POWER_COUNT * sizeof(power[0])); 23505fda900fSstsp 23515fda900fSstsp /* Compute per-CCK rate Tx power. */ 235234a2cadeSkevlo cckpow = rom->txpwr.cck_tx_pwr[group]; 235334a2cadeSkevlo for (ridx = RTWN_RIDX_CCK1; ridx <= RTWN_RIDX_CCK11; ridx++) { 2354067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) 2355067851b1Skevlo power[ridx] = cckpow; 2356067851b1Skevlo else 2357067851b1Skevlo power[ridx] = (ridx == RTWN_RIDX_CCK2) ? 2358067851b1Skevlo cckpow - 9 : cckpow; 23595fda900fSstsp if (power[ridx] > R92C_MAX_TX_PWR) 23605fda900fSstsp power[ridx] = R92C_MAX_TX_PWR; 23615fda900fSstsp } 23625fda900fSstsp 2363067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) 2364067851b1Skevlo htpow = rom->txpwr.ht40_tx_pwr[group]; 2365067851b1Skevlo else 23665fb81a6cSkevlo htpow = (group == 5) ? rom->txpwr.ht40_tx_pwr[group - 1] : 23675fb81a6cSkevlo rom->txpwr.ht40_tx_pwr[group]; 23685fda900fSstsp 23695fda900fSstsp /* Compute per-OFDM rate Tx power. */ 237034a2cadeSkevlo diff = RTWN_SIGN4TO8(MS(rom->txpwr.ht20_ofdm_tx_pwr_diff, 237134a2cadeSkevlo R88E_ROM_TXPWR_OFDM_DIFF)); 237234a2cadeSkevlo ofdmpow = htpow + diff; 237334a2cadeSkevlo for (ridx = RTWN_RIDX_OFDM6; ridx <= RTWN_RIDX_OFDM54; ridx++) { 237434a2cadeSkevlo power[ridx] = ofdmpow; 23755fda900fSstsp if (power[ridx] > R92C_MAX_TX_PWR) 23765fda900fSstsp power[ridx] = R92C_MAX_TX_PWR; 23775fda900fSstsp } 23785fda900fSstsp 237934a2cadeSkevlo /* Compute per-MCS Tx power. */ 238034a2cadeSkevlo if (extc == NULL) { 238134a2cadeSkevlo diff = RTWN_SIGN4TO8(MS(rom->txpwr.ht20_ofdm_tx_pwr_diff, 238234a2cadeSkevlo R88E_ROM_TXPWR_HT20_DIFF)); 238334a2cadeSkevlo htpow += diff; 238434a2cadeSkevlo } 238534a2cadeSkevlo for (ridx = RTWN_RIDX_MCS0; ridx < RTWN_RIDX_MCS8; ridx++) { 238634a2cadeSkevlo power[ridx] = htpow; 23875fda900fSstsp if (power[ridx] > R92C_MAX_TX_PWR) 23885fda900fSstsp power[ridx] = R92C_MAX_TX_PWR; 23895fda900fSstsp } 23905fda900fSstsp } 23915fda900fSstsp 23925fda900fSstsp void 23938b9da0f5Sstsp rtwn_set_txpower(struct rtwn_softc *sc, struct ieee80211_channel *c, 23948b9da0f5Sstsp struct ieee80211_channel *extc) 23958b9da0f5Sstsp { 239634a2cadeSkevlo uint16_t power[RTWN_POWER_COUNT]; 23978b9da0f5Sstsp int i; 23988b9da0f5Sstsp 23998b9da0f5Sstsp for (i = 0; i < sc->ntxchains; i++) { 24008b9da0f5Sstsp /* Compute per-rate Tx power values. */ 24018b9da0f5Sstsp rtwn_get_txpower(sc, i, c, extc, power); 24028b9da0f5Sstsp /* Write per-rate Tx power values to hardware. */ 24038b9da0f5Sstsp rtwn_write_txpower(sc, i, power); 24048b9da0f5Sstsp } 24058b9da0f5Sstsp } 24068b9da0f5Sstsp 24078b9da0f5Sstsp void 24088b9da0f5Sstsp rtwn_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c, 24098b9da0f5Sstsp struct ieee80211_channel *extc) 24108b9da0f5Sstsp { 24118b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 24128b9da0f5Sstsp u_int chan; 241390540544Sjmatthew uint32_t reg; 24148b9da0f5Sstsp int i; 24158b9da0f5Sstsp 24168b9da0f5Sstsp chan = ieee80211_chan2ieee(ic, c); /* XXX center freq! */ 24178b9da0f5Sstsp 24188b9da0f5Sstsp /* Set Tx power for this new channel. */ 24198b9da0f5Sstsp rtwn_set_txpower(sc, c, extc); 24208b9da0f5Sstsp 24218b9da0f5Sstsp if (extc != NULL) { 24228b9da0f5Sstsp /* Is secondary channel below or above primary? */ 24238b9da0f5Sstsp int prichlo = c->ic_freq < extc->ic_freq; 24248b9da0f5Sstsp 242590540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 242690540544Sjmatthew reg = rtwn_read_2(sc, R92C_WMAC_TRXPTCL_CTL); 242790540544Sjmatthew reg &= ~R92C_WMAC_TRXPTCL_CTL_BW_MASK; 242890540544Sjmatthew reg |= R92C_WMAC_TRXPTCL_CTL_BW_40; 242990540544Sjmatthew rtwn_write_2(sc, R92C_WMAC_TRXPTCL_CTL, reg); 243090540544Sjmatthew rtwn_write_1(sc, R92E_DATA_SC, 0); 243190540544Sjmatthew } else { 24328b9da0f5Sstsp rtwn_write_1(sc, R92C_BWOPMODE, 243390540544Sjmatthew rtwn_read_1(sc, R92C_BWOPMODE) & 243490540544Sjmatthew ~R92C_BWOPMODE_20MHZ); 243590540544Sjmatthew } 24368b9da0f5Sstsp 24378b9da0f5Sstsp reg = rtwn_read_1(sc, R92C_RRSR + 2); 24388b9da0f5Sstsp reg = (reg & ~0x6f) | (prichlo ? 1 : 2) << 5; 24398b9da0f5Sstsp rtwn_write_1(sc, R92C_RRSR + 2, reg); 24408b9da0f5Sstsp 24418b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFMOD, 24428b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA0_RFMOD) | R92C_RFMOD_40MHZ); 24438b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA1_RFMOD, 24448b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA1_RFMOD) | R92C_RFMOD_40MHZ); 24458b9da0f5Sstsp 24468b9da0f5Sstsp /* Set CCK side band. */ 24478b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_CCK0_SYSTEM); 24488b9da0f5Sstsp reg = (reg & ~0x00000010) | (prichlo ? 0 : 1) << 4; 24498b9da0f5Sstsp rtwn_bb_write(sc, R92C_CCK0_SYSTEM, reg); 24508b9da0f5Sstsp 24518b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM1_LSTF); 24528b9da0f5Sstsp reg = (reg & ~0x00000c00) | (prichlo ? 1 : 2) << 10; 24538b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM1_LSTF, reg); 24548b9da0f5Sstsp 245590540544Sjmatthew if (!(sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_92E))) { 24568b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, 24578b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) & 24588b9da0f5Sstsp ~R92C_FPGA0_ANAPARAM2_CBW20); 24592f5d27e0Skevlo } 24608b9da0f5Sstsp 24618b9da0f5Sstsp reg = rtwn_bb_read(sc, 0x818); 24628b9da0f5Sstsp reg = (reg & ~0x0c000000) | (prichlo ? 2 : 1) << 26; 24638b9da0f5Sstsp rtwn_bb_write(sc, 0x818, reg); 24648b9da0f5Sstsp 24658b9da0f5Sstsp /* Select 40MHz bandwidth. */ 246690540544Sjmatthew for (i = 0; i < sc->nrxchains; i++) { 246790540544Sjmatthew rtwn_rf_write(sc, i, R92C_RF_CHNLBW, 246890540544Sjmatthew (sc->rf_chnlbw[i] & ~0xfff) | chan); 246990540544Sjmatthew } 247090540544Sjmatthew } else { 247190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 247290540544Sjmatthew reg = rtwn_read_2(sc, R92C_WMAC_TRXPTCL_CTL); 247390540544Sjmatthew reg &= ~R92C_WMAC_TRXPTCL_CTL_BW_MASK; 247490540544Sjmatthew rtwn_write_2(sc, R92C_WMAC_TRXPTCL_CTL, reg); 247590540544Sjmatthew rtwn_write_1(sc, R92E_DATA_SC, 0); 2476067851b1Skevlo } else if (!(sc->chip & RTWN_CHIP_88F)) { 24778b9da0f5Sstsp rtwn_write_1(sc, R92C_BWOPMODE, 247890540544Sjmatthew rtwn_read_1(sc, R92C_BWOPMODE) | 247990540544Sjmatthew R92C_BWOPMODE_20MHZ); 248090540544Sjmatthew } 24818b9da0f5Sstsp 24828b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFMOD, 24838b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA0_RFMOD) & ~R92C_RFMOD_40MHZ); 24848b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA1_RFMOD, 24858b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA1_RFMOD) & ~R92C_RFMOD_40MHZ); 24868b9da0f5Sstsp 2487067851b1Skevlo if (!(sc->chip & 2488067851b1Skevlo (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) { 24898b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_ANAPARAM2, 24908b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) | 24918b9da0f5Sstsp R92C_FPGA0_ANAPARAM2_CBW20); 2492067851b1Skevlo } else if (sc->chip & (RTWN_CHIP_88F | RTWN_CHIP_92E)) { 2493067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) { 2494067851b1Skevlo reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); 2495067851b1Skevlo reg = (reg & ~0x00000700) | 0x7 << 8; 2496067851b1Skevlo rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); 2497067851b1Skevlo reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); 2498067851b1Skevlo reg = (reg & ~0x00007000) | 0x5 << 12; 2499067851b1Skevlo rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); 2500067851b1Skevlo } 2501067851b1Skevlo 2502067851b1Skevlo reg = rtwn_bb_read(sc, R92C_OFDM0_TX_PSDO_NOISE_WEIGHT); 250390540544Sjmatthew reg &= ~0xc0000000; 2504067851b1Skevlo rtwn_bb_write(sc, R92C_OFDM0_TX_PSDO_NOISE_WEIGHT, reg); 2505067851b1Skevlo 2506067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) { 2507067851b1Skevlo /* Small bandwidth */ 2508067851b1Skevlo reg = rtwn_bb_read(sc, 2509067851b1Skevlo R92C_OFDM0_TX_PSDO_NOISE_WEIGHT); 2510067851b1Skevlo reg |= 0x30000000; 2511067851b1Skevlo rtwn_bb_write(sc, 2512067851b1Skevlo R92C_OFDM0_TX_PSDO_NOISE_WEIGHT, reg); 2513067851b1Skevlo /* ADC buffer clk */ 2514067851b1Skevlo rtwn_bb_write(sc, R92C_OFDM0_RXAFE, 2515067851b1Skevlo rtwn_bb_read(sc, R92C_OFDM0_RXAFE) | 2516067851b1Skevlo 0x30000000); 2517067851b1Skevlo /* OFDM Rx DFIR */ 2518067851b1Skevlo rtwn_bb_write(sc, R88F_RX_DFIR, 2519067851b1Skevlo rtwn_bb_read(sc, R88F_RX_DFIR) & 2520067851b1Skevlo ~0x00080000); 2521067851b1Skevlo reg = rtwn_bb_read(sc, R88F_RX_DFIR); 2522067851b1Skevlo reg = (reg & ~0x00f00000) | 0x3 << 15; 2523067851b1Skevlo rtwn_bb_write(sc, R88F_RX_DFIR, reg); 2524067851b1Skevlo } 252523340becSstsp } 25268b9da0f5Sstsp 25278b9da0f5Sstsp /* Select 20MHz bandwidth. */ 252890540544Sjmatthew for (i = 0; i < sc->nrxchains; i++) { 252990540544Sjmatthew rtwn_rf_write(sc, i, R92C_RF_CHNLBW, 253090540544Sjmatthew (sc->rf_chnlbw[i] & ~0xfff) | chan | 2531067851b1Skevlo ((sc->chip & 2532067851b1Skevlo (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) ? 253390540544Sjmatthew R88E_RF_CHNLBW_BW20 : R92C_RF_CHNLBW_BW20)); 2534067851b1Skevlo 2535067851b1Skevlo if (sc->chip & RTWN_CHIP_88F) { 2536067851b1Skevlo rtwn_rf_write(sc, i, 0x87, 0x65); 2537067851b1Skevlo rtwn_rf_write(sc, i, 0x1c, 0); 2538067851b1Skevlo rtwn_rf_write(sc, i, 0xdf, 0x0140); 2539*479c151dSjsg rtwn_rf_write(sc, i, 0x1b, 0x1c6c); 2540067851b1Skevlo } 254190540544Sjmatthew } 25428b9da0f5Sstsp } 2543d8e41173Sjmatthew 2544d8e41173Sjmatthew if (sc->chip == (RTWN_CHIP_88E | RTWN_CHIP_PCI)) 2545d8e41173Sjmatthew DELAY(25000); 25468b9da0f5Sstsp } 25478b9da0f5Sstsp 25488b9da0f5Sstsp int 2549067851b1Skevlo rtwn_chan2group(int chan) 2550067851b1Skevlo { 2551067851b1Skevlo int group; 2552067851b1Skevlo 2553067851b1Skevlo if (chan <= 2) 2554067851b1Skevlo group = 0; 2555067851b1Skevlo else if (chan <= 5) 2556067851b1Skevlo group = 1; 2557067851b1Skevlo else if (chan <= 8) 2558067851b1Skevlo group = 2; 2559067851b1Skevlo else if (chan <= 11) 2560067851b1Skevlo group = 3; 2561067851b1Skevlo else 2562067851b1Skevlo group = 4; 2563067851b1Skevlo 2564067851b1Skevlo return (group); 2565067851b1Skevlo } 2566067851b1Skevlo 2567067851b1Skevlo int 25688b9da0f5Sstsp rtwn_iq_calib_chain(struct rtwn_softc *sc, int chain, uint16_t tx[2], 25698b9da0f5Sstsp uint16_t rx[2]) 25708b9da0f5Sstsp { 25718b9da0f5Sstsp uint32_t status; 25728b9da0f5Sstsp int offset = chain * 0x20; 257390540544Sjmatthew uint32_t iqk_tone_92c[] = { 257490540544Sjmatthew 0x10008c1f, 0x10008c1f, 0x82140102, 0x28160202, 0x10008c22 257590540544Sjmatthew }; 257690540544Sjmatthew uint32_t iqk_tone_92e[] = { 257790540544Sjmatthew 0x18008c1c, 0x38008c1c, 0x82140303, 0x68160000, 0x38008c1c 257890540544Sjmatthew }; 257990540544Sjmatthew uint32_t *iqk_tone; 258090540544Sjmatthew 258190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) 258290540544Sjmatthew iqk_tone = iqk_tone_92e; 258390540544Sjmatthew else 258490540544Sjmatthew iqk_tone = iqk_tone_92c; 25858b9da0f5Sstsp 25868b9da0f5Sstsp if (chain == 0) { /* IQ calibration for chain 0. */ 25878b9da0f5Sstsp /* IQ calibration settings for chain 0. */ 258890540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK_TONE_A, iqk_tone[0]); 258990540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_TONE_B, iqk_tone[1]); 259090540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK_PI_A, iqk_tone[2]); 25918b9da0f5Sstsp 25928b9da0f5Sstsp if (sc->ntxchains > 1) { 259390540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_PI_A, iqk_tone[3]); 25948b9da0f5Sstsp /* IQ calibration settings for chain 1. */ 259590540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK_TONE_B, iqk_tone[4]); 259690540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_TONE_B, iqk_tone[4]); 259790540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK_PI_B, 0x82140102); 259890540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_PI_B, 0x28160202); 25998b9da0f5Sstsp } else 260090540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_PI_A, 0x28160502); 26018b9da0f5Sstsp 26028b9da0f5Sstsp /* LO calibration settings. */ 260390540544Sjmatthew if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_92E)) 260490540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_RSP, 0x00462911); 260590540544Sjmatthew else 260690540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_RSP, 0x001028d1); 26078b9da0f5Sstsp /* We're doing LO and IQ calibration in one shot. */ 260890540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_PTS, 0xf9000000); 260990540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_PTS, 0xf8000000); 26108b9da0f5Sstsp 26118b9da0f5Sstsp } else { /* IQ calibration for chain 1. */ 26128b9da0f5Sstsp /* We're doing LO and IQ calibration in one shot. */ 261390540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_CONT, 0x00000002); 261490540544Sjmatthew rtwn_bb_write(sc, R92C_IQK_AGC_CONT, 0x00000000); 26158b9da0f5Sstsp } 26168b9da0f5Sstsp 26178b9da0f5Sstsp /* Give LO and IQ calibrations the time to complete. */ 26188b9da0f5Sstsp DELAY(1000); 26198b9da0f5Sstsp 26208b9da0f5Sstsp /* Read IQ calibration status. */ 26218b9da0f5Sstsp status = rtwn_bb_read(sc, 0xeac); 26228b9da0f5Sstsp 26238b9da0f5Sstsp if (status & (1 << (28 + chain * 3))) 26248b9da0f5Sstsp return (0); /* Tx failed. */ 26258b9da0f5Sstsp /* Read Tx IQ calibration results. */ 262690540544Sjmatthew tx[0] = (rtwn_bb_read(sc, R92C_TX_POWER_BEFORE_IQK_A + offset) >> 16) 262790540544Sjmatthew & 0x3ff; 262890540544Sjmatthew tx[1] = (rtwn_bb_read(sc, R92C_TX_POWER_AFTER_IQK_A + offset) >> 16) 262990540544Sjmatthew & 0x3ff; 26308b9da0f5Sstsp if (tx[0] == 0x142 || tx[1] == 0x042) 26318b9da0f5Sstsp return (0); /* Tx failed. */ 26328b9da0f5Sstsp 26338b9da0f5Sstsp if (status & (1 << (27 + chain * 3))) 26348b9da0f5Sstsp return (1); /* Rx failed. */ 26358b9da0f5Sstsp /* Read Rx IQ calibration results. */ 263690540544Sjmatthew rx[0] = (rtwn_bb_read(sc, R92C_RX_POWER_BEFORE_IQK_A_2 + offset) >> 16) 263790540544Sjmatthew & 0x3ff; 263890540544Sjmatthew rx[1] = (rtwn_bb_read(sc, R92C_RX_POWER_AFTER_IQK_A_2 + offset) >> 16) 263990540544Sjmatthew & 0x3ff; 26408b9da0f5Sstsp if (rx[0] == 0x132 || rx[1] == 0x036) 26418b9da0f5Sstsp return (1); /* Rx failed. */ 26428b9da0f5Sstsp 26438b9da0f5Sstsp return (3); /* Both Tx and Rx succeeded. */ 26448b9da0f5Sstsp } 26458b9da0f5Sstsp 26468b9da0f5Sstsp void 26478b9da0f5Sstsp rtwn_iq_calib_run(struct rtwn_softc *sc, int n, uint16_t tx[2][2], 26483150bca2Sstsp uint16_t rx[2][2], struct rtwn_iq_cal_regs *iq_cal_regs) 26498b9da0f5Sstsp { 26508b9da0f5Sstsp static const uint16_t reg_adda[16] = { 26518b9da0f5Sstsp 0x85c, 0xe6c, 0xe70, 0xe74, 26528b9da0f5Sstsp 0xe78, 0xe7c, 0xe80, 0xe84, 26538b9da0f5Sstsp 0xe88, 0xe8c, 0xed0, 0xed4, 26548b9da0f5Sstsp 0xed8, 0xedc, 0xee0, 0xeec 26558b9da0f5Sstsp }; 265690540544Sjmatthew static const uint32_t adda_92c[] = { 265790540544Sjmatthew 0x0b1b25a0, 0x0bdb25a0, 0x04db25a4, 0x0b1b25a4 265890540544Sjmatthew }; 265990540544Sjmatthew static const uint32_t adda_92e[] = { 266090540544Sjmatthew 0x0fc01616, 0x0fc01616, 0x0fc01616, 0x0fc01616 266190540544Sjmatthew }; 266290540544Sjmatthew const uint32_t *adda_vals; 266390540544Sjmatthew 26648b9da0f5Sstsp int i, chain; 266590540544Sjmatthew uint32_t hssi_param1, reg; 266690540544Sjmatthew uint8_t xa_agc, xb_agc; 266790540544Sjmatthew 266890540544Sjmatthew xa_agc = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)) & 0xff; 266990540544Sjmatthew xb_agc = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)) & 0xff; 267090540544Sjmatthew 267190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) 267290540544Sjmatthew adda_vals = adda_92e; 267390540544Sjmatthew else 267490540544Sjmatthew adda_vals = adda_92c; 26758b9da0f5Sstsp 26768b9da0f5Sstsp if (n == 0) { 26778b9da0f5Sstsp for (i = 0; i < nitems(reg_adda); i++) 26783150bca2Sstsp iq_cal_regs->adda[i] = rtwn_bb_read(sc, reg_adda[i]); 26798b9da0f5Sstsp 26803150bca2Sstsp iq_cal_regs->txpause = rtwn_read_1(sc, R92C_TXPAUSE); 26813150bca2Sstsp iq_cal_regs->bcn_ctrl = rtwn_read_1(sc, R92C_BCN_CTRL); 26823150bca2Sstsp iq_cal_regs->bcn_ctrl1 = rtwn_read_1(sc, R92C_BCN_CTRL1); 26833150bca2Sstsp iq_cal_regs->gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG); 26848b9da0f5Sstsp } 26858b9da0f5Sstsp 26868b9da0f5Sstsp if (sc->ntxchains == 1) { 268790540544Sjmatthew rtwn_bb_write(sc, reg_adda[0], adda_vals[0]); 26888b9da0f5Sstsp for (i = 1; i < nitems(reg_adda); i++) 268990540544Sjmatthew rtwn_bb_write(sc, reg_adda[i], adda_vals[1]); 26908b9da0f5Sstsp } else { 26918b9da0f5Sstsp for (i = 0; i < nitems(reg_adda); i++) 269290540544Sjmatthew rtwn_bb_write(sc, reg_adda[i], adda_vals[2]); 26938b9da0f5Sstsp } 26948b9da0f5Sstsp 26958b9da0f5Sstsp if (n == 0) { 26963150bca2Sstsp iq_cal_regs->ofdm0_trxpathena = 26978b9da0f5Sstsp rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); 26983150bca2Sstsp iq_cal_regs->ofdm0_trmuxpar = 26998b9da0f5Sstsp rtwn_bb_read(sc, R92C_OFDM0_TRMUXPAR); 270090540544Sjmatthew iq_cal_regs->fpga0_rfifacesw0 = 270190540544Sjmatthew rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(0)); 27023150bca2Sstsp iq_cal_regs->fpga0_rfifacesw1 = 27038b9da0f5Sstsp rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(1)); 270490540544Sjmatthew iq_cal_regs->fpga0_rfifaceoe0 = 270590540544Sjmatthew rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(0)); 270690540544Sjmatthew iq_cal_regs->fpga0_rfifaceoe1 = 270790540544Sjmatthew rtwn_bb_read(sc, R92C_FPGA0_RFIFACEOE(1)); 270890540544Sjmatthew iq_cal_regs->config_ant_a = 270990540544Sjmatthew rtwn_bb_read(sc, R92C_CONFIG_ANT_A); 271090540544Sjmatthew iq_cal_regs->config_ant_b = 271190540544Sjmatthew rtwn_bb_read(sc, R92C_CONFIG_ANT_B); 271290540544Sjmatthew iq_cal_regs->cck0_afesetting = 271390540544Sjmatthew rtwn_bb_read(sc, R92C_CCK0_AFESETTING); 271490540544Sjmatthew } 271590540544Sjmatthew 271690540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 271790540544Sjmatthew rtwn_write_4(sc, R92C_CCK0_AFESETTING, rtwn_read_4(sc, 271890540544Sjmatthew R92C_CCK0_AFESETTING) | 0x0f000000); 271990540544Sjmatthew } else { 272090540544Sjmatthew hssi_param1 = rtwn_bb_read(sc, R92C_HSSI_PARAM1(0)); 272190540544Sjmatthew if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { 272290540544Sjmatthew rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), 272390540544Sjmatthew hssi_param1 | R92C_HSSI_PARAM1_PI); 272490540544Sjmatthew rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), 272590540544Sjmatthew hssi_param1 | R92C_HSSI_PARAM1_PI); 272690540544Sjmatthew } 27278b9da0f5Sstsp } 27288b9da0f5Sstsp 27298b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600); 27308b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4); 273190540544Sjmatthew 273290540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 273390540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 0x22208200); 273490540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(0), 273590540544Sjmatthew rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(0)) | (1 << 10) | 273690540544Sjmatthew (1 << 26)); 273790540544Sjmatthew 273890540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(0), rtwn_bb_read(sc, 273990540544Sjmatthew R92C_FPGA0_RFIFACEOE(0)) | (1 << 10)); 274090540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(1), rtwn_bb_read(sc, 274190540544Sjmatthew R92C_FPGA0_RFIFACEOE(1)) | (1 << 10)); 274290540544Sjmatthew } else { 27438b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000); 274490540544Sjmatthew 27458b9da0f5Sstsp if (sc->ntxchains > 1) { 27468b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); 27478b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00010000); 27488b9da0f5Sstsp } 27498b9da0f5Sstsp 275056fbb35bSkevlo rtwn_write_1(sc, R92C_TXPAUSE, R92C_TXPAUSE_AC_VO | 275190540544Sjmatthew R92C_TXPAUSE_AC_VI | R92C_TXPAUSE_AC_BE | 275290540544Sjmatthew R92C_TXPAUSE_AC_BK | R92C_TXPAUSE_MGNT | 275390540544Sjmatthew R92C_TXPAUSE_HIGH); 275490540544Sjmatthew } 2755eaa9bfb9Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, 27563150bca2Sstsp iq_cal_regs->bcn_ctrl & ~(R92C_BCN_CTRL_EN_BCN)); 2757eaa9bfb9Sstsp rtwn_write_1(sc, R92C_BCN_CTRL1, 27583150bca2Sstsp iq_cal_regs->bcn_ctrl1 & ~(R92C_BCN_CTRL_EN_BCN)); 27598b9da0f5Sstsp rtwn_write_1(sc, R92C_GPIO_MUXCFG, 27603150bca2Sstsp iq_cal_regs->gpio_muxcfg & ~(R92C_GPIO_MUXCFG_ENBT)); 27618b9da0f5Sstsp 276290540544Sjmatthew rtwn_bb_write(sc, R92C_CONFIG_ANT_A, 0x00080000); 27638b9da0f5Sstsp if (sc->ntxchains > 1) 276490540544Sjmatthew rtwn_bb_write(sc, R92C_CONFIG_ANT_B, 0x00080000); 27658b9da0f5Sstsp 276690540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_IQK, 0x80800000); 276790540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK, 0x01007c00); 276890540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK, 0x01004800); 27698b9da0f5Sstsp 277090540544Sjmatthew rtwn_bb_write(sc, R92C_CONFIG_ANT_A, 0x00080000); 27718b9da0f5Sstsp 27728b9da0f5Sstsp for (chain = 0; chain < sc->ntxchains; chain++) { 27738b9da0f5Sstsp if (chain > 0) { 27748b9da0f5Sstsp /* Put chain 0 on standby. */ 277590540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_IQK, 0x00); 27768b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); 277790540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_IQK, 0x80800000); 27788b9da0f5Sstsp 27798b9da0f5Sstsp /* Enable chain 1. */ 27808b9da0f5Sstsp for (i = 0; i < nitems(reg_adda); i++) 278190540544Sjmatthew rtwn_bb_write(sc, reg_adda[i], adda_vals[3]); 27828b9da0f5Sstsp } 27838b9da0f5Sstsp 27848b9da0f5Sstsp /* Run IQ calibration twice. */ 27858b9da0f5Sstsp for (i = 0; i < 2; i++) { 27868b9da0f5Sstsp int ret; 27878b9da0f5Sstsp 27888b9da0f5Sstsp ret = rtwn_iq_calib_chain(sc, chain, 27898b9da0f5Sstsp tx[chain], rx[chain]); 27908b9da0f5Sstsp if (ret == 0) { 27918b9da0f5Sstsp DPRINTF(("%s: chain %d: Tx failed.\n", 27928b9da0f5Sstsp __func__, chain)); 27938b9da0f5Sstsp tx[chain][0] = 0xff; 27948b9da0f5Sstsp tx[chain][1] = 0xff; 27958b9da0f5Sstsp rx[chain][0] = 0xff; 27968b9da0f5Sstsp rx[chain][1] = 0xff; 27978b9da0f5Sstsp } else if (ret == 1) { 27988b9da0f5Sstsp DPRINTF(("%s: chain %d: Rx failed.\n", 27998b9da0f5Sstsp __func__, chain)); 28008b9da0f5Sstsp rx[chain][0] = 0xff; 28018b9da0f5Sstsp rx[chain][1] = 0xff; 28028b9da0f5Sstsp } else if (ret == 3) { 28038b9da0f5Sstsp DPRINTF(("%s: chain %d: Both Tx and Rx " 28048b9da0f5Sstsp "succeeded.\n", __func__, chain)); 28058b9da0f5Sstsp } 28068b9da0f5Sstsp } 28078b9da0f5Sstsp 28088b9da0f5Sstsp DPRINTF(("%s: results for run %d chain %d: tx[0]=0x%x, " 28098b9da0f5Sstsp "tx[1]=0x%x rx[0]=0x%x rx[1]=0x%x\n", __func__, n, chain, 28108b9da0f5Sstsp tx[chain][0], tx[chain][1], rx[chain][0], rx[chain][1])); 28118b9da0f5Sstsp } 28128b9da0f5Sstsp 281390540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_IQK, 0x00); 28148b9da0f5Sstsp 281590540544Sjmatthew if (!(sc->chip & RTWN_CHIP_92E)) { 28168b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00032ed3); 28178b9da0f5Sstsp if (sc->ntxchains > 1) 28188b9da0f5Sstsp rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00032ed3); 281990540544Sjmatthew } 28208b9da0f5Sstsp 28218b9da0f5Sstsp if (n != 0) { 282290540544Sjmatthew if (!(sc->chip & RTWN_CHIP_92E)) { 28238b9da0f5Sstsp if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { 282490540544Sjmatthew rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), 282590540544Sjmatthew hssi_param1); 282690540544Sjmatthew rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), 282790540544Sjmatthew hssi_param1); 282890540544Sjmatthew } 28298b9da0f5Sstsp } 28308b9da0f5Sstsp 28318b9da0f5Sstsp for (i = 0; i < nitems(reg_adda); i++) 28323150bca2Sstsp rtwn_bb_write(sc, reg_adda[i], iq_cal_regs->adda[i]); 28338b9da0f5Sstsp 28343150bca2Sstsp rtwn_write_1(sc, R92C_TXPAUSE, iq_cal_regs->txpause); 28353150bca2Sstsp rtwn_write_1(sc, R92C_BCN_CTRL, iq_cal_regs->bcn_ctrl); 28363150bca2Sstsp rtwn_write_1(sc, R92C_BCN_CTRL1, iq_cal_regs->bcn_ctrl1); 28373150bca2Sstsp rtwn_write_4(sc, R92C_GPIO_MUXCFG, iq_cal_regs->gpio_muxcfg); 283890540544Sjmatthew 283990540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 284090540544Sjmatthew iq_cal_regs->ofdm0_trxpathena); 284190540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(0), 284290540544Sjmatthew iq_cal_regs->fpga0_rfifacesw0); 284390540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 284490540544Sjmatthew iq_cal_regs->fpga0_rfifacesw1); 284590540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(0), 284690540544Sjmatthew iq_cal_regs->fpga0_rfifaceoe0); 284790540544Sjmatthew rtwn_bb_write(sc, R92C_FPGA0_RFIFACEOE(1), 284890540544Sjmatthew iq_cal_regs->fpga0_rfifaceoe1); 284990540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, 285090540544Sjmatthew iq_cal_regs->ofdm0_trmuxpar); 285190540544Sjmatthew rtwn_bb_write(sc, R92C_CONFIG_ANT_A, 285290540544Sjmatthew iq_cal_regs->config_ant_a); 285390540544Sjmatthew rtwn_bb_write(sc, R92C_CONFIG_ANT_B, 285490540544Sjmatthew iq_cal_regs->config_ant_b); 285590540544Sjmatthew rtwn_bb_write(sc, R92C_CCK0_AFESETTING, 285690540544Sjmatthew iq_cal_regs->cck0_afesetting); 285790540544Sjmatthew 285890540544Sjmatthew reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(0)); 285990540544Sjmatthew reg &= ~0xff; 286090540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg | 0x50); 286190540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(0), reg | xa_agc); 286290540544Sjmatthew 286390540544Sjmatthew reg = rtwn_bb_read(sc, R92C_OFDM0_AGCCORE1(1)); 286490540544Sjmatthew reg &= ~0xff; 286590540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg | 0x50); 286690540544Sjmatthew rtwn_bb_write(sc, R92C_OFDM0_AGCCORE1(1), reg | xb_agc); 286790540544Sjmatthew 286890540544Sjmatthew rtwn_bb_write(sc, R92C_TX_IQK_TONE_A, 0x01008c00); 286990540544Sjmatthew rtwn_bb_write(sc, R92C_RX_IQK_TONE_A, 0x01008c00); 28708b9da0f5Sstsp } 28718b9da0f5Sstsp } 28728b9da0f5Sstsp 28738b9da0f5Sstsp #define RTWN_IQ_CAL_MAX_TOLERANCE 5 28748b9da0f5Sstsp int 28758b9da0f5Sstsp rtwn_iq_calib_compare_results(uint16_t tx1[2][2], uint16_t rx1[2][2], 28768b9da0f5Sstsp uint16_t tx2[2][2], uint16_t rx2[2][2], int ntxchains) 28778b9da0f5Sstsp { 28788b9da0f5Sstsp int chain, i, tx_ok[2], rx_ok[2]; 28798b9da0f5Sstsp 28808b9da0f5Sstsp tx_ok[0] = tx_ok[1] = rx_ok[0] = rx_ok[1] = 0; 28818b9da0f5Sstsp for (chain = 0; chain < ntxchains; chain++) { 28828b9da0f5Sstsp for (i = 0; i < 2; i++) { 28838b9da0f5Sstsp if (tx1[chain][i] == 0xff || tx2[chain][i] == 0xff || 28848b9da0f5Sstsp rx1[chain][i] == 0xff || rx2[chain][i] == 0xff) 28858b9da0f5Sstsp continue; 28868b9da0f5Sstsp 28878b9da0f5Sstsp tx_ok[chain] = (abs(tx1[chain][i] - tx2[chain][i]) <= 28888b9da0f5Sstsp RTWN_IQ_CAL_MAX_TOLERANCE); 28898b9da0f5Sstsp 28908b9da0f5Sstsp rx_ok[chain] = (abs(rx1[chain][i] - rx2[chain][i]) <= 28918b9da0f5Sstsp RTWN_IQ_CAL_MAX_TOLERANCE); 28928b9da0f5Sstsp } 28938b9da0f5Sstsp } 28948b9da0f5Sstsp 28958b9da0f5Sstsp if (ntxchains > 1) 28968b9da0f5Sstsp return (tx_ok[0] && tx_ok[1] && rx_ok[0] && rx_ok[1]); 28978b9da0f5Sstsp else 28988b9da0f5Sstsp return (tx_ok[0] && rx_ok[0]); 28998b9da0f5Sstsp } 29008b9da0f5Sstsp #undef RTWN_IQ_CAL_MAX_TOLERANCE 29018b9da0f5Sstsp 29028b9da0f5Sstsp void 29038b9da0f5Sstsp rtwn_iq_calib_write_results(struct rtwn_softc *sc, uint16_t tx[2], 29048b9da0f5Sstsp uint16_t rx[2], int chain) 29058b9da0f5Sstsp { 29068b9da0f5Sstsp uint32_t reg, val, x; 29078b9da0f5Sstsp long y, tx_c; 29088b9da0f5Sstsp 29098b9da0f5Sstsp if (tx[0] == 0xff || tx[1] == 0xff) 29108b9da0f5Sstsp return; 29118b9da0f5Sstsp 29128b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); 29138b9da0f5Sstsp val = ((reg >> 22) & 0x3ff); 29148b9da0f5Sstsp x = tx[0]; 291593f2c0a6Sstsp if (x & 0x00000200) 291693f2c0a6Sstsp x |= 0xfffffc00; 291793f2c0a6Sstsp reg &= ~0x3ff; 291893f2c0a6Sstsp reg |= (((x * val) >> 8) & 0x3ff); 29198b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); 29208b9da0f5Sstsp 29218b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); 29228b9da0f5Sstsp if (((x * val) >> 7) & 0x01) 29238b9da0f5Sstsp reg |= 0x80000000; 29248b9da0f5Sstsp else 29258b9da0f5Sstsp reg &= ~0x80000000; 29268b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); 29278b9da0f5Sstsp 29288b9da0f5Sstsp y = tx[1]; 29298b9da0f5Sstsp if (y & 0x00000200) 29308b9da0f5Sstsp y |= 0xfffffc00; 29318b9da0f5Sstsp tx_c = (y * val) >> 8; 29328b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_TXAFE(chain)); 293393f2c0a6Sstsp reg &= ~0xf0000000; 293493f2c0a6Sstsp reg |= ((tx_c & 0x3c0) << 22); 29358b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_TXAFE(chain), reg); 29368b9da0f5Sstsp 29378b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); 293893f2c0a6Sstsp reg &= ~0x003f0000; 293993f2c0a6Sstsp reg |= ((tx_c & 0x3f) << 16); 29408b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_TXIQIMBALANCE(chain), reg); 29418b9da0f5Sstsp 29428b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_ECCATHRESHOLD); 29438b9da0f5Sstsp if (((y * val) >> 7) & 0x01) 29448b9da0f5Sstsp reg |= 0x20000000; 29458b9da0f5Sstsp else 29468b9da0f5Sstsp reg &= ~0x20000000; 29478b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_ECCATHRESHOLD, reg); 29488b9da0f5Sstsp 29498b9da0f5Sstsp if (rx[0] == 0xff || rx[1] == 0xff) 29508b9da0f5Sstsp return; 29518b9da0f5Sstsp 29528b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQIMBALANCE(chain)); 295393f2c0a6Sstsp reg &= ~0x3ff; 29548b9da0f5Sstsp reg |= (rx[0] & 0x3ff); 29558b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); 295693f2c0a6Sstsp 295793f2c0a6Sstsp reg &= ~0xfc00; 295893f2c0a6Sstsp reg |= ((rx[1] & 0x03f) << 10); 29598b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_RXIQIMBALANCE(chain), reg); 29608b9da0f5Sstsp 29618b9da0f5Sstsp if (chain == 0) { 29628b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_RXIQEXTANTA); 296393f2c0a6Sstsp reg &= ~0xf0000000; 296493f2c0a6Sstsp reg |= ((rx[1] & 0x3c0) << 22); 29658b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_RXIQEXTANTA, reg); 29668b9da0f5Sstsp } else { 29678b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_OFDM0_AGCRSSITABLE); 296893f2c0a6Sstsp reg &= ~0xf000; 296993f2c0a6Sstsp reg |= ((rx[1] & 0x3c0) << 6); 29708b9da0f5Sstsp rtwn_bb_write(sc, R92C_OFDM0_AGCRSSITABLE, reg); 29718b9da0f5Sstsp } 29728b9da0f5Sstsp } 29738b9da0f5Sstsp 29748b9da0f5Sstsp #define RTWN_IQ_CAL_NRUN 3 29758b9da0f5Sstsp void 29768b9da0f5Sstsp rtwn_iq_calib(struct rtwn_softc *sc) 29778b9da0f5Sstsp { 29788b9da0f5Sstsp uint16_t tx[RTWN_IQ_CAL_NRUN][2][2], rx[RTWN_IQ_CAL_NRUN][2][2]; 29798b9da0f5Sstsp int n, valid; 29803150bca2Sstsp struct rtwn_iq_cal_regs regs; 29818b9da0f5Sstsp 29828b9da0f5Sstsp valid = 0; 29833150bca2Sstsp memset(®s, 0, sizeof(regs)); 29848b9da0f5Sstsp for (n = 0; n < RTWN_IQ_CAL_NRUN; n++) { 29853150bca2Sstsp rtwn_iq_calib_run(sc, n, tx[n], rx[n], ®s); 29868b9da0f5Sstsp 29878b9da0f5Sstsp if (n == 0) 29888b9da0f5Sstsp continue; 29898b9da0f5Sstsp 29908b9da0f5Sstsp /* Valid results remain stable after consecutive runs. */ 29918b9da0f5Sstsp valid = rtwn_iq_calib_compare_results(tx[n - 1], rx[n - 1], 29928b9da0f5Sstsp tx[n], rx[n], sc->ntxchains); 29938b9da0f5Sstsp if (valid) 29948b9da0f5Sstsp break; 29958b9da0f5Sstsp } 29968b9da0f5Sstsp 29978b9da0f5Sstsp if (valid) { 29988b9da0f5Sstsp rtwn_iq_calib_write_results(sc, tx[n][0], rx[n][0], 0); 29998b9da0f5Sstsp if (sc->ntxchains > 1) 30008b9da0f5Sstsp rtwn_iq_calib_write_results(sc, tx[n][1], rx[n][1], 1); 30018b9da0f5Sstsp } 30028b9da0f5Sstsp } 30038b9da0f5Sstsp #undef RTWN_IQ_CAL_NRUN 30048b9da0f5Sstsp 30058b9da0f5Sstsp void 30068b9da0f5Sstsp rtwn_lc_calib(struct rtwn_softc *sc) 30078b9da0f5Sstsp { 30088b9da0f5Sstsp uint32_t rf_ac[2]; 30098b9da0f5Sstsp uint8_t txmode; 30108b9da0f5Sstsp int i; 30118b9da0f5Sstsp 30128b9da0f5Sstsp txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3); 30138b9da0f5Sstsp if ((txmode & 0x70) != 0) { 30148b9da0f5Sstsp /* Disable all continuous Tx. */ 30158b9da0f5Sstsp rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70); 30168b9da0f5Sstsp 30178b9da0f5Sstsp /* Set RF mode to standby mode. */ 30188b9da0f5Sstsp for (i = 0; i < sc->nrxchains; i++) { 30198b9da0f5Sstsp rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC); 30208b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_AC, 30218b9da0f5Sstsp RW(rf_ac[i], R92C_RF_AC_MODE, 30228b9da0f5Sstsp R92C_RF_AC_MODE_STANDBY)); 30238b9da0f5Sstsp } 30248b9da0f5Sstsp } else { 30258b9da0f5Sstsp /* Block all Tx queues. */ 302656fbb35bSkevlo rtwn_write_1(sc, R92C_TXPAUSE, R92C_TXPAUSE_ALL); 30278b9da0f5Sstsp } 30288b9da0f5Sstsp /* Start calibration. */ 30298b9da0f5Sstsp rtwn_rf_write(sc, 0, R92C_RF_CHNLBW, 30308b9da0f5Sstsp rtwn_rf_read(sc, 0, R92C_RF_CHNLBW) | R92C_RF_CHNLBW_LCSTART); 30318b9da0f5Sstsp 30328b9da0f5Sstsp /* Give calibration the time to complete. */ 30338b9da0f5Sstsp DELAY(100); 30348b9da0f5Sstsp 30358b9da0f5Sstsp /* Restore configuration. */ 30368b9da0f5Sstsp if ((txmode & 0x70) != 0) { 30378b9da0f5Sstsp /* Restore Tx mode. */ 30388b9da0f5Sstsp rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode); 30398b9da0f5Sstsp /* Restore RF mode. */ 30408b9da0f5Sstsp for (i = 0; i < sc->nrxchains; i++) 30418b9da0f5Sstsp rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]); 30428b9da0f5Sstsp } else { 30438b9da0f5Sstsp /* Unblock all Tx queues. */ 30444192ff9bSstsp rtwn_write_1(sc, R92C_TXPAUSE, 0x00); 30458b9da0f5Sstsp } 30468b9da0f5Sstsp } 30478b9da0f5Sstsp 30488b9da0f5Sstsp void 30498b9da0f5Sstsp rtwn_temp_calib(struct rtwn_softc *sc) 30508b9da0f5Sstsp { 305190540544Sjmatthew int temp, t_meter_reg, t_meter_val; 305290540544Sjmatthew 305390540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 305490540544Sjmatthew t_meter_reg = R92E_RF_T_METER; 305590540544Sjmatthew t_meter_val = 0x37cf8; 305690540544Sjmatthew } else { 305790540544Sjmatthew t_meter_reg = R92C_RF_T_METER; 305890540544Sjmatthew t_meter_val = 0x60; 305990540544Sjmatthew } 30608b9da0f5Sstsp 30618b9da0f5Sstsp if (sc->thcal_state == 0) { 30628b9da0f5Sstsp /* Start measuring temperature. */ 306390540544Sjmatthew rtwn_rf_write(sc, 0, t_meter_reg, t_meter_val); 30648b9da0f5Sstsp sc->thcal_state = 1; 30658b9da0f5Sstsp return; 30668b9da0f5Sstsp } 30678b9da0f5Sstsp sc->thcal_state = 0; 30688b9da0f5Sstsp 30698b9da0f5Sstsp /* Read measured temperature. */ 307090540544Sjmatthew temp = rtwn_rf_read(sc, 0, t_meter_reg) & 0x1f; 30718b9da0f5Sstsp if (temp == 0) /* Read failed, skip. */ 30728b9da0f5Sstsp return; 30738b9da0f5Sstsp DPRINTFN(2, ("temperature=%d\n", temp)); 30748b9da0f5Sstsp 30758b9da0f5Sstsp /* 30768b9da0f5Sstsp * Redo IQ and LC calibration if temperature changed significantly 30778b9da0f5Sstsp * since last calibration. 30788b9da0f5Sstsp */ 30798b9da0f5Sstsp if (sc->thcal_lctemp == 0) { 30808b9da0f5Sstsp /* First calibration is performed in rtwn_init(). */ 30818b9da0f5Sstsp sc->thcal_lctemp = temp; 30828b9da0f5Sstsp } else if (abs(temp - sc->thcal_lctemp) > 1) { 30838b9da0f5Sstsp DPRINTF(("IQ/LC calib triggered by temp: %d -> %d\n", 30848b9da0f5Sstsp sc->thcal_lctemp, temp)); 30858b9da0f5Sstsp rtwn_iq_calib(sc); 30868b9da0f5Sstsp rtwn_lc_calib(sc); 30878b9da0f5Sstsp /* Record temperature of last calibration. */ 30888b9da0f5Sstsp sc->thcal_lctemp = temp; 30898b9da0f5Sstsp } 30908b9da0f5Sstsp } 30918b9da0f5Sstsp 309223340becSstsp void 309323340becSstsp rtwn_enable_intr(struct rtwn_softc *sc) 309423340becSstsp { 309590540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 30968bca19d0Skevlo rtwn_write_4(sc, R88E_HISR, 0xffffffff); 30978bca19d0Skevlo rtwn_write_4(sc, R88E_HISRE, 0xffffffff); 309890540544Sjmatthew rtwn_write_4(sc, R88E_HIMR, 0); 309990540544Sjmatthew rtwn_write_4(sc, R88E_HIMRE, 0); 310090540544Sjmatthew } else if (sc->chip & RTWN_CHIP_88E) { 310123340becSstsp rtwn_write_4(sc, R88E_HISR, 0xffffffff); 3102d8e41173Sjmatthew if (sc->chip & RTWN_CHIP_USB) { 310323340becSstsp rtwn_write_4(sc, R88E_HIMR, R88E_HIMR_CPWM | 310423340becSstsp R88E_HIMR_CPWM2 | R88E_HIMR_TBDER | 310523340becSstsp R88E_HIMR_PSTIMEOUT); 310623340becSstsp rtwn_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW | 310723340becSstsp R88E_HIMRE_TXFOVW | R88E_HIMRE_RXERR | 310823340becSstsp R88E_HIMRE_TXERR); 3109d8e41173Sjmatthew } else { 3110d8e41173Sjmatthew rtwn_write_4(sc, R88E_HIMR, 3111d8e41173Sjmatthew RTWN_88E_INT_ENABLE); 3112d8e41173Sjmatthew rtwn_write_4(sc, R88E_HIMRE, 3113d8e41173Sjmatthew R88E_HIMRE_RXFOVW); 3114d8e41173Sjmatthew rtwn_write_1(sc, R92C_C2HEVT_CLEAR, 0); 3115d8e41173Sjmatthew rtwn_write_4(sc, R92C_HSIMR, 3116d8e41173Sjmatthew R88E_HSIMR_PDN_INT_EN | R88E_HSIMR_RON_INT_EN); 3117d8e41173Sjmatthew } 3118d8e41173Sjmatthew 311923340becSstsp if (sc->chip & RTWN_CHIP_USB) { 312023340becSstsp rtwn_write_1(sc, R92C_USB_SPECIAL_OPTION, 312123340becSstsp rtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) | 312223340becSstsp R92C_USB_SPECIAL_OPTION_INT_BULK_SEL); 312323340becSstsp } 312423340becSstsp } else { 312523340becSstsp uint32_t imask = 0; 312623340becSstsp 312723340becSstsp if (sc->chip & RTWN_CHIP_USB) 312823340becSstsp imask = 0xffffffff; 312923340becSstsp else if (sc->chip & RTWN_CHIP_PCI) 3130d8e41173Sjmatthew imask = RTWN_92C_INT_ENABLE; 313123340becSstsp else 313223340becSstsp panic("unknown chip type 0x%x", sc->chip); 313323340becSstsp 313423340becSstsp /* CLear pending interrupts. */ 313523340becSstsp rtwn_write_4(sc, R92C_HISR, 0xffffffff); 313623340becSstsp 313723340becSstsp /* Enable interrupts. */ 313823340becSstsp rtwn_write_4(sc, R92C_HIMR, imask); 313923340becSstsp } 314023340becSstsp } 314123340becSstsp 314223340becSstsp void 314323340becSstsp rtwn_disable_intr(struct rtwn_softc *sc) 314423340becSstsp { 314523340becSstsp if (sc->chip & RTWN_CHIP_88E) { 314623340becSstsp rtwn_write_4(sc, R88E_HISR, 0x00000000); 314723340becSstsp rtwn_write_4(sc, R88E_HIMR, 0x00000000); 314823340becSstsp rtwn_write_4(sc, R88E_HIMRE, 0x00000000); 314923340becSstsp if (sc->chip & RTWN_CHIP_USB) { 315023340becSstsp rtwn_write_1(sc, R92C_USB_SPECIAL_OPTION, 315123340becSstsp rtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) & 315223340becSstsp ~R92C_USB_SPECIAL_OPTION_INT_BULK_SEL); 315323340becSstsp } 315423340becSstsp } else { 315523340becSstsp rtwn_write_4(sc, R92C_HISR, 0x00000000); 315623340becSstsp rtwn_write_4(sc, R92C_HIMR, 0x00000000); 315723340becSstsp } 315823340becSstsp } 315923340becSstsp 31608b9da0f5Sstsp int 31618b9da0f5Sstsp rtwn_init(struct ifnet *ifp) 31628b9da0f5Sstsp { 31638b9da0f5Sstsp struct rtwn_softc *sc = ifp->if_softc; 31648b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 31658b9da0f5Sstsp uint32_t reg; 31668b9da0f5Sstsp int i, error; 31678b9da0f5Sstsp 31688b9da0f5Sstsp /* Init firmware commands ring. */ 31698b9da0f5Sstsp sc->fwcur = 0; 31708b9da0f5Sstsp 317123340becSstsp error = sc->sc_ops.alloc_buffers(sc->sc_ops.cookie); 317223340becSstsp if (error) 317323340becSstsp goto fail; 317423340becSstsp 31758b9da0f5Sstsp /* Power on adapter. */ 31765fda900fSstsp error = sc->sc_ops.power_on(sc->sc_ops.cookie); 31778b9da0f5Sstsp if (error != 0) { 31788b9da0f5Sstsp printf("%s: could not power on adapter\n", 31798b9da0f5Sstsp sc->sc_pdev->dv_xname); 31808b9da0f5Sstsp goto fail; 31818b9da0f5Sstsp } 31828b9da0f5Sstsp 31838b9da0f5Sstsp /* Initialize DMA. */ 3184b81d0ffeSstsp error = sc->sc_ops.dma_init(sc->sc_ops.cookie); 31858b9da0f5Sstsp if (error != 0) { 31868b9da0f5Sstsp printf("%s: could not initialize DMA\n", 31878b9da0f5Sstsp sc->sc_pdev->dv_xname); 31888b9da0f5Sstsp goto fail; 31898b9da0f5Sstsp } 31908b9da0f5Sstsp 31918b9da0f5Sstsp /* Set info size in Rx descriptors (in 64-bit words). */ 31928b9da0f5Sstsp rtwn_write_1(sc, R92C_RX_DRVINFO_SZ, 4); 31938b9da0f5Sstsp 3194067851b1Skevlo if ((sc->chip & RTWN_CHIP_USB) && !(sc->chip & RTWN_CHIP_88F)) { 319523340becSstsp /* Init interrupts. */ 319623340becSstsp rtwn_enable_intr(sc); 319723340becSstsp } else if (sc->chip & RTWN_CHIP_PCI) { 319823340becSstsp rtwn_disable_intr(sc); 319923340becSstsp } 32008b9da0f5Sstsp 32018b9da0f5Sstsp /* Set MAC address. */ 32028b9da0f5Sstsp IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); 32038b9da0f5Sstsp for (i = 0; i < IEEE80211_ADDR_LEN; i++) 32048b9da0f5Sstsp rtwn_write_1(sc, R92C_MACID + i, ic->ic_myaddr[i]); 32058b9da0f5Sstsp 32068b9da0f5Sstsp /* Set initial network type. */ 320756fbb35bSkevlo rtwn_set_nettype(sc, IEEE80211_M_MONITOR); 32088b9da0f5Sstsp 32098b9da0f5Sstsp rtwn_rxfilter_init(sc); 32108b9da0f5Sstsp 32118b9da0f5Sstsp reg = rtwn_read_4(sc, R92C_RRSR); 321223340becSstsp if (sc->chip & RTWN_CHIP_USB) { 321323340becSstsp reg = RW(reg, R92C_RRSR_RATE_BITMAP, 321423340becSstsp R92C_RRSR_RATE_CCK_ONLY_1M); 321523340becSstsp } else { 32168b9da0f5Sstsp reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_ALL); 321723340becSstsp } 32188b9da0f5Sstsp rtwn_write_4(sc, R92C_RRSR, reg); 32198b9da0f5Sstsp 32208b9da0f5Sstsp /* Set short/long retry limits. */ 322123340becSstsp if (sc->chip & RTWN_CHIP_USB) { 322223340becSstsp rtwn_write_2(sc, R92C_RL, 322323340becSstsp SM(R92C_RL_SRL, 0x30) | SM(R92C_RL_LRL, 0x30)); 322423340becSstsp } else { 32258b9da0f5Sstsp rtwn_write_2(sc, R92C_RL, 32268b9da0f5Sstsp SM(R92C_RL_SRL, 0x07) | SM(R92C_RL_LRL, 0x07)); 322723340becSstsp } 32288b9da0f5Sstsp 32298b9da0f5Sstsp /* Initialize EDCA parameters. */ 32308b9da0f5Sstsp rtwn_edca_init(sc); 32318b9da0f5Sstsp 32328b9da0f5Sstsp /* Set data and response automatic rate fallback retry counts. */ 32335fb81a6cSkevlo rtwn_rate_fallback_init(sc); 32348b9da0f5Sstsp 323523340becSstsp if (sc->chip & RTWN_CHIP_USB) { 323623340becSstsp rtwn_write_1(sc, R92C_FWHW_TXQ_CTRL, 323723340becSstsp rtwn_read_1(sc, R92C_FWHW_TXQ_CTRL) | 323823340becSstsp R92C_FWHW_TXQ_CTRL_AMPDU_RTY_NEW); 323923340becSstsp } else { 32408b9da0f5Sstsp rtwn_write_2(sc, R92C_FWHW_TXQ_CTRL, 0x1f80); 324123340becSstsp } 32428b9da0f5Sstsp 32438b9da0f5Sstsp /* Set ACK timeout. */ 32448b9da0f5Sstsp rtwn_write_1(sc, R92C_ACKTO, 0x40); 32458b9da0f5Sstsp 324623340becSstsp /* Setup USB aggregation. */ 32472d2a7e26Skevlo if (sc->chip & RTWN_CHIP_USB) 32482d2a7e26Skevlo sc->sc_ops.aggr_init(sc->sc_ops.cookie); 324923340becSstsp 32508b9da0f5Sstsp /* Initialize beacon parameters. */ 32515fb81a6cSkevlo rtwn_write_2(sc, R92C_BCN_CTRL, 32525fb81a6cSkevlo (R92C_BCN_CTRL_DIS_TSF_UDT0 << 8) | R92C_BCN_CTRL_DIS_TSF_UDT0); 32538b9da0f5Sstsp rtwn_write_2(sc, R92C_TBTT_PROHIBIT, 0x6404); 3254067851b1Skevlo if (!(sc->chip & RTWN_CHIP_88F)) 32555fb81a6cSkevlo rtwn_write_1(sc, R92C_DRVERLYINT, R92C_DRVERLYINT_INIT_TIME); 32565fb81a6cSkevlo rtwn_write_1(sc, R92C_BCNDMATIM, R92C_BCNDMATIM_INIT_TIME); 32578b9da0f5Sstsp rtwn_write_2(sc, R92C_BCNTCFG, 0x660f); 32588b9da0f5Sstsp 3259067851b1Skevlo if (!(sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) { 32608b9da0f5Sstsp /* Setup AMPDU aggregation. */ 32618b9da0f5Sstsp rtwn_write_4(sc, R92C_AGGLEN_LMT, 0x99997631); /* MCS7~0 */ 32628b9da0f5Sstsp rtwn_write_1(sc, R92C_AGGR_BREAK_TIME, 0x16); 326323340becSstsp rtwn_write_2(sc, R92C_MAX_AGGR_NUM, 0x0708); 32648b9da0f5Sstsp 32658b9da0f5Sstsp rtwn_write_1(sc, R92C_BCN_MAX_ERR, 0xff); 326623340becSstsp } 32678b9da0f5Sstsp 32685fb81a6cSkevlo if (sc->chip & RTWN_CHIP_PCI) { 32695fb81a6cSkevlo /* Reset H2C protection register. */ 32708b9da0f5Sstsp rtwn_write_4(sc, R92C_MCUTST_1, 0x0); 327123340becSstsp } 32728b9da0f5Sstsp 32738b9da0f5Sstsp /* Load 8051 microcode. */ 32748b9da0f5Sstsp error = rtwn_load_firmware(sc); 32758b9da0f5Sstsp if (error != 0) 32768b9da0f5Sstsp goto fail; 32778b9da0f5Sstsp 32788b9da0f5Sstsp /* Initialize MAC/BB/RF blocks. */ 32795fda900fSstsp sc->sc_ops.mac_init(sc->sc_ops.cookie); 32805fda900fSstsp sc->sc_ops.bb_init(sc->sc_ops.cookie); 32818b9da0f5Sstsp rtwn_rf_init(sc); 32828b9da0f5Sstsp 3283067851b1Skevlo if (sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E)) { 328423340becSstsp rtwn_write_2(sc, R92C_CR, 328523340becSstsp rtwn_read_2(sc, R92C_CR) | R92C_CR_MACTXEN | 328623340becSstsp R92C_CR_MACRXEN); 328723340becSstsp } 328823340becSstsp 32898b9da0f5Sstsp /* Turn CCK and OFDM blocks on. */ 32908b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); 32918b9da0f5Sstsp reg |= R92C_RFMOD_CCK_EN; 32928b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); 32938b9da0f5Sstsp reg = rtwn_bb_read(sc, R92C_FPGA0_RFMOD); 32948b9da0f5Sstsp reg |= R92C_RFMOD_OFDM_EN; 32958b9da0f5Sstsp rtwn_bb_write(sc, R92C_FPGA0_RFMOD, reg); 32968b9da0f5Sstsp 32978b9da0f5Sstsp /* Clear per-station keys table. */ 32988b9da0f5Sstsp rtwn_cam_init(sc); 32998b9da0f5Sstsp 3300a299d652Sjmatthew /* Enable decryption / encryption. */ 3301a299d652Sjmatthew if (sc->chip & RTWN_CHIP_USB) { 3302a299d652Sjmatthew rtwn_write_2(sc, R92C_SECCFG, 3303a299d652Sjmatthew R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF | 3304a299d652Sjmatthew R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXENC_ENA | 3305a299d652Sjmatthew R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF); 3306a299d652Sjmatthew } 3307a299d652Sjmatthew 33088b9da0f5Sstsp /* Enable hardware sequence numbering. */ 33098b9da0f5Sstsp rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff); 33108b9da0f5Sstsp 331190540544Sjmatthew if (sc->chip & RTWN_CHIP_92E) { 331290540544Sjmatthew rtwn_write_1(sc, R92C_QUEUE_CTRL, 331390540544Sjmatthew rtwn_read_1(sc, R92C_QUEUE_CTRL) & ~0x08); 331490540544Sjmatthew } 331590540544Sjmatthew 33168b9da0f5Sstsp /* Perform LO and IQ calibrations. */ 33178b9da0f5Sstsp rtwn_iq_calib(sc); 33188b9da0f5Sstsp /* Perform LC calibration. */ 33198b9da0f5Sstsp rtwn_lc_calib(sc); 33208b9da0f5Sstsp 332123340becSstsp /* Fix USB interference issue. */ 332290540544Sjmatthew if (sc->chip & RTWN_CHIP_USB) { 3323067851b1Skevlo if (!(sc->chip & 3324067851b1Skevlo (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) { 332523340becSstsp rtwn_write_1(sc, 0xfe40, 0xe0); 332623340becSstsp rtwn_write_1(sc, 0xfe41, 0x8d); 332723340becSstsp rtwn_write_1(sc, 0xfe42, 0x80); 332823340becSstsp 33298b9da0f5Sstsp rtwn_pa_bias_init(sc); 333023340becSstsp } 333190540544Sjmatthew } 33328b9da0f5Sstsp 33338b9da0f5Sstsp /* Initialize GPIO setting. */ 33348b9da0f5Sstsp rtwn_write_1(sc, R92C_GPIO_MUXCFG, 33358b9da0f5Sstsp rtwn_read_1(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENBT); 33368b9da0f5Sstsp 33378b9da0f5Sstsp /* Fix for lower temperature. */ 3338067851b1Skevlo if (!(sc->chip & (RTWN_CHIP_88E | RTWN_CHIP_88F | RTWN_CHIP_92E))) 33398b9da0f5Sstsp rtwn_write_1(sc, 0x15, 0xe9); 33408b9da0f5Sstsp 33418b9da0f5Sstsp /* Set default channel. */ 33428b9da0f5Sstsp ic->ic_bss->ni_chan = ic->ic_ibss_chan; 33438b9da0f5Sstsp rtwn_set_chan(sc, ic->ic_ibss_chan, NULL); 33448b9da0f5Sstsp 334523340becSstsp if (sc->chip & RTWN_CHIP_PCI) 334623340becSstsp rtwn_enable_intr(sc); 334723340becSstsp 334823340becSstsp error = sc->sc_ops.init(sc->sc_ops.cookie); 334923340becSstsp if (error) 335023340becSstsp goto fail; 33518b9da0f5Sstsp 33528b9da0f5Sstsp /* We're ready to go. */ 33538b9da0f5Sstsp ifq_clr_oactive(&ifp->if_snd); 33548b9da0f5Sstsp ifp->if_flags |= IFF_RUNNING; 33558b9da0f5Sstsp 3356a299d652Sjmatthew if ((ic->ic_flags & IEEE80211_F_WEPON) && 3357a299d652Sjmatthew (sc->chip & RTWN_CHIP_USB)) { 33588b9da0f5Sstsp /* Install WEP keys. */ 33598b9da0f5Sstsp for (i = 0; i < IEEE80211_WEP_NKID; i++) 336023340becSstsp ic->ic_set_key(ic, NULL, &ic->ic_nw_keys[i]); 336123340becSstsp sc->sc_ops.wait_async(sc->sc_ops.cookie); 33628b9da0f5Sstsp } 3363a299d652Sjmatthew 33648b9da0f5Sstsp if (ic->ic_opmode == IEEE80211_M_MONITOR) 33658b9da0f5Sstsp ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 33668b9da0f5Sstsp else 33678b9da0f5Sstsp ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 33688b9da0f5Sstsp return (0); 33698b9da0f5Sstsp fail: 33708b9da0f5Sstsp rtwn_stop(ifp); 33718b9da0f5Sstsp return (error); 33728b9da0f5Sstsp } 33738b9da0f5Sstsp 33748b9da0f5Sstsp void 33758b9da0f5Sstsp rtwn_init_task(void *arg1) 33768b9da0f5Sstsp { 33778b9da0f5Sstsp struct rtwn_softc *sc = arg1; 33788b9da0f5Sstsp struct ifnet *ifp = &sc->sc_ic.ic_if; 33798b9da0f5Sstsp int s; 33808b9da0f5Sstsp 33818b9da0f5Sstsp s = splnet(); 33828b9da0f5Sstsp while (sc->sc_flags & RTWN_FLAG_BUSY) 338303604742Smpi tsleep_nsec(&sc->sc_flags, 0, "rtwnpwr", INFSLP); 33848b9da0f5Sstsp sc->sc_flags |= RTWN_FLAG_BUSY; 33858b9da0f5Sstsp 33868b9da0f5Sstsp rtwn_stop(ifp); 33878b9da0f5Sstsp 33888b9da0f5Sstsp if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP) 33898b9da0f5Sstsp rtwn_init(ifp); 33908b9da0f5Sstsp 33918b9da0f5Sstsp sc->sc_flags &= ~RTWN_FLAG_BUSY; 33928b9da0f5Sstsp wakeup(&sc->sc_flags); 33938b9da0f5Sstsp splx(s); 33948b9da0f5Sstsp } 33958b9da0f5Sstsp 33968b9da0f5Sstsp void 33978b9da0f5Sstsp rtwn_stop(struct ifnet *ifp) 33988b9da0f5Sstsp { 33998b9da0f5Sstsp struct rtwn_softc *sc = ifp->if_softc; 34008b9da0f5Sstsp struct ieee80211com *ic = &sc->sc_ic; 34018b9da0f5Sstsp int s; 34028b9da0f5Sstsp 34038b9da0f5Sstsp sc->sc_tx_timer = 0; 34048b9da0f5Sstsp ifp->if_timer = 0; 34058b9da0f5Sstsp ifp->if_flags &= ~IFF_RUNNING; 34068b9da0f5Sstsp ifq_clr_oactive(&ifp->if_snd); 34078b9da0f5Sstsp 34088b9da0f5Sstsp s = splnet(); 34098b9da0f5Sstsp ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 341023340becSstsp splx(s); 341123340becSstsp 341223340becSstsp sc->sc_ops.wait_async(sc->sc_ops.cookie); 341323340becSstsp 341423340becSstsp s = splnet(); 34158b9da0f5Sstsp 3416e9f037d1Sstsp sc->sc_ops.cancel_scan(sc->sc_ops.cookie); 3417e9f037d1Sstsp sc->sc_ops.cancel_calib(sc->sc_ops.cookie); 34188b9da0f5Sstsp 34198b9da0f5Sstsp task_del(systq, &sc->init_task); 34208b9da0f5Sstsp 34218b9da0f5Sstsp splx(s); 34228b9da0f5Sstsp 342323340becSstsp sc->sc_ops.stop(sc->sc_ops.cookie); 34248b9da0f5Sstsp } 3425