Commit 15d2fcc6 authored by Ping-Ke Shih's avatar Ping-Ke Shih Committed by Kalle Valo
Browse files

rtw88: add legacy firmware download for 8723D devices



The WLAN CPU of 8723D device is different from others, add legacy
firmware download function for it. A new variable wlan_cpu is used to
decide which firmware download function we should use.

Legacy firmware file contains 32 bytes header including version and
subversion. When downloading to wlan cpu, header is excluded.

Firmware is downloaded via beacon queue to reserved page that is a part of
TX buffer. Since 11N WLAN CPU uses different control registers, this patch
introduces related control registers.

Signed-off-by: default avatarPing-Ke Shih <pkshih@realtek.com>
Signed-off-by: default avatarYan-Hsuan Chuang <yhchuang@realtek.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20200422034607.28747-2-yhchuang@realtek.com
parent 2b7aadd3
Loading
Loading
Loading
Loading
+18 −3
Original line number Diff line number Diff line
@@ -1079,6 +1079,8 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
	u8 bckp[2];
	u8 val;
	u16 rsvd_pg_head;
	u32 bcn_valid_addr;
	u32 bcn_valid_mask;
	int ret;

	lockdep_assert_held(&rtwdev->mutex);
@@ -1086,8 +1088,13 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
	if (!size)
		return -EINVAL;

	if (rtw_chip_wcpu_11n(rtwdev)) {
		rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID);
	} else {
		pg_addr &= BIT_MASK_BCN_HEAD_1_V1;
	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr | BIT_BCN_VALID_V1);
		pg_addr |= BIT_BCN_VALID_V1;
		rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr);
	}

	val = rtw_read8(rtwdev, REG_CR + 1);
	bckp[0] = val;
@@ -1105,7 +1112,15 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
		goto restore;
	}

	if (!check_hw_ready(rtwdev, REG_FIFOPAGE_CTRL_2, BIT_BCN_VALID_V1, 1)) {
	if (rtw_chip_wcpu_11n(rtwdev)) {
		bcn_valid_addr = REG_DWBCN0_CTRL;
		bcn_valid_mask = BIT_BCN_VALID;
	} else {
		bcn_valid_addr = REG_FIFOPAGE_CTRL_2;
		bcn_valid_mask = BIT_BCN_VALID_V1;
	}

	if (!check_hw_ready(rtwdev, bcn_valid_addr, bcn_valid_mask, 1)) {
		rtw_err(rtwdev, "error beacon valid\n");
		ret = -EBUSY;
	}
+25 −0
Original line number Diff line number Diff line
@@ -19,6 +19,12 @@
#define RSVD_PAGE_START_ADDR		0x780
#define FIFO_DUMP_ADDR			0x8000

#define DLFW_PAGE_SIZE_SHIFT_LEGACY	12
#define DLFW_PAGE_SIZE_LEGACY		0x1000
#define DLFW_BLK_SIZE_SHIFT_LEGACY	2
#define DLFW_BLK_SIZE_LEGACY		4
#define FW_START_ADDR_LEGACY		0x1000

enum rtw_c2h_cmd_id {
	C2H_BT_INFO = 0x09,
	C2H_BT_MP_INFO = 0x0b,
@@ -192,6 +198,25 @@ struct rtw_fw_hdr {
	__le32 imem_addr;
} __packed;

struct rtw_fw_hdr_legacy {
	__le16 signature;
	u8 category;
	u8 function;
	__le16 version;	/* 0x04 */
	u8 subversion1;
	u8 subversion2;
	u8 month;	/* 0x08 */
	u8 day;
	u8 hour;
	u8 minute;
	__le16 size;
	__le16 rsvd2;
	__le32 idx;	/* 0x10 */
	__le32 rsvd3;
	__le32 rsvd4;	/* 0x18 */
	__le32 rsvd5;
} __packed;

/* C2H */
#define GET_CCX_REPORT_SEQNUM(c2h_payload)	(c2h_payload[8] & 0xfc)
#define GET_CCX_REPORT_STATUS(c2h_payload)	(c2h_payload[9] & 0xc0)
+145 −1
Original line number Diff line number Diff line
@@ -650,7 +650,7 @@ static void download_firmware_end_flow(struct rtw_dev *rtwdev)
	rtw_write16(rtwdev, REG_MCUFW_CTRL, fw_ctrl);
}

int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
int __rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
{
	struct rtw_backup_info bckp[DLFW_RESTORE_REG_NUM];
	const u8 *data = fw->firmware->data;
@@ -704,6 +704,150 @@ dlfw_fail:
	return ret;
}

static void en_download_firmware_legacy(struct rtw_dev *rtwdev, bool en)
{
	int try;

	if (en) {
		wlan_cpu_enable(rtwdev, false);
		wlan_cpu_enable(rtwdev, true);

		rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);

		for (try = 0; try < 10; try++) {
			if (rtw_read8(rtwdev, REG_MCUFW_CTRL) & BIT_MCUFWDL_EN)
				goto fwdl_ready;
			rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
			msleep(20);
		}
		rtw_err(rtwdev, "failed to check fw download ready\n");
fwdl_ready:
		rtw_write32_clr(rtwdev, REG_MCUFW_CTRL, BIT_ROM_DLEN);
	} else {
		rtw_write8_clr(rtwdev, REG_MCUFW_CTRL, BIT_MCUFWDL_EN);
	}
}

