1*7453645fSAndriy Voskoboinyk /* $OpenBSD: if_rtwn.c,v 1.6 2015/08/28 00:03:53 deraadt Exp $ */ 2*7453645fSAndriy Voskoboinyk 3*7453645fSAndriy Voskoboinyk /*- 4*7453645fSAndriy Voskoboinyk * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> 5*7453645fSAndriy Voskoboinyk * Copyright (c) 2015 Stefan Sperling <stsp@openbsd.org> 6*7453645fSAndriy Voskoboinyk * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org> 7*7453645fSAndriy Voskoboinyk * 8*7453645fSAndriy Voskoboinyk * Permission to use, copy, modify, and distribute this software for any 9*7453645fSAndriy Voskoboinyk * purpose with or without fee is hereby granted, provided that the above 10*7453645fSAndriy Voskoboinyk * copyright notice and this permission notice appear in all copies. 11*7453645fSAndriy Voskoboinyk * 12*7453645fSAndriy Voskoboinyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13*7453645fSAndriy Voskoboinyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14*7453645fSAndriy Voskoboinyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15*7453645fSAndriy Voskoboinyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16*7453645fSAndriy Voskoboinyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17*7453645fSAndriy Voskoboinyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18*7453645fSAndriy Voskoboinyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19*7453645fSAndriy Voskoboinyk */ 20*7453645fSAndriy Voskoboinyk 21*7453645fSAndriy Voskoboinyk #include <sys/cdefs.h> 22*7453645fSAndriy Voskoboinyk __FBSDID("$FreeBSD$"); 23*7453645fSAndriy Voskoboinyk 24*7453645fSAndriy Voskoboinyk #include "opt_wlan.h" 25*7453645fSAndriy Voskoboinyk 26*7453645fSAndriy Voskoboinyk #include <sys/param.h> 27*7453645fSAndriy Voskoboinyk #include <sys/lock.h> 28*7453645fSAndriy Voskoboinyk #include <sys/mutex.h> 29*7453645fSAndriy Voskoboinyk #include <sys/mbuf.h> 30*7453645fSAndriy Voskoboinyk #include <sys/kernel.h> 31*7453645fSAndriy Voskoboinyk #include <sys/socket.h> 32*7453645fSAndriy Voskoboinyk #include <sys/systm.h> 33*7453645fSAndriy Voskoboinyk #include <sys/malloc.h> 34*7453645fSAndriy Voskoboinyk #include <sys/queue.h> 35*7453645fSAndriy Voskoboinyk #include <sys/taskqueue.h> 36*7453645fSAndriy Voskoboinyk #include <sys/bus.h> 37*7453645fSAndriy Voskoboinyk #include <sys/endian.h> 38*7453645fSAndriy Voskoboinyk #include <sys/linker.h> 39*7453645fSAndriy Voskoboinyk 40*7453645fSAndriy Voskoboinyk #include <machine/bus.h> 41*7453645fSAndriy Voskoboinyk #include <machine/resource.h> 42*7453645fSAndriy Voskoboinyk #include <sys/rman.h> 43*7453645fSAndriy Voskoboinyk 44*7453645fSAndriy Voskoboinyk #include <net/if.h> 45*7453645fSAndriy Voskoboinyk #include <net/ethernet.h> 46*7453645fSAndriy Voskoboinyk #include <net/if_media.h> 47*7453645fSAndriy Voskoboinyk 48*7453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h> 49*7453645fSAndriy Voskoboinyk #include <net80211/ieee80211_radiotap.h> 50*7453645fSAndriy Voskoboinyk 51*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h> 52*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h> 53*7453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h> 54*7453645fSAndriy Voskoboinyk 55*7453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_var.h> 56*7453645fSAndriy Voskoboinyk 57*7453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce.h> 58*7453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce_reg.h> 59*7453645fSAndriy Voskoboinyk 60*7453645fSAndriy Voskoboinyk 61*7453645fSAndriy Voskoboinyk /* Registers to save and restore during IQ calibration. */ 62*7453645fSAndriy Voskoboinyk struct r92ce_iq_cal_reg_vals { 63*7453645fSAndriy Voskoboinyk uint32_t adda[16]; 64*7453645fSAndriy Voskoboinyk uint8_t txpause; 65*7453645fSAndriy Voskoboinyk uint8_t bcn_ctrl[2]; 66*7453645fSAndriy Voskoboinyk uint32_t gpio_muxcfg; 67*7453645fSAndriy Voskoboinyk uint32_t ofdm0_trxpathena; 68*7453645fSAndriy Voskoboinyk uint32_t ofdm0_trmuxpar; 69*7453645fSAndriy Voskoboinyk uint32_t fpga0_rfifacesw1; 70*7453645fSAndriy Voskoboinyk }; 71*7453645fSAndriy Voskoboinyk 72*7453645fSAndriy Voskoboinyk /* XXX 92CU? */ 73*7453645fSAndriy Voskoboinyk static int 74*7453645fSAndriy Voskoboinyk r92ce_iq_calib_chain(struct rtwn_softc *sc, int chain, uint16_t tx[2], 75*7453645fSAndriy Voskoboinyk uint16_t rx[2]) 76*7453645fSAndriy Voskoboinyk { 77*7453645fSAndriy Voskoboinyk uint32_t status; 78*7453645fSAndriy Voskoboinyk int offset = chain * 0x20; 79*7453645fSAndriy Voskoboinyk 80*7453645fSAndriy Voskoboinyk if (chain == 0) { /* IQ calibration for chain 0. */ 81*7453645fSAndriy Voskoboinyk /* IQ calibration settings for chain 0. */ 82*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe30, 0x10008c1f); 83*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe34, 0x10008c1f); 84*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe38, 0x82140102); 85*7453645fSAndriy Voskoboinyk 86*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) { 87*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe3c, 0x28160202); /* 2T */ 88*7453645fSAndriy Voskoboinyk /* IQ calibration settings for chain 1. */ 89*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe50, 0x10008c22); 90*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe54, 0x10008c22); 91*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe58, 0x82140102); 92*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe5c, 0x28160202); 93*7453645fSAndriy Voskoboinyk } else 94*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe3c, 0x28160502); /* 1T */ 95*7453645fSAndriy Voskoboinyk 96*7453645fSAndriy Voskoboinyk /* LO calibration settings. */ 97*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe4c, 0x001028d1); 98*7453645fSAndriy Voskoboinyk /* We're doing LO and IQ calibration in one shot. */ 99*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe48, 0xf9000000); 100*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe48, 0xf8000000); 101*7453645fSAndriy Voskoboinyk 102*7453645fSAndriy Voskoboinyk } else { /* IQ calibration for chain 1. */ 103*7453645fSAndriy Voskoboinyk /* We're doing LO and IQ calibration in one shot. */ 104*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe60, 0x00000002); 105*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0xe60, 0x00000000); 106*7453645fSAndriy Voskoboinyk } 107*7453645fSAndriy Voskoboinyk 108*7453645fSAndriy Voskoboinyk /* Give LO and IQ calibrations the time to complete. */ 109*7453645fSAndriy Voskoboinyk rtwn_delay(sc, 1000); 110*7453645fSAndriy Voskoboinyk 111*7453645fSAndriy Voskoboinyk /* Read IQ calibration status. */ 112*7453645fSAndriy Voskoboinyk status = rtwn_bb_read(sc, 0xeac); 113*7453645fSAndriy Voskoboinyk 114*7453645fSAndriy Voskoboinyk if (status & (1 << (28 + chain * 3))) 115*7453645fSAndriy Voskoboinyk return (0); /* Tx failed. */ 116*7453645fSAndriy Voskoboinyk /* Read Tx IQ calibration results. */ 117*7453645fSAndriy Voskoboinyk tx[0] = (rtwn_bb_read(sc, 0xe94 + offset) >> 16) & 0x3ff; 118*7453645fSAndriy Voskoboinyk tx[1] = (rtwn_bb_read(sc, 0xe9c + offset) >> 16) & 0x3ff; 119*7453645fSAndriy Voskoboinyk if (tx[0] == 0x142 || tx[1] == 0x042) 120*7453645fSAndriy Voskoboinyk return (0); /* Tx failed. */ 121*7453645fSAndriy Voskoboinyk 122*7453645fSAndriy Voskoboinyk if (status & (1 << (27 + chain * 3))) 123*7453645fSAndriy Voskoboinyk return (1); /* Rx failed. */ 124*7453645fSAndriy Voskoboinyk /* Read Rx IQ calibration results. */ 125*7453645fSAndriy Voskoboinyk rx[0] = (rtwn_bb_read(sc, 0xea4 + offset) >> 16) & 0x3ff; 126*7453645fSAndriy Voskoboinyk rx[1] = (rtwn_bb_read(sc, 0xeac + offset) >> 16) & 0x3ff; 127*7453645fSAndriy Voskoboinyk if (rx[0] == 0x132 || rx[1] == 0x036) 128*7453645fSAndriy Voskoboinyk return (1); /* Rx failed. */ 129*7453645fSAndriy Voskoboinyk 130*7453645fSAndriy Voskoboinyk return (3); /* Both Tx and Rx succeeded. */ 131*7453645fSAndriy Voskoboinyk } 132*7453645fSAndriy Voskoboinyk 133*7453645fSAndriy Voskoboinyk static void 134*7453645fSAndriy Voskoboinyk r92ce_iq_calib_run(struct rtwn_softc *sc, int n, uint16_t tx[2][2], 135*7453645fSAndriy Voskoboinyk uint16_t rx[2][2], struct r92ce_iq_cal_reg_vals *vals) 136*7453645fSAndriy Voskoboinyk { 137*7453645fSAndriy Voskoboinyk /* Registers to save and restore during IQ calibration. */ 138*7453645fSAndriy Voskoboinyk static const uint16_t reg_adda[16] = { 139*7453645fSAndriy Voskoboinyk 0x85c, 0xe6c, 0xe70, 0xe74, 140*7453645fSAndriy Voskoboinyk 0xe78, 0xe7c, 0xe80, 0xe84, 141*7453645fSAndriy Voskoboinyk 0xe88, 0xe8c, 0xed0, 0xed4, 142*7453645fSAndriy Voskoboinyk 0xed8, 0xedc, 0xee0, 0xeec 143*7453645fSAndriy Voskoboinyk }; 144*7453645fSAndriy Voskoboinyk int i, chain; 145*7453645fSAndriy Voskoboinyk uint32_t hssi_param1; 146*7453645fSAndriy Voskoboinyk 147*7453645fSAndriy Voskoboinyk if (n == 0) { 148*7453645fSAndriy Voskoboinyk for (i = 0; i < nitems(reg_adda); i++) 149*7453645fSAndriy Voskoboinyk vals->adda[i] = rtwn_bb_read(sc, reg_adda[i]); 150*7453645fSAndriy Voskoboinyk 151*7453645fSAndriy Voskoboinyk vals->txpause = rtwn_read_1(sc, R92C_TXPAUSE); 152*7453645fSAndriy Voskoboinyk vals->bcn_ctrl[0] = rtwn_read_1(sc, R92C_BCN_CTRL(0)); 153*7453645fSAndriy Voskoboinyk vals->bcn_ctrl[1] = rtwn_read_1(sc, R92C_BCN_CTRL(1)); 154*7453645fSAndriy Voskoboinyk vals->gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG); 155*7453645fSAndriy Voskoboinyk } 156*7453645fSAndriy Voskoboinyk 157*7453645fSAndriy Voskoboinyk if (sc->ntxchains == 1) { 158*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, reg_adda[0], 0x0b1b25a0); 159*7453645fSAndriy Voskoboinyk for (i = 1; i < nitems(reg_adda); i++) 160*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, reg_adda[i], 0x0bdb25a0); 161*7453645fSAndriy Voskoboinyk } else { 162*7453645fSAndriy Voskoboinyk for (i = 0; i < nitems(reg_adda); i++) 163*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, reg_adda[i], 0x04db25a4); 164*7453645fSAndriy Voskoboinyk } 165*7453645fSAndriy Voskoboinyk 166*7453645fSAndriy Voskoboinyk hssi_param1 = rtwn_bb_read(sc, R92C_HSSI_PARAM1(0)); 167*7453645fSAndriy Voskoboinyk if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { 168*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), 169*7453645fSAndriy Voskoboinyk hssi_param1 | R92C_HSSI_PARAM1_PI); 170*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), 171*7453645fSAndriy Voskoboinyk hssi_param1 | R92C_HSSI_PARAM1_PI); 172*7453645fSAndriy Voskoboinyk } 173*7453645fSAndriy Voskoboinyk 174*7453645fSAndriy Voskoboinyk if (n == 0) { 175*7453645fSAndriy Voskoboinyk vals->ofdm0_trxpathena = 176*7453645fSAndriy Voskoboinyk rtwn_bb_read(sc, R92C_OFDM0_TRXPATHENA); 177*7453645fSAndriy Voskoboinyk vals->ofdm0_trmuxpar = rtwn_bb_read(sc, R92C_OFDM0_TRMUXPAR); 178*7453645fSAndriy Voskoboinyk vals->fpga0_rfifacesw1 = 179*7453645fSAndriy Voskoboinyk rtwn_bb_read(sc, R92C_FPGA0_RFIFACESW(1)); 180*7453645fSAndriy Voskoboinyk } 181*7453645fSAndriy Voskoboinyk 182*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600); 183*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4); 184*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000); 185*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) { 186*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); 187*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00010000); 188*7453645fSAndriy Voskoboinyk } 189*7453645fSAndriy Voskoboinyk 190*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_TXPAUSE, 191*7453645fSAndriy Voskoboinyk R92C_TX_QUEUE_AC | R92C_TX_QUEUE_MGT | R92C_TX_QUEUE_HIGH); 192*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(0), 193*7453645fSAndriy Voskoboinyk vals->bcn_ctrl[0] & ~R92C_BCN_CTRL_EN_BCN); 194*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(1), 195*7453645fSAndriy Voskoboinyk vals->bcn_ctrl[1] & ~R92C_BCN_CTRL_EN_BCN); 196*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_GPIO_MUXCFG, 197*7453645fSAndriy Voskoboinyk vals->gpio_muxcfg & ~R92C_GPIO_MUXCFG_ENBT); 198*7453645fSAndriy Voskoboinyk 199*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0b68, 0x00080000); 200*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) 201*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0b6c, 0x00080000); 202*7453645fSAndriy Voskoboinyk 203*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e28, 0x80800000); 204*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e40, 0x01007c00); 205*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e44, 0x01004800); 206*7453645fSAndriy Voskoboinyk 207*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0b68, 0x00080000); 208*7453645fSAndriy Voskoboinyk 209*7453645fSAndriy Voskoboinyk for (chain = 0; chain < sc->ntxchains; chain++) { 210*7453645fSAndriy Voskoboinyk if (chain > 0) { 211*7453645fSAndriy Voskoboinyk /* Put chain 0 on standby. */ 212*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e28, 0x00); 213*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00010000); 214*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e28, 0x80800000); 215*7453645fSAndriy Voskoboinyk 216*7453645fSAndriy Voskoboinyk /* Enable chain 1. */ 217*7453645fSAndriy Voskoboinyk for (i = 0; i < nitems(reg_adda); i++) 218*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, reg_adda[i], 0x0b1b25a4); 219*7453645fSAndriy Voskoboinyk } 220*7453645fSAndriy Voskoboinyk 221*7453645fSAndriy Voskoboinyk /* Run IQ calibration twice. */ 222*7453645fSAndriy Voskoboinyk for (i = 0; i < 2; i++) { 223*7453645fSAndriy Voskoboinyk int ret; 224*7453645fSAndriy Voskoboinyk 225*7453645fSAndriy Voskoboinyk ret = r92ce_iq_calib_chain(sc, chain, 226*7453645fSAndriy Voskoboinyk tx[chain], rx[chain]); 227*7453645fSAndriy Voskoboinyk if (ret == 0) { 228*7453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, 229*7453645fSAndriy Voskoboinyk "%s: chain %d: Tx failed.\n", 230*7453645fSAndriy Voskoboinyk __func__, chain); 231*7453645fSAndriy Voskoboinyk tx[chain][0] = 0xff; 232*7453645fSAndriy Voskoboinyk tx[chain][1] = 0xff; 233*7453645fSAndriy Voskoboinyk rx[chain][0] = 0xff; 234*7453645fSAndriy Voskoboinyk rx[chain][1] = 0xff; 235*7453645fSAndriy Voskoboinyk } else if (ret == 1) { 236*7453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, 237*7453645fSAndriy Voskoboinyk "%s: chain %d: Rx failed.\n", 238*7453645fSAndriy Voskoboinyk __func__, chain); 239*7453645fSAndriy Voskoboinyk rx[chain][0] = 0xff; 240*7453645fSAndriy Voskoboinyk rx[chain][1] = 0xff; 241*7453645fSAndriy Voskoboinyk } else if (ret == 3) { 242*7453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, 243*7453645fSAndriy Voskoboinyk "%s: chain %d: Both Tx and Rx " 244*7453645fSAndriy Voskoboinyk "succeeded.\n", __func__, chain); 245*7453645fSAndriy Voskoboinyk } 246*7453645fSAndriy Voskoboinyk } 247*7453645fSAndriy Voskoboinyk 248*7453645fSAndriy Voskoboinyk RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, 249*7453645fSAndriy Voskoboinyk "%s: results for run %d chain %d: tx[0] 0x%x, " 250*7453645fSAndriy Voskoboinyk "tx[1] 0x%x, rx[0] 0x%x, rx[1] 0x%x\n", __func__, n, chain, 251*7453645fSAndriy Voskoboinyk tx[chain][0], tx[chain][1], rx[chain][0], rx[chain][1]); 252*7453645fSAndriy Voskoboinyk } 253*7453645fSAndriy Voskoboinyk 254*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_OFDM0_TRXPATHENA, 255*7453645fSAndriy Voskoboinyk vals->ofdm0_trxpathena); 256*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_FPGA0_RFIFACESW(1), 257*7453645fSAndriy Voskoboinyk vals->fpga0_rfifacesw1); 258*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_OFDM0_TRMUXPAR, vals->ofdm0_trmuxpar); 259*7453645fSAndriy Voskoboinyk 260*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, 0x0e28, 0x00); 261*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_LSSI_PARAM(0), 0x00032ed3); 262*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) 263*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_LSSI_PARAM(1), 0x00032ed3); 264*7453645fSAndriy Voskoboinyk 265*7453645fSAndriy Voskoboinyk if (n != 0) { 266*7453645fSAndriy Voskoboinyk if (!(hssi_param1 & R92C_HSSI_PARAM1_PI)) { 267*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_HSSI_PARAM1(0), hssi_param1); 268*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, R92C_HSSI_PARAM1(1), hssi_param1); 269*7453645fSAndriy Voskoboinyk } 270*7453645fSAndriy Voskoboinyk 271*7453645fSAndriy Voskoboinyk for (i = 0; i < nitems(reg_adda); i++) 272*7453645fSAndriy Voskoboinyk rtwn_bb_write(sc, reg_adda[i], vals->adda[i]); 273*7453645fSAndriy Voskoboinyk 274*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_TXPAUSE, vals->txpause); 275*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(0), vals->bcn_ctrl[0]); 276*7453645fSAndriy Voskoboinyk rtwn_write_1(sc, R92C_BCN_CTRL(1), vals->bcn_ctrl[1]); 277*7453645fSAndriy Voskoboinyk rtwn_write_4(sc, R92C_GPIO_MUXCFG, vals->gpio_muxcfg); 278*7453645fSAndriy Voskoboinyk } 279*7453645fSAndriy Voskoboinyk } 280*7453645fSAndriy Voskoboinyk 281*7453645fSAndriy Voskoboinyk #define RTWN_IQ_CAL_MAX_TOLERANCE 5 282*7453645fSAndriy Voskoboinyk static int 283*7453645fSAndriy Voskoboinyk r92ce_iq_calib_compare_results(struct rtwn_softc *sc, uint16_t tx1[2][2], 284*7453645fSAndriy Voskoboinyk uint16_t rx1[2][2], uint16_t tx2[2][2], uint16_t rx2[2][2]) 285*7453645fSAndriy Voskoboinyk { 286*7453645fSAndriy Voskoboinyk int chain, i, tx_ok[2], rx_ok[2]; 287*7453645fSAndriy Voskoboinyk 288*7453645fSAndriy Voskoboinyk tx_ok[0] = tx_ok[1] = rx_ok[0] = rx_ok[1] = 0; 289*7453645fSAndriy Voskoboinyk for (chain = 0; chain < sc->ntxchains; chain++) { 290*7453645fSAndriy Voskoboinyk for (i = 0; i < 2; i++) { 291*7453645fSAndriy Voskoboinyk if (tx1[chain][i] == 0xff || tx2[chain][i] == 0xff || 292*7453645fSAndriy Voskoboinyk rx1[chain][i] == 0xff || rx2[chain][i] == 0xff) 293*7453645fSAndriy Voskoboinyk continue; 294*7453645fSAndriy Voskoboinyk 295*7453645fSAndriy Voskoboinyk tx_ok[chain] = (abs(tx1[chain][i] - tx2[chain][i]) <= 296*7453645fSAndriy Voskoboinyk RTWN_IQ_CAL_MAX_TOLERANCE); 297*7453645fSAndriy Voskoboinyk 298*7453645fSAndriy Voskoboinyk rx_ok[chain] = (abs(rx1[chain][i] - rx2[chain][i]) <= 299*7453645fSAndriy Voskoboinyk RTWN_IQ_CAL_MAX_TOLERANCE); 300*7453645fSAndriy Voskoboinyk } 301*7453645fSAndriy Voskoboinyk } 302*7453645fSAndriy Voskoboinyk 303*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) 304*7453645fSAndriy Voskoboinyk return (tx_ok[0] && tx_ok[1] && rx_ok[0] && rx_ok[1]); 305*7453645fSAndriy Voskoboinyk else 306*7453645fSAndriy Voskoboinyk return (tx_ok[0] && rx_ok[0]); 307*7453645fSAndriy Voskoboinyk } 308*7453645fSAndriy Voskoboinyk #undef RTWN_IQ_CAL_MAX_TOLERANCE 309*7453645fSAndriy Voskoboinyk 310*7453645fSAndriy Voskoboinyk static void 311*7453645fSAndriy Voskoboinyk r92ce_iq_calib_write_results(struct rtwn_softc *sc, uint16_t tx[2], 312*7453645fSAndriy Voskoboinyk uint16_t rx[2], int chain) 313*7453645fSAndriy Voskoboinyk { 314*7453645fSAndriy Voskoboinyk uint32_t reg, val, x; 315*7453645fSAndriy Voskoboinyk long y, tx_c; 316*7453645fSAndriy Voskoboinyk 317*7453645fSAndriy Voskoboinyk if (tx[0] == 0xff || tx[1] == 0xff) 318*7453645fSAndriy Voskoboinyk return; 319*7453645fSAndriy Voskoboinyk 320*7453645fSAndriy Voskoboinyk reg = rtwn_bb_read(sc, R92C_OFDM0_TXIQIMBALANCE(chain)); 321*7453645fSAndriy Voskoboinyk val = ((reg >> 22) & 0x3ff); 322*7453645fSAndriy Voskoboinyk x = tx[0]; 323*7453645fSAndriy Voskoboinyk if (x & 0x00000200) 324*7453645fSAndriy Voskoboinyk x |= 0xfffffc00; 325*7453645fSAndriy Voskoboinyk reg = (((x * val) >> 8) & 0x3ff); 326*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_TXIQIMBALANCE(chain), 0x3ff, reg); 327*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_ECCATHRESHOLD, 0x80000000, 328*7453645fSAndriy Voskoboinyk ((x * val) & 0x80) << 24); 329*7453645fSAndriy Voskoboinyk 330*7453645fSAndriy Voskoboinyk y = tx[1]; 331*7453645fSAndriy Voskoboinyk if (y & 0x00000200) 332*7453645fSAndriy Voskoboinyk y |= 0xfffffc00; 333*7453645fSAndriy Voskoboinyk tx_c = (y * val) >> 8; 334*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_TXAFE(chain), 0xf0000000, 335*7453645fSAndriy Voskoboinyk (tx_c & 0x3c0) << 22); 336*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_TXIQIMBALANCE(chain), 0x003f0000, 337*7453645fSAndriy Voskoboinyk (tx_c & 0x3f) << 16); 338*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_ECCATHRESHOLD, 0x20000000, 339*7453645fSAndriy Voskoboinyk ((y * val) & 0x80) << 22); 340*7453645fSAndriy Voskoboinyk 341*7453645fSAndriy Voskoboinyk if (rx[0] == 0xff || rx[1] == 0xff) 342*7453645fSAndriy Voskoboinyk return; 343*7453645fSAndriy Voskoboinyk 344*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_RXIQIMBALANCE(chain), 0x3ff, 345*7453645fSAndriy Voskoboinyk rx[0] & 0x3ff); 346*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_RXIQIMBALANCE(chain), 0xfc00, 347*7453645fSAndriy Voskoboinyk (rx[1] & 0x3f) << 10); 348*7453645fSAndriy Voskoboinyk 349*7453645fSAndriy Voskoboinyk if (chain == 0) { 350*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_RXIQEXTANTA, 0xf0000000, 351*7453645fSAndriy Voskoboinyk (rx[1] & 0x3c0) << 22); 352*7453645fSAndriy Voskoboinyk } else { 353*7453645fSAndriy Voskoboinyk rtwn_bb_setbits(sc, R92C_OFDM0_AGCRSSITABLE, 0xf000, 354*7453645fSAndriy Voskoboinyk (rx[1] & 0x3c0) << 6); 355*7453645fSAndriy Voskoboinyk } 356*7453645fSAndriy Voskoboinyk } 357*7453645fSAndriy Voskoboinyk 358*7453645fSAndriy Voskoboinyk #define RTWN_IQ_CAL_NRUN 3 359*7453645fSAndriy Voskoboinyk void 360*7453645fSAndriy Voskoboinyk r92ce_iq_calib(struct rtwn_softc *sc) 361*7453645fSAndriy Voskoboinyk { 362*7453645fSAndriy Voskoboinyk struct r92ce_iq_cal_reg_vals vals; 363*7453645fSAndriy Voskoboinyk uint16_t tx[RTWN_IQ_CAL_NRUN][2][2], rx[RTWN_IQ_CAL_NRUN][2][2]; 364*7453645fSAndriy Voskoboinyk int n, valid; 365*7453645fSAndriy Voskoboinyk 366*7453645fSAndriy Voskoboinyk valid = 0; 367*7453645fSAndriy Voskoboinyk for (n = 0; n < RTWN_IQ_CAL_NRUN; n++) { 368*7453645fSAndriy Voskoboinyk r92ce_iq_calib_run(sc, n, tx[n], rx[n], &vals); 369*7453645fSAndriy Voskoboinyk 370*7453645fSAndriy Voskoboinyk if (n == 0) 371*7453645fSAndriy Voskoboinyk continue; 372*7453645fSAndriy Voskoboinyk 373*7453645fSAndriy Voskoboinyk /* Valid results remain stable after consecutive runs. */ 374*7453645fSAndriy Voskoboinyk valid = r92ce_iq_calib_compare_results(sc, tx[n - 1], 375*7453645fSAndriy Voskoboinyk rx[n - 1], tx[n], rx[n]); 376*7453645fSAndriy Voskoboinyk if (valid) 377*7453645fSAndriy Voskoboinyk break; 378*7453645fSAndriy Voskoboinyk } 379*7453645fSAndriy Voskoboinyk 380*7453645fSAndriy Voskoboinyk if (valid) { 381*7453645fSAndriy Voskoboinyk r92ce_iq_calib_write_results(sc, tx[n][0], rx[n][0], 0); 382*7453645fSAndriy Voskoboinyk if (sc->ntxchains > 1) 383*7453645fSAndriy Voskoboinyk r92ce_iq_calib_write_results(sc, tx[n][1], rx[n][1], 1); 384*7453645fSAndriy Voskoboinyk } 385*7453645fSAndriy Voskoboinyk } 386*7453645fSAndriy Voskoboinyk #undef RTWN_IQ_CAL_NRUN 387