summary refs log tree commit diff
path: root/drivers/scsi/qla2xxx
diff options
context:
space:
mode:
authorandrew.vasquez@qlogic.com <andrew.vasquez@qlogic.com>2006-01-31 16:05:07 -0800
committer <jejb@mulgrave.il.steeleye.com>2006-02-04 16:11:57 -0600
commitf6df144cca19cc60dda6dcce65d236b70cc46494 (patch)
tree2cd4ae47df5808e46e0387c44ff436879b0e0111 /drivers/scsi/qla2xxx
parent392e2f651c8a83484116a407a9f121e534c22b5a (diff)
downloadlinux-f6df144cca19cc60dda6dcce65d236b70cc46494.tar.gz
[SCSI] qla2xxx: Add beacon support via class-device attribute.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c50
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h21
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h9
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c16
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c294
5 files changed, 387 insertions, 3 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 5a8d5c4c69ba..049e5cf1af7f 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -196,6 +196,9 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha)
 
 	sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_fw_dump_attr);
 	sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr);
+
+	if (ha->beacon_blink_led == 1)
+		ha->isp_ops.beacon_off(ha);
 }
 
 /* Scsi_Host attributes. */
@@ -383,6 +386,50 @@ qla2x00_zio_timer_store(struct class_device *cdev, const char *buf,
 	return strlen(buf);
 }
 
+static ssize_t
+qla2x00_beacon_show(struct class_device *cdev, char *buf)
+{
+	scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev));
+	int len = 0;
+
+	if (ha->beacon_blink_led)
+		len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n");
+	else
+		len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n");
+	return len;
+}
+
+static ssize_t
+qla2x00_beacon_store(struct class_device *cdev, const char *buf,
+    size_t count)
+{
+	scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev));
+	int val = 0;
+	int rval;
+
+	if (IS_QLA2100(ha) || IS_QLA2200(ha))
+		return -EPERM;
+
+	if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) {
+		qla_printk(KERN_WARNING, ha,
+		    "Abort ISP active -- ignoring beacon request.\n");
+		return -EBUSY;
+	}
+
+	if (sscanf(buf, "%d", &val) != 1)
+		return -EINVAL;
+
+	if (val)
+		rval = ha->isp_ops.beacon_on(ha);
+	else
+		rval = ha->isp_ops.beacon_off(ha);
+
+	if (rval != QLA_SUCCESS)
+		count = 0;
+
+	return count;
+}
+
 static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show,
 	NULL);
 static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
@@ -397,6 +444,8 @@ static CLASS_DEVICE_ATTR(zio, S_IRUGO | S_IWUSR, qla2x00_zio_show,
     qla2x00_zio_store);
 static CLASS_DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show,
     qla2x00_zio_timer_store);