static void
write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size)
{
	u32 val32;
	u32 block_nr;
	u32 remain_size;
	u32 write_addr = FW_START_ADDR_LEGACY;
	const __le32 *ptr = (const __le32 *)data;
	u32 block;
	__le32 remain_data = 0;

	block_nr = size >> DLFW_BLK_SIZE_SHIFT_LEGACY;
	remain_size = size & (DLFW_BLK_SIZE_LEGACY - 1);

	val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
	val32 &= ~BIT_ROM_PGE;
	val32 |= (page << BIT_SHIFT_ROM_PGE) & BIT_ROM_PGE;
	rtw_write32(rtwdev, REG_MCUFW_CTRL, val32);

	for (block = 0; block < block_nr; block++) {
		rtw_write32(rtwdev, write_addr, le32_to_cpu(*ptr));

		write_addr += DLFW_BLK_SIZE_LEGACY;
		ptr++;
	}

	if (remain_size) {
		memcpy(&remain_data, ptr, remain_size);
		rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data));
	}
}

static int
download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size)
{
	u32 page;
	u32 total_page;
	u32 last_page_size;

	data += sizeof(struct rtw_fw_hdr_legacy);
	size -= sizeof(struct rtw_fw_hdr_legacy);

	total_page = size >> DLFW_PAGE_SIZE_SHIFT_LEGACY;
	last_page_size = size & (DLFW_PAGE_SIZE_LEGACY - 1);

	rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT);

	for (page = 0; page < total_page; page++) {
		write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY);
		data += DLFW_PAGE_SIZE_LEGACY;
	}
	if (last_page_size)
		write_firmware_page(rtwdev, page, data, last_page_size);

	if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) {
		rtw_err(rtwdev, "failed to check download fimrware report\n");
		return -EINVAL;
	}

	return 0;
}

static int download_firmware_validate_legacy(struct rtw_dev *rtwdev)
{
	u32 val32;
	int try;

	val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
	val32 |= BIT_MCUFWDL_RDY;
	val32 &= ~BIT_WINTINI_RDY;
	rtw_write32(rtwdev, REG_MCUFW_CTRL, val32);

	wlan_cpu_enable(rtwdev, false);
	wlan_cpu_enable(rtwdev, true);

	for (try = 0; try < 10; try++) {
		val32 = rtw_read32(rtwdev, REG_MCUFW_CTRL);
		if ((val32 & FW_READY_LEGACY) == FW_READY_LEGACY)
			return 0;
		msleep(20);
	}

	rtw_err(rtwdev, "failed to validate fimrware\n");
	return -EINVAL;
}

int __rtw_download_firmware_legacy(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
{
	int ret = 0;

	en_download_firmware_legacy(rtwdev, true);
	ret = download_firmware_legacy(rtwdev, fw->firmware->data, fw->firmware->size);
	en_download_firmware_legacy(rtwdev, false);
	if (ret)
		goto out;

	ret = download_firmware_validate_legacy(rtwdev);
	if (ret)
		goto out;

	/* reset desc and index */
	rtw_hci_setup(rtwdev);

	rtwdev->h2c.last_box_num = 0;
	rtwdev->h2c.seq = 0;

	set_bit(RTW_FLAG_FW_RUNNING, rtwdev->flags);

out:
	return ret;
}

int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw)
{
	if (rtw_chip_wcpu_11n(rtwdev))
		return __rtw_download_firmware_legacy(rtwdev, fw);

	return __rtw_download_firmware(rtwdev, fw);
}

