Commit d865ebb4 authored by Sony Chacko's avatar Sony Chacko Committed by David S. Miller
Browse files

qlcnic: 83xx adpater flash interface routines



83xx adapter flash memory map, data structures and interface routines
Signed-off-by: default avatarHimanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: default avatarSony Chacko <sony.chacko@qlogic.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 319ecf12
......@@ -204,6 +204,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
......@@ -224,6 +225,36 @@ struct qlcnic_flt_entry {
u32 end_addr;
};
/* Flash Descriptor Table */
struct qlcnic_fdt {
u32 valid;
u16 ver;
u16 len;
u16 cksum;
u16 unused;
u8 model[16];
u16 mfg_id;
u16 id;
u8 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
......@@ -404,6 +435,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_fdt fdt;
struct qlcnic_intrpt_config *intr_tbl;
u32 *reg_tbl;
u32 *ext_reg_tbl;
......@@ -972,6 +1004,7 @@ struct qlcnic_adapter {
u64 dev_rst_time;
u8 mac_learn;
unsigned long vlans[BITS_TO_LONGS(VLAN_N_VID)];
u8 flash_mfg_id;
struct qlcnic_npar_info *npars;
struct qlcnic_eswitch *eswitch;
struct qlcnic_nic_template *nic_ops;
......
......@@ -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;
}
......@@ -134,6 +134,46 @@ struct qlcnic_macvlan_mbx {
#define QLC_83XX_DEFAULT_MODE 0x0
#define QLCNIC_BRDTYPE_83XX_10G 0x0083
#define QLC_83XX_FLASH_SPI_STATUS 0x2808E010
#define QLC_83XX_FLASH_SPI_CONTROL 0x2808E014
#define QLC_83XX_FLASH_STATUS 0x42100004
#define QLC_83XX_FLASH_CONTROL 0x42110004
#define QLC_83XX_FLASH_ADDR 0x42110008
#define QLC_83XX_FLASH_WRDATA 0x4211000C
#define QLC_83XX_FLASH_RDDATA 0x42110018
#define QLC_83XX_FLASH_DIRECT_WINDOW 0x42110030
#define QLC_83XX_FLASH_DIRECT_DATA(DATA) (0x42150000 | (0x0000FFFF&DATA))
#define QLC_83XX_FLASH_SECTOR_ERASE_CMD 0xdeadbeef
#define QLC_83XX_FLASH_WRITE_CMD 0xdacdacda
#define QLC_83XX_FLASH_BULK_WRITE_CMD 0xcadcadca
#define QLC_83XX_FLASH_READ_RETRY_COUNT 5000
#define QLC_83XX_FLASH_STATUS_READY 0x6
#define QLC_83XX_FLASH_BULK_WRITE_MIN 2
#define QLC_83XX_FLASH_BULK_WRITE_MAX 64
#define QLC_83XX_FLASH_STATUS_REG_POLL_DELAY 1
#define QLC_83XX_ERASE_MODE 1
#define QLC_83XX_WRITE_MODE 2
#define QLC_83XX_BULK_WRITE_MODE 3
#define QLC_83XX_FLASH_FDT_WRITE_DEF_SIG 0xFD0100
#define QLC_83XX_FLASH_FDT_ERASE_DEF_SIG 0xFD0300
#define QLC_83XX_FLASH_FDT_READ_MFG_ID_VAL 0xFD009F
#define QLC_83XX_FLASH_OEM_ERASE_SIG 0xFD03D8
#define QLC_83XX_FLASH_OEM_WRITE_SIG 0xFD0101
#define QLC_83XX_FLASH_OEM_READ_SIG 0xFD0005
#define QLC_83XX_FLASH_ADDR_TEMP_VAL 0x00800000
#define QLC_83XX_FLASH_ADDR_SECOND_TEMP_VAL 0x00800001
#define QLC_83XX_FLASH_WRDATA_DEF 0x0
#define QLC_83XX_FLASH_READ_CTRL 0x3F
#define QLC_83XX_FLASH_SPI_CTRL 0x4
#define QLC_83XX_FLASH_FIRST_ERASE_MS_VAL 0x2
#define QLC_83XX_FLASH_SECOND_ERASE_MS_VAL 0x5
#define QLC_83XX_FLASH_LAST_ERASE_MS_VAL 0x3D
#define QLC_83XX_FLASH_FIRST_MS_PATTERN 0x43
#define QLC_83XX_FLASH_SECOND_MS_PATTERN 0x7F
#define QLC_83XX_FLASH_LAST_MS_PATTERN 0x7D
#define QLC_83xx_FLASH_MAX_WAIT_USEC 100
#define QLC_83XX_FLASH_LOCK_TIMEOUT 10000
/* Additional registers in 83xx */
enum qlc_83xx_ext_regs {
QLCNIC_GLOBAL_RESET = 0,
......@@ -253,4 +293,14 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *);
void qlcnic_83xx_register_map(struct qlcnic_hardware_context *);
void qlcnic_83xx_idc_aen_work(struct work_struct *);
void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *, __be32, int);
int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *, u32);
int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *, u32, u32 *, int);
int qlcnic_83xx_flash_write32(struct qlcnic_adapter *, u32, u32 *);
int qlcnic_83xx_lock_flash(struct qlcnic_adapter *);
void qlcnic_83xx_unlock_flash(struct qlcnic_adapter *);
int qlcnic_83xx_save_flash_status(struct qlcnic_adapter *);
int qlcnic_83xx_restore_flash_status(struct qlcnic_adapter *, int);
int qlcnic_83xx_read_flash_mfg_id(struct qlcnic_adapter *);
int qlcnic_83xx_read_flash_descriptor_table(struct qlcnic_adapter *);
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment