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