1/****************************************************************************** 2 * rtl871x_eeprom.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 _RTL871X_EEPROM_C_ 30 31#include "osdep_service.h" 32#include "drv_types.h" 33 34static void up_clk(struct _adapter *padapter, u16 *x) 35{ 36 *x = *x | _EESK; 37 r8712_write8(padapter, EE_9346CR, (u8)*x); 38 udelay(CLOCK_RATE); 39} 40 41static void down_clk(struct _adapter *padapter, u16 *x) 42{ 43 *x = *x & ~_EESK; 44 r8712_write8(padapter, EE_9346CR, (u8)*x); 45 udelay(CLOCK_RATE); 46} 47 48static void shift_out_bits(struct _adapter *padapter, u16 data, u16 count) 49{ 50 u16 x, mask; 51 52 if (padapter->bSurpriseRemoved == true) 53 goto out; 54 mask = 0x01 << (count - 1); 55 x = r8712_read8(padapter, EE_9346CR); 56 x &= ~(_EEDO | _EEDI); 57 do { 58 x &= ~_EEDI; 59 if (data & mask) 60 x |= _EEDI; 61 if (padapter->bSurpriseRemoved == true) 62 goto out; 63 r8712_write8(padapter, EE_9346CR, (u8)x); 64 udelay(CLOCK_RATE); 65 up_clk(padapter, &x); 66 down_clk(padapter, &x); 67 mask >>= 1; 68 } while (mask); 69 if (padapter->bSurpriseRemoved == true) 70 goto out; 71 x &= ~_EEDI; 72 r8712_write8(padapter, EE_9346CR, (u8)x); 73out:; 74} 75 76static u16 shift_in_bits(struct _adapter *padapter) 77{ 78 u16 x, d = 0, i; 79 80 if (padapter->bSurpriseRemoved == true) 81 goto out; 82 x = r8712_read8(padapter, EE_9346CR); 83 x &= ~(_EEDO | _EEDI); 84 d = 0; 85 for (i = 0; i < 16; i++) { 86 d <<= 1; 87 up_clk(padapter, &x); 88 if (padapter->bSurpriseRemoved == true) 89 goto out; 90 x = r8712_read8(padapter, EE_9346CR); 91 x &= ~(_EEDI); 92 if (x & _EEDO) 93 d |= 1; 94 down_clk(padapter, &x); 95 } 96out: 97 return d; 98} 99 100static void standby(struct _adapter *padapter) 101{ 102 u8 x; 103 104 x = r8712_read8(padapter, EE_9346CR); 105 x &= ~(_EECS | _EESK); 106 r8712_write8(padapter, EE_9346CR, x); 107 udelay(CLOCK_RATE); 108 x |= _EECS; 109 r8712_write8(padapter, EE_9346CR, x); 110 udelay(CLOCK_RATE); 111} 112 113static u16 wait_eeprom_cmd_done(struct _adapter *padapter) 114{ 115 u8 x; 116 u16 i; 117 118 standby(padapter); 119 for (i = 0; i < 200; i++) { 120 x = r8712_read8(padapter, EE_9346CR); 121 if (x & _EEDO) 122 return true; 123 udelay(CLOCK_RATE); 124 } 125 return false; 126} 127 128static void eeprom_clean(struct _adapter *padapter) 129{ 130 u16 x; 131 132 if (padapter->bSurpriseRemoved == true) 133 return; 134 x = r8712_read8(padapter, EE_9346CR); 135 if (padapter->bSurpriseRemoved == true) 136 return; 137 x &= ~(_EECS | _EEDI); 138 r8712_write8(padapter, EE_9346CR, (u8)x); 139 if (padapter->bSurpriseRemoved == true) 140 return; 141 up_clk(padapter, &x); 142 if (padapter->bSurpriseRemoved == true) 143 return; 144 down_clk(padapter, &x); 145} 146 147void r8712_eeprom_write16(struct _adapter *padapter, u16 reg, u16 data) 148{ 149 u8 x; 150 u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new; 151 152 tmp8_ori = r8712_read8(padapter, 0x102502f1); 153 tmp8_new = tmp8_ori & 0xf7; 154 if (tmp8_ori != tmp8_new) 155 r8712_write8(padapter, 0x102502f1, tmp8_new); 156 tmp8_clk_ori = r8712_read8(padapter, 0x10250003); 157 tmp8_clk_new = tmp8_clk_ori | 0x20; 158 if (tmp8_clk_new != tmp8_clk_ori) 159 r8712_write8(padapter, 0x10250003, tmp8_clk_new); 160 x = r8712_read8(padapter, EE_9346CR); 161 x &= ~(_EEDI | _EEDO | _EESK | _EEM0); 162 x |= _EEM1 | _EECS; 163 r8712_write8(padapter, EE_9346CR, x); 164 shift_out_bits(padapter, EEPROM_EWEN_OPCODE, 5); 165 if (padapter->EepromAddressSize == 8) /*CF+ and SDIO*/ 166 shift_out_bits(padapter, 0, 6); 167 else /* USB */ 168 shift_out_bits(padapter, 0, 4); 169 standby(padapter); 170 /* Erase this particular word. Write the erase opcode and register 171 * number in that order. The opcode is 3bits in length; reg is 6 172 * bits long. 173 */ 174 standby(padapter); 175 /* write the new word to the EEPROM 176 * send the write opcode the EEPORM 177 */ 178 shift_out_bits(padapter, EEPROM_WRITE_OPCODE, 3); 179 /* select which word in the EEPROM that we are writing to. */ 180 shift_out_bits(padapter, reg, padapter->EepromAddressSize); 181 /* write the data to the selected EEPROM word. */ 182 shift_out_bits(padapter, data, 16); 183 if (wait_eeprom_cmd_done(padapter)) { 184 standby(padapter); 185 shift_out_bits(padapter, EEPROM_EWDS_OPCODE, 5); 186 shift_out_bits(padapter, reg, 4); 187 eeprom_clean(padapter); 188 } 189 if (tmp8_clk_new != tmp8_clk_ori) 190 r8712_write8(padapter, 0x10250003, tmp8_clk_ori); 191 if (tmp8_new != tmp8_ori) 192 r8712_write8(padapter, 0x102502f1, tmp8_ori); 193} 194 195u16 r8712_eeprom_read16(struct _adapter *padapter, u16 reg) /*ReadEEprom*/ 196{ 197 u16 x; 198 u16 data = 0; 199 u8 tmp8_ori, tmp8_new, tmp8_clk_ori, tmp8_clk_new; 200 201 tmp8_ori = r8712_read8(padapter, 0x102502f1); 202 tmp8_new = tmp8_ori & 0xf7; 203 if (tmp8_ori != tmp8_new) 204 r8712_write8(padapter, 0x102502f1, tmp8_new); 205 tmp8_clk_ori = r8712_read8(padapter, 0x10250003); 206 tmp8_clk_new = tmp8_clk_ori | 0x20; 207 if (tmp8_clk_new != tmp8_clk_ori) 208 r8712_write8(padapter, 0x10250003, tmp8_clk_new); 209 if (padapter->bSurpriseRemoved == true) 210 goto out; 211 /* select EEPROM, reset bits, set _EECS */ 212 x = r8712_read8(padapter, EE_9346CR); 213 if (padapter->bSurpriseRemoved == true) 214 goto out; 215 x &= ~(_EEDI | _EEDO | _EESK | _EEM0); 216 x |= _EEM1 | _EECS; 217 r8712_write8(padapter, EE_9346CR, (unsigned char)x); 218 /* write the read opcode and register number in that order 219 * The opcode is 3bits in length, reg is 6 bits long 220 */ 221 shift_out_bits(padapter, EEPROM_READ_OPCODE, 3); 222 shift_out_bits(padapter, reg, padapter->EepromAddressSize); 223 /* Now read the data (16 bits) in from the selected EEPROM word */ 224 data = shift_in_bits(padapter); 225 eeprom_clean(padapter); 226out: 227 if (tmp8_clk_new != tmp8_clk_ori) 228 r8712_write8(padapter, 0x10250003, tmp8_clk_ori); 229 if (tmp8_new != tmp8_ori) 230 r8712_write8(padapter, 0x102502f1, tmp8_ori); 231 return data; 232} 233 234