Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -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;
+360 −0
Original line number Diff line number Diff line
@@ -1782,3 +1782,363 @@ int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type)
	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;
}
+50 −0
Original line number Diff line number Diff line
@@ -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