cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rtl8712_efuse.c (14543B)


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