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 (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)) &&
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 
efuse_one_byte_write(struct _adapter * padapter,u16 addr,u8 data)101 static 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 
efuse_one_byte_rw(struct _adapter * padapter,u8 bRead,u16 addr,u8 * data)122 static 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 
efuse_is_empty(struct _adapter * padapter,u8 * empty)158 static 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 (value == 0xFF)
165 			*empty = true;
166 		else
167 			*empty = false;
168 	} else {
169 		ret = false;
170 	}
171 	return ret;
172 }
173 
r8712_efuse_change_max_size(struct _adapter * padapter)174 void 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 
r8712_efuse_get_max_size(struct _adapter * padapter)190 int r8712_efuse_get_max_size(struct _adapter *padapter)
191 {
192 	return	efuse_available_max_size;
193 }
194 
calculate_word_cnts(const u8 word_en)195 static 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 
pgpacket_copy_data(const u8 word_en,const u8 * sourdata,u8 * targetdata)206 static 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 
r8712_efuse_get_current_size(struct _adapter * padapter)221 u16 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 
r8712_efuse_pg_packet_read(struct _adapter * padapter,u8 offset,u8 * data)242 u8 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)
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 
fix_header(struct _adapter * padapter,u8 header,u16 header_addr)286 static 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 (value == 0xFF) /* 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 (value == 0xFF) /* 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 
r8712_efuse_pg_packet_write(struct _adapter * padapter,const u8 offset,const u8 word_en,const u8 * data)367 u8 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 (efuse_data == 0xFF)
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 
r8712_efuse_access(struct _adapter * padapter,u8 bRead,u16 start_addr,u16 cnts,u8 * data)436 u8 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 
r8712_efuse_map_read(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)465 u8 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 
r8712_efuse_map_write(struct _adapter * padapter,u16 addr,u16 cnts,u8 * data)497 u8 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