iwlwifi: traverse linklist to find the valid OTP block
[pandora-kernel.git] / drivers / net / wireless / iwlwifi / iwl-eeprom.c
index ded6332..01b95e8 100644 (file)
@@ -152,6 +152,19 @@ int iwlcore_eeprom_verify_signature(struct iwl_priv *priv)
 }
 EXPORT_SYMBOL(iwlcore_eeprom_verify_signature);
 
+static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode)
+{
+       u32 otpgp;
+
+       otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+       if (mode == IWL_OTP_ACCESS_ABSOLUTE)
+               iwl_clear_bit(priv, CSR_OTP_GP_REG,
+                               CSR_OTP_GP_REG_OTP_ACCESS_MODE);
+       else
+               iwl_set_bit(priv, CSR_OTP_GP_REG,
+                               CSR_OTP_GP_REG_OTP_ACCESS_MODE);
+}
+
 static int iwlcore_get_nvm_type(struct iwl_priv *priv)
 {
        u32 otpgp;
@@ -252,6 +265,124 @@ static int iwl_init_otp_access(struct iwl_priv *priv)
        return ret;
 }
 
+static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, u16 *eeprom_data)
+{
+       int ret = 0;
+       u32 r;
+       u32 otpgp;
+
+       _iwl_write32(priv, CSR_EEPROM_REG,
+                    CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
+       ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
+                                 CSR_EEPROM_REG_READ_VALID_MSK,
+                                 IWL_EEPROM_ACCESS_TIMEOUT);
+       if (ret < 0) {
+               IWL_ERR(priv, "Time out reading OTP[%d]\n", addr);
+               return ret;
+       }
+       r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
+       /* check for ECC errors: */
+       otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
+       if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
+               /* stop in this case */
+               /* set the uncorrectable OTP ECC bit for acknowledgement */
+               iwl_set_bit(priv, CSR_OTP_GP_REG,
+                       CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
+               IWL_ERR(priv, "Uncorrectable OTP ECC error, abort OTP read\n");
+               return -EINVAL;
+       }
+       if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
+               /* continue in this case */
+               /* set the correctable OTP ECC bit for acknowledgement */
+               iwl_set_bit(priv, CSR_OTP_GP_REG,
+                               CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
+               IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
+       }
+       *eeprom_data = le16_to_cpu((__force __le16)(r >> 16));
+       return 0;
+}
+
+/*
+ * iwl_is_otp_empty: check for empty OTP
+ */
+static bool iwl_is_otp_empty(struct iwl_priv *priv)
+{
+       u16 next_link_addr = 0, link_value;
+       bool is_empty = false;
+
+       /* locate the beginning of OTP link list */
+       if (!iwl_read_otp_word(priv, next_link_addr, &link_value)) {
+               if (!link_value) {
+                       IWL_ERR(priv, "OTP is empty\n");
+                       is_empty = true;
+               }
+       } else {
+               IWL_ERR(priv, "Unable to read first block of OTP list.\n");
+               is_empty = true;
+       }
+
+       return is_empty;
+}
+
+
+/*
+ * iwl_find_otp_image: find EEPROM image in OTP
+ *   finding the OTP block that contains the EEPROM image.
+ *   the last valid block on the link list (the block _before_ the last block)
+ *   is the block we should read and used to configure the device.
+ *   If all the available OTP blocks are full, the last block will be the block
+ *   we should read and used to configure the device.
+ *   only perform this operation if shadow RAM is disabled
+ */
+static int iwl_find_otp_image(struct iwl_priv *priv,
+                                       u16 *validblockaddr)
+{
+       u16 next_link_addr = 0, link_value = 0, valid_addr;
+       int ret = 0;
+       int usedblocks = 0;
+
+       /* set addressing mode to absolute to traverse the link list */
+       iwl_set_otp_access(priv, IWL_OTP_ACCESS_ABSOLUTE);
+
+       /* checking for empty OTP or error */
+       if (iwl_is_otp_empty(priv))
+               return -EINVAL;
+
+       /*
+        * start traverse link list
+        * until reach the max number of OTP blocks
+        * different devices have different number of OTP blocks
+        */
+       do {
+               /* save current valid block address
+                * check for more block on the link list
+                */
+               valid_addr = next_link_addr;
+               next_link_addr = link_value;
+               IWL_DEBUG_INFO(priv, "OTP blocks %d addr 0x%x\n",
+                              usedblocks, next_link_addr);
+               if (iwl_read_otp_word(priv, next_link_addr, &link_value))
+                       return -EINVAL;
+               if (!link_value) {
+                       /*
+                        * reach the end of link list,
+                        * set address point to the starting address
+                        * of the image
+                        */
+                       goto done;
+               }
+               /* more in the link list, continue */
+               usedblocks++;
+       } while (usedblocks < priv->cfg->max_ll_items);
+       /* OTP full, use last block */
+       IWL_DEBUG_INFO(priv, "OTP is full, use last block\n");
+done:
+       *validblockaddr = valid_addr;
+       /* skip first 2 bytes (link list pointer) */
+       *validblockaddr += 2;
+       return ret;
+}
+
 /**
  * iwl_eeprom_init - read EEPROM contents
  *
@@ -266,15 +397,14 @@ int iwl_eeprom_init(struct iwl_priv *priv)
        int sz;
        int ret;
        u16 addr;
-       u32 otpgp;
+       u16 validblockaddr = 0;
+       u16 cache_addr = 0;
 
        priv->nvm_device_type = iwlcore_get_nvm_type(priv);
        if (priv->nvm_device_type == -ENOENT)
                return -ENOENT;
        /* allocate eeprom */
