162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/* Copyright(c) 2018-2019  Realtek Corporation
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/iopoll.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "main.h"
862306a36Sopenharmony_ci#include "efuse.h"
962306a36Sopenharmony_ci#include "reg.h"
1062306a36Sopenharmony_ci#include "debug.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define RTW_EFUSE_BANK_WIFI		0x0
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic void switch_efuse_bank(struct rtw_dev *rtwdev)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	rtw_write32_mask(rtwdev, REG_LDO_EFUSE_CTRL, BIT_MASK_EFUSE_BANK_SEL,
1762306a36Sopenharmony_ci			 RTW_EFUSE_BANK_WIFI);
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define invalid_efuse_header(hdr1, hdr2) \
2162306a36Sopenharmony_ci	((hdr1) == 0xff || (((hdr1) & 0x1f) == 0xf && (hdr2) == 0xff))
2262306a36Sopenharmony_ci#define invalid_efuse_content(word_en, i) \
2362306a36Sopenharmony_ci	(((word_en) & BIT(i)) != 0x0)
2462306a36Sopenharmony_ci#define get_efuse_blk_idx_2_byte(hdr1, hdr2) \
2562306a36Sopenharmony_ci	((((hdr2) & 0xf0) >> 1) | (((hdr1) >> 5) & 0x07))
2662306a36Sopenharmony_ci#define get_efuse_blk_idx_1_byte(hdr1) \
2762306a36Sopenharmony_ci	(((hdr1) & 0xf0) >> 4)
2862306a36Sopenharmony_ci#define block_idx_to_logical_idx(blk_idx, i) \
2962306a36Sopenharmony_ci	(((blk_idx) << 3) + ((i) << 1))
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* efuse header format
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * | 7        5   4    0 | 7        4   3          0 | 15  8  7   0 |
3462306a36Sopenharmony_ci *   block[2:0]   0 1111   block[6:3]   word_en[3:0]   byte0  byte1
3562306a36Sopenharmony_ci * | header 1 (optional) |          header 2         |    word N    |
3662306a36Sopenharmony_ci *
3762306a36Sopenharmony_ci * word_en: 4 bits each word. 0 -> write; 1 -> not write
3862306a36Sopenharmony_ci * N: 1~4, depends on word_en
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_cistatic int rtw_dump_logical_efuse_map(struct rtw_dev *rtwdev, u8 *phy_map,
4162306a36Sopenharmony_ci				      u8 *log_map)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	u32 physical_size = rtwdev->efuse.physical_size;
4462306a36Sopenharmony_ci	u32 protect_size = rtwdev->efuse.protect_size;
4562306a36Sopenharmony_ci	u32 logical_size = rtwdev->efuse.logical_size;
4662306a36Sopenharmony_ci	u32 phy_idx, log_idx;
4762306a36Sopenharmony_ci	u8 hdr1, hdr2;
4862306a36Sopenharmony_ci	u8 blk_idx;
4962306a36Sopenharmony_ci	u8 word_en;
5062306a36Sopenharmony_ci	int i;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for (phy_idx = 0; phy_idx < physical_size - protect_size;) {
5362306a36Sopenharmony_ci		hdr1 = phy_map[phy_idx];
5462306a36Sopenharmony_ci		hdr2 = phy_map[phy_idx + 1];
5562306a36Sopenharmony_ci		if (invalid_efuse_header(hdr1, hdr2))
5662306a36Sopenharmony_ci			break;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		if ((hdr1 & 0x1f) == 0xf) {
5962306a36Sopenharmony_ci			/* 2-byte header format */
6062306a36Sopenharmony_ci			blk_idx = get_efuse_blk_idx_2_byte(hdr1, hdr2);
6162306a36Sopenharmony_ci			word_en = hdr2 & 0xf;
6262306a36Sopenharmony_ci			phy_idx += 2;
6362306a36Sopenharmony_ci		} else {
6462306a36Sopenharmony_ci			/* 1-byte header format */
6562306a36Sopenharmony_ci			blk_idx = get_efuse_blk_idx_1_byte(hdr1);
6662306a36Sopenharmony_ci			word_en = hdr1 & 0xf;
6762306a36Sopenharmony_ci			phy_idx += 1;
6862306a36Sopenharmony_ci		}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		for (i = 0; i < 4; i++) {
7162306a36Sopenharmony_ci			if (invalid_efuse_content(word_en, i))
7262306a36Sopenharmony_ci				continue;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			log_idx = block_idx_to_logical_idx(blk_idx, i);
7562306a36Sopenharmony_ci			if (phy_idx + 1 > physical_size - protect_size ||
7662306a36Sopenharmony_ci			    log_idx + 1 > logical_size)
7762306a36Sopenharmony_ci				return -EINVAL;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci			log_map[log_idx] = phy_map[phy_idx];
8062306a36Sopenharmony_ci			log_map[log_idx + 1] = phy_map[phy_idx + 1];
8162306a36Sopenharmony_ci			phy_idx += 2;
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int rtw_dump_physical_efuse_map(struct rtw_dev *rtwdev, u8 *map)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	const struct rtw_chip_info *chip = rtwdev->chip;
9062306a36Sopenharmony_ci	u32 size = rtwdev->efuse.physical_size;
9162306a36Sopenharmony_ci	u32 efuse_ctl;
9262306a36Sopenharmony_ci	u32 addr;
9362306a36Sopenharmony_ci	u32 cnt;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	rtw_chip_efuse_grant_on(rtwdev);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	switch_efuse_bank(rtwdev);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* disable 2.5V LDO */
10062306a36Sopenharmony_ci	chip->ops->cfg_ldo25(rtwdev, false);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	for (addr = 0; addr < size; addr++) {
10562306a36Sopenharmony_ci		efuse_ctl &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR);
10662306a36Sopenharmony_ci		efuse_ctl |= (addr & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR;
10762306a36Sopenharmony_ci		rtw_write32(rtwdev, REG_EFUSE_CTRL, efuse_ctl & (~BIT_EF_FLAG));
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci		cnt = 1000000;
11062306a36Sopenharmony_ci		do {
11162306a36Sopenharmony_ci			udelay(1);
11262306a36Sopenharmony_ci			efuse_ctl = rtw_read32(rtwdev, REG_EFUSE_CTRL);
11362306a36Sopenharmony_ci			if (--cnt == 0)
11462306a36Sopenharmony_ci				return -EBUSY;
11562306a36Sopenharmony_ci		} while (!(efuse_ctl & BIT_EF_FLAG));
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		*(map + addr) = (u8)(efuse_ctl & BIT_MASK_EF_DATA);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	rtw_chip_efuse_grant_off(rtwdev);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ciint rtw_read8_physical_efuse(struct rtw_dev *rtwdev, u16 addr, u8 *data)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	u32 efuse_ctl;
12862306a36Sopenharmony_ci	int ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	rtw_write32_mask(rtwdev, REG_EFUSE_CTRL, 0x3ff00, addr);
13162306a36Sopenharmony_ci	rtw_write32_clr(rtwdev, REG_EFUSE_CTRL, BIT_EF_FLAG);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	ret = read_poll_timeout(rtw_read32, efuse_ctl, efuse_ctl & BIT_EF_FLAG,
13462306a36Sopenharmony_ci				1000, 100000, false, rtwdev, REG_EFUSE_CTRL);
13562306a36Sopenharmony_ci	if (ret) {
13662306a36Sopenharmony_ci		*data = EFUSE_READ_FAIL;
13762306a36Sopenharmony_ci		return ret;
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	*data = rtw_read8(rtwdev, REG_EFUSE_CTRL);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_read8_physical_efuse);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciint rtw_parse_efuse_map(struct rtw_dev *rtwdev)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	const struct rtw_chip_info *chip = rtwdev->chip;
14962306a36Sopenharmony_ci	struct rtw_efuse *efuse = &rtwdev->efuse;
15062306a36Sopenharmony_ci	u32 phy_size = efuse->physical_size;
15162306a36Sopenharmony_ci	u32 log_size = efuse->logical_size;
15262306a36Sopenharmony_ci	u8 *phy_map = NULL;
15362306a36Sopenharmony_ci	u8 *log_map = NULL;
15462306a36Sopenharmony_ci	int ret = 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	phy_map = kmalloc(phy_size, GFP_KERNEL);
15762306a36Sopenharmony_ci	log_map = kmalloc(log_size, GFP_KERNEL);
15862306a36Sopenharmony_ci	if (!phy_map || !log_map) {
15962306a36Sopenharmony_ci		ret = -ENOMEM;
16062306a36Sopenharmony_ci		goto out_free;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	ret = rtw_dump_physical_efuse_map(rtwdev, phy_map);
16462306a36Sopenharmony_ci	if (ret) {
16562306a36Sopenharmony_ci		rtw_err(rtwdev, "failed to dump efuse physical map\n");
16662306a36Sopenharmony_ci		goto out_free;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	memset(log_map, 0xff, log_size);
17062306a36Sopenharmony_ci	ret = rtw_dump_logical_efuse_map(rtwdev, phy_map, log_map);
17162306a36Sopenharmony_ci	if (ret) {
17262306a36Sopenharmony_ci		rtw_err(rtwdev, "failed to dump efuse logical map\n");
17362306a36Sopenharmony_ci		goto out_free;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	ret = chip->ops->read_efuse(rtwdev, log_map);
17762306a36Sopenharmony_ci	if (ret) {
17862306a36Sopenharmony_ci		rtw_err(rtwdev, "failed to read efuse map\n");
17962306a36Sopenharmony_ci		goto out_free;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciout_free:
18362306a36Sopenharmony_ci	kfree(log_map);
18462306a36Sopenharmony_ci	kfree(phy_map);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return ret;
18762306a36Sopenharmony_ci}
188