Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Larry Finger | 2836 | 95.97% | 1 | 10.00% |
Nishka Dasgupta | 76 | 2.57% | 1 | 10.00% |
Luis de Bethencourt | 26 | 0.88% | 2 | 20.00% |
Rakhi Sharma | 8 | 0.27% | 1 | 10.00% |
Ali Bahar | 4 | 0.14% | 1 | 10.00% |
Michael Straube | 2 | 0.07% | 1 | 10.00% |
Sandhya Bankar | 1 | 0.03% | 1 | 10.00% |
Justin P. Mattock | 1 | 0.03% | 1 | 10.00% |
Serguey Parkhomovsky | 1 | 0.03% | 1 | 10.00% |
Total | 2955 | 10 |
// SPDX-License-Identifier: GPL-2.0 /* * rtl8712_efuse.c * * Copyright(c) 2007 - 2010 Realtek Corporation. All rights reserved. * Linux device driver for RTL8192SU * * Modifications for inclusion into the Linux staging tree are * Copyright(c) 2010 Larry Finger. All rights reserved. * * Contact information: * WLAN FAE <wlanfae@realtek.com>. * Larry Finger <Larry.Finger@lwfinger.net> * ******************************************************************************/ #define _RTL8712_EFUSE_C_ #include "osdep_service.h" #include "drv_types.h" #include "rtl8712_efuse.h" /* reserve 3 bytes for HW stop read */ static int efuse_available_max_size = EFUSE_MAX_SIZE - 3 /*0x1FD*/; static void efuse_reg_ctrl(struct _adapter *adapter, u8 bPowerOn) { u8 tmpu8 = 0; if (bPowerOn) { /* -----------------e-fuse pwr & clk reg ctrl --------------- * Enable LDOE25 Macro Block */ tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3); tmpu8 |= 0x80; r8712_write8(adapter, EFUSE_TEST + 3, tmpu8); msleep(20); /* for some platform , need some delay time */ /* Change Efuse Clock for write action to 40MHZ */ r8712_write8(adapter, EFUSE_CLK_CTRL, 0x03); msleep(20); /* for some platform , need some delay time */ } else { /* -----------------e-fuse pwr & clk reg ctrl ----------------- * Disable LDOE25 Macro Block */ tmpu8 = r8712_read8(adapter, EFUSE_TEST + 3); tmpu8 &= 0x7F; r8712_write8(adapter, EFUSE_TEST + 3, tmpu8); /* Change Efuse Clock for write action to 500K */ r8712_write8(adapter, EFUSE_CLK_CTRL, 0x02); } } /* * Before write E-Fuse, this function must be called. */ u8 r8712_efuse_reg_init(struct _adapter *adapter) { return true; } void r8712_efuse_reg_uninit(struct _adapter *adapter) { efuse_reg_ctrl(adapter, false); } static u8 efuse_one_byte_read(struct _adapter *adapter, u16 addr, u8 *data) { u8 tmpidx = 0, bResult; /* -----------------e-fuse reg ctrl --------------------------------- */ r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) | (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC)); r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */ /* wait for complete */ while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && (tmpidx < 100)) tmpidx++; if (tmpidx < 100) { *data = r8712_read8(adapter, EFUSE_CTRL); bResult = true; } else { *data = 0xff; bResult = false; } return bResult; } static u8 efuse_one_byte_write(struct _adapter *adapter, u16 addr, u8 data) { u8 tmpidx = 0, bResult; /* -----------------e-fuse reg ctrl -------------------------------- */ r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ r8712_write8(adapter, EFUSE_CTRL + 2, ((u8)((addr >> 8) & 0x03)) | (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC)); r8712_write8(adapter, EFUSE_CTRL, data); /* data */ r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */ /* wait for complete */ while ((0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && (tmpidx < 100)) tmpidx++; if (tmpidx < 100) bResult = true; else bResult = false; return bResult; } static u8 efuse_one_byte_rw(struct _adapter *adapter, u8 bRead, u16 addr, u8 *data) { u8 tmpidx = 0, tmpv8 = 0, bResult; /* -----------------e-fuse reg ctrl --------------------------------- */ r8712_write8(adapter, EFUSE_CTRL + 1, (u8)(addr & 0xFF)); /* address */ tmpv8 = ((u8)((addr >> 8) & 0x03)) | (r8712_read8(adapter, EFUSE_CTRL + 2) & 0xFC); r8712_write8(adapter, EFUSE_CTRL + 2, tmpv8); if (bRead) { r8712_write8(adapter, EFUSE_CTRL + 3, 0x72); /* read cmd */ while (!(0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && (tmpidx < 100)) tmpidx++; if (tmpidx < 100) { *data = r8712_read8(adapter, EFUSE_CTRL); bResult = true; } else { *data = 0; bResult = false; } } else { r8712_write8(adapter, EFUSE_CTRL, *data); /* data */ r8712_write8(adapter, EFUSE_CTRL + 3, 0xF2); /* write cmd */ while ((0x80 & r8712_read8(adapter, EFUSE_CTRL + 3)) && (tmpidx < 100)) tmpidx++; if (tmpidx < 100) bResult = true; else bResult = false; } return bResult; } static u8 efuse_is_empty(struct _adapter *adapter, u8 *empty) { u8 value, ret = true; /* read one byte to check if E-Fuse is empty */ if (efuse_one_byte_rw(adapter, true, 0, &value)) { if (value == 0xFF) *empty = true; else *empty = false; } else { ret = false; } return ret; } void r8712_efuse_change_max_size(struct _adapter *adapter) { u16 pre_pg_data_saddr = 0x1FB; u16 i; u16 pre_pg_data_size = 5; u8 pre_pg_data[5]; for (i = 0; i < pre_pg_data_size; i++) efuse_one_byte_read(adapter, pre_pg_data_saddr + i, &pre_pg_data[i]); if ((pre_pg_data[0] == 0x03) && (pre_pg_data[1] == 0x00) && (pre_pg_data[2] == 0x00) && (pre_pg_data[3] == 0x00) && (pre_pg_data[4] == 0x0C)) efuse_available_max_size -= pre_pg_data_size; } int r8712_efuse_get_max_size(struct _adapter *adapter) { return efuse_available_max_size; } static u8 calculate_word_cnts(const u8 word_en) { u8 word_cnts = 0; u8 word_idx; for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) if (!(word_en & BIT(word_idx))) word_cnts++; /* 0 : write enable */ return word_cnts; } static void pgpacket_copy_data(const u8 word_en, const u8 *sourdata, u8 *targetdata) { u8 tmpindex = 0; u8 word_idx, byte_idx; for (word_idx = 0; word_idx < PGPKG_MAX_WORDS; word_idx++) { if (!(word_en & BIT(word_idx))) { byte_idx = word_idx * 2; targetdata[byte_idx] = sourdata[tmpindex++]; targetdata[byte_idx + 1] = sourdata[tmpindex++]; } } } u16 r8712_efuse_get_current_size(struct _adapter *adapter) { int bContinual = true; u16 efuse_addr = 0; u8 hworden = 0; u8 efuse_data, word_cnts = 0; while (bContinual && efuse_one_byte_read(adapter, efuse_addr, &efuse_data) && (efuse_addr < efuse_available_max_size)) { if (efuse_data != 0xFF) { hworden = efuse_data & 0x0F; word_cnts = calculate_word_cnts(hworden); /* read next header */ efuse_addr = efuse_addr + (word_cnts * 2) + 1; } else { bContinual = false; } } return efuse_addr; } u8 r8712_efuse_pg_packet_read(struct _adapter *adapter, u8 offset, u8 *data) { u8 hoffset = 0, hworden = 0, word_cnts = 0; u16 efuse_addr = 0; u8 efuse_data; u8 tmpidx = 0; u8 tmpdata[PGPKT_DATA_SIZE]; u8 ret = true; if (!data) return false; if (offset > 0x0f) return false; memset(data, 0xFF, sizeof(u8) * PGPKT_DATA_SIZE); while (efuse_addr < efuse_available_max_size) { if (efuse_one_byte_read(adapter, efuse_addr, &efuse_data)) { if (efuse_data == 0xFF) break; hoffset = (efuse_data >> 4) & 0x0F; hworden = efuse_data & 0x0F; word_cnts = calculate_word_cnts(hworden); if (hoffset == offset) { memset(tmpdata, 0xFF, PGPKT_DATA_SIZE); for (tmpidx = 0; tmpidx < word_cnts * 2; tmpidx++) { if (efuse_one_byte_read(adapter, efuse_addr + 1 + tmpidx, &efuse_data)) { tmpdata[tmpidx] = efuse_data; } else { ret = false; } } pgpacket_copy_data(hworden, tmpdata, data); } efuse_addr += 1 + (word_cnts * 2); } else { ret = false; break; } } return ret; } static u8 fix_header(struct _adapter *adapter, u8 header, u16 header_addr) { struct PGPKT_STRUCT pkt; u8 offset, word_en, value; u16 addr; int i; u8 ret = true; pkt.offset = GET_EFUSE_OFFSET(header); pkt.word_en = GET_EFUSE_WORD_EN(header); addr = header_addr + 1 + calculate_word_cnts(pkt.word_en) * 2; if (addr > efuse_available_max_size) return false; /* retrieve original data */ addr = 0; while (addr < header_addr) { if (!efuse_one_byte_read(adapter, addr++, &value)) { ret = false; break; } offset = GET_EFUSE_OFFSET(value); word_en = GET_EFUSE_WORD_EN(value); if (pkt.offset != offset) { addr += calculate_word_cnts(word_en) * 2; continue; } for (i = 0; i < PGPKG_MAX_WORDS; i++) { if (BIT(i) & word_en) { if (BIT(i) & pkt.word_en) { if (efuse_one_byte_read( adapter, addr, &value)) pkt.data[i * 2] = value; else return false; if (efuse_one_byte_read( adapter, addr + 1, &value)) pkt.data[i * 2 + 1] = value; else return false; } addr += 2; } } } if (addr != header_addr) return false; addr++; /* fill original data */ for (i = 0; i < PGPKG_MAX_WORDS; i++) { if (BIT(i) & pkt.word_en) { efuse_one_byte_write(adapter, addr, pkt.data[i * 2]); efuse_one_byte_write(adapter, addr + 1, pkt.data[i * 2 + 1]); /* additional check */ if (!efuse_one_byte_read(adapter, addr, &value)) { ret = false; } else if (pkt.data[i * 2] != value) { ret = false; if (value == 0xFF) /* write again */ efuse_one_byte_write(adapter, addr, pkt.data[i * 2]); } if (!efuse_one_byte_read(adapter, addr + 1, &value)) { ret = false; } else if (pkt.data[i * 2 + 1] != value) { ret = false; if (value == 0xFF) /* write again */ efuse_one_byte_write(adapter, addr + 1, pkt.data[i * 2 + 1]); } } addr += 2; } return ret; } u8 r8712_efuse_pg_packet_write(struct _adapter *adapter, const u8 offset, const u8 word_en, const u8 *data) { u8 pg_header = 0; u16 efuse_addr = 0, curr_size = 0; u8 efuse_data, target_word_cnts = 0; int repeat_times; int sub_repeat; u8 bResult = true; /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ efuse_data = r8712_read8(adapter, EFUSE_CLK_CTRL); if (efuse_data != 0x03) return false; pg_header = MAKE_EFUSE_HEADER(offset, word_en); target_word_cnts = calculate_word_cnts(word_en); repeat_times = 0; efuse_addr = 0; while (efuse_addr < efuse_available_max_size) { curr_size = r8712_efuse_get_current_size(adapter); if ((curr_size + 1 + target_word_cnts * 2) > efuse_available_max_size) return false; /*target_word_cnts + pg header(1 byte)*/ efuse_addr = curr_size; /* current size is also the last addr*/ efuse_one_byte_write(adapter, efuse_addr, pg_header); /*hdr*/ sub_repeat = 0; /* check if what we read is what we write */ while (!efuse_one_byte_read(adapter, efuse_addr, &efuse_data)) { if (++sub_repeat > _REPEAT_THRESHOLD_) { bResult = false; /* continue to blind write */ break; /* continue to blind write */ } } if ((sub_repeat > _REPEAT_THRESHOLD_) || (pg_header == efuse_data)) { /* write header ok OR can't check header(creep) */ u8 i; /* go to next address */ efuse_addr++; for (i = 0; i < target_word_cnts * 2; i++) { efuse_one_byte_write(adapter, efuse_addr + i, *(data + i)); if (!efuse_one_byte_read(adapter, efuse_addr + i, &efuse_data)) bResult = false; else if (*(data + i) != efuse_data) /* fail */ bResult = false; } break; } /* write header fail */ bResult = false; if (efuse_data == 0xFF) return bResult; /* nothing damaged. */ /* call rescue procedure */ if (!fix_header(adapter, efuse_data, efuse_addr)) return false; /* rescue fail */ if (++repeat_times > _REPEAT_THRESHOLD_) /* fail */ break; /* otherwise, take another risk... */ } return bResult; } u8 r8712_efuse_access(struct _adapter *adapter, u8 bRead, u16 start_addr, u16 cnts, u8 *data) { int i; u8 res = true; if (start_addr > EFUSE_MAX_SIZE) return false; if (!bRead && ((start_addr + cnts) > efuse_available_max_size)) return false; if (!bRead && !r8712_efuse_reg_init(adapter)) return false; /* -----------------e-fuse one byte read / write ---------------------*/ for (i = 0; i < cnts; i++) { if ((start_addr + i) > EFUSE_MAX_SIZE) { res = false; break; } res = efuse_one_byte_rw(adapter, bRead, start_addr + i, data + i); if (!bRead && !res) break; } if (!bRead) r8712_efuse_reg_uninit(adapter); return res; } u8 r8712_efuse_map_read(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data) { u8 offset, ret = true; u8 pktdata[PGPKT_DATA_SIZE]; int i, idx; if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) return false; if (efuse_is_empty(adapter, &offset) && offset) { for (i = 0; i < cnts; i++) data[i] = 0xFF; return ret; } offset = (addr >> 3) & 0xF; ret = r8712_efuse_pg_packet_read(adapter, offset, pktdata); i = addr & 0x7; /* pktdata index */ idx = 0; /* data index */ do { for (; i < PGPKT_DATA_SIZE; i++) { data[idx++] = pktdata[i]; if (idx == cnts) return ret; } offset++; if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) ret = false; i = 0; } while (1); return ret; } u8 r8712_efuse_map_write(struct _adapter *adapter, u16 addr, u16 cnts, u8 *data) { u8 offset, word_en, empty; u8 pktdata[PGPKT_DATA_SIZE], newdata[PGPKT_DATA_SIZE]; int i, j, idx; if ((addr + cnts) > EFUSE_MAP_MAX_SIZE) return false; /* check if E-Fuse Clock Enable and E-Fuse Clock is 40M */ empty = r8712_read8(adapter, EFUSE_CLK_CTRL); if (empty != 0x03) return false; if (efuse_is_empty(adapter, &empty)) { if (empty) memset(pktdata, 0xFF, PGPKT_DATA_SIZE); } else { return false; } offset = (addr >> 3) & 0xF; if (!empty) if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) return false; word_en = 0xF; memset(newdata, 0xFF, PGPKT_DATA_SIZE); i = addr & 0x7; /* pktdata index */ j = 0; /* newdata index */ idx = 0; /* data index */ if (i & 0x1) { /* odd start */ if (data[idx] != pktdata[i]) { word_en &= ~BIT(i >> 1); newdata[j++] = pktdata[i - 1]; newdata[j++] = data[idx]; } i++; idx++; } do { for (; i < PGPKT_DATA_SIZE; i += 2) { if ((cnts - idx) == 1) { if (data[idx] != pktdata[i]) { word_en &= ~BIT(i >> 1); newdata[j++] = data[idx]; newdata[j++] = pktdata[1 + 1]; } idx++; break; } if ((data[idx] != pktdata[i]) || (data[idx + 1] != pktdata[i + 1])) { word_en &= ~BIT(i >> 1); newdata[j++] = data[idx]; newdata[j++] = data[idx + 1]; } idx += 2; if (idx == cnts) break; } if (word_en != 0xF) if (!r8712_efuse_pg_packet_write(adapter, offset, word_en, newdata)) return false; if (idx == cnts) break; offset++; if (!empty) if (!r8712_efuse_pg_packet_read(adapter, offset, pktdata)) return false; i = 0; j = 0; word_en = 0xF; memset(newdata, 0xFF, PGPKT_DATA_SIZE); } while (1); return true; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1