1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2023 Realtek Corporation
3 */
4
5 #include "debug.h"
6 #include "efuse.h"
7 #include "mac.h"
8 #include "reg.h"
9
rtw89_enable_efuse_pwr_cut_ddv_be(struct rtw89_dev * rtwdev)10 static void rtw89_enable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev)
11 {
12 const struct rtw89_chip_info *chip = rtwdev->chip;
13 struct rtw89_hal *hal = &rtwdev->hal;
14 bool aphy_patch = true;
15
16 if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV)
17 aphy_patch = false;
18
19 rtw89_write8_set(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK);
20
21 if (aphy_patch) {
22 rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S);
23 mdelay(1);
24 rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B);
25 rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE);
26 }
27
28 rtw89_write32_set(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST);
29 }
30
rtw89_disable_efuse_pwr_cut_ddv_be(struct rtw89_dev * rtwdev)31 static void rtw89_disable_efuse_pwr_cut_ddv_be(struct rtw89_dev *rtwdev)
32 {
33 const struct rtw89_chip_info *chip = rtwdev->chip;
34 struct rtw89_hal *hal = &rtwdev->hal;
35 bool aphy_patch = true;
36
37 if (chip->chip_id == RTL8922A && hal->cv == CHIP_CAV)
38 aphy_patch = false;
39
40 if (aphy_patch) {
41 rtw89_write16_set(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_ISO_EB2CORE);
42 rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_B);
43 mdelay(1);
44 rtw89_write16_clr(rtwdev, R_BE_SYS_ISO_CTRL, B_BE_PWC_EV2EF_S);
45 }
46
47 rtw89_write8_clr(rtwdev, R_BE_PMC_DBG_CTRL2, B_BE_SYSON_DIS_PMCR_BE_WRMSK);
48 rtw89_write32_clr(rtwdev, R_BE_EFUSE_CTRL_2_V1, B_BE_EF_BURST);
49 }
50
rtw89_dump_physical_efuse_map_ddv_be(struct rtw89_dev * rtwdev,u8 * map,u32 dump_addr,u32 dump_size)51 static int rtw89_dump_physical_efuse_map_ddv_be(struct rtw89_dev *rtwdev, u8 *map,
52 u32 dump_addr, u32 dump_size)
53 {
54 u32 efuse_ctl;
55 u32 addr;
56 u32 data;
57 int ret;
58
59 if (!IS_ALIGNED(dump_addr, 4) || !IS_ALIGNED(dump_size, 4)) {
60 rtw89_err(rtwdev, "Efuse addr 0x%x or size 0x%x not aligned\n",
61 dump_addr, dump_size);
62 return -EINVAL;
63 }
64
65 rtw89_enable_efuse_pwr_cut_ddv_be(rtwdev);
66
67 for (addr = dump_addr; addr < dump_addr + dump_size; addr += 4, map += 4) {
68 efuse_ctl = u32_encode_bits(addr, B_BE_EF_ADDR_MASK);
69 rtw89_write32(rtwdev, R_BE_EFUSE_CTRL, efuse_ctl & ~B_BE_EF_RDY);
70
71 ret = read_poll_timeout_atomic(rtw89_read32, efuse_ctl,
72 efuse_ctl & B_BE_EF_RDY, 1, 1000000,
73 true, rtwdev, R_BE_EFUSE_CTRL);
74 if (ret)
75 return -EBUSY;
76
77 data = rtw89_read32(rtwdev, R_BE_EFUSE_CTRL_1_V1);
78 *((__le32 *)map) = cpu_to_le32(data);
79 }
80
81 rtw89_disable_efuse_pwr_cut_ddv_be(rtwdev);
82
83 return 0;
84 }
85
rtw89_dump_physical_efuse_map_dav_be(struct rtw89_dev * rtwdev,u8 * map,u32 dump_addr,u32 dump_size)86 static int rtw89_dump_physical_efuse_map_dav_be(struct rtw89_dev *rtwdev, u8 *map,
87 u32 dump_addr, u32 dump_size)
88 {
89 u32 addr;
90 u8 val8;
91 int err;
92 int ret;
93
94 for (addr = dump_addr; addr < dump_addr + dump_size; addr++) {
95 ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0x40,
96 FULL_BIT_MASK);
97 if (ret)
98 return ret;
99 ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_LOW_ADDR, addr & 0xff,
100 XTAL_SI_LOW_ADDR_MASK);
101 if (ret)
102 return ret;
103 ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, addr >> 8,
104 XTAL_SI_HIGH_ADDR_MASK);
105 if (ret)
106 return ret;
107 ret = rtw89_mac_write_xtal_si(rtwdev, XTAL_SI_CTRL, 0,
108 XTAL_SI_MODE_SEL_MASK);
109 if (ret)
110 return ret;
111
112 ret = read_poll_timeout_atomic(rtw89_mac_read_xtal_si, err,
113 !err && (val8 & XTAL_SI_RDY),
114 1, 10000, false,
115 rtwdev, XTAL_SI_CTRL, &val8);
116 if (ret) {
117 rtw89_warn(rtwdev, "failed to read dav efuse\n");
118 return ret;
119 }
120
121 ret = rtw89_mac_read_xtal_si(rtwdev, XTAL_SI_READ_VAL, &val8);
122 if (ret)
123 return ret;
124 *map++ = val8;
125 }
126
127 return 0;
128 }
129
rtw89_cnv_efuse_state_be(struct rtw89_dev * rtwdev,bool idle)130 int rtw89_cnv_efuse_state_be(struct rtw89_dev *rtwdev, bool idle)
131 {
132 u32 val;
133 int ret = 0;
134
135 if (idle) {
136 rtw89_write32_set(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN);
137 } else {
138 rtw89_write32_clr(rtwdev, R_BE_WL_BT_PWR_CTRL, B_BE_BT_DISN_EN);
139
140 ret = read_poll_timeout(rtw89_read32_mask, val,
141 val == MAC_AX_SYS_ACT, 50, 5000,
142 false, rtwdev, R_BE_IC_PWR_STATE,
143 B_BE_WHOLE_SYS_PWR_STE_MASK);
144 if (ret)
145 rtw89_warn(rtwdev, "failed to convert efuse state\n");
146 }
147
148 return ret;
149 }
150
rtw89_dump_physical_efuse_map_be(struct rtw89_dev * rtwdev,u8 * map,u32 dump_addr,u32 dump_size,bool dav)151 static int rtw89_dump_physical_efuse_map_be(struct rtw89_dev *rtwdev, u8 *map,
152 u32 dump_addr, u32 dump_size, bool dav)
153 {
154 int ret;
155
156 if (!map || dump_size == 0)
157 return 0;
158
159 rtw89_cnv_efuse_state_be(rtwdev, false);
160
161 if (dav) {
162 ret = rtw89_dump_physical_efuse_map_dav_be(rtwdev, map,
163 dump_addr, dump_size);
164 if (ret)
165 return ret;
166
167 rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map dav: ", map, dump_size);
168 } else {
169 ret = rtw89_dump_physical_efuse_map_ddv_be(rtwdev, map,
170 dump_addr, dump_size);
171 if (ret)
172 return ret;
173
174 rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "phy_map ddv: ", map, dump_size);
175 }
176
177 rtw89_cnv_efuse_state_be(rtwdev, true);
178
179 return 0;
180 }
181
182 #define EFUSE_HDR_CONST_MASK GENMASK(23, 20)
183 #define EFUSE_HDR_PAGE_MASK GENMASK(19, 17)
184 #define EFUSE_HDR_OFFSET_MASK GENMASK(16, 4)
185 #define EFUSE_HDR_OFFSET_DAV_MASK GENMASK(11, 4)
186 #define EFUSE_HDR_WORD_EN_MASK GENMASK(3, 0)
187
188 #define invalid_efuse_header_be(hdr1, hdr2, hdr3) \
189 ((hdr1) == 0xff || (hdr2) == 0xff || (hdr3) == 0xff)
190 #define invalid_efuse_content_be(word_en, i) \
191 (((word_en) & BIT(i)) != 0x0)
192 #define get_efuse_blk_idx_be(hdr1, hdr2, hdr3) \
193 (((hdr1) << 16) | ((hdr2) << 8) | (hdr3))
194 #define block_idx_to_logical_idx_be(blk_idx, i) \
195 (((blk_idx) << 3) + ((i) << 1))
196
197 #define invalid_efuse_header_dav_be(hdr1, hdr2) \
198 ((hdr1) == 0xff || (hdr2) == 0xff)
199 #define get_efuse_blk_idx_dav_be(hdr1, hdr2) \
200 (((hdr1) << 8) | (hdr2))
201
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)202 static int rtw89_eeprom_parser_be(struct rtw89_dev *rtwdev,
203 const u8 *phy_map, u32 phy_size, u8 *log_map,
204 const struct rtw89_efuse_block_cfg *efuse_block)
205 {
206 const struct rtw89_chip_info *chip = rtwdev->chip;
207 enum rtw89_efuse_block blk_page, page;
208 u32 size = efuse_block->size;
209 u32 phy_idx, log_idx;
210 u32 hdr, page_offset;
211 u8 hdr1, hdr2, hdr3;
212 u8 i, val0, val1;
213 u32 min, max;
214 u16 blk_idx;
215 u8 word_en;
216
217 page = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_ID_MASK);
218 page_offset = u32_get_bits(efuse_block->offset, RTW89_EFUSE_BLOCK_SIZE_MASK);
219
220 min = ALIGN_DOWN(page_offset, 2);
221 max = ALIGN(page_offset + size, 2);
222
223 memset(log_map, 0xff, size);
224
225 phy_idx = chip->sec_ctrl_efuse_size;
226
227 do {
228 if (page == RTW89_EFUSE_BLOCK_ADIE) {
229 hdr1 = phy_map[phy_idx];
230 hdr2 = phy_map[phy_idx + 1];
231 if (invalid_efuse_header_dav_be(hdr1, hdr2))
232 break;
233
234 phy_idx += 2;
235
236 hdr = get_efuse_blk_idx_dav_be(hdr1, hdr2);
237
238 blk_page = RTW89_EFUSE_BLOCK_ADIE;
239 blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_DAV_MASK);
240 word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK);
241 } else {
242 hdr1 = phy_map[phy_idx];
243 hdr2 = phy_map[phy_idx + 1];
244 hdr3 = phy_map[phy_idx + 2];
245 if (invalid_efuse_header_be(hdr1, hdr2, hdr3))
246 break;
247
248 phy_idx += 3;
249
250 hdr = get_efuse_blk_idx_be(hdr1, hdr2, hdr3);
251
252 blk_page = u32_get_bits(hdr, EFUSE_HDR_PAGE_MASK);
253 blk_idx = u32_get_bits(hdr, EFUSE_HDR_OFFSET_MASK);
254 word_en = u32_get_bits(hdr, EFUSE_HDR_WORD_EN_MASK);
255 }
256
257 if (blk_idx >= RTW89_EFUSE_MAX_BLOCK_SIZE >> 3) {
258 rtw89_err(rtwdev, "[ERR]efuse idx:0x%X\n", phy_idx - 3);
259 rtw89_err(rtwdev, "[ERR]read hdr:0x%X\n", hdr);
260 return -EINVAL;
261 }
262
263 for (i = 0; i < 4; i++) {
264 if (invalid_efuse_content_be(word_en, i))
265 continue;
266
267 if (phy_idx >= phy_size - 1)
268 return -EINVAL;
269
270 log_idx = block_idx_to_logical_idx_be(blk_idx, i);
271
272 if (blk_page == page && log_idx >= min && log_idx < max) {
273 val0 = phy_map[phy_idx];
274 val1 = phy_map[phy_idx + 1];
275
276 if (log_idx == min && page_offset > min) {
277 log_map[log_idx - page_offset + 1] = val1;
278 } else if (log_idx + 2 == max &&
279 page_offset + size < max) {
280 log_map[log_idx - page_offset] = val0;
281 } else {
282 log_map[log_idx - page_offset] = val0;
283 log_map[log_idx - page_offset + 1] = val1;
284 }
285 }
286 phy_idx += 2;
287 }
288 } while (phy_idx < phy_size);
289
290 return 0;
291 }
292
rtw89_parse_logical_efuse_block_be(struct rtw89_dev * rtwdev,const u8 * phy_map,u32 phy_size,enum rtw89_efuse_block block)293 static int rtw89_parse_logical_efuse_block_be(struct rtw89_dev *rtwdev,
294 const u8 *phy_map, u32 phy_size,
295 enum rtw89_efuse_block block)
296 {
297 const struct rtw89_chip_info *chip = rtwdev->chip;
298 const struct rtw89_efuse_block_cfg *efuse_block;
299 u8 *log_map;
300 int ret;
301
302 efuse_block = &chip->efuse_blocks[block];
303
304 log_map = kmalloc(efuse_block->size, GFP_KERNEL);
305 if (!log_map)
306 return -ENOMEM;
307
308 ret = rtw89_eeprom_parser_be(rtwdev, phy_map, phy_size, log_map, efuse_block);
309 if (ret) {
310 rtw89_warn(rtwdev, "failed to dump efuse logical block %d\n", block);
311 goto out_free;
312 }
313
314 rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "log_map: ", log_map, efuse_block->size);
315
316 ret = rtwdev->chip->ops->read_efuse(rtwdev, log_map, block);
317 if (ret) {
318 rtw89_warn(rtwdev, "failed to read efuse map\n");
319 goto out_free;
320 }
321
322 out_free:
323 kfree(log_map);
324
325 return ret;
326 }
327
rtw89_parse_efuse_map_be(struct rtw89_dev * rtwdev)328 int rtw89_parse_efuse_map_be(struct rtw89_dev *rtwdev)
329 {
330 u32 phy_size = rtwdev->chip->physical_efuse_size;
331 u32 dav_phy_size = rtwdev->chip->dav_phy_efuse_size;
332 enum rtw89_efuse_block block;
333 u8 *phy_map = NULL;
334 u8 *dav_phy_map = NULL;
335 int ret;
336
337 if (rtw89_read16(rtwdev, R_BE_SYS_WL_EFUSE_CTRL) & B_BE_AUTOLOAD_SUS)
338 rtwdev->efuse.valid = true;
339 else
340 rtw89_warn(rtwdev, "failed to check efuse autoload\n");
341
342 phy_map = kmalloc(phy_size, GFP_KERNEL);
343 if (dav_phy_size)
344 dav_phy_map = kmalloc(dav_phy_size, GFP_KERNEL);
345
346 if (!phy_map || (dav_phy_size && !dav_phy_map)) {
347 ret = -ENOMEM;
348 goto out_free;
349 }
350
351 ret = rtw89_dump_physical_efuse_map_be(rtwdev, phy_map, 0, phy_size, false);
352 if (ret) {
353 rtw89_warn(rtwdev, "failed to dump efuse physical map\n");
354 goto out_free;
355 }
356 ret = rtw89_dump_physical_efuse_map_be(rtwdev, dav_phy_map, 0, dav_phy_size, true);
357 if (ret) {
358 rtw89_warn(rtwdev, "failed to dump efuse dav physical map\n");
359 goto out_free;
360 }
361
362 if (rtwdev->hci.type == RTW89_HCI_TYPE_USB)
363 block = RTW89_EFUSE_BLOCK_HCI_DIG_USB;
364 else
365 block = RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO;
366
367 ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size, block);
368 if (ret) {
369 rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n",
370 RTW89_EFUSE_BLOCK_HCI_DIG_PCIE_SDIO);
371 goto out_free;
372 }
373
374 ret = rtw89_parse_logical_efuse_block_be(rtwdev, phy_map, phy_size,
375 RTW89_EFUSE_BLOCK_RF);
376 if (ret) {
377 rtw89_warn(rtwdev, "failed to parse efuse logic block %d\n",
378 RTW89_EFUSE_BLOCK_RF);
379 goto out_free;
380 }
381
382 out_free:
383 kfree(dav_phy_map);
384 kfree(phy_map);
385
386 return ret;
387 }
388
rtw89_parse_phycap_map_be(struct rtw89_dev * rtwdev)389 int rtw89_parse_phycap_map_be(struct rtw89_dev *rtwdev)
390 {
391 u32 phycap_addr = rtwdev->chip->phycap_addr;
392 u32 phycap_size = rtwdev->chip->phycap_size;
393 u8 *phycap_map = NULL;
394 int ret = 0;
395
396 if (!phycap_size)
397 return 0;
398
399 phycap_map = kmalloc(phycap_size, GFP_KERNEL);
400 if (!phycap_map)
401 return -ENOMEM;
402
403 ret = rtw89_dump_physical_efuse_map_be(rtwdev, phycap_map,
404 phycap_addr, phycap_size, false);
405 if (ret) {
406 rtw89_warn(rtwdev, "failed to dump phycap map\n");
407 goto out_free;
408 }
409
410 ret = rtwdev->chip->ops->read_phycap(rtwdev, phycap_map);
411 if (ret) {
412 rtw89_warn(rtwdev, "failed to read phycap map\n");
413 goto out_free;
414 }
415
416 out_free:
417 kfree(phycap_map);
418
419 return ret;
420 }
421