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"
10e3037485SYan-Hsuan Chuang 
11e3037485SYan-Hsuan Chuang void rtw_set_channel_mac(struct rtw_dev *rtwdev, u8 channel, u8 bw,
12e3037485SYan-Hsuan Chuang 			 u8 primary_ch_idx)
13e3037485SYan-Hsuan Chuang {
14e3037485SYan-Hsuan Chuang 	u8 txsc40 = 0, txsc20 = 0;
15e3037485SYan-Hsuan Chuang 	u32 value32;
16e3037485SYan-Hsuan Chuang 	u8 value8;
17e3037485SYan-Hsuan Chuang 
18e3037485SYan-Hsuan Chuang 	txsc20 = primary_ch_idx;
19e3037485SYan-Hsuan Chuang 	if (txsc20 == 1 || txsc20 == 3)
20e3037485SYan-Hsuan Chuang 		txsc40 = 9;
21e3037485SYan-Hsuan Chuang 	else
22e3037485SYan-Hsuan Chuang 		txsc40 = 10;
23e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_DATA_SC,
24e3037485SYan-Hsuan Chuang 		   BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40));
25e3037485SYan-Hsuan Chuang 
26e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_WMAC_TRXPTCL_CTL);
27e3037485SYan-Hsuan Chuang 	value32 &= ~BIT_RFMOD;
28e3037485SYan-Hsuan Chuang 	switch (bw) {
29e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_80:
30e3037485SYan-Hsuan Chuang 		value32 |= BIT_RFMOD_80M;
31e3037485SYan-Hsuan Chuang 		break;
32e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_40:
33e3037485SYan-Hsuan Chuang 		value32 |= BIT_RFMOD_40M;
34e3037485SYan-Hsuan Chuang 		break;
35e3037485SYan-Hsuan Chuang 	case RTW_CHANNEL_WIDTH_20:
36e3037485SYan-Hsuan Chuang 	default:
37e3037485SYan-Hsuan Chuang 		break;
38e3037485SYan-Hsuan Chuang 	}
39e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_WMAC_TRXPTCL_CTL, value32);
40e3037485SYan-Hsuan Chuang 
41e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_AFE_CTRL1) & ~(BIT_MAC_CLK_SEL);
42e3037485SYan-Hsuan Chuang 	value32 |= (MAC_CLK_HW_DEF_80M << BIT_SHIFT_MAC_CLK_SEL);
43e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_AFE_CTRL1, value32);
44e3037485SYan-Hsuan Chuang 
45e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_USTIME_TSF, MAC_CLK_SPEED);
46e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_USTIME_EDCA, MAC_CLK_SPEED);
47e3037485SYan-Hsuan Chuang 
48e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_CCK_CHECK);
49e3037485SYan-Hsuan Chuang 	value8 = value8 & ~BIT_CHECK_CCK_EN;
50e3037485SYan-Hsuan Chuang 	if (channel > 35)
51e3037485SYan-Hsuan Chuang 		value8 |= BIT_CHECK_CCK_EN;
52e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CCK_CHECK, value8);
53e3037485SYan-Hsuan Chuang }
54e3037485SYan-Hsuan Chuang 
55e3037485SYan-Hsuan Chuang static int rtw_mac_pre_system_cfg(struct rtw_dev *rtwdev)
56e3037485SYan-Hsuan Chuang {
57e3037485SYan-Hsuan Chuang 	u32 value32;
58e3037485SYan-Hsuan Chuang 	u8 value8;
59e3037485SYan-Hsuan Chuang 
60e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RSV_CTRL, 0);
61e3037485SYan-Hsuan Chuang 
62e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
63e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
64e3037485SYan-Hsuan Chuang 		rtw_write32_set(rtwdev, REG_HCI_OPT_CTRL, BIT_BT_DIG_CLK_EN);
65e3037485SYan-Hsuan Chuang 		break;
66e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
67e3037485SYan-Hsuan Chuang 		break;
68e3037485SYan-Hsuan Chuang 	default:
69e3037485SYan-Hsuan Chuang 		return -EINVAL;
70e3037485SYan-Hsuan Chuang 	}
71e3037485SYan-Hsuan Chuang 
72e3037485SYan-Hsuan Chuang 	/* config PIN Mux */
73e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_PAD_CTRL1);
74e3037485SYan-Hsuan Chuang 	value32 |= BIT_PAPE_WLBT_SEL | BIT_LNAON_WLBT_SEL;
75e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_PAD_CTRL1, value32);
76e3037485SYan-Hsuan Chuang 
77e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_LED_CFG);
78e3037485SYan-Hsuan Chuang 	value32 &= ~(BIT_PAPE_SEL_EN | BIT_LNAON_SEL_EN);
79e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_LED_CFG, value32);
80e3037485SYan-Hsuan Chuang 
81e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_GPIO_MUXCFG);
82e3037485SYan-Hsuan Chuang 	value32 |= BIT_WLRFE_4_5_EN;
83e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_GPIO_MUXCFG, value32);
84e3037485SYan-Hsuan Chuang 
85e3037485SYan-Hsuan Chuang 	/* disable BB/RF */
86e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_SYS_FUNC_EN);
87e3037485SYan-Hsuan Chuang 	value8 &= ~(BIT_FEN_BB_RSTB | BIT_FEN_BB_GLB_RST);
88e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_SYS_FUNC_EN, value8);
89e3037485SYan-Hsuan Chuang 
90e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_RF_CTRL);
91e3037485SYan-Hsuan Chuang 	value8 &= ~(BIT_RF_SDM_RSTB | BIT_RF_RSTB | BIT_RF_EN);
92e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RF_CTRL, value8);
93e3037485SYan-Hsuan Chuang 
94e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_WLRF1);
95e3037485SYan-Hsuan Chuang 	value32 &= ~BIT_WLRF1_BBRF_EN;
96e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_WLRF1, value32);
97e3037485SYan-Hsuan Chuang 
98e3037485SYan-Hsuan Chuang 	return 0;
99e3037485SYan-Hsuan Chuang }
100e3037485SYan-Hsuan Chuang 
101e3037485SYan-Hsuan Chuang static int rtw_pwr_cmd_polling(struct rtw_dev *rtwdev,
102e3037485SYan-Hsuan Chuang 			       struct rtw_pwr_seq_cmd *cmd)
103e3037485SYan-Hsuan Chuang {
104e3037485SYan-Hsuan Chuang 	u8 value;
105e3037485SYan-Hsuan Chuang 	u8 flag = 0;
106e3037485SYan-Hsuan Chuang 	u32 offset;
107e3037485SYan-Hsuan Chuang 	u32 cnt = RTW_PWR_POLLING_CNT;
108e3037485SYan-Hsuan Chuang 
109e3037485SYan-Hsuan Chuang 	if (cmd->base == RTW_PWR_ADDR_SDIO)
110e3037485SYan-Hsuan Chuang 		offset = cmd->offset | SDIO_LOCAL_OFFSET;
111e3037485SYan-Hsuan Chuang 	else
112e3037485SYan-Hsuan Chuang 		offset = cmd->offset;
113e3037485SYan-Hsuan Chuang 
114e3037485SYan-Hsuan Chuang 	do {
115e3037485SYan-Hsuan Chuang 		cnt--;
116e3037485SYan-Hsuan Chuang 		value = rtw_read8(rtwdev, offset);
117e3037485SYan-Hsuan Chuang 		value &= cmd->mask;
118e3037485SYan-Hsuan Chuang 		if (value == (cmd->value & cmd->mask))
119e3037485SYan-Hsuan Chuang 			return 0;
120e3037485SYan-Hsuan Chuang 		if (cnt == 0) {
121e3037485SYan-Hsuan Chuang 			if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE &&
122e3037485SYan-Hsuan Chuang 			    flag == 0) {
123e3037485SYan-Hsuan Chuang 				value = rtw_read8(rtwdev, REG_SYS_PW_CTRL);
124e3037485SYan-Hsuan Chuang 				value |= BIT(3);
125e3037485SYan-Hsuan Chuang 				rtw_write8(rtwdev, REG_SYS_PW_CTRL, value);
126e3037485SYan-Hsuan Chuang 				value &= ~BIT(3);
127e3037485SYan-Hsuan Chuang 				rtw_write8(rtwdev, REG_SYS_PW_CTRL, value);
128e3037485SYan-Hsuan Chuang 				cnt = RTW_PWR_POLLING_CNT;
129e3037485SYan-Hsuan Chuang 				flag = 1;
130e3037485SYan-Hsuan Chuang 			} else {
131e3037485SYan-Hsuan Chuang 				return -EBUSY;
132e3037485SYan-Hsuan Chuang 			}
133e3037485SYan-Hsuan Chuang 		} else {
134e3037485SYan-Hsuan Chuang 			udelay(50);
135e3037485SYan-Hsuan Chuang 		}
136e3037485SYan-Hsuan Chuang 	} while (1);
137e3037485SYan-Hsuan Chuang }
138e3037485SYan-Hsuan Chuang 
139e3037485SYan-Hsuan Chuang static int rtw_sub_pwr_seq_parser(struct rtw_dev *rtwdev, u8 intf_mask,
140e3037485SYan-Hsuan Chuang 				  u8 cut_mask, struct rtw_pwr_seq_cmd *cmd)
141e3037485SYan-Hsuan Chuang {
142e3037485SYan-Hsuan Chuang 	struct rtw_pwr_seq_cmd *cur_cmd;
143e3037485SYan-Hsuan Chuang 	u32 offset;
144e3037485SYan-Hsuan Chuang 	u8 value;
145e3037485SYan-Hsuan Chuang 
146e3037485SYan-Hsuan Chuang 	for (cur_cmd = cmd; cur_cmd->cmd != RTW_PWR_CMD_END; cur_cmd++) {
147e3037485SYan-Hsuan Chuang 		if (!(cur_cmd->intf_mask & intf_mask) ||
148e3037485SYan-Hsuan Chuang 		    !(cur_cmd->cut_mask & cut_mask))
149e3037485SYan-Hsuan Chuang 			continue;
150e3037485SYan-Hsuan Chuang 
151e3037485SYan-Hsuan Chuang 		switch (cur_cmd->cmd) {
152e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_WRITE:
153e3037485SYan-Hsuan Chuang 			offset = cur_cmd->offset;
154e3037485SYan-Hsuan Chuang 
155e3037485SYan-Hsuan Chuang 			if (cur_cmd->base == RTW_PWR_ADDR_SDIO)
156e3037485SYan-Hsuan Chuang 				offset |= SDIO_LOCAL_OFFSET;
157e3037485SYan-Hsuan Chuang 
158e3037485SYan-Hsuan Chuang 			value = rtw_read8(rtwdev, offset);
159e3037485SYan-Hsuan Chuang 			value &= ~cur_cmd->mask;
160e3037485SYan-Hsuan Chuang 			value |= (cur_cmd->value & cur_cmd->mask);
161e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, offset, value);
162e3037485SYan-Hsuan Chuang 			break;
163e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_POLLING:
164e3037485SYan-Hsuan Chuang 			if (rtw_pwr_cmd_polling(rtwdev, cur_cmd))
165e3037485SYan-Hsuan Chuang 				return -EBUSY;
166e3037485SYan-Hsuan Chuang 			break;
167e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_DELAY:
168e3037485SYan-Hsuan Chuang 			if (cur_cmd->value == RTW_PWR_DELAY_US)
169e3037485SYan-Hsuan Chuang 				udelay(cur_cmd->offset);
170e3037485SYan-Hsuan Chuang 			else
171e3037485SYan-Hsuan Chuang 				mdelay(cur_cmd->offset);
172e3037485SYan-Hsuan Chuang 			break;
173e3037485SYan-Hsuan Chuang 		case RTW_PWR_CMD_READ:
174e3037485SYan-Hsuan Chuang 			break;
175e3037485SYan-Hsuan Chuang 		default:
176e3037485SYan-Hsuan Chuang 			return -EINVAL;
177e3037485SYan-Hsuan Chuang 		}
178e3037485SYan-Hsuan Chuang 	}
179e3037485SYan-Hsuan Chuang 
180e3037485SYan-Hsuan Chuang 	return 0;
181e3037485SYan-Hsuan Chuang }
182e3037485SYan-Hsuan Chuang 
183e3037485SYan-Hsuan Chuang static int rtw_pwr_seq_parser(struct rtw_dev *rtwdev,
184e3037485SYan-Hsuan Chuang 			      struct rtw_pwr_seq_cmd **cmd_seq)
185e3037485SYan-Hsuan Chuang {
186e3037485SYan-Hsuan Chuang 	u8 cut_mask;
187e3037485SYan-Hsuan Chuang 	u8 intf_mask;
188e3037485SYan-Hsuan Chuang 	u8 cut;
189e3037485SYan-Hsuan Chuang 	u32 idx = 0;
190e3037485SYan-Hsuan Chuang 	struct rtw_pwr_seq_cmd *cmd;
191e3037485SYan-Hsuan Chuang 	int ret;
192e3037485SYan-Hsuan Chuang 
193e3037485SYan-Hsuan Chuang 	cut = rtwdev->hal.cut_version;
194e3037485SYan-Hsuan Chuang 	cut_mask = cut_version_to_mask(cut);
195e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
196e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
197e3037485SYan-Hsuan Chuang 		intf_mask = BIT(2);
198e3037485SYan-Hsuan Chuang 		break;
199e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
200e3037485SYan-Hsuan Chuang 		intf_mask = BIT(1);
201e3037485SYan-Hsuan Chuang 		break;
202e3037485SYan-Hsuan Chuang 	default:
203e3037485SYan-Hsuan Chuang 		return -EINVAL;
204e3037485SYan-Hsuan Chuang 	}
205e3037485SYan-Hsuan Chuang 
206e3037485SYan-Hsuan Chuang 	do {
207e3037485SYan-Hsuan Chuang 		cmd = cmd_seq[idx];
208e3037485SYan-Hsuan Chuang 		if (!cmd)
209e3037485SYan-Hsuan Chuang 			break;
210e3037485SYan-Hsuan Chuang 
211e3037485SYan-Hsuan Chuang 		ret = rtw_sub_pwr_seq_parser(rtwdev, intf_mask, cut_mask, cmd);
212e3037485SYan-Hsuan Chuang 		if (ret)
213e3037485SYan-Hsuan Chuang 			return -EBUSY;
214e3037485SYan-Hsuan Chuang 
215e3037485SYan-Hsuan Chuang 		idx++;
216e3037485SYan-Hsuan Chuang 	} while (1);
217e3037485SYan-Hsuan Chuang 
218e3037485SYan-Hsuan Chuang 	return 0;
219e3037485SYan-Hsuan Chuang }
220e3037485SYan-Hsuan Chuang 
221e3037485SYan-Hsuan Chuang static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on)
222e3037485SYan-Hsuan Chuang {
223e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
224e3037485SYan-Hsuan Chuang 	struct rtw_pwr_seq_cmd **pwr_seq;
225e3037485SYan-Hsuan Chuang 	u8 rpwm;
226e3037485SYan-Hsuan Chuang 	bool cur_pwr;
227e3037485SYan-Hsuan Chuang 
228e3037485SYan-Hsuan Chuang 	rpwm = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
229e3037485SYan-Hsuan Chuang 
230e3037485SYan-Hsuan Chuang 	/* Check FW still exist or not */
231e3037485SYan-Hsuan Chuang 	if (rtw_read16(rtwdev, REG_MCUFW_CTRL) == 0xC078) {
232e3037485SYan-Hsuan Chuang 		rpwm = (rpwm ^ BIT_RPWM_TOGGLE) & BIT_RPWM_TOGGLE;
233e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, rpwm);
234e3037485SYan-Hsuan Chuang 	}
235e3037485SYan-Hsuan Chuang 
236e3037485SYan-Hsuan Chuang 	if (rtw_read8(rtwdev, REG_CR) == 0xea)
237e3037485SYan-Hsuan Chuang 		cur_pwr = false;
238e3037485SYan-Hsuan Chuang 	else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
239e3037485SYan-Hsuan Chuang 		 (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0)))
240e3037485SYan-Hsuan Chuang 		cur_pwr = false;
241e3037485SYan-Hsuan Chuang 	else
242e3037485SYan-Hsuan Chuang 		cur_pwr = true;
243e3037485SYan-Hsuan Chuang 
244e3037485SYan-Hsuan Chuang 	if (pwr_on && cur_pwr)
245e3037485SYan-Hsuan Chuang 		return -EALREADY;
246e3037485SYan-Hsuan Chuang 
247e3037485SYan-Hsuan Chuang 	pwr_seq = pwr_on ? chip->pwr_on_seq : chip->pwr_off_seq;
248e3037485SYan-Hsuan Chuang 	if (rtw_pwr_seq_parser(rtwdev, pwr_seq))
249e3037485SYan-Hsuan Chuang 		return -EINVAL;
250e3037485SYan-Hsuan Chuang 
251e3037485SYan-Hsuan Chuang 	return 0;
252e3037485SYan-Hsuan Chuang }
253e3037485SYan-Hsuan Chuang 
254e3037485SYan-Hsuan Chuang static int rtw_mac_init_system_cfg(struct rtw_dev *rtwdev)
255e3037485SYan-Hsuan Chuang {
256e3037485SYan-Hsuan Chuang 	u8 sys_func_en = rtwdev->chip->sys_func_en;
257e3037485SYan-Hsuan Chuang 	u8 value8;
258e3037485SYan-Hsuan Chuang 	u32 value, tmp;
259e3037485SYan-Hsuan Chuang 
260e3037485SYan-Hsuan Chuang 	value = rtw_read32(rtwdev, REG_CPU_DMEM_CON);
261e3037485SYan-Hsuan Chuang 	value |= BIT_WL_PLATFORM_RST | BIT_DDMA_EN;
262e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_CPU_DMEM_CON, value);
263e3037485SYan-Hsuan Chuang 
264e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_SYS_FUNC_EN + 1, sys_func_en);
265e3037485SYan-Hsuan Chuang 	value8 = (rtw_read8(rtwdev, REG_CR_EXT + 3) & 0xF0) | 0x0C;
266e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR_EXT + 3, value8);
267e3037485SYan-Hsuan Chuang 
268e3037485SYan-Hsuan Chuang 	/* disable boot-from-flash for driver's DL FW */
269e3037485SYan-Hsuan Chuang 	tmp = rtw_read32(rtwdev, REG_MCUFW_CTRL);
270e3037485SYan-Hsuan Chuang 	if (tmp & BIT_BOOT_FSPI_EN) {
271e3037485SYan-Hsuan Chuang 		rtw_write32(rtwdev, REG_MCUFW_CTRL, tmp & (~BIT_BOOT_FSPI_EN));
272e3037485SYan-Hsuan Chuang 		value = rtw_read32(rtwdev, REG_GPIO_MUXCFG) & (~BIT_FSPI_EN);
273e3037485SYan-Hsuan Chuang 		rtw_write32(rtwdev, REG_GPIO_MUXCFG, value);
274e3037485SYan-Hsuan Chuang 	}
275e3037485SYan-Hsuan Chuang 
276e3037485SYan-Hsuan Chuang 	return 0;
277e3037485SYan-Hsuan Chuang }
278e3037485SYan-Hsuan Chuang 
279e3037485SYan-Hsuan Chuang int rtw_mac_power_on(struct rtw_dev *rtwdev)
280e3037485SYan-Hsuan Chuang {
281e3037485SYan-Hsuan Chuang 	int ret = 0;
282e3037485SYan-Hsuan Chuang 
283e3037485SYan-Hsuan Chuang 	ret = rtw_mac_pre_system_cfg(rtwdev);
284e3037485SYan-Hsuan Chuang 	if (ret)
285e3037485SYan-Hsuan Chuang 		goto err;
286e3037485SYan-Hsuan Chuang 
287e3037485SYan-Hsuan Chuang 	ret = rtw_mac_power_switch(rtwdev, true);
288e3037485SYan-Hsuan Chuang 	if (ret)
289e3037485SYan-Hsuan Chuang 		goto err;
290e3037485SYan-Hsuan Chuang 
291e3037485SYan-Hsuan Chuang 	ret = rtw_mac_init_system_cfg(rtwdev);
292e3037485SYan-Hsuan Chuang 	if (ret)
293e3037485SYan-Hsuan Chuang 		goto err;
294e3037485SYan-Hsuan Chuang 
295e3037485SYan-Hsuan Chuang 	return 0;
296e3037485SYan-Hsuan Chuang 
297e3037485SYan-Hsuan Chuang err:
298e3037485SYan-Hsuan Chuang 	rtw_err(rtwdev, "mac power on failed");
299e3037485SYan-Hsuan Chuang 	return ret;
300e3037485SYan-Hsuan Chuang }
301e3037485SYan-Hsuan Chuang 
302e3037485SYan-Hsuan Chuang void rtw_mac_power_off(struct rtw_dev *rtwdev)
303e3037485SYan-Hsuan Chuang {
304e3037485SYan-Hsuan Chuang 	rtw_mac_power_switch(rtwdev, false);
305e3037485SYan-Hsuan Chuang }
306e3037485SYan-Hsuan Chuang 
307e3037485SYan-Hsuan Chuang static bool check_firmware_size(const u8 *data, u32 size)
308e3037485SYan-Hsuan Chuang {
309e3037485SYan-Hsuan Chuang 	u32 dmem_size;
310e3037485SYan-Hsuan Chuang 	u32 imem_size;
311e3037485SYan-Hsuan Chuang 	u32 emem_size;
312e3037485SYan-Hsuan Chuang 	u32 real_size;
313e3037485SYan-Hsuan Chuang 
314e3037485SYan-Hsuan Chuang 	dmem_size = le32_to_cpu(*((__le32 *)(data + FW_HDR_DMEM_SIZE)));
315e3037485SYan-Hsuan Chuang 	imem_size = le32_to_cpu(*((__le32 *)(data + FW_HDR_IMEM_SIZE)));
316e3037485SYan-Hsuan Chuang 	emem_size = ((*(data + FW_HDR_MEM_USAGE)) & BIT(4)) ?
317e3037485SYan-Hsuan Chuang 		    le32_to_cpu(*((__le32 *)(data + FW_HDR_EMEM_SIZE))) : 0;
318e3037485SYan-Hsuan Chuang 
319e3037485SYan-Hsuan Chuang 	dmem_size += FW_HDR_CHKSUM_SIZE;
320e3037485SYan-Hsuan Chuang 	imem_size += FW_HDR_CHKSUM_SIZE;
321e3037485SYan-Hsuan Chuang 	emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0;
322e3037485SYan-Hsuan Chuang 	real_size = FW_HDR_SIZE + dmem_size + imem_size + emem_size;
323e3037485SYan-Hsuan Chuang 	if (real_size != size)
324e3037485SYan-Hsuan Chuang 		return false;
325e3037485SYan-Hsuan Chuang 
326e3037485SYan-Hsuan Chuang 	return true;
327e3037485SYan-Hsuan Chuang }
328e3037485SYan-Hsuan Chuang 
329e3037485SYan-Hsuan Chuang static void wlan_cpu_enable(struct rtw_dev *rtwdev, bool enable)
330e3037485SYan-Hsuan Chuang {
331e3037485SYan-Hsuan Chuang 	if (enable) {
332e3037485SYan-Hsuan Chuang 		/* cpu io interface enable */
333e3037485SYan-Hsuan Chuang 		rtw_write8_set(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF);
334e3037485SYan-Hsuan Chuang 
335e3037485SYan-Hsuan Chuang 		/* cpu enable */
336e3037485SYan-Hsuan Chuang 		rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
337e3037485SYan-Hsuan Chuang 	} else {
338e3037485SYan-Hsuan Chuang 		/* cpu io interface disable */
339e3037485SYan-Hsuan Chuang 		rtw_write8_clr(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
340e3037485SYan-Hsuan Chuang 
341e3037485SYan-Hsuan Chuang 		/* cpu disable */
342e3037485SYan-Hsuan Chuang 		rtw_write8_clr(rtwdev, REG_RSV_CTRL + 1, BIT_WLMCU_IOIF);
343e3037485SYan-Hsuan Chuang 	}
344e3037485SYan-Hsuan Chuang }
345e3037485SYan-Hsuan Chuang 
346e3037485SYan-Hsuan Chuang #define DLFW_RESTORE_REG_NUM 6
347e3037485SYan-Hsuan Chuang 
348e3037485SYan-Hsuan Chuang static void download_firmware_reg_backup(struct rtw_dev *rtwdev,
349e3037485SYan-Hsuan Chuang 					 struct rtw_backup_info *bckp)
350e3037485SYan-Hsuan Chuang {
351e3037485SYan-Hsuan Chuang 	u8 tmp;
352e3037485SYan-Hsuan Chuang 	u8 bckp_idx = 0;
353e3037485SYan-Hsuan Chuang 
354e3037485SYan-Hsuan Chuang 	/* set HIQ to hi priority */
355e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
356e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_TXDMA_PQ_MAP + 1;
357e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read8(rtwdev, REG_TXDMA_PQ_MAP + 1);
358e3037485SYan-Hsuan Chuang 	bckp_idx++;
359e3037485SYan-Hsuan Chuang 	tmp = RTW_DMA_MAPPING_HIGH << 6;
360e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_TXDMA_PQ_MAP + 1, tmp);
361e3037485SYan-Hsuan Chuang 
362e3037485SYan-Hsuan Chuang 	/* DLFW only use HIQ, map HIQ to hi priority */
363e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
364e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_CR;
365e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read8(rtwdev, REG_CR);
366e3037485SYan-Hsuan Chuang 	bckp_idx++;
367e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 4;
368e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_H2CQ_CSR;
369e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = BIT_H2CQ_FULL;
370e3037485SYan-Hsuan Chuang 	bckp_idx++;
371e3037485SYan-Hsuan Chuang 	tmp = BIT_HCI_TXDMA_EN | BIT_TXDMA_EN;
372e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, tmp);
373e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL);
374e3037485SYan-Hsuan Chuang 
375e3037485SYan-Hsuan Chuang 	/* Config hi priority queue and public priority queue page number */
376e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 2;
377e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_FIFOPAGE_INFO_1;
378e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read16(rtwdev, REG_FIFOPAGE_INFO_1);
379e3037485SYan-Hsuan Chuang 	bckp_idx++;
380e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 4;
381e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_RQPN_CTRL_2;
382e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = rtw_read32(rtwdev, REG_RQPN_CTRL_2) | BIT_LD_RQPN;
383e3037485SYan-Hsuan Chuang 	bckp_idx++;
384e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, 0x200);
385e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_RQPN_CTRL_2, bckp[bckp_idx - 1].val);
386e3037485SYan-Hsuan Chuang 
387e3037485SYan-Hsuan Chuang 	/* Disable beacon related functions */
388e3037485SYan-Hsuan Chuang 	tmp = rtw_read8(rtwdev, REG_BCN_CTRL);
389e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].len = 1;
390e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].reg = REG_BCN_CTRL;
391e3037485SYan-Hsuan Chuang 	bckp[bckp_idx].val = tmp;
392e3037485SYan-Hsuan Chuang 	bckp_idx++;
393e3037485SYan-Hsuan Chuang 	tmp = (u8)((tmp & (~BIT_EN_BCN_FUNCTION)) | BIT_DIS_TSF_UDT);
394e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_BCN_CTRL, tmp);
395e3037485SYan-Hsuan Chuang 
396e3037485SYan-Hsuan Chuang 	WARN(bckp_idx != DLFW_RESTORE_REG_NUM, "wrong backup number\n");
397e3037485SYan-Hsuan Chuang }
398e3037485SYan-Hsuan Chuang 
399e3037485SYan-Hsuan Chuang static void download_firmware_reset_platform(struct rtw_dev *rtwdev)
400e3037485SYan-Hsuan Chuang {
401e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16);
402e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8);
403e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_CPU_DMEM_CON + 2, BIT_WL_PLATFORM_RST >> 16);
404e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_SYS_CLK_CTRL + 1, BIT_CPU_CLK_EN >> 8);
405e3037485SYan-Hsuan Chuang }
406e3037485SYan-Hsuan Chuang 
407e3037485SYan-Hsuan Chuang static void download_firmware_reg_restore(struct rtw_dev *rtwdev,
408e3037485SYan-Hsuan Chuang 					  struct rtw_backup_info *bckp,
409e3037485SYan-Hsuan Chuang 					  u8 bckp_num)
410e3037485SYan-Hsuan Chuang {
411e3037485SYan-Hsuan Chuang 	rtw_restore_reg(rtwdev, bckp, bckp_num);
412e3037485SYan-Hsuan Chuang }
413e3037485SYan-Hsuan Chuang 
414e3037485SYan-Hsuan Chuang #define TX_DESC_SIZE 48
415e3037485SYan-Hsuan Chuang 
416e3037485SYan-Hsuan Chuang static int send_firmware_pkt_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
417e3037485SYan-Hsuan Chuang 				       const u8 *data, u32 size)
418e3037485SYan-Hsuan Chuang {
419e3037485SYan-Hsuan Chuang 	u8 *buf;
420e3037485SYan-Hsuan Chuang 	int ret;
421e3037485SYan-Hsuan Chuang 
422e3037485SYan-Hsuan Chuang 	buf = kmemdup(data, size, GFP_KERNEL);
423e3037485SYan-Hsuan Chuang 	if (!buf)
424e3037485SYan-Hsuan Chuang 		return -ENOMEM;
425e3037485SYan-Hsuan Chuang 
426e3037485SYan-Hsuan Chuang 	ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size);
427e3037485SYan-Hsuan Chuang 	kfree(buf);
428e3037485SYan-Hsuan Chuang 	return ret;
429e3037485SYan-Hsuan Chuang }
430e3037485SYan-Hsuan Chuang 
431e3037485SYan-Hsuan Chuang static int
432e3037485SYan-Hsuan Chuang send_firmware_pkt(struct rtw_dev *rtwdev, u16 pg_addr, const u8 *data, u32 size)
433e3037485SYan-Hsuan Chuang {
434e3037485SYan-Hsuan Chuang 	int ret;
435e3037485SYan-Hsuan Chuang 
436e3037485SYan-Hsuan Chuang 	if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB &&
437e3037485SYan-Hsuan Chuang 	    !((size + TX_DESC_SIZE) & (512 - 1)))
438e3037485SYan-Hsuan Chuang 		size += 1;
439e3037485SYan-Hsuan Chuang 
440e3037485SYan-Hsuan Chuang 	ret = send_firmware_pkt_rsvd_page(rtwdev, pg_addr, data, size);
441e3037485SYan-Hsuan Chuang 	if (ret)
442e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "failed to download rsvd page\n");
443e3037485SYan-Hsuan Chuang 
444e3037485SYan-Hsuan Chuang 	return ret;
445e3037485SYan-Hsuan Chuang }
446e3037485SYan-Hsuan Chuang 
447e3037485SYan-Hsuan Chuang static int
448e3037485SYan-Hsuan Chuang iddma_enable(struct rtw_dev *rtwdev, u32 src, u32 dst, u32 ctrl)
449e3037485SYan-Hsuan Chuang {
450e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0SA, src);
451e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0DA, dst);
452e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0CTRL, ctrl);
453e3037485SYan-Hsuan Chuang 
454e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0))
455e3037485SYan-Hsuan Chuang 		return -EBUSY;
456e3037485SYan-Hsuan Chuang 
457e3037485SYan-Hsuan Chuang 	return 0;
458e3037485SYan-Hsuan Chuang }
459e3037485SYan-Hsuan Chuang 
460e3037485SYan-Hsuan Chuang static int iddma_download_firmware(struct rtw_dev *rtwdev, u32 src, u32 dst,
461e3037485SYan-Hsuan Chuang 				   u32 len, u8 first)
462e3037485SYan-Hsuan Chuang {
463e3037485SYan-Hsuan Chuang 	u32 ch0_ctrl = BIT_DDMACH0_CHKSUM_EN | BIT_DDMACH0_OWN;
464e3037485SYan-Hsuan Chuang 
465e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_DDMA_CH0CTRL, BIT_DDMACH0_OWN, 0))
466e3037485SYan-Hsuan Chuang 		return -EBUSY;
467e3037485SYan-Hsuan Chuang 
468e3037485SYan-Hsuan Chuang 	ch0_ctrl |= len & BIT_MASK_DDMACH0_DLEN;
469e3037485SYan-Hsuan Chuang 	if (!first)
470e3037485SYan-Hsuan Chuang 		ch0_ctrl |= BIT_DDMACH0_CHKSUM_CONT;
471e3037485SYan-Hsuan Chuang 
472e3037485SYan-Hsuan Chuang 	if (iddma_enable(rtwdev, src, dst, ch0_ctrl))
473e3037485SYan-Hsuan Chuang 		return -EBUSY;
474e3037485SYan-Hsuan Chuang 
475e3037485SYan-Hsuan Chuang 	return 0;
476e3037485SYan-Hsuan Chuang }
477e3037485SYan-Hsuan Chuang 
478e3037485SYan-Hsuan Chuang static bool
479e3037485SYan-Hsuan Chuang check_fw_checksum(struct rtw_dev *rtwdev, u32 addr)
480e3037485SYan-Hsuan Chuang {
481e3037485SYan-Hsuan Chuang 	u8 fw_ctrl;
482e3037485SYan-Hsuan Chuang 
483e3037485SYan-Hsuan Chuang 	fw_ctrl = rtw_read8(rtwdev, REG_MCUFW_CTRL);
484e3037485SYan-Hsuan Chuang 
485e3037485SYan-Hsuan Chuang 	if (rtw_read32(rtwdev, REG_DDMA_CH0CTRL) & BIT_DDMACH0_CHKSUM_STS) {
486e3037485SYan-Hsuan Chuang 		if (addr < OCPBASE_DMEM_88XX) {
487e3037485SYan-Hsuan Chuang 			fw_ctrl |= BIT_IMEM_DW_OK;
488e3037485SYan-Hsuan Chuang 			fw_ctrl &= ~BIT_IMEM_CHKSUM_OK;
489e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
490e3037485SYan-Hsuan Chuang 		} else {
491e3037485SYan-Hsuan Chuang 			fw_ctrl |= BIT_DMEM_DW_OK;
492e3037485SYan-Hsuan Chuang 			fw_ctrl &= ~BIT_DMEM_CHKSUM_OK;
493e3037485SYan-Hsuan Chuang 			rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
494e3037485SYan-Hsuan Chuang 		}
495e3037485SYan-Hsuan Chuang 
496e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "invalid fw checksum\n");
497e3037485SYan-Hsuan Chuang 
498e3037485SYan-Hsuan Chuang 		return false;
499e3037485SYan-Hsuan Chuang 	}
500e3037485SYan-Hsuan Chuang 
501e3037485SYan-Hsuan Chuang 	if (addr < OCPBASE_DMEM_88XX) {
502e3037485SYan-Hsuan Chuang 		fw_ctrl |= (BIT_IMEM_DW_OK | BIT_IMEM_CHKSUM_OK);
503e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
504e3037485SYan-Hsuan Chuang 	} else {
505e3037485SYan-Hsuan Chuang 		fw_ctrl |= (BIT_DMEM_DW_OK | BIT_DMEM_CHKSUM_OK);
506e3037485SYan-Hsuan Chuang 		rtw_write8(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
507e3037485SYan-Hsuan Chuang 	}
508e3037485SYan-Hsuan Chuang 
509e3037485SYan-Hsuan Chuang 	return true;
510e3037485SYan-Hsuan Chuang }
511e3037485SYan-Hsuan Chuang 
512e3037485SYan-Hsuan Chuang static int
513e3037485SYan-Hsuan Chuang download_firmware_to_mem(struct rtw_dev *rtwdev, const u8 *data,
514e3037485SYan-Hsuan Chuang 			 u32 src, u32 dst, u32 size)
515e3037485SYan-Hsuan Chuang {
516e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
517e3037485SYan-Hsuan Chuang 	u32 desc_size = chip->tx_pkt_desc_sz;
518e3037485SYan-Hsuan Chuang 	u8 first_part;
519e3037485SYan-Hsuan Chuang 	u32 mem_offset;
520e3037485SYan-Hsuan Chuang 	u32 residue_size;
521e3037485SYan-Hsuan Chuang 	u32 pkt_size;
522e3037485SYan-Hsuan Chuang 	u32 max_size = 0x1000;
523e3037485SYan-Hsuan Chuang 	u32 val;
524e3037485SYan-Hsuan Chuang 	int ret;
525e3037485SYan-Hsuan Chuang 
526e3037485SYan-Hsuan Chuang 	mem_offset = 0;
527e3037485SYan-Hsuan Chuang 	first_part = 1;
528e3037485SYan-Hsuan Chuang 	residue_size = size;
529e3037485SYan-Hsuan Chuang 
530e3037485SYan-Hsuan Chuang 	val = rtw_read32(rtwdev, REG_DDMA_CH0CTRL);
531e3037485SYan-Hsuan Chuang 	val |= BIT_DDMACH0_RESET_CHKSUM_STS;
532e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_DDMA_CH0CTRL, val);
533e3037485SYan-Hsuan Chuang 
534e3037485SYan-Hsuan Chuang 	while (residue_size) {
535e3037485SYan-Hsuan Chuang 		if (residue_size >= max_size)
536e3037485SYan-Hsuan Chuang 			pkt_size = max_size;
537e3037485SYan-Hsuan Chuang 		else
538e3037485SYan-Hsuan Chuang 			pkt_size = residue_size;
539e3037485SYan-Hsuan Chuang 
540e3037485SYan-Hsuan Chuang 		ret = send_firmware_pkt(rtwdev, (u16)(src >> 7),
541e3037485SYan-Hsuan Chuang 					data + mem_offset, pkt_size);
542e3037485SYan-Hsuan Chuang 		if (ret)
543e3037485SYan-Hsuan Chuang 			return ret;
544e3037485SYan-Hsuan Chuang 
545e3037485SYan-Hsuan Chuang 		ret = iddma_download_firmware(rtwdev, OCPBASE_TXBUF_88XX +
546e3037485SYan-Hsuan Chuang 					      src + desc_size,
547e3037485SYan-Hsuan Chuang 					      dst + mem_offset, pkt_size,
548e3037485SYan-Hsuan Chuang 					      first_part);
549e3037485SYan-Hsuan Chuang 		if (ret)
550e3037485SYan-Hsuan Chuang 			return ret;
551e3037485SYan-Hsuan Chuang 
552e3037485SYan-Hsuan Chuang 		first_part = 0;
553e3037485SYan-Hsuan Chuang 		mem_offset += pkt_size;
554e3037485SYan-Hsuan Chuang 		residue_size -= pkt_size;
555e3037485SYan-Hsuan Chuang 	}
556e3037485SYan-Hsuan Chuang 
557e3037485SYan-Hsuan Chuang 	if (!check_fw_checksum(rtwdev, dst))
558e3037485SYan-Hsuan Chuang 		return -EINVAL;
559e3037485SYan-Hsuan Chuang 
560e3037485SYan-Hsuan Chuang 	return 0;
561e3037485SYan-Hsuan Chuang }
562e3037485SYan-Hsuan Chuang 
563e3037485SYan-Hsuan Chuang static void update_firmware_info(struct rtw_dev *rtwdev,
564e3037485SYan-Hsuan Chuang 				 struct rtw_fw_state *fw)
565e3037485SYan-Hsuan Chuang {
566e3037485SYan-Hsuan Chuang 	const u8 *data = fw->firmware->data;
567e3037485SYan-Hsuan Chuang 
568e3037485SYan-Hsuan Chuang 	fw->h2c_version =
569e3037485SYan-Hsuan Chuang 		le16_to_cpu(*((__le16 *)(data + FW_HDR_H2C_FMT_VER)));
570e3037485SYan-Hsuan Chuang 	fw->version =
571e3037485SYan-Hsuan Chuang 		le16_to_cpu(*((__le16 *)(data + FW_HDR_VERSION)));
572e3037485SYan-Hsuan Chuang 	fw->sub_version = *(data + FW_HDR_SUBVERSION);
573e3037485SYan-Hsuan Chuang 	fw->sub_index = *(data + FW_HDR_SUBINDEX);
574e3037485SYan-Hsuan Chuang 
575e3037485SYan-Hsuan Chuang 	rtw_dbg(rtwdev, RTW_DBG_FW, "fw h2c version: %x\n", fw->h2c_version);
576e3037485SYan-Hsuan Chuang 	rtw_dbg(rtwdev, RTW_DBG_FW, "fw version:     %x\n", fw->version);
577e3037485SYan-Hsuan Chuang 	rtw_dbg(rtwdev, RTW_DBG_FW, "fw sub version: %x\n", fw->sub_version);
578e3037485SYan-Hsuan Chuang 	rtw_dbg(rtwdev, RTW_DBG_FW, "fw sub index:   %x\n", fw->sub_index);
579e3037485SYan-Hsuan Chuang }
580e3037485SYan-Hsuan Chuang 
581e3037485SYan-Hsuan Chuang static int
582e3037485SYan-Hsuan Chuang start_download_firmware(struct rtw_dev *rtwdev, const u8 *data, u32 size)
583e3037485SYan-Hsuan Chuang {
584e3037485SYan-Hsuan Chuang 	const u8 *cur_fw;
585e3037485SYan-Hsuan Chuang 	u16 val;
586e3037485SYan-Hsuan Chuang 	u32 imem_size;
587e3037485SYan-Hsuan Chuang 	u32 dmem_size;
588e3037485SYan-Hsuan Chuang 	u32 emem_size;
589e3037485SYan-Hsuan Chuang 	u32 addr;
590e3037485SYan-Hsuan Chuang 	int ret;
591e3037485SYan-Hsuan Chuang 
592e3037485SYan-Hsuan Chuang 	dmem_size = le32_to_cpu(*((__le32 *)(data + FW_HDR_DMEM_SIZE)));
593e3037485SYan-Hsuan Chuang 	imem_size = le32_to_cpu(*((__le32 *)(data + FW_HDR_IMEM_SIZE)));
594e3037485SYan-Hsuan Chuang 	emem_size = ((*(data + FW_HDR_MEM_USAGE)) & BIT(4)) ?
595e3037485SYan-Hsuan Chuang 		    le32_to_cpu(*((__le32 *)(data + FW_HDR_EMEM_SIZE))) : 0;
596e3037485SYan-Hsuan Chuang 	dmem_size += FW_HDR_CHKSUM_SIZE;
597e3037485SYan-Hsuan Chuang 	imem_size += FW_HDR_CHKSUM_SIZE;
598e3037485SYan-Hsuan Chuang 	emem_size += emem_size ? FW_HDR_CHKSUM_SIZE : 0;
599e3037485SYan-Hsuan Chuang 
600e3037485SYan-Hsuan Chuang 	val = (u16)(rtw_read16(rtwdev, REG_MCUFW_CTRL) & 0x3800);
601e3037485SYan-Hsuan Chuang 	val |= BIT_MCUFWDL_EN;
602e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_MCUFW_CTRL, val);
603e3037485SYan-Hsuan Chuang 
604e3037485SYan-Hsuan Chuang 	cur_fw = data + FW_HDR_SIZE;
605e3037485SYan-Hsuan Chuang 	addr = le32_to_cpu(*((__le32 *)(data + FW_HDR_DMEM_ADDR)));
606e3037485SYan-Hsuan Chuang 	addr &= ~BIT(31);
607e3037485SYan-Hsuan Chuang 	ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, dmem_size);
608e3037485SYan-Hsuan Chuang 	if (ret)
609e3037485SYan-Hsuan Chuang 		return ret;
610e3037485SYan-Hsuan Chuang 
611e3037485SYan-Hsuan Chuang 	cur_fw = data + FW_HDR_SIZE + dmem_size;
612e3037485SYan-Hsuan Chuang 	addr = le32_to_cpu(*((__le32 *)(data + FW_HDR_IMEM_ADDR)));
613e3037485SYan-Hsuan Chuang 	addr &= ~BIT(31);
614e3037485SYan-Hsuan Chuang 	ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr, imem_size);
615e3037485SYan-Hsuan Chuang 	if (ret)
616e3037485SYan-Hsuan Chuang 		return ret;
617e3037485SYan-Hsuan Chuang 
618e3037485SYan-Hsuan Chuang 	if (emem_size) {
619e3037485SYan-Hsuan Chuang 		cur_fw = data + FW_HDR_SIZE + dmem_size + imem_size;
620e3037485SYan-Hsuan Chuang 		addr = le32_to_cpu(*((__le32 *)(data + FW_HDR_EMEM_ADDR)));
621e3037485SYan-Hsuan Chuang 		addr &= ~BIT(31);
622e3037485SYan-Hsuan Chuang 		ret = download_firmware_to_mem(rtwdev, cur_fw, 0, addr,
623e3037485SYan-Hsuan Chuang 					       emem_size);
624e3037485SYan-Hsuan Chuang 		if (ret)
625e3037485SYan-Hsuan Chuang 			return ret;
626e3037485SYan-Hsuan Chuang 	}
627e3037485SYan-Hsuan Chuang 
628e3037485SYan-Hsuan Chuang 	return 0;
629e3037485SYan-Hsuan Chuang }
630e3037485SYan-Hsuan Chuang 
631e3037485SYan-Hsuan Chuang static int download_firmware_validate(struct rtw_dev *rtwdev)
632e3037485SYan-Hsuan Chuang {
633e3037485SYan-Hsuan Chuang 	u32 fw_key;
634e3037485SYan-Hsuan Chuang 
635e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, FW_READY_MASK, FW_READY)) {
636e3037485SYan-Hsuan Chuang 		fw_key = rtw_read32(rtwdev, REG_FW_DBG7) & FW_KEY_MASK;
637e3037485SYan-Hsuan Chuang 		if (fw_key == ILLEGAL_KEY_GROUP)
638e3037485SYan-Hsuan Chuang 			rtw_err(rtwdev, "invalid fw key\n");
639e3037485SYan-Hsuan Chuang 		return -EINVAL;
640e3037485SYan-Hsuan Chuang 	}
641e3037485SYan-Hsuan Chuang 
642e3037485SYan-Hsuan Chuang 	return 0;
643e3037485SYan-Hsuan Chuang }
644e3037485SYan-Hsuan Chuang 
645e3037485SYan-Hsuan Chuang static void download_firmware_end_flow(struct rtw_dev *rtwdev)
646e3037485SYan-Hsuan Chuang {
647e3037485SYan-Hsuan Chuang 	u16 fw_ctrl;
648e3037485SYan-Hsuan Chuang 
649e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_TXDMA_STATUS, BTI_PAGE_OVF);
650e3037485SYan-Hsuan Chuang 
651e3037485SYan-Hsuan Chuang 	/* Check IMEM & DMEM checksum is OK or not */
652e3037485SYan-Hsuan Chuang 	fw_ctrl = rtw_read16(rtwdev, REG_MCUFW_CTRL);
653e3037485SYan-Hsuan Chuang 	if ((fw_ctrl & BIT_CHECK_SUM_OK) != BIT_CHECK_SUM_OK)
654e3037485SYan-Hsuan Chuang 		return;
655e3037485SYan-Hsuan Chuang 
656e3037485SYan-Hsuan Chuang 	fw_ctrl = (fw_ctrl | BIT_FW_DW_RDY) & ~BIT_MCUFWDL_EN;
657e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
658e3037485SYan-Hsuan Chuang }
659e3037485SYan-Hsuan Chuang 
660e3037485SYan-Hsuan Chuang int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
661e3037485SYan-Hsuan Chuang {
662e3037485SYan-Hsuan Chuang 	struct rtw_backup_info bckp[DLFW_RESTORE_REG_NUM];
663e3037485SYan-Hsuan Chuang 	const u8 *data = fw->firmware->data;
664e3037485SYan-Hsuan Chuang 	u32 size = fw->firmware->size;
665e3037485SYan-Hsuan Chuang 	u32 ltecoex_bckp;
666e3037485SYan-Hsuan Chuang 	int ret;
667e3037485SYan-Hsuan Chuang 
668e3037485SYan-Hsuan Chuang 	if (!check_firmware_size(data, size))
669e3037485SYan-Hsuan Chuang 		return -EINVAL;
670e3037485SYan-Hsuan Chuang 
671e3037485SYan-Hsuan Chuang 	if (!ltecoex_read_reg(rtwdev, 0x38, &ltecoex_bckp))
672e3037485SYan-Hsuan Chuang 		return -EBUSY;
673e3037485SYan-Hsuan Chuang 
674e3037485SYan-Hsuan Chuang 	wlan_cpu_enable(rtwdev, false);
675e3037485SYan-Hsuan Chuang 
676e3037485SYan-Hsuan Chuang 	download_firmware_reg_backup(rtwdev, bckp);
677e3037485SYan-Hsuan Chuang 	download_firmware_reset_platform(rtwdev);
678e3037485SYan-Hsuan Chuang 
679e3037485SYan-Hsuan Chuang 	ret = start_download_firmware(rtwdev, data, size);
680e3037485SYan-Hsuan Chuang 	if (ret)
681e3037485SYan-Hsuan Chuang 		goto dlfw_fail;
682e3037485SYan-Hsuan Chuang 
683e3037485SYan-Hsuan Chuang 	download_firmware_reg_restore(rtwdev, bckp, DLFW_RESTORE_REG_NUM);
684e3037485SYan-Hsuan Chuang 
685e3037485SYan-Hsuan Chuang 	download_firmware_end_flow(rtwdev);
686e3037485SYan-Hsuan Chuang 
687e3037485SYan-Hsuan Chuang 	wlan_cpu_enable(rtwdev, true);
688e3037485SYan-Hsuan Chuang 
689e3037485SYan-Hsuan Chuang 	if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp))
690e3037485SYan-Hsuan Chuang 		return -EBUSY;
691e3037485SYan-Hsuan Chuang 
692e3037485SYan-Hsuan Chuang 	ret = download_firmware_validate(rtwdev);
693e3037485SYan-Hsuan Chuang 	if (ret)
694e3037485SYan-Hsuan Chuang 		goto dlfw_fail;
695e3037485SYan-Hsuan Chuang 
696e3037485SYan-Hsuan Chuang 	update_firmware_info(rtwdev, fw);
697e3037485SYan-Hsuan Chuang 
698e3037485SYan-Hsuan Chuang 	/* reset desc and index */
699e3037485SYan-Hsuan Chuang 	rtw_hci_setup(rtwdev);
700e3037485SYan-Hsuan Chuang 
701e3037485SYan-Hsuan Chuang 	rtwdev->h2c.last_box_num = 0;
702e3037485SYan-Hsuan Chuang 	rtwdev->h2c.seq = 0;
703e3037485SYan-Hsuan Chuang 
704e3037485SYan-Hsuan Chuang 	rtw_fw_send_general_info(rtwdev);
705e3037485SYan-Hsuan Chuang 	rtw_fw_send_phydm_info(rtwdev);
706e3037485SYan-Hsuan Chuang 
707e3037485SYan-Hsuan Chuang 	rtw_flag_set(rtwdev, RTW_FLAG_FW_RUNNING);
708e3037485SYan-Hsuan Chuang 
709e3037485SYan-Hsuan Chuang 	return 0;
710e3037485SYan-Hsuan Chuang 
711e3037485SYan-Hsuan Chuang dlfw_fail:
712e3037485SYan-Hsuan Chuang 	/* Disable FWDL_EN */
713e3037485SYan-Hsuan Chuang 	rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
714e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_SYS_FUNC_EN + 1, BIT_FEN_CPUEN);
715e3037485SYan-Hsuan Chuang 
716e3037485SYan-Hsuan Chuang 	return ret;
717e3037485SYan-Hsuan Chuang }
718e3037485SYan-Hsuan Chuang 
719e3037485SYan-Hsuan Chuang static int txdma_queue_mapping(struct rtw_dev *rtwdev)
720e3037485SYan-Hsuan Chuang {
721e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
722e3037485SYan-Hsuan Chuang 	struct rtw_rqpn *rqpn = NULL;
723e3037485SYan-Hsuan Chuang 	u16 txdma_pq_map = 0;
724e3037485SYan-Hsuan Chuang 
725e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
726e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
727e3037485SYan-Hsuan Chuang 		rqpn = &chip->rqpn_table[1];
728e3037485SYan-Hsuan Chuang 		break;
729e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
730e3037485SYan-Hsuan Chuang 		if (rtwdev->hci.bulkout_num == 2)
731e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[2];
732e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 3)
733e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[3];
734e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 4)
735e3037485SYan-Hsuan Chuang 			rqpn = &chip->rqpn_table[4];
736e3037485SYan-Hsuan Chuang 		else
737e3037485SYan-Hsuan Chuang 			return -EINVAL;
738e3037485SYan-Hsuan Chuang 		break;
739e3037485SYan-Hsuan Chuang 	default:
740e3037485SYan-Hsuan Chuang 		return -EINVAL;
741e3037485SYan-Hsuan Chuang 	}
742e3037485SYan-Hsuan Chuang 
743e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_HIQ_MAP(rqpn->dma_map_hi);
744e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_MGQ_MAP(rqpn->dma_map_mg);
745e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_BKQ_MAP(rqpn->dma_map_bk);
746e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_BEQ_MAP(rqpn->dma_map_be);
747e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_VIQ_MAP(rqpn->dma_map_vi);
748e3037485SYan-Hsuan Chuang 	txdma_pq_map |= BIT_TXDMA_VOQ_MAP(rqpn->dma_map_vo);
749e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_TXDMA_PQ_MAP, txdma_pq_map);
750e3037485SYan-Hsuan Chuang 
751e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, 0);
752e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR, MAC_TRX_ENABLE);
753e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2CQ_CSR, BIT_H2CQ_FULL);
754e3037485SYan-Hsuan Chuang 
755e3037485SYan-Hsuan Chuang 	return 0;
756e3037485SYan-Hsuan Chuang }
757e3037485SYan-Hsuan Chuang 
758e3037485SYan-Hsuan Chuang static int set_trx_fifo_info(struct rtw_dev *rtwdev)
759e3037485SYan-Hsuan Chuang {
760e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
761e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
762e3037485SYan-Hsuan Chuang 	u16 cur_pg_addr;
763e3037485SYan-Hsuan Chuang 	u8 csi_buf_pg_num = chip->csi_buf_pg_num;
764e3037485SYan-Hsuan Chuang 
765e3037485SYan-Hsuan Chuang 	/* config rsvd page num */
766e3037485SYan-Hsuan Chuang 	fifo->rsvd_drv_pg_num = 8;
767e3037485SYan-Hsuan Chuang 	fifo->txff_pg_num = chip->txff_size >> 7;
768e3037485SYan-Hsuan Chuang 	fifo->rsvd_pg_num = fifo->rsvd_drv_pg_num +
769e3037485SYan-Hsuan Chuang 			   RSVD_PG_H2C_EXTRAINFO_NUM +
770e3037485SYan-Hsuan Chuang 			   RSVD_PG_H2C_STATICINFO_NUM +
771e3037485SYan-Hsuan Chuang 			   RSVD_PG_H2CQ_NUM +
772e3037485SYan-Hsuan Chuang 			   RSVD_PG_CPU_INSTRUCTION_NUM +
773e3037485SYan-Hsuan Chuang 			   RSVD_PG_FW_TXBUF_NUM +
774e3037485SYan-Hsuan Chuang 			   csi_buf_pg_num;
775e3037485SYan-Hsuan Chuang 
776e3037485SYan-Hsuan Chuang 	if (fifo->rsvd_pg_num > fifo->txff_pg_num)
777e3037485SYan-Hsuan Chuang 		return -ENOMEM;
778e3037485SYan-Hsuan Chuang 
779e3037485SYan-Hsuan Chuang 	fifo->acq_pg_num = fifo->txff_pg_num - fifo->rsvd_pg_num;
780e3037485SYan-Hsuan Chuang 	fifo->rsvd_boundary = fifo->txff_pg_num - fifo->rsvd_pg_num;
781e3037485SYan-Hsuan Chuang 
782e3037485SYan-Hsuan Chuang 	cur_pg_addr = fifo->txff_pg_num;
783e3037485SYan-Hsuan Chuang 	cur_pg_addr -= csi_buf_pg_num;
784e3037485SYan-Hsuan Chuang 	fifo->rsvd_csibuf_addr = cur_pg_addr;
785e3037485SYan-Hsuan Chuang 	cur_pg_addr -= RSVD_PG_FW_TXBUF_NUM;
786e3037485SYan-Hsuan Chuang 	fifo->rsvd_fw_txbuf_addr = cur_pg_addr;
787e3037485SYan-Hsuan Chuang 	cur_pg_addr -= RSVD_PG_CPU_INSTRUCTION_NUM;
788e3037485SYan-Hsuan Chuang 	fifo->rsvd_cpu_instr_addr = cur_pg_addr;
789e3037485SYan-Hsuan Chuang 	cur_pg_addr -= RSVD_PG_H2CQ_NUM;
790e3037485SYan-Hsuan Chuang 	fifo->rsvd_h2cq_addr = cur_pg_addr;
791e3037485SYan-Hsuan Chuang 	cur_pg_addr -= RSVD_PG_H2C_STATICINFO_NUM;
792e3037485SYan-Hsuan Chuang 	fifo->rsvd_h2c_sta_info_addr = cur_pg_addr;
793e3037485SYan-Hsuan Chuang 	cur_pg_addr -= RSVD_PG_H2C_EXTRAINFO_NUM;
794e3037485SYan-Hsuan Chuang 	fifo->rsvd_h2c_info_addr = cur_pg_addr;
795e3037485SYan-Hsuan Chuang 	cur_pg_addr -= fifo->rsvd_drv_pg_num;
796e3037485SYan-Hsuan Chuang 	fifo->rsvd_drv_addr = cur_pg_addr;
797e3037485SYan-Hsuan Chuang 
798e3037485SYan-Hsuan Chuang 	if (fifo->rsvd_boundary != fifo->rsvd_drv_addr) {
799e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "wrong rsvd driver address\n");
800e3037485SYan-Hsuan Chuang 		return -EINVAL;
801e3037485SYan-Hsuan Chuang 	}
802e3037485SYan-Hsuan Chuang 
803e3037485SYan-Hsuan Chuang 	return 0;
804e3037485SYan-Hsuan Chuang }
805e3037485SYan-Hsuan Chuang 
806e3037485SYan-Hsuan Chuang static int priority_queue_cfg(struct rtw_dev *rtwdev)
807e3037485SYan-Hsuan Chuang {
808e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
809e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
810e3037485SYan-Hsuan Chuang 	struct rtw_page_table *pg_tbl = NULL;
811e3037485SYan-Hsuan Chuang 	u16 pubq_num;
812e3037485SYan-Hsuan Chuang 	int ret;
813e3037485SYan-Hsuan Chuang 
814e3037485SYan-Hsuan Chuang 	ret = set_trx_fifo_info(rtwdev);
815e3037485SYan-Hsuan Chuang 	if (ret)
816e3037485SYan-Hsuan Chuang 		return ret;
817e3037485SYan-Hsuan Chuang 
818e3037485SYan-Hsuan Chuang 	switch (rtw_hci_type(rtwdev)) {
819e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_PCIE:
820e3037485SYan-Hsuan Chuang 		pg_tbl = &chip->page_table[1];
821e3037485SYan-Hsuan Chuang 		break;
822e3037485SYan-Hsuan Chuang 	case RTW_HCI_TYPE_USB:
823e3037485SYan-Hsuan Chuang 		if (rtwdev->hci.bulkout_num == 2)
824e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[2];
825e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 3)
826e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[3];
827e3037485SYan-Hsuan Chuang 		else if (rtwdev->hci.bulkout_num == 4)
828e3037485SYan-Hsuan Chuang 			pg_tbl = &chip->page_table[4];
829e3037485SYan-Hsuan Chuang 		else
830e3037485SYan-Hsuan Chuang 			return -EINVAL;
831e3037485SYan-Hsuan Chuang 		break;
832e3037485SYan-Hsuan Chuang 	default:
833e3037485SYan-Hsuan Chuang 		return -EINVAL;
834e3037485SYan-Hsuan Chuang 	}
835e3037485SYan-Hsuan Chuang 
836e3037485SYan-Hsuan Chuang 	pubq_num = fifo->acq_pg_num - pg_tbl->hq_num - pg_tbl->lq_num -
837e3037485SYan-Hsuan Chuang 		   pg_tbl->nq_num - pg_tbl->exq_num - pg_tbl->gapq_num;
838e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_1, pg_tbl->hq_num);
839e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_2, pg_tbl->lq_num);
840e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_3, pg_tbl->nq_num);
841e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_4, pg_tbl->exq_num);
842e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_INFO_5, pubq_num);
843e3037485SYan-Hsuan Chuang 	rtw_write32_set(rtwdev, REG_RQPN_CTRL_2, BIT_LD_RQPN);
844e3037485SYan-Hsuan Chuang 
845e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, fifo->rsvd_boundary);
846e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL + 2, BIT_EN_WR_FREE_TAIL >> 16);
847e3037485SYan-Hsuan Chuang 
848e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_BCNQ_BDNY_V1, fifo->rsvd_boundary);
849e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2 + 2, fifo->rsvd_boundary);
850e3037485SYan-Hsuan Chuang 	rtw_write16(rtwdev, REG_BCNQ1_BDNY_V1, fifo->rsvd_boundary);
851e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_RXFF_BNDY, chip->rxff_size - C2H_PKT_BUF - 1);
852e3037485SYan-Hsuan Chuang 	rtw_write8_set(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1);
853e3037485SYan-Hsuan Chuang 
854e3037485SYan-Hsuan Chuang 	if (!check_hw_ready(rtwdev, REG_AUTO_LLT_V1, BIT_AUTO_INIT_LLT_V1, 0))
855e3037485SYan-Hsuan Chuang 		return -EBUSY;
856e3037485SYan-Hsuan Chuang 
857e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_CR + 3, 0);
858e3037485SYan-Hsuan Chuang 
859e3037485SYan-Hsuan Chuang 	return 0;
860e3037485SYan-Hsuan Chuang }
861e3037485SYan-Hsuan Chuang 
862e3037485SYan-Hsuan Chuang static int init_h2c(struct rtw_dev *rtwdev)
863e3037485SYan-Hsuan Chuang {
864e3037485SYan-Hsuan Chuang 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
865e3037485SYan-Hsuan Chuang 	u8 value8;
866e3037485SYan-Hsuan Chuang 	u32 value32;
867e3037485SYan-Hsuan Chuang 	u32 h2cq_addr;
868e3037485SYan-Hsuan Chuang 	u32 h2cq_size;
869e3037485SYan-Hsuan Chuang 	u32 h2cq_free;
870e3037485SYan-Hsuan Chuang 	u32 wp, rp;
871e3037485SYan-Hsuan Chuang 
872e3037485SYan-Hsuan Chuang 	h2cq_addr = fifo->rsvd_h2cq_addr << TX_PAGE_SIZE_SHIFT;
873e3037485SYan-Hsuan Chuang 	h2cq_size = RSVD_PG_H2CQ_NUM << TX_PAGE_SIZE_SHIFT;
874e3037485SYan-Hsuan Chuang 
875e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_HEAD);
876e3037485SYan-Hsuan Chuang 	value32 = (value32 & 0xFFFC0000) | h2cq_addr;
877e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_HEAD, value32);
878e3037485SYan-Hsuan Chuang 
879e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_READ_ADDR);
880e3037485SYan-Hsuan Chuang 	value32 = (value32 & 0xFFFC0000) | h2cq_addr;
881e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_READ_ADDR, value32);
882e3037485SYan-Hsuan Chuang 
883e3037485SYan-Hsuan Chuang 	value32 = rtw_read32(rtwdev, REG_H2C_TAIL);
884e3037485SYan-Hsuan Chuang 	value32 &= 0xFFFC0000;
885e3037485SYan-Hsuan Chuang 	value32 |= (h2cq_addr + h2cq_size);
886e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_H2C_TAIL, value32);
887e3037485SYan-Hsuan Chuang 
888e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_H2C_INFO);
889e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0xFC) | 0x01);
890e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_H2C_INFO, value8);
891e3037485SYan-Hsuan Chuang 
892e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_H2C_INFO);
893e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0xFB) | 0x04);
894e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_H2C_INFO, value8);
895e3037485SYan-Hsuan Chuang 
896e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_TXDMA_OFFSET_CHK + 1);
897e3037485SYan-Hsuan Chuang 	value8 = (u8)((value8 & 0x7f) | 0x80);
898e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_TXDMA_OFFSET_CHK + 1, value8);
899e3037485SYan-Hsuan Chuang 
900e3037485SYan-Hsuan Chuang 	wp = rtw_read32(rtwdev, REG_H2C_PKT_WRITEADDR) & 0x3FFFF;
901e3037485SYan-Hsuan Chuang 	rp = rtw_read32(rtwdev, REG_H2C_PKT_READADDR) & 0x3FFFF;
902e3037485SYan-Hsuan Chuang 	h2cq_free = wp >= rp ? h2cq_size - (wp - rp) : rp - wp;
903e3037485SYan-Hsuan Chuang 
904e3037485SYan-Hsuan Chuang 	if (h2cq_size != h2cq_free) {
905e3037485SYan-Hsuan Chuang 		rtw_err(rtwdev, "H2C queue mismatch\n");
906e3037485SYan-Hsuan Chuang 		return -EINVAL;
907e3037485SYan-Hsuan Chuang 	}
908e3037485SYan-Hsuan Chuang 
909e3037485SYan-Hsuan Chuang 	return 0;
910e3037485SYan-Hsuan Chuang }
911e3037485SYan-Hsuan Chuang 
912e3037485SYan-Hsuan Chuang static int rtw_init_trx_cfg(struct rtw_dev *rtwdev)
913e3037485SYan-Hsuan Chuang {
914e3037485SYan-Hsuan Chuang 	int ret;
915e3037485SYan-Hsuan Chuang 
916e3037485SYan-Hsuan Chuang 	ret = txdma_queue_mapping(rtwdev);
917e3037485SYan-Hsuan Chuang 	if (ret)
918e3037485SYan-Hsuan Chuang 		return ret;
919e3037485SYan-Hsuan Chuang 
920e3037485SYan-Hsuan Chuang 	ret = priority_queue_cfg(rtwdev);
921e3037485SYan-Hsuan Chuang 	if (ret)
922e3037485SYan-Hsuan Chuang 		return ret;
923e3037485SYan-Hsuan Chuang 
924e3037485SYan-Hsuan Chuang 	ret = init_h2c(rtwdev);
925e3037485SYan-Hsuan Chuang 	if (ret)
926e3037485SYan-Hsuan Chuang 		return ret;
927e3037485SYan-Hsuan Chuang 
928e3037485SYan-Hsuan Chuang 	return 0;
929e3037485SYan-Hsuan Chuang }
930e3037485SYan-Hsuan Chuang 
931e3037485SYan-Hsuan Chuang static int rtw_drv_info_cfg(struct rtw_dev *rtwdev)
932e3037485SYan-Hsuan Chuang {
933e3037485SYan-Hsuan Chuang 	u8 value8;
934e3037485SYan-Hsuan Chuang 
935e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_RX_DRVINFO_SZ, PHY_STATUS_SIZE);
936e3037485SYan-Hsuan Chuang 	value8 = rtw_read8(rtwdev, REG_TRXFF_BNDY + 1);
937e3037485SYan-Hsuan Chuang 	value8 &= 0xF0;
938e3037485SYan-Hsuan Chuang 	/* For rxdesc len = 0 issue */
939e3037485SYan-Hsuan Chuang 	value8 |= 0xF;
940e3037485SYan-Hsuan Chuang 	rtw_write8(rtwdev, REG_TRXFF_BNDY + 1, value8);
941e3037485SYan-Hsuan Chuang 	rtw_write32_set(rtwdev, REG_RCR, BIT_APP_PHYSTS);
942e3037485SYan-Hsuan Chuang 	rtw_write32_clr(rtwdev, REG_WMAC_OPTION_FUNCTION + 4, BIT(8) | BIT(9));
943e3037485SYan-Hsuan Chuang 
944e3037485SYan-Hsuan Chuang 	return 0;
945e3037485SYan-Hsuan Chuang }
946e3037485SYan-Hsuan Chuang 
947e3037485SYan-Hsuan Chuang int rtw_mac_init(struct rtw_dev *rtwdev)
948e3037485SYan-Hsuan Chuang {
949e3037485SYan-Hsuan Chuang 	struct rtw_chip_info *chip = rtwdev->chip;
950e3037485SYan-Hsuan Chuang 	int ret;
951e3037485SYan-Hsuan Chuang 
952e3037485SYan-Hsuan Chuang 	ret = rtw_init_trx_cfg(rtwdev);
953e3037485SYan-Hsuan Chuang 	if (ret)
954e3037485SYan-Hsuan Chuang 		return ret;
955e3037485SYan-Hsuan Chuang 
956e3037485SYan-Hsuan Chuang 	ret = chip->ops->mac_init(rtwdev);
957e3037485SYan-Hsuan Chuang 	if (ret)
958e3037485SYan-Hsuan Chuang 		return ret;
959e3037485SYan-Hsuan Chuang 
960e3037485SYan-Hsuan Chuang 	ret = rtw_drv_info_cfg(rtwdev);
961e3037485SYan-Hsuan Chuang 	if (ret)
962e3037485SYan-Hsuan Chuang 		return ret;
963e3037485SYan-Hsuan Chuang 
964e3037485SYan-Hsuan Chuang 	return 0;
965e3037485SYan-Hsuan Chuang }
966