1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * rtl8712_efuse.c
4  *
5  * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved.
6  * Linux device driver for RTL8192SU
7  *
8  * Modifications for inclusion into the Linux staging tree are
9  * Copyright(c) 2010 Larry Finger. All rights reserved.
10  *
11  * Contact information:
12  * WLAN FAE <wlanfae@realtek.com>.
13  * Larry Finger <Larry.Finger@lwfinger.net>
14  *
15  ******************************************************************************/
16 
17 #define _RTL8712_EFUSE_C_
18 
19 #include "osdep_service.h"
20 #include "drv_types.h"
21 #include "rtl8712_efuse.h"
22 
23 /* reserve 3 bytes for HW stop read */
24 static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
25 
26 static void efuse_reg_ctrl(struct _adapter *padapter, u8 bPowerOn)
27 {
28 	u8 tmpu8 = 0;
29 
30 	if (bPowerOn) {
31 		/* -----------------e-fuse pwr & clk reg ctrl ---------------
32 		 * Enable LDOE25 Macro Block
33 		 */
34 		tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
35 		tmpu8 |= 0x80;
36 		r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
37 		msleep(20); /* for some platform , need some delay time */
38 		/* Change Efuse Clock for write action to 40MHZ */
39 		r8712_write8(padapter, EFUSE_CLK_CTRL, 0x03);
40 		msleep(20); /* for some platform , need some delay time */
41 	} else {
42 		/* -----------------e-fuse pwr & clk reg ctrl -----------------
43 		 * Disable LDOE25 Macro Block
44 		 */
45 		tmpu8 = r8712_read8(padapter, EFUSE_TEST + 3);
46 		tmpu8 &= 0x7F;
47 		r8712_write8(padapter, EFUSE_TEST + 3, tmpu8);
48 		/* Change Efuse Clock for write action to 500K */
49 		r8712_write8(padapter, EFUSE_CLK_CTRL, 0x02);
50 	}
51 }
52 
53 /*
54  * Before write E-Fuse, this function must be called.
55  */
56 u8 r8712_efuse_reg_init(struct _adapter *padapter)
57 {
58 	return true;
59 }
60 
61 void r8712_efuse_reg_uninit(struct _adapter *padapter)
62 {
63 	efuse_reg_ctrl(padapter, false);
64 }
65 
66 static u8 efuse_one_byte_read(struct _adapter *padapter, u16 addr, u8 *data)
67 {
68 	u8 tmpidx = 0, bResult;
69 
70 	/* -----------------e-fuse reg ctrl --------------------------------- */
71 	r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
72 	r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
73 	       (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
74 	r8712_write8(padapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
75 	/* wait for complete */
76 	while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
77 	       (tmpidx < 100))
78 		tmpidx++;
79 	if (tmpidx < 100) {
80 		*data = r8712_read8(padapter, EFUSE_CTRL);
81 		bResult = true;
82 	} else {
83 		*data = 0xff;
84 		bResult = false;
85 	}
86 	return bResult;
87 }
88 
89 static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data)
90 {
91 	u8 tmpidx = 0, bResult;
92 
93 	/* -----------------e-fuse reg ctrl -------------------------------- */
94 	r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
95 	r8712_write8(padapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
96 	       (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC));
97 	r8712_write8(padapter, EFUSE_CTRL, data); /* data */
98 	r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
99 	/* wait for complete */
100 	while ((0x80 &  r8712_read8(padapter, EFUSE_CTRL + 3)) &&
101 	       (tmpidx < 100))
102 		tmpidx++;
103 	if (tmpidx < 100)
104 		bResult = true;
105 	else
106 		bResult = false;
107 	return bResult;
108 }
109 
110 static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr,
111 			    u8 *data)
112 {
113 	u8 tmpidx = 0, tmpv8 = 0, bResult;
114 
115 	/* -----------------e-fuse reg ctrl --------------------------------- */
116 	r8712_write8(padapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
117 	tmpv8 = ((u8)((addr >> 8) & 0x03)) |
118 		 (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC);
119 	r8712_write8(padapter, EFUSE_CTRL + 2, tmpv8);
120 	if (bRead) {
121 		r8712_write8(padapter, EFUSE_CTRL + 3,  0x72); /* read cmd */
122 		while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
123 		       (tmpidx < 100))
124 			tmpidx++;
125 		if (tmpidx < 100) {
126 			*data = r8712_read8(padapter, EFUSE_CTRL);
127 			bResult = true;
128 		} else {
129 			*data = 0;
130 			bResult = false;
131 		}
132 	} else {
133 		r8712_write8(padapter, EFUSE_CTRL, *data); /* data */
134 		r8712_write8(padapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
135 		while ((0x80 & r8712_read8(padapter, EFUSE_CTRL + 3)) &&
136 		       (tmpidx < 100))
137 			tmpidx++;
138 		if (tmpidx < 100)
139 			bResult = true;
140 		else
141 			bResult = false;
142 	}
143 	return bResult;
144 }
145 
146 static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty)
147 {
148 	u8 value, ret = true;
149 
150 	/* read one byte to check if E-Fuse is empty */
151 	if (efuse_one_byte_rw(padapter, true, 0, &value)) {
152 		if (value == 0xFF)
153 			*empty = true;
154 		else
155 			*empty = false;
156 	} else {
157 		ret = false;
158 	}
159 	return ret;
160 }
161 
162 void r8712_efuse_change_max_size(struct _adapter *padapter)
163 {
164 	u16 pre_pg_data_saddr = 0x1FB;
165 	u16 i;
166 	u16 pre_pg_data_size = 5;
167 	u8 pre_pg_data[5];
168 
169 	for (i = 0; i < pre_pg_data_size; i++)
170 		efuse_one_byte_read(padapter, pre_pg_data_saddr + i,
171 				    &pre_pg_data[i]);
172 	if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
173 	    (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
174 	    (pre_pg_data[4] == 0x0C))
175 		efuse_available_max_size -= pre_pg_data_size;
176 }
177 
178 int r8712_efuse_get_max_size(struct _adapter *padapter)
179 {
180 	return	efuse_available_max_size;
181 }
182 
183 static u8 calculate_word_cnts(const u8 word_en)
184 {
185 	u8 word_cnts = 0;
186 	u8 word_idx;
187 
188 	for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
189 		if (!(word_en & BIT(word_idx)))
190 			word_cnts++; /* 0 : write enable */
191 	return word_cnts;
192 }
193 
194 static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
195 			       u8 *targetdata)
196 {
197 	u8 tmpindex = 0;
198 	u8 word_idx, byte_idx;
199 
200 	for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
201 		if (!(word_en & BIT(word_idx))) {
202 			byte_idx = word_idx * 2;
203 			targetdata[byte_idx] = sourdata[tmpindex++];
204 			targetdata[byte_idx + 1] = sourdata[tmpindex++];
205 		}
206 	}
207 }
208 
209 u16 r8712_efuse_get_current_size(struct _adapter *padapter)
210 {
211 	int bContinual = true;
212 	u16 efuse_addr = 0;
213 	u8 hworden = 0;
214 	u8 efuse_data, word_cnts = 0;
215 
216 	while (bContinual && efuse_one_byte_read(padapter, efuse_addr,
217 	       &efuse_data) && (efuse_addr < efuse_available_max_size)) {
218 		if (efuse_data != 0xFF) {
219 			hworden =  efuse_data & 0x0F;
220 			word_cnts = calculate_word_cnts(hworden);
221 			/* read next header */
222 			efuse_addr = efuse_addr + (word_cnts * 2) + 1;
223 		} else {
224 			bContinual = false;
225 		}
226 	}
227 	return efuse_addr;
228 }
229 
230 u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
231 {
232 	u8 hoffset = 0, hworden = 0, word_cnts = 0;
233 	u16 efuse_addr = 0;
234 	u8 efuse_data;
235 	u8 tmpidx = 0;
236 	u8 tmpdata[PGPKT_DATA_SIZE];
237 	u8 ret = true;
238 
239 	if (!data)
240 		return false;
241 	if (offset > 0x0f)
242 		return false;
243 	memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE);
244 	while (efuse_addr < efuse_available_max_size) {
245 		if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data)) {
246 			if (efuse_data == 0xFF)
247 				break;
248 			hoffset = (efuse_data >> 4) & 0x0F;
249 			hworden =  efuse_data & 0x0F;
250 			word_cnts = calculate_word_cnts(hworden);
251 			if (hoffset == offset) {
252 				memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
253 				for (tmpidx = 0; tmpidx < word_cnts * 2;
254 				     tmpidx++) {
255 					if (efuse_one_byte_read(padapter,
256 					    efuse_addr + 1 + tmpidx,
257 					    &efuse_data)) {
258 						tmpdata[tmpidx] = efuse_data;
259 					} else {
260 						ret = false;
261 					}
262 				}
263 				pgpacket_copy_data(hworden, tmpdata, data);
264 			}
265 			efuse_addr += 1 + (word_cnts * 2);
266 		} else {
267 			ret = false;
268 			break;
269 		}
270 	}
271 	return ret;
272 }
273 
274 static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
275 {
276 	struct PGPKT_STRUCT pkt;
277 	u8 offset, word_en, value;
278 	u16 addr;
279 	int i;
280 	u8 ret = true;
281 
282 	pkt.offset = GET_EFUSE_OFFSET(header);
283 	pkt.word_en = GET_EFUSE_WORD_EN(header);
284 	addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
285 	if (addr > efuse_available_max_size)
286 		return false;
287 	/* retrieve original data */
288 	addr = 0;
289 	while (addr < header_addr) {
290 		if (!efuse_one_byte_read(padapter, addr++, &value)) {
291 			ret = false;
292 			break;
293 		}
294 		offset = GET_EFUSE_OFFSET(value);
295 		word_en = GET_EFUSE_WORD_EN(value);
296 		if (pkt.offset != offset) {
297 			addr += calculate_word_cnts(word_en) * 2;
298 			continue;
299 		}
300 		for (i = 0; i < PGPKG_MAX_WORDS; i++) {
301 			if (BIT(i) & word_en) {
302 				if (BIT(i) & pkt.word_en) {
303 					if (efuse_one_byte_read(
304 							padapter, addr,
305 							&value))
306 						pkt.data[i * 2] = value;
307 					else
308 						return false;
309 					if (efuse_one_byte_read(
310 							padapter,
311 							addr + 1,
312 							&value))
313 						pkt.data[i * 2 + 1] =
314 							value;
315 					else
316 						return false;
317 				}
318 				addr += 2;
319 			}
320 		}
321 	}
322 	if (addr != header_addr)
323 		return false;
324 	addr++;
325 	/* fill original data */
326 	for (i = 0; i < PGPKG_MAX_WORDS; i++) {
327 		if (BIT(i) & pkt.word_en) {
328 			efuse_one_byte_write(padapter, addr, pkt.data[i * 2]);
329 			efuse_one_byte_write(padapter, addr + 1,
330 					     pkt.data[i * 2 + 1]);
331 			/* additional check */
332 			if (!efuse_one_byte_read(padapter, addr, &value)) {
333 				ret = false;
334 			} else if (pkt.data[i * 2] != value) {
335 				ret = false;
336 				if (value == 0xFF) /* write again */
337 					efuse_one_byte_write(padapter, addr,
338 							     pkt.data[i * 2]);
339 			}
340 			if (!efuse_one_byte_read(padapter, addr + 1, &value)) {
341 				ret = false;
342 			} else if (pkt.data[i * 2 + 1] != value) {
343 				ret = false;
344 				if (value == 0xFF) /* write again */
345 					efuse_one_byte_write(padapter, addr + 1,
346 							     pkt.data[i * 2 +
347 								      1]);
348 			}
349 		}
350 		addr += 2;
351 	}
352 	return ret;
353 }
354 
355 u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
356 			       const u8 word_en, const u8 *data)
357 {
358 	u8 pg_header = 0;
359 	u16 efuse_addr = 0, curr_size = 0;
360 	u8 efuse_data, target_word_cnts = 0;
361 	int repeat_times;
362 	int sub_repeat;
363 	u8 bResult = true;
364 
365 	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
366 	efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
367 	if (efuse_data != 0x03)
368 		return false;
369 	pg_header = MAKE_EFUSE_HEADER(offset, word_en);
370 	target_word_cnts = calculate_word_cnts(word_en);
371 	repeat_times = 0;
372 	efuse_addr = 0;
373 	while (efuse_addr < efuse_available_max_size) {
374 		curr_size = r8712_efuse_get_current_size(padapter);
375 		if ((curr_size + 1 + target_word_cnts * 2) >
376 		     efuse_available_max_size)
377 			return false; /*target_word_cnts + pg header(1 byte)*/
378 		efuse_addr = curr_size; /* current size is also the last addr*/
379 		efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
380 		sub_repeat = 0;
381 		/* check if what we read is what we write */
382 		while (!efuse_one_byte_read(padapter, efuse_addr,
383 					    &efuse_data)) {
384 			if (++sub_repeat > _REPEAT_THRESHOLD_) {
385 				bResult = false; /* continue to blind write */
386 				break; /* continue to blind write */
387 			}
388 		}
389 		if ((sub_repeat > _REPEAT_THRESHOLD_) ||
390 		    (pg_header == efuse_data)) {
391 			/* write header ok OR can't check header(creep) */
392 			u8 i;
393 
394 			/* go to next address */
395 			efuse_addr++;
396 			for (i = 0; i < target_word_cnts * 2; i++) {
397 				efuse_one_byte_write(padapter,
398 						     efuse_addr + i,
399 						     *(data + i));
400 				if (!efuse_one_byte_read(padapter,
401 							 efuse_addr + i,
402 							 &efuse_data))
403 					bResult = false;
404 				else if (*(data + i) != efuse_data) /* fail */
405 					bResult = false;
406 			}
407 			break;
408 		}
409 		/* write header fail */
410 		bResult = false;
411 		if (efuse_data == 0xFF)
412 			return bResult; /* nothing damaged. */
413 		/* call rescue procedure */
414 		if (!fix_header(padapter, efuse_data, efuse_addr))
415 			return false; /* rescue fail */
416 
417 		if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
418 			break;
419 		/* otherwise, take another risk... */
420 	}
421 	return bResult;
422 }
423 
424 u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
425 		      u16 cnts, u8 *data)
426 {
427 	int i;
428 	u8 res = true;
429 
430 	if (start_addr > EFUSE_MAX_SIZE)
431 		return false;
432 	if (!bRead && ((start_addr + cnts) >
433 	   efuse_available_max_size))
434 		return false;
435 	if (!bRead && !r8712_efuse_reg_init(padapter))
436 		return false;
437 	/* -----------------e-fuse one byte read / write ---------------------*/
438 	for (i = 0; i < cnts; i++) {
439 		if ((start_addr + i) > EFUSE_MAX_SIZE) {
440 			res = false;
441 			break;
442 		}
443 		res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
444 					data + i);
445 		if (!bRead && !res)
446 			break;
447 	}
448 	if (!bRead)
449 		r8712_efuse_reg_uninit(padapter);
450 	return res;
451 }
452 
453 u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
454 {
455 	u8 offset, ret = true;
456 	u8 pktdata[PGPKT_DATA_SIZE];
457 	int i, idx;
458 
459 	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
460 		return false;
461 	if (efuse_is_empty(padapter, &offset) && offset) {
462 		for (i = 0; i < cnts; i++)
463 			data[i] = 0xFF;
464 		return ret;
465 	}
466 	offset = (addr >> 3) & 0xF;
467 	ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
468 	i = addr & 0x7;	/* pktdata index */
469 	idx = 0;	/* data index */
470 
471 	do {
472 		for (; i < PGPKT_DATA_SIZE; i++) {
473 			data[idx++] = pktdata[i];
474 			if (idx == cnts)
475 				return ret;
476 		}
477 		offset++;
478 		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
479 			ret = false;
480 		i = 0;
481 	} while (1);
482 	return ret;
483 }
484 
485 u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
486 			 u8 *data)
487 {
488 	u8 offset, word_en, empty;
489 	u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
490 	int i, j, idx;
491 
492 	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
493 		return false;
494 	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
495 	empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
496 	if (empty != 0x03)
497 		return false;
498 	if (efuse_is_empty(padapter, &empty)) {
499 		if (empty)
500 			memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
501 	} else {
502 		return false;
503 	}
504 	offset = (addr >> 3) & 0xF;
505 	if (!empty)
506 		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
507 			return false;
508 	word_en = 0xF;
509 	memset(newdata, 0xFF, PGPKT_DATA_SIZE);
510 	i = addr & 0x7;	/* pktdata index */
511 	j = 0;		/* newdata index */
512 	idx = 0;	/* data index */
513 
514 	if (i & 0x1) {
515 		/*  odd start */
516 		if (data[idx] != pktdata[i]) {
517 			word_en &= ~BIT(i >> 1);
518 			newdata[j++] = pktdata[i - 1];
519 			newdata[j++] = data[idx];
520 		}
521 		i++;
522 		idx++;
523 	}
524 	do {
525 		for (; i < PGPKT_DATA_SIZE; i += 2) {
526 			if ((cnts - idx) == 1) {
527 				if (data[idx] != pktdata[i]) {
528 					word_en &= ~BIT(i >> 1);
529 					newdata[j++] = data[idx];
530 					newdata[j++] = pktdata[1 + 1];
531 				}
532 				idx++;
533 				break;
534 			}
535 
536 			if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
537 			     pktdata[i + 1])) {
538 				word_en &= ~BIT(i >> 1);
539 				newdata[j++] = data[idx];
540 				newdata[j++] = data[idx + 1];
541 			}
542 			idx += 2;
543 
544 			if (idx == cnts)
545 				break;
546 		}
547 
548 		if (word_en != 0xF)
549 			if (!r8712_efuse_pg_packet_write(padapter, offset,
550 							 word_en, newdata))
551 				return false;
552 		if (idx == cnts)
553 			break;
554 		offset++;
555 		if (!empty)
556 			if (!r8712_efuse_pg_packet_read(padapter, offset,
557 							pktdata))
558 				return false;
559 		i = 0;
560 		j = 0;
561 		word_en = 0xF;
562 		memset(newdata, 0xFF, PGPKT_DATA_SIZE);
563 	} while (1);
564 
565 	return true;
566 }
567