18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
28c2ecf20Sopenharmony_ci/* Copyright(c) 2018-2019  Realtek Corporation
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/iopoll.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "main.h"
88c2ecf20Sopenharmony_ci#include "efuse.h"
98c2ecf20Sopenharmony_ci#include "reg.h"
108c2ecf20Sopenharmony_ci#include "debug.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#define RTW_EFUSE_BANK_WIFI		0x0
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic void switch_efuse_bank(struct rtw_dev *rtwdev)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	rtw_write32_mask(rtwdev, REG_LDO_EFUSE_CTRL, BIT_MASK_EFUSE_BANK_SEL,
178c2ecf20Sopenharmony_ci			 RTW_EFUSE_BANK_WIFI);
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define invalid_efuse_header(hdr1, hdr2) \
218c2ecf20Sopenharmony_ci	((hdr1) == 0xff || (((hdr1) & 0x1f) == 0xf && (hdr2) == 0xff))
228c2ecf20Sopenharmony_ci#define invalid_efuse_content(word_en, i) \
238c2ecf20Sopenharmony_ci	(((word_en) & BIT(i)) != 0x0)
248c2ecf20Sopenharmony_ci#define get_efuse_blk_idx_2_byte(hdr1, hdr2) \
258c2ecf20Sopenharmony_ci	((((hdr2) & 0xf0) >> 1) | (((hdr1) >> 5) & 0x07))
268c2ecf20Sopenharmony_ci#define get_efuse_blk_idx_1_byte(hdr1) \
278c2ecf20Sopenharmony_ci	(((hdr1) & 0xf0) >> 4)
288c2ecf20Sopenharmony_ci#define block_idx_to_logical_idx(blk_idx, i) \
298c2ecf20Sopenharmony_ci	(((blk_idx) << 3) + ((i) << 1))
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* efuse header format
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * | 7        5   4    0 | 7        4   3          0 | 15  8  7   0 |
348c2ecf20Sopenharmony_ci *   block[2:0]   0 1111   block[6:3]   word_en[3:0]   byte0  byte1
358c2ecf20Sopenharmony_ci * | header 1 (optional) |          header 2         |    word N    |
368c2ecf20Sopenharmony_ci *
378c2ecf20Sopenharmony_ci * word_en: 4 bits each word. 0 -> write; 1 -> not write
388c2ecf20Sopenharmony_ci * N: 1~4, depends on word_en
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_cistatic int rtw_dump_logical_efuse_map(struct rtw_dev *rtwdev, u8 *phy_map,
418c2ecf20Sopenharmony_ci				      u8 *log_map)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	u32 physical_size = rtwdev->efuse.physical_size;
448c2ecf20Sopenharmony_ci	u32 protect_size = rtwdev->efuse.protect_size;
458c2ecf20Sopenharmony_ci	u32 logical_size = rtwdev->efuse.logical_size;
468c2ecf20Sopenharmony_ci	u32 phy_idx, log_idx;
478c2ecf20Sopenharmony_ci	u8 hdr1, hdr2;
488c2ecf20Sopenharmony_ci	u8 blk_idx;
498c2ecf20Sopenharmony_ci	u8 word_en;
508c2ecf20Sopenharmony_ci	int i;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	for (phy_idx = 0; phy_idx < physical_size - protect_size;) {
538c2ecf20Sopenharmony_ci		hdr1 = phy_map[phy_idx];
548c2ecf20Sopenharmony_ci		hdr2 = phy_map[phy_idx + 1];
558c2ecf20Sopenharmony_ci		if (invalid_efuse_header(hdr1, hdr2))
568c2ecf20Sopenharmony_ci			break;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		if ((hdr1 & 0x1f) == 0xf) {
598c2ecf20Sopenharmony_ci			/* 2-byte header format */
608c2ecf20Sopenharmony_ci			blk_idx = get_efuse_blk_idx_2_byte(hdr1, hdr2);
618c2ecf20Sopenharmony_ci			word_en = hdr2 & 0xf;
628c2ecf20Sopenharmony_ci			phy_idx += 2;
638c2ecf20Sopenharmony_ci		} else {
648c2ecf20Sopenharmony_ci			/* 1-byte header format */
658c2ecf20Sopenharmony_ci			blk_idx = get_efuse_blk_idx_1_byte(hdr1);
668c2ecf20Sopenharmony_ci			word_en = hdr1 & 0xf;
678c2ecf20Sopenharmony_ci			phy_idx += 1;
688c2ecf20Sopenharmony_ci		}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		for (i = 0; i < 4; i++) {
718c2ecf20Sopenharmony_ci			if (invalid_efuse_content(word_en, i))
728c2ecf20Sopenharmony_ci				continue;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci			log_idx = block_idx_to_logical_idx(blk_idx, i);
758c2ecf20Sopenharmony_ci			if (phy_idx + 1 > physical_size - protect_size ||
768c2ecf20Sopenharmony_ci			    log_idx + 1 > logical_size)
778c2ecf20Sopenharmony_ci				return -EINVAL;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci			log_map[log_idx] = phy_map[phy_idx];
808c2ecf20Sopenharmony_ci			log_map[log_idx + 1] = phy_map[phy_idx + 1];
818c2ecf20Sopenharmony_ci			phy_idx += 2;
828c2ecf20Sopenharmony_ci		}
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int rtw_dump_physical_efuse_map(struct rtw_dev *rtwdev, u8 *map)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	struct rtw_chip_info *chip = rtwdev->chip;
908c2ecf20Sopenharmony_ci	u32 size = rtwdev->efuse.physical_size;
918c2ecf20Sopenharmony_ci	u32 efuse_ctl;
928c2ecf20Sopenharmony_ci	u32 addr;
938c2ecf20Sopenharmony_ci	u32 cnt;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	rtw_chip_efuse_grant_on(rtwdev);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	switch_efuse_bank(rtwdev);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* disable 2.5V LDO */
1008c2ecf20Sopenharmony_ci	chip->ops->cfg_ldo25(rtwdev, false);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	for (addr = 0; addr < size; addr++) {
1058c2ecf20Sopenharmony_ci		efuse_ctl &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR);
1068c2ecf20Sopenharmony_ci		efuse_ctl |= (addr & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR;
1078c2ecf20Sopenharmony_ci		rtw_write32(rtwdev, REG_EFUSE_CTRL, efuse_ctl & (~BIT_EF_FLAG));
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci		cnt = 1000000;
1108c2ecf20Sopenharmony_ci		do {
1118c2ecf20Sopenharmony_ci			udelay(1);
1128c2ecf20Sopenharmony_ci			efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
1138c2ecf20Sopenharmony_ci			if (--cnt == 0)
1148c2ecf20Sopenharmony_ci				return -EBUSY;
1158c2ecf20Sopenharmony_ci		} while (!(efuse_ctl & BIT_EF_FLAG));
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		*(map + addr) = (u8)(efuse_ctl & BIT_MASK_EF_DATA);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	rtw_chip_efuse_grant_off(rtwdev);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ciint rtw_read8_physical_efuse(struct rtw_dev *rtwdev, u16 addr, u8 *data)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	u32 efuse_ctl;
1288c2ecf20Sopenharmony_ci	int ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	rtw_write32_mask(rtwdev, REG_EFUSE_CTRL, 0x3ff00, addr);
1318c2ecf20Sopenharmony_ci	rtw_write32_clr(rtwdev, REG_EFUSE_CTRL, BIT_EF_FLAG);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	ret = read_poll_timeout(rtw_read32, efuse_ctl, efuse_ctl & BIT_EF_FLAG,
1348c2ecf20Sopenharmony_ci				1000, 100000, false, rtwdev, REG_EFUSE_CTRL);
1358c2ecf20Sopenharmony_ci	if (ret) {
1368c2ecf20Sopenharmony_ci		*data = EFUSE_READ_FAIL;
1378c2ecf20Sopenharmony_ci		return ret;
1388c2ecf20Sopenharmony_ci	}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	*data = rtw_read8(rtwdev, REG_EFUSE_CTRL);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(rtw_read8_physical_efuse);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciint rtw_parse_efuse_map(struct rtw_dev *rtwdev)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	struct rtw_chip_info *chip = rtwdev->chip;
1498c2ecf20Sopenharmony_ci	struct rtw_efuse *efuse = &rtwdev->efuse;
1508c2ecf20Sopenharmony_ci	u32 phy_size = efuse->physical_size;
1518c2ecf20Sopenharmony_ci	u32 log_size = efuse->logical_size;
1528c2ecf20Sopenharmony_ci	u8 *phy_map = NULL;
1538c2ecf20Sopenharmony_ci	u8 *log_map = NULL;
1548c2ecf20Sopenharmony_ci	int ret = 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	phy_map = kmalloc(phy_size, GFP_KERNEL);
1578c2ecf20Sopenharmony_ci	log_map = kmalloc(log_size, GFP_KERNEL);
1588c2ecf20Sopenharmony_ci	if (!phy_map || !log_map) {
1598c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1608c2ecf20Sopenharmony_ci		goto out_free;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	ret = rtw_dump_physical_efuse_map(rtwdev, phy_map);
1648c2ecf20Sopenharmony_ci	if (ret) {
1658c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to dump efuse physical map\n");
1668c2ecf20Sopenharmony_ci		goto out_free;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	memset(log_map, 0xff, log_size);
1708c2ecf20Sopenharmony_ci	ret = rtw_dump_logical_efuse_map(rtwdev, phy_map, log_map);
1718c2ecf20Sopenharmony_ci	if (ret) {
1728c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to dump efuse logical map\n");
1738c2ecf20Sopenharmony_ci		goto out_free;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ret = chip->ops->read_efuse(rtwdev, log_map);
1778c2ecf20Sopenharmony_ci	if (ret) {
1788c2ecf20Sopenharmony_ci		rtw_err(rtwdev, "failed to read efuse map\n");
1798c2ecf20Sopenharmony_ci		goto out_free;
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ciout_free:
1838c2ecf20Sopenharmony_ci	kfree(log_map);
1848c2ecf20Sopenharmony_ci	kfree(phy_map);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return ret;
1878c2ecf20Sopenharmony_ci}
188