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