162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2005-2014, 2018-2019, 2021 Intel Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#include <linux/types.h>
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/export.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "iwl-drv.h"
1062306a36Sopenharmony_ci#include "iwl-debug.h"
1162306a36Sopenharmony_ci#include "iwl-eeprom-read.h"
1262306a36Sopenharmony_ci#include "iwl-io.h"
1362306a36Sopenharmony_ci#include "iwl-prph.h"
1462306a36Sopenharmony_ci#include "iwl-csr.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci/*
1762306a36Sopenharmony_ci * EEPROM access time values:
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG.
2062306a36Sopenharmony_ci * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1).
2162306a36Sopenharmony_ci * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec.
2262306a36Sopenharmony_ci * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG.
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#define IWL_EEPROM_ACCESS_TIMEOUT	5000 /* uSec */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * The device's EEPROM semaphore prevents conflicts between driver and uCode
2862306a36Sopenharmony_ci * when accessing the EEPROM; each access is a series of pulses to/from the
2962306a36Sopenharmony_ci * EEPROM chip, not a single event, so even reads could conflict if they
3062306a36Sopenharmony_ci * weren't arbitrated by the semaphore.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define IWL_EEPROM_SEM_TIMEOUT		10   /* microseconds */
3362306a36Sopenharmony_ci#define IWL_EEPROM_SEM_RETRY_LIMIT	1000 /* number of attempts (not time) */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic int iwl_eeprom_acquire_semaphore(struct iwl_trans *trans)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	u16 count;
3962306a36Sopenharmony_ci	int ret;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (count = 0; count < IWL_EEPROM_SEM_RETRY_LIMIT; count++) {
4262306a36Sopenharmony_ci		/* Request semaphore */
4362306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG,
4462306a36Sopenharmony_ci			    CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci		/* See if we got it */
4762306a36Sopenharmony_ci		ret = iwl_poll_bit(trans, CSR_HW_IF_CONFIG_REG,
4862306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
4962306a36Sopenharmony_ci				CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM,
5062306a36Sopenharmony_ci				IWL_EEPROM_SEM_TIMEOUT);
5162306a36Sopenharmony_ci		if (ret >= 0) {
5262306a36Sopenharmony_ci			IWL_DEBUG_EEPROM(trans->dev,
5362306a36Sopenharmony_ci					 "Acquired semaphore after %d tries.\n",
5462306a36Sopenharmony_ci					 count+1);
5562306a36Sopenharmony_ci			return ret;
5662306a36Sopenharmony_ci		}
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return ret;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void iwl_eeprom_release_semaphore(struct iwl_trans *trans)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	iwl_clear_bit(trans, CSR_HW_IF_CONFIG_REG,
6562306a36Sopenharmony_ci		      CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int iwl_eeprom_verify_signature(struct iwl_trans *trans, bool nvm_is_otp)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 gp = iwl_read32(trans, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	IWL_DEBUG_EEPROM(trans->dev, "EEPROM signature=0x%08x\n", gp);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	switch (gp) {
7562306a36Sopenharmony_ci	case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP:
7662306a36Sopenharmony_ci		if (!nvm_is_otp) {
7762306a36Sopenharmony_ci			IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n",
7862306a36Sopenharmony_ci				gp);
7962306a36Sopenharmony_ci			return -ENOENT;
8062306a36Sopenharmony_ci		}
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci	case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K:
8362306a36Sopenharmony_ci	case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K:
8462306a36Sopenharmony_ci		if (nvm_is_otp) {
8562306a36Sopenharmony_ci			IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp);
8662306a36Sopenharmony_ci			return -ENOENT;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci		return 0;
8962306a36Sopenharmony_ci	case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP:
9062306a36Sopenharmony_ci	default:
9162306a36Sopenharmony_ci		IWL_ERR(trans,
9262306a36Sopenharmony_ci			"bad EEPROM/OTP signature, type=%s, EEPROM_GP=0x%08x\n",
9362306a36Sopenharmony_ci			nvm_is_otp ? "OTP" : "EEPROM", gp);
9462306a36Sopenharmony_ci		return -ENOENT;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/******************************************************************************
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * OTP related functions
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci******************************************************************************/
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void iwl_set_otp_access_absolute(struct iwl_trans *trans)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	iwl_read32(trans, CSR_OTP_GP_REG);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	iwl_clear_bit(trans, CSR_OTP_GP_REG,
10962306a36Sopenharmony_ci		      CSR_OTP_GP_REG_OTP_ACCESS_MODE);
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int iwl_nvm_is_otp(struct iwl_trans *trans)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	u32 otpgp;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	/* OTP only valid for CP/PP and after */
11762306a36Sopenharmony_ci	switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) {
11862306a36Sopenharmony_ci	case CSR_HW_REV_TYPE_NONE:
11962306a36Sopenharmony_ci		IWL_ERR(trans, "Unknown hardware type\n");
12062306a36Sopenharmony_ci		return -EIO;
12162306a36Sopenharmony_ci	case CSR_HW_REV_TYPE_5300:
12262306a36Sopenharmony_ci	case CSR_HW_REV_TYPE_5350:
12362306a36Sopenharmony_ci	case CSR_HW_REV_TYPE_5100:
12462306a36Sopenharmony_ci	case CSR_HW_REV_TYPE_5150:
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci	default:
12762306a36Sopenharmony_ci		otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
12862306a36Sopenharmony_ci		if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT)
12962306a36Sopenharmony_ci			return 1;
13062306a36Sopenharmony_ci		return 0;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int iwl_init_otp_access(struct iwl_trans *trans)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	ret = iwl_finish_nic_init(trans);
13962306a36Sopenharmony_ci	if (ret)
14062306a36Sopenharmony_ci		return ret;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	iwl_set_bits_prph(trans, APMG_PS_CTRL_REG,
14362306a36Sopenharmony_ci			  APMG_PS_CTRL_VAL_RESET_REQ);
14462306a36Sopenharmony_ci	udelay(5);
14562306a36Sopenharmony_ci	iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG,
14662306a36Sopenharmony_ci			    APMG_PS_CTRL_VAL_RESET_REQ);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * CSR auto clock gate disable bit -
15062306a36Sopenharmony_ci	 * this is only applicable for HW with OTP shadow RAM
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	if (trans->trans_cfg->base_params->shadow_ram_support)
15362306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG,
15462306a36Sopenharmony_ci			    CSR_RESET_LINK_PWR_MGMT_DISABLED);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic int iwl_read_otp_word(struct iwl_trans *trans, u16 addr,
16062306a36Sopenharmony_ci			     __le16 *eeprom_data)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	int ret = 0;
16362306a36Sopenharmony_ci	u32 r;
16462306a36Sopenharmony_ci	u32 otpgp;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	iwl_write32(trans, CSR_EEPROM_REG,
16762306a36Sopenharmony_ci		    CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
16862306a36Sopenharmony_ci	ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
16962306a36Sopenharmony_ci				 CSR_EEPROM_REG_READ_VALID_MSK,
17062306a36Sopenharmony_ci				 CSR_EEPROM_REG_READ_VALID_MSK,
17162306a36Sopenharmony_ci				 IWL_EEPROM_ACCESS_TIMEOUT);
17262306a36Sopenharmony_ci	if (ret < 0) {
17362306a36Sopenharmony_ci		IWL_ERR(trans, "Time out reading OTP[%d]\n", addr);
17462306a36Sopenharmony_ci		return ret;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	r = iwl_read32(trans, CSR_EEPROM_REG);
17762306a36Sopenharmony_ci	/* check for ECC errors: */
17862306a36Sopenharmony_ci	otpgp = iwl_read32(trans, CSR_OTP_GP_REG);
17962306a36Sopenharmony_ci	if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
18062306a36Sopenharmony_ci		/* stop in this case */
18162306a36Sopenharmony_ci		/* set the uncorrectable OTP ECC bit for acknowledgment */
18262306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_OTP_GP_REG,
18362306a36Sopenharmony_ci			    CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
18462306a36Sopenharmony_ci		IWL_ERR(trans, "Uncorrectable OTP ECC error, abort OTP read\n");
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
18862306a36Sopenharmony_ci		/* continue in this case */
18962306a36Sopenharmony_ci		/* set the correctable OTP ECC bit for acknowledgment */
19062306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_OTP_GP_REG,
19162306a36Sopenharmony_ci			    CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
19262306a36Sopenharmony_ci		IWL_ERR(trans, "Correctable OTP ECC error, continue read\n");
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci	*eeprom_data = cpu_to_le16(r >> 16);
19562306a36Sopenharmony_ci	return 0;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/*
19962306a36Sopenharmony_ci * iwl_is_otp_empty: check for empty OTP
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_cistatic bool iwl_is_otp_empty(struct iwl_trans *trans)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	u16 next_link_addr = 0;
20462306a36Sopenharmony_ci	__le16 link_value;
20562306a36Sopenharmony_ci	bool is_empty = false;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* locate the beginning of OTP link list */
20862306a36Sopenharmony_ci	if (!iwl_read_otp_word(trans, next_link_addr, &link_value)) {
20962306a36Sopenharmony_ci		if (!link_value) {
21062306a36Sopenharmony_ci			IWL_ERR(trans, "OTP is empty\n");
21162306a36Sopenharmony_ci			is_empty = true;
21262306a36Sopenharmony_ci		}
21362306a36Sopenharmony_ci	} else {
21462306a36Sopenharmony_ci		IWL_ERR(trans, "Unable to read first block of OTP list.\n");
21562306a36Sopenharmony_ci		is_empty = true;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	return is_empty;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci/*
22362306a36Sopenharmony_ci * iwl_find_otp_image: find EEPROM image in OTP
22462306a36Sopenharmony_ci *   finding the OTP block that contains the EEPROM image.
22562306a36Sopenharmony_ci *   the last valid block on the link list (the block _before_ the last block)
22662306a36Sopenharmony_ci *   is the block we should read and used to configure the device.
22762306a36Sopenharmony_ci *   If all the available OTP blocks are full, the last block will be the block
22862306a36Sopenharmony_ci *   we should read and used to configure the device.
22962306a36Sopenharmony_ci *   only perform this operation if shadow RAM is disabled
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_cistatic int iwl_find_otp_image(struct iwl_trans *trans,
23262306a36Sopenharmony_ci					u16 *validblockaddr)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	u16 next_link_addr = 0, valid_addr;
23562306a36Sopenharmony_ci	__le16 link_value = 0;
23662306a36Sopenharmony_ci	int usedblocks = 0;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* set addressing mode to absolute to traverse the link list */
23962306a36Sopenharmony_ci	iwl_set_otp_access_absolute(trans);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* checking for empty OTP or error */
24262306a36Sopenharmony_ci	if (iwl_is_otp_empty(trans))
24362306a36Sopenharmony_ci		return -EINVAL;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/*
24662306a36Sopenharmony_ci	 * start traverse link list
24762306a36Sopenharmony_ci	 * until reach the max number of OTP blocks
24862306a36Sopenharmony_ci	 * different devices have different number of OTP blocks
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	do {
25162306a36Sopenharmony_ci		/* save current valid block address
25262306a36Sopenharmony_ci		 * check for more block on the link list
25362306a36Sopenharmony_ci		 */
25462306a36Sopenharmony_ci		valid_addr = next_link_addr;
25562306a36Sopenharmony_ci		next_link_addr = le16_to_cpu(link_value) * sizeof(u16);
25662306a36Sopenharmony_ci		IWL_DEBUG_EEPROM(trans->dev, "OTP blocks %d addr 0x%x\n",
25762306a36Sopenharmony_ci				 usedblocks, next_link_addr);
25862306a36Sopenharmony_ci		if (iwl_read_otp_word(trans, next_link_addr, &link_value))
25962306a36Sopenharmony_ci			return -EINVAL;
26062306a36Sopenharmony_ci		if (!link_value) {
26162306a36Sopenharmony_ci			/*
26262306a36Sopenharmony_ci			 * reach the end of link list, return success and
26362306a36Sopenharmony_ci			 * set address point to the starting address
26462306a36Sopenharmony_ci			 * of the image
26562306a36Sopenharmony_ci			 */
26662306a36Sopenharmony_ci			*validblockaddr = valid_addr;
26762306a36Sopenharmony_ci			/* skip first 2 bytes (link list pointer) */
26862306a36Sopenharmony_ci			*validblockaddr += 2;
26962306a36Sopenharmony_ci			return 0;
27062306a36Sopenharmony_ci		}
27162306a36Sopenharmony_ci		/* more in the link list, continue */
27262306a36Sopenharmony_ci		usedblocks++;
27362306a36Sopenharmony_ci	} while (usedblocks <= trans->trans_cfg->base_params->max_ll_items);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* OTP has no valid blocks */
27662306a36Sopenharmony_ci	IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n");
27762306a36Sopenharmony_ci	return -EINVAL;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/*
28162306a36Sopenharmony_ci * iwl_read_eeprom - read EEPROM contents
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * Load the EEPROM contents from adapter and return it
28462306a36Sopenharmony_ci * and its size.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * NOTE:  This routine uses the non-debug IO access functions.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_ciint iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	__le16 *e;
29162306a36Sopenharmony_ci	u32 gp = iwl_read32(trans, CSR_EEPROM_GP);
29262306a36Sopenharmony_ci	int sz;
29362306a36Sopenharmony_ci	int ret;
29462306a36Sopenharmony_ci	u16 addr;
29562306a36Sopenharmony_ci	u16 validblockaddr = 0;
29662306a36Sopenharmony_ci	u16 cache_addr = 0;
29762306a36Sopenharmony_ci	int nvm_is_otp;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!eeprom || !eeprom_size)
30062306a36Sopenharmony_ci		return -EINVAL;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	nvm_is_otp = iwl_nvm_is_otp(trans);
30362306a36Sopenharmony_ci	if (nvm_is_otp < 0)
30462306a36Sopenharmony_ci		return nvm_is_otp;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	sz = trans->trans_cfg->base_params->eeprom_size;
30762306a36Sopenharmony_ci	IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	e = kmalloc(sz, GFP_KERNEL);
31062306a36Sopenharmony_ci	if (!e)
31162306a36Sopenharmony_ci		return -ENOMEM;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ret = iwl_eeprom_verify_signature(trans, nvm_is_otp);
31462306a36Sopenharmony_ci	if (ret < 0) {
31562306a36Sopenharmony_ci		IWL_ERR(trans, "EEPROM not found, EEPROM_GP=0x%08x\n", gp);
31662306a36Sopenharmony_ci		goto err_free;
31762306a36Sopenharmony_ci	}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	/* Make sure driver (instead of uCode) is allowed to read EEPROM */
32062306a36Sopenharmony_ci	ret = iwl_eeprom_acquire_semaphore(trans);
32162306a36Sopenharmony_ci	if (ret < 0) {
32262306a36Sopenharmony_ci		IWL_ERR(trans, "Failed to acquire EEPROM semaphore.\n");
32362306a36Sopenharmony_ci		goto err_free;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (nvm_is_otp) {
32762306a36Sopenharmony_ci		ret = iwl_init_otp_access(trans);
32862306a36Sopenharmony_ci		if (ret) {
32962306a36Sopenharmony_ci			IWL_ERR(trans, "Failed to initialize OTP access.\n");
33062306a36Sopenharmony_ci			goto err_unlock;
33162306a36Sopenharmony_ci		}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		iwl_write32(trans, CSR_EEPROM_GP,
33462306a36Sopenharmony_ci			    iwl_read32(trans, CSR_EEPROM_GP) &
33562306a36Sopenharmony_ci			    ~CSR_EEPROM_GP_IF_OWNER_MSK);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		iwl_set_bit(trans, CSR_OTP_GP_REG,
33862306a36Sopenharmony_ci			    CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
33962306a36Sopenharmony_ci			    CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
34062306a36Sopenharmony_ci		/* traversing the linked list if no shadow ram supported */
34162306a36Sopenharmony_ci		if (!trans->trans_cfg->base_params->shadow_ram_support) {
34262306a36Sopenharmony_ci			ret = iwl_find_otp_image(trans, &validblockaddr);
34362306a36Sopenharmony_ci			if (ret)
34462306a36Sopenharmony_ci				goto err_unlock;
34562306a36Sopenharmony_ci		}
34662306a36Sopenharmony_ci		for (addr = validblockaddr; addr < validblockaddr + sz;
34762306a36Sopenharmony_ci		     addr += sizeof(u16)) {
34862306a36Sopenharmony_ci			__le16 eeprom_data;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci			ret = iwl_read_otp_word(trans, addr, &eeprom_data);
35162306a36Sopenharmony_ci			if (ret)
35262306a36Sopenharmony_ci				goto err_unlock;
35362306a36Sopenharmony_ci			e[cache_addr / 2] = eeprom_data;
35462306a36Sopenharmony_ci			cache_addr += sizeof(u16);
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci	} else {
35762306a36Sopenharmony_ci		/* eeprom is an array of 16bit values */
35862306a36Sopenharmony_ci		for (addr = 0; addr < sz; addr += sizeof(u16)) {
35962306a36Sopenharmony_ci			u32 r;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci			iwl_write32(trans, CSR_EEPROM_REG,
36262306a36Sopenharmony_ci				    CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci			ret = iwl_poll_bit(trans, CSR_EEPROM_REG,
36562306a36Sopenharmony_ci					   CSR_EEPROM_REG_READ_VALID_MSK,
36662306a36Sopenharmony_ci					   CSR_EEPROM_REG_READ_VALID_MSK,
36762306a36Sopenharmony_ci					   IWL_EEPROM_ACCESS_TIMEOUT);
36862306a36Sopenharmony_ci			if (ret < 0) {
36962306a36Sopenharmony_ci				IWL_ERR(trans,
37062306a36Sopenharmony_ci					"Time out reading EEPROM[%d]\n", addr);
37162306a36Sopenharmony_ci				goto err_unlock;
37262306a36Sopenharmony_ci			}
37362306a36Sopenharmony_ci			r = iwl_read32(trans, CSR_EEPROM_REG);
37462306a36Sopenharmony_ci			e[addr / 2] = cpu_to_le16(r >> 16);
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	IWL_DEBUG_EEPROM(trans->dev, "NVM Type: %s\n",
37962306a36Sopenharmony_ci			 nvm_is_otp ? "OTP" : "EEPROM");
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	iwl_eeprom_release_semaphore(trans);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	*eeprom_size = sz;
38462306a36Sopenharmony_ci	*eeprom = (u8 *)e;
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci err_unlock:
38862306a36Sopenharmony_ci	iwl_eeprom_release_semaphore(trans);
38962306a36Sopenharmony_ci err_free:
39062306a36Sopenharmony_ci	kfree(e);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return ret;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ciIWL_EXPORT_SYMBOL(iwl_read_eeprom);
395