1e3037485SYan-Hsuan Chuang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2e3037485SYan-Hsuan Chuang /* Copyright(c) 2018-2019  Realtek Corporation
3e3037485SYan-Hsuan Chuang  */
4e3037485SYan-Hsuan Chuang 
5e3037485SYan-Hsuan Chuang #include "main.h"
6e3037485SYan-Hsuan Chuang #include "mac.h"
7e3037485SYan-Hsuan Chuang #include "reg.h"
8e3037485SYan-Hsuan Chuang #include "fw.h"
9e3037485SYan-Hsuan Chuang #include "debug.h"
1065371a3fSMartin Blumenstingl #include "sdio.h"
11e3037485SYan-Hsuan Chuang 
rtw_set_channel_mac(struct rtw_dev * rtwdev,u8 channel,u8 bw,u8 primary_ch_idx)12e3037485SYan-Hsuan Chuang void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw,
13e3037485SYan-Hsuan Chuang 			 u8 primary_ch_idx)
14e3037485SYan-Hsuan Chuang {
15e3037485SYan-Hsuan Chuang 	u8 txsc40 = 0, txsc20 = 0;
16e3037485SYan-Hsuan Chuang 	u32 value32;
17e3037485SYan-Hsuan Chuang 	u8 value8;
18e3037485SYan-Hsuan Chuang 
19e3037485SYan-Hsuan Chuang 	txsc20 = primary_ch_idx;
2073a2d0b8SPing-Ke Shih 	if (bw == RTW_CHANNEL_WIDTH_80) {
2140fb04b2SPing-Ke Shih 		if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST)
2240fb04b2SPing-Ke Shih 			txsc40 = RTW_SC_40_UPPER;
23e3037485SYan-Hsuan Chuang 		else
2440fb04b2SPing-Ke Shih 			txsc40 = RTW_SC_40_LOWER;
2573a2d0b8SPing-Ke Shih 	}
26e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_DATA_SC,
27e3037485SYan-Hsuan Chuang 		   BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40));
28e3037485SYan-Hsuan Chuang 
29e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_WMAC_TRXPTCL_CTL);
30e3037485SYan-Hsuan Chuang 	value32 &= ~BIT_RFMOD;
31e3037485SYan-Hsuan Chuang 	switch (bw) {
32e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_80:
33e3037485SYan-Hsuan Chuang 		value32 |= BIT_RFMOD_80M;
34e3037485SYan-Hsuan Chuang 		break;
35e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_40:
36e3037485SYan-Hsuan Chuang 		value32 |= BIT_RFMOD_40M;
37e3037485SYan-Hsuan Chuang 		break;
38e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_20:
39e3037485SYan-Hsuan Chuang 	default:
40e3037485SYan-Hsuan Chuang 		break;
41e3037485SYan-Hsuan Chuang 	}
42e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_WMAC_TRXPTCL_CTL, value32);
43e3037485SYan-Hsuan Chuang 
445f028a9cSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
455f028a9cSPing-Ke Shih 		return;
465f028a9cSPing-Ke Shih 
47e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_AFE_CTRL1) & ~(BIT_MAC_CLK_SEL);
48e3037485SYan-Hsuan Chuang 	value32 |= (MAC_CLK_HW_DEF_80M << BIT_SHIFT_MAC_CLK_SEL);
49e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_AFE_CTRL1, value32);
50e3037485SYan-Hsuan Chuang 
51e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED);
52e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED);
53e3037485SYan-Hsuan Chuang 
54e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_CCK_CHECK);
55e3037485SYan-Hsuan Chuang 	value8 = value8 & ~BIT_CHECK_CCK_EN;
568575b534SYan-Hsuan Chuang 	if (IS_CH_5G_BAND(channel))
57e3037485SYan-Hsuan Chuang 		value8 |= BIT_CHECK_CCK_EN;
58e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CCK_CHECK, value8);
59e3037485SYan-Hsuan Chuang }
60449be866SZong-Zhe Yang EXPORT_SYMBOL(rtw_set_channel_mac);
61e3037485SYan-Hsuan Chuang 
rtw_mac_pre_system_cfg(struct rtw_dev * rtwdev)62e3037485SYan-Hsuan Chuang static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev)
63e3037485SYan-Hsuan Chuang {
64b722e5b1SMartin Blumenstingl 	unsigned int retry;
65e3037485SYan-Hsuan Chuang 	u32 value32;
66e3037485SYan-Hsuan Chuang 	u8 value8;
67e3037485SYan-Hsuan Chuang 
68e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RSV_CTRL, 0);
69e3037485SYan-Hsuan Chuang 
704e223a5fSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev)) {
714e223a5fSPing-Ke Shih 		if (rtw_read32(rtwdev, REG_SYS_CFG1) & BIT_LDO)
724e223a5fSPing-Ke Shih 			rtw_write8(rtwdev, REG_LDO_SWR_CTRL, LDO_SEL);
734e223a5fSPing-Ke Shih 		else
744e223a5fSPing-Ke Shih 			rtw_write8(rtwdev, REG_LDO_SWR_CTRL, SPS_SEL);
754e223a5fSPing-Ke Shih 		return 0;
764e223a5fSPing-Ke Shih 	}
774e223a5fSPing-Ke Shih 
78e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
79e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
80fc6234d7SKevin Lo 		rtw_write32_set(rtwdev, REG_HCI_OPT_CTRL, BIT_USB_SUS_DIS);
81e3037485SYan-Hsuan Chuang 		break;
82b722e5b1SMartin Blumenstingl 	case RTW_HCI_TYPE_SDIO:
83b722e5b1SMartin Blumenstingl 		rtw_write8_clr(rtwdev, REG_SDIO_HSUS_CTRL, BIT_HCI_SUS_REQ);
84b722e5b1SMartin Blumenstingl 
85b722e5b1SMartin Blumenstingl 		for (retry = 0; retry < RTW_PWR_POLLING_CNT; retry++) {
86b722e5b1SMartin Blumenstingl 			if (rtw_read8(rtwdev, REG_SDIO_HSUS_CTRL) & BIT_HCI_RESUME_RDY)
87b722e5b1SMartin Blumenstingl 				break;
88b722e5b1SMartin Blumenstingl 
89b722e5b1SMartin Blumenstingl 			usleep_range(10, 50);
90b722e5b1SMartin Blumenstingl 		}
91b722e5b1SMartin Blumenstingl 
92b722e5b1SMartin Blumenstingl 		if (retry == RTW_PWR_POLLING_CNT) {
93b722e5b1SMartin Blumenstingl 			rtw_err(rtwdev, "failed to poll REG_SDIO_HSUS_CTRL[1]");
94b722e5b1SMartin Blumenstingl 			return -ETIMEDOUT;
95b722e5b1SMartin Blumenstingl 		}
96b722e5b1SMartin Blumenstingl 
97b722e5b1SMartin Blumenstingl 		if (rtw_sdio_is_sdio30_supported(rtwdev))
98b722e5b1SMartin Blumenstingl 			rtw_write8_set(rtwdev, REG_HCI_OPT_CTRL + 2,
99b722e5b1SMartin Blumenstingl 				       BIT_SDIO_PAD_E5 >> 16);
100b722e5b1SMartin Blumenstingl 		else
101b722e5b1SMartin Blumenstingl 			rtw_write8_clr(rtwdev, REG_HCI_OPT_CTRL + 2,
102b722e5b1SMartin Blumenstingl 				       BIT_SDIO_PAD_E5 >> 16);
103b722e5b1SMartin Blumenstingl 		break;
104e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
105e3037485SYan-Hsuan Chuang 		break;
106e3037485SYan-Hsuan Chuang 	default:
107e3037485SYan-Hsuan Chuang 		return -EINVAL;
108e3037485SYan-Hsuan Chuang 	}
109e3037485SYan-Hsuan Chuang 
110e3037485SYan-Hsuan Chuang 	/* config PIN Mux */
111e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_PAD_CTRL1);
112e3037485SYan-Hsuan Chuang 	value32 |= BIT_PAPE_WLBT_SEL | BIT_LNAON_WLBT_SEL;
113e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_PAD_CTRL1, value32);
114e3037485SYan-Hsuan Chuang 
115e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_LED_CFG);
116e3037485SYan-Hsuan Chuang 	value32 &= ~(BIT_PAPE_SEL_EN | BIT_LNAON_SEL_EN);
117e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_LED_CFG, value32);
118e3037485SYan-Hsuan Chuang 
119e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_GPIO_MUXCFG);
120e3037485SYan-Hsuan Chuang 	value32 |= BIT_WLRFE_4_5_EN;
121e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_GPIO_MUXCFG, value32);
122e3037485SYan-Hsuan Chuang 
123e3037485SYan-Hsuan Chuang 	/* disable BB/RF */
124e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
125e3037485SYan-Hsuan Chuang 	value8 &= ~(BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST);
126e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_SYS_FUNC_EN, value8);
127e3037485SYan-Hsuan Chuang 
128e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_RF_CTRL);
129e3037485SYan-Hsuan Chuang 	value8 &= ~(BIT_RF_SDM_RSTB | BIT_RF_RSTB | BIT_RF_EN);
130e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RF_CTRL, value8);
131e3037485SYan-Hsuan Chuang 
132e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_WLRF1);
133e3037485SYan-Hsuan Chuang 	value32 &= ~BIT_WLRF1_BBRF_EN;
134e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_WLRF1, value32);
135e3037485SYan-Hsuan Chuang 
136e3037485SYan-Hsuan Chuang 	return 0;
137e3037485SYan-Hsuan Chuang }
138e3037485SYan-Hsuan Chuang 
do_pwr_poll_cmd(struct rtw_dev * rtwdev,u32 addr,u32 mask,u32 target)139fd9ead38SPing-Ke Shih static bool do_pwr_poll_cmd(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target)
140fd9ead38SPing-Ke Shih {
14186fbf264SBrian Norris 	u32 val;
142fd9ead38SPing-Ke Shih 
143fd9ead38SPing-Ke Shih 	target &= mask;
144fd9ead38SPing-Ke Shih 
14586fbf264SBrian Norris 	return read_poll_timeout_atomic(rtw_read8, val, (val & mask) == target,
14686fbf264SBrian Norris 					50, 50 * RTW_PWR_POLLING_CNT, false,
14786fbf264SBrian Norris 					rtwdev, addr) == 0;
148fd9ead38SPing-Ke Shih }
149fd9ead38SPing-Ke Shih 
rtw_pwr_cmd_polling(struct rtw_dev * rtwdev,const struct rtw_pwr_seq_cmd * cmd)150e3037485SYan-Hsuan Chuang static int rtw_pwr_cmd_polling(struct rtw_dev *rtwdev,
151d49f2c50SJoe Perches 			       const struct rtw_pwr_seq_cmd *cmd)
152e3037485SYan-Hsuan Chuang {
153e3037485SYan-Hsuan Chuang 	u8 value;
154e3037485SYan-Hsuan Chuang 	u32 offset;
155e3037485SYan-Hsuan Chuang 
156e3037485SYan-Hsuan Chuang 	if (cmd->base == RTW_PWR_ADDR_SDIO)
157e3037485SYan-Hsuan Chuang 		offset = cmd->offset | SDIO_LOCAL_OFFSET;
158e3037485SYan-Hsuan Chuang 	else
159e3037485SYan-Hsuan Chuang 		offset = cmd->offset;
160e3037485SYan-Hsuan Chuang 
161fd9ead38SPing-Ke Shih 	if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value))
162e3037485SYan-Hsuan Chuang 		return 0;
1634e223a5fSPing-Ke Shih 
164fd9ead38SPing-Ke Shih 	if (rtw_hci_type(rtwdev) != RTW_HCI_TYPE_PCIE)
165fd9ead38SPing-Ke Shih 		goto err;
166fd9ead38SPing-Ke Shih 
167fd9ead38SPing-Ke Shih 	/* if PCIE, toggle BIT_PFM_WOWL and try again */
168fd9ead38SPing-Ke Shih 	value = rtw_read8(rtwdev, REG_SYS_PW_CTRL);
169fd9ead38SPing-Ke Shih 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D)
170fd9ead38SPing-Ke Shih 		rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL);
171fd9ead38SPing-Ke Shih 	rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL);
172fd9ead38SPing-Ke Shih 	rtw_write8(rtwdev, REG_SYS_PW_CTRL, value & ~BIT_PFM_WOWL);
173fd9ead38SPing-Ke Shih 	if (rtwdev->chip->id == RTW_CHIP_TYPE_8723D)
174fd9ead38SPing-Ke Shih 		rtw_write8(rtwdev, REG_SYS_PW_CTRL, value | BIT_PFM_WOWL);
175fd9ead38SPing-Ke Shih 
176fd9ead38SPing-Ke Shih 	if (do_pwr_poll_cmd(rtwdev, offset, cmd->mask, cmd->value))
177fd9ead38SPing-Ke Shih 		return 0;
178fd9ead38SPing-Ke Shih 
179fd9ead38SPing-Ke Shih err:
180fd9ead38SPing-Ke Shih 	rtw_err(rtwdev, "failed to poll offset=0x%x mask=0x%x value=0x%x\n",
181fd9ead38SPing-Ke Shih 		offset, cmd->mask, cmd->value);
182e3037485SYan-Hsuan Chuang 	return -EBUSY;
183e3037485SYan-Hsuan Chuang }
184e3037485SYan-Hsuan Chuang 
rtw_sub_pwr_seq_parser(struct rtw_dev * rtwdev,u8 intf_mask,u8 cut_mask,const struct rtw_pwr_seq_cmd * cmd)185e3037485SYan-Hsuan Chuang static int rtw_sub_pwr_seq_parser(struct rtw_dev *rtwdev, u8 intf_mask,
186d49f2c50SJoe Perches 				  u8 cut_mask,
187d49f2c50SJoe Perches 				  const struct rtw_pwr_seq_cmd *cmd)
188e3037485SYan-Hsuan Chuang {
189d49f2c50SJoe Perches 	const struct rtw_pwr_seq_cmd *cur_cmd;
190e3037485SYan-Hsuan Chuang 	u32 offset;
191e3037485SYan-Hsuan Chuang 	u8 value;
192e3037485SYan-Hsuan Chuang 
193e3037485SYan-Hsuan Chuang 	for (cur_cmd = cmd; cur_cmd->cmd != RTW_PWR_CMD_END; cur_cmd++) {
194e3037485SYan-Hsuan Chuang 		if (!(cur_cmd->intf_mask & intf_mask) ||
195e3037485SYan-Hsuan Chuang 		    !(cur_cmd->cut_mask & cut_mask))
196e3037485SYan-Hsuan Chuang 			continue;
197e3037485SYan-Hsuan Chuang 
198e3037485SYan-Hsuan Chuang 		switch (cur_cmd->cmd) {
199e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_WRITE:
200e3037485SYan-Hsuan Chuang 			offset = cur_cmd->offset;
201e3037485SYan-Hsuan Chuang 
202e3037485SYan-Hsuan Chuang 			if (cur_cmd->base == RTW_PWR_ADDR_SDIO)
203e3037485SYan-Hsuan Chuang 				offset |= SDIO_LOCAL_OFFSET;
204e3037485SYan-Hsuan Chuang 
205e3037485SYan-Hsuan Chuang 			value = rtw_read8(rtwdev, offset);
206e3037485SYan-Hsuan Chuang 			value &= ~cur_cmd->mask;
207e3037485SYan-Hsuan Chuang 			value |= (cur_cmd->value & cur_cmd->mask);
208e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, offset, value);
209e3037485SYan-Hsuan Chuang 			break;
210e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_POLLING:
211e3037485SYan-Hsuan Chuang 			if (rtw_pwr_cmd_polling(rtwdev, cur_cmd))
212e3037485SYan-Hsuan Chuang 				return -EBUSY;
213e3037485SYan-Hsuan Chuang 			break;
214e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_DELAY:
215e3037485SYan-Hsuan Chuang 			if (cur_cmd->value == RTW_PWR_DELAY_US)
216e3037485SYan-Hsuan Chuang 				udelay(cur_cmd->offset);
217e3037485SYan-Hsuan Chuang 			else
218e3037485SYan-Hsuan Chuang 				mdelay(cur_cmd->offset);
219e3037485SYan-Hsuan Chuang 			break;
220e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_READ:
221e3037485SYan-Hsuan Chuang 			break;
222e3037485SYan-Hsuan Chuang 		default:
223e3037485SYan-Hsuan Chuang 			return -EINVAL;
224e3037485SYan-Hsuan Chuang 		}
225e3037485SYan-Hsuan Chuang 	}
226e3037485SYan-Hsuan Chuang 
227e3037485SYan-Hsuan Chuang 	return 0;
228e3037485SYan-Hsuan Chuang }
229e3037485SYan-Hsuan Chuang 
rtw_pwr_seq_parser(struct rtw_dev * rtwdev,const struct rtw_pwr_seq_cmd ** cmd_seq)230e3037485SYan-Hsuan Chuang static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev,
231d49f2c50SJoe Perches 			      const struct rtw_pwr_seq_cmd **cmd_seq)
232e3037485SYan-Hsuan Chuang {
233e3037485SYan-Hsuan Chuang 	u8 cut_mask;
234e3037485SYan-Hsuan Chuang 	u8 intf_mask;
235e3037485SYan-Hsuan Chuang 	u8 cut;
236e3037485SYan-Hsuan Chuang 	u32 idx = 0;
237d49f2c50SJoe Perches 	const struct rtw_pwr_seq_cmd *cmd;
238e3037485SYan-Hsuan Chuang 	int ret;
239e3037485SYan-Hsuan Chuang 
240e3037485SYan-Hsuan Chuang 	cut = rtwdev->hal.cut_version;
241e3037485SYan-Hsuan Chuang 	cut_mask = cut_version_to_mask(cut);
242e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
243e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
24424d54855SMartin Blumenstingl 		intf_mask = RTW_PWR_INTF_PCI_MSK;
245e3037485SYan-Hsuan Chuang 		break;
246e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
24724d54855SMartin Blumenstingl 		intf_mask = RTW_PWR_INTF_USB_MSK;
248e3037485SYan-Hsuan Chuang 		break;
24996c79da2SMartin Blumenstingl 	case RTW_HCI_TYPE_SDIO:
25096c79da2SMartin Blumenstingl 		intf_mask = RTW_PWR_INTF_SDIO_MSK;
25196c79da2SMartin Blumenstingl 		break;
252e3037485SYan-Hsuan Chuang 	default:
253e3037485SYan-Hsuan Chuang 		return -EINVAL;
254e3037485SYan-Hsuan Chuang 	}
255e3037485SYan-Hsuan Chuang 
256e3037485SYan-Hsuan Chuang 	do {
257e3037485SYan-Hsuan Chuang 		cmd = cmd_seq[idx];
258e3037485SYan-Hsuan Chuang 		if (!cmd)
259e3037485SYan-Hsuan Chuang 			break;
260e3037485SYan-Hsuan Chuang 
261e3037485SYan-Hsuan Chuang 		ret = rtw_sub_pwr_seq_parser(rtwdev, intf_mask, cut_mask, cmd);
262e3037485SYan-Hsuan Chuang 		if (ret)
263b7ed9fa2SMartin Blumenstingl 			return ret;
264e3037485SYan-Hsuan Chuang 
265e3037485SYan-Hsuan Chuang 		idx++;
266e3037485SYan-Hsuan Chuang 	} while (1);
267e3037485SYan-Hsuan Chuang 
268e3037485SYan-Hsuan Chuang 	return 0;
269e3037485SYan-Hsuan Chuang }
270e3037485SYan-Hsuan Chuang 
rtw_mac_power_switch(struct rtw_dev * rtwdev,bool pwr_on)271e3037485SYan-Hsuan Chuang static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on)
272e3037485SYan-Hsuan Chuang {
273dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
274d49f2c50SJoe Perches 	const struct rtw_pwr_seq_cmd **pwr_seq;
275b722e5b1SMartin Blumenstingl 	u32 imr = 0;
276e3037485SYan-Hsuan Chuang 	u8 rpwm;
277e3037485SYan-Hsuan Chuang 	bool cur_pwr;
27815c8e267SMartin Blumenstingl 	int ret;
279e3037485SYan-Hsuan Chuang 
2804e223a5fSPing-Ke Shih 	if (rtw_chip_wcpu_11ac(rtwdev)) {
281e3037485SYan-Hsuan Chuang 		rpwm = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
282e3037485SYan-Hsuan Chuang 
283e3037485SYan-Hsuan Chuang 		/* Check FW still exist or not */
284e3037485SYan-Hsuan Chuang 		if (rtw_read16(rtwdev, REG_MCUFW_CTRL) == 0xC078) {
285e3037485SYan-Hsuan Chuang 			rpwm = (rpwm ^ BIT_RPWM_TOGGLE) & BIT_RPWM_TOGGLE;
286e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, rpwm);
287e3037485SYan-Hsuan Chuang 		}
2884e223a5fSPing-Ke Shih 	}
289e3037485SYan-Hsuan Chuang 
290e3037485SYan-Hsuan Chuang 	if (rtw_read8(rtwdev, REG_CR) == 0xea)
291e3037485SYan-Hsuan Chuang 		cur_pwr = false;
292e3037485SYan-Hsuan Chuang 	else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
293e3037485SYan-Hsuan Chuang 		 (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0)))
294e3037485SYan-Hsuan Chuang 		cur_pwr = false;
295e3037485SYan-Hsuan Chuang 	else
296e3037485SYan-Hsuan Chuang 		cur_pwr = true;
297e3037485SYan-Hsuan Chuang 
2984e223a5fSPing-Ke Shih 	if (pwr_on == cur_pwr)
299e3037485SYan-Hsuan Chuang 		return -EALREADY;
300e3037485SYan-Hsuan Chuang 
301b722e5b1SMartin Blumenstingl 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
302b722e5b1SMartin Blumenstingl 		imr = rtw_read32(rtwdev, REG_SDIO_HIMR);
303b722e5b1SMartin Blumenstingl 		rtw_write32(rtwdev, REG_SDIO_HIMR, 0);
304b722e5b1SMartin Blumenstingl 	}
305b722e5b1SMartin Blumenstingl 
3066a925660SMartin Blumenstingl 	if (!pwr_on)
3076a925660SMartin Blumenstingl 		clear_bit(RTW_FLAG_POWERON, rtwdev->flags);
3086a925660SMartin Blumenstingl 
309e3037485SYan-Hsuan Chuang 	pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq;
31015c8e267SMartin Blumenstingl 	ret = rtw_pwr_seq_parser(rtwdev, pwr_seq);
311e3037485SYan-Hsuan Chuang 
312648b41f2SBitterblue Smith 	if (pwr_on && rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) {
313648b41f2SBitterblue Smith 		if (chip->id == RTW_CHIP_TYPE_8822C ||
314648b41f2SBitterblue Smith 		    chip->id == RTW_CHIP_TYPE_8822B ||
315648b41f2SBitterblue Smith 		    chip->id == RTW_CHIP_TYPE_8821C)
316648b41f2SBitterblue Smith 			rtw_write8_clr(rtwdev, REG_SYS_STATUS1 + 1, BIT(0));
317648b41f2SBitterblue Smith 	}
318648b41f2SBitterblue Smith 
319b722e5b1SMartin Blumenstingl 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO)
320b722e5b1SMartin Blumenstingl 		rtw_write32(rtwdev, REG_SDIO_HIMR, imr);
321b722e5b1SMartin Blumenstingl 
322b722e5b1SMartin Blumenstingl 	if (!ret && pwr_on)
3234a267bc5SPing-Ke Shih 		set_bit(RTW_FLAG_POWERON, rtwdev->flags);
3244a267bc5SPing-Ke Shih 
325b722e5b1SMartin Blumenstingl 	return ret;
326e3037485SYan-Hsuan Chuang }
327e3037485SYan-Hsuan Chuang 
__rtw_mac_init_system_cfg(struct rtw_dev * rtwdev)3284e223a5fSPing-Ke Shih static int __rtw_mac_init_system_cfg(struct rtw_dev *rtwdev)
329e3037485SYan-Hsuan Chuang {
330e3037485SYan-Hsuan Chuang 	u8 sys_func_en = rtwdev->chip->sys_func_en;
331e3037485SYan-Hsuan Chuang 	u8 value8;
332e3037485SYan-Hsuan Chuang 	u32 value, tmp;
333e3037485SYan-Hsuan Chuang 
334e3037485SYan-Hsuan Chuang 	value = rtw_read32(rtwdev, REG_CPU_DMEM_CON);
335e3037485SYan-Hsuan Chuang 	value |= BIT_WL_PLATFORM_RST | BIT_DDMA_EN;
336e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_CPU_DMEM_CON, value);
337e3037485SYan-Hsuan Chuang 
338a4835410SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, sys_func_en);
339e3037485SYan-Hsuan Chuang 	value8 = (rtw_read8(rtwdev, REG_CR_EXT + 3) & 0xF0) | 0x0C;
340e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR_EXT + 3, value8);
341e3037485SYan-Hsuan Chuang 
342e3037485SYan-Hsuan Chuang 	/* disable boot-from-flash for driver's DL FW */
343e3037485SYan-Hsuan Chuang 	tmp = rtw_read32(rtwdev, REG_MCUFW_CTRL);
344e3037485SYan-Hsuan Chuang 	if (tmp & BIT_BOOT_FSPI_EN) {
345e3037485SYan-Hsuan Chuang 		rtw_write32(rtwdev, REG_MCUFW_CTRL, tmp & (~BIT_BOOT_FSPI_EN));
346e3037485SYan-Hsuan Chuang 		value = rtw_read32(rtwdev, REG_GPIO_MUXCFG) & (~BIT_FSPI_EN);
347e3037485SYan-Hsuan Chuang 		rtw_write32(rtwdev, REG_GPIO_MUXCFG, value);
348e3037485SYan-Hsuan Chuang 	}
349e3037485SYan-Hsuan Chuang 
350e3037485SYan-Hsuan Chuang 	return 0;
351e3037485SYan-Hsuan Chuang }
352e3037485SYan-Hsuan Chuang 
__rtw_mac_init_system_cfg_legacy(struct rtw_dev * rtwdev)3534e223a5fSPing-Ke Shih static int __rtw_mac_init_system_cfg_legacy(struct rtw_dev *rtwdev)
3544e223a5fSPing-Ke Shih {
3554e223a5fSPing-Ke Shih 	rtw_write8(rtwdev, REG_CR, 0xff);
3564e223a5fSPing-Ke Shih 	mdelay(2);
3574e223a5fSPing-Ke Shih 	rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0x7f);
3584e223a5fSPing-Ke Shih 	mdelay(2);
3594e223a5fSPing-Ke Shih 
3604e223a5fSPing-Ke Shih 	rtw_write8_set(rtwdev, REG_SYS_CLKR, BIT_WAKEPAD_EN);
3614e223a5fSPing-Ke Shih 	rtw_write16_clr(rtwdev, REG_GPIO_MUXCFG, BIT_EN_SIC);
3624e223a5fSPing-Ke Shih 
3634e223a5fSPing-Ke Shih 	rtw_write16(rtwdev, REG_CR, 0x2ff);
3644e223a5fSPing-Ke Shih 
3654e223a5fSPing-Ke Shih 	return 0;
3664e223a5fSPing-Ke Shih }
3674e223a5fSPing-Ke Shih 
rtw_mac_init_system_cfg(struct rtw_dev * rtwdev)3684e223a5fSPing-Ke Shih static int rtw_mac_init_system_cfg(struct rtw_dev *rtwdev)
3694e223a5fSPing-Ke Shih {
3704e223a5fSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
3714e223a5fSPing-Ke Shih 		return __rtw_mac_init_system_cfg_legacy(rtwdev);
3724e223a5fSPing-Ke Shih 
3734e223a5fSPing-Ke Shih 	return __rtw_mac_init_system_cfg(rtwdev);
3744e223a5fSPing-Ke Shih }
3754e223a5fSPing-Ke Shih 
rtw_mac_power_on(struct rtw_dev * rtwdev)376e3037485SYan-Hsuan Chuang int rtw_mac_power_on(struct rtw_dev *rtwdev)
377e3037485SYan-Hsuan Chuang {
378e3037485SYan-Hsuan Chuang 	int ret = 0;
379e3037485SYan-Hsuan Chuang 
380e3037485SYan-Hsuan Chuang 	ret = rtw_mac_pre_system_cfg(rtwdev);
381e3037485SYan-Hsuan Chuang 	if (ret)
382e3037485SYan-Hsuan Chuang 		goto err;
383e3037485SYan-Hsuan Chuang 
384e3037485SYan-Hsuan Chuang 	ret = rtw_mac_power_switch(rtwdev, true);
385d41673b9SYan-Hsuan Chuang 	if (ret == -EALREADY) {
386d41673b9SYan-Hsuan Chuang 		rtw_mac_power_switch(rtwdev, false);
3874a267bc5SPing-Ke Shih 
3884a267bc5SPing-Ke Shih 		ret = rtw_mac_pre_system_cfg(rtwdev);
3894a267bc5SPing-Ke Shih 		if (ret)
3904a267bc5SPing-Ke Shih 			goto err;
3914a267bc5SPing-Ke Shih 
392d41673b9SYan-Hsuan Chuang 		ret = rtw_mac_power_switch(rtwdev, true);
393e3037485SYan-Hsuan Chuang 		if (ret)
394e3037485SYan-Hsuan Chuang 			goto err;
395d41673b9SYan-Hsuan Chuang 	} else if (ret) {
396d41673b9SYan-Hsuan Chuang 		goto err;
397d41673b9SYan-Hsuan Chuang 	}
398e3037485SYan-Hsuan Chuang 
399e3037485SYan-Hsuan Chuang 	ret = rtw_mac_init_system_cfg(rtwdev);
400e3037485SYan-Hsuan Chuang 	if (ret)
401e3037485SYan-Hsuan Chuang 		goto err;
402e3037485SYan-Hsuan Chuang 
403e3037485SYan-Hsuan Chuang 	return 0;
404e3037485SYan-Hsuan Chuang 
405e3037485SYan-Hsuan Chuang err:
406e3037485SYan-Hsuan Chuang 	rtw_err(rtwdev, "mac power on failed");
407e3037485SYan-Hsuan Chuang 	return ret;
408e3037485SYan-Hsuan Chuang }
409e3037485SYan-Hsuan Chuang 
rtw_mac_power_off(struct rtw_dev * rtwdev)410e3037485SYan-Hsuan Chuang void rtw_mac_power_off(struct rtw_dev *rtwdev)
411e3037485SYan-Hsuan Chuang {
412e3037485SYan-Hsuan Chuang 	rtw_mac_power_switch(rtwdev, false);
413e3037485SYan-Hsuan Chuang }
414e3037485SYan-Hsuan Chuang 
check_firmware_size(const u8 * data,u32 size)415e3037485SYan-Hsuan Chuang static bool check_firmware_size(const u8 *data, u32 size)
416e3037485SYan-Hsuan Chuang {
417cc20a713SPing-Ke Shih 	const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data;
418e3037485SYan-Hsuan Chuang 	u32 dmem_size;
419e3037485SYan-Hsuan Chuang 	u32 imem_size;
420e3037485SYan-Hsuan Chuang 	u32 emem_size;
421e3037485SYan-Hsuan Chuang 	u32 real_size;
422e3037485SYan-Hsuan Chuang 
423cc20a713SPing-Ke Shih 	dmem_size = le32_to_cpu(fw_hdr->dmem_size);
424cc20a713SPing-Ke Shih 	imem_size = le32_to_cpu(fw_hdr->imem_size);
425cc20a713SPing-Ke Shih 	emem_size = (fw_hdr->mem_usage & BIT(4)) ?
426cc20a713SPing-Ke Shih 		    le32_to_cpu(fw_hdr->emem_size) : 0;
427e3037485SYan-Hsuan Chuang 
428e3037485SYan-Hsuan Chuang 	dmem_size += FW_HDR_CHKSUM_SIZE;
429e3037485SYan-Hsuan Chuang 	imem_size += FW_HDR_CHKSUM_SIZE;
430e3037485SYan-Hsuan Chuang 	emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0;
431e3037485SYan-Hsuan Chuang 	real_size = FW_HDR_SIZE + dmem_size + imem_size + emem_size;
432e3037485SYan-Hsuan Chuang 	if (real_size != size)
433e3037485SYan-Hsuan Chuang 		return false;
434e3037485SYan-Hsuan Chuang 
435e3037485SYan-Hsuan Chuang 	return true;
436e3037485SYan-Hsuan Chuang }
437e3037485SYan-Hsuan Chuang 
wlan_cpu_enable(struct rtw_dev * rtwdev,bool enable)438e3037485SYan-Hsuan Chuang static void wlan_cpu_enable(struct rtw_dev *rtwdev, bool enable)
439e3037485SYan-Hsuan Chuang {
440e3037485SYan-Hsuan Chuang 	if (enable) {
441e3037485SYan-Hsuan Chuang 		/* cpu io interface enable */
442e3037485SYan-Hsuan Chuang 		rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF);
443e3037485SYan-Hsuan Chuang 
444e3037485SYan-Hsuan Chuang 		/* cpu enable */
445e3037485SYan-Hsuan Chuang 		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
446e3037485SYan-Hsuan Chuang 	} else {
447e3037485SYan-Hsuan Chuang 		/* cpu io interface disable */
448e3037485SYan-Hsuan Chuang 		rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
449e3037485SYan-Hsuan Chuang 
450e3037485SYan-Hsuan Chuang 		/* cpu disable */
451e3037485SYan-Hsuan Chuang 		rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF);
452e3037485SYan-Hsuan Chuang 	}
453e3037485SYan-Hsuan Chuang }
454e3037485SYan-Hsuan Chuang 
455e3037485SYan-Hsuan Chuang #define DLFW_RESTORE_REG_NUM 6
456e3037485SYan-Hsuan Chuang 
download_firmware_reg_backup(struct rtw_dev * rtwdev,struct rtw_backup_info * bckp)457e3037485SYan-Hsuan Chuang static void download_firmware_reg_backup(struct rtw_dev *rtwdev,
458e3037485SYan-Hsuan Chuang 					 struct rtw_backup_info *bckp)
459e3037485SYan-Hsuan Chuang {
460e3037485SYan-Hsuan Chuang 	u8 tmp;
461e3037485SYan-Hsuan Chuang 	u8 bckp_idx = 0;
462e3037485SYan-Hsuan Chuang 
463e3037485SYan-Hsuan Chuang 	/* set HIQ to hi priority */
464e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
465e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_TXDMA_PQ_MAP + 1;
466e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read8(rtwdev, REG_TXDMA_PQ_MAP + 1);
467e3037485SYan-Hsuan Chuang 	bckp_idx++;
468e3037485SYan-Hsuan Chuang 	tmp = RTW_DMA_MAPPING_HIGH << 6;
469e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_TXDMA_PQ_MAP + 1, tmp);
470e3037485SYan-Hsuan Chuang 
471e3037485SYan-Hsuan Chuang 	/* DLFW only use HIQ, map HIQ to hi priority */
472e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
473e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_CR;
474e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read8(rtwdev, REG_CR);
475e3037485SYan-Hsuan Chuang 	bckp_idx++;
476e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 4;
477e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_H2CQ_CSR;
478e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = BIT_H2CQ_FULL;
479e3037485SYan-Hsuan Chuang 	bckp_idx++;
480e3037485SYan-Hsuan Chuang 	tmp = BIT_HCI_TXDMA_EN | BIT_TXDMA_EN;
481e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, tmp);
482e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL);
483e3037485SYan-Hsuan Chuang 
484e3037485SYan-Hsuan Chuang 	/* Config hi priority queue and public priority queue page number */
485e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 2;
486e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_FIFOPAGE_INFO_1;
487e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read16(rtwdev, REG_FIFOPAGE_INFO_1);
488e3037485SYan-Hsuan Chuang 	bckp_idx++;
489e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 4;
490e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_RQPN_CTRL_2;
491e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read32(rtwdev, REG_RQPN_CTRL_2) | BIT_LD_RQPN;
492e3037485SYan-Hsuan Chuang 	bckp_idx++;
493e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, 0x200);
494e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_RQPN_CTRL_2, bckp[bckp_idx - 1].val);
495e3037485SYan-Hsuan Chuang 
496b722e5b1SMartin Blumenstingl 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO)
497b722e5b1SMartin Blumenstingl 		rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
498b722e5b1SMartin Blumenstingl 
499e3037485SYan-Hsuan Chuang 	/* Disable beacon related functions */
500e3037485SYan-Hsuan Chuang 	tmp = rtw_read8(rtwdev, REG_BCN_CTRL);
501e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
502e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_BCN_CTRL;
503e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = tmp;
504e3037485SYan-Hsuan Chuang 	bckp_idx++;
505e3037485SYan-Hsuan Chuang 	tmp = (u8)((tmp & (~BIT_EN_BCN_FUNCTION)) | BIT_DIS_TSF_UDT);
506e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_BCN_CTRL, tmp);
507e3037485SYan-Hsuan Chuang 
508e3037485SYan-Hsuan Chuang 	WARN(bckp_idx != DLFW_RESTORE_REG_NUM, "wrong backup number\n");
509e3037485SYan-Hsuan Chuang }
510e3037485SYan-Hsuan Chuang 
download_firmware_reset_platform(struct rtw_dev * rtwdev)511e3037485SYan-Hsuan Chuang static void download_firmware_reset_platform(struct rtw_dev *rtwdev)
512e3037485SYan-Hsuan Chuang {
513e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16);
514e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8);
515e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16);
516e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8);
517e3037485SYan-Hsuan Chuang }
518e3037485SYan-Hsuan Chuang 
download_firmware_reg_restore(struct rtw_dev * rtwdev,struct rtw_backup_info * bckp,u8 bckp_num)519e3037485SYan-Hsuan Chuang static void download_firmware_reg_restore(struct rtw_dev *rtwdev,
520e3037485SYan-Hsuan Chuang 					  struct rtw_backup_info *bckp,
521e3037485SYan-Hsuan Chuang 					  u8 bckp_num)
522e3037485SYan-Hsuan Chuang {
523e3037485SYan-Hsuan Chuang 	rtw_restore_reg(rtwdev, bckp, bckp_num);
524e3037485SYan-Hsuan Chuang }
525e3037485SYan-Hsuan Chuang 
526e3037485SYan-Hsuan Chuang #define TX_DESC_SIZE 48
527e3037485SYan-Hsuan Chuang 
send_firmware_pkt_rsvd_page(struct rtw_dev * rtwdev,u16 pg_addr,const u8 * data,u32 size)528e3037485SYan-Hsuan Chuang static int send_firmware_pkt_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
529e3037485SYan-Hsuan Chuang 				       const u8 *data, u32 size)
530e3037485SYan-Hsuan Chuang {
531e3037485SYan-Hsuan Chuang 	u8 *buf;
532e3037485SYan-Hsuan Chuang 	int ret;
533e3037485SYan-Hsuan Chuang 
534e3037485SYan-Hsuan Chuang 	buf = kmemdup(data, size, GFP_KERNEL);
535e3037485SYan-Hsuan Chuang 	if (!buf)
536e3037485SYan-Hsuan Chuang 		return -ENOMEM;
537e3037485SYan-Hsuan Chuang 
538e3037485SYan-Hsuan Chuang 	ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size);
539e3037485SYan-Hsuan Chuang 	kfree(buf);
540e3037485SYan-Hsuan Chuang 	return ret;
541e3037485SYan-Hsuan Chuang }
542e3037485SYan-Hsuan Chuang 
543e3037485SYan-Hsuan Chuang static int
send_firmware_pkt(struct rtw_dev * rtwdev,u16 pg_addr,const u8 * data,u32 size)544e3037485SYan-Hsuan Chuang send_firmware_pkt(struct rtw_dev *rtwdev, u16 pg_addr, const u8 *data, u32 size)
545e3037485SYan-Hsuan Chuang {
546e3037485SYan-Hsuan Chuang 	int ret;
547e3037485SYan-Hsuan Chuang 
548e3037485SYan-Hsuan Chuang 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
549e3037485SYan-Hsuan Chuang 	    !((size + TX_DESC_SIZE) & (512 - 1)))
550e3037485SYan-Hsuan Chuang 		size += 1;
551e3037485SYan-Hsuan Chuang 
552e3037485SYan-Hsuan Chuang 	ret = send_firmware_pkt_rsvd_page(rtwdev, pg_addr, data, size);
553e3037485SYan-Hsuan Chuang 	if (ret)
554e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "failed to download rsvd page\n");
555e3037485SYan-Hsuan Chuang 
556e3037485SYan-Hsuan Chuang 	return ret;
557e3037485SYan-Hsuan Chuang }
558e3037485SYan-Hsuan Chuang 
559e3037485SYan-Hsuan Chuang static int
iddma_enable(struct rtw_dev * rtwdev,u32 src,u32 dst,u32 ctrl)560e3037485SYan-Hsuan Chuang iddma_enable(struct rtw_dev *rtwdev, u32 src, u32 dst, u32 ctrl)
561e3037485SYan-Hsuan Chuang {
562e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0SA, src);
563e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0DA, dst);
564e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0CTRL, ctrl);
565e3037485SYan-Hsuan Chuang 
566e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0))
567e3037485SYan-Hsuan Chuang 		return -EBUSY;
568e3037485SYan-Hsuan Chuang 
569e3037485SYan-Hsuan Chuang 	return 0;
570e3037485SYan-Hsuan Chuang }
571e3037485SYan-Hsuan Chuang 
iddma_download_firmware(struct rtw_dev * rtwdev,u32 src,u32 dst,u32 len,u8 first)572e3037485SYan-Hsuan Chuang static int iddma_download_firmware(struct rtw_dev *rtwdev, u32 src, u32 dst,
573e3037485SYan-Hsuan Chuang 				   u32 len, u8 first)
574e3037485SYan-Hsuan Chuang {
575e3037485SYan-Hsuan Chuang 	u32 ch0_ctrl = BIT_DDMACH0_CHKSUM_EN | BIT_DDMACH0_OWN;
576e3037485SYan-Hsuan Chuang 
577e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0))
578e3037485SYan-Hsuan Chuang 		return -EBUSY;
579e3037485SYan-Hsuan Chuang 
580e3037485SYan-Hsuan Chuang 	ch0_ctrl |= len & BIT_MASK_DDMACH0_DLEN;
581e3037485SYan-Hsuan Chuang 	if (!first)
582e3037485SYan-Hsuan Chuang 		ch0_ctrl |= BIT_DDMACH0_CHKSUM_CONT;
583e3037485SYan-Hsuan Chuang 
584e3037485SYan-Hsuan Chuang 	if (iddma_enable(rtwdev, src, dst, ch0_ctrl))
585e3037485SYan-Hsuan Chuang 		return -EBUSY;
586e3037485SYan-Hsuan Chuang 
587e3037485SYan-Hsuan Chuang 	return 0;
588e3037485SYan-Hsuan Chuang }
589e3037485SYan-Hsuan Chuang 
rtw_ddma_to_fw_fifo(struct rtw_dev * rtwdev,u32 ocp_src,u32 size)59013ce240aSZong-Zhe Yang int rtw_ddma_to_fw_fifo(struct rtw_dev *rtwdev, u32 ocp_src, u32 size)
59113ce240aSZong-Zhe Yang {
59213ce240aSZong-Zhe Yang 	u32 ch0_ctrl = BIT_DDMACH0_OWN | BIT_DDMACH0_DDMA_MODE;
59313ce240aSZong-Zhe Yang 
59413ce240aSZong-Zhe Yang 	if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0)) {
59513ce240aSZong-Zhe Yang 		rtw_dbg(rtwdev, RTW_DBG_FW, "busy to start ddma\n");
59613ce240aSZong-Zhe Yang 		return -EBUSY;
59713ce240aSZong-Zhe Yang 	}
59813ce240aSZong-Zhe Yang 
59913ce240aSZong-Zhe Yang 	ch0_ctrl |= size & BIT_MASK_DDMACH0_DLEN;
60013ce240aSZong-Zhe Yang 
60113ce240aSZong-Zhe Yang 	if (iddma_enable(rtwdev, ocp_src, OCPBASE_RXBUF_FW_88XX, ch0_ctrl)) {
60213ce240aSZong-Zhe Yang 		rtw_dbg(rtwdev, RTW_DBG_FW, "busy to complete ddma\n");
60313ce240aSZong-Zhe Yang 		return -EBUSY;
60413ce240aSZong-Zhe Yang 	}
60513ce240aSZong-Zhe Yang 
60613ce240aSZong-Zhe Yang 	return 0;
60713ce240aSZong-Zhe Yang }
60813ce240aSZong-Zhe Yang 
609e3037485SYan-Hsuan Chuang static bool
check_fw_checksum(struct rtw_dev * rtwdev,u32 addr)610e3037485SYan-Hsuan Chuang check_fw_checksum(struct rtw_dev *rtwdev, u32 addr)
611e3037485SYan-Hsuan Chuang {
612e3037485SYan-Hsuan Chuang 	u8 fw_ctrl;
613e3037485SYan-Hsuan Chuang 
614e3037485SYan-Hsuan Chuang 	fw_ctrl = rtw_read8(rtwdev, REG_MCUFW_CTRL);
615e3037485SYan-Hsuan Chuang 
616e3037485SYan-Hsuan Chuang 	if (rtw_read32(rtwdev, REG_DDMA_CH0CTRL) & BIT_DDMACH0_CHKSUM_STS) {
617e3037485SYan-Hsuan Chuang 		if (addr < OCPBASE_DMEM_88XX) {
618e3037485SYan-Hsuan Chuang 			fw_ctrl |= BIT_IMEM_DW_OK;
619e3037485SYan-Hsuan Chuang 			fw_ctrl &= ~BIT_IMEM_CHKSUM_OK;
620e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
621e3037485SYan-Hsuan Chuang 		} else {
622e3037485SYan-Hsuan Chuang 			fw_ctrl |= BIT_DMEM_DW_OK;
623e3037485SYan-Hsuan Chuang 			fw_ctrl &= ~BIT_DMEM_CHKSUM_OK;
624e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
625e3037485SYan-Hsuan Chuang 		}
626e3037485SYan-Hsuan Chuang 
627e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "invalid fw checksum\n");
628e3037485SYan-Hsuan Chuang 
629e3037485SYan-Hsuan Chuang 		return false;
630e3037485SYan-Hsuan Chuang 	}
631e3037485SYan-Hsuan Chuang 
632e3037485SYan-Hsuan Chuang 	if (addr < OCPBASE_DMEM_88XX) {
633e3037485SYan-Hsuan Chuang 		fw_ctrl |= (BIT_IMEM_DW_OK | BIT_IMEM_CHKSUM_OK);
634e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
635e3037485SYan-Hsuan Chuang 	} else {
636e3037485SYan-Hsuan Chuang 		fw_ctrl |= (BIT_DMEM_DW_OK | BIT_DMEM_CHKSUM_OK);
637e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
638e3037485SYan-Hsuan Chuang 	}
639e3037485SYan-Hsuan Chuang 
640e3037485SYan-Hsuan Chuang 	return true;
641e3037485SYan-Hsuan Chuang }
642e3037485SYan-Hsuan Chuang 
643e3037485SYan-Hsuan Chuang static int
download_firmware_to_mem(struct rtw_dev * rtwdev,const u8 * data,u32 src,u32 dst,u32 size)644e3037485SYan-Hsuan Chuang download_firmware_to_mem(struct rtw_dev *rtwdev, const u8 *data,
645e3037485SYan-Hsuan Chuang 			 u32 src, u32 dst, u32 size)
646e3037485SYan-Hsuan Chuang {
647dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
648e3037485SYan-Hsuan Chuang 	u32 desc_size = chip->tx_pkt_desc_sz;
649e3037485SYan-Hsuan Chuang 	u8 first_part;
650e3037485SYan-Hsuan Chuang 	u32 mem_offset;
651e3037485SYan-Hsuan Chuang 	u32 residue_size;
652e3037485SYan-Hsuan Chuang 	u32 pkt_size;
653e3037485SYan-Hsuan Chuang 	u32 max_size = 0x1000;
654e3037485SYan-Hsuan Chuang 	u32 val;
655e3037485SYan-Hsuan Chuang 	int ret;
656e3037485SYan-Hsuan Chuang 
657e3037485SYan-Hsuan Chuang 	mem_offset = 0;
658e3037485SYan-Hsuan Chuang 	first_part = 1;
659e3037485SYan-Hsuan Chuang 	residue_size = size;
660e3037485SYan-Hsuan Chuang 
661e3037485SYan-Hsuan Chuang 	val = rtw_read32(rtwdev, REG_DDMA_CH0CTRL);
662e3037485SYan-Hsuan Chuang 	val |= BIT_DDMACH0_RESET_CHKSUM_STS;
663e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0CTRL, val);
664e3037485SYan-Hsuan Chuang 
665e3037485SYan-Hsuan Chuang 	while (residue_size) {
666e3037485SYan-Hsuan Chuang 		if (residue_size >= max_size)
667e3037485SYan-Hsuan Chuang 			pkt_size = max_size;
668e3037485SYan-Hsuan Chuang 		else
669e3037485SYan-Hsuan Chuang 			pkt_size = residue_size;
670e3037485SYan-Hsuan Chuang 
671e3037485SYan-Hsuan Chuang 		ret = send_firmware_pkt(rtwdev, (u16)(src >> 7),
672e3037485SYan-Hsuan Chuang 					data + mem_offset, pkt_size);
673e3037485SYan-Hsuan Chuang 		if (ret)
674e3037485SYan-Hsuan Chuang 			return ret;
675e3037485SYan-Hsuan Chuang 
676e3037485SYan-Hsuan Chuang 		ret = iddma_download_firmware(rtwdev, OCPBASE_TXBUF_88XX +
677e3037485SYan-Hsuan Chuang 					      src + desc_size,
678e3037485SYan-Hsuan Chuang 					      dst + mem_offset, pkt_size,
679e3037485SYan-Hsuan Chuang 					      first_part);
680e3037485SYan-Hsuan Chuang 		if (ret)
681e3037485SYan-Hsuan Chuang 			return ret;
682e3037485SYan-Hsuan Chuang 
683e3037485SYan-Hsuan Chuang 		first_part = 0;
684e3037485SYan-Hsuan Chuang 		mem_offset += pkt_size;
685e3037485SYan-Hsuan Chuang 		residue_size -= pkt_size;
686e3037485SYan-Hsuan Chuang 	}
687e3037485SYan-Hsuan Chuang 
688e3037485SYan-Hsuan Chuang 	if (!check_fw_checksum(rtwdev, dst))
689e3037485SYan-Hsuan Chuang 		return -EINVAL;
690e3037485SYan-Hsuan Chuang 
691e3037485SYan-Hsuan Chuang 	return 0;
692e3037485SYan-Hsuan Chuang }
693e3037485SYan-Hsuan Chuang 
694e3037485SYan-Hsuan Chuang static int
start_download_firmware(struct rtw_dev * rtwdev,const u8 * data,u32 size)695e3037485SYan-Hsuan Chuang start_download_firmware(struct rtw_dev *rtwdev, const u8 *data, u32 size)
696e3037485SYan-Hsuan Chuang {
697cc20a713SPing-Ke Shih 	const struct rtw_fw_hdr *fw_hdr = (const struct rtw_fw_hdr *)data;
698e3037485SYan-Hsuan Chuang 	const u8 *cur_fw;
699e3037485SYan-Hsuan Chuang 	u16 val;
700e3037485SYan-Hsuan Chuang 	u32 imem_size;
701e3037485SYan-Hsuan Chuang 	u32 dmem_size;
702e3037485SYan-Hsuan Chuang 	u32 emem_size;
703e3037485SYan-Hsuan Chuang 	u32 addr;
704e3037485SYan-Hsuan Chuang 	int ret;
705e3037485SYan-Hsuan Chuang 
706cc20a713SPing-Ke Shih 	dmem_size = le32_to_cpu(fw_hdr->dmem_size);
707cc20a713SPing-Ke Shih 	imem_size = le32_to_cpu(fw_hdr->imem_size);
708cc20a713SPing-Ke Shih 	emem_size = (fw_hdr->mem_usage & BIT(4)) ?
709cc20a713SPing-Ke Shih 		    le32_to_cpu(fw_hdr->emem_size) : 0;
710e3037485SYan-Hsuan Chuang 	dmem_size += FW_HDR_CHKSUM_SIZE;
711e3037485SYan-Hsuan Chuang 	imem_size += FW_HDR_CHKSUM_SIZE;
712e3037485SYan-Hsuan Chuang 	emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0;
713e3037485SYan-Hsuan Chuang 
714e3037485SYan-Hsuan Chuang 	val = (u16)(rtw_read16(rtwdev, REG_MCUFW_CTRL) & 0x3800);
715e3037485SYan-Hsuan Chuang 	val |= BIT_MCUFWDL_EN;
716e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_MCUFW_CTRL, val);
717e3037485SYan-Hsuan Chuang 
718e3037485SYan-Hsuan Chuang 	cur_fw = data + FW_HDR_SIZE;
719cc20a713SPing-Ke Shih 	addr = le32_to_cpu(fw_hdr->dmem_addr);
720e3037485SYan-Hsuan Chuang 	addr &= ~BIT(31);
721e3037485SYan-Hsuan Chuang 	ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, dmem_size);
722e3037485SYan-Hsuan Chuang 	if (ret)
723e3037485SYan-Hsuan Chuang 		return ret;
724e3037485SYan-Hsuan Chuang 
725e3037485SYan-Hsuan Chuang 	cur_fw = data + FW_HDR_SIZE + dmem_size;
726cc20a713SPing-Ke Shih 	addr = le32_to_cpu(fw_hdr->imem_addr);
727e3037485SYan-Hsuan Chuang 	addr &= ~BIT(31);
728e3037485SYan-Hsuan Chuang 	ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, imem_size);
729e3037485SYan-Hsuan Chuang 	if (ret)
730e3037485SYan-Hsuan Chuang 		return ret;
731e3037485SYan-Hsuan Chuang 
732e3037485SYan-Hsuan Chuang 	if (emem_size) {
733e3037485SYan-Hsuan Chuang 		cur_fw = data + FW_HDR_SIZE + dmem_size + imem_size;
734cc20a713SPing-Ke Shih 		addr = le32_to_cpu(fw_hdr->emem_addr);
735e3037485SYan-Hsuan Chuang 		addr &= ~BIT(31);
736e3037485SYan-Hsuan Chuang 		ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr,
737e3037485SYan-Hsuan Chuang 					       emem_size);
738e3037485SYan-Hsuan Chuang 		if (ret)
739e3037485SYan-Hsuan Chuang 			return ret;
740e3037485SYan-Hsuan Chuang 	}
741e3037485SYan-Hsuan Chuang 
742e3037485SYan-Hsuan Chuang 	return 0;
743e3037485SYan-Hsuan Chuang }
744e3037485SYan-Hsuan Chuang 
download_firmware_validate(struct rtw_dev * rtwdev)745e3037485SYan-Hsuan Chuang static int download_firmware_validate(struct rtw_dev *rtwdev)
746e3037485SYan-Hsuan Chuang {
747e3037485SYan-Hsuan Chuang 	u32 fw_key;
748e3037485SYan-Hsuan Chuang 
749e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, FW_READY_MASK, FW_READY)) {
750e3037485SYan-Hsuan Chuang 		fw_key = rtw_read32(rtwdev, REG_FW_DBG7) & FW_KEY_MASK;
751e3037485SYan-Hsuan Chuang 		if (fw_key == ILLEGAL_KEY_GROUP)
752e3037485SYan-Hsuan Chuang 			rtw_err(rtwdev, "invalid fw key\n");
753e3037485SYan-Hsuan Chuang 		return -EINVAL;
754e3037485SYan-Hsuan Chuang 	}
755e3037485SYan-Hsuan Chuang 
756e3037485SYan-Hsuan Chuang 	return 0;
757e3037485SYan-Hsuan Chuang }
758e3037485SYan-Hsuan Chuang 
download_firmware_end_flow(struct rtw_dev * rtwdev)759e3037485SYan-Hsuan Chuang static void download_firmware_end_flow(struct rtw_dev *rtwdev)
760e3037485SYan-Hsuan Chuang {
761e3037485SYan-Hsuan Chuang 	u16 fw_ctrl;
762e3037485SYan-Hsuan Chuang 
763e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_TXDMA_STATUS, BTI_PAGE_OVF);
764e3037485SYan-Hsuan Chuang 
765e3037485SYan-Hsuan Chuang 	/* Check IMEM & DMEM checksum is OK or not */
766e3037485SYan-Hsuan Chuang 	fw_ctrl = rtw_read16(rtwdev, REG_MCUFW_CTRL);
767e3037485SYan-Hsuan Chuang 	if ((fw_ctrl & BIT_CHECK_SUM_OK) != BIT_CHECK_SUM_OK)
768e3037485SYan-Hsuan Chuang 		return;
769e3037485SYan-Hsuan Chuang 
770e3037485SYan-Hsuan Chuang 	fw_ctrl = (fw_ctrl | BIT_FW_DW_RDY) & ~BIT_MCUFWDL_EN;
771e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
772e3037485SYan-Hsuan Chuang }
773e3037485SYan-Hsuan Chuang 
__rtw_download_firmware(struct rtw_dev * rtwdev,struct rtw_fw_state * fw)7743d8bf508SYan-Hsuan Chuang static int __rtw_download_firmware(struct rtw_dev *rtwdev,
7753d8bf508SYan-Hsuan Chuang 				   struct rtw_fw_state *fw)
776e3037485SYan-Hsuan Chuang {
777e3037485SYan-Hsuan Chuang 	struct rtw_backup_info bckp[DLFW_RESTORE_REG_NUM];
778e3037485SYan-Hsuan Chuang 	const u8 *data = fw->firmware->data;
779e3037485SYan-Hsuan Chuang 	u32 size = fw->firmware->size;
780e3037485SYan-Hsuan Chuang 	u32 ltecoex_bckp;
781e3037485SYan-Hsuan Chuang 	int ret;
782e3037485SYan-Hsuan Chuang 
783e3037485SYan-Hsuan Chuang 	if (!check_firmware_size(data, size))
784e3037485SYan-Hsuan Chuang 		return -EINVAL;
785e3037485SYan-Hsuan Chuang 
786e3037485SYan-Hsuan Chuang 	if (!ltecoex_read_reg(rtwdev, 0x38, &ltecoex_bckp))
787e3037485SYan-Hsuan Chuang 		return -EBUSY;
788e3037485SYan-Hsuan Chuang 
789e3037485SYan-Hsuan Chuang 	wlan_cpu_enable(rtwdev, false);
790e3037485SYan-Hsuan Chuang 
791e3037485SYan-Hsuan Chuang 	download_firmware_reg_backup(rtwdev, bckp);
792e3037485SYan-Hsuan Chuang 	download_firmware_reset_platform(rtwdev);
793e3037485SYan-Hsuan Chuang 
794e3037485SYan-Hsuan Chuang 	ret = start_download_firmware(rtwdev, data, size);
795e3037485SYan-Hsuan Chuang 	if (ret)
796e3037485SYan-Hsuan Chuang 		goto dlfw_fail;
797e3037485SYan-Hsuan Chuang 
798e3037485SYan-Hsuan Chuang 	download_firmware_reg_restore(rtwdev, bckp, DLFW_RESTORE_REG_NUM);
799e3037485SYan-Hsuan Chuang 
800e3037485SYan-Hsuan Chuang 	download_firmware_end_flow(rtwdev);
801e3037485SYan-Hsuan Chuang 
802e3037485SYan-Hsuan Chuang 	wlan_cpu_enable(rtwdev, true);
803e3037485SYan-Hsuan Chuang 
80491ccdbb9SPing-Ke Shih 	if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) {
80591ccdbb9SPing-Ke Shih 		ret = -EBUSY;
80691ccdbb9SPing-Ke Shih 		goto dlfw_fail;
80791ccdbb9SPing-Ke Shih 	}
808e3037485SYan-Hsuan Chuang 
809e3037485SYan-Hsuan Chuang 	ret = download_firmware_validate(rtwdev);
810e3037485SYan-Hsuan Chuang 	if (ret)
811e3037485SYan-Hsuan Chuang 		goto dlfw_fail;
812e3037485SYan-Hsuan Chuang 
813e3037485SYan-Hsuan Chuang 	/* reset desc and index */
814e3037485SYan-Hsuan Chuang 	rtw_hci_setup(rtwdev);
815e3037485SYan-Hsuan Chuang 
816e3037485SYan-Hsuan Chuang 	rtwdev->h2c.last_box_num = 0;
817e3037485SYan-Hsuan Chuang 	rtwdev->h2c.seq = 0;
818e3037485SYan-Hsuan Chuang 
8193c519605SYan-Hsuan Chuang 	set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags);
820e3037485SYan-Hsuan Chuang 
821e3037485SYan-Hsuan Chuang 	return 0;
822e3037485SYan-Hsuan Chuang 
823e3037485SYan-Hsuan Chuang dlfw_fail:
824e3037485SYan-Hsuan Chuang 	/* Disable FWDL_EN */
825e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
826e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
827e3037485SYan-Hsuan Chuang 
828e3037485SYan-Hsuan Chuang 	return ret;
829e3037485SYan-Hsuan Chuang }
830e3037485SYan-Hsuan Chuang 
en_download_firmware_legacy(struct rtw_dev * rtwdev,bool en)83115d2fcc6SPing-Ke Shih static void en_download_firmware_legacy(struct rtw_dev *rtwdev, bool en)
83215d2fcc6SPing-Ke Shih {
83315d2fcc6SPing-Ke Shih 	int try;
83415d2fcc6SPing-Ke Shih 
83515d2fcc6SPing-Ke Shih 	if (en) {
83615d2fcc6SPing-Ke Shih 		wlan_cpu_enable(rtwdev, false);
83715d2fcc6SPing-Ke Shih 		wlan_cpu_enable(rtwdev, true);
83815d2fcc6SPing-Ke Shih 
83915d2fcc6SPing-Ke Shih 		rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
84015d2fcc6SPing-Ke Shih 
84115d2fcc6SPing-Ke Shih 		for (try = 0; try < 10; try++) {
84215d2fcc6SPing-Ke Shih 			if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_MCUFWDL_EN)
84315d2fcc6SPing-Ke Shih 				goto fwdl_ready;
84415d2fcc6SPing-Ke Shih 			rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
84515d2fcc6SPing-Ke Shih 			msleep(20);
84615d2fcc6SPing-Ke Shih 		}
84715d2fcc6SPing-Ke Shih 		rtw_err(rtwdev, "failed to check fw download ready\n");
84815d2fcc6SPing-Ke Shih fwdl_ready:
84915d2fcc6SPing-Ke Shih 		rtw_write32_clr(rtwdev, REG_MCUFW_CTRL, BIT_ROM_DLEN);
85015d2fcc6SPing-Ke Shih 	} else {
85115d2fcc6SPing-Ke Shih 		rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
85215d2fcc6SPing-Ke Shih 	}
85315d2fcc6SPing-Ke Shih }
85415d2fcc6SPing-Ke Shih 
85515d2fcc6SPing-Ke Shih static void
write_firmware_page(struct rtw_dev * rtwdev,u32 page,const u8 * data,u32 size)85615d2fcc6SPing-Ke Shih write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
85715d2fcc6SPing-Ke Shih {
85815d2fcc6SPing-Ke Shih 	u32 val32;
85915d2fcc6SPing-Ke Shih 	u32 block_nr;
86015d2fcc6SPing-Ke Shih 	u32 remain_size;
86115d2fcc6SPing-Ke Shih 	u32 write_addr = FW_START_ADDR_LEGACY;
86215d2fcc6SPing-Ke Shih 	const __le32 *ptr = (const __le32 *)data;
86315d2fcc6SPing-Ke Shih 	u32 block;
86415d2fcc6SPing-Ke Shih 	__le32 remain_data = 0;
86515d2fcc6SPing-Ke Shih 
86615d2fcc6SPing-Ke Shih 	block_nr = size >> DLFW_BLK_SIZE_SHIFT_LEGACY;
86715d2fcc6SPing-Ke Shih 	remain_size = size & (DLFW_BLK_SIZE_LEGACY - 1);
86815d2fcc6SPing-Ke Shih 
86915d2fcc6SPing-Ke Shih 	val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
87015d2fcc6SPing-Ke Shih 	val32 &= ~BIT_ROM_PGE;
87115d2fcc6SPing-Ke Shih 	val32 |= (page << BIT_SHIFT_ROM_PGE) & BIT_ROM_PGE;
87215d2fcc6SPing-Ke Shih 	rtw_write32(rtwdev, REG_MCUFW_CTRL, val32);
87315d2fcc6SPing-Ke Shih 
87415d2fcc6SPing-Ke Shih 	for (block = 0; block < block_nr; block++) {
87515d2fcc6SPing-Ke Shih 		rtw_write32(rtwdev, write_addr, le32_to_cpu(*ptr));
87615d2fcc6SPing-Ke Shih 
87715d2fcc6SPing-Ke Shih 		write_addr += DLFW_BLK_SIZE_LEGACY;
87815d2fcc6SPing-Ke Shih 		ptr++;
87915d2fcc6SPing-Ke Shih 	}
88015d2fcc6SPing-Ke Shih 
88115d2fcc6SPing-Ke Shih 	if (remain_size) {
88215d2fcc6SPing-Ke Shih 		memcpy(&remain_data, ptr, remain_size);
88315d2fcc6SPing-Ke Shih 		rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data));
88415d2fcc6SPing-Ke Shih 	}
88515d2fcc6SPing-Ke Shih }
88615d2fcc6SPing-Ke Shih 
88715d2fcc6SPing-Ke Shih static int
download_firmware_legacy(struct rtw_dev * rtwdev,const u8 * data,u32 size)88815d2fcc6SPing-Ke Shih download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
88915d2fcc6SPing-Ke Shih {
89015d2fcc6SPing-Ke Shih 	u32 page;
89115d2fcc6SPing-Ke Shih 	u32 total_page;
89215d2fcc6SPing-Ke Shih 	u32 last_page_size;
89315d2fcc6SPing-Ke Shih 
89415d2fcc6SPing-Ke Shih 	data += sizeof(struct rtw_fw_hdr_legacy);
89515d2fcc6SPing-Ke Shih 	size -= sizeof(struct rtw_fw_hdr_legacy);
89615d2fcc6SPing-Ke Shih 
89715d2fcc6SPing-Ke Shih 	total_page = size >> DLFW_PAGE_SIZE_SHIFT_LEGACY;
89815d2fcc6SPing-Ke Shih 	last_page_size = size & (DLFW_PAGE_SIZE_LEGACY - 1);
89915d2fcc6SPing-Ke Shih 
90015d2fcc6SPing-Ke Shih 	rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT);
90115d2fcc6SPing-Ke Shih 
90215d2fcc6SPing-Ke Shih 	for (page = 0; page < total_page; page++) {
90315d2fcc6SPing-Ke Shih 		write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY);
90415d2fcc6SPing-Ke Shih 		data += DLFW_PAGE_SIZE_LEGACY;
90515d2fcc6SPing-Ke Shih 	}
90615d2fcc6SPing-Ke Shih 	if (last_page_size)
90715d2fcc6SPing-Ke Shih 		write_firmware_page(rtwdev, page, data, last_page_size);
90815d2fcc6SPing-Ke Shih 
90915d2fcc6SPing-Ke Shih 	if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) {
910a6336094SColin Ian King 		rtw_err(rtwdev, "failed to check download firmware report\n");
91115d2fcc6SPing-Ke Shih 		return -EINVAL;
91215d2fcc6SPing-Ke Shih 	}
91315d2fcc6SPing-Ke Shih 
91415d2fcc6SPing-Ke Shih 	return 0;
91515d2fcc6SPing-Ke Shih }
91615d2fcc6SPing-Ke Shih 
download_firmware_validate_legacy(struct rtw_dev * rtwdev)91715d2fcc6SPing-Ke Shih static int download_firmware_validate_legacy(struct rtw_dev *rtwdev)
91815d2fcc6SPing-Ke Shih {
91915d2fcc6SPing-Ke Shih 	u32 val32;
92015d2fcc6SPing-Ke Shih 	int try;
92115d2fcc6SPing-Ke Shih 
92215d2fcc6SPing-Ke Shih 	val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
92315d2fcc6SPing-Ke Shih 	val32 |= BIT_MCUFWDL_RDY;
92415d2fcc6SPing-Ke Shih 	val32 &= ~BIT_WINTINI_RDY;
92515d2fcc6SPing-Ke Shih 	rtw_write32(rtwdev, REG_MCUFW_CTRL, val32);
92615d2fcc6SPing-Ke Shih 
92715d2fcc6SPing-Ke Shih 	wlan_cpu_enable(rtwdev, false);
92815d2fcc6SPing-Ke Shih 	wlan_cpu_enable(rtwdev, true);
92915d2fcc6SPing-Ke Shih 
93015d2fcc6SPing-Ke Shih 	for (try = 0; try < 10; try++) {
93115d2fcc6SPing-Ke Shih 		val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
93215d2fcc6SPing-Ke Shih 		if ((val32 & FW_READY_LEGACY) == FW_READY_LEGACY)
93315d2fcc6SPing-Ke Shih 			return 0;
93415d2fcc6SPing-Ke Shih 		msleep(20);
93515d2fcc6SPing-Ke Shih 	}
93615d2fcc6SPing-Ke Shih 
937a6336094SColin Ian King 	rtw_err(rtwdev, "failed to validate firmware\n");
93815d2fcc6SPing-Ke Shih 	return -EINVAL;
93915d2fcc6SPing-Ke Shih }
94015d2fcc6SPing-Ke Shih 
__rtw_download_firmware_legacy(struct rtw_dev * rtwdev,struct rtw_fw_state * fw)9413d8bf508SYan-Hsuan Chuang static int __rtw_download_firmware_legacy(struct rtw_dev *rtwdev,
9423d8bf508SYan-Hsuan Chuang 					  struct rtw_fw_state *fw)
94315d2fcc6SPing-Ke Shih {
94415d2fcc6SPing-Ke Shih 	int ret = 0;
94515d2fcc6SPing-Ke Shih 
94615d2fcc6SPing-Ke Shih 	en_download_firmware_legacy(rtwdev, true);
94715d2fcc6SPing-Ke Shih 	ret = download_firmware_legacy(rtwdev, fw->firmware->data, fw->firmware->size);
94815d2fcc6SPing-Ke Shih 	en_download_firmware_legacy(rtwdev, false);
94915d2fcc6SPing-Ke Shih 	if (ret)
95015d2fcc6SPing-Ke Shih 		goto out;
95115d2fcc6SPing-Ke Shih 
95215d2fcc6SPing-Ke Shih 	ret = download_firmware_validate_legacy(rtwdev);
95315d2fcc6SPing-Ke Shih 	if (ret)
95415d2fcc6SPing-Ke Shih 		goto out;
95515d2fcc6SPing-Ke Shih 
95615d2fcc6SPing-Ke Shih 	/* reset desc and index */
95715d2fcc6SPing-Ke Shih 	rtw_hci_setup(rtwdev);
95815d2fcc6SPing-Ke Shih 
95915d2fcc6SPing-Ke Shih 	rtwdev->h2c.last_box_num = 0;
96015d2fcc6SPing-Ke Shih 	rtwdev->h2c.seq = 0;
96115d2fcc6SPing-Ke Shih 
96215d2fcc6SPing-Ke Shih 	set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags);
96315d2fcc6SPing-Ke Shih 
96415d2fcc6SPing-Ke Shih out:
96515d2fcc6SPing-Ke Shih 	return ret;
96615d2fcc6SPing-Ke Shih }
96715d2fcc6SPing-Ke Shih 
9687c57d3dcSPing-Ke Shih static
_rtw_download_firmware(struct rtw_dev * rtwdev,struct rtw_fw_state * fw)9697c57d3dcSPing-Ke Shih int _rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
97015d2fcc6SPing-Ke Shih {
97115d2fcc6SPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
97215d2fcc6SPing-Ke Shih 		return __rtw_download_firmware_legacy(rtwdev, fw);
97315d2fcc6SPing-Ke Shih 
97415d2fcc6SPing-Ke Shih 	return __rtw_download_firmware(rtwdev, fw);
97515d2fcc6SPing-Ke Shih }
97615d2fcc6SPing-Ke Shih 
rtw_download_firmware(struct rtw_dev * rtwdev,struct rtw_fw_state * fw)9777c57d3dcSPing-Ke Shih int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
9787c57d3dcSPing-Ke Shih {
9797c57d3dcSPing-Ke Shih 	int ret;
9807c57d3dcSPing-Ke Shih 
9817c57d3dcSPing-Ke Shih 	ret = _rtw_download_firmware(rtwdev, fw);
9827c57d3dcSPing-Ke Shih 	if (ret)
9837c57d3dcSPing-Ke Shih 		return ret;
9847c57d3dcSPing-Ke Shih 
9857c57d3dcSPing-Ke Shih 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE &&
9867c57d3dcSPing-Ke Shih 	    rtwdev->chip->id == RTW_CHIP_TYPE_8821C)
9877c57d3dcSPing-Ke Shih 		rtw_fw_set_recover_bt_device(rtwdev);
9887c57d3dcSPing-Ke Shih 
9897c57d3dcSPing-Ke Shih 	return 0;
9907c57d3dcSPing-Ke Shih }
9917c57d3dcSPing-Ke Shih 
get_priority_queues(struct rtw_dev * rtwdev,u32 queues)9921131ad7fSYan-Hsuan Chuang static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues)
9931131ad7fSYan-Hsuan Chuang {
994d49f2c50SJoe Perches 	const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn;
9951131ad7fSYan-Hsuan Chuang 	u32 prio_queues = 0;
9961131ad7fSYan-Hsuan Chuang 
9971131ad7fSYan-Hsuan Chuang 	if (queues & BIT(IEEE80211_AC_VO))
9981131ad7fSYan-Hsuan Chuang 		prio_queues |= BIT(rqpn->dma_map_vo);
9991131ad7fSYan-Hsuan Chuang 	if (queues & BIT(IEEE80211_AC_VI))
10001131ad7fSYan-Hsuan Chuang 		prio_queues |= BIT(rqpn->dma_map_vi);
10011131ad7fSYan-Hsuan Chuang 	if (queues & BIT(IEEE80211_AC_BE))
10021131ad7fSYan-Hsuan Chuang 		prio_queues |= BIT(rqpn->dma_map_be);
10031131ad7fSYan-Hsuan Chuang 	if (queues & BIT(IEEE80211_AC_BK))
10041131ad7fSYan-Hsuan Chuang 		prio_queues |= BIT(rqpn->dma_map_bk);
10051131ad7fSYan-Hsuan Chuang 
10061131ad7fSYan-Hsuan Chuang 	return prio_queues;
10071131ad7fSYan-Hsuan Chuang }
10081131ad7fSYan-Hsuan Chuang 
__rtw_mac_flush_prio_queue(struct rtw_dev * rtwdev,u32 prio_queue,bool drop)10091131ad7fSYan-Hsuan Chuang static void __rtw_mac_flush_prio_queue(struct rtw_dev *rtwdev,
10101131ad7fSYan-Hsuan Chuang 				       u32 prio_queue, bool drop)
10111131ad7fSYan-Hsuan Chuang {
1012dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
10137d754f97SPing-Ke Shih 	const struct rtw_prioq_addr *addr;
10147d754f97SPing-Ke Shih 	bool wsize;
10151131ad7fSYan-Hsuan Chuang 	u16 avail_page, rsvd_page;
10161131ad7fSYan-Hsuan Chuang 	int i;
10171131ad7fSYan-Hsuan Chuang 
10187d754f97SPing-Ke Shih 	if (prio_queue >= RTW_DMA_MAPPING_MAX)
10191131ad7fSYan-Hsuan Chuang 		return;
10207d754f97SPing-Ke Shih 
10217d754f97SPing-Ke Shih 	addr = &chip->prioq_addrs->prio[prio_queue];
10227d754f97SPing-Ke Shih 	wsize = chip->prioq_addrs->wsize;
10231131ad7fSYan-Hsuan Chuang 
10241131ad7fSYan-Hsuan Chuang 	/* check if all of the reserved pages are available for 100 msecs */
10251131ad7fSYan-Hsuan Chuang 	for (i = 0; i < 5; i++) {
10267d754f97SPing-Ke Shih 		rsvd_page = wsize ? rtw_read16(rtwdev, addr->rsvd) :
10277d754f97SPing-Ke Shih 				     rtw_read8(rtwdev, addr->rsvd);
10287d754f97SPing-Ke Shih 		avail_page = wsize ? rtw_read16(rtwdev, addr->avail) :
10297d754f97SPing-Ke Shih 				      rtw_read8(rtwdev, addr->avail);
10301131ad7fSYan-Hsuan Chuang 		if (rsvd_page == avail_page)
10311131ad7fSYan-Hsuan Chuang 			return;
10321131ad7fSYan-Hsuan Chuang 
10331131ad7fSYan-Hsuan Chuang 		msleep(20);
10341131ad7fSYan-Hsuan Chuang 	}
10351131ad7fSYan-Hsuan Chuang 
10361131ad7fSYan-Hsuan Chuang 	/* priority queue is still not empty, throw a warning,
10371131ad7fSYan-Hsuan Chuang 	 *
10381131ad7fSYan-Hsuan Chuang 	 * Note that if we want to flush the tx queue when having a lot of
10391131ad7fSYan-Hsuan Chuang 	 * traffic (ex, 100Mbps up), some of the packets could be dropped.
10401131ad7fSYan-Hsuan Chuang 	 * And it requires like ~2secs to flush the full priority queue.
10411131ad7fSYan-Hsuan Chuang 	 */
10421131ad7fSYan-Hsuan Chuang 	if (!drop)
10431131ad7fSYan-Hsuan Chuang 		rtw_warn(rtwdev, "timed out to flush queue %d\n", prio_queue);
10441131ad7fSYan-Hsuan Chuang }
10451131ad7fSYan-Hsuan Chuang 
rtw_mac_flush_prio_queues(struct rtw_dev * rtwdev,u32 prio_queues,bool drop)10461131ad7fSYan-Hsuan Chuang static void rtw_mac_flush_prio_queues(struct rtw_dev *rtwdev,
10471131ad7fSYan-Hsuan Chuang 				      u32 prio_queues, bool drop)
10481131ad7fSYan-Hsuan Chuang {
10491131ad7fSYan-Hsuan Chuang 	u32 q;
10501131ad7fSYan-Hsuan Chuang 
10511131ad7fSYan-Hsuan Chuang 	for (q = 0; q < RTW_DMA_MAPPING_MAX; q++)
10521131ad7fSYan-Hsuan Chuang 		if (prio_queues & BIT(q))
10531131ad7fSYan-Hsuan Chuang 			__rtw_mac_flush_prio_queue(rtwdev, q, drop);
10541131ad7fSYan-Hsuan Chuang }
10551131ad7fSYan-Hsuan Chuang 
rtw_mac_flush_queues(struct rtw_dev * rtwdev,u32 queues,bool drop)10561131ad7fSYan-Hsuan Chuang void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop)
10571131ad7fSYan-Hsuan Chuang {
10581131ad7fSYan-Hsuan Chuang 	u32 prio_queues = 0;
10591131ad7fSYan-Hsuan Chuang 
10601131ad7fSYan-Hsuan Chuang 	/* If all of the hardware queues are requested to flush,
10611131ad7fSYan-Hsuan Chuang 	 * or the priority queues are not mapped yet,
10621131ad7fSYan-Hsuan Chuang 	 * flush all of the priority queues
10631131ad7fSYan-Hsuan Chuang 	 */
10641131ad7fSYan-Hsuan Chuang 	if (queues == BIT(rtwdev->hw->queues) - 1 || !rtwdev->fifo.rqpn)
10651131ad7fSYan-Hsuan Chuang 		prio_queues = BIT(RTW_DMA_MAPPING_MAX) - 1;
10661131ad7fSYan-Hsuan Chuang 	else
10671131ad7fSYan-Hsuan Chuang 		prio_queues = get_priority_queues(rtwdev, queues);
10681131ad7fSYan-Hsuan Chuang 
10691131ad7fSYan-Hsuan Chuang 	rtw_mac_flush_prio_queues(rtwdev, prio_queues, drop);
10701131ad7fSYan-Hsuan Chuang }
10711131ad7fSYan-Hsuan Chuang 
txdma_queue_mapping(struct rtw_dev * rtwdev)1072e3037485SYan-Hsuan Chuang static int txdma_queue_mapping(struct rtw_dev *rtwdev)
1073e3037485SYan-Hsuan Chuang {
1074dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1075d49f2c50SJoe Perches 	const struct rtw_rqpn *rqpn = NULL;
1076e3037485SYan-Hsuan Chuang 	u16 txdma_pq_map = 0;
1077e3037485SYan-Hsuan Chuang 
1078e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
1079e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
1080e3037485SYan-Hsuan Chuang 		rqpn = &chip->rqpn_table[1];
1081e3037485SYan-Hsuan Chuang 		break;
1082e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
1083e3037485SYan-Hsuan Chuang 		if (rtwdev->hci.bulkout_num == 2)
1084e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[2];
1085e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 3)
1086e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[3];
1087e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 4)
1088e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[4];
1089e3037485SYan-Hsuan Chuang 		else
1090e3037485SYan-Hsuan Chuang 			return -EINVAL;
1091e3037485SYan-Hsuan Chuang 		break;
10928599ea40SMartin Blumenstingl 	case RTW_HCI_TYPE_SDIO:
10938599ea40SMartin Blumenstingl 		rqpn = &chip->rqpn_table[0];
10948599ea40SMartin Blumenstingl 		break;
1095e3037485SYan-Hsuan Chuang 	default:
1096e3037485SYan-Hsuan Chuang 		return -EINVAL;
1097e3037485SYan-Hsuan Chuang 	}
1098e3037485SYan-Hsuan Chuang 
10991131ad7fSYan-Hsuan Chuang 	rtwdev->fifo.rqpn = rqpn;
1100e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi);
1101e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg);
1102e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk);
1103e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be);
1104e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi);
1105e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo);
1106e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map);
1107e3037485SYan-Hsuan Chuang 
1108e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, 0);
1109e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, MAC_TRX_ENABLE);
11107907b52dSPing-Ke Shih 	if (rtw_chip_wcpu_11ac(rtwdev))
1111e3037485SYan-Hsuan Chuang 		rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL);
1112e3037485SYan-Hsuan Chuang 
1113b722e5b1SMartin Blumenstingl 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_SDIO) {
1114b722e5b1SMartin Blumenstingl 		rtw_read32(rtwdev, REG_SDIO_FREE_TXPG);
1115b722e5b1SMartin Blumenstingl 		rtw_write32(rtwdev, REG_SDIO_TX_CTRL, 0);
1116b722e5b1SMartin Blumenstingl 	} else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) {
1117a82dfd33SSascha Hauer 		rtw_write8_set(rtwdev, REG_TXDMA_PQ_MAP, BIT_RXDMA_ARBBW_EN);
1118b722e5b1SMartin Blumenstingl 	}
1119a82dfd33SSascha Hauer 
1120e3037485SYan-Hsuan Chuang 	return 0;
1121e3037485SYan-Hsuan Chuang }
1122e3037485SYan-Hsuan Chuang 
set_trx_fifo_info(struct rtw_dev * rtwdev)1123e3037485SYan-Hsuan Chuang static int set_trx_fifo_info(struct rtw_dev *rtwdev)
1124e3037485SYan-Hsuan Chuang {
1125dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1126e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
1127e3037485SYan-Hsuan Chuang 	u16 cur_pg_addr;
1128e3037485SYan-Hsuan Chuang 	u8 csi_buf_pg_num = chip->csi_buf_pg_num;
1129e3037485SYan-Hsuan Chuang 
1130e3037485SYan-Hsuan Chuang 	/* config rsvd page num */
1131ffa71c54SPo-Hao Huang 	fifo->rsvd_drv_pg_num = chip->rsvd_drv_pg_num;
1132e3037485SYan-Hsuan Chuang 	fifo->txff_pg_num = chip->txff_size >> 7;
1133d91277deSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
1134d91277deSPing-Ke Shih 		fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num;
1135d91277deSPing-Ke Shih 	else
1136e3037485SYan-Hsuan Chuang 		fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num +
1137e3037485SYan-Hsuan Chuang 				   RSVD_PG_H2C_EXTRAINFO_NUM +
1138e3037485SYan-Hsuan Chuang 				   RSVD_PG_H2C_STATICINFO_NUM +
1139e3037485SYan-Hsuan Chuang 				   RSVD_PG_H2CQ_NUM +
1140e3037485SYan-Hsuan Chuang 				   RSVD_PG_CPU_INSTRUCTION_NUM +
1141e3037485SYan-Hsuan Chuang 				   RSVD_PG_FW_TXBUF_NUM +
1142e3037485SYan-Hsuan Chuang 				   csi_buf_pg_num;
1143e3037485SYan-Hsuan Chuang 
1144e3037485SYan-Hsuan Chuang 	if (fifo->rsvd_pg_num > fifo->txff_pg_num)
1145e3037485SYan-Hsuan Chuang 		return -ENOMEM;
1146e3037485SYan-Hsuan Chuang 
1147e3037485SYan-Hsuan Chuang 	fifo->acq_pg_num = fifo->txff_pg_num - fifo->rsvd_pg_num;
1148e3037485SYan-Hsuan Chuang 	fifo->rsvd_boundary = fifo->txff_pg_num - fifo->rsvd_pg_num;
1149e3037485SYan-Hsuan Chuang 
1150e3037485SYan-Hsuan Chuang 	cur_pg_addr = fifo->txff_pg_num;
1151d91277deSPing-Ke Shih 	if (rtw_chip_wcpu_11ac(rtwdev)) {
1152e3037485SYan-Hsuan Chuang 		cur_pg_addr -= csi_buf_pg_num;
1153e3037485SYan-Hsuan Chuang 		fifo->rsvd_csibuf_addr = cur_pg_addr;
1154e3037485SYan-Hsuan Chuang 		cur_pg_addr -= RSVD_PG_FW_TXBUF_NUM;
1155e3037485SYan-Hsuan Chuang 		fifo->rsvd_fw_txbuf_addr = cur_pg_addr;
1156e3037485SYan-Hsuan Chuang 		cur_pg_addr -= RSVD_PG_CPU_INSTRUCTION_NUM;
1157e3037485SYan-Hsuan Chuang 		fifo->rsvd_cpu_instr_addr = cur_pg_addr;
1158e3037485SYan-Hsuan Chuang 		cur_pg_addr -= RSVD_PG_H2CQ_NUM;
1159e3037485SYan-Hsuan Chuang 		fifo->rsvd_h2cq_addr = cur_pg_addr;
1160e3037485SYan-Hsuan Chuang 		cur_pg_addr -= RSVD_PG_H2C_STATICINFO_NUM;
1161e3037485SYan-Hsuan Chuang 		fifo->rsvd_h2c_sta_info_addr = cur_pg_addr;
1162e3037485SYan-Hsuan Chuang 		cur_pg_addr -= RSVD_PG_H2C_EXTRAINFO_NUM;
1163e3037485SYan-Hsuan Chuang 		fifo->rsvd_h2c_info_addr = cur_pg_addr;
1164d91277deSPing-Ke Shih 	}
1165e3037485SYan-Hsuan Chuang 	cur_pg_addr -= fifo->rsvd_drv_pg_num;
1166e3037485SYan-Hsuan Chuang 	fifo->rsvd_drv_addr = cur_pg_addr;
1167e3037485SYan-Hsuan Chuang 
1168e3037485SYan-Hsuan Chuang 	if (fifo->rsvd_boundary != fifo->rsvd_drv_addr) {
1169e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "wrong rsvd driver address\n");
1170e3037485SYan-Hsuan Chuang 		return -EINVAL;
1171e3037485SYan-Hsuan Chuang 	}
1172e3037485SYan-Hsuan Chuang 
1173e3037485SYan-Hsuan Chuang 	return 0;
1174e3037485SYan-Hsuan Chuang }
1175e3037485SYan-Hsuan Chuang 
__priority_queue_cfg(struct rtw_dev * rtwdev,const struct rtw_page_table * pg_tbl,u16 pubq_num)1176d91277deSPing-Ke Shih static int __priority_queue_cfg(struct rtw_dev *rtwdev,
1177d91277deSPing-Ke Shih 				const struct rtw_page_table *pg_tbl,
1178d91277deSPing-Ke Shih 				u16 pubq_num)
1179d91277deSPing-Ke Shih {
1180dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1181d91277deSPing-Ke Shih 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
1182d91277deSPing-Ke Shih 
1183d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, pg_tbl->hq_num);
1184d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_2, pg_tbl->lq_num);
1185d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_3, pg_tbl->nq_num);
1186d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_4, pg_tbl->exq_num);
1187d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_5, pubq_num);
1188d91277deSPing-Ke Shih 	rtw_write32_set(rtwdev, REG_RQPN_CTRL_2, BIT_LD_RQPN);
1189d91277deSPing-Ke Shih 
1190d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, fifo->rsvd_boundary);
1191d91277deSPing-Ke Shih 	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL + 2, BIT_EN_WR_FREE_TAIL >> 16);
1192d91277deSPing-Ke Shih 
1193d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_BCNQ_BDNY_V1, fifo->rsvd_boundary);
1194d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2 + 2, fifo->rsvd_boundary);
1195d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_BCNQ1_BDNY_V1, fifo->rsvd_boundary);
1196d91277deSPing-Ke Shih 	rtw_write32(rtwdev, REG_RXFF_BNDY, chip->rxff_size - C2H_PKT_BUF - 1);
1197d91277deSPing-Ke Shih 	rtw_write8_set(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1);
1198d91277deSPing-Ke Shih 
1199d91277deSPing-Ke Shih 	if (!check_hw_ready(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1, 0))
1200d91277deSPing-Ke Shih 		return -EBUSY;
1201d91277deSPing-Ke Shih 
1202d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_CR + 3, 0);
1203d91277deSPing-Ke Shih 
1204d91277deSPing-Ke Shih 	return 0;
1205d91277deSPing-Ke Shih }
1206d91277deSPing-Ke Shih 
__priority_queue_cfg_legacy(struct rtw_dev * rtwdev,const struct rtw_page_table * pg_tbl,u16 pubq_num)1207d91277deSPing-Ke Shih static int __priority_queue_cfg_legacy(struct rtw_dev *rtwdev,
1208d91277deSPing-Ke Shih 				       const struct rtw_page_table *pg_tbl,
1209d91277deSPing-Ke Shih 				       u16 pubq_num)
1210d91277deSPing-Ke Shih {
1211dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1212d91277deSPing-Ke Shih 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
1213d91277deSPing-Ke Shih 	u32 val32;
1214d91277deSPing-Ke Shih 
1215d91277deSPing-Ke Shih 	val32 = BIT_RQPN_NE(pg_tbl->nq_num, pg_tbl->exq_num);
1216d91277deSPing-Ke Shih 	rtw_write32(rtwdev, REG_RQPN_NPQ, val32);
1217d91277deSPing-Ke Shih 	val32 = BIT_RQPN_HLP(pg_tbl->hq_num, pg_tbl->lq_num, pubq_num);
1218d91277deSPing-Ke Shih 	rtw_write32(rtwdev, REG_RQPN, val32);
1219d91277deSPing-Ke Shih 
1220d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_TRXFF_BNDY, fifo->rsvd_boundary);
1221d91277deSPing-Ke Shih 	rtw_write16(rtwdev, REG_TRXFF_BNDY + 2, chip->rxff_size - REPORT_BUF - 1);
1222d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_DWBCN0_CTRL + 1, fifo->rsvd_boundary);
1223d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_BCNQ_BDNY, fifo->rsvd_boundary);
1224d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_MGQ_BDNY, fifo->rsvd_boundary);
1225d91277deSPing-Ke Shih 	rtw_write8(rtwdev, REG_WMAC_LBK_BF_HD, fifo->rsvd_boundary);
1226d91277deSPing-Ke Shih 
1227d91277deSPing-Ke Shih 	rtw_write32_set(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT);
1228d91277deSPing-Ke Shih 
1229d91277deSPing-Ke Shih 	if (!check_hw_ready(rtwdev, REG_AUTO_LLT, BIT_AUTO_INIT_LLT, 0))
1230d91277deSPing-Ke Shih 		return -EBUSY;
1231d91277deSPing-Ke Shih 
1232d91277deSPing-Ke Shih 	return 0;
1233d91277deSPing-Ke Shih }
1234d91277deSPing-Ke Shih 
priority_queue_cfg(struct rtw_dev * rtwdev)1235e3037485SYan-Hsuan Chuang static int priority_queue_cfg(struct rtw_dev *rtwdev)
1236e3037485SYan-Hsuan Chuang {
1237dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1238e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
1239d49f2c50SJoe Perches 	const struct rtw_page_table *pg_tbl = NULL;
1240e3037485SYan-Hsuan Chuang 	u16 pubq_num;
1241e3037485SYan-Hsuan Chuang 	int ret;
1242e3037485SYan-Hsuan Chuang 
1243e3037485SYan-Hsuan Chuang 	ret = set_trx_fifo_info(rtwdev);
1244e3037485SYan-Hsuan Chuang 	if (ret)
1245e3037485SYan-Hsuan Chuang 		return ret;
1246e3037485SYan-Hsuan Chuang 
1247e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
1248e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
1249e3037485SYan-Hsuan Chuang 		pg_tbl = &chip->page_table[1];
1250e3037485SYan-Hsuan Chuang 		break;
1251e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
1252e3037485SYan-Hsuan Chuang 		if (rtwdev->hci.bulkout_num == 2)
1253e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[2];
1254e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 3)
1255e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[3];
1256e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 4)
1257e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[4];
1258e3037485SYan-Hsuan Chuang 		else
1259e3037485SYan-Hsuan Chuang 			return -EINVAL;
1260e3037485SYan-Hsuan Chuang 		break;
12618599ea40SMartin Blumenstingl 	case RTW_HCI_TYPE_SDIO:
12628599ea40SMartin Blumenstingl 		pg_tbl = &chip->page_table[0];
12638599ea40SMartin Blumenstingl 		break;
1264e3037485SYan-Hsuan Chuang 	default:
1265e3037485SYan-Hsuan Chuang 		return -EINVAL;
1266e3037485SYan-Hsuan Chuang 	}
1267e3037485SYan-Hsuan Chuang 
1268e3037485SYan-Hsuan Chuang 	pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
1269e3037485SYan-Hsuan Chuang 		   pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
1270d91277deSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
1271d91277deSPing-Ke Shih 		return __priority_queue_cfg_legacy(rtwdev, pg_tbl, pubq_num);
1272d91277deSPing-Ke Shih 	else
1273d91277deSPing-Ke Shih 		return __priority_queue_cfg(rtwdev, pg_tbl, pubq_num);
1274e3037485SYan-Hsuan Chuang }
1275e3037485SYan-Hsuan Chuang 
init_h2c(struct rtw_dev * rtwdev)1276e3037485SYan-Hsuan Chuang static int init_h2c(struct rtw_dev *rtwdev)
1277e3037485SYan-Hsuan Chuang {
1278e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
1279e3037485SYan-Hsuan Chuang 	u8 value8;
1280e3037485SYan-Hsuan Chuang 	u32 value32;
1281e3037485SYan-Hsuan Chuang 	u32 h2cq_addr;
1282e3037485SYan-Hsuan Chuang 	u32 h2cq_size;
1283e3037485SYan-Hsuan Chuang 	u32 h2cq_free;
1284e3037485SYan-Hsuan Chuang 	u32 wp, rp;
1285e3037485SYan-Hsuan Chuang 
12867907b52dSPing-Ke Shih 	if (rtw_chip_wcpu_11n(rtwdev))
12877907b52dSPing-Ke Shih 		return 0;
12887907b52dSPing-Ke Shih 
1289e3037485SYan-Hsuan Chuang 	h2cq_addr = fifo->rsvd_h2cq_addr << TX_PAGE_SIZE_SHIFT;
1290e3037485SYan-Hsuan Chuang 	h2cq_size = RSVD_PG_H2CQ_NUM << TX_PAGE_SIZE_SHIFT;
1291e3037485SYan-Hsuan Chuang 
1292e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_HEAD);
1293e3037485SYan-Hsuan Chuang 	value32 = (value32 & 0xFFFC0000) | h2cq_addr;
1294e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_HEAD, value32);
1295e3037485SYan-Hsuan Chuang 
1296e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_READ_ADDR);
1297e3037485SYan-Hsuan Chuang 	value32 = (value32 & 0xFFFC0000) | h2cq_addr;
1298e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_READ_ADDR, value32);
1299e3037485SYan-Hsuan Chuang 
1300e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_TAIL);
1301e3037485SYan-Hsuan Chuang 	value32 &= 0xFFFC0000;
1302e3037485SYan-Hsuan Chuang 	value32 |= (h2cq_addr + h2cq_size);
1303e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_TAIL, value32);
1304e3037485SYan-Hsuan Chuang 
1305e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_H2C_INFO);
1306e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0xFC) | 0x01);
1307e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_H2C_INFO, value8);
1308e3037485SYan-Hsuan Chuang 
1309e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_H2C_INFO);
1310e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0xFB) | 0x04);
1311e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_H2C_INFO, value8);
1312e3037485SYan-Hsuan Chuang 
1313e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_TXDMA_OFFSET_CHK + 1);
1314e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0x7f) | 0x80);
1315e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_TXDMA_OFFSET_CHK + 1, value8);
1316e3037485SYan-Hsuan Chuang 
1317e3037485SYan-Hsuan Chuang 	wp = rtw_read32(rtwdev, REG_H2C_PKT_WRITEADDR) & 0x3FFFF;
1318e3037485SYan-Hsuan Chuang 	rp = rtw_read32(rtwdev, REG_H2C_PKT_READADDR) & 0x3FFFF;
1319e3037485SYan-Hsuan Chuang 	h2cq_free = wp >= rp ? h2cq_size - (wp - rp) : rp - wp;
1320e3037485SYan-Hsuan Chuang 
1321e3037485SYan-Hsuan Chuang 	if (h2cq_size != h2cq_free) {
1322e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "H2C queue mismatch\n");
1323e3037485SYan-Hsuan Chuang 		return -EINVAL;
1324e3037485SYan-Hsuan Chuang 	}
1325e3037485SYan-Hsuan Chuang 
1326e3037485SYan-Hsuan Chuang 	return 0;
1327e3037485SYan-Hsuan Chuang }
1328e3037485SYan-Hsuan Chuang 
rtw_init_trx_cfg(struct rtw_dev * rtwdev)1329e3037485SYan-Hsuan Chuang static int rtw_init_trx_cfg(struct rtw_dev *rtwdev)
1330e3037485SYan-Hsuan Chuang {
1331e3037485SYan-Hsuan Chuang 	int ret;
1332e3037485SYan-Hsuan Chuang 
1333e3037485SYan-Hsuan Chuang 	ret = txdma_queue_mapping(rtwdev);
1334e3037485SYan-Hsuan Chuang 	if (ret)
1335e3037485SYan-Hsuan Chuang 		return ret;
1336e3037485SYan-Hsuan Chuang 
1337e3037485SYan-Hsuan Chuang 	ret = priority_queue_cfg(rtwdev);
1338e3037485SYan-Hsuan Chuang 	if (ret)
1339e3037485SYan-Hsuan Chuang 		return ret;
1340e3037485SYan-Hsuan Chuang 
1341e3037485SYan-Hsuan Chuang 	ret = init_h2c(rtwdev);
1342e3037485SYan-Hsuan Chuang 	if (ret)
1343e3037485SYan-Hsuan Chuang 		return ret;
1344e3037485SYan-Hsuan Chuang 
1345e3037485SYan-Hsuan Chuang 	return 0;
1346e3037485SYan-Hsuan Chuang }
1347e3037485SYan-Hsuan Chuang 
rtw_drv_info_cfg(struct rtw_dev * rtwdev)1348e3037485SYan-Hsuan Chuang static int rtw_drv_info_cfg(struct rtw_dev *rtwdev)
1349e3037485SYan-Hsuan Chuang {
1350e3037485SYan-Hsuan Chuang 	u8 value8;
1351e3037485SYan-Hsuan Chuang 
1352e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
1353d91277deSPing-Ke Shih 	if (rtw_chip_wcpu_11ac(rtwdev)) {
1354e3037485SYan-Hsuan Chuang 		value8 = rtw_read8(rtwdev, REG_TRXFF_BNDY + 1);
1355e3037485SYan-Hsuan Chuang 		value8 &= 0xF0;
1356e3037485SYan-Hsuan Chuang 		/* For rxdesc len = 0 issue */
1357e3037485SYan-Hsuan Chuang 		value8 |= 0xF;
1358e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, REG_TRXFF_BNDY + 1, value8);
1359d91277deSPing-Ke Shih 	}
1360e3037485SYan-Hsuan Chuang 	rtw_write32_set(rtwdev, REG_RCR, BIT_APP_PHYSTS);
1361e3037485SYan-Hsuan Chuang 	rtw_write32_clr(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, BIT(8) | BIT(9));
1362e3037485SYan-Hsuan Chuang 
1363e3037485SYan-Hsuan Chuang 	return 0;
1364e3037485SYan-Hsuan Chuang }
1365e3037485SYan-Hsuan Chuang 
rtw_mac_init(struct rtw_dev * rtwdev)1366e3037485SYan-Hsuan Chuang int rtw_mac_init(struct rtw_dev *rtwdev)
1367e3037485SYan-Hsuan Chuang {
1368dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
1369e3037485SYan-Hsuan Chuang 	int ret;
1370e3037485SYan-Hsuan Chuang 
1371e3037485SYan-Hsuan Chuang 	ret = rtw_init_trx_cfg(rtwdev);
1372e3037485SYan-Hsuan Chuang 	if (ret)
1373e3037485SYan-Hsuan Chuang 		return ret;
1374e3037485SYan-Hsuan Chuang 
1375e3037485SYan-Hsuan Chuang 	ret = chip->ops->mac_init(rtwdev);
1376e3037485SYan-Hsuan Chuang 	if (ret)
1377e3037485SYan-Hsuan Chuang 		return ret;
1378e3037485SYan-Hsuan Chuang 
1379e3037485SYan-Hsuan Chuang 	ret = rtw_drv_info_cfg(rtwdev);
1380e3037485SYan-Hsuan Chuang 	if (ret)
1381e3037485SYan-Hsuan Chuang 		return ret;
1382e3037485SYan-Hsuan Chuang 
138378622104SYan-Hsuan Chuang 	rtw_hci_interface_cfg(rtwdev);
138478622104SYan-Hsuan Chuang 
1385e3037485SYan-Hsuan Chuang 	return 0;
1386e3037485SYan-Hsuan Chuang }
1387