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 */
36 static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/;
37 
efuse_reg_ctrl(struct _adapter * padapter,u8 bPowerOn)38 static 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  */
r8712_efuse_reg_init(struct _adapter * padapter)68 u8 r8712_efuse_reg_init(struct _adapter *padapter)
69 {
70 	return true;
71 }
72 
r8712_efuse_reg_uninit(struct _adapter * padapter)73 void r8712_efuse_reg_uninit(struct _adapter *padapter)
74 {
75 	efuse_reg_ctrl(padapter, false);
76 }
77 
efuse_one_byte_read(struct _adapter * padapter,u16 addr,u8 * data)78 static 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 
efuse_one_byte_write(struct _adapter * padapter,u16 addr,u8 data)100 static 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 
efuse_one_byte_rw(struct _adapter * padapter,u8 bRead,u16 addr,u8 * data)120 static 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 
efuse_is_empty(struct _adapter * padapter,u8 * empty)156 static 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 
r8712_efuse_change_max_size(struct _adapter * padapter)171 void 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 
r8712_efuse_get_max_size(struct _adapter * padapter)187 int r8712_efuse_get_max_size(struct _adapter *padapter)
188 {
189 	return	efuse_available_max_size;
190 }
191 
calculate_word_cnts(const u8 word_en)192 static 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 
pgpacket_copy_data(const u8 word_en,const u8 * sourdata,u8 * targetdata)203 static 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 
r8712_efuse_get_current_size(struct _adapter * padapter)218 u16 r8712_efuse_get_current_size(struct _adapter *padapter)
219 {
220 	int bContinual = true;
221 	u16 efuse_addr = 0;
222 	u8 hoffset = 0, 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 			hoffset = (efuse_data >> 4) & 0x0F;
229 			hworden =  efuse_data & 0x0F;
230 			word_cnts = calculate_word_cnts(hworden);
231 			/* read next header */
232 			efuse_addr = efuse_addr + (word_cnts * 2) + 1;
233 		} else
234 			bContinual = false ;
235 	}
236 	return efuse_addr;
237 }
238 
r8712_efuse_pg_packet_read(struct _adapter * padapter,u8 offset,u8 * data)239 u8 r8712_efuse_pg_packet_read(struct _adapter *padapter, u8 offset, u8 *data)
240 {
241 	u8 hoffset = 0, hworden = 0, word_cnts = 0;
242 	u16 efuse_addr = 0;
243 	u8 efuse_data;
244 	u8 tmpidx = 0;
245 	u8 tmpdata[PGPKT_DATA_SIZE];
246 	u8 ret = true;
247 
248 	if (data == NULL)
249 		return false;
250 	if (offset > 0x0f)
251 		return false;
252 	memset(data, 0xFF, sizeof(u8)*PGPKT_DATA_SIZE);
253 	while (efuse_addr < efuse_available_max_size) {
254 		if (efuse_one_byte_read(padapter, efuse_addr, &efuse_data) ==
255 		    true) {
256 			if (efuse_data == 0xFF)
257 				break;
258 			hoffset = (efuse_data >> 4) & 0x0F;
259 			hworden =  efuse_data & 0x0F;
260 			word_cnts = calculate_word_cnts(hworden);
261 			if (hoffset == offset) {
262 				memset(tmpdata, 0xFF, PGPKT_DATA_SIZE);
263 				for (tmpidx = 0; tmpidx < word_cnts * 2;
264 				     tmpidx++) {
265 					if (efuse_one_byte_read(padapter,
266 					    efuse_addr+1+tmpidx, &efuse_data) ==
267 					     true) {
268 						tmpdata[tmpidx] = efuse_data;
269 					} else
270 						ret = false;
271 				}
272 				pgpacket_copy_data(hworden, tmpdata, data);
273 			}
274 			efuse_addr += 1 + (word_cnts*2);
275 		} else {
276 			ret = false;
277 			break;
278 		}
279 	}
280 	return ret;
281 }
282 
fix_header(struct _adapter * padapter,u8 header,u16 header_addr)283 static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
284 {
285 	struct PGPKT_STRUCT pkt;
286 	u8 offset, word_en, value;
287 	u16 addr;
288 	int i;
289 	u8 ret = true;
290 
291 	pkt.offset = GET_EFUSE_OFFSET(header);
292 	pkt.word_en = GET_EFUSE_WORD_EN(header);
293 	addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2;
294 	if (addr > efuse_available_max_size)
295 		return false;
296 	/* retrieve original data */
297 	addr = 0;
298 	while (addr < header_addr) {
299 		if (efuse_one_byte_read(padapter, addr++, &value) == false) {
300 			ret = false;
301 			break;
302 		}
303 		offset = GET_EFUSE_OFFSET(value);
304 		word_en = GET_EFUSE_WORD_EN(value);
305 		if (pkt.offset != offset) {
306 			addr += calculate_word_cnts(word_en)*2;
307 			continue;
308 		}
309 		for (i = 0; i < PGPKG_MAX_WORDS; i++) {
310 			if (BIT(i) & word_en) {
311 				if (BIT(i) & pkt.word_en) {
312 					if (efuse_one_byte_read(
313 							padapter, addr,
314 							&value) == true)
315 						pkt.data[i*2] = value;
316 					else
317 						return false;
318 					if (efuse_one_byte_read(
319 							padapter,
320 							addr + 1,
321 							&value) == true)
322 						pkt.data[i*2 + 1] =
323 							value;
324 					else
325 						return false;
326 				}
327 				addr += 2;
328 			}
329 		}
330 	}
331 	if (addr != header_addr)
332 		return false;
333 	addr++;
334 	/* fill original data */
335 	for (i = 0; i < PGPKG_MAX_WORDS; i++) {
336 		if (BIT(i) & pkt.word_en) {
337 			efuse_one_byte_write(padapter, addr, pkt.data[i*2]);
338 			efuse_one_byte_write(padapter, addr+1,
339 					pkt.data[i*2 + 1]);
340 			/* additional check */
341 			if (efuse_one_byte_read(padapter, addr, &value)
342 				== false)
343 				ret = false;
344 			else if (pkt.data[i*2] != value) {
345 				ret = false;
346 				if (0xFF == value) /* write again */
347 					efuse_one_byte_write(padapter, addr,
348 							pkt.data[i * 2]);
349 			}
350 			if (efuse_one_byte_read(padapter, addr+1, &value) ==
351 				false)
352 				ret = false;
353 			else if (pkt.data[i*2 + 1] != value) {
354 				ret = false;
355 				if (0xFF == value) /* write again */
356 					efuse_one_byte_write(padapter, addr+1,
357 							pkt.data[i*2 + 1]);
358 			}
359 		}
360 		addr += 2;
361 	}
362 	return ret;
363 }
364 
r8712_efuse_pg_packet_write(struct _adapter * padapter,const u8 offset,const u8 word_en,const u8 * data)365 u8 r8712_efuse_pg_packet_write(struct _adapter *padapter, const u8 offset,
366 			 const u8 word_en, const u8 *data)
367 {
368 	u8 pg_header = 0;
369 	u16 efuse_addr = 0, curr_size = 0;
370 	u8 efuse_data, target_word_cnts = 0;
371 	static int repeat_times;
372 	int sub_repeat;
373 	u8 bResult = true;
374 
375 	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
376 	efuse_data = r8712_read8(padapter, EFUSE_CLK_CTRL);
377 	if (efuse_data != 0x03)
378 		return false;
379 	pg_header = MAKE_EFUSE_HEADER(offset, word_en);
380 	target_word_cnts = calculate_word_cnts(word_en);
381 	repeat_times = 0;
382 	efuse_addr = 0;
383 	while (efuse_addr < efuse_available_max_size) {
384 		curr_size = r8712_efuse_get_current_size(padapter);
385 		if ((curr_size + 1 + target_word_cnts * 2) >
386 		     efuse_available_max_size)
387 			return false; /*target_word_cnts + pg header(1 byte)*/
388 		efuse_addr = curr_size; /* current size is also the last addr*/
389 		efuse_one_byte_write(padapter, efuse_addr, pg_header); /*hdr*/
390 		sub_repeat = 0;
391 		/* check if what we read is what we write */
392 		while (efuse_one_byte_read(padapter, efuse_addr,
393 					   &efuse_data) == false) {
394 			if (++sub_repeat > _REPEAT_THRESHOLD_) {
395 				bResult = false; /* continue to blind write */
396 				break; /* continue to blind write */
397 			}
398 		}
399 		if ((sub_repeat > _REPEAT_THRESHOLD_) ||
400 		    (pg_header == efuse_data)) {
401 			/* write header ok OR can't check header(creep) */
402 			u8 i;
403 
404 			/* go to next address */
405 			efuse_addr++;
406 			for (i = 0; i < target_word_cnts*2; i++) {
407 				efuse_one_byte_write(padapter,
408 						     efuse_addr + i,
409 						     *(data + i));
410 				if (efuse_one_byte_read(padapter,
411 				    efuse_addr + i, &efuse_data) == false)
412 					bResult = false;
413 				else if (*(data+i) != efuse_data) /* fail */
414 					bResult = false;
415 			}
416 			break;
417 		} else { /* write header fail */
418 			bResult = false;
419 			if (0xFF == efuse_data)
420 				return bResult; /* not thing damaged. */
421 			/* call rescue procedure */
422 			if (fix_header(padapter, efuse_data, efuse_addr) ==
423 			    false)
424 				return false; /* rescue fail */
425 
426 			if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */
427 				break;
428 			/* otherwise, take another risk... */
429 		}
430 	}
431 	return bResult;
432 }
433 
r8712_efuse_access(struct _adapter * padapter,u8 bRead,u16 start_addr,u16 cnts,u8 * data)434 u8 r8712_efuse_access(struct _adapter *padapter, u8 bRead, u16 start_addr,
435 		      u16 cnts, u8 *data)
436 {
437 	int i;
438 	u8 res = true;
439 
440 	if (start_addr > EFUSE_MAX_SIZE)
441 		return false;
442 	if ((bRead == false) && ((start_addr + cnts) >
443 	   efuse_available_max_size))
444 		return false;
445 	if ((false == bRead) && (r8712_efuse_reg_init(padapter) == false))
446 		return false;
447 	/* -----------------e-fuse one byte read / write ---------------------*/
448 	for (i = 0; i < cnts; i++) {
449 		if ((start_addr + i) > EFUSE_MAX_SIZE) {
450 			res = false;
451 			break;
452 		}
453 		res = efuse_one_byte_rw(padapter, bRead, start_addr + i,
454 		      data + i);
455 		if ((false == bRead) && (false == res))
456 			break;
457 	}
458 	if (false == bRead)
459 		r8712_efuse_reg_uninit(padapter);
460 	return res;
461 }
462 
r8712_efuse_map_read(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)463 u8 r8712_efuse_map_read(struct _adapter *padapter, u16 addr, u16 cnts, u8 *data)
464 {
465 	u8 offset, ret = true;
466 	u8 pktdata[PGPKT_DATA_SIZE];
467 	int i, idx;
468 
469 	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
470 		return false;
471 	if ((efuse_is_empty(padapter, &offset) == true) && (offset ==
472 	     true)) {
473 		for (i = 0; i < cnts; i++)
474 			data[i] = 0xFF;
475 		return ret;
476 	}
477 	offset = (addr >> 3) & 0xF;
478 	ret = r8712_efuse_pg_packet_read(padapter, offset, pktdata);
479 	i = addr & 0x7;	/* pktdata index */
480 	idx = 0;	/* data index */
481 
482 	do {
483 		for (; i < PGPKT_DATA_SIZE; i++) {
484 			data[idx++] = pktdata[i];
485 			if (idx == cnts)
486 				return ret;
487 		}
488 		offset++;
489 		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
490 			ret = false;
491 		i = 0;
492 	} while (1);
493 	return ret;
494 }
495 
r8712_efuse_map_write(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)496 u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
497 			 u8 *data)
498 {
499 	u8 offset, word_en, empty;
500 	u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE];
501 	int i, j, idx;
502 
503 	if ((addr + cnts) > EFUSE_MAP_MAX_SIZE)
504 		return false;
505 	/* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */
506 	empty = r8712_read8(padapter, EFUSE_CLK_CTRL);
507 	if (empty != 0x03)
508 		return false;
509 	if (efuse_is_empty(padapter, &empty) == true) {
510 		if (true == empty)
511 			memset(pktdata, 0xFF, PGPKT_DATA_SIZE);
512 	} else
513 		return false;
514 	offset = (addr >> 3) & 0xF;
515 	if (empty == false)
516 		if (!r8712_efuse_pg_packet_read(padapter, offset, pktdata))
517 			return false;
518 	word_en = 0xF;
519 	memset(newdata, 0xFF, PGPKT_DATA_SIZE);
520 	i = addr & 0x7;	/* pktdata index */
521 	j = 0;		/* newdata index */
522 	idx = 0;	/* data index */
523 
524 	if (i & 0x1) {
525 		/*  odd start */
526 		if (data[idx] != pktdata[i]) {
527 			word_en &= ~BIT(i >> 1);
528 			newdata[j++] = pktdata[i - 1];
529 			newdata[j++] = data[idx];
530 		}
531 		i++;
532 		idx++;
533 	}
534 	do {
535 		for (; i < PGPKT_DATA_SIZE; i += 2) {
536 			if ((cnts - idx) == 1) {
537 				if (data[idx] != pktdata[i]) {
538 					word_en &= ~BIT(i >> 1);
539 					newdata[j++] = data[idx];
540 					newdata[j++] = pktdata[1 + 1];
541 				}
542 				idx++;
543 				break;
544 			} else {
545 				if ((data[idx] != pktdata[i]) || (data[idx+1] !=
546 				     pktdata[i+1])) {
547 					word_en &= ~BIT(i >> 1);
548 					newdata[j++] = data[idx];
549 					newdata[j++] = data[idx + 1];
550 				}
551 				idx += 2;
552 			}
553 			if (idx == cnts)
554 				break;
555 		}
556 
557 		if (word_en != 0xF)
558 			if (r8712_efuse_pg_packet_write(padapter, offset,
559 			    word_en, newdata) == false)
560 				return false;
561 		if (idx == cnts)
562 			break;
563 		offset++;
564 		if (empty == false)
565 			if (!r8712_efuse_pg_packet_read(padapter, offset,
566 			    pktdata))
567 				return false;
568 		i = 0;
569 		j = 0;
570 		word_en = 0xF;
571 		memset(newdata, 0xFF, PGPKT_DATA_SIZE);
572 	} while (1);
573 
574 	return true;
575 }
576