162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2009-2012  Realtek Corporation.*/
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include "../wifi.h"
562306a36Sopenharmony_ci#include "../pci.h"
662306a36Sopenharmony_ci#include "../base.h"
762306a36Sopenharmony_ci#include "reg.h"
862306a36Sopenharmony_ci#include "def.h"
962306a36Sopenharmony_ci#include "fw.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic void _rtl92s_fw_set_rqpn(struct ieee80211_hw *hw)
1262306a36Sopenharmony_ci{
1362306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	rtl_write_dword(rtlpriv, RQPN, 0xffffffff);
1662306a36Sopenharmony_ci	rtl_write_dword(rtlpriv, RQPN + 4, 0xffffffff);
1762306a36Sopenharmony_ci	rtl_write_byte(rtlpriv, RQPN + 8, 0xff);
1862306a36Sopenharmony_ci	rtl_write_byte(rtlpriv, RQPN + 0xB, 0x80);
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic bool _rtl92s_firmware_enable_cpu(struct ieee80211_hw *hw)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
2462306a36Sopenharmony_ci	u32 ichecktime = 200;
2562306a36Sopenharmony_ci	u16 tmpu2b;
2662306a36Sopenharmony_ci	u8 tmpu1b, cpustatus = 0;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	_rtl92s_fw_set_rqpn(hw);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/* Enable CPU. */
3162306a36Sopenharmony_ci	tmpu1b = rtl_read_byte(rtlpriv, SYS_CLKR);
3262306a36Sopenharmony_ci	/* AFE source */
3362306a36Sopenharmony_ci	rtl_write_byte(rtlpriv, SYS_CLKR, (tmpu1b | SYS_CPU_CLKSEL));
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	tmpu2b = rtl_read_word(rtlpriv, REG_SYS_FUNC_EN);
3662306a36Sopenharmony_ci	rtl_write_word(rtlpriv, REG_SYS_FUNC_EN, (tmpu2b | FEN_CPUEN));
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Polling IMEM Ready after CPU has refilled. */
3962306a36Sopenharmony_ci	do {
4062306a36Sopenharmony_ci		cpustatus = rtl_read_byte(rtlpriv, TCR);
4162306a36Sopenharmony_ci		if (cpustatus & IMEM_RDY) {
4262306a36Sopenharmony_ci			rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
4362306a36Sopenharmony_ci				"IMEM Ready after CPU has refilled\n");
4462306a36Sopenharmony_ci			break;
4562306a36Sopenharmony_ci		}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		udelay(100);
4862306a36Sopenharmony_ci	} while (ichecktime--);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (!(cpustatus & IMEM_RDY))
5162306a36Sopenharmony_ci		return false;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return true;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic enum fw_status _rtl92s_firmware_get_nextstatus(
5762306a36Sopenharmony_ci		enum fw_status fw_currentstatus)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	enum fw_status	next_fwstatus = 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	switch (fw_currentstatus) {
6262306a36Sopenharmony_ci	case FW_STATUS_INIT:
6362306a36Sopenharmony_ci		next_fwstatus = FW_STATUS_LOAD_IMEM;
6462306a36Sopenharmony_ci		break;
6562306a36Sopenharmony_ci	case FW_STATUS_LOAD_IMEM:
6662306a36Sopenharmony_ci		next_fwstatus = FW_STATUS_LOAD_EMEM;
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci	case FW_STATUS_LOAD_EMEM:
6962306a36Sopenharmony_ci		next_fwstatus = FW_STATUS_LOAD_DMEM;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	case FW_STATUS_LOAD_DMEM:
7262306a36Sopenharmony_ci		next_fwstatus = FW_STATUS_READY;
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	default:
7562306a36Sopenharmony_ci		break;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return next_fwstatus;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic u8 _rtl92s_firmware_header_map_rftype(struct ieee80211_hw *hw)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
8462306a36Sopenharmony_ci	struct rtl_phy *rtlphy = &(rtlpriv->phy);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	switch (rtlphy->rf_type) {
8762306a36Sopenharmony_ci	case RF_1T1R:
8862306a36Sopenharmony_ci		return 0x11;
8962306a36Sopenharmony_ci	case RF_1T2R:
9062306a36Sopenharmony_ci		return 0x12;
9162306a36Sopenharmony_ci	case RF_2T2R:
9262306a36Sopenharmony_ci		return 0x22;
9362306a36Sopenharmony_ci	default:
9462306a36Sopenharmony_ci		pr_err("Unknown RF type(%x)\n", rtlphy->rf_type);
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	return 0x22;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic void _rtl92s_firmwareheader_priveupdate(struct ieee80211_hw *hw,
10162306a36Sopenharmony_ci		struct fw_priv *pfw_priv)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	/* Update RF types for RATR settings. */
10462306a36Sopenharmony_ci	pfw_priv->rf_config = _rtl92s_firmware_header_map_rftype(hw);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic bool _rtl92s_cmd_send_packet(struct ieee80211_hw *hw,
11062306a36Sopenharmony_ci		struct sk_buff *skb, u8 last)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
11362306a36Sopenharmony_ci	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));
11462306a36Sopenharmony_ci	struct rtl8192_tx_ring *ring;
11562306a36Sopenharmony_ci	struct rtl_tx_desc *pdesc;
11662306a36Sopenharmony_ci	unsigned long flags;
11762306a36Sopenharmony_ci	u8 idx = 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ring = &rtlpci->tx_ring[TXCMD_QUEUE];
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	idx = (ring->idx + skb_queue_len(&ring->queue)) % ring->entries;
12462306a36Sopenharmony_ci	pdesc = &ring->desc[idx];
12562306a36Sopenharmony_ci	rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *)pdesc, 1, 1, skb);
12662306a36Sopenharmony_ci	__skb_queue_tail(&ring->queue, skb);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	return true;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic bool _rtl92s_firmware_downloadcode(struct ieee80211_hw *hw,
13462306a36Sopenharmony_ci		u8 *code_virtual_address, u32 buffer_len)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
13762306a36Sopenharmony_ci	struct sk_buff *skb;
13862306a36Sopenharmony_ci	struct rtl_tcb_desc *tcb_desc;
13962306a36Sopenharmony_ci	u16 frag_threshold = MAX_FIRMWARE_CODE_SIZE;
14062306a36Sopenharmony_ci	u16 frag_length, frag_offset = 0;
14162306a36Sopenharmony_ci	u16 extra_descoffset = 0;
14262306a36Sopenharmony_ci	u8 last_inipkt = 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	_rtl92s_fw_set_rqpn(hw);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (buffer_len >= MAX_FIRMWARE_CODE_SIZE) {
14762306a36Sopenharmony_ci		pr_err("Size over FIRMWARE_CODE_SIZE!\n");
14862306a36Sopenharmony_ci		return false;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	extra_descoffset = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	do {
15462306a36Sopenharmony_ci		if ((buffer_len - frag_offset) > frag_threshold) {
15562306a36Sopenharmony_ci			frag_length = frag_threshold + extra_descoffset;
15662306a36Sopenharmony_ci		} else {
15762306a36Sopenharmony_ci			frag_length = (u16)(buffer_len - frag_offset +
15862306a36Sopenharmony_ci					    extra_descoffset);
15962306a36Sopenharmony_ci			last_inipkt = 1;
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		/* Allocate skb buffer to contain firmware */
16362306a36Sopenharmony_ci		/* info and tx descriptor info. */
16462306a36Sopenharmony_ci		skb = dev_alloc_skb(frag_length);
16562306a36Sopenharmony_ci		if (!skb)
16662306a36Sopenharmony_ci			return false;
16762306a36Sopenharmony_ci		skb_reserve(skb, extra_descoffset);
16862306a36Sopenharmony_ci		skb_put_data(skb, code_virtual_address + frag_offset,
16962306a36Sopenharmony_ci			     (u32)(frag_length - extra_descoffset));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		tcb_desc = (struct rtl_tcb_desc *)(skb->cb);
17262306a36Sopenharmony_ci		tcb_desc->queue_index = TXCMD_QUEUE;
17362306a36Sopenharmony_ci		tcb_desc->cmd_or_init = DESC_PACKET_TYPE_INIT;
17462306a36Sopenharmony_ci		tcb_desc->last_inipkt = last_inipkt;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		_rtl92s_cmd_send_packet(hw, skb, last_inipkt);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		frag_offset += (frag_length - extra_descoffset);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	} while (frag_offset < buffer_len);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	rtl_write_byte(rtlpriv, TP_POLL, TPPOLL_CQ);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	return true ;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic bool _rtl92s_firmware_checkready(struct ieee80211_hw *hw,
18862306a36Sopenharmony_ci		u8 loadfw_status)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
19162306a36Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
19262306a36Sopenharmony_ci	struct rt_firmware *firmware = (struct rt_firmware *)rtlhal->pfirmware;
19362306a36Sopenharmony_ci	u32 tmpu4b;
19462306a36Sopenharmony_ci	u8 cpustatus = 0;
19562306a36Sopenharmony_ci	short pollingcnt = 1000;
19662306a36Sopenharmony_ci	bool rtstatus = true;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
19962306a36Sopenharmony_ci		"LoadStaus(%d)\n", loadfw_status);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	firmware->fwstatus = (enum fw_status)loadfw_status;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	switch (loadfw_status) {
20462306a36Sopenharmony_ci	case FW_STATUS_LOAD_IMEM:
20562306a36Sopenharmony_ci		/* Polling IMEM code done. */
20662306a36Sopenharmony_ci		do {
20762306a36Sopenharmony_ci			cpustatus = rtl_read_byte(rtlpriv, TCR);
20862306a36Sopenharmony_ci			if (cpustatus & IMEM_CODE_DONE)
20962306a36Sopenharmony_ci				break;
21062306a36Sopenharmony_ci			udelay(5);
21162306a36Sopenharmony_ci		} while (pollingcnt--);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		if (!(cpustatus & IMEM_CHK_RPT) || (pollingcnt <= 0)) {
21462306a36Sopenharmony_ci			pr_err("FW_STATUS_LOAD_IMEM FAIL CPU, Status=%x\n",
21562306a36Sopenharmony_ci			       cpustatus);
21662306a36Sopenharmony_ci			goto status_check_fail;
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci		break;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	case FW_STATUS_LOAD_EMEM:
22162306a36Sopenharmony_ci		/* Check Put Code OK and Turn On CPU */
22262306a36Sopenharmony_ci		/* Polling EMEM code done. */
22362306a36Sopenharmony_ci		do {
22462306a36Sopenharmony_ci			cpustatus = rtl_read_byte(rtlpriv, TCR);
22562306a36Sopenharmony_ci			if (cpustatus & EMEM_CODE_DONE)
22662306a36Sopenharmony_ci				break;
22762306a36Sopenharmony_ci			udelay(5);
22862306a36Sopenharmony_ci		} while (pollingcnt--);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci		if (!(cpustatus & EMEM_CHK_RPT) || (pollingcnt <= 0)) {
23162306a36Sopenharmony_ci			pr_err("FW_STATUS_LOAD_EMEM FAIL CPU, Status=%x\n",
23262306a36Sopenharmony_ci			       cpustatus);
23362306a36Sopenharmony_ci			goto status_check_fail;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		/* Turn On CPU */
23762306a36Sopenharmony_ci		rtstatus = _rtl92s_firmware_enable_cpu(hw);
23862306a36Sopenharmony_ci		if (!rtstatus) {
23962306a36Sopenharmony_ci			pr_err("Enable CPU fail!\n");
24062306a36Sopenharmony_ci			goto status_check_fail;
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci		break;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	case FW_STATUS_LOAD_DMEM:
24562306a36Sopenharmony_ci		/* Polling DMEM code done */
24662306a36Sopenharmony_ci		do {
24762306a36Sopenharmony_ci			cpustatus = rtl_read_byte(rtlpriv, TCR);
24862306a36Sopenharmony_ci			if (cpustatus & DMEM_CODE_DONE)
24962306a36Sopenharmony_ci				break;
25062306a36Sopenharmony_ci			udelay(5);
25162306a36Sopenharmony_ci		} while (pollingcnt--);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (!(cpustatus & DMEM_CODE_DONE) || (pollingcnt <= 0)) {
25462306a36Sopenharmony_ci			pr_err("Polling DMEM code done fail ! cpustatus(%#x)\n",
25562306a36Sopenharmony_ci			       cpustatus);
25662306a36Sopenharmony_ci			goto status_check_fail;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
26062306a36Sopenharmony_ci			"DMEM code download success, cpustatus(%#x)\n",
26162306a36Sopenharmony_ci			cpustatus);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci		/* Prevent Delay too much and being scheduled out */
26462306a36Sopenharmony_ci		/* Polling Load Firmware ready */
26562306a36Sopenharmony_ci		pollingcnt = 2000;
26662306a36Sopenharmony_ci		do {
26762306a36Sopenharmony_ci			cpustatus = rtl_read_byte(rtlpriv, TCR);
26862306a36Sopenharmony_ci			if (cpustatus & FWRDY)
26962306a36Sopenharmony_ci				break;
27062306a36Sopenharmony_ci			udelay(40);
27162306a36Sopenharmony_ci		} while (pollingcnt--);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
27462306a36Sopenharmony_ci			"Polling Load Firmware ready, cpustatus(%x)\n",
27562306a36Sopenharmony_ci			cpustatus);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci		if (((cpustatus & LOAD_FW_READY) != LOAD_FW_READY) ||
27862306a36Sopenharmony_ci		    (pollingcnt <= 0)) {
27962306a36Sopenharmony_ci			pr_err("Polling Load Firmware ready fail ! cpustatus(%x)\n",
28062306a36Sopenharmony_ci			       cpustatus);
28162306a36Sopenharmony_ci			goto status_check_fail;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		/* If right here, we can set TCR/RCR to desired value  */
28562306a36Sopenharmony_ci		/* and config MAC lookback mode to normal mode */
28662306a36Sopenharmony_ci		tmpu4b = rtl_read_dword(rtlpriv, TCR);
28762306a36Sopenharmony_ci		rtl_write_dword(rtlpriv, TCR, (tmpu4b & (~TCR_ICV)));
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		tmpu4b = rtl_read_dword(rtlpriv, RCR);
29062306a36Sopenharmony_ci		rtl_write_dword(rtlpriv, RCR, (tmpu4b | RCR_APPFCS |
29162306a36Sopenharmony_ci				RCR_APP_ICV | RCR_APP_MIC));
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
29462306a36Sopenharmony_ci			"Current RCR settings(%#x)\n", tmpu4b);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci		/* Set to normal mode. */
29762306a36Sopenharmony_ci		rtl_write_byte(rtlpriv, LBKMD_SEL, LBK_NORMAL);
29862306a36Sopenharmony_ci		break;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	default:
30162306a36Sopenharmony_ci		pr_err("Unknown status check!\n");
30262306a36Sopenharmony_ci		rtstatus = false;
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatus_check_fail:
30762306a36Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
30862306a36Sopenharmony_ci		"loadfw_status(%d), rtstatus(%x)\n",
30962306a36Sopenharmony_ci		loadfw_status, rtstatus);
31062306a36Sopenharmony_ci	return rtstatus;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ciint rtl92s_download_fw(struct ieee80211_hw *hw)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
31662306a36Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
31762306a36Sopenharmony_ci	struct rt_firmware *firmware = NULL;
31862306a36Sopenharmony_ci	struct fw_hdr *pfwheader;
31962306a36Sopenharmony_ci	struct fw_priv *pfw_priv = NULL;
32062306a36Sopenharmony_ci	u8 *puc_mappedfile = NULL;
32162306a36Sopenharmony_ci	u32 ul_filelength = 0;
32262306a36Sopenharmony_ci	u8 fwhdr_size = RT_8192S_FIRMWARE_HDR_SIZE;
32362306a36Sopenharmony_ci	u8 fwstatus = FW_STATUS_INIT;
32462306a36Sopenharmony_ci	bool rtstatus = true;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (rtlpriv->max_fw_size == 0 || !rtlhal->pfirmware)
32762306a36Sopenharmony_ci		return 1;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	firmware = (struct rt_firmware *)rtlhal->pfirmware;
33062306a36Sopenharmony_ci	firmware->fwstatus = FW_STATUS_INIT;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	puc_mappedfile = firmware->sz_fw_tmpbuffer;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	/* 1. Retrieve FW header. */
33562306a36Sopenharmony_ci	firmware->pfwheader = (struct fw_hdr *) puc_mappedfile;
33662306a36Sopenharmony_ci	pfwheader = firmware->pfwheader;
33762306a36Sopenharmony_ci	firmware->firmwareversion =  byte(pfwheader->version, 0);
33862306a36Sopenharmony_ci	firmware->pfwheader->fwpriv.hci_sel = 1;/* pcie */
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD,
34162306a36Sopenharmony_ci		"signature:%x, version:%x, size:%x, imemsize:%x, sram size:%x\n",
34262306a36Sopenharmony_ci		pfwheader->signature,
34362306a36Sopenharmony_ci		pfwheader->version, pfwheader->dmem_size,
34462306a36Sopenharmony_ci		pfwheader->img_imem_size, pfwheader->img_sram_size);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* 2. Retrieve IMEM image. */
34762306a36Sopenharmony_ci	if ((pfwheader->img_imem_size == 0) || (pfwheader->img_imem_size >
34862306a36Sopenharmony_ci	    sizeof(firmware->fw_imem))) {
34962306a36Sopenharmony_ci		pr_err("memory for data image is less than IMEM required\n");
35062306a36Sopenharmony_ci		goto fail;
35162306a36Sopenharmony_ci	} else {
35262306a36Sopenharmony_ci		puc_mappedfile += fwhdr_size;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		memcpy(firmware->fw_imem, puc_mappedfile,
35562306a36Sopenharmony_ci		       pfwheader->img_imem_size);
35662306a36Sopenharmony_ci		firmware->fw_imem_len = pfwheader->img_imem_size;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* 3. Retriecve EMEM image. */
36062306a36Sopenharmony_ci	if (pfwheader->img_sram_size > sizeof(firmware->fw_emem)) {
36162306a36Sopenharmony_ci		pr_err("memory for data image is less than EMEM required\n");
36262306a36Sopenharmony_ci		goto fail;
36362306a36Sopenharmony_ci	} else {
36462306a36Sopenharmony_ci		puc_mappedfile += firmware->fw_imem_len;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		memcpy(firmware->fw_emem, puc_mappedfile,
36762306a36Sopenharmony_ci		       pfwheader->img_sram_size);
36862306a36Sopenharmony_ci		firmware->fw_emem_len = pfwheader->img_sram_size;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/* 4. download fw now */
37262306a36Sopenharmony_ci	fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus);
37362306a36Sopenharmony_ci	while (fwstatus != FW_STATUS_READY) {
37462306a36Sopenharmony_ci		/* Image buffer redirection. */
37562306a36Sopenharmony_ci		switch (fwstatus) {
37662306a36Sopenharmony_ci		case FW_STATUS_LOAD_IMEM:
37762306a36Sopenharmony_ci			puc_mappedfile = firmware->fw_imem;
37862306a36Sopenharmony_ci			ul_filelength = firmware->fw_imem_len;
37962306a36Sopenharmony_ci			break;
38062306a36Sopenharmony_ci		case FW_STATUS_LOAD_EMEM:
38162306a36Sopenharmony_ci			puc_mappedfile = firmware->fw_emem;
38262306a36Sopenharmony_ci			ul_filelength = firmware->fw_emem_len;
38362306a36Sopenharmony_ci			break;
38462306a36Sopenharmony_ci		case FW_STATUS_LOAD_DMEM:
38562306a36Sopenharmony_ci			/* Partial update the content of header private. */
38662306a36Sopenharmony_ci			pfwheader = firmware->pfwheader;
38762306a36Sopenharmony_ci			pfw_priv = &pfwheader->fwpriv;
38862306a36Sopenharmony_ci			_rtl92s_firmwareheader_priveupdate(hw, pfw_priv);
38962306a36Sopenharmony_ci			puc_mappedfile = (u8 *)(firmware->pfwheader) +
39062306a36Sopenharmony_ci					RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE;
39162306a36Sopenharmony_ci			ul_filelength = fwhdr_size -
39262306a36Sopenharmony_ci					RT_8192S_FIRMWARE_HDR_EXCLUDE_PRI_SIZE;
39362306a36Sopenharmony_ci			break;
39462306a36Sopenharmony_ci		default:
39562306a36Sopenharmony_ci			pr_err("Unexpected Download step!!\n");
39662306a36Sopenharmony_ci			goto fail;
39762306a36Sopenharmony_ci		}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		/* <2> Download image file */
40062306a36Sopenharmony_ci		rtstatus = _rtl92s_firmware_downloadcode(hw, puc_mappedfile,
40162306a36Sopenharmony_ci				ul_filelength);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci		if (!rtstatus) {
40462306a36Sopenharmony_ci			pr_err("fail!\n");
40562306a36Sopenharmony_ci			goto fail;
40662306a36Sopenharmony_ci		}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		/* <3> Check whether load FW process is ready */
40962306a36Sopenharmony_ci		rtstatus = _rtl92s_firmware_checkready(hw, fwstatus);
41062306a36Sopenharmony_ci		if (!rtstatus) {
41162306a36Sopenharmony_ci			pr_err("rtl8192se: firmware fail!\n");
41262306a36Sopenharmony_ci			goto fail;
41362306a36Sopenharmony_ci		}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		fwstatus = _rtl92s_firmware_get_nextstatus(firmware->fwstatus);
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return rtstatus;
41962306a36Sopenharmony_cifail:
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic u32 _rtl92s_fill_h2c_cmd(struct sk_buff *skb, u32 h2cbufferlen,
42462306a36Sopenharmony_ci				u32 cmd_num, u32 *pelement_id, u32 *pcmd_len,
42562306a36Sopenharmony_ci				u8 **pcmb_buffer, u8 *cmd_start_seq)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	u32 totallen = 0, len = 0, tx_desclen = 0;
42862306a36Sopenharmony_ci	u32 pre_continueoffset = 0;
42962306a36Sopenharmony_ci	u8 *ph2c_buffer;
43062306a36Sopenharmony_ci	u8 i = 0;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	do {
43362306a36Sopenharmony_ci		/* 8 - Byte alignment */
43462306a36Sopenharmony_ci		len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci		/* Buffer length is not enough */
43762306a36Sopenharmony_ci		if (h2cbufferlen < totallen + len + tx_desclen)
43862306a36Sopenharmony_ci			break;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		/* Clear content */
44162306a36Sopenharmony_ci		ph2c_buffer = skb_put(skb, (u32)len);
44262306a36Sopenharmony_ci		memset((ph2c_buffer + totallen + tx_desclen), 0, len);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci		/* CMD len */
44562306a36Sopenharmony_ci		le32p_replace_bits((__le32 *)(ph2c_buffer + totallen +
44662306a36Sopenharmony_ci					      tx_desclen), pcmd_len[i],
44762306a36Sopenharmony_ci				   GENMASK(15, 0));
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		/* CMD ID */
45062306a36Sopenharmony_ci		le32p_replace_bits((__le32 *)(ph2c_buffer + totallen +
45162306a36Sopenharmony_ci					      tx_desclen), pelement_id[i],
45262306a36Sopenharmony_ci				   GENMASK(23, 16));
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		/* CMD Sequence */
45562306a36Sopenharmony_ci		*cmd_start_seq = *cmd_start_seq % 0x80;
45662306a36Sopenharmony_ci		le32p_replace_bits((__le32 *)(ph2c_buffer + totallen +
45762306a36Sopenharmony_ci					      tx_desclen), *cmd_start_seq,
45862306a36Sopenharmony_ci				   GENMASK(30, 24));
45962306a36Sopenharmony_ci		++*cmd_start_seq;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* Copy memory */
46262306a36Sopenharmony_ci		memcpy((ph2c_buffer + totallen + tx_desclen +
46362306a36Sopenharmony_ci			H2C_TX_CMD_HDR_LEN), pcmb_buffer[i], pcmd_len[i]);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/* CMD continue */
46662306a36Sopenharmony_ci		/* set the continue in prevoius cmd. */
46762306a36Sopenharmony_ci		if (i < cmd_num - 1)
46862306a36Sopenharmony_ci			le32p_replace_bits((__le32 *)(ph2c_buffer +
46962306a36Sopenharmony_ci						      pre_continueoffset),
47062306a36Sopenharmony_ci					   1, BIT(31));
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		pre_continueoffset = totallen;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		totallen += len;
47562306a36Sopenharmony_ci	} while (++i < cmd_num);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return totallen;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic u32 _rtl92s_get_h2c_cmdlen(u32 h2cbufferlen, u32 cmd_num, u32 *pcmd_len)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	u32 totallen = 0, len = 0, tx_desclen = 0;
48362306a36Sopenharmony_ci	u8 i = 0;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	do {
48662306a36Sopenharmony_ci		/* 8 - Byte alignment */
48762306a36Sopenharmony_ci		len = H2C_TX_CMD_HDR_LEN + N_BYTE_ALIGMENT(pcmd_len[i], 8);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		/* Buffer length is not enough */
49062306a36Sopenharmony_ci		if (h2cbufferlen < totallen + len + tx_desclen)
49162306a36Sopenharmony_ci			break;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		totallen += len;
49462306a36Sopenharmony_ci	} while (++i < cmd_num);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return totallen + tx_desclen;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic bool _rtl92s_firmware_set_h2c_cmd(struct ieee80211_hw *hw, u8 h2c_cmd,
50062306a36Sopenharmony_ci					 u8 *pcmd_buffer)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	struct rtl_priv *rtlpriv = rtl_priv(hw);
50362306a36Sopenharmony_ci	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
50462306a36Sopenharmony_ci	struct rtl_tcb_desc *cb_desc;
50562306a36Sopenharmony_ci	struct sk_buff *skb;
50662306a36Sopenharmony_ci	u32	element_id = 0;
50762306a36Sopenharmony_ci	u32	cmd_len = 0;
50862306a36Sopenharmony_ci	u32	len;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	switch (h2c_cmd) {
51162306a36Sopenharmony_ci	case FW_H2C_SETPWRMODE:
51262306a36Sopenharmony_ci		element_id = H2C_SETPWRMODE_CMD ;
51362306a36Sopenharmony_ci		cmd_len = sizeof(struct h2c_set_pwrmode_parm);
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci	case FW_H2C_JOINBSSRPT:
51662306a36Sopenharmony_ci		element_id = H2C_JOINBSSRPT_CMD;
51762306a36Sopenharmony_ci		cmd_len = sizeof(struct h2c_joinbss_rpt_parm);
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	case FW_H2C_WOWLAN_UPDATE_GTK:
52062306a36Sopenharmony_ci		element_id = H2C_WOWLAN_UPDATE_GTK_CMD;
52162306a36Sopenharmony_ci		cmd_len = sizeof(struct h2c_wpa_two_way_parm);
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	case FW_H2C_WOWLAN_UPDATE_IV:
52462306a36Sopenharmony_ci		element_id = H2C_WOWLAN_UPDATE_IV_CMD;
52562306a36Sopenharmony_ci		cmd_len = sizeof(unsigned long long);
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	case FW_H2C_WOWLAN_OFFLOAD:
52862306a36Sopenharmony_ci		element_id = H2C_WOWLAN_FW_OFFLOAD;
52962306a36Sopenharmony_ci		cmd_len = sizeof(u8);
53062306a36Sopenharmony_ci		break;
53162306a36Sopenharmony_ci	default:
53262306a36Sopenharmony_ci		break;
53362306a36Sopenharmony_ci	}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	len = _rtl92s_get_h2c_cmdlen(MAX_TRANSMIT_BUFFER_SIZE, 1, &cmd_len);
53662306a36Sopenharmony_ci	skb = dev_alloc_skb(len);
53762306a36Sopenharmony_ci	if (!skb)
53862306a36Sopenharmony_ci		return false;
53962306a36Sopenharmony_ci	cb_desc = (struct rtl_tcb_desc *)(skb->cb);
54062306a36Sopenharmony_ci	cb_desc->queue_index = TXCMD_QUEUE;
54162306a36Sopenharmony_ci	cb_desc->cmd_or_init = DESC_PACKET_TYPE_NORMAL;
54262306a36Sopenharmony_ci	cb_desc->last_inipkt = false;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	_rtl92s_fill_h2c_cmd(skb, MAX_TRANSMIT_BUFFER_SIZE, 1, &element_id,
54562306a36Sopenharmony_ci			&cmd_len, &pcmd_buffer,	&rtlhal->h2c_txcmd_seq);
54662306a36Sopenharmony_ci	_rtl92s_cmd_send_packet(hw, skb, false);
54762306a36Sopenharmony_ci	rtlpriv->cfg->ops->tx_polling(hw, TXCMD_QUEUE);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return true;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_civoid rtl92s_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode)
55362306a36Sopenharmony_ci{
55462306a36Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
55562306a36Sopenharmony_ci	struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw));
55662306a36Sopenharmony_ci	struct h2c_set_pwrmode_parm	pwrmode;
55762306a36Sopenharmony_ci	u16 max_wakeup_period = 0;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	pwrmode.mode = mode;
56062306a36Sopenharmony_ci	pwrmode.flag_low_traffic_en = 0;
56162306a36Sopenharmony_ci	pwrmode.flag_lpnav_en = 0;
56262306a36Sopenharmony_ci	pwrmode.flag_rf_low_snr_en = 0;
56362306a36Sopenharmony_ci	pwrmode.flag_dps_en = 0;
56462306a36Sopenharmony_ci	pwrmode.bcn_rx_en = 0;
56562306a36Sopenharmony_ci	pwrmode.bcn_to = 0;
56662306a36Sopenharmony_ci	le16p_replace_bits((__le16 *)(((u8 *)(&pwrmode) + 8)),
56762306a36Sopenharmony_ci			   mac->vif->bss_conf.beacon_int, GENMASK(15, 0));
56862306a36Sopenharmony_ci	pwrmode.app_itv = 0;
56962306a36Sopenharmony_ci	pwrmode.awake_bcn_itvl = ppsc->reg_max_lps_awakeintvl;
57062306a36Sopenharmony_ci	pwrmode.smart_ps = 1;
57162306a36Sopenharmony_ci	pwrmode.bcn_pass_period = 10;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* Set beacon pass count */
57462306a36Sopenharmony_ci	if (pwrmode.mode == FW_PS_MIN_MODE)
57562306a36Sopenharmony_ci		max_wakeup_period = mac->vif->bss_conf.beacon_int;
57662306a36Sopenharmony_ci	else if (pwrmode.mode == FW_PS_MAX_MODE)
57762306a36Sopenharmony_ci		max_wakeup_period = mac->vif->bss_conf.beacon_int *
57862306a36Sopenharmony_ci			mac->vif->bss_conf.dtim_period;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (max_wakeup_period >= 500)
58162306a36Sopenharmony_ci		pwrmode.bcn_pass_cnt = 1;
58262306a36Sopenharmony_ci	else if ((max_wakeup_period >= 300) && (max_wakeup_period < 500))
58362306a36Sopenharmony_ci		pwrmode.bcn_pass_cnt = 2;
58462306a36Sopenharmony_ci	else if ((max_wakeup_period >= 200) && (max_wakeup_period < 300))
58562306a36Sopenharmony_ci		pwrmode.bcn_pass_cnt = 3;
58662306a36Sopenharmony_ci	else if ((max_wakeup_period >= 20) && (max_wakeup_period < 200))
58762306a36Sopenharmony_ci		pwrmode.bcn_pass_cnt = 5;
58862306a36Sopenharmony_ci	else
58962306a36Sopenharmony_ci		pwrmode.bcn_pass_cnt = 1;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	_rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_SETPWRMODE, (u8 *)&pwrmode);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_civoid rtl92s_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw,
59662306a36Sopenharmony_ci		u8 mstatus, u8 ps_qosinfo)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct rtl_mac *mac = rtl_mac(rtl_priv(hw));
59962306a36Sopenharmony_ci	struct h2c_joinbss_rpt_parm joinbss_rpt;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	joinbss_rpt.opmode = mstatus;
60262306a36Sopenharmony_ci	joinbss_rpt.ps_qos_info = ps_qosinfo;
60362306a36Sopenharmony_ci	joinbss_rpt.bssid[0] = mac->bssid[0];
60462306a36Sopenharmony_ci	joinbss_rpt.bssid[1] = mac->bssid[1];
60562306a36Sopenharmony_ci	joinbss_rpt.bssid[2] = mac->bssid[2];
60662306a36Sopenharmony_ci	joinbss_rpt.bssid[3] = mac->bssid[3];
60762306a36Sopenharmony_ci	joinbss_rpt.bssid[4] = mac->bssid[4];
60862306a36Sopenharmony_ci	joinbss_rpt.bssid[5] = mac->bssid[5];
60962306a36Sopenharmony_ci	le16p_replace_bits((__le16 *)(((u8 *)(&joinbss_rpt) + 8)),
61062306a36Sopenharmony_ci			   mac->vif->bss_conf.beacon_int, GENMASK(15, 0));
61162306a36Sopenharmony_ci	le16p_replace_bits((__le16 *)(((u8 *)(&joinbss_rpt) + 10)),
61262306a36Sopenharmony_ci			   mac->assoc_id, GENMASK(15, 0));
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	_rtl92s_firmware_set_h2c_cmd(hw, FW_H2C_JOINBSSRPT, (u8 *)&joinbss_rpt);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
617