|
@@ -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;
|
|
|
+}
|