+static CLASS_DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show,
+    qla2x00_beacon_store);
 
 struct class_device_attribute *qla2x00_host_attrs[] = {
 	&class_device_attr_driver_version,
@@ -410,6 +459,7 @@ struct class_device_attribute *qla2x00_host_attrs[] = {
 	&class_device_attr_state,
 	&class_device_attr_zio,
 	&class_device_attr_zio_timer,
+	&class_device_attr_beacon,
 	NULL,
 };
 
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 414580800dc0..7cb8b5a5e659 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -182,6 +182,13 @@
 #define WRT_REG_DWORD(addr, data)	writel(data,addr)
 
 /*
+ * The ISP2312 v2 chip cannot access the FLASH/GPIO registers via MMIO in an
+ * 133Mhz slot.
+ */
+#define RD_REG_WORD_PIO(addr)		(inw((unsigned long)addr))
+#define WRT_REG_WORD_PIO(addr, data)	(outw(data,(unsigned long)addr))
+
+/*
  * Fibre Channel device definitions.
  */
 #define WWN_SIZE		8	/* Size of WWPN, WWN & WWNN */
@@ -433,6 +440,9 @@ struct device_reg_2xxx {
 #define GPIO_LED_GREEN_ON_AMBER_OFF	0x0040
 #define GPIO_LED_GREEN_OFF_AMBER_ON	0x0080
 #define GPIO_LED_GREEN_ON_AMBER_ON	0x00C0
+#define GPIO_LED_ALL_OFF		0x0000
+#define GPIO_LED_RED_ON_OTHER_OFF	0x0001	/* isp2322 */
+#define GPIO_LED_RGA_ON			0x00C1	/* isp2322: red green amber */
 
 	union {
 		struct {
@@ -2200,6 +2210,10 @@ struct isp_operations {
 
 	void (*fw_dump) (struct scsi_qla_host *, int);
 	void (*ascii_fw_dump) (struct scsi_qla_host *);
+
+	int (*beacon_on) (struct scsi_qla_host *);
+	int (*beacon_off) (struct scsi_qla_host *);
+	void (*beacon_blink) (struct scsi_qla_host *);
 };
 
 /*
@@ -2493,7 +2507,12 @@ typedef struct scsi_qla_host {
 
 	/* Needed for BEACON */
 	uint16_t	beacon_blink_led;
-	uint16_t	beacon_green_on;
+	uint8_t		beacon_color_state;
+#define QLA_LED_GRN_ON		0x01
+#define QLA_LED_YLW_ON		0x02
+#define QLA_LED_ABR_ON		0x04
+#define QLA_LED_ALL_ON		0x07	/* yellow, green, amber. */
+					/* ISP2322: red, green, amber. */
 
 	uint16_t	zio_mode;
 	uint16_t	zio_timer;
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index f2f5454a05e9..eb35198bc8cf 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -75,8 +75,6 @@ extern void qla2x00_cmd_timeout(srb_t *);
 extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int);
 extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *, int);
 
-extern void qla2x00_blink_led(scsi_qla_host_t *);
-
 extern int qla2x00_down_timeout(struct semaphore *, unsigned long);
 
 extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
@@ -235,6 +233,13 @@ extern int qla2x00_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
 extern int qla24xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
     uint32_t);
 
+extern int qla2x00_beacon_on(struct scsi_qla_host *);
+extern int qla2x00_beacon_off(struct scsi_qla_host *);
+extern void qla2x00_beacon_blink(struct scsi_qla_host *);
+extern int qla24xx_beacon_on(struct scsi_qla_host *);
+extern int qla24xx_beacon_off(struct scsi_qla_host *);
+extern void qla24xx_beacon_blink(struct scsi_qla_host *);
+
 /*
  * Global Function Prototypes in qla_dbg.c source file.
  */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 6e133edb2016..57179dabcccf 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -1365,6 +1365,9 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info)
 		ha->isp_ops.intr_handler = qla2300_intr_handler;
 		ha->isp_ops.fw_dump = qla2300_fw_dump;
 		ha->isp_ops.ascii_fw_dump = qla2300_ascii_fw_dump;
+		ha->isp_ops.beacon_on = qla2x00_beacon_on;
+		ha->isp_ops.beacon_off = qla2x00_beacon_off;
+		ha->isp_ops.beacon_blink = qla2x00_beacon_blink;
 		ha->gid_list_info_size = 6;
 	} else if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) {
 		host->max_id = MAX_TARGETS_2200;
@@ -1401,6 +1404,9 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info)
 		ha->isp_ops.write_nvram = qla24xx_write_nvram_data;
 		ha->isp_ops.fw_dump = qla24xx_fw_dump;
 		ha->isp_ops.ascii_fw_dump = qla24xx_ascii_fw_dump;
+		ha->isp_ops.beacon_on = qla24xx_beacon_on;
+		ha->isp_ops.beacon_off = qla24xx_beacon_off;
+		ha->isp_ops.beacon_blink = qla24xx_beacon_blink;
 		ha->gid_list_info_size = 8;
 	}
 	host->can_queue = ha->request_q_length + 128;
@@ -2315,6 +2321,9 @@ qla2x00_do_dpc(void *data)
 		if (!ha->interrupts_on)
 			ha->isp_ops.enable_intrs(ha);
 
+		if (test_and_clear_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags))
+			ha->isp_ops.beacon_blink(ha);
+
 		ha->dpc_active = 0;
 	} /* End of while(1) */
 
@@ -2492,6 +2501,12 @@ qla2x00_timer(scsi_qla_host_t *ha)
 		    atomic_read(&ha->loop_down_timer)));
 	}
 
+	/* Check if beacon LED needs to be blinked */
+	if (ha->beacon_blink_led == 1) {
+		set_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags);
+		start_dpc++;
+	}
+
 	/* Schedule the DPC routine if needed */
 	if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
 	    test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
@@ -2500,6 +2515,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
 	    start_dpc ||
 	    test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
 	    test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
+	    test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
 	    test_bit(RELOGIN_NEEDED, &ha->dpc_flags)) &&
 	    ha->dpc_wait && !ha->dpc_active) {
 
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index f4d755a643e4..3105bb93cc19 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -695,3 +695,297 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr,
 
 	return ret;
 }
