summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla4xxx/ql4_nvram.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-10-04 18:57:35 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-10-04 18:57:35 -0700
commit97d41e90fe61399b99d74820cb7f2d6e0fbac91d (patch)
treef759371424a26963b04badbb4433e360be4e8750 /drivers/scsi/qla4xxx/ql4_nvram.c
parent3bdc9d0b408e01c4e556daba0035ba37f603e920 (diff)
parentafaf5a2d341d33b66b47c2716a263ce593460a08 (diff)
Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (54 commits) [SCSI] Initial Commit of qla4xxx [SCSI] raid class: handle component-add errors [SCSI] SCSI megaraid_sas: handle thrown errors [SCSI] SCSI aic94xx: handle sysfs errors [SCSI] SCSI st: fix error handling in module init, sysfs [SCSI] SCSI sd: fix module init/exit error handling [SCSI] SCSI osst: add error handling to module init, sysfs [SCSI] scsi: remove hosts.h [SCSI] scsi: Scsi_Cmnd convertion in aic7xxx_old.c [SCSI] megaraid_sas: sets ioctl timeout and updates version,changelog [SCSI] megaraid_sas: adds tasklet for cmd completion [SCSI] megaraid_sas: prints pending cmds before setting hw_crit_error [SCSI] megaraid_sas: function pointer for disable interrupt [SCSI] megaraid_sas: frame count optimization [SCSI] megaraid_sas: FW transition and q size changes [SCSI] qla2xxx: Update version number to 8.01.07-k2. [SCSI] qla2xxx: Stall mid-layer error handlers while rport is blocked. [SCSI] qla2xxx: Add MODULE_FIRMWARE tags. [SCSI] qla2xxx: Add support for host port state FC transport attribute. [SCSI] qla2xxx: Add support for fabric name FC transport attribute. ...
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_nvram.c')
-rw-r--r--drivers/scsi/qla4xxx/ql4_nvram.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_nvram.c b/drivers/scsi/qla4xxx/ql4_nvram.c
new file mode 100644
index 000000000000..e3957ca5b645
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_nvram.c
@@ -0,0 +1,224 @@
+/*
+ * QLogic iSCSI HBA Driver
+ * Copyright (c) 2003-2006 QLogic Corporation
+ *
+ * See LICENSE.qla4xxx for copyright and licensing details.
+ */
+
+#include "ql4_def.h"
+
+static inline int eeprom_size(struct scsi_qla_host *ha)
+{
+ return is_qla4022(ha) ? FM93C86A_SIZE_16 : FM93C66A_SIZE_16;
+}
+
+static inline int eeprom_no_addr_bits(struct scsi_qla_host *ha)
+{
+ return is_qla4022(ha) ? FM93C86A_NO_ADDR_BITS_16 :
+ FM93C56A_NO_ADDR_BITS_16;
+}
+
+static inline int eeprom_no_data_bits(struct scsi_qla_host *ha)
+{
+ return FM93C56A_DATA_BITS_16;
+}
+
+static int fm93c56a_select(struct scsi_qla_host * ha)
+{
+ DEBUG5(printk(KERN_ERR "fm93c56a_select:\n"));
+
+ ha->eeprom_cmd_data = AUBURN_EEPROM_CS_1 | 0x000f0000;
+ writel(ha->eeprom_cmd_data, isp_nvram(ha));
+ readl(isp_nvram(ha));
+ return 1;
+}
+
+static int fm93c56a_cmd(struct scsi_qla_host * ha, int cmd, int addr)
+{
+ int i;
+ int mask;
+ int dataBit;
+ int previousBit;
+
+ /* Clock in a zero, then do the start bit. */
+ writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1, isp_nvram(ha));
+ writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
+ AUBURN_EEPROM_CLK_RISE, isp_nvram(ha));
+ writel(ha->eeprom_cmd_data | AUBURN_EEPROM_DO_1 |
+ AUBURN_EEPROM_CLK_FALL, isp_nvram(ha));
+ readl(isp_nvram(ha));
+ mask = 1 << (FM93C56A_CMD_BITS - 1);
+
+ /* Force the previous data bit to be different. */
+ previousBit = 0xffff;
+ for (i = 0; i < FM93C56A_CMD_BITS; i++) {
+ dataBit =
+ (cmd & mask) ? AUBURN_EEPROM_DO_1 : AUBURN_EEPROM_DO_0;
+ if (previousBit != dataBit) {
+
+ /*
+ * If the bit changed, then change the DO state to
+ * match.
+ */
+ writel(ha->eeprom_cmd_data | dataBit, isp_nvram(ha));
+ previousBit = dataBit;
+ }
+ writel(ha->eeprom_cmd_data | dataBit |
+ AUBURN_EEPROM_CLK_RISE, isp_nvram(ha));
+ writel(ha->eeprom_cmd_data | dataBit |
+ AUBURN_EEPROM_CLK_FALL, isp_nvram(ha));
+ readl(isp_nvram(ha));
+ cmd = cmd << 1;
+ }
+ mask = 1 << (eeprom_no_addr_bits(ha) - 1);
+
+ /* Force the previous data bit to be different. */
+ previousBit = 0xffff;
+ for (i = 0; i < eeprom_no_addr_bits(ha); i++) {
+ dataBit = addr & mask ? AUBURN_EEPROM_DO_1 :
+ AUBURN_EEPROM_DO_0;
+ if (previousBit != dataBit) {
+ /*
+ * If the bit changed, then change the DO state to
+ * match.
+ */
+ writel(ha->eeprom_cmd_data | dataBit, isp_nvram(ha));
+ previousBit = dataBit;
+ }
+ writel(ha->eeprom_cmd_data | dataBit |
+ AUBURN_EEPROM_CLK_RISE, isp_nvram(ha));
+ writel(ha->eeprom_cmd_data | dataBit |
+ AUBURN_EEPROM_CLK_FALL, isp_nvram(ha));
+ readl(isp_nvram(ha));
+ addr = addr << 1;
+ }
+ return 1;
+}
+
+static int fm93c56a_deselect(struct scsi_qla_host * ha)
+{
+ ha->eeprom_cmd_data = AUBURN_EEPROM_CS_0 | 0x000f0000;
+ writel(ha->eeprom_cmd_data, isp_nvram(ha));
+ readl(isp_nvram(ha));
+ return 1;
+}
+
+static int fm93c56a_datain(struct scsi_qla_host * ha, unsigned short *value)
+{
+ int i;
+ int data = 0;
+ int dataBit;
+
+ /* Read the data bits
+ * The first bit is a dummy. Clock right over it. */
+ for (i = 0; i < eeprom_no_data_bits(ha); i++) {
+ writel(ha->eeprom_cmd_data |
+ AUBURN_EEPROM_CLK_RISE, isp_nvram(ha));
+ writel(ha->eeprom_cmd_data |
+ AUBURN_EEPROM_CLK_FALL, isp_nvram(ha));
+ dataBit =
+ (readw(isp_nvram(ha)) & AUBURN_EEPROM_DI_1) ? 1 : 0;
+ data = (data << 1) | dataBit;
+ }
+
+ *value = data;
+ return 1;
+}
+
+static int eeprom_readword(int eepromAddr, u16 * value,
+ struct scsi_qla_host * ha)
+{
+ fm93c56a_select(ha);
+ fm93c56a_cmd(ha, FM93C56A_READ, eepromAddr);
+ fm93c56a_datain(ha, value);
+ fm93c56a_deselect(ha);
+ return 1;
+}
+
+/* Hardware_lock must be set before calling */
+u16 rd_nvram_word(struct scsi_qla_host * ha, int offset)
+{
+ u16 val;
+
+ /* NOTE: NVRAM uses half-word addresses */
+ eeprom_readword(offset, &val, ha);
+ return val;
+}
+
+int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha)
+{
+ int status = QLA_ERROR;
+ uint16_t checksum = 0;
+ uint32_t index;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (index = 0; index < eeprom_size(ha); index++)
+ checksum += rd_nvram_word(ha, index);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (checksum == 0)
+ status = QLA_SUCCESS;
+
+ return status;
+}
+
+/*************************************************************************
+ *
+ * Hardware Semaphore routines
+ *
+ *************************************************************************/
+int ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
+{
+ uint32_t value;
+ unsigned long flags;
+ unsigned int seconds = 30;
+
+ DEBUG2(printk("scsi%ld : Trying to get SEM lock - mask= 0x%x, code = "
+ "0x%x\n", ha->host_no, sem_mask, sem_bits));
+ do {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ writel((sem_mask | sem_bits), isp_semaphore(ha));
+ value = readw(isp_semaphore(ha));
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if ((value & (sem_mask >> 16)) == sem_bits) {
+ DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, "
+ "code = 0x%x\n", ha->host_no,
+ sem_mask, sem_bits));
+ return QLA_SUCCESS;
+ }
+ ssleep(1);
+ } while (--seconds);
+ return QLA_ERROR;
+}
+
+void ql4xxx_sem_unlock(struct scsi_qla_host * ha, u32 sem_mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ writel(sem_mask, isp_semaphore(ha));
+ readl(isp_semaphore(ha));
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG2(printk("scsi%ld : UNLOCK SEM - mask= 0x%x\n", ha->host_no,
+ sem_mask));
+}
+
+int ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits)
+{
+ uint32_t value;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ writel((sem_mask | sem_bits), isp_semaphore(ha));
+ value = readw(isp_semaphore(ha));
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if ((value & (sem_mask >> 16)) == sem_bits) {
+ DEBUG2(printk("scsi%ld : Got SEM LOCK - mask= 0x%x, code = "
+ "0x%x, sema code=0x%x\n", ha->host_no,
+ sem_mask, sem_bits, value));
+ return 1;
+ }
+ return 0;
+}