16d67aabdSBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 26d67aabdSBjoern A. Zeeb /* Copyright(c) 2023 Realtek Corporation 36d67aabdSBjoern A. Zeeb */ 46d67aabdSBjoern A. Zeeb 56d67aabdSBjoern A. Zeeb #include "debug.h" 66d67aabdSBjoern A. Zeeb #include "efuse.h" 76d67aabdSBjoern A. Zeeb #include "mac.h" 86d67aabdSBjoern A. Zeeb #include "reg.h" 96d67aabdSBjoern A. Zeeb 106d67aabdSBjoern A. Zeeb #define EFUSE_EXTERNALPN_ADDR_BE 0x1580 116d67aabdSBjoern A. Zeeb #define EFUSE_SERIALNUM_ADDR_BE 0x1581 126d67aabdSBjoern A. Zeeb #define EFUSE_SB_CRYP_SEL_ADDR 0x1582 136d67aabdSBjoern A. Zeeb #define EFUSE_SB_CRYP_SEL_SIZE 2 146d67aabdSBjoern A. Zeeb #define EFUSE_SB_CRYP_SEL_DEFAULT 0xFFFF 156d67aabdSBjoern A. Zeeb #define SB_SEL_MGN_MAX_SIZE 2 166d67aabdSBjoern A. Zeeb #define EFUSE_SEC_BE_START 0x1580 176d67aabdSBjoern A. Zeeb #define EFUSE_SEC_BE_SIZE 4 186d67aabdSBjoern A. Zeeb 196d67aabdSBjoern A. Zeeb static const u32 sb_sel_mgn[SB_SEL_MGN_MAX_SIZE] = { 206d67aabdSBjoern A. Zeeb 0x8000100, 0xC000180 216d67aabdSBjoern A. Zeeb }; 226d67aabdSBjoern A. Zeeb 236d67aabdSBjoern A. Zeeb static void rtw89_enable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev) 246d67aabdSBjoern A. Zeeb { 256d67aabdSBjoern A. Zeeb const struct rtw89_chip_info *chip = rtwdev->chip; 266d67aabdSBjoern A. Zeeb struct rtw89_hal *hal = &rtwdev->hal; 276d67aabdSBjoern A. Zeeb bool aphy_patch = true; 286d67aabdSBjoern A. Zeeb 296d67aabdSBjoern A. Zeeb if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV) 306d67aabdSBjoern A. Zeeb aphy_patch = false; 316d67aabdSBjoern A. Zeeb 326d67aabdSBjoern A. Zeeb rtw89_write8_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); 336d67aabdSBjoern A. Zeeb 346d67aabdSBjoern A. Zeeb if (aphy_patch) { 356d67aabdSBjoern A. Zeeb rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S); 366d67aabdSBjoern A. Zeeb mdelay(1); 376d67aabdSBjoern A. Zeeb rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B); 386d67aabdSBjoern A. Zeeb rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); 396d67aabdSBjoern A. Zeeb } 406d67aabdSBjoern A. Zeeb 416d67aabdSBjoern A. Zeeb rtw89_write32_set(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST); 426d67aabdSBjoern A. Zeeb } 436d67aabdSBjoern A. Zeeb 446d67aabdSBjoern A. Zeeb static void rtw89_disable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev) 456d67aabdSBjoern A. Zeeb { 466d67aabdSBjoern A. Zeeb const struct rtw89_chip_info *chip = rtwdev->chip; 476d67aabdSBjoern A. Zeeb struct rtw89_hal *hal = &rtwdev->hal; 486d67aabdSBjoern A. Zeeb bool aphy_patch = true; 496d67aabdSBjoern A. Zeeb 506d67aabdSBjoern A. Zeeb if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV) 516d67aabdSBjoern A. Zeeb aphy_patch = false; 526d67aabdSBjoern A. Zeeb 536d67aabdSBjoern A. Zeeb if (aphy_patch) { 546d67aabdSBjoern A. Zeeb rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE); 556d67aabdSBjoern A. Zeeb rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B); 566d67aabdSBjoern A. Zeeb mdelay(1); 576d67aabdSBjoern A. Zeeb rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S); 586d67aabdSBjoern A. Zeeb } 596d67aabdSBjoern A. Zeeb 606d67aabdSBjoern A. Zeeb rtw89_write8_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK); 616d67aabdSBjoern A. Zeeb rtw89_write32_clr(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST); 626d67aabdSBjoern A. Zeeb } 636d67aabdSBjoern A. Zeeb 646d67aabdSBjoern A. Zeeb static int rtw89_dump_physical_efuse_map_ddv_be(struct rtw89_dev *rtwdev, u8 *map, 656d67aabdSBjoern A. Zeeb u32 dump_addr, u32 dump_size) 666d67aabdSBjoern A. Zeeb { 676d67aabdSBjoern A. Zeeb u32 efuse_ctl; 686d67aabdSBjoern A. Zeeb u32 addr; 696d67aabdSBjoern A. Zeeb u32 data; 706d67aabdSBjoern A. Zeeb int ret; 716d67aabdSBjoern A. Zeeb 726d67aabdSBjoern A. Zeeb if (!IS_ALIGNED(dump_addr, 4) || !IS_ALIGNED(dump_size, 4)) { 736d67aabdSBjoern A. Zeeb rtw89_err(rtwdev, "Efuse addr 0x%x or size 0x%x not aligned\n", 746d67aabdSBjoern A. Zeeb dump_addr, dump_size); 756d67aabdSBjoern A. Zeeb return -EINVAL; 766d67aabdSBjoern A. Zeeb } 776d67aabdSBjoern A. Zeeb 786d67aabdSBjoern A. Zeeb rtw89_enable_efuse_pwr_cut_ddv_be(rtwdev); 796d67aabdSBjoern A. Zeeb 806d67aabdSBjoern A. Zeeb for (addr = dump_addr; addr < dump_addr + dump_size; addr += 4, map += 4) { 816d67aabdSBjoern A. Zeeb efuse_ctl = u32_encode_bits(addr, B_BE_EF_ADDR_MASK); 826d67aabdSBjoern A. Zeeb rtw89_write32(rtwdev, R_BE_EFUSE_CTRL, efuse_ctl & ~B_BE_EF_RDY); 836d67aabdSBjoern A. Zeeb 846d67aabdSBjoern A. Zeeb ret = read_poll_timeout_atomic(rtw89_read32, efuse_ctl, 856d67aabdSBjoern A. Zeeb efuse_ctl & B_BE_EF_RDY, 1, 1000000, 866d67aabdSBjoern A. Zeeb true, rtwdev, R_BE_EFUSE_CTRL); 876d67aabdSBjoern A. Zeeb if (ret) 886d67aabdSBjoern A. Zeeb return -EBUSY; 896d67aabdSBjoern A. Zeeb 906d67aabdSBjoern A. Zeeb data = rtw89_read32(rtwdev, R_BE_EFUSE_CTRL_1_V1); 916d67aabdSBjoern A. Zeeb *((__le32 *)map) = cpu_to_le32(data); 926d67aabdSBjoern A. Zeeb } 936d67aabdSBjoern A. Zeeb 946d67aabdSBjoern A. Zeeb rtw89_disable_efuse_pwr_cut_ddv_be(rtwdev); 956d67aabdSBjoern A. Zeeb 966d67aabdSBjoern A. Zeeb return 0; 976d67aabdSBjoern A. Zeeb } 986d67aabdSBjoern A. Zeeb 996d67aabdSBjoern A. Zeeb static int rtw89_dump_physical_efuse_map_dav_be(struct rtw89_dev *rtwdev, u8 *map, 1006d67aabdSBjoern A. Zeeb u32 dump_addr, u32 dump_size) 1016d67aabdSBjoern A. Zeeb { 1026d67aabdSBjoern A. Zeeb u32 addr; 1036d67aabdSBjoern A. Zeeb u8 val8; 1046d67aabdSBjoern A. Zeeb int err; 1056d67aabdSBjoern A. Zeeb int ret; 1066d67aabdSBjoern A. Zeeb 1076d67aabdSBjoern A. Zeeb for (addr = dump_addr; addr < dump_addr + dump_size; addr++) { 1086d67aabdSBjoern A. Zeeb ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0x40, 1096d67aabdSBjoern A. Zeeb FULL_BIT_MASK); 1106d67aabdSBjoern A. Zeeb if (ret) 1116d67aabdSBjoern A. Zeeb return ret; 1126d67aabdSBjoern A. Zeeb ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_LOW_ADDR, addr & 0xff, 1136d67aabdSBjoern A. Zeeb XTAL_SI_LOW_ADDR_MASK); 1146d67aabdSBjoern A. Zeeb if (ret) 1156d67aabdSBjoern A. Zeeb return ret; 1166d67aabdSBjoern A. Zeeb ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, addr >> 8, 1176d67aabdSBjoern A. Zeeb XTAL_SI_HIGH_ADDR_MASK); 1186d67aabdSBjoern A. Zeeb if (ret) 1196d67aabdSBjoern A. Zeeb return ret; 1206d67aabdSBjoern A. Zeeb ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0, 1216d67aabdSBjoern A. Zeeb XTAL_SI_MODE_SEL_MASK); 1226d67aabdSBjoern A. Zeeb if (ret) 1236d67aabdSBjoern A. Zeeb return ret; 1246d67aabdSBjoern A. Zeeb 1256d67aabdSBjoern A. Zeeb ret = read_poll_timeout_atomic(rtw89_mac_read_xtal_si, err, 1266d67aabdSBjoern A. Zeeb !err && (val8 & XTAL_SI_RDY), 1276d67aabdSBjoern A. Zeeb 1, 10000, false, 1286d67aabdSBjoern A. Zeeb rtwdev, XTAL_SI_CTRL, &val8); 1296d67aabdSBjoern A. Zeeb if (ret) { 1306d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to read dav efuse\n"); 1316d67aabdSBjoern A. Zeeb return ret; 1326d67aabdSBjoern A. Zeeb } 1336d67aabdSBjoern A. Zeeb 1346d67aabdSBjoern A. Zeeb ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_READ_VAL, &val8); 1356d67aabdSBjoern A. Zeeb if (ret) 1366d67aabdSBjoern A. Zeeb return ret; 1376d67aabdSBjoern A. Zeeb *map++ = val8; 1386d67aabdSBjoern A. Zeeb } 1396d67aabdSBjoern A. Zeeb 1406d67aabdSBjoern A. Zeeb return 0; 1416d67aabdSBjoern A. Zeeb } 1426d67aabdSBjoern A. Zeeb 1436d67aabdSBjoern A. Zeeb int rtw89_cnv_efuse_state_be(struct rtw89_dev *rtwdev, bool idle) 1446d67aabdSBjoern A. Zeeb { 1456d67aabdSBjoern A. Zeeb u32 val; 1466d67aabdSBjoern A. Zeeb int ret = 0; 1476d67aabdSBjoern A. Zeeb 1486d67aabdSBjoern A. Zeeb if (idle) { 1496d67aabdSBjoern A. Zeeb rtw89_write32_set(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN); 1506d67aabdSBjoern A. Zeeb } else { 1516d67aabdSBjoern A. Zeeb rtw89_write32_clr(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN); 1526d67aabdSBjoern A. Zeeb 1536d67aabdSBjoern A. Zeeb ret = read_poll_timeout(rtw89_read32_mask, val, 1546d67aabdSBjoern A. Zeeb val == MAC_AX_SYS_ACT, 50, 5000, 1556d67aabdSBjoern A. Zeeb false, rtwdev, R_BE_IC_PWR_STATE, 1566d67aabdSBjoern A. Zeeb B_BE_WHOLE_SYS_PWR_STE_MASK); 1576d67aabdSBjoern A. Zeeb if (ret) 1586d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to convert efuse state\n"); 1596d67aabdSBjoern A. Zeeb } 1606d67aabdSBjoern A. Zeeb 1616d67aabdSBjoern A. Zeeb return ret; 1626d67aabdSBjoern A. Zeeb } 1636d67aabdSBjoern A. Zeeb 1646d67aabdSBjoern A. Zeeb static int rtw89_dump_physical_efuse_map_be(struct rtw89_dev *rtwdev, u8 *map, 1656d67aabdSBjoern A. Zeeb u32 dump_addr, u32 dump_size, bool dav) 1666d67aabdSBjoern A. Zeeb { 1676d67aabdSBjoern A. Zeeb int ret; 1686d67aabdSBjoern A. Zeeb 1696d67aabdSBjoern A. Zeeb if (!map || dump_size == 0) 1706d67aabdSBjoern A. Zeeb return 0; 1716d67aabdSBjoern A. Zeeb 1726d67aabdSBjoern A. Zeeb rtw89_cnv_efuse_state_be(rtwdev, false); 1736d67aabdSBjoern A. Zeeb 1746d67aabdSBjoern A. Zeeb if (dav) { 1756d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_dav_be(rtwdev, map, 1766d67aabdSBjoern A. Zeeb dump_addr, dump_size); 1776d67aabdSBjoern A. Zeeb if (ret) 1786d67aabdSBjoern A. Zeeb return ret; 1796d67aabdSBjoern A. Zeeb 1806d67aabdSBjoern A. Zeeb rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map dav: ", map, dump_size); 1816d67aabdSBjoern A. Zeeb } else { 1826d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_ddv_be(rtwdev, map, 1836d67aabdSBjoern A. Zeeb dump_addr, dump_size); 1846d67aabdSBjoern A. Zeeb if (ret) 1856d67aabdSBjoern A. Zeeb return ret; 1866d67aabdSBjoern A. Zeeb 1876d67aabdSBjoern A. Zeeb rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map ddv: ", map, dump_size); 1886d67aabdSBjoern A. Zeeb } 1896d67aabdSBjoern A. Zeeb 1906d67aabdSBjoern A. Zeeb rtw89_cnv_efuse_state_be(rtwdev, true); 1916d67aabdSBjoern A. Zeeb 1926d67aabdSBjoern A. Zeeb return 0; 1936d67aabdSBjoern A. Zeeb } 1946d67aabdSBjoern A. Zeeb 1956d67aabdSBjoern A. Zeeb #define EFUSE_HDR_CONST_MASK GENMASK(23, 20) 1966d67aabdSBjoern A. Zeeb #define EFUSE_HDR_PAGE_MASK GENMASK(19, 17) 1976d67aabdSBjoern A. Zeeb #define EFUSE_HDR_OFFSET_MASK GENMASK(16, 4) 1986d67aabdSBjoern A. Zeeb #define EFUSE_HDR_OFFSET_DAV_MASK GENMASK(11, 4) 1996d67aabdSBjoern A. Zeeb #define EFUSE_HDR_WORD_EN_MASK GENMASK(3, 0) 2006d67aabdSBjoern A. Zeeb 2016d67aabdSBjoern A. Zeeb #define invalid_efuse_header_be(hdr1, hdr2, hdr3) \ 2026d67aabdSBjoern A. Zeeb ((hdr1) == 0xff || (hdr2) == 0xff || (hdr3) == 0xff) 2036d67aabdSBjoern A. Zeeb #define invalid_efuse_content_be(word_en, i) \ 2046d67aabdSBjoern A. Zeeb (((word_en) & BIT(i)) != 0x0) 2056d67aabdSBjoern A. Zeeb #define get_efuse_blk_idx_be(hdr1, hdr2, hdr3) \ 2066d67aabdSBjoern A. Zeeb (((hdr1) << 16) | ((hdr2) << 8) | (hdr3)) 2076d67aabdSBjoern A. Zeeb #define block_idx_to_logical_idx_be(blk_idx, i) \ 2086d67aabdSBjoern A. Zeeb (((blk_idx) << 3) + ((i) << 1)) 2096d67aabdSBjoern A. Zeeb 2106d67aabdSBjoern A. Zeeb #define invalid_efuse_header_dav_be(hdr1, hdr2) \ 2116d67aabdSBjoern A. Zeeb ((hdr1) == 0xff || (hdr2) == 0xff) 2126d67aabdSBjoern A. Zeeb #define get_efuse_blk_idx_dav_be(hdr1, hdr2) \ 2136d67aabdSBjoern A. Zeeb (((hdr1) << 8) | (hdr2)) 2146d67aabdSBjoern A. Zeeb 2156d67aabdSBjoern A. Zeeb static int rtw89_eeprom_parser_be(struct rtw89_dev *rtwdev, 2166d67aabdSBjoern A. Zeeb const u8 *phy_map, u32 phy_size, u8 *log_map, 2176d67aabdSBjoern A. Zeeb const struct rtw89_efuse_block_cfg *efuse_block) 2186d67aabdSBjoern A. Zeeb { 2196d67aabdSBjoern A. Zeeb const struct rtw89_chip_info *chip = rtwdev->chip; 2206d67aabdSBjoern A. Zeeb enum rtw89_efuse_block blk_page, page; 2216d67aabdSBjoern A. Zeeb u32 size = efuse_block->size; 2226d67aabdSBjoern A. Zeeb u32 phy_idx, log_idx; 2236d67aabdSBjoern A. Zeeb u32 hdr, page_offset; 2246d67aabdSBjoern A. Zeeb u8 hdr1, hdr2, hdr3; 2256d67aabdSBjoern A. Zeeb u8 i, val0, val1; 2266d67aabdSBjoern A. Zeeb u32 min, max; 2276d67aabdSBjoern A. Zeeb u16 blk_idx; 2286d67aabdSBjoern A. Zeeb u8 word_en; 2296d67aabdSBjoern A. Zeeb 2306d67aabdSBjoern A. Zeeb page = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_ID_MASK); 2316d67aabdSBjoern A. Zeeb page_offset = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_SIZE_MASK); 2326d67aabdSBjoern A. Zeeb 2336d67aabdSBjoern A. Zeeb min = ALIGN_DOWN(page_offset, 2); 2346d67aabdSBjoern A. Zeeb max = ALIGN(page_offset + size, 2); 2356d67aabdSBjoern A. Zeeb 2366d67aabdSBjoern A. Zeeb memset(log_map, 0xff, size); 2376d67aabdSBjoern A. Zeeb 2386d67aabdSBjoern A. Zeeb phy_idx = chip->sec_ctrl_efuse_size; 2396d67aabdSBjoern A. Zeeb 2406d67aabdSBjoern A. Zeeb do { 2416d67aabdSBjoern A. Zeeb if (page == RTW89_EFUSE_BLOCK_ADIE) { 2426d67aabdSBjoern A. Zeeb hdr1 = phy_map[phy_idx]; 2436d67aabdSBjoern A. Zeeb hdr2 = phy_map[phy_idx + 1]; 2446d67aabdSBjoern A. Zeeb if (invalid_efuse_header_dav_be(hdr1, hdr2)) 2456d67aabdSBjoern A. Zeeb break; 2466d67aabdSBjoern A. Zeeb 2476d67aabdSBjoern A. Zeeb phy_idx += 2; 2486d67aabdSBjoern A. Zeeb 2496d67aabdSBjoern A. Zeeb hdr = get_efuse_blk_idx_dav_be(hdr1, hdr2); 2506d67aabdSBjoern A. Zeeb 2516d67aabdSBjoern A. Zeeb blk_page = RTW89_EFUSE_BLOCK_ADIE; 2526d67aabdSBjoern A. Zeeb blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_DAV_MASK); 2536d67aabdSBjoern A. Zeeb word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK); 2546d67aabdSBjoern A. Zeeb } else { 2556d67aabdSBjoern A. Zeeb hdr1 = phy_map[phy_idx]; 2566d67aabdSBjoern A. Zeeb hdr2 = phy_map[phy_idx + 1]; 2576d67aabdSBjoern A. Zeeb hdr3 = phy_map[phy_idx + 2]; 2586d67aabdSBjoern A. Zeeb if (invalid_efuse_header_be(hdr1, hdr2, hdr3)) 2596d67aabdSBjoern A. Zeeb break; 2606d67aabdSBjoern A. Zeeb 2616d67aabdSBjoern A. Zeeb phy_idx += 3; 2626d67aabdSBjoern A. Zeeb 2636d67aabdSBjoern A. Zeeb hdr = get_efuse_blk_idx_be(hdr1, hdr2, hdr3); 2646d67aabdSBjoern A. Zeeb 2656d67aabdSBjoern A. Zeeb blk_page = u32_get_bits(hdr, EFUSE_HDR_PAGE_MASK); 2666d67aabdSBjoern A. Zeeb blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_MASK); 2676d67aabdSBjoern A. Zeeb word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK); 2686d67aabdSBjoern A. Zeeb } 2696d67aabdSBjoern A. Zeeb 2706d67aabdSBjoern A. Zeeb if (blk_idx >= RTW89_EFUSE_MAX_BLOCK_SIZE >> 3) { 2716d67aabdSBjoern A. Zeeb rtw89_err(rtwdev, "[ERR]efuse idx:0x%X\n", phy_idx - 3); 2726d67aabdSBjoern A. Zeeb rtw89_err(rtwdev, "[ERR]read hdr:0x%X\n", hdr); 2736d67aabdSBjoern A. Zeeb return -EINVAL; 2746d67aabdSBjoern A. Zeeb } 2756d67aabdSBjoern A. Zeeb 2766d67aabdSBjoern A. Zeeb for (i = 0; i < 4; i++) { 2776d67aabdSBjoern A. Zeeb if (invalid_efuse_content_be(word_en, i)) 2786d67aabdSBjoern A. Zeeb continue; 2796d67aabdSBjoern A. Zeeb 2806d67aabdSBjoern A. Zeeb if (phy_idx >= phy_size - 1) 2816d67aabdSBjoern A. Zeeb return -EINVAL; 2826d67aabdSBjoern A. Zeeb 2836d67aabdSBjoern A. Zeeb log_idx = block_idx_to_logical_idx_be(blk_idx, i); 2846d67aabdSBjoern A. Zeeb 2856d67aabdSBjoern A. Zeeb if (blk_page == page && log_idx >= min && log_idx < max) { 2866d67aabdSBjoern A. Zeeb val0 = phy_map[phy_idx]; 2876d67aabdSBjoern A. Zeeb val1 = phy_map[phy_idx + 1]; 2886d67aabdSBjoern A. Zeeb 2896d67aabdSBjoern A. Zeeb if (log_idx == min && page_offset > min) { 2906d67aabdSBjoern A. Zeeb log_map[log_idx - page_offset + 1] = val1; 2916d67aabdSBjoern A. Zeeb } else if (log_idx + 2 == max && 2926d67aabdSBjoern A. Zeeb page_offset + size < max) { 2936d67aabdSBjoern A. Zeeb log_map[log_idx - page_offset] = val0; 2946d67aabdSBjoern A. Zeeb } else { 2956d67aabdSBjoern A. Zeeb log_map[log_idx - page_offset] = val0; 2966d67aabdSBjoern A. Zeeb log_map[log_idx - page_offset + 1] = val1; 2976d67aabdSBjoern A. Zeeb } 2986d67aabdSBjoern A. Zeeb } 2996d67aabdSBjoern A. Zeeb phy_idx += 2; 3006d67aabdSBjoern A. Zeeb } 3016d67aabdSBjoern A. Zeeb } while (phy_idx < phy_size); 3026d67aabdSBjoern A. Zeeb 3036d67aabdSBjoern A. Zeeb return 0; 3046d67aabdSBjoern A. Zeeb } 3056d67aabdSBjoern A. Zeeb 3066d67aabdSBjoern A. Zeeb static int rtw89_parse_logical_efuse_block_be(struct rtw89_dev *rtwdev, 3076d67aabdSBjoern A. Zeeb const u8 *phy_map, u32 phy_size, 3086d67aabdSBjoern A. Zeeb enum rtw89_efuse_block block) 3096d67aabdSBjoern A. Zeeb { 3106d67aabdSBjoern A. Zeeb const struct rtw89_chip_info *chip = rtwdev->chip; 3116d67aabdSBjoern A. Zeeb const struct rtw89_efuse_block_cfg *efuse_block; 3126d67aabdSBjoern A. Zeeb u8 *log_map; 3136d67aabdSBjoern A. Zeeb int ret; 3146d67aabdSBjoern A. Zeeb 3156d67aabdSBjoern A. Zeeb efuse_block = &chip->efuse_blocks[block]; 3166d67aabdSBjoern A. Zeeb 3176d67aabdSBjoern A. Zeeb log_map = kmalloc(efuse_block->size, GFP_KERNEL); 3186d67aabdSBjoern A. Zeeb if (!log_map) 3196d67aabdSBjoern A. Zeeb return -ENOMEM; 3206d67aabdSBjoern A. Zeeb 3216d67aabdSBjoern A. Zeeb ret = rtw89_eeprom_parser_be(rtwdev, phy_map, phy_size, log_map, efuse_block); 3226d67aabdSBjoern A. Zeeb if (ret) { 3236d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to dump efuse logical block %d\n", block); 3246d67aabdSBjoern A. Zeeb goto out_free; 3256d67aabdSBjoern A. Zeeb } 3266d67aabdSBjoern A. Zeeb 3276d67aabdSBjoern A. Zeeb rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, efuse_block->size); 3286d67aabdSBjoern A. Zeeb 3296d67aabdSBjoern A. Zeeb ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map, block); 3306d67aabdSBjoern A. Zeeb if (ret) { 3316d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to read efuse map\n"); 3326d67aabdSBjoern A. Zeeb goto out_free; 3336d67aabdSBjoern A. Zeeb } 3346d67aabdSBjoern A. Zeeb 3356d67aabdSBjoern A. Zeeb out_free: 3366d67aabdSBjoern A. Zeeb kfree(log_map); 3376d67aabdSBjoern A. Zeeb 3386d67aabdSBjoern A. Zeeb return ret; 3396d67aabdSBjoern A. Zeeb } 3406d67aabdSBjoern A. Zeeb 3416d67aabdSBjoern A. Zeeb int rtw89_parse_efuse_map_be(struct rtw89_dev *rtwdev) 3426d67aabdSBjoern A. Zeeb { 3436d67aabdSBjoern A. Zeeb u32 phy_size = rtwdev->chip->physical_efuse_size; 3446d67aabdSBjoern A. Zeeb u32 dav_phy_size = rtwdev->chip->dav_phy_efuse_size; 3456d67aabdSBjoern A. Zeeb enum rtw89_efuse_block block; 3466d67aabdSBjoern A. Zeeb u8 *phy_map = NULL; 3476d67aabdSBjoern A. Zeeb u8 *dav_phy_map = NULL; 3486d67aabdSBjoern A. Zeeb int ret; 3496d67aabdSBjoern A. Zeeb 3506d67aabdSBjoern A. Zeeb if (rtw89_read16(rtwdev, R_BE_SYS_WL_EFUSE_CTRL) & B_BE_AUTOLOAD_SUS) 3516d67aabdSBjoern A. Zeeb rtwdev->efuse.valid = true; 3526d67aabdSBjoern A. Zeeb else 3536d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to check efuse autoload\n"); 3546d67aabdSBjoern A. Zeeb 3556d67aabdSBjoern A. Zeeb phy_map = kmalloc(phy_size, GFP_KERNEL); 3566d67aabdSBjoern A. Zeeb if (dav_phy_size) 3576d67aabdSBjoern A. Zeeb dav_phy_map = kmalloc(dav_phy_size, GFP_KERNEL); 3586d67aabdSBjoern A. Zeeb 3596d67aabdSBjoern A. Zeeb if (!phy_map || (dav_phy_size && !dav_phy_map)) { 3606d67aabdSBjoern A. Zeeb ret = -ENOMEM; 3616d67aabdSBjoern A. Zeeb goto out_free; 3626d67aabdSBjoern A. Zeeb } 3636d67aabdSBjoern A. Zeeb 3646d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_be(rtwdev, phy_map, 0, phy_size, false); 3656d67aabdSBjoern A. Zeeb if (ret) { 3666d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to dump efuse physical map\n"); 3676d67aabdSBjoern A. Zeeb goto out_free; 3686d67aabdSBjoern A. Zeeb } 3696d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_be(rtwdev, dav_phy_map, 0, dav_phy_size, true); 3706d67aabdSBjoern A. Zeeb if (ret) { 3716d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to dump efuse dav physical map\n"); 3726d67aabdSBjoern A. Zeeb goto out_free; 3736d67aabdSBjoern A. Zeeb } 3746d67aabdSBjoern A. Zeeb 3756d67aabdSBjoern A. Zeeb if (rtwdev->hci.type == RTW89_HCI_TYPE_USB) 3766d67aabdSBjoern A. Zeeb block = RTW89_EFUSE_BLOCK_HCI_DIG_USB; 3776d67aabdSBjoern A. Zeeb else 3786d67aabdSBjoern A. Zeeb block = RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO; 3796d67aabdSBjoern A. Zeeb 3806d67aabdSBjoern A. Zeeb ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size, block); 3816d67aabdSBjoern A. Zeeb if (ret) { 3826d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n", 3836d67aabdSBjoern A. Zeeb RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO); 3846d67aabdSBjoern A. Zeeb goto out_free; 3856d67aabdSBjoern A. Zeeb } 3866d67aabdSBjoern A. Zeeb 3876d67aabdSBjoern A. Zeeb ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size, 3886d67aabdSBjoern A. Zeeb RTW89_EFUSE_BLOCK_RF); 3896d67aabdSBjoern A. Zeeb if (ret) { 3906d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n", 3916d67aabdSBjoern A. Zeeb RTW89_EFUSE_BLOCK_RF); 3926d67aabdSBjoern A. Zeeb goto out_free; 3936d67aabdSBjoern A. Zeeb } 3946d67aabdSBjoern A. Zeeb 3956d67aabdSBjoern A. Zeeb out_free: 3966d67aabdSBjoern A. Zeeb kfree(dav_phy_map); 3976d67aabdSBjoern A. Zeeb kfree(phy_map); 3986d67aabdSBjoern A. Zeeb 3996d67aabdSBjoern A. Zeeb return ret; 4006d67aabdSBjoern A. Zeeb } 4016d67aabdSBjoern A. Zeeb 4026d67aabdSBjoern A. Zeeb int rtw89_parse_phycap_map_be(struct rtw89_dev *rtwdev) 4036d67aabdSBjoern A. Zeeb { 4046d67aabdSBjoern A. Zeeb u32 phycap_addr = rtwdev->chip->phycap_addr; 4056d67aabdSBjoern A. Zeeb u32 phycap_size = rtwdev->chip->phycap_size; 4066d67aabdSBjoern A. Zeeb u8 *phycap_map = NULL; 4076d67aabdSBjoern A. Zeeb int ret = 0; 4086d67aabdSBjoern A. Zeeb 4096d67aabdSBjoern A. Zeeb if (!phycap_size) 4106d67aabdSBjoern A. Zeeb return 0; 4116d67aabdSBjoern A. Zeeb 4126d67aabdSBjoern A. Zeeb phycap_map = kmalloc(phycap_size, GFP_KERNEL); 4136d67aabdSBjoern A. Zeeb if (!phycap_map) 4146d67aabdSBjoern A. Zeeb return -ENOMEM; 4156d67aabdSBjoern A. Zeeb 4166d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_be(rtwdev, phycap_map, 4176d67aabdSBjoern A. Zeeb phycap_addr, phycap_size, false); 4186d67aabdSBjoern A. Zeeb if (ret) { 4196d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to dump phycap map\n"); 4206d67aabdSBjoern A. Zeeb goto out_free; 4216d67aabdSBjoern A. Zeeb } 4226d67aabdSBjoern A. Zeeb 4236d67aabdSBjoern A. Zeeb ret = rtwdev->chip->ops->read_phycap(rtwdev, phycap_map); 4246d67aabdSBjoern A. Zeeb if (ret) { 4256d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to read phycap map\n"); 4266d67aabdSBjoern A. Zeeb goto out_free; 4276d67aabdSBjoern A. Zeeb } 4286d67aabdSBjoern A. Zeeb 4296d67aabdSBjoern A. Zeeb out_free: 4306d67aabdSBjoern A. Zeeb kfree(phycap_map); 4316d67aabdSBjoern A. Zeeb 4326d67aabdSBjoern A. Zeeb return ret; 4336d67aabdSBjoern A. Zeeb } 4346d67aabdSBjoern A. Zeeb 4356d67aabdSBjoern A. Zeeb static u16 get_sb_cryp_sel_idx(u16 sb_cryp_sel) 4366d67aabdSBjoern A. Zeeb { 4376d67aabdSBjoern A. Zeeb u8 low_bit, high_bit, cnt_zero = 0; 4386d67aabdSBjoern A. Zeeb u8 idx, sel_form_v, sel_idx_v; 4396d67aabdSBjoern A. Zeeb u16 sb_cryp_sel_v = 0x0; 4406d67aabdSBjoern A. Zeeb 4416d67aabdSBjoern A. Zeeb sel_form_v = u16_get_bits(sb_cryp_sel, MASKBYTE0); 4426d67aabdSBjoern A. Zeeb sel_idx_v = u16_get_bits(sb_cryp_sel, MASKBYTE1); 4436d67aabdSBjoern A. Zeeb 4446d67aabdSBjoern A. Zeeb for (idx = 0; idx < 4; idx++) { 4456d67aabdSBjoern A. Zeeb low_bit = !!(sel_form_v & BIT(idx)); 4466d67aabdSBjoern A. Zeeb high_bit = !!(sel_form_v & BIT(7 - idx)); 4476d67aabdSBjoern A. Zeeb if (low_bit != high_bit) 4486d67aabdSBjoern A. Zeeb return U16_MAX; 4496d67aabdSBjoern A. Zeeb if (low_bit) 4506d67aabdSBjoern A. Zeeb continue; 4516d67aabdSBjoern A. Zeeb 4526d67aabdSBjoern A. Zeeb cnt_zero++; 4536d67aabdSBjoern A. Zeeb if (cnt_zero == 1) 4546d67aabdSBjoern A. Zeeb sb_cryp_sel_v = idx * 16; 4556d67aabdSBjoern A. Zeeb else if (cnt_zero > 1) 4566d67aabdSBjoern A. Zeeb return U16_MAX; 4576d67aabdSBjoern A. Zeeb } 4586d67aabdSBjoern A. Zeeb 4596d67aabdSBjoern A. Zeeb low_bit = u8_get_bits(sel_idx_v, 0x0F); 4606d67aabdSBjoern A. Zeeb high_bit = u8_get_bits(sel_idx_v, 0xF0); 4616d67aabdSBjoern A. Zeeb 4626d67aabdSBjoern A. Zeeb if ((low_bit ^ high_bit) != 0xF) 4636d67aabdSBjoern A. Zeeb return U16_MAX; 4646d67aabdSBjoern A. Zeeb 4656d67aabdSBjoern A. Zeeb return sb_cryp_sel_v + low_bit; 4666d67aabdSBjoern A. Zeeb } 4676d67aabdSBjoern A. Zeeb 4686d67aabdSBjoern A. Zeeb int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev) 4696d67aabdSBjoern A. Zeeb { 4706d67aabdSBjoern A. Zeeb struct rtw89_fw_secure *sec = &rtwdev->fw.sec; 4716d67aabdSBjoern A. Zeeb u32 sec_addr = EFUSE_SEC_BE_START; 4726d67aabdSBjoern A. Zeeb u32 sec_size = EFUSE_SEC_BE_SIZE; 4736d67aabdSBjoern A. Zeeb u16 sb_cryp_sel, sb_cryp_sel_idx; 4746d67aabdSBjoern A. Zeeb u8 sec_map[EFUSE_SEC_BE_SIZE]; 4756d67aabdSBjoern A. Zeeb u8 b1, b2; 4766d67aabdSBjoern A. Zeeb int ret; 4776d67aabdSBjoern A. Zeeb 4786d67aabdSBjoern A. Zeeb ret = rtw89_dump_physical_efuse_map_be(rtwdev, sec_map, 4796d67aabdSBjoern A. Zeeb sec_addr, sec_size, false); 4806d67aabdSBjoern A. Zeeb if (ret) { 4816d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "failed to dump secsel map\n"); 4826d67aabdSBjoern A. Zeeb return ret; 4836d67aabdSBjoern A. Zeeb } 4846d67aabdSBjoern A. Zeeb 4856d67aabdSBjoern A. Zeeb sb_cryp_sel = sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr] | 4866d67aabdSBjoern A. Zeeb sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr + 1] << 8; 4876d67aabdSBjoern A. Zeeb if (sb_cryp_sel == EFUSE_SB_CRYP_SEL_DEFAULT) 4886d67aabdSBjoern A. Zeeb goto out; 4896d67aabdSBjoern A. Zeeb 4906d67aabdSBjoern A. Zeeb sb_cryp_sel_idx = get_sb_cryp_sel_idx(sb_cryp_sel); 4916d67aabdSBjoern A. Zeeb if (sb_cryp_sel_idx >= SB_SEL_MGN_MAX_SIZE) { 4926d67aabdSBjoern A. Zeeb rtw89_warn(rtwdev, "invalid SB cryp sel idx %d\n", sb_cryp_sel_idx); 4936d67aabdSBjoern A. Zeeb goto out; 4946d67aabdSBjoern A. Zeeb } 4956d67aabdSBjoern A. Zeeb 4966d67aabdSBjoern A. Zeeb sec->sb_sel_mgn = sb_sel_mgn[sb_cryp_sel_idx]; 4976d67aabdSBjoern A. Zeeb 4986d67aabdSBjoern A. Zeeb b1 = sec_map[EFUSE_EXTERNALPN_ADDR_BE - sec_addr]; 4996d67aabdSBjoern A. Zeeb b2 = sec_map[EFUSE_SERIALNUM_ADDR_BE - sec_addr]; 5006d67aabdSBjoern A. Zeeb 501*df279a26SBjoern A. Zeeb ret = rtw89_efuse_recognize_mss_info_v1(rtwdev, b1, b2); 502*df279a26SBjoern A. Zeeb if (ret) 5036d67aabdSBjoern A. Zeeb goto out; 5046d67aabdSBjoern A. Zeeb 5056d67aabdSBjoern A. Zeeb sec->secure_boot = true; 5066d67aabdSBjoern A. Zeeb 5076d67aabdSBjoern A. Zeeb out: 5086d67aabdSBjoern A. Zeeb rtw89_debug(rtwdev, RTW89_DBG_FW, 5096d67aabdSBjoern A. Zeeb "MSS secure_boot=%d dev_type=%d cust_idx=%d key_num=%d\n", 5106d67aabdSBjoern A. Zeeb sec->secure_boot, sec->mss_dev_type, sec->mss_cust_idx, 5116d67aabdSBjoern A. Zeeb sec->mss_key_num); 5126d67aabdSBjoern A. Zeeb 5136d67aabdSBjoern A. Zeeb return 0; 5146d67aabdSBjoern A. Zeeb } 515