rtl8712_efuse.c 15 KB

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