linux/drivers/net/wireless/realtek/rtw89/efuse.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/* Copyright(c) 2019-2020  Realtek Corporation
   3 */
   4
   5#include "debug.h"
   6#include "efuse.h"
   7#include "reg.h"
   8
   9enum rtw89_efuse_bank {
  10        RTW89_EFUSE_BANK_WIFI,
  11        RTW89_EFUSE_BANK_BT,
  12};
  13
  14static int rtw89_switch_efuse_bank(struct rtw89_dev *rtwdev,
  15                                   enum rtw89_efuse_bank bank)
  16{
  17        u8 val;
  18
  19        val = rtw89_read32_mask(rtwdev, R_AX_EFUSE_CTRL_1,
  20                                B_AX_EF_CELL_SEL_MASK);
  21        if (bank == val)
  22                return 0;
  23
  24        rtw89_write32_mask(rtwdev, R_AX_EFUSE_CTRL_1, B_AX_EF_CELL_SEL_MASK,
  25                           bank);
  26
  27        val = rtw89_read32_mask(rtwdev, R_AX_EFUSE_CTRL_1,
  28                                B_AX_EF_CELL_SEL_MASK);
  29        if (bank == val)
  30                return 0;
  31
  32        return -EBUSY;
  33}
  34
  35static int rtw89_dump_physical_efuse_map(struct rtw89_dev *rtwdev, u8 *map,
  36                                         u32 dump_addr, u32 dump_size)
  37{
  38        u32 efuse_ctl;
  39        u32 addr;
  40        int ret;
  41
  42        rtw89_switch_efuse_bank(rtwdev, RTW89_EFUSE_BANK_WIFI);
  43
  44        for (addr = dump_addr; addr < dump_addr + dump_size; addr++) {
  45                efuse_ctl = u32_encode_bits(addr, B_AX_EF_ADDR_MASK);
  46                rtw89_write32(rtwdev, R_AX_EFUSE_CTRL, efuse_ctl & ~B_AX_EF_RDY);
  47
  48                ret = read_poll_timeout_atomic(rtw89_read32, efuse_ctl,
  49                                               efuse_ctl & B_AX_EF_RDY, 1, 1000000,
  50                                               true, rtwdev, R_AX_EFUSE_CTRL);
  51                if (ret)
  52                        return -EBUSY;
  53
  54                *map++ = (u8)(efuse_ctl & 0xff);
  55        }
  56
  57        return 0;
  58}
  59
  60#define invalid_efuse_header(hdr1, hdr2) \
  61        ((hdr1) == 0xff || (hdr2) == 0xff)
  62#define invalid_efuse_content(word_en, i) \
  63        (((word_en) & BIT(i)) != 0x0)
  64#define get_efuse_blk_idx(hdr1, hdr2) \
  65        ((((hdr2) & 0xf0) >> 4) | (((hdr1) & 0x0f) << 4))
  66#define block_idx_to_logical_idx(blk_idx, i) \
  67        (((blk_idx) << 3) + ((i) << 1))
  68static int rtw89_dump_logical_efuse_map(struct rtw89_dev *rtwdev, u8 *phy_map,
  69                                        u8 *log_map)
  70{
  71        u32 physical_size = rtwdev->chip->physical_efuse_size;
  72        u32 logical_size = rtwdev->chip->logical_efuse_size;
  73        u8 sec_ctrl_size = rtwdev->chip->sec_ctrl_efuse_size;
  74        u32 phy_idx = sec_ctrl_size;
  75        u32 log_idx;
  76        u8 hdr1, hdr2;
  77        u8 blk_idx;
  78        u8 word_en;
  79        int i;
  80
  81        while (phy_idx < physical_size - sec_ctrl_size) {
  82                hdr1 = phy_map[phy_idx];
  83                hdr2 = phy_map[phy_idx + 1];
  84                if (invalid_efuse_header(hdr1, hdr2))
  85                        break;
  86
  87                blk_idx = get_efuse_blk_idx(hdr1, hdr2);
  88                word_en = hdr2 & 0xf;
  89                phy_idx += 2;
  90
  91                for (i = 0; i < 4; i++) {
  92                        if (invalid_efuse_content(word_en, i))
  93                                continue;
  94
  95                        log_idx = block_idx_to_logical_idx(blk_idx, i);
  96                        if (phy_idx + 1 > physical_size - sec_ctrl_size - 1 ||
  97                            log_idx + 1 > logical_size)
  98                                return -EINVAL;
  99
 100                        log_map[log_idx] = phy_map[phy_idx];
 101                        log_map[log_idx + 1] = phy_map[phy_idx + 1];
 102                        phy_idx += 2;
 103                }
 104        }
 105        return 0;
 106}
 107
 108int rtw89_parse_efuse_map(struct rtw89_dev *rtwdev)
 109{
 110        u32 phy_size = rtwdev->chip->physical_efuse_size;
 111        u32 log_size = rtwdev->chip->logical_efuse_size;
 112        u8 *phy_map = NULL;
 113        u8 *log_map = NULL;
 114        int ret;
 115
 116        if (rtw89_read16(rtwdev, R_AX_SYS_WL_EFUSE_CTRL) & B_AX_AUTOLOAD_SUS)
 117                rtwdev->efuse.valid = true;
 118        else
 119                rtw89_warn(rtwdev, "failed to check efuse autoload\n");
 120
 121        phy_map = kmalloc(phy_size, GFP_KERNEL);
 122        log_map = kmalloc(log_size, GFP_KERNEL);
 123
 124        if (!phy_map || !log_map) {
 125                ret = -ENOMEM;
 126                goto out_free;
 127        }
 128
 129        ret = rtw89_dump_physical_efuse_map(rtwdev, phy_map, 0, phy_size);
 130        if (ret) {
 131                rtw89_warn(rtwdev, "failed to dump efuse physical map\n");
 132                goto out_free;
 133        }
 134
 135        memset(log_map, 0xff, log_size);
 136        ret = rtw89_dump_logical_efuse_map(rtwdev, phy_map, log_map);
 137        if (ret) {
 138                rtw89_warn(rtwdev, "failed to dump efuse logical map\n");
 139                goto out_free;
 140        }
 141
 142        rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, log_size);
 143
 144        ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map);
 145        if (ret) {
 146                rtw89_warn(rtwdev, "failed to read efuse map\n");
 147                goto out_free;
 148        }
 149
 150out_free:
 151        kfree(log_map);
 152        kfree(phy_map);
 153
 154        return ret;
 155}
 156
 157int rtw89_parse_phycap_map(struct rtw89_dev *rtwdev)
 158{
 159        u32 phycap_addr = rtwdev->chip->phycap_addr;
 160        u32 phycap_size = rtwdev->chip->phycap_size;
 161        u8 *phycap_map = NULL;
 162        int ret = 0;
 163
 164        if (!phycap_size)
 165                return 0;
 166
 167        phycap_map = kmalloc(phycap_size, GFP_KERNEL);
 168        if (!phycap_map)
 169                return -ENOMEM;
 170
 171        ret = rtw89_dump_physical_efuse_map(rtwdev, phycap_map,
 172                                            phycap_addr, phycap_size);
 173        if (ret) {
 174                rtw89_warn(rtwdev, "failed to dump phycap map\n");
 175                goto out_free;
 176        }
 177
 178        ret = rtwdev->chip->ops->read_phycap(rtwdev, phycap_map);
 179        if (ret) {
 180                rtw89_warn(rtwdev, "failed to read phycap map\n");
 181                goto out_free;
 182        }
 183
 184out_free:
 185        kfree(phycap_map);
 186
 187        return ret;
 188}
 189