162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * QLogic iSCSI HBA Driver
462306a36Sopenharmony_ci * Copyright (c)  2003-2013 QLogic Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include "ql4_def.h"
862306a36Sopenharmony_ci#include "ql4_glbl.h"
962306a36Sopenharmony_ci#include "ql4_dbg.h"
1062306a36Sopenharmony_ci#include "ql4_inline.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic inline void eeprom_cmd(uint32_t cmd, struct scsi_qla_host *ha)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	writel(cmd, isp_nvram(ha));
1562306a36Sopenharmony_ci	readl(isp_nvram(ha));
1662306a36Sopenharmony_ci	udelay(1);
1762306a36Sopenharmony_ci}
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic inline int eeprom_size(struct scsi_qla_host *ha)
2062306a36Sopenharmony_ci{
2162306a36Sopenharmony_ci	return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic inline int eeprom_no_addr_bits(struct scsi_qla_host *ha)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	return is_qla4010(ha) ? FM93C56A_NO_ADDR_BITS_16 :
2762306a36Sopenharmony_ci		FM93C86A_NO_ADDR_BITS_16 ;
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic inline int eeprom_no_data_bits(struct scsi_qla_host *ha)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	return FM93C56A_DATA_BITS_16;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic int fm93c56a_select(struct scsi_qla_host * ha)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	DEBUG5(printk(KERN_ERR "fm93c56a_select:\n"));
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	ha->eeprom_cmd_data = AUBURN_EEPROM_CS_1 | 0x000f0000;
4062306a36Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data, ha);
4162306a36Sopenharmony_ci	return 1;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int i;
4762306a36Sopenharmony_ci	int mask;
4862306a36Sopenharmony_ci	int dataBit;
4962306a36Sopenharmony_ci	int previousBit;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* Clock in a zero, then do the start bit. */
5262306a36Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, ha);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
5562306a36Sopenharmony_ci	       AUBURN_EEPROM_CLK_RISE, ha);
5662306a36Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
5762306a36Sopenharmony_ci	       AUBURN_EEPROM_CLK_FALL, ha);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	mask = 1 << (FM93C56A_CMD_BITS - 1);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	/* Force the previous data bit to be different. */
6262306a36Sopenharmony_ci	previousBit = 0xffff;
6362306a36Sopenharmony_ci	for (i = 0; i < FM93C56A_CMD_BITS; i++) {
6462306a36Sopenharmony_ci		dataBit =
6562306a36Sopenharmony_ci			(cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
6662306a36Sopenharmony_ci		if (previousBit != dataBit) {
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci			/*
6962306a36Sopenharmony_ci			 * If the bit changed, then change the DO state to
7062306a36Sopenharmony_ci			 * match.
7162306a36Sopenharmony_ci			 */
7262306a36Sopenharmony_ci			eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
7362306a36Sopenharmony_ci			previousBit = dataBit;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
7662306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
7762306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
7862306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		cmd = cmd << 1;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	mask = 1 << (eeprom_no_addr_bits(ha) - 1);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* Force the previous data bit to be different. */
8562306a36Sopenharmony_ci	previousBit = 0xffff;
8662306a36Sopenharmony_ci	for (i = 0; i < eeprom_no_addr_bits(ha); i++) {
8762306a36Sopenharmony_ci		dataBit = addr & mask ? AUBURN_EEPROM_DO_1 :
8862306a36Sopenharmony_ci			AUBURN_EEPROM_DO_0;
8962306a36Sopenharmony_ci		if (previousBit != dataBit) {
9062306a36Sopenharmony_ci			/*
9162306a36Sopenharmony_ci			 * If the bit changed, then change the DO state to
9262306a36Sopenharmony_ci			 * match.
9362306a36Sopenharmony_ci			 */
9462306a36Sopenharmony_ci			eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci			previousBit = dataBit;
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
9962306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
10062306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
10162306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		addr = addr << 1;
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci	return 1;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int fm93c56a_deselect(struct scsi_qla_host * ha)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000;
11162306a36Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data, ha);
11262306a36Sopenharmony_ci	return 1;
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int fm93c56a_datain(struct scsi_qla_host * ha, unsigned short *value)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	int i;
11862306a36Sopenharmony_ci	int data = 0;
11962306a36Sopenharmony_ci	int dataBit;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* Read the data bits
12262306a36Sopenharmony_ci	 * The first bit is a dummy.  Clock right over it. */
12362306a36Sopenharmony_ci	for (i = 0; i < eeprom_no_data_bits(ha); i++) {
12462306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data |
12562306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
12662306a36Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data |
12762306a36Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		dataBit = (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		data = (data << 1) | dataBit;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	*value = data;
13562306a36Sopenharmony_ci	return 1;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int eeprom_readword(int eepromAddr, u16 * value,
13962306a36Sopenharmony_ci			   struct scsi_qla_host * ha)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	fm93c56a_select(ha);
14262306a36Sopenharmony_ci	fm93c56a_cmd(ha, FM93C56A_READ, eepromAddr);
14362306a36Sopenharmony_ci	fm93c56a_datain(ha, value);
14462306a36Sopenharmony_ci	fm93c56a_deselect(ha);
14562306a36Sopenharmony_ci	return 1;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Hardware_lock must be set before calling */
14962306a36Sopenharmony_ciu16 rd_nvram_word(struct scsi_qla_host * ha, int offset)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	u16 val = 0;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* NOTE: NVRAM uses half-word addresses */
15462306a36Sopenharmony_ci	eeprom_readword(offset, &val, ha);
15562306a36Sopenharmony_ci	return val;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciu8 rd_nvram_byte(struct scsi_qla_host *ha, int offset)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	u16 val = 0;
16162306a36Sopenharmony_ci	u8 rval = 0;
16262306a36Sopenharmony_ci	int index = 0;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (offset & 0x1)
16562306a36Sopenharmony_ci		index = (offset - 1) / 2;
16662306a36Sopenharmony_ci	else
16762306a36Sopenharmony_ci		index = offset / 2;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	val = le16_to_cpu(rd_nvram_word(ha, index));
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (offset & 0x1)
17262306a36Sopenharmony_ci		rval = (u8)((val & 0xff00) >> 8);
17362306a36Sopenharmony_ci	else
17462306a36Sopenharmony_ci		rval = (u8)((val & 0x00ff));
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	return rval;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ciint qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int status = QLA_ERROR;
18262306a36Sopenharmony_ci	uint16_t checksum = 0;
18362306a36Sopenharmony_ci	uint32_t index;
18462306a36Sopenharmony_ci	unsigned long flags;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
18762306a36Sopenharmony_ci	for (index = 0; index < eeprom_size(ha); index++)
18862306a36Sopenharmony_ci		checksum += rd_nvram_word(ha, index);
18962306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	if (checksum == 0)
19262306a36Sopenharmony_ci		status = QLA_SUCCESS;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return status;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/*************************************************************************
19862306a36Sopenharmony_ci *
19962306a36Sopenharmony_ci *			Hardware Semaphore routines
20062306a36Sopenharmony_ci *
20162306a36Sopenharmony_ci *************************************************************************/
20262306a36Sopenharmony_ciint ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	uint32_t value;
20562306a36Sopenharmony_ci	unsigned long flags;
20662306a36Sopenharmony_ci	unsigned int seconds = 30;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	DEBUG2(printk("scsi%ld : Trying to get SEM lock - mask= 0x%x, code = "
20962306a36Sopenharmony_ci		      "0x%x\n", ha->host_no, sem_mask, sem_bits));
21062306a36Sopenharmony_ci	do {
21162306a36Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
21262306a36Sopenharmony_ci		writel((sem_mask | sem_bits), isp_semaphore(ha));
21362306a36Sopenharmony_ci		value = readw(isp_semaphore(ha));
21462306a36Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
21562306a36Sopenharmony_ci		if ((value & (sem_mask >> 16)) == sem_bits) {
21662306a36Sopenharmony_ci			DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, "
21762306a36Sopenharmony_ci				      "code = 0x%x\n", ha->host_no,
21862306a36Sopenharmony_ci				      sem_mask, sem_bits));
21962306a36Sopenharmony_ci			return QLA_SUCCESS;
22062306a36Sopenharmony_ci		}
22162306a36Sopenharmony_ci		ssleep(1);
22262306a36Sopenharmony_ci	} while (--seconds);
22362306a36Sopenharmony_ci	return QLA_ERROR;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_civoid ql4xxx_sem_unlock(struct scsi_qla_host * ha, u32 sem_mask)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	unsigned long flags;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
23162306a36Sopenharmony_ci	writel(sem_mask, isp_semaphore(ha));
23262306a36Sopenharmony_ci	readl(isp_semaphore(ha));
23362306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n", ha->host_no,
23662306a36Sopenharmony_ci		      sem_mask));
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ciint ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	uint32_t value;
24262306a36Sopenharmony_ci	unsigned long flags;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
24562306a36Sopenharmony_ci	writel((sem_mask | sem_bits), isp_semaphore(ha));
24662306a36Sopenharmony_ci	value = readw(isp_semaphore(ha));
24762306a36Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
24862306a36Sopenharmony_ci	if ((value & (sem_mask >> 16)) == sem_bits) {
24962306a36Sopenharmony_ci		DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, code = "
25062306a36Sopenharmony_ci			      "0x%x, sema code=0x%x\n", ha->host_no,
25162306a36Sopenharmony_ci			      sem_mask, sem_bits, value));
25262306a36Sopenharmony_ci		return 1;
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
256