qlcnic: 83xx adpater flash interface routines
[pandora-kernel.git] / drivers / net / ethernet / qlogic / qlcnic / qlcnic_83xx_hw.c
index 6d4d792..a4a521e 100644 (file)
@@ -1782,3 +1782,363 @@ out:
        qlcnic_free_mbx_args(&cmd);
        return err;
 }
+
+int qlcnic_83xx_lock_flash(struct qlcnic_adapter *adapter)
+{
+       int id, timeout = 0;
+       u32 status = 0;
+
+       while (status == 0) {
+               status = QLC_SHARED_REG_RD32(adapter, QLCNIC_FLASH_LOCK);
+               if (status)
+                       break;
+
+               if (++timeout >= QLC_83XX_FLASH_LOCK_TIMEOUT) {
+                       id = QLC_SHARED_REG_RD32(adapter,
+                                                QLCNIC_FLASH_LOCK_OWNER);
+                       dev_err(&adapter->pdev->dev,
+                               "%s: failed, lock held by %d\n", __func__, id);
+                       return -EIO;
+               }
+               usleep_range(1000, 2000);
+       }
+
+       QLC_SHARED_REG_WR32(adapter, QLCNIC_FLASH_LOCK_OWNER, adapter->portnum);
+       return 0;
+}
+
+void qlcnic_83xx_unlock_flash(struct qlcnic_adapter *adapter)
+{
+       QLC_SHARED_REG_RD32(adapter, QLCNIC_FLASH_UNLOCK);
+       QLC_SHARED_REG_WR32(adapter, QLCNIC_FLASH_LOCK_OWNER, 0xFF);
+}
+
+static int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *adapter,
+                                            u32 flash_addr, u8 *p_data,
+                                            int count)
+{
+       int i, ret;
+       u32 word, range, flash_offset, addr = flash_addr;
+       ulong indirect_add, direct_window;
+
+       flash_offset = addr & (QLCNIC_FLASH_SECTOR_SIZE - 1);
+       if (addr & 0x3) {
+               dev_err(&adapter->pdev->dev, "Illegal addr = 0x%x\n", addr);
+               return -EIO;
+       }
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_DIRECT_WINDOW,
+                                    (addr));
+
+       range = flash_offset + (count * sizeof(u32));
+       /* Check if data is spread across multiple sectors */
+       if (range > (QLCNIC_FLASH_SECTOR_SIZE - 1)) {
+
+               /* Multi sector read */
+               for (i = 0; i < count; i++) {
+                       indirect_add = QLC_83XX_FLASH_DIRECT_DATA(addr);
+                       ret = qlcnic_83xx_rd_reg_indirect(adapter,
+                                                         indirect_add);
+                       if (ret == -EIO)
+                               return -EIO;
+
+                       word = ret;
+                       *(u32 *)p_data  = word;
+                       p_data = p_data + 4;
+                       addr = addr + 4;
+                       flash_offset = flash_offset + 4;
+
+                       if (flash_offset > (QLCNIC_FLASH_SECTOR_SIZE - 1)) {
+                               direct_window = QLC_83XX_FLASH_DIRECT_WINDOW;
+                               /* This write is needed once for each sector */
+                               qlcnic_83xx_wrt_reg_indirect(adapter,
+                                                            direct_window,
+                                                            (addr));
+                               flash_offset = 0;
+                       }
+               }
+       } else {
+               /* Single sector read */
+               for (i = 0; i < count; i++) {
+                       indirect_add = QLC_83XX_FLASH_DIRECT_DATA(addr);
+                       ret = qlcnic_83xx_rd_reg_indirect(adapter,
+                                                         indirect_add);
+                       if (ret == -EIO)
+                               return -EIO;
+
+                       word = ret;
+                       *(u32 *)p_data  = word;
+                       p_data = p_data + 4;
+                       addr = addr + 4;
+               }
+       }
+
+       return 0;
+}
+
+static int qlcnic_83xx_poll_flash_status_reg(struct qlcnic_adapter *adapter)
+{
+       u32 status;
+       int retries = QLC_83XX_FLASH_READ_RETRY_COUNT;
+
+       do {
+               status = qlcnic_83xx_rd_reg_indirect(adapter,
+                                                    QLC_83XX_FLASH_STATUS);
+               if ((status & QLC_83XX_FLASH_STATUS_READY) ==
+                   QLC_83XX_FLASH_STATUS_READY)
+                       break;
+
+               msleep(QLC_83XX_FLASH_STATUS_REG_POLL_DELAY);
+       } while (--retries);
+
+       if (!retries)
+               return -EIO;
+
+       return 0;
+}
+
+static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)
+{
+       int ret;
+       u32 cmd;
+       cmd = adapter->ahw->fdt.write_statusreg_cmd;
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    (QLC_83XX_FLASH_FDT_WRITE_DEF_SIG | cmd));
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA,
+                                    adapter->ahw->fdt.write_enable_bits);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_SECOND_ERASE_MS_VAL);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret)
+               return -EIO;
+
+       return 0;
+}
+
+static int qlcnic_83xx_disable_flash_write_op(struct qlcnic_adapter *adapter)
+{
+       int ret;
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    (QLC_83XX_FLASH_FDT_WRITE_DEF_SIG |
+                                    adapter->ahw->fdt.write_statusreg_cmd));
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA,
+                                    adapter->ahw->fdt.write_disable_bits);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_SECOND_ERASE_MS_VAL);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret)
+               return -EIO;
+
+       return 0;
+}
+
+int qlcnic_83xx_read_flash_mfg_id(struct qlcnic_adapter *adapter)
+{
+       int ret, mfg_id;
+
+       if (qlcnic_83xx_lock_flash(adapter))
+               return -EIO;
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    QLC_83XX_FLASH_FDT_READ_MFG_ID_VAL);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_READ_CTRL);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               qlcnic_83xx_unlock_flash(adapter);
+               return -EIO;
+       }
+
+       mfg_id = qlcnic_83xx_rd_reg_indirect(adapter, QLC_83XX_FLASH_RDDATA);
+       if (mfg_id == -EIO)
+               return -EIO;
+
+       adapter->flash_mfg_id = (mfg_id & 0xFF);
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+int qlcnic_83xx_read_flash_descriptor_table(struct qlcnic_adapter *adapter)
+{
+       int count, fdt_size, ret = 0;
+
+       fdt_size = sizeof(struct qlcnic_fdt);
+       count = fdt_size / sizeof(u32);
+
+       if (qlcnic_83xx_lock_flash(adapter))
+               return -EIO;
+
+       memset(&adapter->ahw->fdt, 0, fdt_size);
+       ret = qlcnic_83xx_lockless_flash_read32(adapter, QLCNIC_FDT_LOCATION,
+                                               (u8 *)&adapter->ahw->fdt,
+                                               count);
+
+       qlcnic_83xx_unlock_flash(adapter);
+       return ret;
+}
+
+int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,
+                                  u32 sector_start_addr)
+{
+       u32 reversed_addr, addr1, addr2, cmd;
+       int ret = -EIO;
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0)
+               return -EIO;
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_enable_flash_write_op(adapter);
+               if (ret) {
+                       qlcnic_83xx_unlock_flash(adapter);
+                       dev_err(&adapter->pdev->dev,
+                               "%s failed at %d\n",
+                               __func__, __LINE__);
+                       return ret;
+               }
+       }
+
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               qlcnic_83xx_unlock_flash(adapter);
+               dev_err(&adapter->pdev->dev,
+                       "%s: failed at %d\n", __func__, __LINE__);
+               return -EIO;
+       }
+
+       addr1 = (sector_start_addr & 0xFF) << 16;
+       addr2 = (sector_start_addr & 0xFF0000) >> 16;
+       reversed_addr = addr1 | addr2;
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA,
+                                    reversed_addr);
+       cmd = QLC_83XX_FLASH_FDT_ERASE_DEF_SIG | adapter->ahw->fdt.erase_cmd;
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id)
+               qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, cmd);
+       else
+               qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                            QLC_83XX_FLASH_OEM_ERASE_SIG);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_LAST_ERASE_MS_VAL);
+
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               qlcnic_83xx_unlock_flash(adapter);
+               dev_err(&adapter->pdev->dev,
+                       "%s: failed at %d\n", __func__, __LINE__);
+               return -EIO;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_disable_flash_write_op(adapter);
+               if (ret) {
+                       qlcnic_83xx_unlock_flash(adapter);
+                       dev_err(&adapter->pdev->dev,
+                               "%s: failed at %d\n", __func__, __LINE__);
+                       return ret;
+               }
+       }
+
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+int qlcnic_83xx_flash_write32(struct qlcnic_adapter *adapter, u32 addr,
+                             u32 *p_data)
+{
+       int ret = -EIO;
+       u32 addr1 = 0x00800000 | (addr >> 2);
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, addr1);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA, *p_data);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_LAST_ERASE_MS_VAL);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "%s: failed at %d\n", __func__, __LINE__);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *adapter, u32 addr,
+                                u32 *p_data, int count)
+{
+       u32 temp;
+       int ret = -EIO;
+
+       if ((count < QLC_83XX_FLASH_BULK_WRITE_MIN) ||
+           (count > QLC_83XX_FLASH_BULK_WRITE_MAX)) {
+               dev_err(&adapter->pdev->dev,
+                       "%s: Invalid word count\n", __func__);
+               return -EIO;
+       }
+
+       temp = qlcnic_83xx_rd_reg_indirect(adapter,
+                                          QLC_83XX_FLASH_SPI_CONTROL);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_SPI_CONTROL,
+                                    (temp | QLC_83XX_FLASH_SPI_CTRL));
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    QLC_83XX_FLASH_ADDR_TEMP_VAL);
+
+       /* First DWORD write */
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA, *p_data++);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_FIRST_MS_PATTERN);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "%s: failed at %d\n", __func__, __LINE__);
+               return -EIO;
+       }
+
+       count--;
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    QLC_83XX_FLASH_ADDR_SECOND_TEMP_VAL);
+       /* Second to N-1 DWORD writes */
+       while (count != 1) {
+               qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA,
+                                            *p_data++);
+               qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                            QLC_83XX_FLASH_SECOND_MS_PATTERN);
+               ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "%s: failed at %d\n", __func__, __LINE__);
+                       return -EIO;
+               }
+               count--;
+       }
+
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR,
+                                    QLC_83XX_FLASH_ADDR_TEMP_VAL |
+                                    (addr >> 2));
+       /* Last DWORD write */
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA, *p_data++);
+       qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL,
+                                    QLC_83XX_FLASH_LAST_MS_PATTERN);
+       ret = qlcnic_83xx_poll_flash_status_reg(adapter);
+       if (ret) {
+               dev_err(&adapter->pdev->dev,
+                       "%s: failed at %d\n", __func__, __LINE__);
+               return -EIO;
+       }
+
+       ret = qlcnic_83xx_rd_reg_indirect(adapter, QLC_83XX_FLASH_SPI_STATUS);
+       if ((ret & QLC_83XX_FLASH_SPI_CTRL) == QLC_83XX_FLASH_SPI_CTRL) {
+               dev_err(&adapter->pdev->dev, "%s: failed at %d\n",
+                       __func__, __LINE__);
+               /* Operation failed, clear error bit */
+               temp = qlcnic_83xx_rd_reg_indirect(adapter,
+                                                  QLC_83XX_FLASH_SPI_CONTROL);
+               qlcnic_83xx_wrt_reg_indirect(adapter,
+                                            QLC_83XX_FLASH_SPI_CONTROL,
+                                            (temp | QLC_83XX_FLASH_SPI_CTRL));
+       }
+
+       return 0;
+}