+
+
+static inline void
+qla2x00_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+{
+	if (IS_QLA2322(ha)) {
+		/* Flip all colors. */
+		if (ha->beacon_color_state == QLA_LED_ALL_ON) {
+			/* Turn off. */
+			ha->beacon_color_state = 0;
+			*pflags = GPIO_LED_ALL_OFF;
+		} else {
+			/* Turn on. */
+			ha->beacon_color_state = QLA_LED_ALL_ON;
+			*pflags = GPIO_LED_RGA_ON;
+		}
+	} else {
+		/* Flip green led only. */
+		if (ha->beacon_color_state == QLA_LED_GRN_ON) {
+			/* Turn off. */
+			ha->beacon_color_state = 0;
+			*pflags = GPIO_LED_GREEN_OFF_AMBER_OFF;
+		} else {
+			/* Turn on. */
+			ha->beacon_color_state = QLA_LED_GRN_ON;
+			*pflags = GPIO_LED_GREEN_ON_AMBER_OFF;
+		}
+	}
+}
+
+void
+qla2x00_beacon_blink(struct scsi_qla_host *ha)
+{
+	uint16_t gpio_enable;
+	uint16_t gpio_data;
+	uint16_t led_color = 0;
+	unsigned long flags;
+	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+	if (ha->pio_address)
+		reg = (struct device_reg_2xxx __iomem *)ha->pio_address;
+
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+
+	/* Save the Original GPIOE. */
+	if (ha->pio_address) {
+		gpio_enable = RD_REG_WORD_PIO(&reg->gpioe);
+		gpio_data = RD_REG_WORD_PIO(&reg->gpiod);
+	} else {
+		gpio_enable = RD_REG_WORD(&reg->gpioe);
+		gpio_data = RD_REG_WORD(&reg->gpiod);
+	}
+
+	/* Set the modified gpio_enable values */
+	gpio_enable |= GPIO_LED_MASK;
+
+	if (ha->pio_address) {
+		WRT_REG_WORD_PIO(&reg->gpioe, gpio_enable);
+	} else {
+		WRT_REG_WORD(&reg->gpioe, gpio_enable);
+		RD_REG_WORD(&reg->gpioe);
+	}
+
+	qla2x00_flip_colors(ha, &led_color);
+
+	/* Clear out any previously set LED color. */
+	gpio_data &= ~GPIO_LED_MASK;
+
+	/* Set the new input LED color to GPIOD. */
+	gpio_data |= led_color;
+
+	/* Set the modified gpio_data values */
+	if (ha->pio_address) {
+		WRT_REG_WORD_PIO(&reg->gpiod, gpio_data);
+	} else {
+		WRT_REG_WORD(&reg->gpiod, gpio_data);
+		RD_REG_WORD(&reg->gpiod);
+	}
+
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+int
+qla2x00_beacon_on(struct scsi_qla_host *ha)
+{
+	uint16_t gpio_enable;
+	uint16_t gpio_data;
+	unsigned long flags;
+	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+	ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
+
+	if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+		qla_printk(KERN_WARNING, ha,
+		    "Unable to update fw options (beacon on).\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	if (ha->pio_address)
+		reg = (struct device_reg_2xxx __iomem *)ha->pio_address;
+
+	/* Turn off LEDs. */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	if (ha->pio_address) {
+		gpio_enable = RD_REG_WORD_PIO(&reg->gpioe);
+		gpio_data = RD_REG_WORD_PIO(&reg->gpiod);
+	} else {
+		gpio_enable = RD_REG_WORD(&reg->gpioe);
+		gpio_data = RD_REG_WORD(&reg->gpiod);
+	}
+	gpio_enable |= GPIO_LED_MASK;
+
+	/* Set the modified gpio_enable values. */
+	if (ha->pio_address) {
+		WRT_REG_WORD_PIO(&reg->gpioe, gpio_enable);
+	} else {
+		WRT_REG_WORD(&reg->gpioe, gpio_enable);
+		RD_REG_WORD(&reg->gpioe);
+	}
+
+	/* Clear out previously set LED colour. */
+	gpio_data &= ~GPIO_LED_MASK;
+	if (ha->pio_address) {
+		WRT_REG_WORD_PIO(&reg->gpiod, gpio_data);
+	} else {
+		WRT_REG_WORD(&reg->gpiod, gpio_data);
+		RD_REG_WORD(&reg->gpiod);
+	}
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	/*
+	 * Let the per HBA timer kick off the blinking process based on
+	 * the following flags. No need to do anything else now.
+	 */
+	ha->beacon_blink_led = 1;
+	ha->beacon_color_state = 0;
+
+	return QLA_SUCCESS;
+}
+
+int
+qla2x00_beacon_off(struct scsi_qla_host *ha)
+{
+	int rval = QLA_SUCCESS;
+
+	ha->beacon_blink_led = 0;
+
+	/* Set the on flag so when it gets flipped it will be off. */
+	if (IS_QLA2322(ha))
+		ha->beacon_color_state = QLA_LED_ALL_ON;
+	else
+		ha->beacon_color_state = QLA_LED_GRN_ON;
+
+	ha->isp_ops.beacon_blink(ha);	/* This turns green LED off */
+
+	ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+	ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
+
+	rval = qla2x00_set_fw_options(ha, ha->fw_options);
+	if (rval != QLA_SUCCESS)
+		qla_printk(KERN_WARNING, ha,
+		    "Unable to update fw options (beacon off).\n");
+	return rval;
+}
+
+
+static inline void
+qla24xx_flip_colors(scsi_qla_host_t *ha, uint16_t *pflags)
+{
+	/* Flip all colors. */
+	if (ha->beacon_color_state == QLA_LED_ALL_ON) {
+		/* Turn off. */
+		ha->beacon_color_state = 0;
+		*pflags = 0;
+	} else {
+		/* Turn on. */
+		ha->beacon_color_state = QLA_LED_ALL_ON;
+		*pflags = GPDX_LED_YELLOW_ON | GPDX_LED_AMBER_ON;
+	}
+}
+
+void
+qla24xx_beacon_blink(struct scsi_qla_host *ha)
+{
+	uint16_t led_color = 0;
+	uint32_t gpio_data;
+	unsigned long flags;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+	/* Save the Original GPIOD. */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+	/* Enable the gpio_data reg for update. */
+	gpio_data |= GPDX_LED_UPDATE_MASK;
+
+	WRT_REG_DWORD(&reg->gpiod, gpio_data);
+	gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+	/* Set the color bits. */
+	qla24xx_flip_colors(ha, &led_color);
+
+	/* Clear out any previously set LED color. */
+	gpio_data &= ~GPDX_LED_COLOR_MASK;
+
+	/* Set the new input LED color to GPIOD. */
+	gpio_data |= led_color;
+
+	/* Set the modified gpio_data values. */
+	WRT_REG_DWORD(&reg->gpiod, gpio_data);
+	gpio_data = RD_REG_DWORD(&reg->gpiod);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+int
+qla24xx_beacon_on(struct scsi_qla_host *ha)
+{
+	uint32_t gpio_data;
+	unsigned long flags;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+	if (ha->beacon_blink_led == 0) {
+		/* Enable firmware for update */
+		ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
+
+		if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS)
+			return QLA_FUNCTION_FAILED;
+
+		if (qla2x00_get_fw_options(ha, ha->fw_options) !=
+		    QLA_SUCCESS) {
+			qla_printk(KERN_WARNING, ha,
+			    "Unable to update fw options (beacon on).\n");
+			return QLA_FUNCTION_FAILED;
+		}
+
+		spin_lock_irqsave(&ha->hardware_lock, flags);
+		gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+		/* Enable the gpio_data reg for update. */
+		gpio_data |= GPDX_LED_UPDATE_MASK;
+		WRT_REG_DWORD(&reg->gpiod, gpio_data);
+		RD_REG_DWORD(&reg->gpiod);
+
+		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	}
+
+	/* So all colors blink together. */
+	ha->beacon_color_state = 0;
+
+	/* Let the per HBA timer kick off the blinking process. */
+	ha->beacon_blink_led = 1;
+
+	return QLA_SUCCESS;
+}
+
+int
+qla24xx_beacon_off(struct scsi_qla_host *ha)
+{
+	uint32_t gpio_data;
+	unsigned long flags;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+	ha->beacon_blink_led = 0;
+	ha->beacon_color_state = QLA_LED_ALL_ON;
+
+	ha->isp_ops.beacon_blink(ha);	/* Will flip to all off. */
+
+	/* Give control back to firmware. */
+	spin_lock_irqsave(&ha->hardware_lock, flags);
+	gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+	/* Disable the gpio_data reg for update. */
+	gpio_data &= ~GPDX_LED_UPDATE_MASK;
+	WRT_REG_DWORD(&reg->gpiod, gpio_data);
+	RD_REG_DWORD(&reg->gpiod);
+	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+	ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
+
+	if (qla2x00_set_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+		qla_printk(KERN_WARNING, ha,
+		    "Unable to update fw options (beacon off).\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	if (qla2x00_get_fw_options(ha, ha->fw_options) != QLA_SUCCESS) {
+		qla_printk(KERN_WARNING, ha,
+		    "Unable to get fw options (beacon off).\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	return QLA_SUCCESS;
+}