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 */
24static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
25
26static void efuse_reg_ctrl(struct _adapter *adapter, 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(adapter, EFUSE_TEST + 3);
35		tmpu8 |= 0x80;
36		r8712_write8(adapter, 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(adapter, 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(adapter, EFUSE_TEST + 3);
46		tmpu8 &= 0x7F;
47		r8712_write8(adapter, EFUSE_TEST + 3, tmpu8);
48		/* Change Efuse Clock for write action to 500K */
49		r8712_write8(adapter, EFUSE_CLK_CTRL, 0x02);
50	}
51}
52
53/*
54 * Before write E-Fuse, this function must be called.
55 */
56u8 r8712_efuse_reg_init(struct _adapter *adapter)
57{
58	return true;
59}
60
61void r8712_efuse_reg_uninit(struct _adapter *adapter)
62{
63	efuse_reg_ctrl(adapter, false);
64}
65
66static u8 efuse_one_byte_read(struct _adapter *adapter, u16 addr, u8 *data)
67{
68	u8 tmpidx = 0, bResult;
69
70	/* -----------------e-fuse reg ctrl --------------------------------- */
71	r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
72	r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
73	       (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC));
74	r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */
75	/* wait for complete */
76	while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
77	       (tmpidx < 100))
78		tmpidx++;
79	if (tmpidx < 100) {
80		*data = r8712_read8(adapter, EFUSE_CTRL);
81		bResult = true;
82	} else {
83		*data = 0xff;
84		bResult = false;
85	}
86	return bResult;
87}
88
89static u8 efuse_one_byte_write(struct _adapter *adapter, u16 addr, u8 data)
90{
91	u8 tmpidx = 0, bResult;
92
93	/* -----------------e-fuse reg ctrl -------------------------------- */
94	r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
95	r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) |
96	       (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC));
97	r8712_write8(adapter, EFUSE_CTRL, data); /* data */
98	r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
99	/* wait for complete */
100	while ((0x80 &  r8712_read8(adapter, 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
110static u8 efuse_one_byte_rw(struct _adapter *adapter, u8 bRead, u16 addr,
111			    u8 *data)
112{
113	u8 tmpidx = 0, tmpv8 = 0, bResult;
114
115	/* -----------------e-fuse reg ctrl --------------------------------- */
116	r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */
117	tmpv8 = ((u8)((addr >> 8) & 0x03)) |
118		 (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC);
119	r8712_write8(adapter, EFUSE_CTRL + 2, tmpv8);
120	if (bRead) {
121		r8712_write8(adapter, EFUSE_CTRL + 3,  0x72); /* read cmd */
122		while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) &&
123		       (tmpidx < 100))
124			tmpidx++;
125		if (tmpidx < 100) {
126			*data = r8712_read8(adapter, EFUSE_CTRL);
127			bResult = true;
128		} else {
129			*data = 0;
130			bResult = false;
131		}
132	} else {
133		r8712_write8(adapter, EFUSE_CTRL, *data); /* data */
134		r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */
135		while ((0x80 & r8712_read8(adapter, 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
146static u8 efuse_is_empty(struct _adapter *adapter, 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(adapter, 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
162void r8712_efuse_change_max_size(struct _adapter *adapter)
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(adapter, 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
178int r8712_efuse_get_max_size(struct _adapter *adapter)
179{
180	return	efuse_available_max_size;
181}
182
183static 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
194static 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
209u16 r8712_efuse_get_current_size(struct _adapter *adapter)
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(adapter, efuse_addr, &efuse_data) &&
217	       (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
230u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, 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(adapter, 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(adapter, efuse_addr + 1 + tmpidx,
256								&efuse_data)) {
257						tmpdata[tmpidx] = efuse_data;
258					} else {
259						ret = false;
260					}
261				}
262				pgpacket_copy_data(hworden, tmpdata, data);
263			}
264			efuse_addr += 1 + (word_cnts * 2);
265		} else {
266			ret = false;
267			break;
268		}
269	}
270	return ret;
271}
272
273static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr)
274{
275	struct PGPKT_STRUCT pkt;
276	u8 offset, word_en, value;
277	u16 addr;
278	int i;
279	u8 ret = true;
280
281	pkt.offset = GET_EFUSE_OFFSET(header);
282	pkt.word_en = GET_EFUSE_WORD_EN(header);
283	addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
284	if (addr > efuse_available_max_size)
285		return false;
286	/* retrieve original data */
287	addr = 0;
288	while (addr < header_addr) {
289		if (!efuse_one_byte_read(adapter, addr++, &value)) {
290			ret = false;
291			break;
292		}
293		offset = GET_EFUSE_OFFSET(value);
294		word_en = GET_EFUSE_WORD_EN(value);
295		if (pkt.offset != offset) {
296			addr += calculate_word_cnts(word_en) * 2;
297			continue;
298		}
299		for (i = 0; i < PGPKG_MAX_WORDS; i++) {
300			if (!(BIT(i) & word_en))
301				continue;
302			if (BIT(i) & pkt.word_en) {
303				if (efuse_one_byte_read(adapter,
304							addr,
305							&value))
306					pkt.data[i * 2] = value;
307				else
308					return false;
309				if (efuse_one_byte_read(adapter,
310							addr + 1,
311							&value))
312					pkt.data[i * 2 + 1] = value;
313				else
314					return false;
315			}
316			addr += 2;
317		}
318	}
319	if (addr != header_addr)
320		return false;
321	addr++;
322	/* fill original data */
323	for (i = 0; i < PGPKG_MAX_WORDS; i++) {
324		if (BIT(i) & pkt.word_en) {
325			efuse_one_byte_write(adapter, addr, pkt.data[i * 2]);
326			efuse_one_byte_write(adapter, addr + 1,
327					     pkt.data[i * 2 + 1]);
328			/* additional check */
329			if (!efuse_one_byte_read(adapter, addr, &value)) {
330				ret = false;
331			} else if (pkt.data[i * 2] != value) {
332				ret = false;
333				if (value == 0xFF) /* write again */
334					efuse_one_byte_write(adapter, addr,
335							     pkt.data[i * 2]);
336			}
337			if (!efuse_one_byte_read(adapter, addr + 1, &value)) {
338				ret = false;
339			} else if (pkt.data[i * 2 + 1] != value) {
340				ret = false;
341				if (value == 0xFF) /* write again */
342					efuse_one_byte_write(adapter, addr + 1,
343							     pkt.data[i * 2 +
344								      1]);
345			}
346		}
347		addr += 2;
348	}
349	return ret;
350}
351
352u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset,
353			       const u8 word_en, const u8 *data)
354{
355	u8 pg_header = 0;
356	u16 efuse_addr = 0, curr_size = 0;
357	u8 efuse_data, target_word_cnts = 0;
358	int repeat_times;
359	int sub_repeat;
360	u8 bResult = true;
361
362	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
363	efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL);
364	if (efuse_data != 0x03)
365		return false;
366	pg_header = MAKE_EFUSE_HEADER(offset, word_en);
367	target_word_cnts = calculate_word_cnts(word_en);
368	repeat_times = 0;
369	efuse_addr = 0;
370	while (efuse_addr < efuse_available_max_size) {
371		curr_size = r8712_efuse_get_current_size(adapter);
372		if ((curr_size + 1 + target_word_cnts * 2) >
373		     efuse_available_max_size)
374			return false; /*target_word_cnts + pg header(1 byte)*/
375		efuse_addr = curr_size; /* current size is also the last addr*/
376		efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/
377		sub_repeat = 0;
378		/* check if what we read is what we write */
379		while (!efuse_one_byte_read(adapter, efuse_addr,
380					    &efuse_data)) {
381			if (++sub_repeat > _REPEAT_THRESHOLD_) {
382				bResult = false; /* continue to blind write */
383				break; /* continue to blind write */
384			}
385		}
386		if ((sub_repeat > _REPEAT_THRESHOLD_) ||
387		    (pg_header == efuse_data)) {
388			/* write header ok OR can't check header(creep) */
389			u8 i;
390
391			/* go to next address */
392			efuse_addr++;
393			for (i = 0; i < target_word_cnts * 2; i++) {
394				efuse_one_byte_write(adapter,
395						     efuse_addr + i,
396						     *(data + i));
397				if (!efuse_one_byte_read(adapter,
398							 efuse_addr + i,
399							 &efuse_data))
400					bResult = false;
401				else if (*(data + i) != efuse_data) /* fail */
402					bResult = false;
403			}
404			break;
405		}
406		/* write header fail */
407		bResult = false;
408		if (efuse_data == 0xFF)
409			return bResult; /* nothing damaged. */
410		/* call rescue procedure */
411		if (!fix_header(adapter, efuse_data, efuse_addr))
412			return false; /* rescue fail */
413
414		if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
415			break;
416		/* otherwise, take another risk... */
417	}
418	return bResult;
419}
420
421u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr,
422		      u16 cnts, u8 *data)
423{
424	int i;
425	u8 res = true;
426
427	if (start_addr > EFUSE_MAX_SIZE)
428		return false;
429	if (!bRead && ((start_addr + cnts) >
430	   efuse_available_max_size))
431		return false;
432	if (!bRead && !r8712_efuse_reg_init(adapter))
433		return false;
434	/* -----------------e-fuse one byte read / write ---------------------*/
435	for (i = 0; i < cnts; i++) {
436		if ((start_addr + i) > EFUSE_MAX_SIZE) {
437			res = false;
438			break;
439		}
440		res = efuse_one_byte_rw(adapter, bRead, start_addr + i,
441					data + i);
442		if (!bRead && !res)
443			break;
444	}
445	if (!bRead)
446		r8712_efuse_reg_uninit(adapter);
447	return res;
448}
449
450u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data)
451{
452	u8 offset, ret = true;
453	u8 pktdata[PGPKT_DATA_SIZE];
454	int i, idx;
455
456	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
457		return false;
458	if (efuse_is_empty(adapter, &offset) && offset) {
459		for (i = 0; i < cnts; i++)
460			data[i] = 0xFF;
461		return ret;
462	}
463	offset = (addr >> 3) & 0xF;
464	ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata);
465	i = addr & 0x7;	/* pktdata index */
466	idx = 0;	/* data index */
467
468	do {
469		for (; i < PGPKT_DATA_SIZE; i++) {
470			data[idx++] = pktdata[i];
471			if (idx == cnts)
472				return ret;
473		}
474		offset++;
475		if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
476			ret = false;
477		i = 0;
478	} while (1);
479	return ret;
480}
481
482u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts,
483			 u8 *data)
484{
485	u8 offset, word_en, empty;
486	u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
487	int i, j, idx;
488
489	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
490		return false;
491	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
492	empty = r8712_read8(adapter, EFUSE_CLK_CTRL);
493	if (empty != 0x03)
494		return false;
495	if (efuse_is_empty(adapter, &empty)) {
496		if (empty)
497			memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
498	} else {
499		return false;
500	}
501	offset = (addr >> 3) & 0xF;
502	if (!empty)
503		if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata))
504			return false;
505	word_en = 0xF;
506	memset(newdata, 0xFF, PGPKT_DATA_SIZE);
507	i = addr & 0x7;	/* pktdata index */
508	j = 0;		/* newdata index */
509	idx = 0;	/* data index */
510
511	if (i & 0x1) {
512		/*  odd start */
513		if (data[idx] != pktdata[i]) {
514			word_en &= ~BIT(i >> 1);
515			newdata[j++] = pktdata[i - 1];
516			newdata[j++] = data[idx];
517		}
518		i++;
519		idx++;
520	}
521	do {
522		for (; i < PGPKT_DATA_SIZE; i += 2) {
523			if ((cnts - idx) == 1) {
524				if (data[idx] != pktdata[i]) {
525					word_en &= ~BIT(i >> 1);
526					newdata[j++] = data[idx];
527					newdata[j++] = pktdata[1 + 1];
528				}
529				idx++;
530				break;
531			}
532
533			if ((data[idx] != pktdata[i]) || (data[idx + 1] !=
534			     pktdata[i + 1])) {
535				word_en &= ~BIT(i >> 1);
536				newdata[j++] = data[idx];
537				newdata[j++] = data[idx + 1];
538			}
539			idx += 2;
540
541			if (idx == cnts)
542				break;
543		}
544
545		if (word_en != 0xF)
546			if (!r8712_efuse_pg_packet_write(adapter, offset,
547							 word_en, newdata))
548				return false;
549		if (idx == cnts)
550			break;
551		offset++;
552		if (!empty)
553			if (!r8712_efuse_pg_packet_read(adapter, offset,
554							pktdata))
555				return false;
556		i = 0;
557		j = 0;
558		word_en = 0xF;
559		memset(newdata, 0xFF, PGPKT_DATA_SIZE);
560	} while (1);
561
562	return true;
563}
564