18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * QLogic iSCSI HBA Driver
48c2ecf20Sopenharmony_ci * Copyright (c)  2003-2013 QLogic Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "ql4_def.h"
88c2ecf20Sopenharmony_ci#include "ql4_glbl.h"
98c2ecf20Sopenharmony_ci#include "ql4_dbg.h"
108c2ecf20Sopenharmony_ci#include "ql4_inline.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistatic inline void eeprom_cmd(uint32_t cmd, struct scsi_qla_host *ha)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	writel(cmd, isp_nvram(ha));
158c2ecf20Sopenharmony_ci	readl(isp_nvram(ha));
168c2ecf20Sopenharmony_ci	udelay(1);
178c2ecf20Sopenharmony_ci}
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic inline int eeprom_size(struct scsi_qla_host *ha)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	return is_qla4010(ha) ? FM93C66A_SIZE_16 : FM93C86A_SIZE_16;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic inline int eeprom_no_addr_bits(struct scsi_qla_host *ha)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	return is_qla4010(ha) ? FM93C56A_NO_ADDR_BITS_16 :
278c2ecf20Sopenharmony_ci		FM93C86A_NO_ADDR_BITS_16 ;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic inline int eeprom_no_data_bits(struct scsi_qla_host *ha)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	return FM93C56A_DATA_BITS_16;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int fm93c56a_select(struct scsi_qla_host * ha)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	DEBUG5(printk(KERN_ERR "fm93c56a_select:\n"));
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ha->eeprom_cmd_data = AUBURN_EEPROM_CS_1 | 0x000f0000;
408c2ecf20Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data, ha);
418c2ecf20Sopenharmony_ci	return 1;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	int i;
478c2ecf20Sopenharmony_ci	int mask;
488c2ecf20Sopenharmony_ci	int dataBit;
498c2ecf20Sopenharmony_ci	int previousBit;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	/* Clock in a zero, then do the start bit. */
528c2ecf20Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, ha);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
558c2ecf20Sopenharmony_ci	       AUBURN_EEPROM_CLK_RISE, ha);
568c2ecf20Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
578c2ecf20Sopenharmony_ci	       AUBURN_EEPROM_CLK_FALL, ha);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	mask = 1 << (FM93C56A_CMD_BITS - 1);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	/* Force the previous data bit to be different. */
628c2ecf20Sopenharmony_ci	previousBit = 0xffff;
638c2ecf20Sopenharmony_ci	for (i = 0; i < FM93C56A_CMD_BITS; i++) {
648c2ecf20Sopenharmony_ci		dataBit =
658c2ecf20Sopenharmony_ci			(cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
668c2ecf20Sopenharmony_ci		if (previousBit != dataBit) {
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci			/*
698c2ecf20Sopenharmony_ci			 * If the bit changed, then change the DO state to
708c2ecf20Sopenharmony_ci			 * match.
718c2ecf20Sopenharmony_ci			 */
728c2ecf20Sopenharmony_ci			eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
738c2ecf20Sopenharmony_ci			previousBit = dataBit;
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
768c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
778c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
788c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		cmd = cmd << 1;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci	mask = 1 << (eeprom_no_addr_bits(ha) - 1);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* Force the previous data bit to be different. */
858c2ecf20Sopenharmony_ci	previousBit = 0xffff;
868c2ecf20Sopenharmony_ci	for (i = 0; i < eeprom_no_addr_bits(ha); i++) {
878c2ecf20Sopenharmony_ci		dataBit = addr & mask ? AUBURN_EEPROM_DO_1 :
888c2ecf20Sopenharmony_ci			AUBURN_EEPROM_DO_0;
898c2ecf20Sopenharmony_ci		if (previousBit != dataBit) {
908c2ecf20Sopenharmony_ci			/*
918c2ecf20Sopenharmony_ci			 * If the bit changed, then change the DO state to
928c2ecf20Sopenharmony_ci			 * match.
938c2ecf20Sopenharmony_ci			 */
948c2ecf20Sopenharmony_ci			eeprom_cmd(ha->eeprom_cmd_data | dataBit, ha);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci			previousBit = dataBit;
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
998c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
1008c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data | dataBit |
1018c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		addr = addr << 1;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	return 1;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int fm93c56a_deselect(struct scsi_qla_host * ha)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000;
1118c2ecf20Sopenharmony_ci	eeprom_cmd(ha->eeprom_cmd_data, ha);
1128c2ecf20Sopenharmony_ci	return 1;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic int fm93c56a_datain(struct scsi_qla_host * ha, unsigned short *value)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int i;
1188c2ecf20Sopenharmony_ci	int data = 0;
1198c2ecf20Sopenharmony_ci	int dataBit;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Read the data bits
1228c2ecf20Sopenharmony_ci	 * The first bit is a dummy.  Clock right over it. */
1238c2ecf20Sopenharmony_ci	for (i = 0; i < eeprom_no_data_bits(ha); i++) {
1248c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data |
1258c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_RISE, ha);
1268c2ecf20Sopenharmony_ci		eeprom_cmd(ha->eeprom_cmd_data |
1278c2ecf20Sopenharmony_ci		       AUBURN_EEPROM_CLK_FALL, ha);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		dataBit = (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci		data = (data << 1) | dataBit;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	*value = data;
1358c2ecf20Sopenharmony_ci	return 1;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int eeprom_readword(int eepromAddr, u16 * value,
1398c2ecf20Sopenharmony_ci			   struct scsi_qla_host * ha)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	fm93c56a_select(ha);
1428c2ecf20Sopenharmony_ci	fm93c56a_cmd(ha, FM93C56A_READ, eepromAddr);
1438c2ecf20Sopenharmony_ci	fm93c56a_datain(ha, value);
1448c2ecf20Sopenharmony_ci	fm93c56a_deselect(ha);
1458c2ecf20Sopenharmony_ci	return 1;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci/* Hardware_lock must be set before calling */
1498c2ecf20Sopenharmony_ciu16 rd_nvram_word(struct scsi_qla_host * ha, int offset)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	u16 val = 0;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	/* NOTE: NVRAM uses half-word addresses */
1548c2ecf20Sopenharmony_ci	eeprom_readword(offset, &val, ha);
1558c2ecf20Sopenharmony_ci	return val;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciu8 rd_nvram_byte(struct scsi_qla_host *ha, int offset)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	u16 val = 0;
1618c2ecf20Sopenharmony_ci	u8 rval = 0;
1628c2ecf20Sopenharmony_ci	int index = 0;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (offset & 0x1)
1658c2ecf20Sopenharmony_ci		index = (offset - 1) / 2;
1668c2ecf20Sopenharmony_ci	else
1678c2ecf20Sopenharmony_ci		index = offset / 2;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	val = le16_to_cpu(rd_nvram_word(ha, index));
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (offset & 0x1)
1728c2ecf20Sopenharmony_ci		rval = (u8)((val & 0xff00) >> 8);
1738c2ecf20Sopenharmony_ci	else
1748c2ecf20Sopenharmony_ci		rval = (u8)((val & 0x00ff));
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return rval;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ciint qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	int status = QLA_ERROR;
1828c2ecf20Sopenharmony_ci	uint16_t checksum = 0;
1838c2ecf20Sopenharmony_ci	uint32_t index;
1848c2ecf20Sopenharmony_ci	unsigned long flags;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
1878c2ecf20Sopenharmony_ci	for (index = 0; index < eeprom_size(ha); index++)
1888c2ecf20Sopenharmony_ci		checksum += rd_nvram_word(ha, index);
1898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	if (checksum == 0)
1928c2ecf20Sopenharmony_ci		status = QLA_SUCCESS;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return status;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci/*************************************************************************
1988c2ecf20Sopenharmony_ci *
1998c2ecf20Sopenharmony_ci *			Hardware Semaphore routines
2008c2ecf20Sopenharmony_ci *
2018c2ecf20Sopenharmony_ci *************************************************************************/
2028c2ecf20Sopenharmony_ciint ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	uint32_t value;
2058c2ecf20Sopenharmony_ci	unsigned long flags;
2068c2ecf20Sopenharmony_ci	unsigned int seconds = 30;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	DEBUG2(printk("scsi%ld : Trying to get SEM lock - mask= 0x%x, code = "
2098c2ecf20Sopenharmony_ci		      "0x%x\n", ha->host_no, sem_mask, sem_bits));
2108c2ecf20Sopenharmony_ci	do {
2118c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ha->hardware_lock, flags);
2128c2ecf20Sopenharmony_ci		writel((sem_mask | sem_bits), isp_semaphore(ha));
2138c2ecf20Sopenharmony_ci		value = readw(isp_semaphore(ha));
2148c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ha->hardware_lock, flags);
2158c2ecf20Sopenharmony_ci		if ((value & (sem_mask >> 16)) == sem_bits) {
2168c2ecf20Sopenharmony_ci			DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, "
2178c2ecf20Sopenharmony_ci				      "code = 0x%x\n", ha->host_no,
2188c2ecf20Sopenharmony_ci				      sem_mask, sem_bits));
2198c2ecf20Sopenharmony_ci			return QLA_SUCCESS;
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci		ssleep(1);
2228c2ecf20Sopenharmony_ci	} while (--seconds);
2238c2ecf20Sopenharmony_ci	return QLA_ERROR;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_civoid ql4xxx_sem_unlock(struct scsi_qla_host * ha, u32 sem_mask)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	unsigned long flags;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
2318c2ecf20Sopenharmony_ci	writel(sem_mask, isp_semaphore(ha));
2328c2ecf20Sopenharmony_ci	readl(isp_semaphore(ha));
2338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n", ha->host_no,
2368c2ecf20Sopenharmony_ci		      sem_mask));
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	uint32_t value;
2428c2ecf20Sopenharmony_ci	unsigned long flags;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ha->hardware_lock, flags);
2458c2ecf20Sopenharmony_ci	writel((sem_mask | sem_bits), isp_semaphore(ha));
2468c2ecf20Sopenharmony_ci	value = readw(isp_semaphore(ha));
2478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ha->hardware_lock, flags);
2488c2ecf20Sopenharmony_ci	if ((value & (sem_mask >> 16)) == sem_bits) {
2498c2ecf20Sopenharmony_ci		DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, code = "
2508c2ecf20Sopenharmony_ci			      "0x%x, sema code=0x%x\n", ha->host_no,
2518c2ecf20Sopenharmony_ci			      sem_mask, sem_bits, value));
2528c2ecf20Sopenharmony_ci		return 1;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	return 0;
2558c2ecf20Sopenharmony_ci}
256