xref: /freebsd/sys/dev/rtwn/rtl8723b/r23b_calib.c (revision 551b7c5e12bfea623a47edf97ad5689732a1233f)
1*551b7c5eSAhmad Khalifa /*
2*551b7c5eSAhmad Khalifa  * Copyright (c) 2026 Ahmad Khalifa <vexeduxr@FreeBSD.org>
3*551b7c5eSAhmad Khalifa  *
4*551b7c5eSAhmad Khalifa  * SPDX-License-Identifier: BSD-2-Clause
5*551b7c5eSAhmad Khalifa  */
6*551b7c5eSAhmad Khalifa 
7*551b7c5eSAhmad Khalifa #include <sys/param.h>
8*551b7c5eSAhmad Khalifa #include <sys/lock.h>
9*551b7c5eSAhmad Khalifa #include <sys/mutex.h>
10*551b7c5eSAhmad Khalifa #include <sys/mbuf.h>
11*551b7c5eSAhmad Khalifa #include <sys/kernel.h>
12*551b7c5eSAhmad Khalifa #include <sys/socket.h>
13*551b7c5eSAhmad Khalifa #include <sys/systm.h>
14*551b7c5eSAhmad Khalifa #include <sys/malloc.h>
15*551b7c5eSAhmad Khalifa #include <sys/queue.h>
16*551b7c5eSAhmad Khalifa #include <sys/taskqueue.h>
17*551b7c5eSAhmad Khalifa #include <sys/bus.h>
18*551b7c5eSAhmad Khalifa #include <sys/endian.h>
19*551b7c5eSAhmad Khalifa #include <sys/linker.h>
20*551b7c5eSAhmad Khalifa 
21*551b7c5eSAhmad Khalifa #include <net/if.h>
22*551b7c5eSAhmad Khalifa #include <net/ethernet.h>
23*551b7c5eSAhmad Khalifa #include <net/if_media.h>
24*551b7c5eSAhmad Khalifa 
25*551b7c5eSAhmad Khalifa #include <net80211/ieee80211_var.h>
26*551b7c5eSAhmad Khalifa #include <net80211/ieee80211_radiotap.h>
27*551b7c5eSAhmad Khalifa 
28*551b7c5eSAhmad Khalifa #include <dev/rtwn/if_rtwnreg.h>
29*551b7c5eSAhmad Khalifa #include <dev/rtwn/if_rtwnvar.h>
30*551b7c5eSAhmad Khalifa 
31*551b7c5eSAhmad Khalifa #include <dev/rtwn/if_rtwn_debug.h>
32*551b7c5eSAhmad Khalifa 
33*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8188e/r88e.h>
34*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8188e/r88e_reg.h>
35*551b7c5eSAhmad Khalifa 
36*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8192c/r92c_reg.h>
37*551b7c5eSAhmad Khalifa 
38*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8192e/r92e_var.h>
39*551b7c5eSAhmad Khalifa 
40*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8723b/r23b.h>
41*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8723b/r23b_var.h>
42*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8723b/r23b_reg.h>
43*551b7c5eSAhmad Khalifa #include <dev/rtwn/rtl8723b/r23b_fw_cmd.h>
44*551b7c5eSAhmad Khalifa 
45*551b7c5eSAhmad Khalifa struct r23b_iqk_reg_vals {
46*551b7c5eSAhmad Khalifa 	uint32_t adaa[16];
47*551b7c5eSAhmad Khalifa 	uint32_t iqk_bb[9];
48*551b7c5eSAhmad Khalifa 	uint8_t txpause;
49*551b7c5eSAhmad Khalifa 	uint8_t bcn_ctrl[2];
50*551b7c5eSAhmad Khalifa 	uint8_t gpio_muxcfg;
51*551b7c5eSAhmad Khalifa };
52*551b7c5eSAhmad Khalifa 
53*551b7c5eSAhmad Khalifa struct r23b_iqk_result {
54*551b7c5eSAhmad Khalifa 	int32_t iqk_before;
55*551b7c5eSAhmad Khalifa 	int32_t iqk_after;
56*551b7c5eSAhmad Khalifa };
57*551b7c5eSAhmad Khalifa 
58*551b7c5eSAhmad Khalifa enum r23b_iqk_stage {
59*551b7c5eSAhmad Khalifa 	R23B_IQK_TX,
60*551b7c5eSAhmad Khalifa 	R23B_IQK_TX_RX,
61*551b7c5eSAhmad Khalifa 	R23B_IQK_RX
62*551b7c5eSAhmad Khalifa };
63*551b7c5eSAhmad Khalifa 
64*551b7c5eSAhmad Khalifa static const uint16_t r23b_iq_calib_reg_adaa[16] = {
65*551b7c5eSAhmad Khalifa 	0x85c, 0xe6c, 0xe70, 0xe74,
66*551b7c5eSAhmad Khalifa 	0xe78, 0xe7c, 0xe80, 0xe84,
67*551b7c5eSAhmad Khalifa 	0xe88, 0xe8c, 0xed0, 0xed4,
68*551b7c5eSAhmad Khalifa 	0xed8, 0xedc, 0xee0, 0xeec
69*551b7c5eSAhmad Khalifa };
70*551b7c5eSAhmad Khalifa 
71*551b7c5eSAhmad Khalifa static const uint16_t r23b_iq_calib_reg_iqk_bb[9] = {
72*551b7c5eSAhmad Khalifa 	0xc04, 0xc08, 0x874, 0xb68,
73*551b7c5eSAhmad Khalifa 	0xb6c, 0x870, 0x860, 0x864,
74*551b7c5eSAhmad Khalifa 	0xa04
75*551b7c5eSAhmad Khalifa };
76*551b7c5eSAhmad Khalifa 
77*551b7c5eSAhmad Khalifa #define R23B_IQK_MAX_TOLERANCE	5
78*551b7c5eSAhmad Khalifa #define R23B_IQK_SIGN(r)	(((r) & 0x200) ? (0xfffffc00 | (r)) : (r))
79*551b7c5eSAhmad Khalifa 
80*551b7c5eSAhmad Khalifa /* XXX
81*551b7c5eSAhmad Khalifa  * Same as r92c_lc_calib except for LDO on and LDO off
82*551b7c5eSAhmad Khalifa  */
83*551b7c5eSAhmad Khalifa void
r23b_lc_calib(struct rtwn_softc * sc)84*551b7c5eSAhmad Khalifa r23b_lc_calib(struct rtwn_softc *sc)
85*551b7c5eSAhmad Khalifa {
86*551b7c5eSAhmad Khalifa 	uint32_t rf_ac[2];
87*551b7c5eSAhmad Khalifa 	uint8_t txmode;
88*551b7c5eSAhmad Khalifa 	int i;
89*551b7c5eSAhmad Khalifa 
90*551b7c5eSAhmad Khalifa 	txmode = rtwn_read_1(sc, R92C_OFDM1_LSTF + 3);
91*551b7c5eSAhmad Khalifa 	if ((txmode & 0x70) != 0) {
92*551b7c5eSAhmad Khalifa 		/* Disable all continuous Tx. */
93*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode & ~0x70);
94*551b7c5eSAhmad Khalifa 
95*551b7c5eSAhmad Khalifa 		/* Set RF mode to standby mode. */
96*551b7c5eSAhmad Khalifa 		for (i = 0; i < sc->nrxchains; i++) {
97*551b7c5eSAhmad Khalifa 			rf_ac[i] = rtwn_rf_read(sc, i, R92C_RF_AC);
98*551b7c5eSAhmad Khalifa 			rtwn_rf_write(sc, i, R92C_RF_AC,
99*551b7c5eSAhmad Khalifa 			    RW(rf_ac[i], R92C_RF_AC_MODE,
100*551b7c5eSAhmad Khalifa 				R92C_RF_AC_MODE_STANDBY));
101*551b7c5eSAhmad Khalifa 		}
102*551b7c5eSAhmad Khalifa 	} else {
103*551b7c5eSAhmad Khalifa 		/* Block all Tx queues. */
104*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_ALL);
105*551b7c5eSAhmad Khalifa 	}
106*551b7c5eSAhmad Khalifa 
107*551b7c5eSAhmad Khalifa 	/* LDO on */
108*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdfbe0);
109*551b7c5eSAhmad Khalifa 
110*551b7c5eSAhmad Khalifa 	/* Start calibration. */
111*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, R92C_RF_CHNLBW, 0, R92C_RF_CHNLBW_LCSTART);
112*551b7c5eSAhmad Khalifa 
113*551b7c5eSAhmad Khalifa 	/* Give calibration the time to complete. */
114*551b7c5eSAhmad Khalifa 	rtwn_delay(sc, 100000);	/* 100ms */
115*551b7c5eSAhmad Khalifa 
116*551b7c5eSAhmad Khalifa 	/* LDO off */
117*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R23B_RF_S0S1, 0xdffe0);
118*551b7c5eSAhmad Khalifa 
119*551b7c5eSAhmad Khalifa 	/* Restore configuration. */
120*551b7c5eSAhmad Khalifa 	if ((txmode & 0x70) != 0) {
121*551b7c5eSAhmad Khalifa 		/* Restore Tx mode. */
122*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_OFDM1_LSTF + 3, txmode);
123*551b7c5eSAhmad Khalifa 		/* Restore RF mode. */
124*551b7c5eSAhmad Khalifa 		for (i = 0; i < sc->nrxchains; i++)
125*551b7c5eSAhmad Khalifa 			rtwn_rf_write(sc, i, R92C_RF_AC, rf_ac[i]);
126*551b7c5eSAhmad Khalifa 	} else {
127*551b7c5eSAhmad Khalifa 		/* Unblock all Tx queues. */
128*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_TXPAUSE, 0x00);
129*551b7c5eSAhmad Khalifa 	}
130*551b7c5eSAhmad Khalifa }
131*551b7c5eSAhmad Khalifa 
132*551b7c5eSAhmad Khalifa static void
r23b_iq_calib_chain(struct rtwn_softc * sc,enum r23b_iqk_stage stage)133*551b7c5eSAhmad Khalifa r23b_iq_calib_chain(struct rtwn_softc *sc, enum r23b_iqk_stage stage)
134*551b7c5eSAhmad Khalifa {
135*551b7c5eSAhmad Khalifa 	struct r23b_softc *rs = sc->sc_priv;
136*551b7c5eSAhmad Khalifa 	uint32_t rf_path;
137*551b7c5eSAhmad Khalifa 
138*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: starting stage: %s\n",
139*551b7c5eSAhmad Khalifa 	    __func__, stage == R23B_IQK_TX ? "R23B_IQK_TX" :
140*551b7c5eSAhmad Khalifa 	        stage == R23B_IQK_TX_RX ? "R23B_IQK_TX_RX" : "R23B_IQK_RX");
141*551b7c5eSAhmad Khalifa 
142*551b7c5eSAhmad Khalifa 	/* Save RF path. */
143*551b7c5eSAhmad Khalifa 	rf_path = rtwn_read_4(sc, R23B_ANT_SEL);
144*551b7c5eSAhmad Khalifa 
145*551b7c5eSAhmad Khalifa 	/* Leave IQK mode. */
146*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
147*551b7c5eSAhmad Khalifa 
148*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
149*551b7c5eSAhmad Khalifa 	switch (stage) {
150*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX:
151*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x20000);
152*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0003f);
153*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xc7f87);
154*551b7c5eSAhmad Khalifa 
155*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK, 0x01007c00);
156*551b7c5eSAhmad Khalifa 		break;
157*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX_RX:
158*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
159*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
160*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7fb7);
161*551b7c5eSAhmad Khalifa 
162*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK, 0x01007c00);
163*551b7c5eSAhmad Khalifa 		break;
164*551b7c5eSAhmad Khalifa 	case R23B_IQK_RX:
165*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
166*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
167*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7d77);
168*551b7c5eSAhmad Khalifa 
169*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, 0xdf, 0xf80);
170*551b7c5eSAhmad Khalifa 		rtwn_rf_write(sc, 0, 0x55, 0x4021f);
171*551b7c5eSAhmad Khalifa 		break;
172*551b7c5eSAhmad Khalifa 	default:
173*551b7c5eSAhmad Khalifa 		KASSERT(0, ("%s: Invalid stage %d", __func__, stage));
174*551b7c5eSAhmad Khalifa 	}
175*551b7c5eSAhmad Khalifa 
176*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_RX_IQK, 0x01004800);
177*551b7c5eSAhmad Khalifa 
178*551b7c5eSAhmad Khalifa 	switch (stage) {
179*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX:
180*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX_RX:
181*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK_TONE(0), 0x18008c1c);
182*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_RX_IQK_TONE(0), 0x38008c1c);
183*551b7c5eSAhmad Khalifa 		break;
184*551b7c5eSAhmad Khalifa 	case R23B_IQK_RX:
185*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK_TONE(0), 0x38008c1c);
186*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_RX_IQK_TONE(0), 0x18008c1c);
187*551b7c5eSAhmad Khalifa 		break;
188*551b7c5eSAhmad Khalifa 	}
189*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_TX_IQK_TONE(1), 0x38008c1c);
190*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_RX_IQK_TONE(1), 0x38008c1c);
191*551b7c5eSAhmad Khalifa 
192*551b7c5eSAhmad Khalifa 	switch (stage) {
193*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX:
194*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x821403ea);
195*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x28110000);
196*551b7c5eSAhmad Khalifa 		break;
197*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX_RX:
198*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x82160ff0);
199*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x28110000);
200*551b7c5eSAhmad Khalifa 		break;
201*551b7c5eSAhmad Khalifa 	case R23B_IQK_RX:
202*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK_PI(0), 0x82110000);
203*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_RX_IQK_PI(0), 0x2816001f);
204*551b7c5eSAhmad Khalifa 		break;
205*551b7c5eSAhmad Khalifa 	}
206*551b7c5eSAhmad Khalifa 
207*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_TX_IQK_PI(1), 0x82110000);
208*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_RX_IQK_PI(1), 0x28110000);
209*551b7c5eSAhmad Khalifa 
210*551b7c5eSAhmad Khalifa 	switch (stage) {
211*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX:
212*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x00462911);
213*551b7c5eSAhmad Khalifa 		break;
214*551b7c5eSAhmad Khalifa 	case R23B_IQK_TX_RX:
215*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x0046a911);
216*551b7c5eSAhmad Khalifa 		break;
217*551b7c5eSAhmad Khalifa 	case R23B_IQK_RX:
218*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_IQK_AGC_RSP, 0x0046a8d1);
219*551b7c5eSAhmad Khalifa 		break;
220*551b7c5eSAhmad Khalifa 	}
221*551b7c5eSAhmad Khalifa 
222*551b7c5eSAhmad Khalifa 	/* Enter IQK mode. */
223*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0x80800000);
224*551b7c5eSAhmad Khalifa 
225*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R23B_ANT_SEL, rs->bt_antnum == 0 ?
226*551b7c5eSAhmad Khalifa 	    R23B_ANT_SEL_S0 : R23B_ANT_SEL_S1);
227*551b7c5eSAhmad Khalifa 
228*551b7c5eSAhmad Khalifa 	/* GNT_BT = 0 */
229*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R23B_GNT_BT, 0x00000800);
230*551b7c5eSAhmad Khalifa 
231*551b7c5eSAhmad Khalifa 	/* We're doing LO and IQ calibration in one shot. */
232*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_IQK_AGC_PTS, 0xf9000000);
233*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_IQK_AGC_PTS, 0xf8000000);
234*551b7c5eSAhmad Khalifa 
235*551b7c5eSAhmad Khalifa 	rtwn_delay(sc, 20000); /* 20ms */
236*551b7c5eSAhmad Khalifa 
237*551b7c5eSAhmad Khalifa 	/* Restore RF path. */
238*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R23B_ANT_SEL, rf_path);
239*551b7c5eSAhmad Khalifa 
240*551b7c5eSAhmad Khalifa 	/* GNT_BT = 1 */
241*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R23B_GNT_BT, 0x00001800);
242*551b7c5eSAhmad Khalifa 
243*551b7c5eSAhmad Khalifa 	/* Leave IQK mode. */
244*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
245*551b7c5eSAhmad Khalifa }
246*551b7c5eSAhmad Khalifa 
247*551b7c5eSAhmad Khalifa static bool
r23b_iq_calib_get_tx_result(struct rtwn_softc * sc,struct r23b_iqk_result * tx_result)248*551b7c5eSAhmad Khalifa r23b_iq_calib_get_tx_result(struct rtwn_softc *sc,
249*551b7c5eSAhmad Khalifa     struct r23b_iqk_result *tx_result)
250*551b7c5eSAhmad Khalifa {
251*551b7c5eSAhmad Khalifa 	uint32_t val;
252*551b7c5eSAhmad Khalifa 
253*551b7c5eSAhmad Khalifa 	val = rtwn_read_4(sc, R92C_RX_POWER_IQK_AFTER(0));
254*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_after (%#x): %#x\n",
255*551b7c5eSAhmad Khalifa 	    __func__, R92C_RX_POWER_IQK_AFTER(0), val);
256*551b7c5eSAhmad Khalifa 	if (val & 0x10000000)
257*551b7c5eSAhmad Khalifa 		return (false);
258*551b7c5eSAhmad Khalifa 
259*551b7c5eSAhmad Khalifa 	val = rtwn_read_4(sc, R92C_TX_POWER_IQK_BEFORE(0));
260*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: tx_before (%#x): %#x\n",
261*551b7c5eSAhmad Khalifa 	    __func__, R92C_TX_POWER_IQK_BEFORE(0), val);
262*551b7c5eSAhmad Khalifa 	val = MS(val, R92C_POWER_IQK_RESULT);
263*551b7c5eSAhmad Khalifa 	if (val == 0x142 || val >= 0x110 || val <= 0xf0)
264*551b7c5eSAhmad Khalifa 		return (false);
265*551b7c5eSAhmad Khalifa 	tx_result->iqk_before = val;
266*551b7c5eSAhmad Khalifa 
267*551b7c5eSAhmad Khalifa 	val = rtwn_read_4(sc, R92C_TX_POWER_IQK_AFTER(0));
268*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: tx_after (%#x): %#x\n",
269*551b7c5eSAhmad Khalifa 	    __func__, R92C_TX_POWER_IQK_AFTER(0), val);
270*551b7c5eSAhmad Khalifa 
271*551b7c5eSAhmad Khalifa 	val = MS(val, R92C_POWER_IQK_RESULT);
272*551b7c5eSAhmad Khalifa 	if (val == 0x42)
273*551b7c5eSAhmad Khalifa 		return (false);
274*551b7c5eSAhmad Khalifa 	tx_result->iqk_after = val;
275*551b7c5eSAhmad Khalifa 
276*551b7c5eSAhmad Khalifa 	if ((val & 0x200) != 0)
277*551b7c5eSAhmad Khalifa 		val = 0x400 - val;
278*551b7c5eSAhmad Khalifa 	return (val < 0xf);
279*551b7c5eSAhmad Khalifa }
280*551b7c5eSAhmad Khalifa 
281*551b7c5eSAhmad Khalifa static bool
r23b_iq_calib_get_rx_result(struct rtwn_softc * sc,struct r23b_iqk_result * rx_result)282*551b7c5eSAhmad Khalifa r23b_iq_calib_get_rx_result(struct rtwn_softc *sc,
283*551b7c5eSAhmad Khalifa     struct r23b_iqk_result *rx_result)
284*551b7c5eSAhmad Khalifa {
285*551b7c5eSAhmad Khalifa 	uint32_t iqk_before, iqk_after, val;
286*551b7c5eSAhmad Khalifa 
287*551b7c5eSAhmad Khalifa 	iqk_after = rtwn_read_4(sc, R92C_RX_POWER_IQK_AFTER(0));
288*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_after (%#x): %#x\n",
289*551b7c5eSAhmad Khalifa 	    __func__, R92C_RX_POWER_IQK_AFTER(0), iqk_after);
290*551b7c5eSAhmad Khalifa 
291*551b7c5eSAhmad Khalifa 	iqk_after = MS(iqk_after, R92C_POWER_IQK_RESULT);
292*551b7c5eSAhmad Khalifa 
293*551b7c5eSAhmad Khalifa 	iqk_before = rtwn_read_4(sc, R92C_RX_POWER_IQK_BEFORE(0));
294*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: rx_before (%#x): %#x\n",
295*551b7c5eSAhmad Khalifa 	    __func__, R92C_RX_POWER_IQK_BEFORE(0), iqk_before);
296*551b7c5eSAhmad Khalifa 
297*551b7c5eSAhmad Khalifa 	iqk_before = MS(iqk_before, R92C_POWER_IQK_RESULT);
298*551b7c5eSAhmad Khalifa 
299*551b7c5eSAhmad Khalifa 	/* Leave IQK mode. */
300*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
301*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, 0xdf, 0x780);
302*551b7c5eSAhmad Khalifa 
303*551b7c5eSAhmad Khalifa 	val = (iqk_after & 0x200) != 0 ? 0x400 - iqk_after : iqk_after;
304*551b7c5eSAhmad Khalifa 	if (iqk_after & 0x8000000)
305*551b7c5eSAhmad Khalifa 		return (false);
306*551b7c5eSAhmad Khalifa 
307*551b7c5eSAhmad Khalifa 	if (iqk_before == 0x132 || iqk_before >= 0x110 || iqk_before <= 0xf0)
308*551b7c5eSAhmad Khalifa 		return (false);
309*551b7c5eSAhmad Khalifa 
310*551b7c5eSAhmad Khalifa 	if (iqk_after == 0x36)
311*551b7c5eSAhmad Khalifa 		return (false);
312*551b7c5eSAhmad Khalifa 
313*551b7c5eSAhmad Khalifa 	if (val >= 0xf)
314*551b7c5eSAhmad Khalifa 		return (false);
315*551b7c5eSAhmad Khalifa 
316*551b7c5eSAhmad Khalifa 	rx_result->iqk_before = iqk_before;
317*551b7c5eSAhmad Khalifa 	rx_result->iqk_after = iqk_after;
318*551b7c5eSAhmad Khalifa 	return (true);
319*551b7c5eSAhmad Khalifa }
320*551b7c5eSAhmad Khalifa 
321*551b7c5eSAhmad Khalifa static inline void
r23b_iq_calib_save_regs(struct rtwn_softc * sc,struct r23b_iqk_reg_vals * vals)322*551b7c5eSAhmad Khalifa r23b_iq_calib_save_regs(struct rtwn_softc *sc,
323*551b7c5eSAhmad Khalifa     struct r23b_iqk_reg_vals *vals)
324*551b7c5eSAhmad Khalifa {
325*551b7c5eSAhmad Khalifa 	for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
326*551b7c5eSAhmad Khalifa 		vals->adaa[i] = rtwn_read_4(sc, r23b_iq_calib_reg_adaa[i]);
327*551b7c5eSAhmad Khalifa 
328*551b7c5eSAhmad Khalifa 	vals->txpause = rtwn_read_1(sc, R92C_TXPAUSE);
329*551b7c5eSAhmad Khalifa 
330*551b7c5eSAhmad Khalifa 	for (int i = 0; i < sizeof(vals->bcn_ctrl); i++)
331*551b7c5eSAhmad Khalifa 		vals->bcn_ctrl[i] = rtwn_read_1(sc, R92C_BCN_CTRL(i));
332*551b7c5eSAhmad Khalifa 
333*551b7c5eSAhmad Khalifa 	vals->gpio_muxcfg = rtwn_read_4(sc, R92C_GPIO_MUXCFG);
334*551b7c5eSAhmad Khalifa 
335*551b7c5eSAhmad Khalifa 	for (int i = 0; i < nitems(r23b_iq_calib_reg_iqk_bb); i++)
336*551b7c5eSAhmad Khalifa 		vals->iqk_bb[i] = rtwn_read_4(sc, r23b_iq_calib_reg_iqk_bb[i]);
337*551b7c5eSAhmad Khalifa }
338*551b7c5eSAhmad Khalifa 
339*551b7c5eSAhmad Khalifa static inline void
r23b_iq_calib_restore_regs(struct rtwn_softc * sc,const struct r23b_iqk_reg_vals * vals)340*551b7c5eSAhmad Khalifa r23b_iq_calib_restore_regs(struct rtwn_softc *sc,
341*551b7c5eSAhmad Khalifa     const struct r23b_iqk_reg_vals *vals)
342*551b7c5eSAhmad Khalifa {
343*551b7c5eSAhmad Khalifa 	for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
344*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, r23b_iq_calib_reg_adaa[i], vals->adaa[i]);
345*551b7c5eSAhmad Khalifa 
346*551b7c5eSAhmad Khalifa 	rtwn_write_1(sc, R92C_TXPAUSE, vals->txpause);
347*551b7c5eSAhmad Khalifa 
348*551b7c5eSAhmad Khalifa 	for (int i = 0; i < sizeof(vals->bcn_ctrl); i++)
349*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_BCN_CTRL(i), vals->bcn_ctrl[i]);
350*551b7c5eSAhmad Khalifa 
351*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_GPIO_MUXCFG, vals->gpio_muxcfg);
352*551b7c5eSAhmad Khalifa 
353*551b7c5eSAhmad Khalifa 	for (int i = 0; i < nitems(r23b_iq_calib_reg_iqk_bb); i++)
354*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, r23b_iq_calib_reg_iqk_bb[i], vals->iqk_bb[i]);
355*551b7c5eSAhmad Khalifa }
356*551b7c5eSAhmad Khalifa 
357*551b7c5eSAhmad Khalifa static void
r23b_iq_calib_run(struct rtwn_softc * sc,struct r23b_iqk_result * tx_result,struct r23b_iqk_result * rx_result,const struct r23b_iqk_reg_vals * vals)358*551b7c5eSAhmad Khalifa r23b_iq_calib_run(struct rtwn_softc *sc, struct r23b_iqk_result *tx_result,
359*551b7c5eSAhmad Khalifa     struct r23b_iqk_result *rx_result, const struct r23b_iqk_reg_vals *vals)
360*551b7c5eSAhmad Khalifa {
361*551b7c5eSAhmad Khalifa 	struct r23b_softc *rs = sc->sc_priv;
362*551b7c5eSAhmad Khalifa 
363*551b7c5eSAhmad Khalifa 	for (int i = 0; i < nitems(r23b_iq_calib_reg_adaa); i++)
364*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, r23b_iq_calib_reg_adaa[i], 0x01c00014);
365*551b7c5eSAhmad Khalifa 
366*551b7c5eSAhmad Khalifa 	/* MAC settings */
367*551b7c5eSAhmad Khalifa 
368*551b7c5eSAhmad Khalifa 	rtwn_write_1(sc, R92C_TXPAUSE, R92C_TX_QUEUE_AC |
369*551b7c5eSAhmad Khalifa 	    R92C_TX_QUEUE_MGT | R92C_TX_QUEUE_HIGH);
370*551b7c5eSAhmad Khalifa 
371*551b7c5eSAhmad Khalifa 	for (int i = 0; i < sizeof(vals->bcn_ctrl); i++) {
372*551b7c5eSAhmad Khalifa 		rtwn_write_1(sc, R92C_BCN_CTRL(i),
373*551b7c5eSAhmad Khalifa 		    vals->bcn_ctrl[i] & ~R92C_BCN_CTRL_EN_BCN);
374*551b7c5eSAhmad Khalifa 	}
375*551b7c5eSAhmad Khalifa 
376*551b7c5eSAhmad Khalifa 	rtwn_write_1(sc, R92C_GPIO_MUXCFG,
377*551b7c5eSAhmad Khalifa 	    vals->gpio_muxcfg & ~R92C_GPIO_MUXCFG_ENBT);
378*551b7c5eSAhmad Khalifa 
379*551b7c5eSAhmad Khalifa 	/* BB settings */
380*551b7c5eSAhmad Khalifa 
381*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_CCK0_AFESETTING, 0, 0x0f000000);
382*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_OFDM0_TRXPATHENA, 0x03a05600);
383*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_OFDM0_TRMUXPAR, 0x000800e4);
384*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R92C_FPGA0_RFIFACESW(1), 0x22204000);
385*551b7c5eSAhmad Khalifa 
386*551b7c5eSAhmad Khalifa 	/* IQ calibration settings */
387*551b7c5eSAhmad Khalifa 
388*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
389*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
390*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x30000);
391*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
392*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(1), 0xf7fb7);
393*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, 0xed, 0, 0x20);
394*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, 0x43, 0x60fbd);
395*551b7c5eSAhmad Khalifa 
396*551b7c5eSAhmad Khalifa 	/* Path A Tx IQ calibration. */
397*551b7c5eSAhmad Khalifa 	for (int retry = 0; retry < 2; retry++) {
398*551b7c5eSAhmad Khalifa 		r23b_iq_calib_chain(sc, R23B_IQK_TX);
399*551b7c5eSAhmad Khalifa 
400*551b7c5eSAhmad Khalifa 		if (r23b_iq_calib_get_tx_result(sc, tx_result)) {
401*551b7c5eSAhmad Khalifa 			rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
402*551b7c5eSAhmad Khalifa 			break;
403*551b7c5eSAhmad Khalifa 		}
404*551b7c5eSAhmad Khalifa 
405*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
406*551b7c5eSAhmad Khalifa 		    "%s: Tx failed, retry=%d\n", __func__, retry);
407*551b7c5eSAhmad Khalifa 	}
408*551b7c5eSAhmad Khalifa 
409*551b7c5eSAhmad Khalifa 	/* Path A Rx IQ calibration. */
410*551b7c5eSAhmad Khalifa 	for (int retry = 0; retry < 2; retry++) {
411*551b7c5eSAhmad Khalifa 		struct r23b_iqk_result result;
412*551b7c5eSAhmad Khalifa 
413*551b7c5eSAhmad Khalifa 		r23b_iq_calib_chain(sc, R23B_IQK_TX_RX);
414*551b7c5eSAhmad Khalifa 		if (!r23b_iq_calib_get_tx_result(sc, &result)) {
415*551b7c5eSAhmad Khalifa 			RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
416*551b7c5eSAhmad Khalifa 			    "%s: Rx (Tx) failed, retry=%d\n", __func__, retry);
417*551b7c5eSAhmad Khalifa 			continue;
418*551b7c5eSAhmad Khalifa 		}
419*551b7c5eSAhmad Khalifa 
420*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
421*551b7c5eSAhmad Khalifa 		    "%s: writing %#x to TX_IQK\n", __func__,
422*551b7c5eSAhmad Khalifa 		    0x80007c00 | (result.iqk_before << 16) | result.iqk_after);
423*551b7c5eSAhmad Khalifa 
424*551b7c5eSAhmad Khalifa 		rtwn_write_4(sc, R92C_TX_IQK,
425*551b7c5eSAhmad Khalifa 		    0x80007c00 | (result.iqk_before << 16) | result.iqk_after);
426*551b7c5eSAhmad Khalifa 
427*551b7c5eSAhmad Khalifa 		r23b_iq_calib_chain(sc, R23B_IQK_RX);
428*551b7c5eSAhmad Khalifa 		if (!r23b_iq_calib_get_rx_result(sc, rx_result)) {
429*551b7c5eSAhmad Khalifa 			RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
430*551b7c5eSAhmad Khalifa 			    "%s: Rx failed, retry=%d\n", __func__, retry);
431*551b7c5eSAhmad Khalifa 			continue;
432*551b7c5eSAhmad Khalifa 		}
433*551b7c5eSAhmad Khalifa 
434*551b7c5eSAhmad Khalifa 		break;
435*551b7c5eSAhmad Khalifa 	}
436*551b7c5eSAhmad Khalifa 
437*551b7c5eSAhmad Khalifa 	if (rs->bt_antnum == 0) {
438*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
439*551b7c5eSAhmad Khalifa 		    "%s: path B IQK not implemented!\n", __func__);
440*551b7c5eSAhmad Khalifa 	}
441*551b7c5eSAhmad Khalifa 
442*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_FPGA0_IQK, ~0xff, 0);
443*551b7c5eSAhmad Khalifa }
444*551b7c5eSAhmad Khalifa 
445*551b7c5eSAhmad Khalifa static bool
r23b_iq_calib_compare_results(struct r23b_iqk_result * tx_result1,struct r23b_iqk_result * rx_result1,struct r23b_iqk_result * tx_result2,struct r23b_iqk_result * rx_result2,struct r23b_iqk_result ** tx_candidate,struct r23b_iqk_result ** rx_candidate)446*551b7c5eSAhmad Khalifa r23b_iq_calib_compare_results(
447*551b7c5eSAhmad Khalifa     struct r23b_iqk_result *tx_result1, struct r23b_iqk_result *rx_result1,
448*551b7c5eSAhmad Khalifa     struct r23b_iqk_result *tx_result2, struct r23b_iqk_result *rx_result2,
449*551b7c5eSAhmad Khalifa     struct r23b_iqk_result **tx_candidate,
450*551b7c5eSAhmad Khalifa     struct r23b_iqk_result **rx_candidate) {
451*551b7c5eSAhmad Khalifa 	uint32_t diff;
452*551b7c5eSAhmad Khalifa 	bool tx_good = false;
453*551b7c5eSAhmad Khalifa 
454*551b7c5eSAhmad Khalifa 	diff = abs(tx_result1->iqk_before - tx_result2->iqk_before);
455*551b7c5eSAhmad Khalifa 	if (diff > R23B_IQK_MAX_TOLERANCE)
456*551b7c5eSAhmad Khalifa 		goto rx_compare;
457*551b7c5eSAhmad Khalifa 
458*551b7c5eSAhmad Khalifa 	diff = abs(R23B_IQK_SIGN(tx_result1->iqk_after) -
459*551b7c5eSAhmad Khalifa 	    R23B_IQK_SIGN(tx_result2->iqk_after));
460*551b7c5eSAhmad Khalifa 	if (diff <= R23B_IQK_MAX_TOLERANCE) {
461*551b7c5eSAhmad Khalifa 		*tx_candidate = tx_result1;
462*551b7c5eSAhmad Khalifa 		tx_good = true;
463*551b7c5eSAhmad Khalifa 	}
464*551b7c5eSAhmad Khalifa 
465*551b7c5eSAhmad Khalifa rx_compare:
466*551b7c5eSAhmad Khalifa 	diff = abs(rx_result1->iqk_before - rx_result2->iqk_before);
467*551b7c5eSAhmad Khalifa 	if (diff > R23B_IQK_MAX_TOLERANCE) {
468*551b7c5eSAhmad Khalifa 		if (tx_good &&
469*551b7c5eSAhmad Khalifa 		    rx_result1->iqk_before + rx_result1->iqk_after == 0)
470*551b7c5eSAhmad Khalifa 			*tx_candidate = tx_result2;
471*551b7c5eSAhmad Khalifa 
472*551b7c5eSAhmad Khalifa 		return (false);
473*551b7c5eSAhmad Khalifa 	}
474*551b7c5eSAhmad Khalifa 
475*551b7c5eSAhmad Khalifa 	diff = abs(R23B_IQK_SIGN(rx_result1->iqk_after) -
476*551b7c5eSAhmad Khalifa 	    R23B_IQK_SIGN(rx_result2->iqk_after));
477*551b7c5eSAhmad Khalifa 	if (diff > R23B_IQK_MAX_TOLERANCE)
478*551b7c5eSAhmad Khalifa 		return (false);
479*551b7c5eSAhmad Khalifa 
480*551b7c5eSAhmad Khalifa 	*rx_candidate = rx_result1;
481*551b7c5eSAhmad Khalifa 	return (tx_good);
482*551b7c5eSAhmad Khalifa }
483*551b7c5eSAhmad Khalifa 
484*551b7c5eSAhmad Khalifa void
r23b_iq_calib(struct rtwn_softc * sc)485*551b7c5eSAhmad Khalifa r23b_iq_calib(struct rtwn_softc *sc)
486*551b7c5eSAhmad Khalifa {
487*551b7c5eSAhmad Khalifa 	struct r23b_iqk_reg_vals vals;
488*551b7c5eSAhmad Khalifa 	struct r23b_iqk_result tx_result[3] = { 0 };
489*551b7c5eSAhmad Khalifa 	struct r23b_iqk_result rx_result[3] = { 0 };
490*551b7c5eSAhmad Khalifa 	struct r23b_iqk_result *tx_candidate = NULL, *rx_candidate = NULL;
491*551b7c5eSAhmad Khalifa 	uint32_t oldval, val;
492*551b7c5eSAhmad Khalifa 	uint32_t gnt_bt;
493*551b7c5eSAhmad Khalifa 
494*551b7c5eSAhmad Khalifa #ifndef RTWN_WITHOUT_UCODE
495*551b7c5eSAhmad Khalifa 	/* Inform firmware that we're begininng IQK. */
496*551b7c5eSAhmad Khalifa 	r88e_fw_cmd(sc, R23B_CMD_WLAN_CALIB, R23B_CMD_WLAN_CALIB_BEGIN,
497*551b7c5eSAhmad Khalifa 	    sizeof(R23B_CMD_WLAN_CALIB_BEGIN));
498*551b7c5eSAhmad Khalifa #endif
499*551b7c5eSAhmad Khalifa 
500*551b7c5eSAhmad Khalifa 	/* Save GNT_BT. */
501*551b7c5eSAhmad Khalifa 	gnt_bt = rtwn_read_4(sc, R23B_GNT_BT);
502*551b7c5eSAhmad Khalifa 
503*551b7c5eSAhmad Khalifa 	r23b_iq_calib_save_regs(sc, &vals);
504*551b7c5eSAhmad Khalifa 	r23b_iq_calib_run(sc, &tx_result[0], &rx_result[0], &vals);
505*551b7c5eSAhmad Khalifa 	r23b_iq_calib_run(sc, &tx_result[1], &rx_result[1], &vals);
506*551b7c5eSAhmad Khalifa 	r23b_iq_calib_restore_regs(sc, &vals);
507*551b7c5eSAhmad Khalifa 
508*551b7c5eSAhmad Khalifa 	if (r23b_iq_calib_compare_results(&tx_result[0], &rx_result[0],
509*551b7c5eSAhmad Khalifa 	    &tx_result[1], &rx_result[1], &tx_candidate, &rx_candidate)) {
510*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
511*551b7c5eSAhmad Khalifa 		    "%s: comparing 0 and 1 succeeded\n", __func__);
512*551b7c5eSAhmad Khalifa 		goto write;
513*551b7c5eSAhmad Khalifa 	}
514*551b7c5eSAhmad Khalifa 
515*551b7c5eSAhmad Khalifa 	r23b_iq_calib_run(sc, &tx_result[2], &rx_result[2], &vals);
516*551b7c5eSAhmad Khalifa 	r23b_iq_calib_restore_regs(sc, &vals);
517*551b7c5eSAhmad Khalifa 
518*551b7c5eSAhmad Khalifa 	if (r23b_iq_calib_compare_results(&tx_result[0], &rx_result[0],
519*551b7c5eSAhmad Khalifa 	    &tx_result[2], &rx_result[2], &tx_candidate, &rx_candidate)) {
520*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
521*551b7c5eSAhmad Khalifa 		    "%s: comparing 0 and 2 succeeded\n", __func__);
522*551b7c5eSAhmad Khalifa 		goto write;
523*551b7c5eSAhmad Khalifa 	}
524*551b7c5eSAhmad Khalifa 
525*551b7c5eSAhmad Khalifa 	if (!r23b_iq_calib_compare_results(&tx_result[1], &rx_result[1],
526*551b7c5eSAhmad Khalifa 	    &tx_result[2], &rx_result[2], &tx_candidate, &rx_candidate)) {
527*551b7c5eSAhmad Khalifa 		/* If no candidate was assigned, set the result to zero. */
528*551b7c5eSAhmad Khalifa 		if (tx_candidate == NULL) {
529*551b7c5eSAhmad Khalifa 			tx_result[0] = (struct r23b_iqk_result){ 0 };
530*551b7c5eSAhmad Khalifa 			tx_candidate = &tx_result[0];
531*551b7c5eSAhmad Khalifa 		}
532*551b7c5eSAhmad Khalifa 
533*551b7c5eSAhmad Khalifa 		if (rx_candidate == NULL) {
534*551b7c5eSAhmad Khalifa 			rx_result[0] = (struct r23b_iqk_result){ 0 };
535*551b7c5eSAhmad Khalifa 			rx_candidate = &rx_result[0];
536*551b7c5eSAhmad Khalifa 		}
537*551b7c5eSAhmad Khalifa 
538*551b7c5eSAhmad Khalifa 		if (tx_candidate->iqk_before == 0 &&
539*551b7c5eSAhmad Khalifa 		    tx_candidate->iqk_after == 0 &&
540*551b7c5eSAhmad Khalifa 		    rx_candidate->iqk_before == 0 &&
541*551b7c5eSAhmad Khalifa 		    rx_candidate->iqk_after == 0) {
542*551b7c5eSAhmad Khalifa 			RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
543*551b7c5eSAhmad Khalifa 			    "%s: IQK failed\n", __func__);
544*551b7c5eSAhmad Khalifa 			goto done;
545*551b7c5eSAhmad Khalifa 		}
546*551b7c5eSAhmad Khalifa 
547*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
548*551b7c5eSAhmad Khalifa 		    "%s: comparison failed but a candidate was assigned\n",
549*551b7c5eSAhmad Khalifa 		    __func__);
550*551b7c5eSAhmad Khalifa 	} else {
551*551b7c5eSAhmad Khalifa 		RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB,
552*551b7c5eSAhmad Khalifa 		    "%s: comparing 1 and 2 succeeded\n", __func__);
553*551b7c5eSAhmad Khalifa 	}
554*551b7c5eSAhmad Khalifa 
555*551b7c5eSAhmad Khalifa write:
556*551b7c5eSAhmad Khalifa 	RTWN_DPRINTF(sc, RTWN_DEBUG_CALIB, "%s: IQK candidate dump:\n"
557*551b7c5eSAhmad Khalifa 	    "\ttx_candidate->iqk_before 0x%x\ttx_candidate->iqk_after 0x%x\n"
558*551b7c5eSAhmad Khalifa 	    "\trx_candidate->iqk_before 0x%x\trx_candidate->iqk_after 0x%x\n",
559*551b7c5eSAhmad Khalifa 	    __func__, tx_candidate->iqk_before, tx_candidate->iqk_after,
560*551b7c5eSAhmad Khalifa 	    rx_candidate->iqk_before, rx_candidate->iqk_after);
561*551b7c5eSAhmad Khalifa 
562*551b7c5eSAhmad Khalifa 	/*
563*551b7c5eSAhmad Khalifa 	 * Don't write both the tx and rx configuration
564*551b7c5eSAhmad Khalifa 	 * values if tx IQK failed.
565*551b7c5eSAhmad Khalifa 	 */
566*551b7c5eSAhmad Khalifa 	if (tx_candidate->iqk_before == 0)
567*551b7c5eSAhmad Khalifa 		goto done;
568*551b7c5eSAhmad Khalifa 
569*551b7c5eSAhmad Khalifa 	/* Write path A values */
570*551b7c5eSAhmad Khalifa 
571*551b7c5eSAhmad Khalifa 	oldval = rtwn_read_4(sc, R92C_OFDM0_TXIQIMBALANCE(0));
572*551b7c5eSAhmad Khalifa 	oldval = (oldval >> 22) & 0x3ff;
573*551b7c5eSAhmad Khalifa 
574*551b7c5eSAhmad Khalifa 	val = (R23B_IQK_SIGN(tx_candidate->iqk_before) * oldval) >> 7;
575*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_TXIQIMBALANCE(0), 0x3ff,
576*551b7c5eSAhmad Khalifa 	    (val >> 1) & 0x3ff);
577*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_ECCATHRESHOLD, 0x80000000, val << 31);
578*551b7c5eSAhmad Khalifa 
579*551b7c5eSAhmad Khalifa 	val = (R23B_IQK_SIGN(tx_candidate->iqk_after) * oldval) >> 7;
580*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_TX_POWER_IQK_BEFORE(0),
581*551b7c5eSAhmad Khalifa 	    0xf0000000, (((val >> 1) & 0x3c0) >> 6) << 28);
582*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_TXIQIMBALANCE(0),
583*551b7c5eSAhmad Khalifa 	    0x3f0000, ((val >> 1) & 0x3f) << 16);
584*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_ECCATHRESHOLD,
585*551b7c5eSAhmad Khalifa 	    0x10000000, (val << 29) & 0x1);
586*551b7c5eSAhmad Khalifa 
587*551b7c5eSAhmad Khalifa 	/* Skip the rx configuration values if rx IQK failed. */
588*551b7c5eSAhmad Khalifa 	if (rx_candidate->iqk_before == 0)
589*551b7c5eSAhmad Khalifa 		goto done;
590*551b7c5eSAhmad Khalifa 
591*551b7c5eSAhmad Khalifa 	val = rx_candidate->iqk_after & 0x3f;
592*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_RXIQIMBALANCE(0), 0x3ff,
593*551b7c5eSAhmad Khalifa 	    rx_candidate->iqk_before);
594*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_RXIQIMBALANCE(0), 0xfc00, val << 10);
595*551b7c5eSAhmad Khalifa 
596*551b7c5eSAhmad Khalifa 	val = (rx_candidate->iqk_after >> 6) & 0xf;
597*551b7c5eSAhmad Khalifa 	rtwn_setbits_4(sc, R92C_OFDM0_RXIQEXTANTA, 0xf0000000, val << 28);
598*551b7c5eSAhmad Khalifa done:
599*551b7c5eSAhmad Khalifa 	/* Restore GNT_BT. */
600*551b7c5eSAhmad Khalifa 	rtwn_write_4(sc, R23B_GNT_BT, gnt_bt);
601*551b7c5eSAhmad Khalifa 
602*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, R88E_RF_WE_LUT, 0, 0x80000);
603*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_RCK_OS, 0x18000);
604*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0x0001f);
605*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, R92C_RF_TXPA_G(0), 0xe6177);
606*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, 0xed, 0, 0x20);
607*551b7c5eSAhmad Khalifa 	rtwn_rf_write(sc, 0, 0x43, 0x300bd);
608*551b7c5eSAhmad Khalifa 
609*551b7c5eSAhmad Khalifa #ifndef RTWN_WITHOUT_UCODE
610*551b7c5eSAhmad Khalifa 	/* Inform firmware that we're done. */
611*551b7c5eSAhmad Khalifa 	r88e_fw_cmd(sc, R23B_CMD_WLAN_CALIB, R23B_CMD_WLAN_CALIB_END,
612*551b7c5eSAhmad Khalifa 	    sizeof(R23B_CMD_WLAN_CALIB_END));
613*551b7c5eSAhmad Khalifa #endif
614*551b7c5eSAhmad Khalifa }
615*551b7c5eSAhmad Khalifa 
616*551b7c5eSAhmad Khalifa void
r23b_temp_measure(struct rtwn_softc * sc)617*551b7c5eSAhmad Khalifa r23b_temp_measure(struct rtwn_softc *sc)
618*551b7c5eSAhmad Khalifa {
619*551b7c5eSAhmad Khalifa 	rtwn_rf_setbits(sc, 0, R88E_RF_T_METER, 0, R88E_RF_T_METER_START);
620*551b7c5eSAhmad Khalifa }
621