Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rtl8712_efuse.c
Go to the documentation of this file.
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 <[email protected]>.
25  * Larry Finger <[email protected]>
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 
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  */
68 u8 r8712_efuse_reg_init(struct _adapter *padapter)
69 {
70  return true;
71 }
72 
73 void r8712_efuse_reg_uninit(struct _adapter *padapter)
74 {
75  efuse_reg_ctrl(padapter, false);
76 }
77 
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 
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 
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 
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 
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 
187 int r8712_efuse_get_max_size(struct _adapter *padapter)
188 {
189  return efuse_available_max_size;
190 }
191 
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 
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 
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 
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 
283 static u8 fix_header(struct _adapter *padapter, u8 header, u16 header_addr)
284 {
285  struct PGPKT_STRUCT pkt;
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 
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; /* nothing 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 
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 
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 
496 u8 r8712_efuse_map_write(struct _adapter *padapter, u16 addr, u16 cnts,
497  u8 *data)
498 {
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 }