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