linux/drivers/net/wireless/realtek/rtw89/efuse_be.c

515 lines
13 KiB
C
Raw Permalink Normal View History

wifi: rtw89: mac: add to access efuse for WiFi 7 chips MAC address, hardware type, calibration values and etc are stored in efuse, so we read them at probe stage and use them as capabilities to register hardware. There are two physical efuse -- one is the main efuse for digital hardware part, and the other is for analog part. Because they are very similar, we only describe the main efuse below. The main efuse is split into two regions -- one is for logic map, and the other is for physical map. For both regions, we use the same method to read data, but need additional parser to get logic map. To allow reading operation, we need to convert power state to active, and turn to idle state after reading. For WiFi 7 chips, we introduce efuse blocks to define feature group easier, and these blocks are discontinue. For example, RF block is from 0x1_0000 ~ 0x1_0240, and the next block PCIE_SDIO is starting from 0x2_0000. Comparing to old one used by WiFi 6 chips, there is only single one logic map, it would be a little hard to add an new field to a group if we don't reserve a room in advance. The relationship between efuse, region and block is shown as below: (logical map) +------------+ +---------------+ +-----------------+ | main efuse | | region 1 | | block 0x1_0000~ | | (digital) | |(to logcal map)| +-----------------+ | | | | => +-----------------+ | | => | | | block 0x2_0000~ | | | | | +-----------------+ | | |---------------| : | | | region 2 | +------------+ +---------------+ +------------+ +-----------------+ | 2nd efuse | ======================> | block 0x7_0000~ | | (analog) | +-----------------+ +------------+ The parser converting from raw data to logic map is to decode block page, block page offset, and word_en bits. Each word_en bit indicates two following bytes as data of logic map, so total four word_en bits can represent eight bytes. Thus, block page offset is 8-byte alignment. The layout of a tuple is shown as below +--------+--------+--------+--------+--------+--------+ | fixed 3 byte header | | | | | | | | | | [19:17] block_page | | | ... | | [16:4] block_page_offset| | | | | [3:0] word_en | ^ | ^ | | +----|---+--------+--------+---|----+----|---+--------+ | | | +-------------------------+---------+ a word_en bit indicates two bytes as data For example, block_page = 0x3 block_page_offset = 0x80 (must 8-byte alignment) word_en = 0x6 (b'0110; 0 means data is presented) following 4 bytes = 34 56 78 90 Then, 0x3_0080 = 34 56 0x3_0086 = 78 90 A special block page is RTW89_EFUSE_BLOCK_ADIE (7) that uses different but similar format, because its real efuse size is smaller than main efuse. Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Signed-off-by: Kalle Valo <kvalo@kernel.org> Link: https://lore.kernel.org/r/20231117024029.113845-4-pkshih@realtek.com
2023-11-17 10:40:26 +08:00
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2023 Realtek Corporation
*/
#include "debug.h"
#include "efuse.h"
#include "mac.h"
#include "reg.h"
#define EFUSE_EXTERNALPN_ADDR_BE 0x1580
#define EFUSE_SERIALNUM_ADDR_BE 0x1581
#define EFUSE_SB_CRYP_SEL_ADDR 0x1582
#define EFUSE_SB_CRYP_SEL_SIZE 2
#define EFUSE_SB_CRYP_SEL_DEFAULT 0xFFFF
#define SB_SEL_MGN_MAX_SIZE 2
#define EFUSE_SEC_BE_START 0x1580
#define EFUSE_SEC_BE_SIZE 4
static const u32 sb_sel_mgn[SB_SEL_MGN_MAX_SIZE] = {
0x8000100, 0xC000180
};
wifi: rtw89: mac: add to access efuse for WiFi 7 chips MAC address, hardware type, calibration values and etc are stored in efuse, so we read them at probe stage and use them as capabilities to register hardware. There are two physical efuse -- one is the main efuse for digital hardware part, and the other is for analog part. Because they are very similar, we only describe the main efuse below. The main efuse is split into two regions -- one is for logic map, and the other is for physical map. For both regions, we use the same method to read data, but need additional parser to get logic map. To allow reading operation, we need to convert power state to active, and turn to idle state after reading. For WiFi 7 chips, we introduce efuse blocks to define feature group easier, and these blocks are discontinue. For example, RF block is from 0x1_0000 ~ 0x1_0240, and the next block PCIE_SDIO is starting from 0x2_0000. Comparing to old one used by WiFi 6 chips, there is only single one logic map, it would be a little hard to add an new field to a group if we don't reserve a room in advance. The relationship between efuse, region and block is shown as below: (logical map) +------------+ +---------------+ +-----------------+ | main efuse | | region 1 | | block 0x1_0000~ | | (digital) | |(to logcal map)| +-----------------+ | | | | => +-----------------+ | | => | | | block 0x2_0000~ | | | | | +-----------------+ | | |---------------| : | | | region 2 | +------------+ +---------------+ +------------+ +-----------------+ | 2nd efuse | ======================> | block 0x7_0000~ | | (analog) | +-----------------+ +------------+ The parser converting from raw data to logic map is to decode block page, block page offset, and word_en bits. Each word_en bit indicates two following bytes as data of logic map, so total four word_en bits can represent eight bytes. Thus, block page offset is 8-byte alignment. The layout of a tuple is shown as below +--------+--------+--------+--------+--------+--------+ | fixed 3 byte header | | | | | | | | | | [19:17] block_page | | | ... | | [16:4] block_page_offset| | | | | [3:0] word_en | ^ | ^ | | +----|---+--------+--------+---|----+----|---+--------+ | | | +-------------------------+---------+ a word_en bit indicates two bytes as data For example, block_page = 0x3 block_page_offset = 0x80 (must 8-byte alignment) word_en = 0x6 (b'0110; 0 means data is presented) following 4 bytes = 34 56 78 90 Then, 0x3_0080 = 34 56 0x3_0086 = 78 90 A special block page is RTW89_EFUSE_BLOCK_ADIE (7) that uses different but similar format, because its real efuse size is smaller than main efuse. Signed-off-by: Ping-Ke Shih <pkshih@realtek.com> Signed-off-by: Kalle Valo <kvalo@kernel.org> Link: https://lore.kernel.org/r/20231117024029.113845-4-pkshih@realtek.com
2023-11-17 10:40:26 +08:00
static void rtw89_enable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_hal *hal = &rtwdev->hal;
bool aphy_patch = true;
if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV)
aphy_patch = false;
rtw89_write8_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK);
if (aphy_patch) {
rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S);
mdelay(1);
rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B);
rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE);
}
rtw89_write32_set(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST);
}
static void rtw89_disable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_hal *hal = &rtwdev->hal;
bool aphy_patch = true;
if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV)
aphy_patch = false;
if (aphy_patch) {
rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE);
rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B);
mdelay(1);
rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S);
}
rtw89_write8_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK);
rtw89_write32_clr(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST);
}
static int rtw89_dump_physical_efuse_map_ddv_be(struct rtw89_dev *rtwdev, u8 *map,
u32 dump_addr, u32 dump_size)
{
u32 efuse_ctl;
u32 addr;
u32 data;
int ret;
if (!IS_ALIGNED(dump_addr, 4) || !IS_ALIGNED(dump_size, 4)) {
rtw89_err(rtwdev, "Efuse addr 0x%x or size 0x%x not aligned\n",
dump_addr, dump_size);
return -EINVAL;
}
rtw89_enable_efuse_pwr_cut_ddv_be(rtwdev);
for (addr = dump_addr; addr < dump_addr + dump_size; addr += 4, map += 4) {
efuse_ctl = u32_encode_bits(addr, B_BE_EF_ADDR_MASK);
rtw89_write32(rtwdev, R_BE_EFUSE_CTRL, efuse_ctl & ~B_BE_EF_RDY);
ret = read_poll_timeout_atomic(rtw89_read32, efuse_ctl,
efuse_ctl & B_BE_EF_RDY, 1, 1000000,
true, rtwdev, R_BE_EFUSE_CTRL);
if (ret)
return -EBUSY;
data = rtw89_read32(rtwdev, R_BE_EFUSE_CTRL_1_V1);
*((__le32 *)map) = cpu_to_le32(data);
}
rtw89_disable_efuse_pwr_cut_ddv_be(rtwdev);
return 0;
}
static int rtw89_dump_physical_efuse_map_dav_be(struct rtw89_dev *rtwdev, u8 *map,
u32 dump_addr, u32 dump_size)
{
u32 addr;
u8 val8;
int err;
int ret;
for (addr = dump_addr; addr < dump_addr + dump_size; addr++) {
ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0x40,
FULL_BIT_MASK);
if (ret)
return ret;
ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_LOW_ADDR, addr & 0xff,
XTAL_SI_LOW_ADDR_MASK);
if (ret)
return ret;
ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, addr >> 8,
XTAL_SI_HIGH_ADDR_MASK);
if (ret)
return ret;
ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0,
XTAL_SI_MODE_SEL_MASK);
if (ret)
return ret;
ret = read_poll_timeout_atomic(rtw89_mac_read_xtal_si, err,
!err && (val8 & XTAL_SI_RDY),
1, 10000, false,
rtwdev, XTAL_SI_CTRL, &val8);
if (ret) {
rtw89_warn(rtwdev, "failed to read dav efuse\n");
return ret;
}
ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_READ_VAL, &val8);
if (ret)
return ret;
*map++ = val8;
}
return 0;
}
int rtw89_cnv_efuse_state_be(struct rtw89_dev *rtwdev, bool idle)
{
u32 val;
int ret = 0;
if (idle) {
rtw89_write32_set(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN);
} else {
rtw89_write32_clr(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN);
ret = read_poll_timeout(rtw89_read32_mask, val,
val == MAC_AX_SYS_ACT, 50, 5000,
false, rtwdev, R_BE_IC_PWR_STATE,
B_BE_WHOLE_SYS_PWR_STE_MASK);
if (ret)
rtw89_warn(rtwdev, "failed to convert efuse state\n");
}
return ret;
}
static int rtw89_dump_physical_efuse_map_be(struct rtw89_dev *rtwdev, u8 *map,
u32 dump_addr, u32 dump_size, bool dav)
{
int ret;
if (!map || dump_size == 0)
return 0;
rtw89_cnv_efuse_state_be(rtwdev, false);
if (dav) {
ret = rtw89_dump_physical_efuse_map_dav_be(rtwdev, map,
dump_addr, dump_size);
if (ret)
return ret;
rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map dav: ", map, dump_size);
} else {
ret = rtw89_dump_physical_efuse_map_ddv_be(rtwdev, map,
dump_addr, dump_size);
if (ret)
return ret;
rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map ddv: ", map, dump_size);
}
rtw89_cnv_efuse_state_be(rtwdev, true);
return 0;
}
#define EFUSE_HDR_CONST_MASK GENMASK(23, 20)
#define EFUSE_HDR_PAGE_MASK GENMASK(19, 17)
#define EFUSE_HDR_OFFSET_MASK GENMASK(16, 4)
#define EFUSE_HDR_OFFSET_DAV_MASK GENMASK(11, 4)
#define EFUSE_HDR_WORD_EN_MASK GENMASK(3, 0)
#define invalid_efuse_header_be(hdr1, hdr2, hdr3) \
((hdr1) == 0xff || (hdr2) == 0xff || (hdr3) == 0xff)
#define invalid_efuse_content_be(word_en, i) \
(((word_en) & BIT(i)) != 0x0)
#define get_efuse_blk_idx_be(hdr1, hdr2, hdr3) \
(((hdr1) << 16) | ((hdr2) << 8) | (hdr3))
#define block_idx_to_logical_idx_be(blk_idx, i) \
(((blk_idx) << 3) + ((i) << 1))
#define invalid_efuse_header_dav_be(hdr1, hdr2) \
((hdr1) == 0xff || (hdr2) == 0xff)
#define get_efuse_blk_idx_dav_be(hdr1, hdr2) \
(((hdr1) << 8) | (hdr2))
static int rtw89_eeprom_parser_be(struct rtw89_dev *rtwdev,
const u8 *phy_map, u32 phy_size, u8 *log_map,
const struct rtw89_efuse_block_cfg *efuse_block)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
enum rtw89_efuse_block blk_page, page;
u32 size = efuse_block->size;
u32 phy_idx, log_idx;
u32 hdr, page_offset;
u8 hdr1, hdr2, hdr3;
u8 i, val0, val1;
u32 min, max;
u16 blk_idx;
u8 word_en;
page = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_ID_MASK);
page_offset = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_SIZE_MASK);
min = ALIGN_DOWN(page_offset, 2);
max = ALIGN(page_offset + size, 2);
memset(log_map, 0xff, size);
phy_idx = chip->sec_ctrl_efuse_size;
do {
if (page == RTW89_EFUSE_BLOCK_ADIE) {
hdr1 = phy_map[phy_idx];
hdr2 = phy_map[phy_idx + 1];
if (invalid_efuse_header_dav_be(hdr1, hdr2))
break;
phy_idx += 2;
hdr = get_efuse_blk_idx_dav_be(hdr1, hdr2);
blk_page = RTW89_EFUSE_BLOCK_ADIE;
blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_DAV_MASK);
word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK);
} else {
hdr1 = phy_map[phy_idx];
hdr2 = phy_map[phy_idx + 1];
hdr3 = phy_map[phy_idx + 2];
if (invalid_efuse_header_be(hdr1, hdr2, hdr3))
break;
phy_idx += 3;
hdr = get_efuse_blk_idx_be(hdr1, hdr2, hdr3);
blk_page = u32_get_bits(hdr, EFUSE_HDR_PAGE_MASK);
blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_MASK);
word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK);
}
if (blk_idx >= RTW89_EFUSE_MAX_BLOCK_SIZE >> 3) {
rtw89_err(rtwdev, "[ERR]efuse idx:0x%X\n", phy_idx - 3);
rtw89_err(rtwdev, "[ERR]read hdr:0x%X\n", hdr);
return -EINVAL;
}
for (i = 0; i < 4; i++) {
if (invalid_efuse_content_be(word_en, i))
continue;
if (phy_idx >= phy_size - 1)
return -EINVAL;
log_idx = block_idx_to_logical_idx_be(blk_idx, i);
if (blk_page == page && log_idx >= min && log_idx < max) {
val0 = phy_map[phy_idx];
val1 = phy_map[phy_idx + 1];
if (log_idx == min && page_offset > min) {
log_map[log_idx - page_offset + 1] = val1;
} else if (log_idx + 2 == max &&
page_offset + size < max) {
log_map[log_idx - page_offset] = val0;
} else {
log_map[log_idx - page_offset] = val0;
log_map[log_idx - page_offset + 1] = val1;
}
}
phy_idx += 2;
}
} while (phy_idx < phy_size);
return 0;
}
static int rtw89_parse_logical_efuse_block_be(struct rtw89_dev *rtwdev,
const u8 *phy_map, u32 phy_size,
enum rtw89_efuse_block block)
{
const struct rtw89_chip_info *chip = rtwdev->chip;
const struct rtw89_efuse_block_cfg *efuse_block;
u8 *log_map;
int ret;
efuse_block = &chip->efuse_blocks[block];
log_map = kmalloc(efuse_block->size, GFP_KERNEL);
if (!log_map)
return -ENOMEM;
ret = rtw89_eeprom_parser_be(rtwdev, phy_map, phy_size, log_map, efuse_block);
if (ret) {
rtw89_warn(rtwdev, "failed to dump efuse logical block %d\n", block);
goto out_free;
}
rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, efuse_block->size);
ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map, block);
if (ret) {
rtw89_warn(rtwdev, "failed to read efuse map\n");
goto out_free;
}
out_free:
kfree(log_map);
return ret;
}
int rtw89_parse_efuse_map_be(struct rtw89_dev *rtwdev)
{
u32 phy_size = rtwdev->chip->physical_efuse_size;
u32 dav_phy_size = rtwdev->chip->dav_phy_efuse_size;
enum rtw89_efuse_block block;
u8 *phy_map = NULL;
u8 *dav_phy_map = NULL;
int ret;
if (rtw89_read16(rtwdev, R_BE_SYS_WL_EFUSE_CTRL) & B_BE_AUTOLOAD_SUS)
rtwdev->efuse.valid = true;
else
rtw89_warn(rtwdev, "failed to check efuse autoload\n");
phy_map = kmalloc(phy_size, GFP_KERNEL);
if (dav_phy_size)
dav_phy_map = kmalloc(dav_phy_size, GFP_KERNEL);
if (!phy_map || (dav_phy_size && !dav_phy_map)) {
ret = -ENOMEM;
goto out_free;
}
ret = rtw89_dump_physical_efuse_map_be(rtwdev, phy_map, 0, phy_size, false);
if (ret) {
rtw89_warn(rtwdev, "failed to dump efuse physical map\n");
goto out_free;
}
ret = rtw89_dump_physical_efuse_map_be(rtwdev, dav_phy_map, 0, dav_phy_size, true);
if (ret) {
rtw89_warn(rtwdev, "failed to dump efuse dav physical map\n");
goto out_free;
}
if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
block = RTW89_EFUSE_BLOCK_HCI_DIG_USB;
else
block = RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO;
ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size, block);
if (ret) {
rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n",
RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO);
goto out_free;
}
ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size,
RTW89_EFUSE_BLOCK_RF);
if (ret) {
rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n",
RTW89_EFUSE_BLOCK_RF);
goto out_free;
}
out_free:
kfree(dav_phy_map);
kfree(phy_map);
return ret;
}
int rtw89_parse_phycap_map_be(struct rtw89_dev *rtwdev)
{
u32 phycap_addr = rtwdev->chip->phycap_addr;
u32 phycap_size = rtwdev->chip->phycap_size;
u8 *phycap_map = NULL;
int ret = 0;
if (!phycap_size)
return 0;
phycap_map = kmalloc(phycap_size, GFP_KERNEL);
if (!phycap_map)
return -ENOMEM;
ret = rtw89_dump_physical_efuse_map_be(rtwdev, phycap_map,
phycap_addr, phycap_size, false);
if (ret) {
rtw89_warn(rtwdev, "failed to dump phycap map\n");
goto out_free;
}
ret = rtwdev->chip->ops->read_phycap(rtwdev, phycap_map);
if (ret) {
rtw89_warn(rtwdev, "failed to read phycap map\n");
goto out_free;
}
out_free:
kfree(phycap_map);
return ret;
}
static u16 get_sb_cryp_sel_idx(u16 sb_cryp_sel)
{
u8 low_bit, high_bit, cnt_zero = 0;
u8 idx, sel_form_v, sel_idx_v;
u16 sb_cryp_sel_v = 0x0;
sel_form_v = u16_get_bits(sb_cryp_sel, MASKBYTE0);
sel_idx_v = u16_get_bits(sb_cryp_sel, MASKBYTE1);
for (idx = 0; idx < 4; idx++) {
low_bit = !!(sel_form_v & BIT(idx));
high_bit = !!(sel_form_v & BIT(7 - idx));
if (low_bit != high_bit)
return U16_MAX;
if (low_bit)
continue;
cnt_zero++;
if (cnt_zero == 1)
sb_cryp_sel_v = idx * 16;
else if (cnt_zero > 1)
return U16_MAX;
}
low_bit = u8_get_bits(sel_idx_v, 0x0F);
high_bit = u8_get_bits(sel_idx_v, 0xF0);
if ((low_bit ^ high_bit) != 0xF)
return U16_MAX;
return sb_cryp_sel_v + low_bit;
}
int rtw89_efuse_read_fw_secure_be(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_secure *sec = &rtwdev->fw.sec;
u32 sec_addr = EFUSE_SEC_BE_START;
u32 sec_size = EFUSE_SEC_BE_SIZE;
u16 sb_cryp_sel, sb_cryp_sel_idx;
u8 sec_map[EFUSE_SEC_BE_SIZE];
u8 b1, b2;
int ret;
ret = rtw89_dump_physical_efuse_map_be(rtwdev, sec_map,
sec_addr, sec_size, false);
if (ret) {
rtw89_warn(rtwdev, "failed to dump secsel map\n");
return ret;
}
sb_cryp_sel = sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr] |
sec_map[EFUSE_SB_CRYP_SEL_ADDR - sec_addr + 1] << 8;
if (sb_cryp_sel == EFUSE_SB_CRYP_SEL_DEFAULT)
goto out;
sb_cryp_sel_idx = get_sb_cryp_sel_idx(sb_cryp_sel);
if (sb_cryp_sel_idx >= SB_SEL_MGN_MAX_SIZE) {
rtw89_warn(rtwdev, "invalid SB cryp sel idx %d\n", sb_cryp_sel_idx);
goto out;
}
sec->sb_sel_mgn = sb_sel_mgn[sb_cryp_sel_idx];
b1 = sec_map[EFUSE_EXTERNALPN_ADDR_BE - sec_addr];
b2 = sec_map[EFUSE_SERIALNUM_ADDR_BE - sec_addr];
ret = rtw89_efuse_recognize_mss_info_v1(rtwdev, b1, b2);
if (ret)
goto out;
sec->secure_boot = true;
out:
rtw89_debug(rtwdev, RTW89_DBG_FW,
"MSS secure_boot=%d dev_type=%d cust_idx=%d key_num=%d\n",
sec->secure_boot, sec->mss_dev_type, sec->mss_cust_idx,
sec->mss_key_num);
return 0;
}