static u32 get_priority_queues(struct rtw_dev *rtwdev, u32 queues)
{
	const struct rtw_rqpn *rqpn = rtwdev->fifo.rqpn;
+34 −7
Original line number Diff line number Diff line
@@ -1042,11 +1042,43 @@ static void rtw_unset_supported_band(struct ieee80211_hw *hw,
	kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]);
}

static void __update_firmware_info(struct rtw_dev *rtwdev,
				   struct rtw_fw_state *fw)
{
	const struct rtw_fw_hdr *fw_hdr =
				(const struct rtw_fw_hdr *)fw->firmware->data;

	fw->h2c_version = le16_to_cpu(fw_hdr->h2c_fmt_ver);
	fw->version = le16_to_cpu(fw_hdr->version);
	fw->sub_version = fw_hdr->subversion;
	fw->sub_index = fw_hdr->subindex;
}

static void __update_firmware_info_legacy(struct rtw_dev *rtwdev,
					  struct rtw_fw_state *fw)
{
	struct rtw_fw_hdr_legacy *legacy =
				(struct rtw_fw_hdr_legacy *)fw->firmware->data;

	fw->h2c_version = 0;
	fw->version = le16_to_cpu(legacy->version);
	fw->sub_version = legacy->subversion1;
	fw->sub_index = legacy->subversion2;
}

static void update_firmware_info(struct rtw_dev *rtwdev,
				 struct rtw_fw_state *fw)
{
	if (rtw_chip_wcpu_11n(rtwdev))
		__update_firmware_info_legacy(rtwdev, fw);
	else
		__update_firmware_info(rtwdev, fw);
}

static void rtw_load_firmware_cb(const struct firmware *firmware, void *context)
{
	struct rtw_fw_state *fw = context;
	struct rtw_dev *rtwdev = fw->rtwdev;
	const struct rtw_fw_hdr *fw_hdr;

	if (!firmware || !firmware->data) {
		rtw_err(rtwdev, "failed to request firmware\n");
@@ -1054,13 +1086,8 @@ static void rtw_load_firmware_cb(const struct firmware *firmware, void *context)
		return;
	}

	fw_hdr = (const struct rtw_fw_hdr *)firmware->data;
	fw->h2c_version = le16_to_cpu(fw_hdr->h2c_fmt_ver);
	fw->version = le16_to_cpu(fw_hdr->version);
	fw->sub_version = fw_hdr->subversion;
	fw->sub_index = fw_hdr->subindex;

	fw->firmware = firmware;
	update_firmware_info(rtwdev, fw);
	complete_all(&fw->completion);

	rtw_info(rtwdev, "Firmware version %u.%u.%u, H2C version %u\n",
+16 −0
Original line number Diff line number Diff line
@@ -1056,12 +1056,18 @@ struct rtw_pwr_track_tbl {
	const u8 *pwrtrk_2g_ccka_p;
};

enum rtw_wlan_cpu {
	RTW_WCPU_11AC,
	RTW_WCPU_11N,
};

/* hardware configuration for each IC */
struct rtw_chip_info {
	struct rtw_chip_ops *ops;
	u8 id;

	const char *fw_name;
	enum rtw_wlan_cpu wlan_cpu;
	u8 tx_pkt_desc_sz;
	u8 tx_buf_desc_sz;
	u8 rx_pkt_desc_sz;
@@ -1725,6 +1731,16 @@ static inline void rtw_chip_efuse_grant_off(struct rtw_dev *rtwdev)
		rtwdev->chip->ops->efuse_grant(rtwdev, false);
}

static inline bool rtw_chip_wcpu_11n(struct rtw_dev *rtwdev)
{
	return rtwdev->chip->wlan_cpu == RTW_WCPU_11N;
}

static inline bool rtw_chip_wcpu_11ac(struct rtw_dev *rtwdev)
{
	return rtwdev->chip->wlan_cpu == RTW_WCPU_11AC;
}

void rtw_get_channel_params(struct cfg80211_chan_def *chandef,
			    struct rtw_channel_params *ch_param);
bool check_hw_ready(struct rtw_dev *rtwdev, u32 addr, u32 mask, u32 target);
Loading