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 (true == 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)) && (tmpidx < 100))
89		tmpidx++;
90	if (tmpidx < 100) {
91		*data = r8712_read8(padapter, EFUSE_CTRL);
92		bResult = true;
93	} else {
94		*data = 0xff;
95		bResult = false;
96	}
97	return bResult;
98}
99
100static u8 efuse_one_byte_write(struct _adapter *padapter, u16 addr, u8 data)
101{
102	u8 tmpidx = 0, bResult;
103
104	/* -----------------e-fuse reg ctrl -------------------------------- */
105	r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */
106	r8712_write8(padapter, EFUSE_CTRL+2, ((u8)((addr>>8)&0x03)) |
107	       (r8712_read8(padapter, EFUSE_CTRL+2)&0xFC));
108	r8712_write8(padapter, EFUSE_CTRL, data); /* data */
109	r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */
110	/* wait for complete */
111	while ((0x80 &  r8712_read8(padapter, EFUSE_CTRL+3)) && (tmpidx < 100))
112		tmpidx++;
113	if (tmpidx < 100)
114		bResult = true;
115	else
116		bResult = false;
117	return bResult;
118}
119
120static u8 efuse_one_byte_rw(struct _adapter *padapter, u8 bRead, u16 addr,
121			    u8 *data)
122{
123	u8 tmpidx = 0, tmpv8 = 0, bResult;
124
125	/* -----------------e-fuse reg ctrl --------------------------------- */
126	r8712_write8(padapter, EFUSE_CTRL+1, (u8)(addr&0xFF)); /* address */
127	tmpv8 = ((u8)((addr >> 8) & 0x03)) |
128		 (r8712_read8(padapter, EFUSE_CTRL + 2) & 0xFC);
129	r8712_write8(padapter, EFUSE_CTRL+2, tmpv8);
130	if (true == bRead) {
131		r8712_write8(padapter, EFUSE_CTRL+3,  0x72); /* read cmd */
132		while (!(0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) &&
133		       (tmpidx < 100))
134			tmpidx++;
135		if (tmpidx < 100) {
136			*data = r8712_read8(padapter, EFUSE_CTRL);
137			bResult = true;
138		} else {
139			*data = 0;
140			bResult = false;
141		}
142	} else {
143		r8712_write8(padapter, EFUSE_CTRL, *data); /* data */
144		r8712_write8(padapter, EFUSE_CTRL+3, 0xF2); /* write cmd */
145		while ((0x80 & r8712_read8(padapter, EFUSE_CTRL+3)) &&
146		       (tmpidx < 100))
147			tmpidx++;
148		if (tmpidx < 100)
149			bResult = true;
150		else
151			bResult = false;
152	}
153	return bResult;
154}
155
156static u8 efuse_is_empty(struct _adapter *padapter, u8 *empty)
157{
158	u8 value, ret = true;
159
160	/* read one byte to check if E-Fuse is empty */
161	if (efuse_one_byte_rw(padapter, true, 0, &value) == true) {
162		if (0xFF == value)
163			*empty = true;
164		else
165			*empty = false;
166	} else
167		ret = false;
168	return ret;
169}
170
171void r8712_efuse_change_max_size(struct _adapter *padapter)
172{
173	u16 pre_pg_data_saddr = 0x1FB;
174	u16 i;
175	u16 pre_pg_data_size = 5;
176	u8 pre_pg_data[5];
177
178	for (i = 0; i < pre_pg_data_size; i++)
179		efuse_one_byte_read(padapter, pre_pg_data_saddr + i,
180				    &pre_pg_data[i]);
181	if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) &&
182	    (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) &&
183	    (pre_pg_data[4] == 0x0C))
184		efuse_available_max_size -= pre_pg_data_size;
185}
186
187int r8712_efuse_get_max_size(struct _adapter *padapter)
188{
189	return	efuse_available_max_size;
190}
191
192static u8 calculate_word_cnts(const u8 word_en)
193{
194	u8 word_cnts = 0;
195	u8 word_idx;
196
197	for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++)
198		if (!(word_en & BIT(word_idx)))
199			word_cnts++; /* 0 : write enable */
200	return word_cnts;
201}
202
203static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata,
204			       u8 *targetdata)
205{
206	u8 tmpindex = 0;
207	u8 word_idx, byte_idx;
208
209	for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) {
210		if (!(word_en&BIT(word_idx))) {
211			byte_idx = word_idx * 2;
212			targetdata[byte_idx] = sourdata[tmpindex++];
213			targetdata[byte_idx + 1] = sourdata[tmpindex++];
214		}
215	}
216}
217
218u16 r8712_efuse_get_current_size(struct _adapter *padapter)
219{
220	int bContinual = true;
221	u16 efuse_addr = 0;
222	u8 hworden = 0;
223	u8 efuse_data, word_cnts = 0;
224
225	while (bContinual && efuse_one_byte_read(padapter, efuse_addr,
226	       &efuse_data) && (efuse_addr < efuse_available_max_size)) {
227		if (efuse_data != 0xFF) {
228			hworden =  efuse_data & 0x0F;
229			word_cnts = calculate_word_cnts(hworden);
230			/* read next header */
231			efuse_addr = efuse_addr + (word_cnts * 2) + 1;
232		} else
233			bContinual = false;
234	}
235	return efuse_addr;
236}
237
238u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
239{
240	u8 hoffset = 0, hworden = 0, word_cnts = 0;
241	u16 efuse_addr = 0;
242	u8 efuse_data;
243	u8 tmpidx = 0;
244	u8 tmpdata[PGPKT_DATA_SIZE];
245	u8 ret = true;
246
247	if (data == NULL)
248		return false;
249	if (offset > 0x0f)
250		return false;
251	memset(data, 0xFF, sizeof(u8)*PGPKT_DATA_SIZE);
252	while (efuse_addr < efuse_available_max_size) {
253		if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data) ==
254		    true) {
255			if (efuse_data == 0xFF)
256				break;
257			hoffset = (efuse_data >> 4) & 0x0F;
258			hworden =  efuse_data & 0x0F;
259			word_cnts = calculate_word_cnts(hworden);
260			if (hoffset == offset) {
261				memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
262				for (tmpidx = 0; tmpidx < word_cnts * 2;
263				     tmpidx++) {
264					if (efuse_one_byte_read(padapter,
265					    efuse_addr+1+tmpidx, &efuse_data) ==
266					     true) {
267						tmpdata[tmpidx] = efuse_data;
268					} else
269						ret = false;
270				}
271				pgpacket_copy_data(hworden, tmpdata, data);
272			}
273			efuse_addr += 1 + (word_cnts*2);
274		} else {
275			ret = false;
276			break;
277		}
278	}
279	return ret;
280}
281
282static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
283{
284	struct PGPKT_STRUCT pkt;
285	u8 offset, word_en, value;
286	u16 addr;
287	int i;
288	u8 ret = true;
289
290	pkt.offset = GET_EFUSE_OFFSET(header);
291	pkt.word_en = GET_EFUSE_WORD_EN(header);
292	addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
293	if (addr > efuse_available_max_size)
294		return false;
295	/* retrieve original data */
296	addr = 0;
297	while (addr < header_addr) {
298		if (efuse_one_byte_read(padapter, addr++, &value) == false) {
299			ret = false;
300			break;
301		}
302		offset = GET_EFUSE_OFFSET(value);
303		word_en = GET_EFUSE_WORD_EN(value);
304		if (pkt.offset != offset) {
305			addr += calculate_word_cnts(word_en)*2;
306			continue;
307		}
308		for (i = 0; i < PGPKG_MAX_WORDS; i++) {
309			if (BIT(i) & word_en) {
310				if (BIT(i) & pkt.word_en) {
311					if (efuse_one_byte_read(
312							padapter, addr,
313							&value) == true)
314						pkt.data[i*2] = value;
315					else
316						return false;
317					if (efuse_one_byte_read(
318							padapter,
319							addr + 1,
320							&value) == true)
321						pkt.data[i*2 + 1] =
322							value;
323					else
324						return false;
325				}
326				addr += 2;
327			}
328		}
329	}
330	if (addr != header_addr)
331		return false;
332	addr++;
333	/* fill original data */
334	for (i = 0; i < PGPKG_MAX_WORDS; i++) {
335		if (BIT(i) & pkt.word_en) {
336			efuse_one_byte_write(padapter, addr, pkt.data[i*2]);
337			efuse_one_byte_write(padapter, addr+1,
338					pkt.data[i*2 + 1]);
339			/* additional check */
340			if (efuse_one_byte_read(padapter, addr, &value)
341				== false)
342				ret = false;
343			else if (pkt.data[i*2] != value) {
344				ret = false;
345				if (0xFF == value) /* write again */
346					efuse_one_byte_write(padapter, addr,
347							pkt.data[i * 2]);
348			}
349			if (efuse_one_byte_read(padapter, addr+1, &value) ==
350				false)
351				ret = false;
352			else if (pkt.data[i*2 + 1] != value) {
353				ret = false;
354				if (0xFF == value) /* write again */
355					efuse_one_byte_write(padapter, addr+1,
356							pkt.data[i*2 + 1]);
357			}
358		}
359		addr += 2;
360	}
361	return ret;
362}
363
364u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
365			 const u8 word_en, const u8 *data)
366{
367	u8 pg_header = 0;
368	u16 efuse_addr = 0, curr_size = 0;
369	u8 efuse_data, target_word_cnts = 0;
370	static int repeat_times;
371	int sub_repeat;
372	u8 bResult = true;
373
374	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
375	efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
376	if (efuse_data != 0x03)
377		return false;
378	pg_header = MAKE_EFUSE_HEADER(offset, word_en);
379	target_word_cnts = calculate_word_cnts(word_en);
380	repeat_times = 0;
381	efuse_addr = 0;
382	while (efuse_addr < efuse_available_max_size) {
383		curr_size = r8712_efuse_get_current_size(padapter);
384		if ((curr_size + 1 + target_word_cnts * 2) >
385		     efuse_available_max_size)
386			return false; /*target_word_cnts + pg header(1 byte)*/
387		efuse_addr = curr_size; /* current size is also the last addr*/
388		efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
389		sub_repeat = 0;
390		/* check if what we read is what we write */
391		while (efuse_one_byte_read(padapter, efuse_addr,
392					   &efuse_data) == false) {
393			if (++sub_repeat > _REPEAT_THRESHOLD_) {
394				bResult = false; /* continue to blind write */
395				break; /* continue to blind write */
396			}
397		}
398		if ((sub_repeat > _REPEAT_THRESHOLD_) ||
399		    (pg_header == efuse_data)) {
400			/* write header ok OR can't check header(creep) */
401			u8 i;
402
403			/* go to next address */
404			efuse_addr++;
405			for (i = 0; i < target_word_cnts*2; i++) {
406				efuse_one_byte_write(padapter,
407						     efuse_addr + i,
408						     *(data + i));
409				if (efuse_one_byte_read(padapter,
410				    efuse_addr + i, &efuse_data) == false)
411					bResult = false;
412				else if (*(data+i) != efuse_data) /* fail */
413					bResult = false;
414			}
415			break;
416		}
417		/* write header fail */
418		bResult = false;
419		if (0xFF == efuse_data)
420			return bResult; /* nothing damaged. */
421		/* call rescue procedure */
422		if (!fix_header(padapter, efuse_data, efuse_addr))
423			return false; /* rescue fail */
424
425		if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
426			break;
427		/* otherwise, take another risk... */
428	}
429	return bResult;
430}
431
432u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
433		      u16 cnts, u8 *data)
434{
435	int i;
436	u8 res = true;
437
438	if (start_addr > EFUSE_MAX_SIZE)
439		return false;
440	if ((bRead == false) && ((start_addr + cnts) >
441	   efuse_available_max_size))
442		return false;
443	if ((false == bRead) && (r8712_efuse_reg_init(padapter) == false))
444		return false;
445	/* -----------------e-fuse one byte read / write ---------------------*/
446	for (i = 0; i < cnts; i++) {
447		if ((start_addr + i) > EFUSE_MAX_SIZE) {
448			res = false;
449			break;
450		}
451		res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
452		      data + i);
453		if ((false == bRead) && (false == res))
454			break;
455	}
456	if (false == bRead)
457		r8712_efuse_reg_uninit(padapter);
458	return res;
459}
460
461u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
462{
463	u8 offset, ret = true;
464	u8 pktdata[PGPKT_DATA_SIZE];
465	int i, idx;
466
467	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
468		return false;
469	if ((efuse_is_empty(padapter, &offset) == true) && (offset ==
470	     true)) {
471		for (i = 0; i < cnts; i++)
472			data[i] = 0xFF;
473		return ret;
474	}
475	offset = (addr >> 3) & 0xF;
476	ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
477	i = addr & 0x7;	/* pktdata index */
478	idx = 0;	/* data index */
479
480	do {
481		for (; i < PGPKT_DATA_SIZE; i++) {
482			data[idx++] = pktdata[i];
483			if (idx == cnts)
484				return ret;
485		}
486		offset++;
487		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
488			ret = false;
489		i = 0;
490	} while (1);
491	return ret;
492}
493
494u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
495			 u8 *data)
496{
497	u8 offset, word_en, empty;
498	u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
499	int i, j, idx;
500
501	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
502		return false;
503	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
504	empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
505	if (empty != 0x03)
506		return false;
507	if (efuse_is_empty(padapter, &empty) == true) {
508		if (true == empty)
509			memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
510	} else
511		return false;
512	offset = (addr >> 3) & 0xF;
513	if (empty == false)
514		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
515			return false;
516	word_en = 0xF;
517	memset(newdata, 0xFF, PGPKT_DATA_SIZE);
518	i = addr & 0x7;	/* pktdata index */
519	j = 0;		/* newdata index */
520	idx = 0;	/* data index */
521
522	if (i & 0x1) {
523		/*  odd start */
524		if (data[idx] != pktdata[i]) {
525			word_en &= ~BIT(i >> 1);
526			newdata[j++] = pktdata[i - 1];
527			newdata[j++] = data[idx];
528		}
529		i++;
530		idx++;
531	}
532	do {
533		for (; i < PGPKT_DATA_SIZE; i += 2) {
534			if ((cnts - idx) == 1) {
535				if (data[idx] != pktdata[i]) {
536					word_en &= ~BIT(i >> 1);
537					newdata[j++] = data[idx];
538					newdata[j++] = pktdata[1 + 1];
539				}
540				idx++;
541				break;
542			}
543
544			if ((data[idx] != pktdata[i]) || (data[idx+1] !=
545			     pktdata[i+1])) {
546				word_en &= ~BIT(i >> 1);
547				newdata[j++] = data[idx];
548				newdata[j++] = data[idx + 1];
549			}
550			idx += 2;
551
552			if (idx == cnts)
553				break;
554		}
555
556		if (word_en != 0xF)
557			if (r8712_efuse_pg_packet_write(padapter, offset,
558			    word_en, newdata) == false)
559				return false;
560		if (idx == cnts)
561			break;
562		offset++;
563		if (empty == false)
564			if (!r8712_efuse_pg_packet_read(padapter, offset,
565			    pktdata))
566				return false;
567		i = 0;
568		j = 0;
569		word_en = 0xF;
570		memset(newdata, 0xFF, PGPKT_DATA_SIZE);
571	} while (1);
572
573	return true;
574}
575