-       if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP)
-               priv->cfg->eeprom_size =
-                       OTP_BLOCK_SIZE * OTP_LOWER_BLOCKS_TOTAL;
+       IWL_DEBUG_INFO(priv, "NVM size = %d\n", priv->cfg->eeprom_size);
        sz = priv->cfg->eeprom_size;
        priv->eeprom = kzalloc(sz, GFP_KERNEL);
        if (!priv->eeprom) {
@@ -302,46 +432,31 @@ int iwl_eeprom_init(struct iwl_priv *priv)
                if (ret) {
                        IWL_ERR(priv, "Failed to initialize OTP access.\n");
                        ret = -ENOENT;
-                       goto err;
+                       goto done;
                }
                _iwl_write32(priv, CSR_EEPROM_GP,
                             iwl_read32(priv, CSR_EEPROM_GP) &
                             ~CSR_EEPROM_GP_IF_OWNER_MSK);
-               /* clear */
-               _iwl_write32(priv, CSR_OTP_GP_REG,
-                            iwl_read32(priv, CSR_OTP_GP_REG) |
+
+               iwl_set_bit(priv, CSR_OTP_GP_REG,
                             CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK |
                             CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK);
-
-               for (addr = 0; addr < sz; addr += sizeof(u16)) {
-                       u32 r;
-
-                       _iwl_write32(priv, CSR_EEPROM_REG,
-                                    CSR_EEPROM_REG_MSK_ADDR & (addr << 1));
-
-                       ret = iwl_poll_direct_bit(priv, CSR_EEPROM_REG,
-                                                 CSR_EEPROM_REG_READ_VALID_MSK,
-                                                 IWL_EEPROM_ACCESS_TIMEOUT);
-                       if (ret < 0) {
-                               IWL_ERR(priv, "Time out reading OTP[%d]\n", addr);
+               /* traversing the linked list if no shadow ram supported */
+               if (!priv->cfg->shadow_ram_support) {
+                       if (iwl_find_otp_image(priv, &validblockaddr)) {
+                               ret = -ENOENT;
                                goto done;
                        }
-                       r = _iwl_read_direct32(priv, CSR_EEPROM_REG);
-                       /* check for ECC errors: */
-                       otpgp = iwl_read32(priv, CSR_OTP_GP_REG);
-                       if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) {
-                               /* stop in this case */
-                               IWL_ERR(priv, "Uncorrectable OTP ECC error, Abort OTP read\n");
+               }
+               for (addr = validblockaddr; addr < validblockaddr + sz;
+                    addr += sizeof(u16)) {
+                       u16 eeprom_data;
+
+                       ret = iwl_read_otp_word(priv, addr, &eeprom_data);
+                       if (ret)
                                goto done;
-                       }
-                       if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) {
-                               /* continue in this case */
-                               _iwl_write32(priv, CSR_OTP_GP_REG,
-                                            iwl_read32(priv, CSR_OTP_GP_REG) |
-                                            CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK);
-                               IWL_ERR(priv, "Correctable OTP ECC error, continue read\n");
-                       }
-                       e[addr / 2] = le16_to_cpu((__force __le16)(r >> 16));
+                       e[cache_addr / 2] = eeprom_data;
+                       cache_addr += sizeof(u16);
                }
        } else {
                /* eeprom is an array of 16bit values */