diff options
author | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2009-05-22 14:01:46 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-05-22 14:06:04 -0400 |
commit | 0848e297c2107dbc12a91a1709c879c73bd188d8 (patch) | |
tree | eb98eab2c1e4701ac84daf68461c80c03d9016b3 /drivers/net/wireless/iwlwifi/iwl-eeprom.c | |
parent | 8a566afea0639fc387add782bc799009512a911b (diff) |
iwlwifi: support NVM access (EEPROM/OTP)
Two type of NVM available for devices 1000, 6000 and after, adding
support to read OTP lower blocks if OTP is used instead of EEPROM.
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-eeprom.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-eeprom.c | 147 |
1 files changed, 129 insertions, 18 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index b400bd510fc5..9cc063b8b2d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c | |||
@@ -152,6 +152,32 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv) | |||
152 | } | 152 | } |
153 | EXPORT_SYMBOL(iwlcore_eeprom_verify_signature); | 153 | EXPORT_SYMBOL(iwlcore_eeprom_verify_signature); |
154 | 154 | ||
155 | static int iwlcore_get_nvm_type(struct iwl_priv *priv) | ||
156 | { | ||
157 | u32 otpgp; | ||
158 | int nvm_type; | ||
159 | |||
160 | /* OTP only valid for CP/PP and after */ | ||
161 | switch (priv->hw_rev & CSR_HW_REV_TYPE_MSK) { | ||
162 | case CSR_HW_REV_TYPE_3945: | ||
163 | case CSR_HW_REV_TYPE_4965: | ||
164 | case CSR_HW_REV_TYPE_5300: | ||
165 | case CSR_HW_REV_TYPE_5350: | ||
166 | case CSR_HW_REV_TYPE_5100: | ||
167 | case CSR_HW_REV_TYPE_5150: | ||
168 | nvm_type = NVM_DEVICE_TYPE_EEPROM; | ||
169 | break; | ||
170 | default: | ||
171 | otpgp = iwl_read32(priv, CSR_OTP_GP_REG); | ||
172 | if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) | ||
173 | nvm_type = NVM_DEVICE_TYPE_OTP; | ||
174 | else | ||
175 | nvm_type = NVM_DEVICE_TYPE_EEPROM; | ||
176 | break; | ||
177 | } | ||
178 | return nvm_type; | ||
179 | } | ||
180 | |||
155 | /* | 181 | /* |
156 | * The device's EEPROM semaphore prevents conflicts between driver and uCode | 182 | * The device's EEPROM semaphore prevents conflicts between driver and uCode |
157 | * when accessing the EEPROM; each access is a series of pulses to/from the | 183 | * when accessing the EEPROM; each access is a series of pulses to/from the |
@@ -198,6 +224,35 @@ const u8 *iwlcore_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) | |||
198 | } | 224 | } |
199 | EXPORT_SYMBOL(iwlcore_eeprom_query_addr); | 225 | EXPORT_SYMBOL(iwlcore_eeprom_query_addr); |
200 | 226 | ||
227 | static int iwl_init_otp_access(struct iwl_priv *priv) | ||
228 | { | ||
229 | int ret; | ||
230 | |||
231 | /* Enable 40MHz radio clock */ | ||
232 | _iwl_write32(priv, CSR_GP_CNTRL, | ||
233 | _iwl_read32(priv, CSR_GP_CNTRL) | | ||
234 | CSR_GP_CNTRL_REG_FLAG_INIT_DONE); | ||
235 | |||
236 | /* wait for clock to be ready */ | ||
237 | ret = iwl_poll_direct_bit(priv, CSR_GP_CNTRL, | ||
238 | CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, | ||
239 | 25000); | ||
240 | if (ret < 0) | ||
241 | IWL_ERR(priv, "Time out access OTP\n"); | ||
242 | else { | ||
243 | ret = iwl_grab_nic_access(priv); | ||
244 | if (!ret) { | ||
245 | iwl_set_bits_prph(priv, APMG_PS_CTRL_REG, | ||
246 | APMG_PS_CTRL_VAL_RESET_REQ); | ||
247 | udelay(5); | ||
248 | iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, | ||
249 | APMG_PS_CTRL_VAL_RESET_REQ); | ||
250 | iwl_release_nic_access(priv); | ||
251 | } | ||
252 | } | ||
253 | return ret; | ||
254 | } | ||
255 | |||
201 | /** | 256 | /** |
202 | * iwl_eeprom_init - read EEPROM contents | 257 | * iwl_eeprom_init - read EEPROM contents |
203 | * | 258 | * |
@@ -209,11 +264,18 @@ int iwl_eeprom_init(struct iwl_priv *priv) | |||
209 | { | 264 | { |
210 | u16 *e; | 265 | u16 *e; |
211 | u32 gp = iwl_read32(priv, CSR_EEPROM_GP); | 266 | u32 gp = iwl_read32(priv, CSR_EEPROM_GP); |
212 | int sz = priv->cfg->eeprom_size; | 267 | int sz; |
213 | int ret; | 268 | int ret; |
214 | u16 addr; | 269 | u16 addr; |
270 | u32 otpgp; | ||
271 | |||
272 | priv->nvm_device_type = iwlcore_get_nvm_type(priv); | ||
215 | 273 | ||
216 | /* allocate eeprom */ | 274 | /* allocate eeprom */ |
275 | if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) | ||
276 | priv->cfg->eeprom_size = | ||
277 | OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL; | ||
278 | sz = priv->cfg->eeprom_size; | ||
217 | priv->eeprom = kzalloc(sz, GFP_KERNEL); | 279 | priv->eeprom = kzalloc(sz, GFP_KERNEL); |
218 | if (!priv->eeprom) { | 280 | if (!priv->eeprom) { |
219 | ret = -ENOMEM; | 281 | ret = -ENOMEM; |
@@ -235,30 +297,77 @@ int iwl_eeprom_init(struct iwl_priv *priv) | |||
235 | ret = -ENOENT; | 297 | ret = -ENOENT; |
236 | goto err; | 298 | goto err; |
237 | } | 299 | } |
238 | 300 | if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { | |
239 | /* eeprom is an array of 16bit values */ | 301 | ret = iwl_init_otp_access(priv); |
240 | for (addr = 0; addr < sz; addr += sizeof(u16)) { | 302 | if (ret) { |
241 | u32 r; | 303 | IWL_ERR(priv, "Failed to initialize OTP access.\n"); |
242 | 304 | ret = -ENOENT; | |
243 | _iwl_write32(priv, CSR_EEPROM_REG, | 305 | goto err; |
244 | CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); | 306 | } |
245 | 307 | _iwl_write32(priv, CSR_EEPROM_GP, | |
246 | ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, | 308 | iwl_read32(priv, CSR_EEPROM_GP) & |
247 | CSR_EEPROM_REG_READ_VALID_MSK, | 309 | ~CSR_EEPROM_GP_IF_OWNER_MSK); |
248 | IWL_EEPROM_ACCESS_TIMEOUT); | 310 | /* clear */ |
249 | if (ret < 0) { | 311 | _iwl_write32(priv, CSR_OTP_GP_REG, |
250 | IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr); | 312 | iwl_read32(priv, CSR_OTP_GP_REG) | |
251 | goto done; | 313 | CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | |
314 | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); | ||
315 | |||
316 | for (addr = 0; addr < sz; addr += sizeof(u16)) { | ||
317 | u32 r; | ||
318 | |||
319 | _iwl_write32(priv, CSR_EEPROM_REG, | ||
320 | CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); | ||
321 | |||
322 | ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, | ||
323 | CSR_EEPROM_REG_READ_VALID_MSK, | ||
324 | IWL_EEPROM_ACCESS_TIMEOUT); | ||
325 | if (ret < 0) { | ||
326 | IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); | ||
327 | goto done; | ||
328 | } | ||
329 | r = _iwl_read_direct32(priv, CSR_EEPROM_REG); | ||
330 | /* check for ECC errors: */ | ||
331 | otpgp = iwl_read32(priv, CSR_OTP_GP_REG); | ||
332 | if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { | ||
333 | /* stop in this case */ | ||
334 | IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n"); | ||
335 | goto done; | ||
336 | } | ||
337 | if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { | ||
338 | /* continue in this case */ | ||
339 | _iwl_write32(priv, CSR_OTP_GP_REG, | ||
340 | iwl_read32(priv, CSR_OTP_GP_REG) | | ||
341 | CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); | ||
342 | IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); | ||
343 | } | ||
344 | e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); | ||
345 | } | ||
346 | } else { | ||
347 | /* eeprom is an array of 16bit values */ | ||
348 | for (addr = 0; addr < sz; addr += sizeof(u16)) { | ||
349 | u32 r; | ||
350 | |||
351 | _iwl_write32(priv, CSR_EEPROM_REG, | ||
352 | CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); | ||
353 | |||
354 | ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG, | ||
355 | CSR_EEPROM_REG_READ_VALID_MSK, | ||
356 | IWL_EEPROM_ACCESS_TIMEOUT); | ||
357 | if (ret < 0) { | ||
358 | IWL_ERR(priv, "Time out reading EEPROM[%d]\n", addr); | ||
359 | goto done; | ||
360 | } | ||
361 | r = _iwl_read_direct32(priv, CSR_EEPROM_REG); | ||
362 | e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); | ||
252 | } | 363 | } |
253 | r = _iwl_read_direct32(priv, CSR_EEPROM_REG); | ||
254 | e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16)); | ||
255 | } | 364 | } |
256 | ret = 0; | 365 | ret = 0; |
257 | done: | 366 | done: |
258 | priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv); | 367 | priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv); |
259 | err: | 368 | err: |
260 | if (ret) | 369 | if (ret) |
261 | kfree(priv->eeprom); | 370 | iwl_eeprom_free(priv); |
262 | alloc_err: | 371 | alloc_err: |
263 | return ret; | 372 | return ret; |
264 | } | 373 | } |
@@ -301,6 +410,8 @@ EXPORT_SYMBOL(iwl_eeprom_query_addr); | |||
301 | 410 | ||
302 | u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) | 411 | u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) |
303 | { | 412 | { |
413 | if (!priv->eeprom) | ||
414 | return 0; | ||
304 | return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); | 415 | return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); |
305 | } | 416 | } |
306 | EXPORT_SYMBOL(iwl_eeprom_query16); | 417 | EXPORT_SYMBOL(iwl_eeprom_query16); |