From patchwork Fri Aug 31 06:28:56 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sony Chacko X-Patchwork-Id: 180918 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 178712C031D for ; Fri, 31 Aug 2012 16:47:09 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752026Ab2HaGq6 (ORCPT ); Fri, 31 Aug 2012 02:46:58 -0400 Received: from mvnat01.qlogic.com ([198.186.3.73]:23271 "HELO linux-zupk.site" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with SMTP id S1751147Ab2HaGp5 (ORCPT ); Fri, 31 Aug 2012 02:45:57 -0400 Received: by linux-zupk.site (Postfix, from userid 0) id 165135204FC; Fri, 31 Aug 2012 02:29:04 -0400 (EDT) From: Sony Chacko To: davem@davemloft.net Cc: netdev@vger.kernel.org, Dept_NX_Linux_NIC_Driver@qlogic.com, Sony Chacko Subject: [PATCH 07/12] qlcnic: 83xx adpater flash interface routines Date: Fri, 31 Aug 2012 02:28:56 -0400 Message-Id: <1346394541-3486-8-git-send-email-sony.chacko@qlogic.com> X-Mailer: git-send-email 1.7.7 In-Reply-To: <1346394541-3486-1-git-send-email-sony.chacko@qlogic.com> References: <1346394541-3486-1-git-send-email-sony.chacko@qlogic.com> Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org From: Sony Chacko Flash interface routines for 83xx adapter Signed-off-by: Himanshu Madhani Signed-off-by: Sony Chacko --- drivers/net/ethernet/qlogic/qlcnic/qlcnic.h | 31 ++ .../net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c | 434 ++++++++++++++++++++ 2 files changed, 465 insertions(+), 0 deletions(-) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index fef8b67..4e75601 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -307,6 +307,7 @@ struct uni_data_desc{ /* Flash Defines and Structures */ #define QLCNIC_FLT_LOCATION 0x3F1000 +#define QLCNIC_FDT_LOCATION 0x3F0000 #define QLCNIC_B0_FW_IMAGE_REGION 0x74 #define QLCNIC_C0_FW_IMAGE_REGION 0x97 #define QLCNIC_BOOTLD_REGION 0X72 @@ -327,6 +328,35 @@ struct qlcnic_flt_entry { u32 end_addr; }; +struct qlcnic_flash_desc_table { + u32 flash_valid; + u16 flash_ver; + u16 flash_len; + u16 flash_cksum; + u16 flash_unused; + u8 flash_model[16]; + u16 flash_manuf; + u16 flash_id; + u8 flash_flag; + u8 erase_cmd; + u8 alt_erase_cmd; + u8 write_enable_cmd; + u8 write_enable_bits; + u8 write_statusreg_cmd; + u8 unprotected_sec_cmd; + u8 read_manuf_cmd; + u32 block_size; + u32 alt_block_size; + u32 flash_size; + u32 write_enable_data; + u8 readid_addr_len; + u8 write_disable_bits; + u8 read_dev_id_len; + u8 chip_erase_cmd; + u16 read_timeo; + u8 protected_sec_cmd; + u8 resvd[65]; +}; /* Magic number to let user know flash is programmed */ #define QLCNIC_BDINFO_MAGIC 0x12345678 @@ -509,6 +539,7 @@ struct qlcnic_hardware_context { struct qlcnic_hardware_ops *hw_ops; struct qlcnic_nic_intr_coalesce coal; struct qlcnic_fw_dump fw_dump; + struct qlcnic_flash_desc_table flash_fdt; struct qlcnic_83xx_reset reset; struct qlcnic_83xx_idc idc; struct qlcnic_83xx_fw_info fw_info; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index 78782ac..a79c62d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -2121,3 +2121,437 @@ 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 = QLCRD(adapter, QLCNIC_FLASH_LOCK); + if (status) + break; + + if (++timeout >= QLC_83XX_FLASH_LOCK_TIMEOUT) { + id = QLCRD(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); + } + + QLCWR(adapter, QLCNIC_FLASH_LOCK_OWNER, adapter->portnum); + return 0; +} + +void +qlcnic_83xx_unlock_flash(struct qlcnic_adapter *adapter) +{ + QLCRD(adapter, QLCNIC_FLASH_UNLOCK); + QLCWR(adapter, QLCNIC_FLASH_LOCK_OWNER, 0xFF); +} + +int +qlcnic_83xx_flash_read32(struct qlcnic_adapter *adapter, u32 flash_addr, + u8 *p_data, int count) +{ + int i, err; + u32 word; + u32 addr = flash_addr; + + if (qlcnic_83xx_lock_flash(adapter) != 0) + return -EIO; + + if (addr & 0x3) { + dev_err(&adapter->pdev->dev, "Illegal addr = 0x%x\n", addr); + qlcnic_83xx_unlock_flash(adapter); + return -EIO; + } + + for (i = 0; i < count; i++) { + if (qlcnic_83xx_wrt_reg_indirect(adapter, + QLC_83XX_FLASH_DIRECT_WINDOW, (addr))) { + qlcnic_83xx_unlock_flash(adapter); + return -EIO; + } + + word = qlcnic_83xx_rd_reg_indirect(adapter, + QLC_83XX_FLASH_DIRECT_DATA(addr), &err); + *(__le32 *)p_data = le32_to_cpu(word); + p_data = p_data + 4; + addr = addr + 4; + } + + qlcnic_83xx_unlock_flash(adapter); + + return 0; +} + +int +qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *adapter, + u32 flash_addr, u8 *p_data, int count) +{ + int err; + u32 i, word, flash_offset; + u32 addr = flash_addr; + + 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)); + + /* Check if data is spread across multiple sectors */ + if ((flash_offset + (count * sizeof(u32))) > + (QLCNIC_FLASH_SECTOR_SIZE - 1)) { + + /* Multi sector read */ + for (i = 0; i < count; i++) { + word = qlcnic_83xx_rd_reg_indirect(adapter, + QLC_83XX_FLASH_DIRECT_DATA(addr), &err); + + *(__le32 *)p_data = le32_to_cpu(word); + p_data = p_data + 4; + addr = addr + 4; + flash_offset = flash_offset + 4; + + if (flash_offset > (QLCNIC_FLASH_SECTOR_SIZE - 1)) { + /* This write is needed once for each sector */ + qlcnic_83xx_wrt_reg_indirect(adapter, + QLC_83XX_FLASH_DIRECT_WINDOW, (addr)); + flash_offset = 0; + } + } + } else { + /* Single sector read */ + for (i = 0; i < count; i++) { + word = qlcnic_83xx_rd_reg_indirect(adapter, + QLC_83XX_FLASH_DIRECT_DATA(addr), &err); + *(__le32 *)p_data = cpu_to_le32(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 err, retries = QLC_83XX_FLASH_READ_RETRY_COUNT; + + do { + status = qlcnic_83xx_rd_reg_indirect(adapter, + QLC_83XX_FLASH_STATUS, &err); + 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; + + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, + (QLC_83XX_FLASH_FDT_WRITE_DEF_SIG | + adapter->ahw->flash_fdt.write_statusreg_cmd)); + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA, + adapter->ahw->flash_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->flash_fdt.write_statusreg_cmd)); + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_WRDATA, + adapter->ahw->flash_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; +} + +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_CONTROL_VAL); + + 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, &ret); + + 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_flash_desc_table); + count = fdt_size/sizeof(u32); + + if (qlcnic_83xx_lock_flash(adapter)) + return -EIO; + + memset(&adapter->ahw->flash_fdt, 0, fdt_size); + ret = qlcnic_83xx_lockless_flash_read32(adapter, + QLCNIC_FDT_LOCATION, + (u8 *)&adapter->ahw->flash_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; + int ret = -EIO; + + if (qlcnic_83xx_lock_flash(adapter) != 0) + return -EIO; + + if (adapter->ahw->flash_fdt.flash_manuf == 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; + } + + reversed_addr = (((sector_start_addr & 0xFF) << 16) | + ((sector_start_addr & 0xFF0000) >> 16)); + + qlcnic_83xx_wrt_reg_indirect(adapter, + QLC_83XX_FLASH_WRDATA, reversed_addr); + + if (adapter->ahw->flash_fdt.flash_manuf == adapter->flash_mfg_id) + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, + (QLC_83XX_FLASH_FDT_ERASE_DEF_SIG | + adapter->ahw->flash_fdt.erase_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->flash_fdt.flash_manuf == 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; + + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, + 0x00800000 | (addr >> 2)); + 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, err; + + 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, &err); + qlcnic_83xx_wrt_reg_indirect(adapter, + QLC_83XX_FLASH_SPI_CONTROL, + (temp | QLC_83XX_FLASH_SPI_CONTROL_VAL)); + 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_WRITE_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_WRITE_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_WRITE_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, &err); + if ((ret & QLC_83XX_FLASH_SPI_CONTROL_VAL) == + QLC_83XX_FLASH_SPI_CONTROL_VAL) { + 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, &err); + qlcnic_83xx_wrt_reg_indirect(adapter, + QLC_83XX_FLASH_SPI_CONTROL, + (temp | QLC_83XX_FLASH_SPI_CONTROL_VAL)); + } + + return 0; +} + +static int +qlcnic_83xx_read_flash_status_reg(struct qlcnic_adapter *adapter) +{ + int ret, err; + + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_ADDR, + QLC_83XX_FLASH_OEM_READ_SIG); + qlcnic_83xx_wrt_reg_indirect(adapter, QLC_83XX_FLASH_CONTROL, + QLC_83XX_FLASH_READ_CONTROL_VAL); + ret = qlcnic_83xx_poll_flash_status_reg(adapter); + if (ret) + return -EIO; + + ret = qlcnic_83xx_rd_reg_indirect(adapter, QLC_83XX_FLASH_RDDATA, &err); + return ret&0xFF; +} + +int qlcnic_83xx_flash_test(struct qlcnic_adapter *adapter) +{ + int status; + + status = qlcnic_83xx_read_flash_status_reg(adapter); + if (status == -EIO) { + dev_info(&adapter->pdev->dev, "%s: EEPROM test failed.\n", + __func__); + return 1; + } + return 0; +}