diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-04 18:57:35 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-04 18:57:35 -0700 |
commit | 97d41e90fe61399b99d74820cb7f2d6e0fbac91d (patch) | |
tree | f759371424a26963b04badbb4433e360be4e8750 | |
parent | 3bdc9d0b408e01c4e556daba0035ba37f603e920 (diff) | |
parent | afaf5a2d341d33b66b47c2716a263ce593460a08 (diff) | |
download | kernel-common-97d41e90fe61399b99d74820cb7f2d6e0fbac91d.tar.gz kernel-common-97d41e90fe61399b99d74820cb7f2d6e0fbac91d.tar.bz2 kernel-common-97d41e90fe61399b99d74820cb7f2d6e0fbac91d.zip |
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.
...
80 files changed, 9921 insertions, 775 deletions
diff --git a/Documentation/scsi/ChangeLog.megaraid_sas b/Documentation/scsi/ChangeLog.megaraid_sas index d9e5960dafd5..5eb927544990 100644 --- a/Documentation/scsi/ChangeLog.megaraid_sas +++ b/Documentation/scsi/ChangeLog.megaraid_sas @@ -1,4 +1,49 @@ +1 Release Date : Mon Oct 02 11:21:32 PDT 2006 - Sumant Patro <Sumant.Patro@lsil.com> +2 Current Version : 00.00.03.05 +3 Older Version : 00.00.03.04 + +i. PCI_DEVICE macro used + + Convert the pci_device_id-table of the megaraid_sas-driver to the PCI_DEVICE-macro, to safe some lines. + + - Henrik Kretzschmar <henne@nachtwindheim.de> +ii. All compiler warnings removed +iii. megasas_ctrl_info struct reverted to 3.02 release +iv. Default value of megasas_dbg_lvl set to 0 +v. Removing in megasas_exit the sysfs entry created for megasas_dbg_lvl +vi. In megasas_teardown_frame_pool(), cmd->frame was passed instead of + cmd->sense to pci_pool_free. Fixed. Bug was pointed out by + Eric Sesterhenn + +1 Release Date : Wed Sep 13 14:22:51 PDT 2006 - Sumant Patro <Sumant.Patro@lsil.com> +2 Current Version : 00.00.03.04 +3 Older Version : 00.00.03.03 + +i. Added Reboot notify +ii. Reduced by 1 max cmds sent to FW from Driver to make the reply_q_sz same + as Max Cmds FW can support + +1 Release Date : Tue Aug 22 16:33:14 PDT 2006 - Sumant Patro <Sumant.Patro@lsil.com> +2 Current Version : 00.00.03.03 +3 Older Version : 00.00.03.02 + +i. Send stop adapter to FW & Dump pending FW cmds before declaring adapter dead. + New varible added to set dbg level. +ii. Disable interrupt made as fn pointer as they are different for 1068 / 1078 +iii. Frame count optimization. Main frame can contain 2 SGE for 64 bit SGLs and + 3 SGE for 32 bit SGL +iv. Tasklet added for cmd completion +v. If FW in operational state before firing INIT, now we send RESET Flag to FW instead of just READY. This is used to do soft reset. +vi. megasas_ctrl_prop structure updated (based on FW struct) +vii. Added print : FW now in Ready State during initialization + +1 Release Date : Sun Aug 06 22:49:52 PDT 2006 - Sumant Patro <Sumant.Patro@lsil.com> +2 Current Version : 00.00.03.02 +3 Older Version : 00.00.03.01 + +i. Added FW tranistion state for Hotplug scenario + 1 Release Date : Sun May 14 22:49:52 PDT 2006 - Sumant Patro <Sumant.Patro@lsil.com> 2 Current Version : 00.00.03.01 3 Older Version : 00.00.02.04 diff --git a/drivers/message/fusion/linux_compat.h b/drivers/message/fusion/linux_compat.h index 048b5b8610e3..bb2bf5aa0b62 100644 --- a/drivers/message/fusion/linux_compat.h +++ b/drivers/message/fusion/linux_compat.h @@ -6,13 +6,4 @@ #include <linux/version.h> #include <scsi/scsi_device.h> -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,6)) -static int inline scsi_device_online(struct scsi_device *sdev) -{ - return sdev->online; -} -#endif - - -/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #endif /* _LINUX_COMPAT_H */ diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index 5a9475e56d0e..da173159cedb 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -2211,7 +2211,7 @@ static int __init twa_init(void) { printk(KERN_WARNING "3ware 9000 Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); - return pci_module_init(&twa_driver); + return pci_register_driver(&twa_driver); } /* End twa_init() */ /* This function is called on driver exit */ diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index f3a5f422a8e4..2d4cb6721fa6 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -2486,7 +2486,7 @@ static int __init tw_init(void) { printk(KERN_WARNING "3ware Storage Controller device driver for Linux v%s.\n", TW_DRIVER_VERSION); - return pci_module_init(&tw_driver); + return pci_register_driver(&tw_driver); } /* End tw_init() */ /* This function is called on driver exit */ diff --git a/drivers/scsi/3w-xxxx.h b/drivers/scsi/3w-xxxx.h index 31fe5ea15920..bbd654a2b9b1 100644 --- a/drivers/scsi/3w-xxxx.h +++ b/drivers/scsi/3w-xxxx.h @@ -74,7 +74,7 @@ static char *tw_aen_string[] = { [0x00D] = "ERROR: Logical unit deleted: Unit #", [0x00F] = "WARNING: SMART threshold exceeded: Port #", [0x021] = "WARNING: ATA UDMA downgrade: Port #", - [0x021] = "WARNING: ATA UDMA upgrade: Port #", + [0x022] = "WARNING: ATA UDMA upgrade: Port #", [0x023] = "WARNING: Sector repair occurred: Port #", [0x024] = "ERROR: SBUF integrity check failure", [0x025] = "ERROR: Lost cached write: Port #", diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c6dfb6fa13bf..9540eb8efdcb 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1016,7 +1016,7 @@ config SCSI_SYM53C8XX_MMIO config SCSI_IPR tristate "IBM Power Linux RAID adapter support" - depends on PCI && SCSI + depends on PCI && SCSI && ATA select FW_LOADER ---help--- This driver supports the IBM Power Linux family RAID adapters. @@ -1246,6 +1246,7 @@ config SCSI_QLOGICPTI module will be called qlogicpti. source "drivers/scsi/qla2xxx/Kconfig" +source "drivers/scsi/qla4xxx/Kconfig" config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" @@ -1262,8 +1263,8 @@ config SCSI_SEAGATE These are 8-bit SCSI controllers; the ST-01 is also supported by this driver. It is explained in section 3.9 of the SCSI-HOWTO, available from <http://www.tldp.org/docs.html#howto>. If it - doesn't work out of the box, you may have to change some settings in - <file:drivers/scsi/seagate.h>. + doesn't work out of the box, you may have to change some macros at + compiletime, which are described in <file:drivers/scsi/seagate.c>. To compile this driver as a module, choose M here: the module will be called seagate. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 1ef951be7a5d..bcca39c3bcbf 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -84,6 +84,7 @@ obj-$(CONFIG_SCSI_QLOGIC_FAS) += qlogicfas408.o qlogicfas.o obj-$(CONFIG_PCMCIA_QLOGIC) += qlogicfas408.o obj-$(CONFIG_SCSI_QLOGIC_1280) += qla1280.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx/ +obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx/ obj-$(CONFIG_SCSI_LPFC) += lpfc/ obj-$(CONFIG_SCSI_PAS16) += pas16.o obj-$(CONFIG_SCSI_SEAGATE) += seagate.o diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c index d7e9fab54c60..2684150917e6 100644 --- a/drivers/scsi/a100u2w.c +++ b/drivers/scsi/a100u2w.c @@ -1187,7 +1187,7 @@ static struct pci_driver inia100_pci_driver = { static int __init inia100_init(void) { - return pci_module_init(&inia100_pci_driver); + return pci_register_driver(&inia100_pci_driver); } static void __exit inia100_exit(void) diff --git a/drivers/scsi/aic7xxx/aic79xx_inline.h b/drivers/scsi/aic7xxx/aic79xx_inline.h index 8ad3ce945b9e..a3266e066c00 100644 --- a/drivers/scsi/aic7xxx/aic79xx_inline.h +++ b/drivers/scsi/aic7xxx/aic79xx_inline.h @@ -527,7 +527,8 @@ ahd_inw(struct ahd_softc *ahd, u_int port) * or have other side effects when the low byte is * read. */ - return ((ahd_inb(ahd, port+1) << 8) | ahd_inb(ahd, port)); + uint16_t r = ahd_inb(ahd, port+1) << 8; + return r | ahd_inb(ahd, port); } static __inline void diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 50a41eda580e..4b5354201807 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -198,7 +198,7 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int ahd_linux_pci_init(void) { - return (pci_module_init(&aic79xx_pci_driver)); + return pci_register_driver(&aic79xx_pci_driver); } void diff --git a/drivers/scsi/aic7xxx/aic7xxx_inline.h b/drivers/scsi/aic7xxx/aic7xxx_inline.h index 2cc8a17ed8b4..8e1954cdd84f 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_inline.h +++ b/drivers/scsi/aic7xxx/aic7xxx_inline.h @@ -300,7 +300,8 @@ ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id, static __inline uint16_t ahc_inw(struct ahc_softc *ahc, u_int port) { - return ((ahc_inb(ahc, port+1) << 8) | ahc_inb(ahc, port)); + uint16_t r = ahc_inb(ahc, port+1) << 8; + return r | ahc_inb(ahc, port); } static __inline void diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index 7e42f07a27f3..d20ca514e9f3 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -246,8 +246,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) int ahc_linux_pci_init(void) { - /* Translate error or zero return into zero or one */ - return pci_module_init(&aic7xxx_pci_driver) ? 0 : 1; + return pci_register_driver(&aic7xxx_pci_driver); } void diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c index 10353379a074..3eae8062a02e 100644 --- a/drivers/scsi/aic7xxx_old.c +++ b/drivers/scsi/aic7xxx_old.c @@ -780,24 +780,26 @@ typedef enum { } ahc_bugs; struct aic7xxx_scb { - struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ - Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */ - struct aic7xxx_scb *q_next; /* next scb in queue */ - volatile scb_flag_type flags; /* current state of scb */ - struct hw_scatterlist *sg_list; /* SG list in adapter format */ - unsigned char tag_action; - unsigned char sg_count; - unsigned char *sense_cmd; /* - * Allocate 6 characters for - * sense command. - */ - unsigned char *cmnd; - unsigned int sg_length; /* We init this during buildscb so we - * don't have to calculate anything - * during underflow/overflow/stat code - */ - void *kmalloc_ptr; - struct aic7xxx_scb_dma *scb_dma; + struct aic7xxx_hwscb *hscb; /* corresponding hardware scb */ + struct scsi_cmnd *cmd; /* scsi_cmnd for this scb */ + struct aic7xxx_scb *q_next; /* next scb in queue */ + volatile scb_flag_type flags; /* current state of scb */ + struct hw_scatterlist *sg_list; /* SG list in adapter format */ + unsigned char tag_action; + unsigned char sg_count; + unsigned char *sense_cmd; /* + * Allocate 6 characters for + * sense command. + */ + unsigned char *cmnd; + unsigned int sg_length; /* + * We init this during + * buildscb so we don't have + * to calculate anything during + * underflow/overflow/stat code + */ + void *kmalloc_ptr; + struct aic7xxx_scb_dma *scb_dma; }; /* @@ -918,79 +920,77 @@ struct aic7xxx_host { * We are grouping things here....first, items that get either read or * written with nearly every interrupt */ - volatile long flags; - ahc_feature features; /* chip features */ - unsigned long base; /* card base address */ - volatile unsigned char __iomem *maddr; /* memory mapped address */ - unsigned long isr_count; /* Interrupt count */ - unsigned long spurious_int; - scb_data_type *scb_data; - struct aic7xxx_cmd_queue { - Scsi_Cmnd *head; - Scsi_Cmnd *tail; - } completeq; + volatile long flags; + ahc_feature features; /* chip features */ + unsigned long base; /* card base address */ + volatile unsigned char __iomem *maddr; /* memory mapped address */ + unsigned long isr_count; /* Interrupt count */ + unsigned long spurious_int; + scb_data_type *scb_data; + struct aic7xxx_cmd_queue { + struct scsi_cmnd *head; + struct scsi_cmnd *tail; + } completeq; - /* - * Things read/written on nearly every entry into aic7xxx_queue() - */ - volatile scb_queue_type waiting_scbs; - unsigned char unpause; /* unpause value for HCNTRL */ - unsigned char pause; /* pause value for HCNTRL */ - volatile unsigned char qoutfifonext; - volatile unsigned char activescbs; /* active scbs */ - volatile unsigned char max_activescbs; - volatile unsigned char qinfifonext; - volatile unsigned char *untagged_scbs; - volatile unsigned char *qoutfifo; - volatile unsigned char *qinfifo; - - unsigned char dev_last_queue_full[MAX_TARGETS]; - unsigned char dev_last_queue_full_count[MAX_TARGETS]; - unsigned short ultraenb; /* Gets downloaded to card as a - bitmap */ - unsigned short discenable; /* Gets downloaded to card as a - bitmap */ - transinfo_type user[MAX_TARGETS]; - - unsigned char msg_buf[13]; /* The message for the target */ - unsigned char msg_type; + /* + * Things read/written on nearly every entry into aic7xxx_queue() + */ + volatile scb_queue_type waiting_scbs; + unsigned char unpause; /* unpause value for HCNTRL */ + unsigned char pause; /* pause value for HCNTRL */ + volatile unsigned char qoutfifonext; + volatile unsigned char activescbs; /* active scbs */ + volatile unsigned char max_activescbs; + volatile unsigned char qinfifonext; + volatile unsigned char *untagged_scbs; + volatile unsigned char *qoutfifo; + volatile unsigned char *qinfifo; + + unsigned char dev_last_queue_full[MAX_TARGETS]; + unsigned char dev_last_queue_full_count[MAX_TARGETS]; + unsigned short ultraenb; /* Gets downloaded to card as a bitmap */ + unsigned short discenable; /* Gets downloaded to card as a bitmap */ + transinfo_type user[MAX_TARGETS]; + + unsigned char msg_buf[13]; /* The message for the target */ + unsigned char msg_type; #define MSG_TYPE_NONE 0x00 #define MSG_TYPE_INITIATOR_MSGOUT 0x01 #define MSG_TYPE_INITIATOR_MSGIN 0x02 - unsigned char msg_len; /* Length of message */ - unsigned char msg_index; /* Index into msg_buf array */ + unsigned char msg_len; /* Length of message */ + unsigned char msg_index; /* Index into msg_buf array */ - /* - * We put the less frequently used host structure items after the more - * frequently used items to try and ease the burden on the cache subsystem. - * These entries are not *commonly* accessed, whereas the preceding entries - * are accessed very often. - */ - - unsigned int irq; /* IRQ for this adapter */ - int instance; /* aic7xxx instance number */ - int scsi_id; /* host adapter SCSI ID */ - int scsi_id_b; /* channel B for twin adapters */ - unsigned int bios_address; - int board_name_index; - unsigned short bios_control; /* bios control - SEEPROM */ - unsigned short adapter_control; /* adapter control - SEEPROM */ - struct pci_dev *pdev; - unsigned char pci_bus; - unsigned char pci_device_fn; - struct seeprom_config sc; - unsigned short sc_type; - unsigned short sc_size; - struct aic7xxx_host *next; /* allow for multiple IRQs */ - struct Scsi_Host *host; /* pointer to scsi host */ - struct list_head aic_devs; /* all aic_dev structs on host */ - int host_no; /* SCSI host number */ - unsigned long mbase; /* I/O memory address */ - ahc_chip chip; /* chip type */ - ahc_bugs bugs; - dma_addr_t fifo_dma; /* DMA handle for fifo arrays */ + /* + * We put the less frequently used host structure items + * after the more frequently used items to try and ease + * the burden on the cache subsystem. + * These entries are not *commonly* accessed, whereas + * the preceding entries are accessed very often. + */ + unsigned int irq; /* IRQ for this adapter */ + int instance; /* aic7xxx instance number */ + int scsi_id; /* host adapter SCSI ID */ + int scsi_id_b; /* channel B for twin adapters */ + unsigned int bios_address; + int board_name_index; + unsigned short bios_control; /* bios control - SEEPROM */ + unsigned short adapter_control; /* adapter control - SEEPROM */ + struct pci_dev *pdev; + unsigned char pci_bus; + unsigned char pci_device_fn; + struct seeprom_config sc; + unsigned short sc_type; + unsigned short sc_size; + struct aic7xxx_host *next; /* allow for multiple IRQs */ + struct Scsi_Host *host; /* pointer to scsi host */ + struct list_head aic_devs; /* all aic_dev structs on host */ + int host_no; /* SCSI host number */ + unsigned long mbase; /* I/O memory address */ + ahc_chip chip; /* chip type */ + ahc_bugs bugs; + dma_addr_t fifo_dma; /* DMA handle for fifo arrays */ }; /* @@ -1271,7 +1271,7 @@ static void aic7xxx_set_syncrate(struct aic7xxx_host *p, static void aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun, unsigned int width, unsigned int type, struct aic_dev_data *aic_dev); -static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd); +static void aic7xxx_panic_abort(struct aic7xxx_host *p, struct scsi_cmnd *cmd); static void aic7xxx_print_card(struct aic7xxx_host *p); static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p); static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded); @@ -2626,7 +2626,7 @@ aic7xxx_allocate_scb(struct aic7xxx_host *p) * we're finished. This function queues the completed commands. *-F*************************************************************************/ static void -aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd) +aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, struct scsi_cmnd *cmd) { aic7xxx_position(cmd) = SCB_LIST_NULL; cmd->host_scribble = (char *)p->completeq.head; @@ -2640,18 +2640,16 @@ aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd) * Description: * Process the completed command queue. *-F*************************************************************************/ -static void -aic7xxx_done_cmds_complete(struct aic7xxx_host *p) +static void aic7xxx_done_cmds_complete(struct aic7xxx_host *p) { - Scsi_Cmnd *cmd; - - while (p->completeq.head != NULL) - { - cmd = p->completeq.head; - p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble; - cmd->host_scribble = NULL; - cmd->scsi_done(cmd); - } + struct scsi_cmnd *cmd; + + while (p->completeq.head != NULL) { + cmd = p->completeq.head; + p->completeq.head = (struct scsi_Cmnd *) cmd->host_scribble; + cmd->host_scribble = NULL; + cmd->scsi_done(cmd); + } } /*+F************************************************************************* @@ -2687,11 +2685,11 @@ aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb) static void aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - Scsi_Cmnd *cmd = scb->cmd; - struct aic_dev_data *aic_dev = cmd->device->hostdata; - int tindex = TARGET_INDEX(cmd); - struct aic7xxx_scb *scbp; - unsigned char queue_depth; + struct scsi_cmnd *cmd = scb->cmd; + struct aic_dev_data *aic_dev = cmd->device->hostdata; + int tindex = TARGET_INDEX(cmd); + struct aic7xxx_scb *scbp; + unsigned char queue_depth; if (cmd->use_sg > 1) { @@ -2891,7 +2889,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb) * aic7xxx_run_done_queue * * Description: - * Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the + * Calls the aic7xxx_done() for the scsi_cmnd of each scb in the * aborted list, and adds each scb to the free list. If complete * is TRUE, we also process the commands complete list. *-F*************************************************************************/ @@ -3826,9 +3824,9 @@ aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width) static void aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb) { - struct aic7xxx_hwscb *hscb; - Scsi_Cmnd *cmd; - int actual, i; + struct aic7xxx_hwscb *hscb; + struct scsi_cmnd *cmd; + int actual, i; cmd = scb->cmd; hscb = scb->hscb; @@ -4219,20 +4217,20 @@ aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat) case BAD_STATUS: { - unsigned char scb_index; - struct aic7xxx_hwscb *hscb; - Scsi_Cmnd *cmd; - - /* The sequencer will notify us when a command has an error that - * would be of interest to the kernel. This allows us to leave - * the sequencer running in the common case of command completes - * without error. The sequencer will have DMA'd the SCB back - * up to us, so we can reference the drivers SCB array. - * - * Set the default return value to 0 indicating not to send - * sense. The sense code will change this if needed and this - * reduces code duplication. - */ + unsigned char scb_index; + struct aic7xxx_hwscb *hscb; + struct scsi_cmnd *cmd; + + /* The sequencer will notify us when a command has an error that + * would be of interest to the kernel. This allows us to leave + * the sequencer running in the common case of command completes + * without error. The sequencer will have DMA'd the SCB back + * up to us, so we can reference the drivers SCB array. + * + * Set the default return value to 0 indicating not to send + * sense. The sense code will change this if needed and this + * reduces code duplication. + */ aic_outb(p, 0, RETURN_1); scb_index = aic_inb(p, SCB_TAG); if (scb_index > p->scb_data->numscbs) @@ -5800,9 +5798,9 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) } else if ((status & SELTO) != 0) { - unsigned char scbptr; - unsigned char nextscb; - Scsi_Cmnd *cmd; + unsigned char scbptr; + unsigned char nextscb; + struct scsi_cmnd *cmd; scbptr = aic_inb(p, WAITING_SCBH); if (scbptr > p->scb_data->maxhscbs) @@ -5941,11 +5939,11 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) /* * Determine the bus phase and queue an appropriate message. */ - char *phase; - Scsi_Cmnd *cmd; - unsigned char mesg_out = MSG_NOOP; - unsigned char lastphase = aic_inb(p, LASTPHASE); - unsigned char sstat2 = aic_inb(p, SSTAT2); + char *phase; + struct scsi_cmnd *cmd; + unsigned char mesg_out = MSG_NOOP; + unsigned char lastphase = aic_inb(p, LASTPHASE); + unsigned char sstat2 = aic_inb(p, SSTAT2); cmd = scb->cmd; switch (lastphase) @@ -6248,10 +6246,10 @@ aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer) static void aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) { - struct aic7xxx_scb *scb = NULL; - struct aic_dev_data *aic_dev; - Scsi_Cmnd *cmd; - unsigned char scb_index, tindex; + struct aic7xxx_scb *scb = NULL; + struct aic_dev_data *aic_dev; + struct scsi_cmnd *cmd; + unsigned char scb_index, tindex; #ifdef AIC7XXX_VERBOSE_DEBUGGING if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) @@ -10131,9 +10129,8 @@ skip_pci_controller: * Description: * Build a SCB. *-F*************************************************************************/ -static void -aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, - struct aic7xxx_scb *scb) +static void aic7xxx_buildscb(struct aic7xxx_host *p, struct scsi_cmnd *cmd, + struct aic7xxx_scb *scb) { unsigned short mask; struct aic7xxx_hwscb *hscb; @@ -10285,8 +10282,7 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd, * Description: * Queue a SCB to the controller. *-F*************************************************************************/ -static int -aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) +static int aic7xxx_queue(struct scsi_cmnd *cmd, void (*fn)(struct scsi_cmnd *)) { struct aic7xxx_host *p; struct aic7xxx_scb *scb; @@ -10319,11 +10315,11 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) } scb->cmd = cmd; - /* - * Make sure the Scsi_Cmnd pointer is saved, the struct it points to - * is set up properly, and the parity error flag is reset, then send - * the SCB to the sequencer and watch the fun begin. - */ + /* + * Make sure the scsi_cmnd pointer is saved, the struct it points to + * is set up properly, and the parity error flag is reset, then send + * the SCB to the sequencer and watch the fun begin. + */ aic7xxx_position(cmd) = scb->hscb->tag; cmd->scsi_done = fn; cmd->result = DID_OK; @@ -10356,8 +10352,7 @@ aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *)) * aborted, then we will reset the channel and have all devices renegotiate. * Returns an enumerated type that indicates the status of the operation. *-F*************************************************************************/ -static int -__aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) +static int __aic7xxx_bus_device_reset(struct scsi_cmnd *cmd) { struct aic7xxx_host *p; struct aic7xxx_scb *scb; @@ -10550,8 +10545,7 @@ __aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) return SUCCESS; } -static int -aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) +static int aic7xxx_bus_device_reset(struct scsi_cmnd *cmd) { int rc; @@ -10570,8 +10564,7 @@ aic7xxx_bus_device_reset(Scsi_Cmnd *cmd) * Description: * Abort the current SCSI command(s). *-F*************************************************************************/ -static void -aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) +static void aic7xxx_panic_abort(struct aic7xxx_host *p, struct scsi_cmnd *cmd) { printk("aic7xxx driver version %s\n", AIC7XXX_C_VERSION); @@ -10595,8 +10588,7 @@ aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd) * Description: * Abort the current SCSI command(s). *-F*************************************************************************/ -static int -__aic7xxx_abort(Scsi_Cmnd *cmd) +static int __aic7xxx_abort(struct scsi_cmnd *cmd) { struct aic7xxx_scb *scb = NULL; struct aic7xxx_host *p; @@ -10813,8 +10805,7 @@ success: return SUCCESS; } -static int -aic7xxx_abort(Scsi_Cmnd *cmd) +static int aic7xxx_abort(struct scsi_cmnd *cmd) { int rc; @@ -10836,8 +10827,7 @@ aic7xxx_abort(Scsi_Cmnd *cmd) * DEVICE RESET message - on the offending target before pulling * the SCSI bus reset line. *-F*************************************************************************/ -static int -aic7xxx_reset(Scsi_Cmnd *cmd) +static int aic7xxx_reset(struct scsi_cmnd *cmd) { struct aic7xxx_scb *scb; struct aic7xxx_host *p; diff --git a/drivers/scsi/aic94xx/Kconfig b/drivers/scsi/aic94xx/Kconfig index 0ed391d8ee84..c83fe751d0bb 100644 --- a/drivers/scsi/aic94xx/Kconfig +++ b/drivers/scsi/aic94xx/Kconfig @@ -28,6 +28,7 @@ config SCSI_AIC94XX tristate "Adaptec AIC94xx SAS/SATA support" depends on PCI select SCSI_SAS_LIBSAS + select FW_LOADER help This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X AIC94xx chip based host adapters. diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 734adc9d5206..99743ca29ca1 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -309,11 +309,29 @@ static ssize_t asd_show_dev_pcba_sn(struct device *dev, } static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL); -static void asd_create_dev_attrs(struct asd_ha_struct *asd_ha) +static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha) { - device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision); - device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); - device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); + int err; + + err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision); + if (err) + return err; + + err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); + if (err) + goto err_rev; + + err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn); + if (err) + goto err_biosb; + + return 0; + +err_biosb: + device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build); +err_rev: + device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision); + return err; } static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha) @@ -645,7 +663,9 @@ static int __devinit asd_pci_probe(struct pci_dev *dev, } ASD_DPRINTK("escbs posted\n"); - asd_create_dev_attrs(asd_ha); + err = asd_create_dev_attrs(asd_ha); + if (err) + goto Err_dev_attrs; err = asd_register_sas_ha(asd_ha); if (err) @@ -668,6 +688,7 @@ Err_en_phys: asd_unregister_sas_ha(asd_ha); Err_reg_sas: asd_remove_dev_attrs(asd_ha); +Err_dev_attrs: Err_escbs: asd_disable_ints(asd_ha); free_irq(dev->irq, asd_ha); @@ -754,9 +775,9 @@ static ssize_t asd_version_show(struct device_driver *driver, char *buf) } static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL); -static void asd_create_driver_attrs(struct device_driver *driver) +static int asd_create_driver_attrs(struct device_driver *driver) { - driver_create_file(driver, &driver_attr_version); + return driver_create_file(driver, &driver_attr_version); } static void asd_remove_driver_attrs(struct device_driver *driver) @@ -834,10 +855,14 @@ static int __init aic94xx_init(void) if (err) goto out_release_transport; - asd_create_driver_attrs(&aic94xx_pci_driver.driver); + err = asd_create_driver_attrs(&aic94xx_pci_driver.driver); + if (err) + goto out_unregister_pcidrv; return err; + out_unregister_pcidrv: + pci_unregister_driver(&aic94xx_pci_driver); out_release_transport: sas_release_transport(aic94xx_transport_template); out_destroy_caches: diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c index 7621e3fa37b1..0525d672e1e6 100644 --- a/drivers/scsi/arm/acornscsi.c +++ b/drivers/scsi/arm/acornscsi.c @@ -194,7 +194,8 @@ unsigned int sdtr_period = SDTR_PERIOD; unsigned int sdtr_size = SDTR_SIZE; -static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result); +static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, + unsigned int result); static int acornscsi_reconnect_finish(AS_Host *host); static void acornscsi_dma_cleanup(AS_Host *host); static void acornscsi_abortcmd(AS_Host *host, unsigned char tag); @@ -712,7 +713,7 @@ static intr_ret_t acornscsi_kick(AS_Host *host) { int from_queue = 0; - Scsi_Cmnd *SCpnt; + struct scsi_cmnd *SCpnt; /* first check to see if a command is waiting to be executed */ SCpnt = host->origSCpnt; @@ -796,15 +797,15 @@ intr_ret_t acornscsi_kick(AS_Host *host) } /* - * Function: void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) + * Function: void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, unsigned int result) * Purpose : complete processing for command * Params : host - interface that completed * result - driver byte of result */ -static -void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result) +static void acornscsi_done(AS_Host *host, struct scsi_cmnd **SCpntp, + unsigned int result) { - Scsi_Cmnd *SCpnt = *SCpntp; + struct scsi_cmnd *SCpnt = *SCpntp; /* clean up */ sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); @@ -1318,7 +1319,7 @@ acornscsi_write_pio(AS_Host *host, char *bytes, int *ptr, int len, unsigned int static void acornscsi_sendcommand(AS_Host *host) { - Scsi_Cmnd *SCpnt = host->SCpnt; + struct scsi_cmnd *SCpnt = host->SCpnt; sbic_arm_write(host->scsi.io_port, SBIC_TRANSCNTH, 0); sbic_arm_writenext(host->scsi.io_port, 0); @@ -1693,7 +1694,7 @@ void acornscsi_message(AS_Host *host) acornscsi_sbic_issuecmd(host, CMND_ASSERTATN); msgqueue_addmsg(&host->scsi.msgs, 1, ABORT); } else { - Scsi_Cmnd *SCpnt = host->SCpnt; + struct scsi_cmnd *SCpnt = host->SCpnt; acornscsi_dma_cleanup(host); @@ -2509,13 +2510,14 @@ acornscsi_intr(int irq, void *dev_id, struct pt_regs *regs) */ /* - * Function : acornscsi_queuecmd(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) + * Function : acornscsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) * Purpose : queues a SCSI command * Params : cmd - SCSI command * done - function called on completion, with pointer to command descriptor * Returns : 0, or < 0 on error. */ -int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +int acornscsi_queuecmd(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) { AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; @@ -2565,17 +2567,18 @@ int acornscsi_queuecmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) } /* - * Prototype: void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) + * Prototype: void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, struct scsi_cmnd **SCpntp2, int result) * Purpose : pass a result to *SCpntp1, and check if *SCpntp1 = *SCpntp2 * Params : SCpntp1 - pointer to command to return * SCpntp2 - pointer to command to check * result - result to pass back to mid-level done function * Returns : *SCpntp2 = NULL if *SCpntp1 is the same command structure as *SCpntp2. */ -static inline -void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result) +static inline void acornscsi_reportstatus(struct scsi_cmnd **SCpntp1, + struct scsi_cmnd **SCpntp2, + int result) { - Scsi_Cmnd *SCpnt = *SCpntp1; + struct scsi_cmnd *SCpnt = *SCpntp1; if (SCpnt) { *SCpntp1 = NULL; @@ -2591,13 +2594,12 @@ void acornscsi_reportstatus(Scsi_Cmnd **SCpntp1, Scsi_Cmnd **SCpntp2, int result enum res_abort { res_not_running, res_success, res_success_clear, res_snooze }; /* - * Prototype: enum res acornscsi_do_abort(Scsi_Cmnd *SCpnt) + * Prototype: enum res acornscsi_do_abort(struct scsi_cmnd *SCpnt) * Purpose : abort a command on this host * Params : SCpnt - command to abort * Returns : our abort status */ -static enum res_abort -acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) +static enum res_abort acornscsi_do_abort(AS_Host *host, struct scsi_cmnd *SCpnt) { enum res_abort res = res_not_running; @@ -2684,12 +2686,12 @@ acornscsi_do_abort(AS_Host *host, Scsi_Cmnd *SCpnt) } /* - * Prototype: int acornscsi_abort(Scsi_Cmnd *SCpnt) + * Prototype: int acornscsi_abort(struct scsi_cmnd *SCpnt) * Purpose : abort a command on this host * Params : SCpnt - command to abort * Returns : one of SCSI_ABORT_ macros */ -int acornscsi_abort(Scsi_Cmnd *SCpnt) +int acornscsi_abort(struct scsi_cmnd *SCpnt) { AS_Host *host = (AS_Host *) SCpnt->device->host->hostdata; int result; @@ -2770,16 +2772,16 @@ int acornscsi_abort(Scsi_Cmnd *SCpnt) } /* - * Prototype: int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) + * Prototype: int acornscsi_reset(struct scsi_cmnd *SCpnt, unsigned int reset_flags) * Purpose : reset a command on this host/reset this host * Params : SCpnt - command causing reset * result - what type of reset to perform * Returns : one of SCSI_RESET_ macros */ -int acornscsi_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +int acornscsi_reset(struct scsi_cmnd *SCpnt, unsigned int reset_flags) { - AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; - Scsi_Cmnd *SCptr; + AS_Host *host = (AS_Host *)SCpnt->device->host->hostdata; + struct scsi_cmnd *SCptr; host->stats.resets += 1; diff --git a/drivers/scsi/arm/acornscsi.h b/drivers/scsi/arm/acornscsi.h index 2142290f8404..d11424b89f42 100644 --- a/drivers/scsi/arm/acornscsi.h +++ b/drivers/scsi/arm/acornscsi.h @@ -277,8 +277,8 @@ struct status_entry { typedef struct acornscsi_hostdata { /* miscellaneous */ struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ + struct scsi_cmnd *SCpnt; /* currently processing command */ + struct scsi_cmnd *origSCpnt; /* original connecting command */ /* driver information */ struct { diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index 4cf7afc31cc7..e05f0c2fc912 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -297,8 +297,8 @@ fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap) printk("scsi%d.%c: %s", info->host->host_no, target, buf); } -static void -fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...) +static void fas216_log_command(FAS216_Info *info, int level, + struct scsi_cmnd *SCpnt, char *fmt, ...) { va_list args; @@ -1662,7 +1662,7 @@ irqreturn_t fas216_intr(FAS216_Info *info) return handled; } -static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +static void __fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt) { int tot_msglen; @@ -1754,7 +1754,7 @@ static int parity_test(FAS216_Info *info, int target) return info->device[target].parity_check; } -static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +static void fas216_start_command(FAS216_Info *info, struct scsi_cmnd *SCpnt) { int disconnect_ok; @@ -1808,7 +1808,7 @@ static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) __fas216_start_command(info, SCpnt); } -static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) +static void fas216_allocate_tag(FAS216_Info *info, struct scsi_cmnd *SCpnt) { #ifdef SCSI2_TAG /* @@ -1842,7 +1842,8 @@ static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt) } } -static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) +static void fas216_do_bus_device_reset(FAS216_Info *info, + struct scsi_cmnd *SCpnt) { struct message *msg; @@ -1890,7 +1891,7 @@ static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt) */ static void fas216_kick(FAS216_Info *info) { - Scsi_Cmnd *SCpnt = NULL; + struct scsi_cmnd *SCpnt = NULL; #define TYPE_OTHER 0 #define TYPE_RESET 1 #define TYPE_QUEUE 2 @@ -1978,8 +1979,8 @@ static void fas216_kick(FAS216_Info *info) /* * Clean up from issuing a BUS DEVICE RESET message to a device. */ -static void -fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +static void fas216_devicereset_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, + unsigned int result) { fas216_log(info, LOG_ERROR, "fas216 device reset complete"); @@ -1996,8 +1997,8 @@ fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result * * Finish processing automatic request sense command */ -static void -fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +static void fas216_rq_sns_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, + unsigned int result) { fas216_log_target(info, LOG_CONNECT, SCpnt->device->id, "request sense complete, result=0x%04x%02x%02x", @@ -2030,7 +2031,7 @@ fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) * Finish processing of standard command */ static void -fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result) +fas216_std_done(FAS216_Info *info, struct scsi_cmnd *SCpnt, unsigned int result) { info->stats.fins += 1; @@ -2142,8 +2143,8 @@ request_sense: */ static void fas216_done(FAS216_Info *info, unsigned int result) { - void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); - Scsi_Cmnd *SCpnt; + void (*fn)(FAS216_Info *, struct scsi_cmnd *, unsigned int); + struct scsi_cmnd *SCpnt; unsigned long flags; fas216_checkmagic(info); @@ -2182,7 +2183,7 @@ static void fas216_done(FAS216_Info *info, unsigned int result) info->device[SCpnt->device->id].parity_check = 0; clear_bit(SCpnt->device->id * 8 + SCpnt->device->lun, info->busyluns); - fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; + fn = (void (*)(FAS216_Info *, struct scsi_cmnd *, unsigned int))SCpnt->host_scribble; fn(info, SCpnt, result); if (info->scsi.irq != NO_IRQ) { @@ -2207,7 +2208,8 @@ no_command: * Returns: 0 on success, else error. * Notes: io_request_lock is held, interrupts are disabled. */ -int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +int fas216_queue_command(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; int result; @@ -2254,7 +2256,7 @@ int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) * * Trigger restart of a waiting thread in fas216_command */ -static void fas216_internal_done(Scsi_Cmnd *SCpnt) +static void fas216_internal_done(struct scsi_cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; @@ -2271,7 +2273,8 @@ static void fas216_internal_done(Scsi_Cmnd *SCpnt) * Returns: scsi result code. * Notes: io_request_lock is held, interrupts are disabled. */ -int fas216_noqueue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +int fas216_noqueue_command(struct scsi_cmnd *SCpnt, + void (*done)(struct scsi_cmnd *)) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; @@ -2350,7 +2353,8 @@ enum res_find { * Decide how to abort a command. * Returns: abort status */ -static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) +static enum res_find fas216_find_command(FAS216_Info *info, + struct scsi_cmnd *SCpnt) { enum res_find res = res_failed; @@ -2417,7 +2421,7 @@ static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt) * Returns: FAILED if unable to abort * Notes: io_request_lock is taken, and irqs are disabled */ -int fas216_eh_abort(Scsi_Cmnd *SCpnt) +int fas216_eh_abort(struct scsi_cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; int result = FAILED; @@ -2474,7 +2478,7 @@ int fas216_eh_abort(Scsi_Cmnd *SCpnt) * Notes: We won't be re-entered, so we'll only have one device * reset on the go at one time. */ -int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) +int fas216_eh_device_reset(struct scsi_cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; unsigned long flags; @@ -2555,7 +2559,7 @@ int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) * Returns: FAILED if unable to reset. * Notes: Further commands are blocked. */ -int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) +int fas216_eh_bus_reset(struct scsi_cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; unsigned long flags; @@ -2655,7 +2659,7 @@ static void fas216_init_chip(FAS216_Info *info) * Returns: FAILED if unable to reset. * Notes: io_request_lock is taken, and irqs are disabled */ -int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) +int fas216_eh_host_reset(struct scsi_cmnd *SCpnt) { FAS216_Info *info = (FAS216_Info *)SCpnt->device->host->hostdata; diff --git a/drivers/scsi/arm/fas216.h b/drivers/scsi/arm/fas216.h index 540914d6fd32..00e5f055afdc 100644 --- a/drivers/scsi/arm/fas216.h +++ b/drivers/scsi/arm/fas216.h @@ -218,11 +218,11 @@ typedef struct { unsigned long magic_start; spinlock_t host_lock; struct Scsi_Host *host; /* host */ - Scsi_Cmnd *SCpnt; /* currently processing command */ - Scsi_Cmnd *origSCpnt; /* original connecting command */ - Scsi_Cmnd *reqSCpnt; /* request sense command */ - Scsi_Cmnd *rstSCpnt; /* reset command */ - Scsi_Cmnd *pending_SCpnt[8]; /* per-device pending commands */ + struct scsi_cmnd *SCpnt; /* currently processing command */ + struct scsi_cmnd *origSCpnt; /* original connecting command */ + struct scsi_cmnd *reqSCpnt; /* request sense command */ + struct scsi_cmnd *rstSCpnt; /* reset command */ + struct scsi_cmnd *pending_SCpnt[8]; /* per-device pending commands */ int next_pending; /* next pending device */ /* @@ -328,21 +328,23 @@ extern int fas216_init (struct Scsi_Host *instance); */ extern int fas216_add (struct Scsi_Host *instance, struct device *dev); -/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +/* Function: int fas216_queue_command(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) * Purpose : queue a command for adapter to process. * Params : SCpnt - Command to queue * done - done function to call once command is complete * Returns : 0 - success, else error */ -extern int fas216_queue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int fas216_queue_command(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); -/* Function: int fas216_noqueue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +/* Function: int fas216_noqueue_command(istruct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) * Purpose : queue a command for adapter to process, and process it to completion. * Params : SCpnt - Command to queue * done - done function to call once command is complete * Returns : 0 - success, else error */ -extern int fas216_noqueue_command (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +extern int fas216_noqueue_command(struct scsi_cmnd *, + void (*done)(struct scsi_cmnd *)); /* Function: irqreturn_t fas216_intr (FAS216_Info *info) * Purpose : handle interrupts from the interface to progress a command @@ -363,32 +365,32 @@ extern int fas216_print_host(FAS216_Info *info, char *buffer); extern int fas216_print_stats(FAS216_Info *info, char *buffer); extern int fas216_print_devices(FAS216_Info *info, char *buffer); -/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) +/* Function: int fas216_eh_abort(struct scsi_cmnd *SCpnt) * Purpose : abort this command * Params : SCpnt - command to abort * Returns : FAILED if unable to abort */ -extern int fas216_eh_abort(Scsi_Cmnd *SCpnt); +extern int fas216_eh_abort(struct scsi_cmnd *SCpnt); -/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt) +/* Function: int fas216_eh_device_reset(struct scsi_cmnd *SCpnt) * Purpose : Reset the device associated with this command * Params : SCpnt - command specifing device to reset * Returns : FAILED if unable to reset */ -extern int fas216_eh_device_reset(Scsi_Cmnd *SCpnt); +extern int fas216_eh_device_reset(struct scsi_cmnd *SCpnt); -/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt) +/* Function: int fas216_eh_bus_reset(struct scsi_cmnd *SCpnt) * Purpose : Reset the complete bus associated with this command * Params : SCpnt - command specifing bus to reset * Returns : FAILED if unable to reset */ -extern int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt); +extern int fas216_eh_bus_reset(struct scsi_cmnd *SCpnt); -/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt) +/* Function: int fas216_eh_host_reset(struct scsi_cmnd *SCpnt) * Purpose : Reset the host associated with this command * Params : SCpnt - command specifing host to reset * Returns : FAILED if unable to reset */ -extern int fas216_eh_host_reset(Scsi_Cmnd *SCpnt); +extern int fas216_eh_host_reset(struct scsi_cmnd *SCpnt); #endif /* FAS216_H */ diff --git a/drivers/scsi/arm/queue.c b/drivers/scsi/arm/queue.c index 8caa5903ce38..cb11ccef54e5 100644 --- a/drivers/scsi/arm/queue.c +++ b/drivers/scsi/arm/queue.c @@ -29,7 +29,7 @@ typedef struct queue_entry { struct list_head list; - Scsi_Cmnd *SCpnt; + struct scsi_cmnd *SCpnt; #ifdef DEBUG unsigned long magic; #endif @@ -96,14 +96,14 @@ void queue_free (Queue_t *queue) /* - * Function: int queue_add_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head) * Purpose : Add a new command onto a queue, adding REQUEST_SENSE to head. * Params : queue - destination queue * SCpnt - command to add * head - add command to head of queue * Returns : 0 on error, !0 on success */ -int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) +int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head) { unsigned long flags; struct list_head *l; @@ -134,7 +134,7 @@ empty: return ret; } -static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) +static struct scsi_cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) { QE_t *q; @@ -152,17 +152,17 @@ static Scsi_Cmnd *__queue_remove(Queue_t *queue, struct list_head *ent) } /* - * Function: Scsi_Cmnd *queue_remove_exclude (queue, exclude) + * Function: struct scsi_cmnd *queue_remove_exclude (queue, exclude) * Purpose : remove a SCSI command from a queue * Params : queue - queue to remove command from * exclude - bit array of target&lun which is busy - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available */ -Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) +struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) { unsigned long flags; struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; + struct scsi_cmnd *SCpnt = NULL; spin_lock_irqsave(&queue->queue_lock, flags); list_for_each(l, &queue->head) { @@ -178,15 +178,15 @@ Scsi_Cmnd *queue_remove_exclude(Queue_t *queue, unsigned long *exclude) } /* - * Function: Scsi_Cmnd *queue_remove (queue) + * Function: struct scsi_cmnd *queue_remove (queue) * Purpose : removes first SCSI command from a queue * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available */ -Scsi_Cmnd *queue_remove(Queue_t *queue) +struct scsi_cmnd *queue_remove(Queue_t *queue) { unsigned long flags; - Scsi_Cmnd *SCpnt = NULL; + struct scsi_cmnd *SCpnt = NULL; spin_lock_irqsave(&queue->queue_lock, flags); if (!list_empty(&queue->head)) @@ -197,19 +197,20 @@ Scsi_Cmnd *queue_remove(Queue_t *queue) } /* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag) * Purpose : remove a SCSI command from the queue for a specified target/lun/tag * Params : queue - queue to remove command from * target - target that we want * lun - lun on device * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements */ -Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag) +struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, int lun, + int tag) { unsigned long flags; struct list_head *l; - Scsi_Cmnd *SCpnt = NULL; + struct scsi_cmnd *SCpnt = NULL; spin_lock_irqsave(&queue->queue_lock, flags); list_for_each(l, &queue->head) { @@ -275,13 +276,13 @@ int queue_probetgtlun (Queue_t *queue, int target, int lun) } /* - * Function: int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) + * Function: int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt) * Purpose : remove a specific command from the queues * Params : queue - queue to look in * SCpnt - command to find * Returns : 0 if not found */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt) +int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt) { unsigned long flags; struct list_head *l; diff --git a/drivers/scsi/arm/queue.h b/drivers/scsi/arm/queue.h index 0c9dec4c1716..3c519c9237b2 100644 --- a/drivers/scsi/arm/queue.h +++ b/drivers/scsi/arm/queue.h @@ -32,46 +32,48 @@ extern int queue_initialise (Queue_t *queue); extern void queue_free (Queue_t *queue); /* - * Function: Scsi_Cmnd *queue_remove (queue) + * Function: struct scsi_cmnd *queue_remove (queue) * Purpose : removes first SCSI command from a queue * Params : queue - queue to remove command from - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available */ -extern Scsi_Cmnd *queue_remove (Queue_t *queue); +extern struct scsi_cmnd *queue_remove (Queue_t *queue); /* - * Function: Scsi_Cmnd *queue_remove_exclude_ref (queue, exclude) + * Function: struct scsi_cmnd *queue_remove_exclude_ref (queue, exclude) * Purpose : remove a SCSI command from a queue * Params : queue - queue to remove command from * exclude - array of busy LUNs - * Returns : Scsi_Cmnd if successful (and a reference), or NULL if no command available + * Returns : struct scsi_cmnd if successful (and a reference), or NULL if no command available */ -extern Scsi_Cmnd *queue_remove_exclude (Queue_t *queue, unsigned long *exclude); +extern struct scsi_cmnd *queue_remove_exclude(Queue_t *queue, + unsigned long *exclude); #define queue_add_cmd_ordered(queue,SCpnt) \ __queue_add(queue,SCpnt,(SCpnt)->cmnd[0] == REQUEST_SENSE) #define queue_add_cmd_tail(queue,SCpnt) \ __queue_add(queue,SCpnt,0) /* - * Function: int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head) + * Function: int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head) * Purpose : Add a new command onto a queue * Params : queue - destination queue * SCpnt - command to add * head - add command to head of queue * Returns : 0 on error, !0 on success */ -extern int __queue_add(Queue_t *queue, Scsi_Cmnd *SCpnt, int head); +extern int __queue_add(Queue_t *queue, struct scsi_cmnd *SCpnt, int head); /* - * Function: Scsi_Cmnd *queue_remove_tgtluntag (queue, target, lun, tag) + * Function: struct scsi_cmnd *queue_remove_tgtluntag (queue, target, lun, tag) * Purpose : remove a SCSI command from the queue for a specified target/lun/tag * Params : queue - queue to remove command from * target - target that we want * lun - lun on device * tag - tag on device - * Returns : Scsi_Cmnd if successful, or NULL if no command satisfies requirements + * Returns : struct scsi_cmnd if successful, or NULL if no command satisfies requirements */ -extern Scsi_Cmnd *queue_remove_tgtluntag (Queue_t *queue, int target, int lun, int tag); +extern struct scsi_cmnd *queue_remove_tgtluntag(Queue_t *queue, int target, + int lun, int tag); /* * Function: queue_remove_all_target(queue, target) @@ -94,12 +96,12 @@ extern void queue_remove_all_target(Queue_t *queue, int target); extern int queue_probetgtlun (Queue_t *queue, int target, int lun); /* - * Function: int queue_remove_cmd (Queue_t *queue, Scsi_Cmnd *SCpnt) + * Function: int queue_remove_cmd (Queue_t *queue, struct scsi_cmnd *SCpnt) * Purpose : remove a specific command from the queues * Params : queue - queue to look in * SCpnt - command to find * Returns : 0 if not found */ -int queue_remove_cmd(Queue_t *queue, Scsi_Cmnd *SCpnt); +int queue_remove_cmd(Queue_t *queue, struct scsi_cmnd *SCpnt); #endif /* QUEUE_H */ diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/scsi.h index 8c2600ffc6af..3a39579bd08e 100644 --- a/drivers/scsi/arm/scsi.h +++ b/drivers/scsi/arm/scsi.h @@ -66,7 +66,7 @@ static inline void put_next_SCp_byte(struct scsi_pointer *SCp, unsigned char c) SCp->this_residual -= 1; } -static inline void init_SCp(Scsi_Cmnd *SCpnt) +static inline void init_SCp(struct scsi_cmnd *SCpnt) { memset(&SCpnt->SCp, 0, sizeof(struct scsi_pointer)); diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index ff2b1796fa34..c6118d99385e 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -1219,7 +1219,7 @@ static void dump_register_info(struct AdapterCtlBlk *acb, srb, srb->cmd, srb->cmd->pid, srb->cmd->cmnd[0], srb->cmd->device->id, srb->cmd->device->lun); - printk(" sglist=%p cnt=%i idx=%i len=%i\n", + printk(" sglist=%p cnt=%i idx=%i len=%Zd\n", srb->segment_x, srb->sg_count, srb->sg_index, srb->total_xfer_length); printk(" state=0x%04x status=0x%02x phase=0x%02x (%sconn.)\n", @@ -4949,7 +4949,7 @@ static struct pci_driver dc395x_driver = { **/ static int __init dc395x_module_init(void) { - return pci_module_init(&dc395x_driver); + return pci_register_driver(&dc395x_driver); } diff --git a/drivers/scsi/dmx3191d.c b/drivers/scsi/dmx3191d.c index 879a26657676..fa738ec8692a 100644 --- a/drivers/scsi/dmx3191d.c +++ b/drivers/scsi/dmx3191d.c @@ -155,7 +155,7 @@ static struct pci_driver dmx3191d_pci_driver = { static int __init dmx3191d_init(void) { - return pci_module_init(&dmx3191d_pci_driver); + return pci_register_driver(&dmx3191d_pci_driver); } static void __exit dmx3191d_exit(void) diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h index d84a281ad944..b3fa7ed71faf 100644 --- a/drivers/scsi/dpt/dpti_i2o.h +++ b/drivers/scsi/dpt/dpti_i2o.h @@ -47,21 +47,11 @@ * I2O Interface Objects */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) - -#define DECLARE_MUTEX(name) struct semaphore name=MUTEX - -typedef struct wait_queue *adpt_wait_queue_head_t; -#define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) adpt_wait_queue_head_t wait = NULL -typedef struct wait_queue adpt_wait_queue_t; -#else - #include <linux/wait.h> typedef wait_queue_head_t adpt_wait_queue_head_t; #define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) DECLARE_WAIT_QUEUE_HEAD(wait) typedef wait_queue_t adpt_wait_queue_t; -#endif /* * message structures */ diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 7b3bd34faf47..b20b37661d6f 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -2212,7 +2212,7 @@ static s32 adpt_scsi_register(adpt_hba* pHba,struct scsi_host_template * sht) */ host->io_port = 0; host->n_io_port = 0; - /* see comments in hosts.h */ + /* see comments in scsi_host.h */ host->max_id = 16; host->max_lun = 256; host->max_channel = pHba->top_scsi_channel + 1; diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h index 2ad2a89b5db4..289983264929 100644 --- a/drivers/scsi/dpti.h +++ b/drivers/scsi/dpti.h @@ -44,7 +44,7 @@ static int adpt_device_reset(struct scsi_cmnd* cmd); /* - * struct scsi_host_template (see hosts.h) + * struct scsi_host_template (see scsi/scsi_host.h) */ #define DPT_DRIVER_NAME "Adaptec I2O RAID" diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 47eae0299750..8c29eafd51c5 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -936,18 +936,12 @@ typedef struct { gdth_binfo_str binfo; /* controller info */ gdth_evt_data dvr; /* event structure */ spinlock_t smp_lock; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) struct pci_dev *pdev; -#endif char oem_name[8]; #ifdef GDTH_DMA_STATISTICS ulong dma32_cnt, dma64_cnt; /* statistics: DMA buffer */ #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) struct scsi_device *sdev; -#else - struct scsi_device sdev; -#endif } gdth_ha_str; /* structure for scsi_register(), SCSI bus != 0 */ @@ -1029,10 +1023,6 @@ typedef struct { /* function prototyping */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) int gdth_proc_info(struct Scsi_Host *, char *,char **,off_t,int,int); -#else -int gdth_proc_info(char *,char **,off_t,int,int,int); -#endif #endif diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h deleted file mode 100644 index c27264bed5d4..000000000000 --- a/drivers/scsi/hosts.h +++ /dev/null @@ -1,2 +0,0 @@ -#warning "This file is obsolete, please use <scsi/scsi_host.h> instead" -#include <scsi/scsi_host.h> diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 7ed4eef8347b..e1fe9494125b 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -70,6 +70,7 @@ #include <linux/firmware.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/libata.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/processor.h> @@ -78,6 +79,7 @@ #include <scsi/scsi_tcq.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_transport.h> #include "ipr.h" /* @@ -199,6 +201,8 @@ struct ipr_error_table_t ipr_error_table[] = { "FFFA: Undefined device response recovered by the IOA"}, {0x014A0000, 1, 1, "FFF6: Device bus error, message or command phase"}, + {0x014A8000, 0, 1, + "FFFE: Task Management Function failed"}, {0x015D0000, 0, 1, "FFF6: Failure prediction threshold exceeded"}, {0x015D9200, 0, 1, @@ -261,6 +265,8 @@ struct ipr_error_table_t ipr_error_table[] = { "Device bus status error"}, {0x04448600, 0, 1, "8157: IOA error requiring IOA reset to recover"}, + {0x04448700, 0, 0, + "ATA device status error"}, {0x04490000, 0, 0, "Message reject received from the device"}, {0x04449200, 0, 1, @@ -273,6 +279,8 @@ struct ipr_error_table_t ipr_error_table[] = { "9082: IOA detected device error"}, {0x044A0000, 1, 1, "3110: Device bus error, message or command phase"}, + {0x044A8000, 1, 1, + "3110: SAS Command / Task Management Function failed"}, {0x04670400, 0, 1, "9091: Incorrect hardware configuration change has been detected"}, {0x04678000, 0, 1, @@ -453,7 +461,8 @@ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd, trace_entry->time = jiffies; trace_entry->op_code = ipr_cmd->ioarcb.cmd_pkt.cdb[0]; trace_entry->type = type; - trace_entry->cmd_index = ipr_cmd->cmd_index; + trace_entry->ata_op_code = ipr_cmd->ioarcb.add_data.u.regs.command; + trace_entry->cmd_index = ipr_cmd->cmd_index & 0xff; trace_entry->res_handle = ipr_cmd->ioarcb.res_handle; trace_entry->u.add_data = add_data; } @@ -480,8 +489,10 @@ static void ipr_reinit_ipr_cmnd(struct ipr_cmnd *ipr_cmd) ioarcb->read_ioadl_len = 0; ioasa->ioasc = 0; ioasa->residual_data_len = 0; + ioasa->u.gata.status = 0; ipr_cmd->scsi_cmd = NULL; + ipr_cmd->qc = NULL; ipr_cmd->sense_buffer[0] = 0; ipr_cmd->dma_use_sg = 0; } @@ -626,6 +637,28 @@ static int ipr_set_pcix_cmd_reg(struct ipr_ioa_cfg *ioa_cfg) } /** + * ipr_sata_eh_done - done function for aborted SATA commands + * @ipr_cmd: ipr command struct + * + * This function is invoked for ops generated to SATA + * devices which are being aborted. + * + * Return value: + * none + **/ +static void ipr_sata_eh_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ata_queued_cmd *qc = ipr_cmd->qc; + struct ipr_sata_port *sata_port = qc->ap->private_data; + + qc->err_mask |= AC_ERR_OTHER; + sata_port->ioasa.status |= ATA_BUSY; + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + ata_qc_complete(qc); +} + +/** * ipr_scsi_eh_done - mid-layer done function for aborted ops * @ipr_cmd: ipr command struct * @@ -669,6 +702,8 @@ static void ipr_fail_all_ops(struct ipr_ioa_cfg *ioa_cfg) if (ipr_cmd->scsi_cmd) ipr_cmd->done = ipr_scsi_eh_done; + else if (ipr_cmd->qc) + ipr_cmd->done = ipr_sata_eh_done; ipr_trc_hook(ipr_cmd, IPR_TRACE_FINISH, IPR_IOASC_IOA_WAS_RESET); del_timer(&ipr_cmd->timer); @@ -825,6 +860,7 @@ static void ipr_init_res_entry(struct ipr_resource_entry *res) res->del_from_ml = 0; res->resetting_device = 0; res->sdev = NULL; + res->sata_port = NULL; } /** @@ -1316,7 +1352,7 @@ static u32 ipr_get_error(u32 ioasc) int i; for (i = 0; i < ARRAY_SIZE(ipr_error_table); i++) - if (ipr_error_table[i].ioasc == ioasc) + if (ipr_error_table[i].ioasc == (ioasc & IPR_IOASC_IOASC_MASK)) return i; return 0; @@ -3051,6 +3087,17 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) { return 0; }; **/ static int ipr_change_queue_depth(struct scsi_device *sdev, int qdepth) { + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)sdev->host->hostdata; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = (struct ipr_resource_entry *)sdev->hostdata; + + if (res && ipr_is_gata(res) && qdepth > IPR_MAX_CMD_PER_ATA_LUN) + qdepth = IPR_MAX_CMD_PER_ATA_LUN; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); return sdev->queue_depth; } @@ -3166,6 +3213,122 @@ static int ipr_biosparam(struct scsi_device *sdev, } /** + * ipr_find_starget - Find target based on bus/target. + * @starget: scsi target struct + * + * Return value: + * resource entry pointer if found / NULL if not found + **/ +static struct ipr_resource_entry *ipr_find_starget(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata; + struct ipr_resource_entry *res; + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if ((res->cfgte.res_addr.bus == starget->channel) && + (res->cfgte.res_addr.target == starget->id) && + (res->cfgte.res_addr.lun == 0)) { + return res; + } + } + + return NULL; +} + +static struct ata_port_info sata_port_info; + +/** + * ipr_target_alloc - Prepare for commands to a SCSI target + * @starget: scsi target struct + * + * If the device is a SATA device, this function allocates an + * ATA port with libata, else it does nothing. + * + * Return value: + * 0 on success / non-0 on failure + **/ +static int ipr_target_alloc(struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) shost->hostdata; + struct ipr_sata_port *sata_port; + struct ata_port *ap; + struct ipr_resource_entry *res; + unsigned long lock_flags; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = ipr_find_starget(starget); + starget->hostdata = NULL; + + if (res && ipr_is_gata(res)) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + sata_port = kzalloc(sizeof(*sata_port), GFP_KERNEL); + if (!sata_port) + return -ENOMEM; + + ap = ata_sas_port_alloc(&ioa_cfg->ata_host, &sata_port_info, shost); + if (ap) { + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + sata_port->ioa_cfg = ioa_cfg; + sata_port->ap = ap; + sata_port->res = res; + + res->sata_port = sata_port; + ap->private_data = sata_port; + starget->hostdata = sata_port; + } else { + kfree(sata_port); + return -ENOMEM; + } + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + return 0; +} + +/** + * ipr_target_destroy - Destroy a SCSI target + * @starget: scsi target struct + * + * If the device was a SATA device, this function frees the libata + * ATA port, else it does nothing. + * + **/ +static void ipr_target_destroy(struct scsi_target *starget) +{ + struct ipr_sata_port *sata_port = starget->hostdata; + + if (sata_port) { + starget->hostdata = NULL; + ata_sas_port_destroy(sata_port->ap); + kfree(sata_port); + } +} + +/** + * ipr_find_sdev - Find device based on bus/target/lun. + * @sdev: scsi device struct + * + * Return value: + * resource entry pointer if found / NULL if not found + **/ +static struct ipr_resource_entry *ipr_find_sdev(struct scsi_device *sdev) +{ + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *) sdev->host->hostdata; + struct ipr_resource_entry *res; + + list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { + if ((res->cfgte.res_addr.bus == sdev->channel) && + (res->cfgte.res_addr.target == sdev->id) && + (res->cfgte.res_addr.lun == sdev->lun)) + return res; + } + + return NULL; +} + +/** * ipr_slave_destroy - Unconfigure a SCSI device * @sdev: scsi device struct * @@ -3183,8 +3346,11 @@ static void ipr_slave_destroy(struct scsi_device *sdev) spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); res = (struct ipr_resource_entry *) sdev->hostdata; if (res) { + if (res->sata_port) + ata_port_disable(res->sata_port->ap); sdev->hostdata = NULL; res->sdev = NULL; + res->sata_port = NULL; } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); } @@ -3219,13 +3385,45 @@ static int ipr_slave_configure(struct scsi_device *sdev) } if (ipr_is_vset_device(res) || ipr_is_scsi_disk(res)) sdev->allow_restart = 1; - scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + if (ipr_is_gata(res) && res->sata_port) { + scsi_adjust_queue_depth(sdev, 0, IPR_MAX_CMD_PER_ATA_LUN); + ata_sas_slave_configure(sdev, res->sata_port->ap); + } else { + scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); + } } spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return 0; } /** + * ipr_ata_slave_alloc - Prepare for commands to a SATA device + * @sdev: scsi device struct + * + * This function initializes an ATA port so that future commands + * sent through queuecommand will work. + * + * Return value: + * 0 on success + **/ +static int ipr_ata_slave_alloc(struct scsi_device *sdev) +{ + struct ipr_sata_port *sata_port = NULL; + int rc = -ENXIO; + + ENTER; + if (sdev->sdev_target) + sata_port = sdev->sdev_target->hostdata; + if (sata_port) + rc = ata_sas_port_init(sata_port->ap); + if (rc) + ipr_slave_destroy(sdev); + + LEAVE; + return rc; +} + +/** * ipr_slave_alloc - Prepare for commands to a device. * @sdev: scsi device struct * @@ -3248,18 +3446,18 @@ static int ipr_slave_alloc(struct scsi_device *sdev) spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); - list_for_each_entry(res, &ioa_cfg->used_res_q, queue) { - if ((res->cfgte.res_addr.bus == sdev->channel) && - (res->cfgte.res_addr.target == sdev->id) && - (res->cfgte.res_addr.lun == sdev->lun)) { - res->sdev = sdev; - res->add_to_ml = 0; - res->in_erp = 0; - sdev->hostdata = res; - if (!ipr_is_naca_model(res)) - res->needs_sync_complete = 1; - rc = 0; - break; + res = ipr_find_sdev(sdev); + if (res) { + res->sdev = sdev; + res->add_to_ml = 0; + res->in_erp = 0; + sdev->hostdata = res; + if (!ipr_is_naca_model(res)) + res->needs_sync_complete = 1; + rc = 0; + if (ipr_is_gata(res)) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return ipr_ata_slave_alloc(sdev); } } @@ -3314,7 +3512,8 @@ static int ipr_eh_host_reset(struct scsi_cmnd * cmd) * This function issues a device reset to the affected device. * If the device is a SCSI device, a LUN reset will be sent * to the device first. If that does not work, a target reset - * will be sent. + * will be sent. If the device is a SATA device, a PHY reset will + * be sent. * * Return value: * 0 on success / non-zero on failure @@ -3325,26 +3524,79 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg, struct ipr_cmnd *ipr_cmd; struct ipr_ioarcb *ioarcb; struct ipr_cmd_pkt *cmd_pkt; + struct ipr_ioarcb_ata_regs *regs; u32 ioasc; ENTER; ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); ioarcb = &ipr_cmd->ioarcb; cmd_pkt = &ioarcb->cmd_pkt; + regs = &ioarcb->add_data.u.regs; ioarcb->res_handle = res->cfgte.res_handle; cmd_pkt->request_type = IPR_RQTYPE_IOACMD; cmd_pkt->cdb[0] = IPR_RESET_DEVICE; + if (ipr_is_gata(res)) { + cmd_pkt->cdb[2] = IPR_ATA_PHY_RESET; + ioarcb->add_cmd_parms_len = cpu_to_be32(sizeof(regs->flags)); + regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION; + } ipr_send_blocking_cmd(ipr_cmd, ipr_timeout, IPR_DEVICE_RESET_TIMEOUT); ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + if (ipr_is_gata(res) && res->sata_port && ioasc != IPR_IOASC_IOA_WAS_RESET) + memcpy(&res->sata_port->ioasa, &ipr_cmd->ioasa.u.gata, + sizeof(struct ipr_ioasa_gata)); LEAVE; return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0); } /** + * ipr_sata_reset - Reset the SATA port + * @ap: SATA port to reset + * @classes: class of the attached device + * + * This function issues a SATA phy reset to the affected ATA port. + * + * Return value: + * 0 on success / non-zero on failure + **/ +static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes) +{ + struct ipr_sata_port *sata_port = ap->private_data; + struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; + struct ipr_resource_entry *res; + unsigned long lock_flags = 0; + int rc = -ENXIO; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + res = sata_port->res; + if (res) { + rc = ipr_device_reset(ioa_cfg, res); + switch(res->cfgte.proto) { + case IPR_PROTO_SATA: + case IPR_PROTO_SAS_STP: + *classes = ATA_DEV_ATA; + break; + case IPR_PROTO_SATA_ATAPI: + case IPR_PROTO_SAS_STP_ATAPI: + *classes = ATA_DEV_ATAPI; + break; + default: + *classes = ATA_DEV_UNKNOWN; + break; + }; + } + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + LEAVE; + return rc; +} + +/** * ipr_eh_dev_reset - Reset the device * @scsi_cmd: scsi command struct * @@ -3360,7 +3612,8 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd) struct ipr_cmnd *ipr_cmd; struct ipr_ioa_cfg *ioa_cfg; struct ipr_resource_entry *res; - int rc; + struct ata_port *ap; + int rc = 0; ENTER; ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata; @@ -3388,7 +3641,14 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd) res->resetting_device = 1; scmd_printk(KERN_ERR, scsi_cmd, "Resetting device\n"); - rc = ipr_device_reset(ioa_cfg, res); + + if (ipr_is_gata(res) && res->sata_port) { + ap = res->sata_port->ap; + spin_unlock_irq(scsi_cmd->device->host->host_lock); + ata_do_eh(ap, NULL, NULL, ipr_sata_reset, NULL); + spin_lock_irq(scsi_cmd->device->host->host_lock); + } else + rc = ipr_device_reset(ioa_cfg, res); res->resetting_device = 0; LEAVE; @@ -4300,6 +4560,9 @@ static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd, return 0; } + if (ipr_is_gata(res) && res->sata_port) + return ata_sas_queuecmd(scsi_cmd, done, res->sata_port->ap); + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); ioarcb = &ipr_cmd->ioarcb; list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); @@ -4345,6 +4608,26 @@ static int ipr_queuecommand(struct scsi_cmnd *scsi_cmd, } /** + * ipr_ioctl - IOCTL handler + * @sdev: scsi device struct + * @cmd: IOCTL cmd + * @arg: IOCTL arg + * + * Return value: + * 0 on success / other on failure + **/ +int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + struct ipr_resource_entry *res; + + res = (struct ipr_resource_entry *)sdev->hostdata; + if (res && ipr_is_gata(res)) + return ata_scsi_ioctl(sdev, cmd, arg); + + return -EINVAL; +} + +/** * ipr_info - Get information about the card/driver * @scsi_host: scsi host struct * @@ -4366,10 +4649,45 @@ static const char * ipr_ioa_info(struct Scsi_Host *host) return buffer; } +/** + * ipr_scsi_timed_out - Handle scsi command timeout + * @scsi_cmd: scsi command struct + * + * Return value: + * EH_NOT_HANDLED + **/ +enum scsi_eh_timer_return ipr_scsi_timed_out(struct scsi_cmnd *scsi_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg; + struct ipr_cmnd *ipr_cmd; + unsigned long flags; + + ENTER; + spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags); + ioa_cfg = (struct ipr_ioa_cfg *)scsi_cmd->device->host->hostdata; + + list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { + if (ipr_cmd->qc && ipr_cmd->qc->scsicmd == scsi_cmd) { + ipr_cmd->qc->err_mask |= AC_ERR_TIMEOUT; + ipr_cmd->qc->flags |= ATA_QCFLAG_FAILED; + break; + } + } + + spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags); + LEAVE; + return EH_NOT_HANDLED; +} + +static struct scsi_transport_template ipr_transport_template = { + .eh_timed_out = ipr_scsi_timed_out +}; + static struct scsi_host_template driver_template = { .module = THIS_MODULE, .name = "IPR", .info = ipr_ioa_info, + .ioctl = ipr_ioctl, .queuecommand = ipr_queuecommand, .eh_abort_handler = ipr_eh_abort, .eh_device_reset_handler = ipr_eh_dev_reset, @@ -4377,6 +4695,8 @@ static struct scsi_host_template driver_template = { .slave_alloc = ipr_slave_alloc, .slave_configure = ipr_slave_configure, .slave_destroy = ipr_slave_destroy, + .target_alloc = ipr_target_alloc, + .target_destroy = ipr_target_destroy, .change_queue_depth = ipr_change_queue_depth, .change_queue_type = ipr_change_queue_type, .bios_param = ipr_biosparam, @@ -4391,6 +4711,330 @@ static struct scsi_host_template driver_template = { .proc_name = IPR_NAME }; +/** + * ipr_ata_phy_reset - libata phy_reset handler + * @ap: ata port to reset + * + **/ +static void ipr_ata_phy_reset(struct ata_port *ap) +{ + unsigned long flags; + struct ipr_sata_port *sata_port = ap->private_data; + struct ipr_resource_entry *res = sata_port->res; + struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; + int rc; + + ENTER; + spin_lock_irqsave(ioa_cfg->host->host_lock, flags); + while(ioa_cfg->in_reset_reload) { + spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags); + wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload); + spin_lock_irqsave(ioa_cfg->host->host_lock, flags); + } + + if (!ioa_cfg->allow_cmds) + goto out_unlock; + + rc = ipr_device_reset(ioa_cfg, res); + + if (rc) { + ap->ops->port_disable(ap); + goto out_unlock; + } + + switch(res->cfgte.proto) { + case IPR_PROTO_SATA: + case IPR_PROTO_SAS_STP: + ap->device[0].class = ATA_DEV_ATA; + break; + case IPR_PROTO_SATA_ATAPI: + case IPR_PROTO_SAS_STP_ATAPI: + ap->device[0].class = ATA_DEV_ATAPI; + break; + default: + ap->device[0].class = ATA_DEV_UNKNOWN; + ap->ops->port_disable(ap); + break; + }; + +out_unlock: + spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags); + LEAVE; +} + +/** + * ipr_ata_post_internal - Cleanup after an internal command + * @qc: ATA queued command + * + * Return value: + * none + **/ +static void ipr_ata_post_internal(struct ata_queued_cmd *qc) +{ + struct ipr_sata_port *sata_port = qc->ap->private_data; + struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; + struct ipr_cmnd *ipr_cmd; + unsigned long flags; + + spin_lock_irqsave(ioa_cfg->host->host_lock, flags); + list_for_each_entry(ipr_cmd, &ioa_cfg->pending_q, queue) { + if (ipr_cmd->qc == qc) { + ipr_device_reset(ioa_cfg, sata_port->res); + break; + } + } + spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags); +} + +/** + * ipr_tf_read - Read the current ATA taskfile for the ATA port + * @ap: ATA port + * @tf: destination ATA taskfile + * + * Return value: + * none + **/ +static void ipr_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ipr_sata_port *sata_port = ap->private_data; + struct ipr_ioasa_gata *g = &sata_port->ioasa; + + tf->feature = g->error; + tf->nsect = g->nsect; + tf->lbal = g->lbal; + tf->lbam = g->lbam; + tf->lbah = g->lbah; + tf->device = g->device; + tf->command = g->status; + tf->hob_nsect = g->hob_nsect; + tf->hob_lbal = g->hob_lbal; + tf->hob_lbam = g->hob_lbam; + tf->hob_lbah = g->hob_lbah; + tf->ctl = g->alt_status; +} + +/** + * ipr_copy_sata_tf - Copy a SATA taskfile to an IOA data structure + * @regs: destination + * @tf: source ATA taskfile + * + * Return value: + * none + **/ +static void ipr_copy_sata_tf(struct ipr_ioarcb_ata_regs *regs, + struct ata_taskfile *tf) +{ + regs->feature = tf->feature; + regs->nsect = tf->nsect; + regs->lbal = tf->lbal; + regs->lbam = tf->lbam; + regs->lbah = tf->lbah; + regs->device = tf->device; + regs->command = tf->command; + regs->hob_feature = tf->hob_feature; + regs->hob_nsect = tf->hob_nsect; + regs->hob_lbal = tf->hob_lbal; + regs->hob_lbam = tf->hob_lbam; + regs->hob_lbah = tf->hob_lbah; + regs->ctl = tf->ctl; +} + +/** + * ipr_sata_done - done function for SATA commands + * @ipr_cmd: ipr command struct + * + * This function is invoked by the interrupt handler for + * ops generated by the SCSI mid-layer to SATA devices + * + * Return value: + * none + **/ +static void ipr_sata_done(struct ipr_cmnd *ipr_cmd) +{ + struct ipr_ioa_cfg *ioa_cfg = ipr_cmd->ioa_cfg; + struct ata_queued_cmd *qc = ipr_cmd->qc; + struct ipr_sata_port *sata_port = qc->ap->private_data; + struct ipr_resource_entry *res = sata_port->res; + u32 ioasc = be32_to_cpu(ipr_cmd->ioasa.ioasc); + + memcpy(&sata_port->ioasa, &ipr_cmd->ioasa.u.gata, + sizeof(struct ipr_ioasa_gata)); + ipr_dump_ioasa(ioa_cfg, ipr_cmd, res); + + if (be32_to_cpu(ipr_cmd->ioasa.ioasc_specific) & IPR_ATA_DEVICE_WAS_RESET) + scsi_report_device_reset(ioa_cfg->host, res->cfgte.res_addr.bus, + res->cfgte.res_addr.target); + + if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR) + qc->err_mask |= __ac_err_mask(ipr_cmd->ioasa.u.gata.status); + else + qc->err_mask |= ac_err_mask(ipr_cmd->ioasa.u.gata.status); + list_add_tail(&ipr_cmd->queue, &ioa_cfg->free_q); + ata_qc_complete(qc); +} + +/** + * ipr_build_ata_ioadl - Build an ATA scatter/gather list + * @ipr_cmd: ipr command struct + * @qc: ATA queued command + * + **/ +static void ipr_build_ata_ioadl(struct ipr_cmnd *ipr_cmd, + struct ata_queued_cmd *qc) +{ + u32 ioadl_flags = 0; + struct ipr_ioarcb *ioarcb = &ipr_cmd->ioarcb; + struct ipr_ioadl_desc *ioadl = ipr_cmd->ioadl; + int len = qc->nbytes + qc->pad_len; + struct scatterlist *sg; + + if (len == 0) + return; + + if (qc->dma_dir == DMA_TO_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_WRITE; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_WRITE_NOT_READ; + ioarcb->write_data_transfer_length = cpu_to_be32(len); + ioarcb->write_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); + } else if (qc->dma_dir == DMA_FROM_DEVICE) { + ioadl_flags = IPR_IOADL_FLAGS_READ; + ioarcb->read_data_transfer_length = cpu_to_be32(len); + ioarcb->read_ioadl_len = + cpu_to_be32(sizeof(struct ipr_ioadl_desc) * ipr_cmd->dma_use_sg); + } + + ata_for_each_sg(sg, qc) { + ioadl->flags_and_data_len = cpu_to_be32(ioadl_flags | sg_dma_len(sg)); + ioadl->address = cpu_to_be32(sg_dma_address(sg)); + if (ata_sg_is_last(sg, qc)) + ioadl->flags_and_data_len |= cpu_to_be32(IPR_IOADL_FLAGS_LAST); + else + ioadl++; + } +} + +/** + * ipr_qc_issue - Issue a SATA qc to a device + * @qc: queued command + * + * Return value: + * 0 if success + **/ +static unsigned int ipr_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ipr_sata_port *sata_port = ap->private_data; + struct ipr_resource_entry *res = sata_port->res; + struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; + struct ipr_cmnd *ipr_cmd; + struct ipr_ioarcb *ioarcb; + struct ipr_ioarcb_ata_regs *regs; + + if (unlikely(!ioa_cfg->allow_cmds || ioa_cfg->ioa_is_dead)) + return -EIO; + + ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); + ioarcb = &ipr_cmd->ioarcb; + regs = &ioarcb->add_data.u.regs; + + memset(&ioarcb->add_data, 0, sizeof(ioarcb->add_data)); + ioarcb->add_cmd_parms_len = cpu_to_be32(sizeof(ioarcb->add_data.u.regs)); + + list_add_tail(&ipr_cmd->queue, &ioa_cfg->pending_q); + ipr_cmd->qc = qc; + ipr_cmd->done = ipr_sata_done; + ipr_cmd->ioarcb.res_handle = res->cfgte.res_handle; + ioarcb->cmd_pkt.request_type = IPR_RQTYPE_ATA_PASSTHRU; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_LINK_DESC; + ioarcb->cmd_pkt.flags_hi |= IPR_FLAGS_HI_NO_ULEN_CHK; + ipr_cmd->dma_use_sg = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + + ipr_build_ata_ioadl(ipr_cmd, qc); + regs->flags |= IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION; + ipr_copy_sata_tf(regs, &qc->tf); + memcpy(ioarcb->cmd_pkt.cdb, qc->cdb, IPR_MAX_CDB_LEN); + ipr_trc_hook(ipr_cmd, IPR_TRACE_START, IPR_GET_PHYS_LOC(res->cfgte.res_addr)); + + switch (qc->tf.protocol) { + case ATA_PROT_NODATA: + case ATA_PROT_PIO: + break; + + case ATA_PROT_DMA: + regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA; + break; + + case ATA_PROT_ATAPI: + case ATA_PROT_ATAPI_NODATA: + regs->flags |= IPR_ATA_FLAG_PACKET_CMD; + break; + + case ATA_PROT_ATAPI_DMA: + regs->flags |= IPR_ATA_FLAG_PACKET_CMD; + regs->flags |= IPR_ATA_FLAG_XFER_TYPE_DMA; + break; + + default: + WARN_ON(1); + return -1; + } + + mb(); + writel(be32_to_cpu(ioarcb->ioarcb_host_pci_addr), + ioa_cfg->regs.ioarrin_reg); + return 0; +} + +/** + * ipr_ata_check_status - Return last ATA status + * @ap: ATA port + * + * Return value: + * ATA status + **/ +static u8 ipr_ata_check_status(struct ata_port *ap) +{ + struct ipr_sata_port *sata_port = ap->private_data; + return sata_port->ioasa.status; +} + +/** + * ipr_ata_check_altstatus - Return last ATA altstatus + * @ap: ATA port + * + * Return value: + * Alt ATA status + **/ +static u8 ipr_ata_check_altstatus(struct ata_port *ap) +{ + struct ipr_sata_port *sata_port = ap->private_data; + return sata_port->ioasa.alt_status; +} + +static struct ata_port_operations ipr_sata_ops = { + .port_disable = ata_port_disable, + .check_status = ipr_ata_check_status, + .check_altstatus = ipr_ata_check_altstatus, + .dev_select = ata_noop_dev_select, + .phy_reset = ipr_ata_phy_reset, + .post_internal_cmd = ipr_ata_post_internal, + .tf_read = ipr_tf_read, + .qc_prep = ata_noop_qc_prep, + .qc_issue = ipr_qc_issue, + .port_start = ata_sas_port_start, + .port_stop = ata_sas_port_stop +}; + +static struct ata_port_info sata_port_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + .pio_mask = 0x10, /* pio4 */ + .mwdma_mask = 0x07, + .udma_mask = 0x7f, /* udma0-6 */ + .port_ops = &ipr_sata_ops +}; + #ifdef CONFIG_PPC_PSERIES static const u16 ipr_blocked_processors[] = { PV_NORTHSTAR, @@ -6352,7 +6996,7 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, struct Scsi_Host *host; unsigned long ipr_regs_pci; void __iomem *ipr_regs; - u32 rc = PCIBIOS_SUCCESSFUL; + int rc = PCIBIOS_SUCCESSFUL; volatile u32 mask, uproc; ENTER; @@ -6374,6 +7018,9 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ioa_cfg = (struct ipr_ioa_cfg *)host->hostdata; memset(ioa_cfg, 0, sizeof(struct ipr_ioa_cfg)); + host->transportt = &ipr_transport_template; + ata_host_init(&ioa_cfg->ata_host, &pdev->dev, + sata_port_info.flags, &ipr_sata_ops); ioa_cfg->chip_cfg = ipr_get_chip_cfg(dev_id); @@ -6749,7 +7396,7 @@ static int __init ipr_init(void) ipr_info("IBM Power RAID SCSI Device Driver version: %s %s\n", IPR_DRIVER_VERSION, IPR_DRIVER_DATE); - return pci_module_init(&ipr_driver); + return pci_register_driver(&ipr_driver); } /** diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 11eaff524327..6d035283af08 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -28,6 +28,7 @@ #include <linux/types.h> #include <linux/completion.h> +#include <linux/libata.h> #include <linux/list.h> #include <linux/kref.h> #include <scsi/scsi.h> @@ -36,8 +37,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.1.4" -#define IPR_DRIVER_DATE "(August 2, 2006)" +#define IPR_DRIVER_VERSION "2.2.0" +#define IPR_DRIVER_DATE "(September 25, 2006)" /* * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding @@ -849,6 +850,13 @@ struct ipr_bus_attributes { u32 max_xfer_rate; }; +struct ipr_sata_port { + struct ipr_ioa_cfg *ioa_cfg; + struct ata_port *ap; + struct ipr_resource_entry *res; + struct ipr_ioasa_gata ioasa; +}; + struct ipr_resource_entry { struct ipr_config_table_entry cfgte; u8 needs_sync_complete:1; @@ -858,6 +866,7 @@ struct ipr_resource_entry { u8 resetting_device:1; struct scsi_device *sdev; + struct ipr_sata_port *sata_port; struct list_head queue; }; @@ -928,10 +937,11 @@ struct ipr_trace_entry { u32 time; u8 op_code; + u8 ata_op_code; u8 type; #define IPR_TRACE_START 0x00 #define IPR_TRACE_FINISH 0xff - u16 cmd_index; + u8 cmd_index; __be32 res_handle; union { @@ -1073,6 +1083,7 @@ struct ipr_ioa_cfg { struct ipr_cmnd *reset_cmd; + struct ata_host ata_host; char ipr_cmd_label[8]; #define IPR_CMD_LABEL "ipr_cmnd" struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS]; @@ -1085,6 +1096,7 @@ struct ipr_cmnd { struct ipr_ioadl_desc ioadl[IPR_NUM_IOADL_ENTRIES]; struct list_head queue; struct scsi_cmnd *scsi_cmd; + struct ata_queued_cmd *qc; struct completion completion; struct timer_list timer; void (*done) (struct ipr_cmnd *); diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 3c639286ec1e..9a9ab297cf17 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -182,14 +182,8 @@ #include <linux/dma-mapping.h> #include <scsi/sg.h> - #include "scsi.h" - -#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) -#include "hosts.h" -#else #include <scsi/scsi_host.h> -#endif #include "ips.h" @@ -250,9 +244,9 @@ module_param(ips, charp, 0); */ static int ips_detect(struct scsi_host_template *); static int ips_release(struct Scsi_Host *); -static int ips_eh_abort(Scsi_Cmnd *); -static int ips_eh_reset(Scsi_Cmnd *); -static int ips_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); +static int ips_eh_abort(struct scsi_cmnd *); +static int ips_eh_reset(struct scsi_cmnd *); +static int ips_queue(struct scsi_cmnd *, void (*)(struct scsi_cmnd *)); static const char *ips_info(struct Scsi_Host *); static irqreturn_t do_ipsintr(int, void *, struct pt_regs *); static int ips_hainit(ips_ha_t *); @@ -325,24 +319,26 @@ static uint32_t ips_statupd_copperhead_memio(ips_ha_t *); static uint32_t ips_statupd_morpheus(ips_ha_t *); static ips_scb_t *ips_getscb(ips_ha_t *); static void ips_putq_scb_head(ips_scb_queue_t *, ips_scb_t *); -static void ips_putq_wait_tail(ips_wait_queue_t *, Scsi_Cmnd *); +static void ips_putq_wait_tail(ips_wait_queue_t *, struct scsi_cmnd *); static void ips_putq_copp_tail(ips_copp_queue_t *, ips_copp_wait_item_t *); static ips_scb_t *ips_removeq_scb_head(ips_scb_queue_t *); static ips_scb_t *ips_removeq_scb(ips_scb_queue_t *, ips_scb_t *); -static Scsi_Cmnd *ips_removeq_wait_head(ips_wait_queue_t *); -static Scsi_Cmnd *ips_removeq_wait(ips_wait_queue_t *, Scsi_Cmnd *); +static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *); +static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *, + struct scsi_cmnd *); static ips_copp_wait_item_t *ips_removeq_copp(ips_copp_queue_t *, ips_copp_wait_item_t *); static ips_copp_wait_item_t *ips_removeq_copp_head(ips_copp_queue_t *); -static int ips_is_passthru(Scsi_Cmnd *); -static int ips_make_passthru(ips_ha_t *, Scsi_Cmnd *, ips_scb_t *, int); +static int ips_is_passthru(struct scsi_cmnd *); +static int ips_make_passthru(ips_ha_t *, struct scsi_cmnd *, ips_scb_t *, int); static int ips_usrcmd(ips_ha_t *, ips_passthru_t *, ips_scb_t *); static void ips_cleanup_passthru(ips_ha_t *, ips_scb_t *); -static void ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data, +static void ips_scmd_buf_write(struct scsi_cmnd * scmd, void *data, unsigned int count); -static void ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned int count); +static void ips_scmd_buf_read(struct scsi_cmnd * scmd, void *data, + unsigned int count); static int ips_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); static int ips_host_info(ips_ha_t *, char *, off_t, int); @@ -812,8 +808,7 @@ ips_halt(struct notifier_block *nb, ulong event, void *buf) /* Abort a command (using the new error code stuff) */ /* Note: this routine is called under the io_request_lock */ /****************************************************************************/ -int -ips_eh_abort(Scsi_Cmnd * SC) +int ips_eh_abort(struct scsi_cmnd *SC) { ips_ha_t *ha; ips_copp_wait_item_t *item; @@ -871,8 +866,7 @@ ips_eh_abort(Scsi_Cmnd * SC) /* NOTE: this routine is called under the io_request_lock spinlock */ /* */ /****************************************************************************/ -static int -__ips_eh_reset(Scsi_Cmnd * SC) +static int __ips_eh_reset(struct scsi_cmnd *SC) { int ret; int i; @@ -968,7 +962,7 @@ __ips_eh_reset(Scsi_Cmnd * SC) ret = (*ha->func.reset) (ha); if (!ret) { - Scsi_Cmnd *scsi_cmd; + struct scsi_cmnd *scsi_cmd; IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Controller reset failed - controller now offline.\n"); @@ -997,7 +991,7 @@ __ips_eh_reset(Scsi_Cmnd * SC) } if (!ips_clear_adapter(ha, IPS_INTR_IORL)) { - Scsi_Cmnd *scsi_cmd; + struct scsi_cmnd *scsi_cmd; IPS_PRINTK(KERN_NOTICE, ha->pcidev, "Controller reset failed - controller now offline.\n"); @@ -1059,8 +1053,7 @@ __ips_eh_reset(Scsi_Cmnd * SC) } -static int -ips_eh_reset(Scsi_Cmnd * SC) +static int ips_eh_reset(struct scsi_cmnd *SC) { int rc; @@ -1083,8 +1076,7 @@ ips_eh_reset(Scsi_Cmnd * SC) /* Linux obtains io_request_lock before calling this function */ /* */ /****************************************************************************/ -static int -ips_queue(Scsi_Cmnd * SC, void (*done) (Scsi_Cmnd *)) +static int ips_queue(struct scsi_cmnd *SC, void (*done) (struct scsi_cmnd *)) { ips_ha_t *ha; ips_passthru_t *pt; @@ -1602,8 +1594,7 @@ ips_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, /* Determine if the specified SCSI command is really a passthru command */ /* */ /****************************************************************************/ -static int -ips_is_passthru(Scsi_Cmnd * SC) +static int ips_is_passthru(struct scsi_cmnd *SC) { unsigned long flags; @@ -1685,7 +1676,7 @@ ips_alloc_passthru_buffer(ips_ha_t * ha, int length) /* */ /****************************************************************************/ static int -ips_make_passthru(ips_ha_t * ha, Scsi_Cmnd * SC, ips_scb_t * scb, int intr) +ips_make_passthru(ips_ha_t *ha, struct scsi_cmnd *SC, ips_scb_t *scb, int intr) { ips_passthru_t *pt; int length = 0; @@ -2734,9 +2725,9 @@ static void ips_next(ips_ha_t * ha, int intr) { ips_scb_t *scb; - Scsi_Cmnd *SC; - Scsi_Cmnd *p; - Scsi_Cmnd *q; + struct scsi_cmnd *SC; + struct scsi_cmnd *p; + struct scsi_cmnd *q; ips_copp_wait_item_t *item; int ret; unsigned long cpu_flags = 0; @@ -2847,7 +2838,7 @@ ips_next(ips_ha_t * ha, int intr) dcdb_active[scmd_channel(p) - 1] & (1 << scmd_id(p)))) { ips_freescb(ha, scb); - p = (Scsi_Cmnd *) p->host_scribble; + p = (struct scsi_cmnd *) p->host_scribble; continue; } @@ -2962,7 +2953,7 @@ ips_next(ips_ha_t * ha, int intr) break; } /* end case */ - p = (Scsi_Cmnd *) p->host_scribble; + p = (struct scsi_cmnd *) p->host_scribble; } /* end while */ @@ -3090,8 +3081,7 @@ ips_removeq_scb(ips_scb_queue_t * queue, ips_scb_t * item) /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static void -ips_putq_wait_tail(ips_wait_queue_t * queue, Scsi_Cmnd * item) +static void ips_putq_wait_tail(ips_wait_queue_t *queue, struct scsi_cmnd *item) { METHOD_TRACE("ips_putq_wait_tail", 1); @@ -3122,10 +3112,9 @@ ips_putq_wait_tail(ips_wait_queue_t * queue, Scsi_Cmnd * item) /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static Scsi_Cmnd * -ips_removeq_wait_head(ips_wait_queue_t * queue) +static struct scsi_cmnd *ips_removeq_wait_head(ips_wait_queue_t *queue) { - Scsi_Cmnd *item; + struct scsi_cmnd *item; METHOD_TRACE("ips_removeq_wait_head", 1); @@ -3135,7 +3124,7 @@ ips_removeq_wait_head(ips_wait_queue_t * queue) return (NULL); } - queue->head = (Scsi_Cmnd *) item->host_scribble; + queue->head = (struct scsi_cmnd *) item->host_scribble; item->host_scribble = NULL; if (queue->tail == item) @@ -3157,10 +3146,10 @@ ips_removeq_wait_head(ips_wait_queue_t * queue) /* ASSUMED to be called from within the HA lock */ /* */ /****************************************************************************/ -static Scsi_Cmnd * -ips_removeq_wait(ips_wait_queue_t * queue, Scsi_Cmnd * item) +static struct scsi_cmnd *ips_removeq_wait(ips_wait_queue_t *queue, + struct scsi_cmnd *item) { - Scsi_Cmnd *p; + struct scsi_cmnd *p; METHOD_TRACE("ips_removeq_wait", 1); @@ -3173,8 +3162,8 @@ ips_removeq_wait(ips_wait_queue_t * queue, Scsi_Cmnd * item) p = queue->head; - while ((p) && (item != (Scsi_Cmnd *) p->host_scribble)) - p = (Scsi_Cmnd *) p->host_scribble; + while ((p) && (item != (struct scsi_cmnd *) p->host_scribble)) + p = (struct scsi_cmnd *) p->host_scribble; if (p) { /* found a match */ @@ -3659,11 +3648,10 @@ ips_send_wait(ips_ha_t * ha, ips_scb_t * scb, int timeout, int intr) /* Routine Name: ips_scmd_buf_write */ /* */ /* Routine Description: */ -/* Write data to Scsi_Cmnd request_buffer at proper offsets */ +/* Write data to struct scsi_cmnd request_buffer at proper offsets */ /****************************************************************************/ static void -ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data, unsigned - int count) +ips_scmd_buf_write(struct scsi_cmnd *scmd, void *data, unsigned int count) { if (scmd->use_sg) { int i; @@ -3698,11 +3686,10 @@ ips_scmd_buf_write(Scsi_Cmnd * scmd, void *data, unsigned /* Routine Name: ips_scmd_buf_read */ /* */ /* Routine Description: */ -/* Copy data from a Scsi_Cmnd to a new, linear buffer */ +/* Copy data from a struct scsi_cmnd to a new, linear buffer */ /****************************************************************************/ static void -ips_scmd_buf_read(Scsi_Cmnd * scmd, void *data, unsigned - int count) +ips_scmd_buf_read(struct scsi_cmnd *scmd, void *data, unsigned int count) { if (scmd->use_sg) { int i; @@ -7078,7 +7065,7 @@ ips_remove_device(struct pci_dev *pci_dev) static int __init ips_module_init(void) { - if (pci_module_init(&ips_pci_driver) < 0) + if (pci_register_driver(&ips_pci_driver) < 0) return -ENODEV; ips_driver_template.module = THIS_MODULE; ips_order_controllers(); diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index f46c382e5599..34680f3dd452 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -6,7 +6,7 @@ /* David Jeffery, Adaptec, Inc. */ /* */ /* Copyright (C) 1999 IBM Corporation */ -/* Copyright (C) 2003 Adaptec, Inc. */ +/* Copyright (C) 2003 Adaptec, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ @@ -1033,14 +1033,14 @@ typedef struct ips_scb_queue { * Wait queue_format */ typedef struct ips_wait_queue { - Scsi_Cmnd *head; - Scsi_Cmnd *tail; - int count; + struct scsi_cmnd *head; + struct scsi_cmnd *tail; + int count; } ips_wait_queue_t; typedef struct ips_copp_wait_item { - Scsi_Cmnd *scsi_cmd; - struct ips_copp_wait_item *next; + struct scsi_cmnd *scsi_cmd; + struct ips_copp_wait_item *next; } ips_copp_wait_item_t; typedef struct ips_copp_queue { @@ -1149,7 +1149,7 @@ typedef struct ips_scb { uint32_t flags; uint32_t op_code; IPS_SG_LIST sg_list; - Scsi_Cmnd *scsi_cmd; + struct scsi_cmnd *scsi_cmd; struct ips_scb *q_next; ips_scb_callback callback; uint32_t sg_busaddr; @@ -1175,7 +1175,7 @@ typedef struct ips_scb_pt { uint32_t flags; uint32_t op_code; IPS_SG_LIST *sg_list; - Scsi_Cmnd *scsi_cmd; + struct scsi_cmnd *scsi_cmd; struct ips_scb *q_next; ips_scb_callback callback; } ips_scb_pt_t; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 4cdf3464267f..a5723ad0a099 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -389,7 +389,8 @@ lpfc_config_port_post(struct lpfc_hba * phba) lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed); pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; - if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) { + rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, @@ -406,7 +407,8 @@ lpfc_config_port_post(struct lpfc_hba * phba) readl(phba->HAregaddr); /* flush */ phba->hba_state = LPFC_HBA_ERROR; - mempool_free(pmb, phba->mbox_mem_pool); + if (rc != MBX_BUSY) + mempool_free(pmb, phba->mbox_mem_pool); return -EIO; } /* MBOX buffer will be freed in mbox compl */ diff --git a/drivers/scsi/megaraid/mega_common.h b/drivers/scsi/megaraid/mega_common.h index 8cd0bd1d0f7c..b50e27e66024 100644 --- a/drivers/scsi/megaraid/mega_common.h +++ b/drivers/scsi/megaraid/mega_common.h @@ -175,7 +175,7 @@ typedef struct { uint8_t max_lun; uint32_t unique_id; - uint8_t irq; + int irq; uint8_t ito; caddr_t ibuf; dma_addr_t ibuf_dma_h; diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 4cab5b534b25..977b6e8d8525 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -10,7 +10,7 @@ * 2 of the License, or (at your option) any later version. * * FILE : megaraid_sas.c - * Version : v00.00.03.01 + * Version : v00.00.03.05 * * Authors: * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com> @@ -71,6 +71,8 @@ static struct megasas_mgmt_info megasas_mgmt_info; static struct fasync_struct *megasas_async_queue; static DEFINE_MUTEX(megasas_async_queue_mutex); +static u32 megasas_dbg_lvl; + /** * megasas_get_cmd - Get a command from the free pool * @instance: Adapter soft state @@ -135,6 +137,19 @@ megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs) } /** + * megasas_disable_intr_xscale -Disables interrupt + * @regs: MFI register set + */ +static inline void +megasas_disable_intr_xscale(struct megasas_register_set __iomem * regs) +{ + u32 mask = 0x1f; + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** * megasas_read_fw_status_reg_xscale - returns the current FW status value * @regs: MFI register set */ @@ -185,6 +200,7 @@ static struct megasas_instance_template megasas_instance_template_xscale = { .fire_cmd = megasas_fire_cmd_xscale, .enable_intr = megasas_enable_intr_xscale, + .disable_intr = megasas_disable_intr_xscale, .clear_intr = megasas_clear_intr_xscale, .read_fw_status_reg = megasas_read_fw_status_reg_xscale, }; @@ -215,6 +231,19 @@ megasas_enable_intr_ppc(struct megasas_register_set __iomem * regs) } /** + * megasas_disable_intr_ppc - Disable interrupt + * @regs: MFI register set + */ +static inline void +megasas_disable_intr_ppc(struct megasas_register_set __iomem * regs) +{ + u32 mask = 0xFFFFFFFF; + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** * megasas_read_fw_status_reg_ppc - returns the current FW status value * @regs: MFI register set */ @@ -265,6 +294,7 @@ static struct megasas_instance_template megasas_instance_template_ppc = { .fire_cmd = megasas_fire_cmd_ppc, .enable_intr = megasas_enable_intr_ppc, + .disable_intr = megasas_disable_intr_ppc, .clear_intr = megasas_clear_intr_ppc, .read_fw_status_reg = megasas_read_fw_status_reg_ppc, }; @@ -275,25 +305,6 @@ static struct megasas_instance_template megasas_instance_template_ppc = { */ /** - * megasas_disable_intr - Disables interrupts - * @regs: MFI register set - */ -static inline void -megasas_disable_intr(struct megasas_instance *instance) -{ - u32 mask = 0x1f; - struct megasas_register_set __iomem *regs = instance->reg_set; - - if(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078R) - mask = 0xffffffff; - - writel(mask, ®s->outbound_intr_mask); - - /* Dummy readl to force pci flush */ - readl(®s->outbound_intr_mask); -} - -/** * megasas_issue_polled - Issues a polling command * @instance: Adapter soft state * @cmd: Command packet to be issued @@ -336,6 +347,7 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) * @cmd: Command to be issued * * This function waits on an event for the command to be returned from ISR. + * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs * Used to issue ioctl commands. */ static int @@ -346,7 +358,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); - wait_event(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA)); + wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), + MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); return 0; } @@ -358,7 +371,8 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, * * MFI firmware can abort previously issued AEN comamnd (automatic event * notification). The megasas_issue_blocked_abort_cmd() issues such abort - * cmd and blocks till it is completed. + * cmd and waits for return status. + * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs */ static int megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, @@ -392,7 +406,8 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, /* * Wait for this cmd to complete */ - wait_event(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF)); + wait_event_timeout(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF), + MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); megasas_return_cmd(instance, cmd); return 0; @@ -495,6 +510,46 @@ megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, return sge_count; } + /** + * megasas_get_frame_count - Computes the number of frames + * @sge_count : number of sg elements + * + * Returns the number of frames required for numnber of sge's (sge_count) + */ + +u32 megasas_get_frame_count(u8 sge_count) +{ + int num_cnt; + int sge_bytes; + u32 sge_sz; + u32 frame_count=0; + + sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : + sizeof(struct megasas_sge32); + + /* + * Main frame can contain 2 SGEs for 64-bit SGLs and + * 3 SGEs for 32-bit SGLs + */ + if (IS_DMA64) + num_cnt = sge_count - 2; + else + num_cnt = sge_count - 3; + + if(num_cnt>0){ + sge_bytes = sge_sz * num_cnt; + + frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + + ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) ; + } + /* Main frame */ + frame_count +=1; + + if (frame_count > 7) + frame_count = 8; + return frame_count; +} + /** * megasas_build_dcdb - Prepares a direct cdb (DCDB) command * @instance: Adapter soft state @@ -508,8 +563,6 @@ static int megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd *cmd) { - u32 sge_sz; - int sge_bytes; u32 is_logical; u32 device_id; u16 flags = 0; @@ -544,9 +597,6 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, /* * Construct SGL */ - sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : - sizeof(struct megasas_sge32); - if (IS_DMA64) { pthru->flags |= MFI_FRAME_SGL64; pthru->sge_count = megasas_make_sgl64(instance, scp, @@ -562,17 +612,11 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, pthru->sense_buf_phys_addr_hi = 0; pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr; - sge_bytes = sge_sz * pthru->sge_count; - /* * Compute the total number of frames this command consumes. FW uses * this number to pull sufficient number of frames from host memory. */ - cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + - ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1; - - if (cmd->frame_count > 7) - cmd->frame_count = 8; + cmd->frame_count = megasas_get_frame_count(pthru->sge_count); return cmd->frame_count; } @@ -589,8 +633,6 @@ static int megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, struct megasas_cmd *cmd) { - u32 sge_sz; - int sge_bytes; u32 device_id; u8 sc = scp->cmnd[0]; u16 flags = 0; @@ -605,7 +647,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, flags = MFI_FRAME_DIR_READ; /* - * Preare the Logical IO frame: 2nd bit is zero for all read cmds + * Prepare the Logical IO frame: 2nd bit is zero for all read cmds */ ldio->cmd = (sc & 0x02) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ; ldio->cmd_status = 0x0; @@ -674,9 +716,6 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, /* * Construct SGL */ - sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : - sizeof(struct megasas_sge32); - if (IS_DMA64) { ldio->flags |= MFI_FRAME_SGL64; ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); @@ -690,13 +729,11 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, ldio->sense_buf_phys_addr_hi = 0; ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr; - sge_bytes = sge_sz * ldio->sge_count; - - cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + - ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1; - - if (cmd->frame_count > 7) - cmd->frame_count = 8; + /* + * Compute the total number of frames this command consumes. FW uses + * this number to pull sufficient number of frames from host memory. + */ + cmd->frame_count = megasas_get_frame_count(ldio->sge_count); return cmd->frame_count; } @@ -727,6 +764,69 @@ static inline int megasas_is_ldio(struct scsi_cmnd *cmd) } } + /** + * megasas_dump_pending_frames - Dumps the frame address of all pending cmds + * in FW + * @instance: Adapter soft state + */ +static inline void +megasas_dump_pending_frames(struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + int i,n; + union megasas_sgl *mfi_sgl; + struct megasas_io_frame *ldio; + struct megasas_pthru_frame *pthru; + u32 sgcount; + u32 max_cmd = instance->max_fw_cmds; + + printk(KERN_ERR "\nmegasas[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no); + printk(KERN_ERR "megasas[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding)); + if (IS_DMA64) + printk(KERN_ERR "\nmegasas[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no); + else + printk(KERN_ERR "\nmegasas[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no); + + printk(KERN_ERR "megasas[%d]: Pending OS cmds in FW : \n",instance->host->host_no); + for (i = 0; i < max_cmd; i++) { + cmd = instance->cmd_list[i]; + if(!cmd->scmd) + continue; + printk(KERN_ERR "megasas[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr); + if (megasas_is_ldio(cmd->scmd)){ + ldio = (struct megasas_io_frame *)cmd->frame; + mfi_sgl = &ldio->sgl; + sgcount = ldio->sge_count; + printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no, cmd->frame_count,ldio->cmd,ldio->target_id, ldio->start_lba_lo,ldio->start_lba_hi,ldio->sense_buf_phys_addr_lo,sgcount); + } + else { + pthru = (struct megasas_pthru_frame *) cmd->frame; + mfi_sgl = &pthru->sgl; + sgcount = pthru->sge_count; + printk(KERN_ERR "megasas[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n",instance->host->host_no,cmd->frame_count,pthru->cmd,pthru->target_id,pthru->lun,pthru->cdb_len , pthru->data_xfer_len,pthru->sense_buf_phys_addr_lo,sgcount); + } + if(megasas_dbg_lvl & MEGASAS_DBG_LVL){ + for (n = 0; n < sgcount; n++){ + if (IS_DMA64) + printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%08lx ",mfi_sgl->sge64[n].length , (unsigned long)mfi_sgl->sge64[n].phys_addr) ; + else + printk(KERN_ERR "megasas: sgl len : 0x%x, sgl addr : 0x%x ",mfi_sgl->sge32[n].length , mfi_sgl->sge32[n].phys_addr) ; + } + } + printk(KERN_ERR "\n"); + } /*for max_cmd*/ + printk(KERN_ERR "\nmegasas[%d]: Pending Internal cmds in FW : \n",instance->host->host_no); + for (i = 0; i < max_cmd; i++) { + + cmd = instance->cmd_list[i]; + + if(cmd->sync_cmd == 1){ + printk(KERN_ERR "0x%08lx : ", (unsigned long)cmd->frame_phys_addr); + } + } + printk(KERN_ERR "megasas[%d]: Dumping Done.\n\n",instance->host->host_no); +} + /** * megasas_queue_command - Queue entry point * @scmd: SCSI command to be queued @@ -832,6 +932,13 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) } if (atomic_read(&instance->fw_outstanding)) { + /* + * Send signal to FW to stop processing any pending cmds. + * The controller will be taken offline by the OS now. + */ + writel(MFI_STOP_ADP, + &instance->reg_set->inbound_doorbell); + megasas_dump_pending_frames(instance); instance->hw_crit_error = 1; return FAILED; } @@ -1168,11 +1275,6 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, static int megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) { - u32 producer; - u32 consumer; - u32 context; - struct megasas_cmd *cmd; - /* * Check if it is our interrupt * Clear the interrupt @@ -1180,23 +1282,10 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) if(instance->instancet->clear_intr(instance->reg_set)) return IRQ_NONE; - producer = *instance->producer; - consumer = *instance->consumer; - - while (consumer != producer) { - context = instance->reply_queue[consumer]; - - cmd = instance->cmd_list[context]; - - megasas_complete_cmd(instance, cmd, alt_status); - - consumer++; - if (consumer == (instance->max_fw_cmds + 1)) { - consumer = 0; - } - } - - *instance->consumer = producer; + /* + * Schedule the tasklet for cmd completion + */ + tasklet_schedule(&instance->isr_tasklet); return IRQ_HANDLED; } @@ -1229,10 +1318,12 @@ megasas_transition_to_ready(struct megasas_instance* instance) fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + if (fw_state != MFI_STATE_READY) + printk(KERN_INFO "megasas: Waiting for FW to come to ready" + " state\n"); + while (fw_state != MFI_STATE_READY) { - printk(KERN_INFO "megasas: Waiting for FW to come to ready" - " state\n"); switch (fw_state) { case MFI_STATE_FAULT: @@ -1244,19 +1335,27 @@ megasas_transition_to_ready(struct megasas_instance* instance) /* * Set the CLR bit in inbound doorbell */ - writel(MFI_INIT_CLEAR_HANDSHAKE, + writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, &instance->reg_set->inbound_doorbell); max_wait = 2; cur_state = MFI_STATE_WAIT_HANDSHAKE; break; + case MFI_STATE_BOOT_MESSAGE_PENDING: + writel(MFI_INIT_HOTPLUG, + &instance->reg_set->inbound_doorbell); + + max_wait = 10; + cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; + break; + case MFI_STATE_OPERATIONAL: /* - * Bring it to READY state; assuming max wait 2 secs + * Bring it to READY state; assuming max wait 10 secs */ - megasas_disable_intr(instance); - writel(MFI_INIT_READY, &instance->reg_set->inbound_doorbell); + instance->instancet->disable_intr(instance->reg_set); + writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell); max_wait = 10; cur_state = MFI_STATE_OPERATIONAL; @@ -1323,6 +1422,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) return -ENODEV; } }; + printk(KERN_INFO "megasas: FW now in Ready state\n"); return 0; } @@ -1352,7 +1452,7 @@ static void megasas_teardown_frame_pool(struct megasas_instance *instance) cmd->frame_phys_addr); if (cmd->sense) - pci_pool_free(instance->sense_dma_pool, cmd->frame, + pci_pool_free(instance->sense_dma_pool, cmd->sense, cmd->sense_phys_addr); } @@ -1628,6 +1728,39 @@ megasas_get_ctrl_info(struct megasas_instance *instance, } /** + * megasas_complete_cmd_dpc - Returns FW's controller structure + * @instance_addr: Address of adapter soft state + * + * Tasklet to complete cmds + */ +void megasas_complete_cmd_dpc(unsigned long instance_addr) +{ + u32 producer; + u32 consumer; + u32 context; + struct megasas_cmd *cmd; + struct megasas_instance *instance = (struct megasas_instance *)instance_addr; + + producer = *instance->producer; + consumer = *instance->consumer; + + while (consumer != producer) { + context = instance->reply_queue[consumer]; + + cmd = instance->cmd_list[context]; + + megasas_complete_cmd(instance, cmd, DID_OK); + + consumer++; + if (consumer == (instance->max_fw_cmds + 1)) { + consumer = 0; + } + } + + *instance->consumer = producer; +} + +/** * megasas_init_mfi - Initializes the FW * @instance: Adapter soft state * @@ -1690,6 +1823,12 @@ static int megasas_init_mfi(struct megasas_instance *instance) * Get various operational parameters from status register */ instance->max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; + /* + * Reduce the max supported cmds by 1. This is to ensure that the + * reply_q_sz (1 more than the max cmd that driver may send) + * does not exceed max cmds that the FW can support + */ + instance->max_fw_cmds = instance->max_fw_cmds-1; instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> 0x10; /* @@ -1754,7 +1893,7 @@ static int megasas_init_mfi(struct megasas_instance *instance) /* * disable the intr before firing the init frame to FW */ - megasas_disable_intr(instance); + instance->instancet->disable_intr(instance->reg_set); /* * Issue the init frame in polled mode @@ -1791,6 +1930,12 @@ static int megasas_init_mfi(struct megasas_instance *instance) kfree(ctrl_info); + /* + * Setup tasklet for cmd completion + */ + + tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, + (unsigned long)instance); return 0; fail_fw_init: @@ -2182,6 +2327,8 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) instance->unique_id = pdev->bus->number << 8 | pdev->devfn; instance->init_id = MEGASAS_DEFAULT_INIT_ID; + megasas_dbg_lvl = 0; + /* * Initialize MFI Firmware */ @@ -2234,7 +2381,7 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) megasas_mgmt_info.max_index--; pci_set_drvdata(pdev, NULL); - megasas_disable_intr(instance); + instance->instancet->disable_intr(instance->reg_set); free_irq(instance->pdev->irq, instance); megasas_release_mfi(instance); @@ -2348,6 +2495,7 @@ static void megasas_detach_one(struct pci_dev *pdev) scsi_remove_host(instance->host); megasas_flush_cache(instance); megasas_shutdown_controller(instance); + tasklet_kill(&instance->isr_tasklet); /* * Take the instance off the instance array. Note that we will not @@ -2364,7 +2512,7 @@ static void megasas_detach_one(struct pci_dev *pdev) pci_set_drvdata(instance->pdev, NULL); - megasas_disable_intr(instance); + instance->instancet->disable_intr(instance->reg_set); free_irq(instance->pdev->irq, instance); @@ -2716,7 +2864,8 @@ static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) int i; int error = 0; - clear_user(ioc, sizeof(*ioc)); + if (clear_user(ioc, sizeof(*ioc))) + return -EFAULT; if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) || copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) || @@ -2808,6 +2957,26 @@ megasas_sysfs_show_release_date(struct device_driver *dd, char *buf) static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, NULL); +static ssize_t +megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf) +{ + return sprintf(buf,"%u",megasas_dbg_lvl); +} + +static ssize_t +megasas_sysfs_set_dbg_lvl(struct device_driver *dd, const char *buf, size_t count) +{ + int retval = count; + if(sscanf(buf,"%u",&megasas_dbg_lvl)<1){ + printk(KERN_ERR "megasas: could not set dbg_lvl\n"); + retval = -EINVAL; + } + return retval; +} + +static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUGO, megasas_sysfs_show_dbg_lvl, + megasas_sysfs_set_dbg_lvl); + /** * megasas_init - Driver load entry point */ @@ -2842,14 +3011,33 @@ static int __init megasas_init(void) if (rval) { printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n"); - unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); - } - - driver_create_file(&megasas_pci_driver.driver, &driver_attr_version); - driver_create_file(&megasas_pci_driver.driver, - &driver_attr_release_date); + goto err_pcidrv; + } + + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_version); + if (rval) + goto err_dcf_attr_ver; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_release_date); + if (rval) + goto err_dcf_rel_date; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_dbg_lvl); + if (rval) + goto err_dcf_dbg_lvl; return rval; +err_dcf_dbg_lvl: + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_release_date); +err_dcf_rel_date: + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); +err_dcf_attr_ver: + pci_unregister_driver(&megasas_pci_driver); +err_pcidrv: + unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); + return rval; } /** @@ -2857,9 +3045,11 @@ static int __init megasas_init(void) */ static void __exit megasas_exit(void) { - driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_dbg_lvl); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_release_date); + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); pci_unregister_driver(&megasas_pci_driver); unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 3531a14222a7..55eddcf8eb15 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -18,9 +18,9 @@ /** * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.03.01" -#define MEGASAS_RELDATE "May 14, 2006" -#define MEGASAS_EXT_VERSION "Sun May 14 22:49:52 PDT 2006" +#define MEGASAS_VERSION "00.00.03.05" +#define MEGASAS_RELDATE "Oct 02, 2006" +#define MEGASAS_EXT_VERSION "Mon Oct 02 11:21:32 PDT 2006" /* * Device IDs @@ -50,6 +50,7 @@ #define MFI_STATE_WAIT_HANDSHAKE 0x60000000 #define MFI_STATE_FW_INIT_2 0x70000000 #define MFI_STATE_DEVICE_SCAN 0x80000000 +#define MFI_STATE_BOOT_MESSAGE_PENDING 0x90000000 #define MFI_STATE_FLUSH_CACHE 0xA0000000 #define MFI_STATE_READY 0xB0000000 #define MFI_STATE_OPERATIONAL 0xC0000000 @@ -64,12 +65,18 @@ * READY : Move from OPERATIONAL to READY state; discard queue info * MFIMODE : Discard (possible) low MFA posted in 64-bit mode (??) * CLR_HANDSHAKE: FW is waiting for HANDSHAKE from BIOS or Driver + * HOTPLUG : Resume from Hotplug + * MFI_STOP_ADP : Send signal to FW to stop processing */ -#define MFI_INIT_ABORT 0x00000000 +#define MFI_INIT_ABORT 0x00000001 #define MFI_INIT_READY 0x00000002 #define MFI_INIT_MFIMODE 0x00000004 #define MFI_INIT_CLEAR_HANDSHAKE 0x00000008 -#define MFI_RESET_FLAGS MFI_INIT_READY|MFI_INIT_MFIMODE +#define MFI_INIT_HOTPLUG 0x00000010 +#define MFI_STOP_ADP 0x00000020 +#define MFI_RESET_FLAGS MFI_INIT_READY| \ + MFI_INIT_MFIMODE| \ + MFI_INIT_ABORT /** * MFI frame flags @@ -530,6 +537,8 @@ struct megasas_ctrl_info { #define MEGASAS_MAX_LUN 8 #define MEGASAS_MAX_LD 64 +#define MEGASAS_DBG_LVL 1 + /* * When SCSI mid-layer calls driver's reset routine, driver waits for * MEGASAS_RESET_WAIT_TIME seconds for all outstanding IO to complete. Note @@ -538,6 +547,7 @@ struct megasas_ctrl_info { * every MEGASAS_RESET_NOTICE_INTERVAL seconds */ #define MEGASAS_RESET_WAIT_TIME 180 +#define MEGASAS_INTERNAL_CMD_WAIT_TIME 180 #define MEGASAS_RESET_NOTICE_INTERVAL 5 #define MEGASAS_IOCTL_CMD 0 @@ -1042,6 +1052,7 @@ struct megasas_evt_detail { void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *); void (*enable_intr)(struct megasas_register_set __iomem *) ; + void (*disable_intr)(struct megasas_register_set __iomem *); int (*clear_intr)(struct megasas_register_set __iomem *); @@ -1092,6 +1103,7 @@ struct megasas_instance { u32 hw_crit_error; struct megasas_instance_template *instancet; + struct tasklet_struct isr_tasklet; }; #define MEGASAS_IS_LOGICAL(scp) \ diff --git a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c index bfb4f49e125d..1c624ce81897 100644 --- a/drivers/scsi/nsp32.c +++ b/drivers/scsi/nsp32.c @@ -3581,7 +3581,7 @@ static struct pci_driver nsp32_driver = { */ static int __init init_nsp32(void) { nsp32_msg(KERN_INFO, "loading..."); - return pci_module_init(&nsp32_driver); + return pci_register_driver(&nsp32_driver); } static void __exit exit_nsp32(void) { diff --git a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h index 5addf9fb1e15..a976e8193d16 100644 --- a/drivers/scsi/nsp32.h +++ b/drivers/scsi/nsp32.h @@ -619,47 +619,5 @@ typedef struct _nsp32_hw_data { #define REQSACK_TIMEOUT_TIME 10000 /* max wait time for REQ/SACK assertion or negation, 10000us == 10ms */ -/************************************************************************** - * Compatibility functions - */ - -/* for Kernel 2.4 */ -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)) -# define scsi_register_host(template) scsi_register_module(MODULE_SCSI_HA, template) -# define scsi_unregister_host(template) scsi_unregister_module(MODULE_SCSI_HA, template) -# define scsi_host_put(host) scsi_unregister(host) -# define pci_name(pci_dev) ((pci_dev)->slot_name) - -typedef void irqreturn_t; -# define IRQ_NONE /* */ -# define IRQ_HANDLED /* */ -# define IRQ_RETVAL(x) /* */ - -/* This is ad-hoc version of scsi_host_get_next() */ -static inline struct Scsi_Host *scsi_host_get_next(struct Scsi_Host *host) -{ - if (host == NULL) { - return scsi_hostlist; - } else { - return host->next; - } -} - -/* This is ad-hoc version of scsi_host_hn_get() */ -static inline struct Scsi_Host *scsi_host_hn_get(unsigned short hostno) -{ - struct Scsi_Host *host; - - for (host = scsi_host_get_next(NULL); host != NULL; - host = scsi_host_get_next(host)) { - if (host->host_no == hostno) { - break; - } - } - - return host; -} -#endif - #endif /* _NSP32_H */ /* end */ diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 4a2fed350d4e..824fe080d1dc 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -4843,8 +4843,7 @@ static int os_scsi_tape_close(struct inode * inode, struct file * filp) static int osst_ioctl(struct inode * inode,struct file * file, unsigned int cmd_in, unsigned long arg) { - int i, cmd_nr, cmd_type, retval = 0; - unsigned int blk; + int i, cmd_nr, cmd_type, blk, retval = 0; struct st_modedef * STm; struct st_partstat * STps; struct osst_request * SRpnt = NULL; @@ -5207,12 +5206,12 @@ static struct osst_buffer * new_tape_buffer( int from_initialization, int need_d priority = GFP_KERNEL; i = sizeof(struct osst_buffer) + (osst_max_sg_segs - 1) * sizeof(struct scatterlist); - tb = (struct osst_buffer *)kmalloc(i, priority); + tb = kzalloc(i, priority); if (!tb) { printk(KERN_NOTICE "osst :I: Can't allocate new tape buffer.\n"); return NULL; } - memset(tb, 0, i); + tb->sg_segs = tb->orig_sg_segs = 0; tb->use_sg = max_sg; tb->in_use = 1; @@ -5575,9 +5574,9 @@ static ssize_t osst_version_show(struct device_driver *ddd, char *buf) static DRIVER_ATTR(version, S_IRUGO, osst_version_show, NULL); -static void osst_create_driverfs_files(struct device_driver *driverfs) +static int osst_create_driverfs_files(struct device_driver *driverfs) { - driver_create_file(driverfs, &driver_attr_version); + return driver_create_file(driverfs, &driver_attr_version); } static void osst_remove_driverfs_files(struct device_driver *driverfs) @@ -5663,50 +5662,70 @@ CLASS_DEVICE_ATTR(file_count, S_IRUGO, osst_filemark_cnt_show, NULL); static struct class *osst_sysfs_class; -static int osst_sysfs_valid = 0; - -static void osst_sysfs_init(void) +static int osst_sysfs_init(void) { osst_sysfs_class = class_create(THIS_MODULE, "onstream_tape"); - if ( IS_ERR(osst_sysfs_class) ) - printk(KERN_WARNING "osst :W: Unable to register sysfs class\n"); - else - osst_sysfs_valid = 1; + if (IS_ERR(osst_sysfs_class)) { + printk(KERN_ERR "osst :W: Unable to register sysfs class\n"); + return PTR_ERR(osst_sysfs_class); + } + + return 0; } -static void osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * STp, char * name) +static void osst_sysfs_destroy(dev_t dev) { - struct class_device *osst_class_member; + class_device_destroy(osst_sysfs_class, dev); +} - if (!osst_sysfs_valid) return; +static int osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * STp, char * name) +{ + struct class_device *osst_class_member; + int err; - osst_class_member = class_device_create(osst_sysfs_class, NULL, dev, device, "%s", name); + osst_class_member = class_device_create(osst_sysfs_class, NULL, dev, + device, "%s", name); if (IS_ERR(osst_class_member)) { printk(KERN_WARNING "osst :W: Unable to add sysfs class member %s\n", name); - return; + return PTR_ERR(osst_class_member); } + class_set_devdata(osst_class_member, STp); - class_device_create_file(osst_class_member, &class_device_attr_ADR_rev); - class_device_create_file(osst_class_member, &class_device_attr_media_version); - class_device_create_file(osst_class_member, &class_device_attr_capacity); - class_device_create_file(osst_class_member, &class_device_attr_BOT_frame); - class_device_create_file(osst_class_member, &class_device_attr_EOD_frame); - class_device_create_file(osst_class_member, &class_device_attr_file_count); -} + err = class_device_create_file(osst_class_member, + &class_device_attr_ADR_rev); + if (err) + goto err_out; + err = class_device_create_file(osst_class_member, + &class_device_attr_media_version); + if (err) + goto err_out; + err = class_device_create_file(osst_class_member, + &class_device_attr_capacity); + if (err) + goto err_out; + err = class_device_create_file(osst_class_member, + &class_device_attr_BOT_frame); + if (err) + goto err_out; + err = class_device_create_file(osst_class_member, + &class_device_attr_EOD_frame); + if (err) + goto err_out; + err = class_device_create_file(osst_class_member, + &class_device_attr_file_count); + if (err) + goto err_out; -static void osst_sysfs_destroy(dev_t dev) -{ - if (!osst_sysfs_valid) return; + return 0; - class_device_destroy(osst_sysfs_class, dev); +err_out: + osst_sysfs_destroy(dev); + return err; } static void osst_sysfs_cleanup(void) { - if (osst_sysfs_valid) { - class_destroy(osst_sysfs_class); - osst_sysfs_valid = 0; - } + class_destroy(osst_sysfs_class); } /* @@ -5721,7 +5740,7 @@ static int osst_probe(struct device *dev) struct st_partstat * STps; struct osst_buffer * buffer; struct gendisk * drive; - int i, dev_num; + int i, dev_num, err = -ENODEV; if (SDp->type != TYPE_TAPE || !osst_supports(SDp)) return -ENODEV; @@ -5849,13 +5868,20 @@ static int osst_probe(struct device *dev) init_MUTEX(&tpnt->lock); osst_nr_dev++; write_unlock(&os_scsi_tapes_lock); + { char name[8]; + /* Rewind entry */ - osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num), dev, tpnt, tape_name(tpnt)); + err = osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num), dev, tpnt, tape_name(tpnt)); + if (err) + goto out_free_buffer; + /* No-rewind entry */ snprintf(name, 8, "%s%s", "n", tape_name(tpnt)); - osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num + 128), dev, tpnt, name); + err = osst_sysfs_add(MKDEV(OSST_MAJOR, dev_num + 128), dev, tpnt, name); + if (err) + goto out_free_sysfs1; } sdev_printk(KERN_INFO, SDp, @@ -5864,9 +5890,13 @@ static int osst_probe(struct device *dev) return 0; +out_free_sysfs1: + osst_sysfs_destroy(MKDEV(OSST_MAJOR, dev_num)); +out_free_buffer: + kfree(buffer); out_put_disk: put_disk(drive); - return -ENODEV; + return err; }; static int osst_remove(struct device *dev) @@ -5903,19 +5933,39 @@ static int osst_remove(struct device *dev) static int __init init_osst(void) { + int err; + printk(KERN_INFO "osst :I: Tape driver with OnStream support version %s\nosst :I: %s\n", osst_version, cvsid); validate_options(); - osst_sysfs_init(); - if ((register_chrdev(OSST_MAJOR,"osst", &osst_fops) < 0) || scsi_register_driver(&osst_template.gendrv)) { + err = osst_sysfs_init(); + if (err) + return err; + + err = register_chrdev(OSST_MAJOR, "osst", &osst_fops); + if (err < 0) { printk(KERN_ERR "osst :E: Unable to register major %d for OnStream tapes\n", OSST_MAJOR); - osst_sysfs_cleanup(); - return 1; + goto err_out; } - osst_create_driverfs_files(&osst_template.gendrv); + + err = scsi_register_driver(&osst_template.gendrv); + if (err) + goto err_out_chrdev; + + err = osst_create_driverfs_files(&osst_template.gendrv); + if (err) + goto err_out_scsidrv; return 0; + +err_out_scsidrv: + scsi_unregister_driver(&osst_template.gendrv); +err_out_chrdev: + unregister_chrdev(OSST_MAJOR, "osst"); +err_out: + osst_sysfs_cleanup(); + return err; } static void __exit exit_osst (void) diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c index 0d4c04e1f3de..053303d36118 100644 --- a/drivers/scsi/pcmcia/nsp_cs.c +++ b/drivers/scsi/pcmcia/nsp_cs.c @@ -80,7 +80,6 @@ static int free_ports = 0; module_param(free_ports, bool, 0); MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))"); -/* /usr/src/linux/drivers/scsi/hosts.h */ static struct scsi_host_template nsp_driver_template = { .proc_name = "nsp_cs", .proc_info = nsp_proc_info, diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index 332151e2a018..9f33e5946c0d 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -2862,7 +2862,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(30); + pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); @@ -3161,7 +3161,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) memset(((char *)pkt + 8), 0, (REQUEST_ENTRY_SIZE - 8)); /* Set ISP command timeout. */ - pkt->timeout = cpu_to_le16(30); + pkt->timeout = cpu_to_le16(cmd->timeout_per_command/HZ); /* Set device target ID and LUN */ pkt->lun = SCSI_LUN_32(cmd); @@ -4484,7 +4484,7 @@ qla1280_init(void) qla1280_setup(qla1280); #endif - return pci_module_init(&qla1280_pci_driver); + return pci_register_driver(&qla1280_pci_driver); } static void __exit diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 87f90c4f08e9..ee75a71f3c66 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -691,13 +691,13 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) uint32_t speed = 0; switch (ha->link_data_rate) { - case LDR_1GB: + case PORT_SPEED_1GB: speed = 1; break; - case LDR_2GB: + case PORT_SPEED_2GB: speed = 2; break; - case LDR_4GB: + case PORT_SPEED_4GB: speed = 4; break; } @@ -849,6 +849,49 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) return pfc_host_stat; } +static void +qla2x00_get_host_symbolic_name(struct Scsi_Host *shost) +{ + scsi_qla_host_t *ha = to_qla_host(shost); + + qla2x00_get_sym_node_name(ha, fc_host_symbolic_name(shost)); +} + +static void +qla2x00_set_host_system_hostname(struct Scsi_Host *shost) +{ + scsi_qla_host_t *ha = to_qla_host(shost); + + set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); +} + +static void +qla2x00_get_host_fabric_name(struct Scsi_Host *shost) +{ + scsi_qla_host_t *ha = to_qla_host(shost); + u64 node_name; + + if (ha->device_flags & SWITCH_FOUND) + node_name = wwn_to_u64(ha->fabric_node_name); + else + node_name = wwn_to_u64(ha->node_name); + + fc_host_fabric_name(shost) = node_name; +} + +static void +qla2x00_get_host_port_state(struct Scsi_Host *shost) +{ + scsi_qla_host_t *ha = to_qla_host(shost); + + if (!ha->flags.online) + fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; + else if (atomic_read(&ha->loop_state) == LOOP_TIMEOUT) + fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; + else + fc_host_port_state(shost) = FC_PORTSTATE_ONLINE; +} + struct fc_function_template qla2xxx_transport_functions = { .show_host_node_name = 1, @@ -861,6 +904,14 @@ struct fc_function_template qla2xxx_transport_functions = { .show_host_speed = 1, .get_host_port_type = qla2x00_get_host_port_type, .show_host_port_type = 1, + .get_host_symbolic_name = qla2x00_get_host_symbolic_name, + .show_host_symbolic_name = 1, + .set_host_system_hostname = qla2x00_set_host_system_hostname, + .show_host_system_hostname = 1, + .get_host_fabric_name = qla2x00_get_host_fabric_name, + .show_host_fabric_name = 1, + .get_host_port_state = qla2x00_get_host_port_state, + .show_host_port_state = 1, .dd_fcrport_size = sizeof(struct fc_port *), .show_rport_supported_classes = 1, diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 0930260aec2c..c37a30aa2146 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -608,6 +608,7 @@ typedef struct { */ #define MBC_SERDES_PARAMS 0x10 /* Serdes Tx Parameters. */ #define MBC_GET_IOCB_STATUS 0x12 /* Get IOCB status command. */ +#define MBC_PORT_PARAMS 0x1A /* Port iDMA Parameters. */ #define MBC_GET_TIMEOUT_PARAMS 0x22 /* Get FW timeouts. */ #define MBC_TRACE_CONTROL 0x27 /* Trace control command. */ #define MBC_GEN_SYSTEM_ERROR 0x2a /* Generate System Error. */ @@ -1497,6 +1498,9 @@ typedef struct { port_id_t d_id; uint8_t node_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE]; + uint8_t fabric_port_name[WWN_SIZE]; + uint16_t fp_speeds; + uint16_t fp_speed; } sw_info_t; /* @@ -1524,6 +1528,9 @@ typedef struct fc_port { uint16_t loop_id; uint16_t old_loop_id; + uint8_t fabric_port_name[WWN_SIZE]; + uint16_t fp_speed; + fc_port_type_t port_type; atomic_t state; @@ -1635,6 +1642,15 @@ typedef struct fc_port { #define RSNN_NN_REQ_SIZE (16 + 8 + 1 + 255) #define RSNN_NN_RSP_SIZE 16 +#define GFPN_ID_CMD 0x11C +#define GFPN_ID_REQ_SIZE (16 + 4) +#define GFPN_ID_RSP_SIZE (16 + 8) + +#define GPSC_CMD 0x127 +#define GPSC_REQ_SIZE (16 + 8) +#define GPSC_RSP_SIZE (16 + 2 + 2) + + /* * HBA attribute types. */ @@ -1748,7 +1764,7 @@ struct ct_sns_req { uint8_t reserved[3]; union { - /* GA_NXT, GPN_ID, GNN_ID, GFT_ID */ + /* GA_NXT, GPN_ID, GNN_ID, GFT_ID, GFPN_ID */ struct { uint8_t reserved; uint8_t port_id[3]; @@ -1823,6 +1839,10 @@ struct ct_sns_req { struct { uint8_t port_name[8]; } dpa; + + struct { + uint8_t port_name[8]; + } gpsc; } req; }; @@ -1886,6 +1906,15 @@ struct ct_sns_rsp { uint8_t port_name[8]; struct ct_fdmi_hba_attributes attrs; } ghat; + + struct { + uint8_t port_name[8]; + } gfpn_id; + + struct { + uint16_t speeds; + uint16_t speed; + } gpsc; } rsp; }; @@ -2182,11 +2211,11 @@ typedef struct scsi_qla_host { uint16_t max_public_loop_ids; uint16_t min_external_loopid; /* First external loop Id */ +#define PORT_SPEED_UNKNOWN 0xFFFF +#define PORT_SPEED_1GB 0x00 +#define PORT_SPEED_2GB 0x01 +#define PORT_SPEED_4GB 0x03 uint16_t link_data_rate; /* F/W operating speed */ -#define LDR_1GB 0 -#define LDR_2GB 1 -#define LDR_4GB 3 -#define LDR_UNKNOWN 0xFFFF uint8_t current_topology; uint8_t prev_topology; @@ -2333,6 +2362,7 @@ typedef struct scsi_qla_host { uint8_t *node_name; uint8_t *port_name; + uint8_t fabric_node_name[WWN_SIZE]; uint32_t isp_abort_cnt; /* Option ROM information. */ diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 8311ac2b93a8..bef7011378c6 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -208,6 +208,12 @@ qla2x00_trace_control(scsi_qla_host_t *, uint16_t, dma_addr_t, uint16_t); extern int qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t, uint16_t); +extern int +qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t *, uint16_t *); + +extern int +qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -279,6 +285,9 @@ extern int qla2x00_rsnn_nn(scsi_qla_host_t *); extern void *qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t); extern void *qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t); extern int qla2x00_fdmi_register(scsi_qla_host_t *); +extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *); +extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *); +extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *); /* * Global Function Prototypes in qla_attr.c source file. diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 2ebf259fccb2..97fbc62ec669 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -612,6 +612,14 @@ qla2x00_rnn_id(scsi_qla_host_t *ha) return (rval); } +void +qla2x00_get_sym_node_name(scsi_qla_host_t *ha, uint8_t *snn) +{ + sprintf(snn, "%s FW:v%d.%02d.%02d DVR:v%s",ha->model_number, + ha->fw_major_version, ha->fw_minor_version, + ha->fw_subminor_version, qla2x00_version_str); +} + /** * qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA. * @ha: HA context @@ -622,9 +630,6 @@ int qla2x00_rsnn_nn(scsi_qla_host_t *ha) { int rval; - uint8_t *snn; - uint8_t version[20]; - ms_iocb_entry_t *ms_pkt; struct ct_sns_req *ct_req; struct ct_sns_rsp *ct_rsp; @@ -649,20 +654,11 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha) memcpy(ct_req->req.rsnn_nn.node_name, ha->node_name, WWN_SIZE); /* Prepare the Symbolic Node Name */ - /* Board type */ - snn = ct_req->req.rsnn_nn.sym_node_name; - strcpy(snn, ha->model_number); - /* Firmware version */ - strcat(snn, " FW:v"); - sprintf(version, "%d.%02d.%02d", ha->fw_major_version, - ha->fw_minor_version, ha->fw_subminor_version); - strcat(snn, version); - /* Driver version */ - strcat(snn, " DVR:v"); - strcat(snn, qla2x00_version_str); + qla2x00_get_sym_node_name(ha, ct_req->req.rsnn_nn.sym_node_name); /* Calculate SNN length */ - ct_req->req.rsnn_nn.name_len = (uint8_t)strlen(snn); + ct_req->req.rsnn_nn.name_len = + (uint8_t)strlen(ct_req->req.rsnn_nn.sym_node_name); /* Update MS IOCB request */ ms_pkt->req_bytecount = @@ -687,7 +683,6 @@ qla2x00_rsnn_nn(scsi_qla_host_t *ha) return (rval); } - /** * qla2x00_prep_sns_cmd() - Prepare common SNS command request fields for query. * @ha: HA context @@ -1585,6 +1580,21 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *ha) DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, ha->host_no, eiter->a.os_dev_name)); + /* Hostname. */ + if (strlen(fc_host_system_hostname(ha->host))) { + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_HOST_NAME); + snprintf(eiter->a.host_name, sizeof(eiter->a.host_name), + "%s", fc_host_system_hostname(ha->host)); + alen = strlen(eiter->a.host_name); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__, + ha->host_no, eiter->a.host_name)); + } + /* Update MS request size. */ qla2x00_update_ms_fdmi_iocb(ha, size + 16); @@ -1647,3 +1657,189 @@ qla2x00_fdmi_register(scsi_qla_host_t *ha) return rval; } + +/** + * qla2x00_gfpn_id() - SNS Get Fabric Port Name (GFPN_ID) query. + * @ha: HA context + * @list: switch info entries to populate + * + * Returns 0 on success. + */ +int +qla2x00_gfpn_id(scsi_qla_host_t *ha, sw_info_t *list) +{ + int rval; + uint16_t i; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + + if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + return QLA_FUNCTION_FAILED; + + for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + /* Issue GFPN_ID */ + memset(list[i].fabric_port_name, 0, WWN_SIZE); + + /* Prepare common MS IOCB */ + ms_pkt = qla2x00_prep_ms_iocb(ha, GFPN_ID_REQ_SIZE, + GFPN_ID_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFPN_ID_CMD, + GFPN_ID_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare CT arguments -- port_id */ + ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain; + ct_req->req.port_id.port_id[1] = list[i].d_id.b.area; + ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa; + + /* Execute MS IOCB */ + rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + sizeof(ms_iocb_entry_t)); + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB " + "failed (%d).\n", ha->host_no, rval)); + } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + "GFPN_ID") != QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + } else { + /* Save fabric portname */ + memcpy(list[i].fabric_port_name, + ct_rsp->rsp.gfpn_id.port_name, WWN_SIZE); + } + + /* Last device exit. */ + if (list[i].d_id.b.rsvd_1 != 0) + break; + } + + return (rval); +} + +static inline void * +qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *ha, uint32_t req_size, + uint32_t rsp_size) +{ + struct ct_entry_24xx *ct_pkt; + + ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; + memset(ct_pkt, 0, sizeof(struct ct_entry_24xx)); + + ct_pkt->entry_type = CT_IOCB_TYPE; + ct_pkt->entry_count = 1; + ct_pkt->nport_handle = cpu_to_le16(ha->mgmt_svr_loop_id); + ct_pkt->timeout = __constant_cpu_to_le16(59); + ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); + ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1); + ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size); + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + + ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); + ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; + + ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); + ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; + + return ct_pkt; +} + + +static inline struct ct_sns_req * +qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd, + uint16_t rsp_size) +{ + memset(ct_req, 0, sizeof(struct ct_sns_pkt)); + + ct_req->header.revision = 0x01; + ct_req->header.gs_type = 0xFA; + ct_req->header.gs_subtype = 0x01; + ct_req->command = cpu_to_be16(cmd); + ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4); + + return ct_req; +} + +/** + * qla2x00_gpsc() - FCS Get Port Speed Capabilities (GPSC) query. + * @ha: HA context + * @list: switch info entries to populate + * + * Returns 0 on success. + */ +int +qla2x00_gpsc(scsi_qla_host_t *ha, sw_info_t *list) +{ + int rval; + uint16_t i; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + + if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha)) + return QLA_FUNCTION_FAILED; + + rval = qla2x00_mgmt_svr_login(ha); + if (rval) + return rval; + + for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + /* Issue GFPN_ID */ + list[i].fp_speeds = list[i].fp_speed = 0; + + /* Prepare common MS IOCB */ + ms_pkt = qla24xx_prep_ms_fm_iocb(ha, GPSC_REQ_SIZE, + GPSC_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla24xx_prep_ct_fm_req(&ha->ct_sns->p.req, + GPSC_CMD, GPSC_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare CT arguments -- port_name */ + memcpy(ct_req->req.gpsc.port_name, list[i].fabric_port_name, + WWN_SIZE); + + /* Execute MS IOCB */ + rval = qla2x00_issue_iocb(ha, ha->ms_iocb, ha->ms_iocb_dma, + sizeof(ms_iocb_entry_t)); + if (rval != QLA_SUCCESS) { + /*EMPTY*/ + DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB " + "failed (%d).\n", ha->host_no, rval)); + } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, + "GPSC") != QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + } else { + /* Save portname */ + list[i].fp_speeds = ct_rsp->rsp.gpsc.speeds; + list[i].fp_speed = ct_rsp->rsp.gpsc.speed; + + DEBUG2_3(printk("scsi(%ld): GPSC ext entry - " + "fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x " + "speed=%04x.\n", ha->host_no, + list[i].fabric_port_name[0], + list[i].fabric_port_name[1], + list[i].fabric_port_name[2], + list[i].fabric_port_name[3], + list[i].fabric_port_name[4], + list[i].fabric_port_name[5], + list[i].fabric_port_name[6], + list[i].fabric_port_name[7], + be16_to_cpu(list[i].fp_speeds), + be16_to_cpu(list[i].fp_speed))); + } + + /* Last device exit. */ + if (list[i].d_id.b.rsvd_1 != 0) + break; + } + + return (rval); +} diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 859649160caa..d5d26273c04e 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2074,6 +2074,19 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) new_fcport->flags &= ~FCF_FABRIC_DEVICE; } + /* Base iIDMA settings on HBA port speed. */ + switch (ha->link_data_rate) { + case PORT_SPEED_1GB: + fcport->fp_speed = cpu_to_be16(BIT_15); + break; + case PORT_SPEED_2GB: + fcport->fp_speed = cpu_to_be16(BIT_14); + break; + case PORT_SPEED_4GB: + fcport->fp_speed = cpu_to_be16(BIT_13); + break; + } + qla2x00_update_fcport(ha, fcport); found_devs++; @@ -2109,6 +2122,62 @@ qla2x00_probe_for_all_luns(scsi_qla_host_t *ha) } } +static void +qla2x00_iidma_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) +{ +#define LS_UNKNOWN 2 + static char *link_speeds[5] = { "1", "2", "?", "4" }; + int rval; + uint16_t port_speed, mb[6]; + + if (!IS_QLA24XX(ha)) + return; + + switch (be16_to_cpu(fcport->fp_speed)) { + case BIT_15: + port_speed = PORT_SPEED_1GB; + break; + case BIT_14: + port_speed = PORT_SPEED_2GB; + break; + case BIT_13: + port_speed = PORT_SPEED_4GB; + break; + default: + DEBUG2(printk("scsi(%ld): %02x%02x%02x%02x%02x%02x%02x%02x -- " + "unsupported FM port operating speed (%04x).\n", + ha->host_no, fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], + be16_to_cpu(fcport->fp_speed))); + port_speed = PORT_SPEED_UNKNOWN; + break; + } + if (port_speed == PORT_SPEED_UNKNOWN) + return; + + rval = qla2x00_set_idma_speed(ha, fcport->loop_id, port_speed, mb); + if (rval != QLA_SUCCESS) { + DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA " + "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n", + ha->host_no, fcport->port_name[0], fcport->port_name[1], + fcport->port_name[2], fcport->port_name[3], + fcport->port_name[4], fcport->port_name[5], + fcport->port_name[6], fcport->port_name[7], rval, + port_speed, mb[0], mb[1])); + } else { + DEBUG2(qla_printk(KERN_INFO, ha, + "iIDMA adjusted to %s GB/s on " + "%02x%02x%02x%02x%02x%02x%02x%02x.\n", + link_speeds[port_speed], fcport->port_name[0], + fcport->port_name[1], fcport->port_name[2], + fcport->port_name[3], fcport->port_name[4], + fcport->port_name[5], fcport->port_name[6], + fcport->port_name[7])); + } +} + /* * qla2x00_update_fcport * Updates device on list. @@ -2135,6 +2204,8 @@ qla2x00_update_fcport(scsi_qla_host_t *ha, fc_port_t *fcport) PORT_RETRY_TIME); fcport->flags &= ~FCF_LOGIN_NEEDED; + qla2x00_iidma_fcport(ha, fcport); + atomic_set(&fcport->state, FCS_ONLINE); if (ha->flags.init_done) @@ -2209,7 +2280,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) loop_id = NPH_F_PORT; else loop_id = SNS_FL_PORT; - rval = qla2x00_get_port_name(ha, loop_id, NULL, 0); + rval = qla2x00_get_port_name(ha, loop_id, ha->fabric_node_name, 1); if (rval != QLA_SUCCESS) { DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL " "Port\n", ha->host_no)); @@ -2217,6 +2288,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) ha->device_flags &= ~SWITCH_FOUND; return (QLA_SUCCESS); } + ha->device_flags |= SWITCH_FOUND; /* Mark devices that need re-synchronization. */ rval2 = qla2x00_device_resync(ha); @@ -2416,6 +2488,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) } else if (qla2x00_gnn_id(ha, swl) != QLA_SUCCESS) { kfree(swl); swl = NULL; + } else if (qla2x00_gfpn_id(ha, swl) == QLA_SUCCESS) { + qla2x00_gpsc(ha, swl); } } swl_idx = 0; @@ -2450,6 +2524,9 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) swl[swl_idx].node_name, WWN_SIZE); memcpy(new_fcport->port_name, swl[swl_idx].port_name, WWN_SIZE); + memcpy(new_fcport->fabric_port_name, + swl[swl_idx].fabric_port_name, WWN_SIZE); + new_fcport->fp_speed = swl[swl_idx].fp_speed; if (swl[swl_idx].d_id.b.rsvd_1 != 0) { last_dev = 1; @@ -2507,6 +2584,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) found++; + /* Update port state. */ + memcpy(fcport->fabric_port_name, + new_fcport->fabric_port_name, WWN_SIZE); + fcport->fp_speed = new_fcport->fp_speed; + /* * If address the same and state FCS_ONLINE, nothing * changed. diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index de0613135f70..5fa933cda992 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -400,7 +400,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) case MBA_LOOP_UP: /* Loop Up Event */ if (IS_QLA2100(ha) || IS_QLA2200(ha)) { link_speed = link_speeds[0]; - ha->link_data_rate = LDR_1GB; + ha->link_data_rate = PORT_SPEED_1GB; } else { link_speed = link_speeds[LS_UNKNOWN]; if (mb[1] < 5) @@ -429,7 +429,7 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) } ha->flags.management_server_logged_in = 0; - ha->link_data_rate = LDR_UNKNOWN; + ha->link_data_rate = PORT_SPEED_UNKNOWN; if (ql2xfdmienable) set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); break; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 879f281e2ea2..4cde76c85cb3 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -2540,3 +2540,89 @@ qla2x00_read_sfp(scsi_qla_host_t *ha, dma_addr_t sfp_dma, uint16_t addr, return rval; } + +int +qla2x00_get_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, + uint16_t *port_speed, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA24XX(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + mcp->mb[2] = mcp->mb[3] = mcp->mb[4] = mcp->mb[5] = 0; + mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + /* Return mailbox statuses. */ + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + mb[4] = mcp->mb[4]; + mb[5] = mcp->mb[5]; + } + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, + ha->host_no, rval)); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + if (port_speed) + *port_speed = mcp->mb[3]; + } + + return rval; +} + +int +qla2x00_set_idma_speed(scsi_qla_host_t *ha, uint16_t loop_id, + uint16_t port_speed, uint16_t *mb) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA24XX(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_PORT_PARAMS; + mcp->mb[1] = loop_id; + mcp->mb[2] = BIT_0; + mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); + mcp->mb[4] = mcp->mb[5] = 0; + mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = 30; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + /* Return mailbox statuses. */ + if (mb != NULL) { + mb[0] = mcp->mb[0]; + mb[1] = mcp->mb[1]; + mb[3] = mcp->mb[3]; + mb[4] = mcp->mb[4]; + mb[5] = mcp->mb[5]; + } + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, + ha->host_no, rval)); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 65cbe2f5eea2..3ba8c239f171 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -589,6 +589,23 @@ qla2x00_wait_for_loop_ready(scsi_qla_host_t *ha) return (return_status); } +static void +qla2x00_block_error_handler(struct scsi_cmnd *cmnd) +{ + struct Scsi_Host *shost = cmnd->device->host; + struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); + unsigned long flags; + + spin_lock_irqsave(shost->host_lock, flags); + while (rport->port_state == FC_PORTSTATE_BLOCKED) { + spin_unlock_irqrestore(shost->host_lock, flags); + msleep(1000); + spin_lock_irqsave(shost->host_lock, flags); + } + spin_unlock_irqrestore(shost->host_lock, flags); + return; +} + /************************************************************************** * qla2xxx_eh_abort * @@ -615,6 +632,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) unsigned long flags; int wait = 0; + qla2x00_block_error_handler(cmd); + if (!CMD_SP(cmd)) return SUCCESS; @@ -748,6 +767,8 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) unsigned int id, lun; unsigned long serial; + qla2x00_block_error_handler(cmd); + ret = FAILED; id = cmd->device->id; @@ -877,6 +898,8 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) unsigned int id, lun; unsigned long serial; + qla2x00_block_error_handler(cmd); + ret = FAILED; id = cmd->device->id; @@ -936,6 +959,8 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) unsigned int id, lun; unsigned long serial; + qla2x00_block_error_handler(cmd); + ret = FAILED; id = cmd->device->id; @@ -1385,7 +1410,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); ha->mgmt_svr_loop_id = MANAGEMENT_SERVER; - ha->link_data_rate = LDR_UNKNOWN; + ha->link_data_rate = PORT_SPEED_UNKNOWN; ha->optrom_size = OPTROM_SIZE_2300; /* Assign ISP specific operations. */ @@ -2564,14 +2589,20 @@ qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout) #define FW_ISP2322 3 #define FW_ISP24XX 4 +#define FW_FILE_ISP21XX "ql2100_fw.bin" +#define FW_FILE_ISP22XX "ql2200_fw.bin" +#define FW_FILE_ISP2300 "ql2300_fw.bin" +#define FW_FILE_ISP2322 "ql2322_fw.bin" +#define FW_FILE_ISP24XX "ql2400_fw.bin" + static DECLARE_MUTEX(qla_fw_lock); static struct fw_blob qla_fw_blobs[FW_BLOBS] = { - { .name = "ql2100_fw.bin", .segs = { 0x1000, 0 }, }, - { .name = "ql2200_fw.bin", .segs = { 0x1000, 0 }, }, - { .name = "ql2300_fw.bin", .segs = { 0x800, 0 }, }, - { .name = "ql2322_fw.bin", .segs = { 0x800, 0x1c000, 0x1e000, 0 }, }, - { .name = "ql2400_fw.bin", }, + { .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, }, + { .name = FW_FILE_ISP22XX, .segs = { 0x1000, 0 }, }, + { .name = FW_FILE_ISP2300, .segs = { 0x800, 0 }, }, + { .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, }, + { .name = FW_FILE_ISP24XX, }, }; struct fw_blob * @@ -2702,3 +2733,8 @@ MODULE_AUTHOR("QLogic Corporation"); MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(QLA2XXX_VERSION); +MODULE_FIRMWARE(FW_FILE_ISP21XX); +MODULE_FIRMWARE(FW_FILE_ISP22XX); +MODULE_FIRMWARE(FW_FILE_ISP2300); +MODULE_FIRMWARE(FW_FILE_ISP2322); +MODULE_FIRMWARE(FW_FILE_ISP24XX); diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 971259032ef7..e57bf45a3393 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.01.07-k1" +#define QLA2XXX_VERSION "8.01.07-k2" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 1 diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig new file mode 100644 index 000000000000..08a07f0b8d94 --- /dev/null +++ b/drivers/scsi/qla4xxx/Kconfig @@ -0,0 +1,7 @@ +config SCSI_QLA_ISCSI + tristate "QLogic ISP4XXX host adapter family support" + depends on PCI && SCSI + select SCSI_ISCSI_ATTRS + ---help--- + This driver supports the QLogic 40xx (ISP4XXX) iSCSI host + adapter family. diff --git a/drivers/scsi/qla4xxx/Makefile b/drivers/scsi/qla4xxx/Makefile new file mode 100644 index 000000000000..86ea37baa0fc --- /dev/null +++ b/drivers/scsi/qla4xxx/Makefile @@ -0,0 +1,5 @@ +qla4xxx-y := ql4_os.o ql4_init.o ql4_mbx.o ql4_iocb.o ql4_isr.o \ + ql4_nvram.o ql4_dbg.o + +obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx.o + diff --git a/drivers/scsi/qla4xxx/ql4_dbg.c b/drivers/scsi/qla4xxx/ql4_dbg.c new file mode 100644 index 000000000000..752031fadfef --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_dbg.c @@ -0,0 +1,197 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" +#include <scsi/scsi_dbg.h> + +static void qla4xxx_print_srb_info(struct srb * srb) +{ + printk("%s: srb = 0x%p, flags=0x%02x\n", __func__, srb, srb->flags); + printk("%s: cmd = 0x%p, saved_dma_handle = 0x%lx\n", + __func__, srb->cmd, (unsigned long) srb->dma_handle); + printk("%s: fw_ddb_index = %d, lun = %d\n", + __func__, srb->fw_ddb_index, srb->cmd->device->lun); + printk("%s: iocb_tov = %d\n", + __func__, srb->iocb_tov); + printk("%s: cc_stat = 0x%x, r_start = 0x%lx, u_start = 0x%lx\n\n", + __func__, srb->cc_stat, srb->r_start, srb->u_start); +} + +void qla4xxx_print_scsi_cmd(struct scsi_cmnd *cmd) +{ + printk("SCSI Command = 0x%p, Handle=0x%p\n", cmd, cmd->host_scribble); + printk(" b=%d, t=%02xh, l=%02xh, cmd_len = %02xh\n", + cmd->device->channel, cmd->device->id, cmd->device->lun, + cmd->cmd_len); + scsi_print_command(cmd); + printk(" seg_cnt = %d\n", cmd->use_sg); + printk(" request buffer = 0x%p, request buffer len = 0x%x\n", + cmd->request_buffer, cmd->request_bufflen); + if (cmd->use_sg) { + struct scatterlist *sg; + sg = (struct scatterlist *)cmd->request_buffer; + printk(" SG buffer: \n"); + qla4xxx_dump_buffer((caddr_t) sg, + (cmd->use_sg * sizeof(*sg))); + } + printk(" tag = %d, transfersize = 0x%x \n", cmd->tag, + cmd->transfersize); + printk(" Pid = %d, SP = 0x%p\n", (int)cmd->pid, cmd->SCp.ptr); + printk(" underflow size = 0x%x, direction=0x%x\n", cmd->underflow, + cmd->sc_data_direction); + printk(" Current time (jiffies) = 0x%lx, " + "timeout expires = 0x%lx\n", jiffies, cmd->eh_timeout.expires); + qla4xxx_print_srb_info((struct srb *) cmd->SCp.ptr); +} + +void __dump_registers(struct scsi_qla_host *ha) +{ + uint8_t i; + for (i = 0; i < MBOX_REG_COUNT; i++) { + printk(KERN_INFO "0x%02X mailbox[%d] = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, mailbox[i]), i, + readw(&ha->reg->mailbox[i])); + } + printk(KERN_INFO "0x%02X flash_address = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, flash_address), + readw(&ha->reg->flash_address)); + printk(KERN_INFO "0x%02X flash_data = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, flash_data), + readw(&ha->reg->flash_data)); + printk(KERN_INFO "0x%02X ctrl_status = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, ctrl_status), + readw(&ha->reg->ctrl_status)); + if (is_qla4010(ha)) { + printk(KERN_INFO "0x%02X nvram = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, u1.isp4010.nvram), + readw(&ha->reg->u1.isp4010.nvram)); + } + + else if (is_qla4022(ha)) { + printk(KERN_INFO "0x%02X intr_mask = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u1.isp4022.intr_mask), + readw(&ha->reg->u1.isp4022.intr_mask)); + printk(KERN_INFO "0x%02X nvram = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, u1.isp4022.nvram), + readw(&ha->reg->u1.isp4022.nvram)); + printk(KERN_INFO "0x%02X semaphore = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u1.isp4022.semaphore), + readw(&ha->reg->u1.isp4022.semaphore)); + } + printk(KERN_INFO "0x%02X req_q_in = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, req_q_in), + readw(&ha->reg->req_q_in)); + printk(KERN_INFO "0x%02X rsp_q_out = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, rsp_q_out), + readw(&ha->reg->rsp_q_out)); + if (is_qla4010(ha)) { + printk(KERN_INFO "0x%02X ext_hw_conf = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4010.ext_hw_conf), + readw(&ha->reg->u2.isp4010.ext_hw_conf)); + printk(KERN_INFO "0x%02X port_ctrl = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4010.port_ctrl), + readw(&ha->reg->u2.isp4010.port_ctrl)); + printk(KERN_INFO "0x%02X port_status = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4010.port_status), + readw(&ha->reg->u2.isp4010.port_status)); + printk(KERN_INFO "0x%02X req_q_out = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4010.req_q_out), + readw(&ha->reg->u2.isp4010.req_q_out)); + printk(KERN_INFO "0x%02X gp_out = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, u2.isp4010.gp_out), + readw(&ha->reg->u2.isp4010.gp_out)); + printk(KERN_INFO "0x%02X gp_in = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, u2.isp4010.gp_in), + readw(&ha->reg->u2.isp4010.gp_in)); + printk(KERN_INFO "0x%02X port_err_status = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4010.port_err_status), + readw(&ha->reg->u2.isp4010.port_err_status)); + } + + else if (is_qla4022(ha)) { + printk(KERN_INFO "Page 0 Registers:\n"); + printk(KERN_INFO "0x%02X ext_hw_conf = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p0.ext_hw_conf), + readw(&ha->reg->u2.isp4022.p0.ext_hw_conf)); + printk(KERN_INFO "0x%02X port_ctrl = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p0.port_ctrl), + readw(&ha->reg->u2.isp4022.p0.port_ctrl)); + printk(KERN_INFO "0x%02X port_status = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p0.port_status), + readw(&ha->reg->u2.isp4022.p0.port_status)); + printk(KERN_INFO "0x%02X gp_out = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p0.gp_out), + readw(&ha->reg->u2.isp4022.p0.gp_out)); + printk(KERN_INFO "0x%02X gp_in = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, u2.isp4022.p0.gp_in), + readw(&ha->reg->u2.isp4022.p0.gp_in)); + printk(KERN_INFO "0x%02X port_err_status = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p0.port_err_status), + readw(&ha->reg->u2.isp4022.p0.port_err_status)); + printk(KERN_INFO "Page 1 Registers:\n"); + writel(HOST_MEM_CFG_PAGE & set_rmask(CSR_SCSI_PAGE_SELECT), + &ha->reg->ctrl_status); + printk(KERN_INFO "0x%02X req_q_out = 0x%08X\n", + (uint8_t) offsetof(struct isp_reg, + u2.isp4022.p1.req_q_out), + readw(&ha->reg->u2.isp4022.p1.req_q_out)); + writel(PORT_CTRL_STAT_PAGE & set_rmask(CSR_SCSI_PAGE_SELECT), + &ha->reg->ctrl_status); + } +} + +void qla4xxx_dump_mbox_registers(struct scsi_qla_host *ha) +{ + unsigned long flags = 0; + int i = 0; + spin_lock_irqsave(&ha->hardware_lock, flags); + for (i = 1; i < MBOX_REG_COUNT; i++) + printk(KERN_INFO " Mailbox[%d] = %08x\n", i, + readw(&ha->reg->mailbox[i])); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void qla4xxx_dump_registers(struct scsi_qla_host *ha) +{ + unsigned long flags = 0; + spin_lock_irqsave(&ha->hardware_lock, flags); + __dump_registers(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void qla4xxx_dump_buffer(void *b, uint32_t size) +{ + uint32_t cnt; + uint8_t *c = b; + + printk(" 0 1 2 3 4 5 6 7 8 9 Ah Bh Ch Dh Eh " + "Fh\n"); + printk("------------------------------------------------------------" + "--\n"); + for (cnt = 0; cnt < size; cnt++, c++) { + printk(KERN_DEBUG "%02x", *c); + if (!(cnt % 16)) + printk(KERN_DEBUG "\n"); + + else + printk(KERN_DEBUG " "); + } + if (cnt % 16) + printk(KERN_DEBUG "\n"); +} diff --git a/drivers/scsi/qla4xxx/ql4_dbg.h b/drivers/scsi/qla4xxx/ql4_dbg.h new file mode 100644 index 000000000000..56ddc227f846 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_dbg.h @@ -0,0 +1,55 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +/* + * Driver debug definitions. + */ +/* #define QL_DEBUG */ /* DEBUG messages */ +/* #define QL_DEBUG_LEVEL_3 */ /* Output function tracing */ +/* #define QL_DEBUG_LEVEL_4 */ +/* #define QL_DEBUG_LEVEL_5 */ +/* #define QL_DEBUG_LEVEL_9 */ + +#define QL_DEBUG_LEVEL_2 /* ALways enable error messagess */ +#if defined(QL_DEBUG) +#define DEBUG(x) do {x;} while (0); +#else +#define DEBUG(x) do {} while (0); +#endif + +#if defined(QL_DEBUG_LEVEL_2) +#define DEBUG2(x) do {if(extended_error_logging == 2) x;} while (0); +#define DEBUG2_3(x) do {x;} while (0); +#else /* */ +#define DEBUG2(x) do {} while (0); +#endif /* */ + +#if defined(QL_DEBUG_LEVEL_3) +#define DEBUG3(x) do {if(extended_error_logging == 3) x;} while (0); +#else /* */ +#define DEBUG3(x) do {} while (0); +#if !defined(QL_DEBUG_LEVEL_2) +#define DEBUG2_3(x) do {} while (0); +#endif /* */ +#endif /* */ +#if defined(QL_DEBUG_LEVEL_4) +#define DEBUG4(x) do {x;} while (0); +#else /* */ +#define DEBUG4(x) do {} while (0); +#endif /* */ + +#if defined(QL_DEBUG_LEVEL_5) +#define DEBUG5(x) do {x;} while (0); +#else /* */ +#define DEBUG5(x) do {} while (0); +#endif /* */ + +#if defined(QL_DEBUG_LEVEL_9) +#define DEBUG9(x) do {x;} while (0); +#else /* */ +#define DEBUG9(x) do {} while (0); +#endif /* */ diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h new file mode 100644 index 000000000000..a7f6c7b1c590 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_def.h @@ -0,0 +1,586 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#ifndef __QL4_DEF_H +#define __QL4_DEF_H + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/dmapool.h> +#include <linux/mempool.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mutex.h> + +#include <net/tcp.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_transport_iscsi.h> + + +#ifndef PCI_DEVICE_ID_QLOGIC_ISP4010 +#define PCI_DEVICE_ID_QLOGIC_ISP4010 0x4010 +#endif + +#ifndef PCI_DEVICE_ID_QLOGIC_ISP4022 +#define PCI_DEVICE_ID_QLOGIC_ISP4022 0x4022 +#endif /* */ + +#define QLA_SUCCESS 0 +#define QLA_ERROR 1 + +/* + * Data bit definitions + */ +#define BIT_0 0x1 +#define BIT_1 0x2 +#define BIT_2 0x4 +#define BIT_3 0x8 +#define BIT_4 0x10 +#define BIT_5 0x20 +#define BIT_6 0x40 +#define BIT_7 0x80 +#define BIT_8 0x100 +#define BIT_9 0x200 +#define BIT_10 0x400 +#define BIT_11 0x800 +#define BIT_12 0x1000 +#define BIT_13 0x2000 +#define BIT_14 0x4000 +#define BIT_15 0x8000 +#define BIT_16 0x10000 +#define BIT_17 0x20000 +#define BIT_18 0x40000 +#define BIT_19 0x80000 +#define BIT_20 0x100000 +#define BIT_21 0x200000 +#define BIT_22 0x400000 +#define BIT_23 0x800000 +#define BIT_24 0x1000000 +#define BIT_25 0x2000000 +#define BIT_26 0x4000000 +#define BIT_27 0x8000000 +#define BIT_28 0x10000000 +#define BIT_29 0x20000000 +#define BIT_30 0x40000000 +#define BIT_31 0x80000000 + +/* + * Host adapter default definitions + ***********************************/ +#define MAX_HBAS 16 +#define MAX_BUSES 1 +#define MAX_TARGETS (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) +#define MAX_LUNS 0xffff +#define MAX_AEN_ENTRIES 256 /* should be > EXT_DEF_MAX_AEN_QUEUE */ +#define MAX_DDB_ENTRIES (MAX_PRST_DEV_DB_ENTRIES + MAX_DEV_DB_ENTRIES) +#define MAX_PDU_ENTRIES 32 +#define INVALID_ENTRY 0xFFFF +#define MAX_CMDS_TO_RISC 1024 +#define MAX_SRBS MAX_CMDS_TO_RISC +#define MBOX_AEN_REG_COUNT 5 +#define MAX_INIT_RETRIES 5 +#define IOCB_HIWAT_CUSHION 16 + +/* + * Buffer sizes + */ +#define REQUEST_QUEUE_DEPTH MAX_CMDS_TO_RISC +#define RESPONSE_QUEUE_DEPTH 64 +#define QUEUE_SIZE 64 +#define DMA_BUFFER_SIZE 512 + +/* + * Misc + */ +#define MAC_ADDR_LEN 6 /* in bytes */ +#define IP_ADDR_LEN 4 /* in bytes */ +#define DRIVER_NAME "qla4xxx" + +#define MAX_LINKED_CMDS_PER_LUN 3 +#define MAX_REQS_SERVICED_PER_INTR 16 + +#define ISCSI_IPADDR_SIZE 4 /* IP address size */ +#define ISCSI_ALIAS_SIZE 32 /* ISCSI Alais name size */ +#define ISCSI_NAME_SIZE 255 /* ISCSI Name size - + * usually a string */ + +#define LSDW(x) ((u32)((u64)(x))) +#define MSDW(x) ((u32)((((u64)(x)) >> 16) >> 16)) + +/* + * Retry & Timeout Values + */ +#define MBOX_TOV 60 +#define SOFT_RESET_TOV 30 +#define RESET_INTR_TOV 3 +#define SEMAPHORE_TOV 10 +#define ADAPTER_INIT_TOV 120 +#define ADAPTER_RESET_TOV 180 +#define EXTEND_CMD_TOV 60 +#define WAIT_CMD_TOV 30 +#define EH_WAIT_CMD_TOV 120 +#define FIRMWARE_UP_TOV 60 +#define RESET_FIRMWARE_TOV 30 +#define LOGOUT_TOV 10 +#define IOCB_TOV_MARGIN 10 +#define RELOGIN_TOV 18 +#define ISNS_DEREG_TOV 5 + +#define MAX_RESET_HA_RETRIES 2 + +/* + * SCSI Request Block structure (srb) that is placed + * on cmd->SCp location of every I/O [We have 22 bytes available] + */ +struct srb { + struct list_head list; /* (8) */ + struct scsi_qla_host *ha; /* HA the SP is queued on */ + struct ddb_entry *ddb; + uint16_t flags; /* (1) Status flags. */ + +#define SRB_DMA_VALID BIT_3 /* DMA Buffer mapped. */ +#define SRB_GOT_SENSE BIT_4 /* sense data recieved. */ + uint8_t state; /* (1) Status flags. */ + +#define SRB_NO_QUEUE_STATE 0 /* Request is in between states */ +#define SRB_FREE_STATE 1 +#define SRB_ACTIVE_STATE 3 +#define SRB_ACTIVE_TIMEOUT_STATE 4 +#define SRB_SUSPENDED_STATE 7 /* Request in suspended state */ + + struct scsi_cmnd *cmd; /* (4) SCSI command block */ + dma_addr_t dma_handle; /* (4) for unmap of single transfers */ + atomic_t ref_count; /* reference count for this srb */ + uint32_t fw_ddb_index; + uint8_t err_id; /* error id */ +#define SRB_ERR_PORT 1 /* Request failed because "port down" */ +#define SRB_ERR_LOOP 2 /* Request failed because "loop down" */ +#define SRB_ERR_DEVICE 3 /* Request failed because "device error" */ +#define SRB_ERR_OTHER 4 + + uint16_t reserved; + uint16_t iocb_tov; + uint16_t iocb_cnt; /* Number of used iocbs */ + uint16_t cc_stat; + u_long r_start; /* Time we recieve a cmd from OS */ + u_long u_start; /* Time when we handed the cmd to F/W */ +}; + + /* + * Device Database (DDB) structure + */ +struct ddb_entry { + struct list_head list; /* ddb list */ + struct scsi_qla_host *ha; + struct iscsi_cls_session *sess; + struct iscsi_cls_conn *conn; + + atomic_t state; /* DDB State */ + + unsigned long flags; /* DDB Flags */ + + unsigned long dev_scan_wait_to_start_relogin; + unsigned long dev_scan_wait_to_complete_relogin; + + uint16_t os_target_id; /* Target ID */ + uint16_t fw_ddb_index; /* DDB firmware index */ + uint8_t reserved[2]; + uint32_t fw_ddb_device_state; /* F/W Device State -- see ql4_fw.h */ + + uint32_t CmdSn; + uint16_t target_session_id; + uint16_t connection_id; + uint16_t exe_throttle; /* Max mumber of cmds outstanding + * simultaneously */ + uint16_t task_mgmt_timeout; /* Min time for task mgmt cmds to + * complete */ + uint16_t default_relogin_timeout; /* Max time to wait for + * relogin to complete */ + uint16_t tcp_source_port_num; + uint32_t default_time2wait; /* Default Min time between + * relogins (+aens) */ + + atomic_t port_down_timer; /* Device connection timer */ + atomic_t retry_relogin_timer; /* Min Time between relogins + * (4000 only) */ + atomic_t relogin_timer; /* Max Time to wait for relogin to complete */ + atomic_t relogin_retry_count; /* Num of times relogin has been + * retried */ + + uint16_t port; + uint32_t tpgt; + uint8_t ip_addr[ISCSI_IPADDR_SIZE]; + uint8_t iscsi_name[ISCSI_NAME_SIZE]; /* 72 x48 */ + uint8_t iscsi_alias[0x20]; +}; + +/* + * DDB states. + */ +#define DDB_STATE_DEAD 0 /* We can no longer talk to + * this device */ +#define DDB_STATE_ONLINE 1 /* Device ready to accept + * commands */ +#define DDB_STATE_MISSING 2 /* Device logged off, trying + * to re-login */ + +/* + * DDB flags. + */ +#define DF_RELOGIN 0 /* Relogin to device */ +#define DF_NO_RELOGIN 1 /* Do not relogin if IOCTL + * logged it out */ +#define DF_ISNS_DISCOVERED 2 /* Device was discovered via iSNS */ +#define DF_FO_MASKED 3 + +/* + * Asynchronous Event Queue structure + */ +struct aen { + uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; +}; + + +#include "ql4_fw.h" +#include "ql4_nvram.h" + +/* + * Linux Host Adapter structure + */ +struct scsi_qla_host { + /* Linux adapter configuration data */ + struct Scsi_Host *host; /* pointer to host data */ + uint32_t tot_ddbs; + unsigned long flags; + +#define AF_ONLINE 0 /* 0x00000001 */ +#define AF_INIT_DONE 1 /* 0x00000002 */ +#define AF_MBOX_COMMAND 2 /* 0x00000004 */ +#define AF_MBOX_COMMAND_DONE 3 /* 0x00000008 */ +#define AF_INTERRUPTS_ON 6 /* 0x00000040 Not Used */ +#define AF_GET_CRASH_RECORD 7 /* 0x00000080 */ +#define AF_LINK_UP 8 /* 0x00000100 */ +#define AF_TOPCAT_CHIP_PRESENT 9 /* 0x00000200 */ +#define AF_IRQ_ATTACHED 10 /* 0x00000400 */ +#define AF_ISNS_CMD_IN_PROCESS 12 /* 0x00001000 */ +#define AF_ISNS_CMD_DONE 13 /* 0x00002000 */ + + unsigned long dpc_flags; + +#define DPC_RESET_HA 1 /* 0x00000002 */ +#define DPC_RETRY_RESET_HA 2 /* 0x00000004 */ +#define DPC_RELOGIN_DEVICE 3 /* 0x00000008 */ +#define DPC_RESET_HA_DESTROY_DDB_LIST 4 /* 0x00000010 */ +#define DPC_RESET_HA_INTR 5 /* 0x00000020 */ +#define DPC_ISNS_RESTART 7 /* 0x00000080 */ +#define DPC_AEN 9 /* 0x00000200 */ +#define DPC_GET_DHCP_IP_ADDR 15 /* 0x00008000 */ + + uint16_t iocb_cnt; + uint16_t iocb_hiwat; + + /* SRB cache. */ +#define SRB_MIN_REQ 128 + mempool_t *srb_mempool; + + /* pci information */ + struct pci_dev *pdev; + + struct isp_reg __iomem *reg; /* Base I/O address */ + unsigned long pio_address; + unsigned long pio_length; +#define MIN_IOBASE_LEN 0x100 + + uint16_t req_q_count; + uint8_t marker_needed; + uint8_t rsvd1; + + unsigned long host_no; + + /* NVRAM registers */ + struct eeprom_data *nvram; + spinlock_t hardware_lock ____cacheline_aligned; + spinlock_t list_lock; + uint32_t eeprom_cmd_data; + + /* Counters for general statistics */ + uint64_t adapter_error_count; + uint64_t device_error_count; + uint64_t total_io_count; + uint64_t total_mbytes_xferred; + uint64_t link_failure_count; + uint64_t invalid_crc_count; + uint32_t spurious_int_count; + uint32_t aborted_io_count; + uint32_t io_timeout_count; + uint32_t mailbox_timeout_count; + uint32_t seconds_since_last_intr; + uint32_t seconds_since_last_heartbeat; + uint32_t mac_index; + + /* Info Needed for Management App */ + /* --- From GetFwVersion --- */ + uint32_t firmware_version[2]; + uint32_t patch_number; + uint32_t build_number; + + /* --- From Init_FW --- */ + /* init_cb_t *init_cb; */ + uint16_t firmware_options; + uint16_t tcp_options; + uint8_t ip_address[IP_ADDR_LEN]; + uint8_t subnet_mask[IP_ADDR_LEN]; + uint8_t gateway[IP_ADDR_LEN]; + uint8_t alias[32]; + uint8_t name_string[256]; + uint8_t heartbeat_interval; + uint8_t rsvd; + + /* --- From FlashSysInfo --- */ + uint8_t my_mac[MAC_ADDR_LEN]; + uint8_t serial_number[16]; + + /* --- From GetFwState --- */ + uint32_t firmware_state; + uint32_t board_id; + uint32_t addl_fw_state; + + /* Linux kernel thread */ + struct workqueue_struct *dpc_thread; + struct work_struct dpc_work; + + /* Linux timer thread */ + struct timer_list timer; + uint32_t timer_active; + + /* Recovery Timers */ + uint32_t port_down_retry_count; + uint32_t discovery_wait; + atomic_t check_relogin_timeouts; + uint32_t retry_reset_ha_cnt; + uint32_t isp_reset_timer; /* reset test timer */ + uint32_t nic_reset_timer; /* simulated nic reset test timer */ + int eh_start; + struct list_head free_srb_q; + uint16_t free_srb_q_count; + uint16_t num_srbs_allocated; + + /* DMA Memory Block */ + void *queues; + dma_addr_t queues_dma; + unsigned long queues_len; + +#define MEM_ALIGN_VALUE \ + ((max(REQUEST_QUEUE_DEPTH, RESPONSE_QUEUE_DEPTH)) * \ + sizeof(struct queue_entry)) + /* request and response queue variables */ + dma_addr_t request_dma; + struct queue_entry *request_ring; + struct queue_entry *request_ptr; + dma_addr_t response_dma; + struct queue_entry *response_ring; + struct queue_entry *response_ptr; + dma_addr_t shadow_regs_dma; + struct shadow_regs *shadow_regs; + uint16_t request_in; /* Current indexes. */ + uint16_t request_out; + uint16_t response_in; + uint16_t response_out; + + /* aen queue variables */ + uint16_t aen_q_count; /* Number of available aen_q entries */ + uint16_t aen_in; /* Current indexes */ + uint16_t aen_out; + struct aen aen_q[MAX_AEN_ENTRIES]; + + /* This mutex protects several threads to do mailbox commands + * concurrently. + */ + struct mutex mbox_sem; + wait_queue_head_t mailbox_wait_queue; + + /* temporary mailbox status registers */ + volatile uint8_t mbox_status_count; + volatile uint32_t mbox_status[MBOX_REG_COUNT]; + + /* local device database list (contains internal ddb entries) */ + struct list_head ddb_list; + + /* Map ddb_list entry by FW ddb index */ + struct ddb_entry *fw_ddb_index_map[MAX_DDB_ENTRIES]; + +}; + +static inline int is_qla4010(struct scsi_qla_host *ha) +{ + return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4010; +} + +static inline int is_qla4022(struct scsi_qla_host *ha) +{ + return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP4022; +} + +static inline int adapter_up(struct scsi_qla_host *ha) +{ + return (test_bit(AF_ONLINE, &ha->flags) != 0) && + (test_bit(AF_LINK_UP, &ha->flags) != 0); +} + +static inline struct scsi_qla_host* to_qla_host(struct Scsi_Host *shost) +{ + return (struct scsi_qla_host *)shost->hostdata; +} + +static inline void __iomem* isp_semaphore(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u1.isp4022.semaphore : + &ha->reg->u1.isp4010.nvram); +} + +static inline void __iomem* isp_nvram(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u1.isp4022.nvram : + &ha->reg->u1.isp4010.nvram); +} + +static inline void __iomem* isp_ext_hw_conf(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u2.isp4022.p0.ext_hw_conf : + &ha->reg->u2.isp4010.ext_hw_conf); +} + +static inline void __iomem* isp_port_status(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u2.isp4022.p0.port_status : + &ha->reg->u2.isp4010.port_status); +} + +static inline void __iomem* isp_port_ctrl(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u2.isp4022.p0.port_ctrl : + &ha->reg->u2.isp4010.port_ctrl); +} + +static inline void __iomem* isp_port_error_status(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u2.isp4022.p0.port_err_status : + &ha->reg->u2.isp4010.port_err_status); +} + +static inline void __iomem * isp_gp_out(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + &ha->reg->u2.isp4022.p0.gp_out : + &ha->reg->u2.isp4010.gp_out); +} + +static inline int eeprom_ext_hw_conf_offset(struct scsi_qla_host *ha) +{ + return (is_qla4022(ha) ? + offsetof(struct eeprom_data, isp4022.ext_hw_conf) / 2 : + offsetof(struct eeprom_data, isp4010.ext_hw_conf) / 2); +} + +int ql4xxx_sem_spinlock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits); +void ql4xxx_sem_unlock(struct scsi_qla_host * ha, u32 sem_mask); +int ql4xxx_sem_lock(struct scsi_qla_host * ha, u32 sem_mask, u32 sem_bits); + +static inline int ql4xxx_lock_flash(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + return ql4xxx_sem_spinlock(a, QL4022_FLASH_SEM_MASK, + (QL4022_RESOURCE_BITS_BASE_CODE | + (a->mac_index)) << 13); + else + return ql4xxx_sem_spinlock(a, QL4010_FLASH_SEM_MASK, + QL4010_FLASH_SEM_BITS); +} + +static inline void ql4xxx_unlock_flash(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + ql4xxx_sem_unlock(a, QL4022_FLASH_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4010_FLASH_SEM_MASK); +} + +static inline int ql4xxx_lock_nvram(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + return ql4xxx_sem_spinlock(a, QL4022_NVRAM_SEM_MASK, + (QL4022_RESOURCE_BITS_BASE_CODE | + (a->mac_index)) << 10); + else + return ql4xxx_sem_spinlock(a, QL4010_NVRAM_SEM_MASK, + QL4010_NVRAM_SEM_BITS); +} + +static inline void ql4xxx_unlock_nvram(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + ql4xxx_sem_unlock(a, QL4022_NVRAM_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4010_NVRAM_SEM_MASK); +} + +static inline int ql4xxx_lock_drvr(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + return ql4xxx_sem_lock(a, QL4022_DRVR_SEM_MASK, + (QL4022_RESOURCE_BITS_BASE_CODE | + (a->mac_index)) << 1); + else + return ql4xxx_sem_lock(a, QL4010_DRVR_SEM_MASK, + QL4010_DRVR_SEM_BITS); +} + +static inline void ql4xxx_unlock_drvr(struct scsi_qla_host *a) +{ + if (is_qla4022(a)) + ql4xxx_sem_unlock(a, QL4022_DRVR_SEM_MASK); + else + ql4xxx_sem_unlock(a, QL4010_DRVR_SEM_MASK); +} + +/*---------------------------------------------------------------------------*/ + +/* Defines for qla4xxx_initialize_adapter() and qla4xxx_recover_adapter() */ +#define PRESERVE_DDB_LIST 0 +#define REBUILD_DDB_LIST 1 + +/* Defines for process_aen() */ +#define PROCESS_ALL_AENS 0 +#define FLUSH_DDB_CHANGED_AENS 1 +#define RELOGIN_DDB_CHANGED_AENS 2 + +#include "ql4_version.h" +#include "ql4_glbl.h" +#include "ql4_dbg.h" +#include "ql4_inline.h" + + +#endif /*_QLA4XXX_H */ diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h new file mode 100644 index 000000000000..427489de64bc --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_fw.h @@ -0,0 +1,843 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#ifndef _QLA4X_FW_H +#define _QLA4X_FW_H + + +#define MAX_PRST_DEV_DB_ENTRIES 64 +#define MIN_DISC_DEV_DB_ENTRY MAX_PRST_DEV_DB_ENTRIES +#define MAX_DEV_DB_ENTRIES 512 + +/************************************************************************* + * + * ISP 4010 I/O Register Set Structure and Definitions + * + *************************************************************************/ + +struct port_ctrl_stat_regs { + __le32 ext_hw_conf; /* 80 x50 R/W */ + __le32 intChipConfiguration; /* 84 x54 */ + __le32 port_ctrl; /* 88 x58 */ + __le32 port_status; /* 92 x5c */ + __le32 HostPrimMACHi; /* 96 x60 */ + __le32 HostPrimMACLow; /* 100 x64 */ + __le32 HostSecMACHi; /* 104 x68 */ + __le32 HostSecMACLow; /* 108 x6c */ + __le32 EPPrimMACHi; /* 112 x70 */ + __le32 EPPrimMACLow; /* 116 x74 */ + __le32 EPSecMACHi; /* 120 x78 */ + __le32 EPSecMACLow; /* 124 x7c */ + __le32 HostPrimIPHi; /* 128 x80 */ + __le32 HostPrimIPMidHi; /* 132 x84 */ + __le32 HostPrimIPMidLow; /* 136 x88 */ + __le32 HostPrimIPLow; /* 140 x8c */ + __le32 HostSecIPHi; /* 144 x90 */ + __le32 HostSecIPMidHi; /* 148 x94 */ + __le32 HostSecIPMidLow; /* 152 x98 */ + __le32 HostSecIPLow; /* 156 x9c */ + __le32 EPPrimIPHi; /* 160 xa0 */ + __le32 EPPrimIPMidHi; /* 164 xa4 */ + __le32 EPPrimIPMidLow; /* 168 xa8 */ + __le32 EPPrimIPLow; /* 172 xac */ + __le32 EPSecIPHi; /* 176 xb0 */ + __le32 EPSecIPMidHi; /* 180 xb4 */ + __le32 EPSecIPMidLow; /* 184 xb8 */ + __le32 EPSecIPLow; /* 188 xbc */ + __le32 IPReassemblyTimeout; /* 192 xc0 */ + __le32 EthMaxFramePayload; /* 196 xc4 */ + __le32 TCPMaxWindowSize; /* 200 xc8 */ + __le32 TCPCurrentTimestampHi; /* 204 xcc */ + __le32 TCPCurrentTimestampLow; /* 208 xd0 */ + __le32 LocalRAMAddress; /* 212 xd4 */ + __le32 LocalRAMData; /* 216 xd8 */ + __le32 PCSReserved1; /* 220 xdc */ + __le32 gp_out; /* 224 xe0 */ + __le32 gp_in; /* 228 xe4 */ + __le32 ProbeMuxAddr; /* 232 xe8 */ + __le32 ProbeMuxData; /* 236 xec */ + __le32 ERMQueueBaseAddr0; /* 240 xf0 */ + __le32 ERMQueueBaseAddr1; /* 244 xf4 */ + __le32 MACConfiguration; /* 248 xf8 */ + __le32 port_err_status; /* 252 xfc COR */ +}; + +struct host_mem_cfg_regs { + __le32 NetRequestQueueOut; /* 80 x50 */ + __le32 NetRequestQueueOutAddrHi; /* 84 x54 */ + __le32 NetRequestQueueOutAddrLow; /* 88 x58 */ + __le32 NetRequestQueueBaseAddrHi; /* 92 x5c */ + __le32 NetRequestQueueBaseAddrLow; /* 96 x60 */ + __le32 NetRequestQueueLength; /* 100 x64 */ + __le32 NetResponseQueueIn; /* 104 x68 */ + __le32 NetResponseQueueInAddrHi; /* 108 x6c */ + __le32 NetResponseQueueInAddrLow; /* 112 x70 */ + __le32 NetResponseQueueBaseAddrHi; /* 116 x74 */ + __le32 NetResponseQueueBaseAddrLow; /* 120 x78 */ + __le32 NetResponseQueueLength; /* 124 x7c */ + __le32 req_q_out; /* 128 x80 */ + __le32 RequestQueueOutAddrHi; /* 132 x84 */ + __le32 RequestQueueOutAddrLow; /* 136 x88 */ + __le32 RequestQueueBaseAddrHi; /* 140 x8c */ + __le32 RequestQueueBaseAddrLow; /* 144 x90 */ + __le32 RequestQueueLength; /* 148 x94 */ + __le32 ResponseQueueIn; /* 152 x98 */ + __le32 ResponseQueueInAddrHi; /* 156 x9c */ + __le32 ResponseQueueInAddrLow; /* 160 xa0 */ + __le32 ResponseQueueBaseAddrHi; /* 164 xa4 */ + __le32 ResponseQueueBaseAddrLow; /* 168 xa8 */ + __le32 ResponseQueueLength; /* 172 xac */ + __le32 NetRxLargeBufferQueueOut; /* 176 xb0 */ + __le32 NetRxLargeBufferQueueBaseAddrHi; /* 180 xb4 */ + __le32 NetRxLargeBufferQueueBaseAddrLow; /* 184 xb8 */ + __le32 NetRxLargeBufferQueueLength; /* 188 xbc */ + __le32 NetRxLargeBufferLength; /* 192 xc0 */ + __le32 NetRxSmallBufferQueueOut; /* 196 xc4 */ + __le32 NetRxSmallBufferQueueBaseAddrHi; /* 200 xc8 */ + __le32 NetRxSmallBufferQueueBaseAddrLow; /* 204 xcc */ + __le32 NetRxSmallBufferQueueLength; /* 208 xd0 */ + __le32 NetRxSmallBufferLength; /* 212 xd4 */ + __le32 HMCReserved0[10]; /* 216 xd8 */ +}; + +struct local_ram_cfg_regs { + __le32 BufletSize; /* 80 x50 */ + __le32 BufletMaxCount; /* 84 x54 */ + __le32 BufletCurrCount; /* 88 x58 */ + __le32 BufletPauseThresholdCount; /* 92 x5c */ + __le32 BufletTCPWinThresholdHi; /* 96 x60 */ + __le32 BufletTCPWinThresholdLow; /* 100 x64 */ + __le32 IPHashTableBaseAddr; /* 104 x68 */ + __le32 IPHashTableSize; /* 108 x6c */ + __le32 TCPHashTableBaseAddr; /* 112 x70 */ + __le32 TCPHashTableSize; /* 116 x74 */ + __le32 NCBAreaBaseAddr; /* 120 x78 */ + __le32 NCBMaxCount; /* 124 x7c */ + __le32 NCBCurrCount; /* 128 x80 */ + __le32 DRBAreaBaseAddr; /* 132 x84 */ + __le32 DRBMaxCount; /* 136 x88 */ + __le32 DRBCurrCount; /* 140 x8c */ + __le32 LRCReserved[28]; /* 144 x90 */ +}; + +struct prot_stat_regs { + __le32 MACTxFrameCount; /* 80 x50 R */ + __le32 MACTxByteCount; /* 84 x54 R */ + __le32 MACRxFrameCount; /* 88 x58 R */ + __le32 MACRxByteCount; /* 92 x5c R */ + __le32 MACCRCErrCount; /* 96 x60 R */ + __le32 MACEncErrCount; /* 100 x64 R */ + __le32 MACRxLengthErrCount; /* 104 x68 R */ + __le32 IPTxPacketCount; /* 108 x6c R */ + __le32 IPTxByteCount; /* 112 x70 R */ + __le32 IPTxFragmentCount; /* 116 x74 R */ + __le32 IPRxPacketCount; /* 120 x78 R */ + __le32 IPRxByteCount; /* 124 x7c R */ + __le32 IPRxFragmentCount; /* 128 x80 R */ + __le32 IPDatagramReassemblyCount; /* 132 x84 R */ + __le32 IPV6RxPacketCount; /* 136 x88 R */ + __le32 IPErrPacketCount; /* 140 x8c R */ + __le32 IPReassemblyErrCount; /* 144 x90 R */ + __le32 TCPTxSegmentCount; /* 148 x94 R */ + __le32 TCPTxByteCount; /* 152 x98 R */ + __le32 TCPRxSegmentCount; /* 156 x9c R */ + __le32 TCPRxByteCount; /* 160 xa0 R */ + __le32 TCPTimerExpCount; /* 164 xa4 R */ + __le32 TCPRxAckCount; /* 168 xa8 R */ + __le32 TCPTxAckCount; /* 172 xac R */ + __le32 TCPRxErrOOOCount; /* 176 xb0 R */ + __le32 PSReserved0; /* 180 xb4 */ + __le32 TCPRxWindowProbeUpdateCount; /* 184 xb8 R */ + __le32 ECCErrCorrectionCount; /* 188 xbc R */ + __le32 PSReserved1[16]; /* 192 xc0 */ +}; + + +/* remote register set (access via PCI memory read/write) */ +struct isp_reg { +#define MBOX_REG_COUNT 8 + __le32 mailbox[MBOX_REG_COUNT]; + + __le32 flash_address; /* 0x20 */ + __le32 flash_data; + __le32 ctrl_status; + + union { + struct { + __le32 nvram; + __le32 reserved1[2]; /* 0x30 */ + } __attribute__ ((packed)) isp4010; + struct { + __le32 intr_mask; + __le32 nvram; /* 0x30 */ + __le32 semaphore; + } __attribute__ ((packed)) isp4022; + } u1; + + __le32 req_q_in; /* SCSI Request Queue Producer Index */ + __le32 rsp_q_out; /* SCSI Completion Queue Consumer Index */ + + __le32 reserved2[4]; /* 0x40 */ + + union { + struct { + __le32 ext_hw_conf; /* 0x50 */ + __le32 flow_ctrl; + __le32 port_ctrl; + __le32 port_status; + + __le32 reserved3[8]; /* 0x60 */ + + __le32 req_q_out; /* 0x80 */ + + __le32 reserved4[23]; /* 0x84 */ + + __le32 gp_out; /* 0xe0 */ + __le32 gp_in; + + __le32 reserved5[5]; + + __le32 port_err_status; /* 0xfc */ + } __attribute__ ((packed)) isp4010; + struct { + union { + struct port_ctrl_stat_regs p0; + struct host_mem_cfg_regs p1; + struct local_ram_cfg_regs p2; + struct prot_stat_regs p3; + __le32 r_union[44]; + }; + + } __attribute__ ((packed)) isp4022; + } u2; +}; /* 256 x100 */ + + +/* Semaphore Defines for 4010 */ +#define QL4010_DRVR_SEM_BITS 0x00000030 +#define QL4010_GPIO_SEM_BITS 0x000000c0 +#define QL4010_SDRAM_SEM_BITS 0x00000300 +#define QL4010_PHY_SEM_BITS 0x00000c00 +#define QL4010_NVRAM_SEM_BITS 0x00003000 +#define QL4010_FLASH_SEM_BITS 0x0000c000 + +#define QL4010_DRVR_SEM_MASK 0x00300000 +#define QL4010_GPIO_SEM_MASK 0x00c00000 +#define QL4010_SDRAM_SEM_MASK 0x03000000 +#define QL4010_PHY_SEM_MASK 0x0c000000 +#define QL4010_NVRAM_SEM_MASK 0x30000000 +#define QL4010_FLASH_SEM_MASK 0xc0000000 + +/* Semaphore Defines for 4022 */ +#define QL4022_RESOURCE_MASK_BASE_CODE 0x7 +#define QL4022_RESOURCE_BITS_BASE_CODE 0x4 + + +#define QL4022_DRVR_SEM_MASK (QL4022_RESOURCE_MASK_BASE_CODE << (1+16)) +#define QL4022_DDR_RAM_SEM_MASK (QL4022_RESOURCE_MASK_BASE_CODE << (4+16)) +#define QL4022_PHY_GIO_SEM_MASK (QL4022_RESOURCE_MASK_BASE_CODE << (7+16)) +#define QL4022_NVRAM_SEM_MASK (QL4022_RESOURCE_MASK_BASE_CODE << (10+16)) +#define QL4022_FLASH_SEM_MASK (QL4022_RESOURCE_MASK_BASE_CODE << (13+16)) + + + +/* Page # defines for 4022 */ +#define PORT_CTRL_STAT_PAGE 0 /* 4022 */ +#define HOST_MEM_CFG_PAGE 1 /* 4022 */ +#define LOCAL_RAM_CFG_PAGE 2 /* 4022 */ +#define PROT_STAT_PAGE 3 /* 4022 */ + +/* Register Mask - sets corresponding mask bits in the upper word */ +static inline uint32_t set_rmask(uint32_t val) +{ + return (val & 0xffff) | (val << 16); +} + + +static inline uint32_t clr_rmask(uint32_t val) +{ + return 0 | (val << 16); +} + +/* ctrl_status definitions */ +#define CSR_SCSI_PAGE_SELECT 0x00000003 +#define CSR_SCSI_INTR_ENABLE 0x00000004 /* 4010 */ +#define CSR_SCSI_RESET_INTR 0x00000008 +#define CSR_SCSI_COMPLETION_INTR 0x00000010 +#define CSR_SCSI_PROCESSOR_INTR 0x00000020 +#define CSR_INTR_RISC 0x00000040 +#define CSR_BOOT_ENABLE 0x00000080 +#define CSR_NET_PAGE_SELECT 0x00000300 /* 4010 */ +#define CSR_FUNC_NUM 0x00000700 /* 4022 */ +#define CSR_NET_RESET_INTR 0x00000800 /* 4010 */ +#define CSR_FORCE_SOFT_RESET 0x00002000 /* 4022 */ +#define CSR_FATAL_ERROR 0x00004000 +#define CSR_SOFT_RESET 0x00008000 +#define ISP_CONTROL_FN_MASK CSR_FUNC_NUM +#define ISP_CONTROL_FN0_SCSI 0x0500 +#define ISP_CONTROL_FN1_SCSI 0x0700 + +#define INTR_PENDING (CSR_SCSI_COMPLETION_INTR |\ + CSR_SCSI_PROCESSOR_INTR |\ + CSR_SCSI_RESET_INTR) + +/* ISP InterruptMask definitions */ +#define IMR_SCSI_INTR_ENABLE 0x00000004 /* 4022 */ + +/* ISP 4022 nvram definitions */ +#define NVR_WRITE_ENABLE 0x00000010 /* 4022 */ + +/* ISP port_status definitions */ + +/* ISP Semaphore definitions */ + +/* ISP General Purpose Output definitions */ +#define GPOR_TOPCAT_RESET 0x00000004 + +/* shadow registers (DMA'd from HA to system memory. read only) */ +struct shadow_regs { + /* SCSI Request Queue Consumer Index */ + __le32 req_q_out; /* 0 x0 R */ + + /* SCSI Completion Queue Producer Index */ + __le32 rsp_q_in; /* 4 x4 R */ +}; /* 8 x8 */ + + +/* External hardware configuration register */ +union external_hw_config_reg { + struct { + /* FIXME: Do we even need this? All values are + * referred to by 16 bit quantities. Platform and + * endianess issues. */ + __le32 bReserved0:1; + __le32 bSDRAMProtectionMethod:2; + __le32 bSDRAMBanks:1; + __le32 bSDRAMChipWidth:1; + __le32 bSDRAMChipSize:2; + __le32 bParityDisable:1; + __le32 bExternalMemoryType:1; + __le32 bFlashBIOSWriteEnable:1; + __le32 bFlashUpperBankSelect:1; + __le32 bWriteBurst:2; + __le32 bReserved1:3; + __le32 bMask:16; + }; + uint32_t Asuint32_t; +}; + +/************************************************************************* + * + * Mailbox Commands Structures and Definitions + * + *************************************************************************/ + +/* Mailbox command definitions */ +#define MBOX_CMD_ABOUT_FW 0x0009 +#define MBOX_CMD_LUN_RESET 0x0016 +#define MBOX_CMD_GET_FW_STATUS 0x001F +#define MBOX_CMD_SET_ISNS_SERVICE 0x0021 +#define ISNS_DISABLE 0 +#define ISNS_ENABLE 1 +#define MBOX_CMD_READ_FLASH 0x0026 +#define MBOX_CMD_CLEAR_DATABASE_ENTRY 0x0031 +#define MBOX_CMD_CONN_CLOSE_SESS_LOGOUT 0x0056 +#define LOGOUT_OPTION_CLOSE_SESSION 0x01 +#define LOGOUT_OPTION_RELOGIN 0x02 +#define MBOX_CMD_EXECUTE_IOCB_A64 0x005A +#define MBOX_CMD_INITIALIZE_FIRMWARE 0x0060 +#define MBOX_CMD_GET_INIT_FW_CTRL_BLOCK 0x0061 +#define MBOX_CMD_REQUEST_DATABASE_ENTRY 0x0062 +#define MBOX_CMD_SET_DATABASE_ENTRY 0x0063 +#define MBOX_CMD_GET_DATABASE_ENTRY 0x0064 +#define DDB_DS_UNASSIGNED 0x00 +#define DDB_DS_NO_CONNECTION_ACTIVE 0x01 +#define DDB_DS_SESSION_ACTIVE 0x04 +#define DDB_DS_SESSION_FAILED 0x06 +#define DDB_DS_LOGIN_IN_PROCESS 0x07 +#define MBOX_CMD_GET_FW_STATE 0x0069 + +/* Mailbox 1 */ +#define FW_STATE_READY 0x0000 +#define FW_STATE_CONFIG_WAIT 0x0001 +#define FW_STATE_ERROR 0x0004 +#define FW_STATE_DHCP_IN_PROGRESS 0x0008 + +/* Mailbox 3 */ +#define FW_ADDSTATE_OPTICAL_MEDIA 0x0001 +#define FW_ADDSTATE_DHCP_ENABLED 0x0002 +#define FW_ADDSTATE_LINK_UP 0x0010 +#define FW_ADDSTATE_ISNS_SVC_ENABLED 0x0020 +#define MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS 0x006B +#define MBOX_CMD_CONN_OPEN_SESS_LOGIN 0x0074 +#define MBOX_CMD_GET_CRASH_RECORD 0x0076 /* 4010 only */ +#define MBOX_CMD_GET_CONN_EVENT_LOG 0x0077 + +/* Mailbox status definitions */ +#define MBOX_COMPLETION_STATUS 4 +#define MBOX_STS_BUSY 0x0007 +#define MBOX_STS_INTERMEDIATE_COMPLETION 0x1000 +#define MBOX_STS_COMMAND_COMPLETE 0x4000 +#define MBOX_STS_COMMAND_ERROR 0x4005 + +#define MBOX_ASYNC_EVENT_STATUS 8 +#define MBOX_ASTS_SYSTEM_ERROR 0x8002 +#define MBOX_ASTS_REQUEST_TRANSFER_ERROR 0x8003 +#define MBOX_ASTS_RESPONSE_TRANSFER_ERROR 0x8004 +#define MBOX_ASTS_PROTOCOL_STATISTIC_ALARM 0x8005 +#define MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED 0x8006 +#define MBOX_ASTS_LINK_UP 0x8010 +#define MBOX_ASTS_LINK_DOWN 0x8011 +#define MBOX_ASTS_DATABASE_CHANGED 0x8014 +#define MBOX_ASTS_UNSOLICITED_PDU_RECEIVED 0x8015 +#define MBOX_ASTS_SELF_TEST_FAILED 0x8016 +#define MBOX_ASTS_LOGIN_FAILED 0x8017 +#define MBOX_ASTS_DNS 0x8018 +#define MBOX_ASTS_HEARTBEAT 0x8019 +#define MBOX_ASTS_NVRAM_INVALID 0x801A +#define MBOX_ASTS_MAC_ADDRESS_CHANGED 0x801B +#define MBOX_ASTS_IP_ADDRESS_CHANGED 0x801C +#define MBOX_ASTS_DHCP_LEASE_EXPIRED 0x801D +#define MBOX_ASTS_DHCP_LEASE_ACQUIRED 0x801F +#define MBOX_ASTS_ISNS_UNSOLICITED_PDU_RECEIVED 0x8021 +#define ISNS_EVENT_DATA_RECEIVED 0x0000 +#define ISNS_EVENT_CONNECTION_OPENED 0x0001 +#define ISNS_EVENT_CONNECTION_FAILED 0x0002 +#define MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR 0x8022 +#define MBOX_ASTS_SUBNET_STATE_CHANGE 0x8027 + +/*************************************************************************/ + +/* Host Adapter Initialization Control Block (from host) */ +struct init_fw_ctrl_blk { + uint8_t Version; /* 00 */ + uint8_t Control; /* 01 */ + + uint16_t FwOptions; /* 02-03 */ +#define FWOPT_HEARTBEAT_ENABLE 0x1000 +#define FWOPT_SESSION_MODE 0x0040 +#define FWOPT_INITIATOR_MODE 0x0020 +#define FWOPT_TARGET_MODE 0x0010 + + uint16_t ExecThrottle; /* 04-05 */ + uint8_t RetryCount; /* 06 */ + uint8_t RetryDelay; /* 07 */ + uint16_t MaxEthFrPayloadSize; /* 08-09 */ + uint16_t AddFwOptions; /* 0A-0B */ + + uint8_t HeartbeatInterval; /* 0C */ + uint8_t InstanceNumber; /* 0D */ + uint16_t RES2; /* 0E-0F */ + uint16_t ReqQConsumerIndex; /* 10-11 */ + uint16_t ComplQProducerIndex; /* 12-13 */ + uint16_t ReqQLen; /* 14-15 */ + uint16_t ComplQLen; /* 16-17 */ + uint32_t ReqQAddrLo; /* 18-1B */ + uint32_t ReqQAddrHi; /* 1C-1F */ + uint32_t ComplQAddrLo; /* 20-23 */ + uint32_t ComplQAddrHi; /* 24-27 */ + uint32_t ShadowRegBufAddrLo; /* 28-2B */ + uint32_t ShadowRegBufAddrHi; /* 2C-2F */ + + uint16_t iSCSIOptions; /* 30-31 */ + + uint16_t TCPOptions; /* 32-33 */ + + uint16_t IPOptions; /* 34-35 */ + + uint16_t MaxPDUSize; /* 36-37 */ + uint16_t RcvMarkerInt; /* 38-39 */ + uint16_t SndMarkerInt; /* 3A-3B */ + uint16_t InitMarkerlessInt; /* 3C-3D */ + uint16_t FirstBurstSize; /* 3E-3F */ + uint16_t DefaultTime2Wait; /* 40-41 */ + uint16_t DefaultTime2Retain; /* 42-43 */ + uint16_t MaxOutStndngR2T; /* 44-45 */ + uint16_t KeepAliveTimeout; /* 46-47 */ + uint16_t PortNumber; /* 48-49 */ + uint16_t MaxBurstSize; /* 4A-4B */ + uint32_t RES4; /* 4C-4F */ + uint8_t IPAddr[4]; /* 50-53 */ + uint8_t RES5[12]; /* 54-5F */ + uint8_t SubnetMask[4]; /* 60-63 */ + uint8_t RES6[12]; /* 64-6F */ + uint8_t GatewayIPAddr[4]; /* 70-73 */ + uint8_t RES7[12]; /* 74-7F */ + uint8_t PriDNSIPAddr[4]; /* 80-83 */ + uint8_t SecDNSIPAddr[4]; /* 84-87 */ + uint8_t RES8[8]; /* 88-8F */ + uint8_t Alias[32]; /* 90-AF */ + uint8_t TargAddr[8]; /* B0-B7 *//* /FIXME: Remove?? */ + uint8_t CHAPNameSecretsTable[8]; /* B8-BF */ + uint8_t EthernetMACAddr[6]; /* C0-C5 */ + uint16_t TargetPortalGroup; /* C6-C7 */ + uint8_t SendScale; /* C8 */ + uint8_t RecvScale; /* C9 */ + uint8_t TypeOfService; /* CA */ + uint8_t Time2Live; /* CB */ + uint16_t VLANPriority; /* CC-CD */ + uint16_t Reserved8; /* CE-CF */ + uint8_t SecIPAddr[4]; /* D0-D3 */ + uint8_t Reserved9[12]; /* D4-DF */ + uint8_t iSNSIPAddr[4]; /* E0-E3 */ + uint16_t iSNSServerPortNumber; /* E4-E5 */ + uint8_t Reserved10[10]; /* E6-EF */ + uint8_t SLPDAIPAddr[4]; /* F0-F3 */ + uint8_t Reserved11[12]; /* F4-FF */ + uint8_t iSCSINameString[256]; /* 100-1FF */ +}; + +/*************************************************************************/ + +struct dev_db_entry { + uint8_t options; /* 00 */ +#define DDB_OPT_DISC_SESSION 0x10 +#define DDB_OPT_TARGET 0x02 /* device is a target */ + + uint8_t control; /* 01 */ + + uint16_t exeThrottle; /* 02-03 */ + uint16_t exeCount; /* 04-05 */ + uint8_t retryCount; /* 06 */ + uint8_t retryDelay; /* 07 */ + uint16_t iSCSIOptions; /* 08-09 */ + + uint16_t TCPOptions; /* 0A-0B */ + + uint16_t IPOptions; /* 0C-0D */ + + uint16_t maxPDUSize; /* 0E-0F */ + uint16_t rcvMarkerInt; /* 10-11 */ + uint16_t sndMarkerInt; /* 12-13 */ + uint16_t iSCSIMaxSndDataSegLen; /* 14-15 */ + uint16_t firstBurstSize; /* 16-17 */ + uint16_t minTime2Wait; /* 18-19 : RA :default_time2wait */ + uint16_t maxTime2Retain; /* 1A-1B */ + uint16_t maxOutstndngR2T; /* 1C-1D */ + uint16_t keepAliveTimeout; /* 1E-1F */ + uint8_t ISID[6]; /* 20-25 big-endian, must be converted + * to little-endian */ + uint16_t TSID; /* 26-27 */ + uint16_t portNumber; /* 28-29 */ + uint16_t maxBurstSize; /* 2A-2B */ + uint16_t taskMngmntTimeout; /* 2C-2D */ + uint16_t reserved1; /* 2E-2F */ + uint8_t ipAddr[0x10]; /* 30-3F */ + uint8_t iSCSIAlias[0x20]; /* 40-5F */ + uint8_t targetAddr[0x20]; /* 60-7F */ + uint8_t userID[0x20]; /* 80-9F */ + uint8_t password[0x20]; /* A0-BF */ + uint8_t iscsiName[0x100]; /* C0-1BF : xxzzy Make this a + * pointer to a string so we + * don't have to reserve soooo + * much RAM */ + uint16_t ddbLink; /* 1C0-1C1 */ + uint16_t CHAPTableIndex; /* 1C2-1C3 */ + uint16_t TargetPortalGroup; /* 1C4-1C5 */ + uint16_t reserved2[2]; /* 1C6-1C7 */ + uint32_t statSN; /* 1C8-1CB */ + uint32_t expStatSN; /* 1CC-1CF */ + uint16_t reserved3[0x2C]; /* 1D0-1FB */ + uint16_t ddbValidCookie; /* 1FC-1FD */ + uint16_t ddbValidSize; /* 1FE-1FF */ +}; + +/*************************************************************************/ + +/* Flash definitions */ + +#define FLASH_OFFSET_SYS_INFO 0x02000000 +#define FLASH_DEFAULTBLOCKSIZE 0x20000 +#define FLASH_EOF_OFFSET (FLASH_DEFAULTBLOCKSIZE-8) /* 4 bytes + * for EOF + * signature */ + +struct sys_info_phys_addr { + uint8_t address[6]; /* 00-05 */ + uint8_t filler[2]; /* 06-07 */ +}; + +struct flash_sys_info { + uint32_t cookie; /* 00-03 */ + uint32_t physAddrCount; /* 04-07 */ + struct sys_info_phys_addr physAddr[4]; /* 08-27 */ + uint8_t vendorId[128]; /* 28-A7 */ + uint8_t productId[128]; /* A8-127 */ + uint32_t serialNumber; /* 128-12B */ + + /* PCI Configuration values */ + uint32_t pciDeviceVendor; /* 12C-12F */ + uint32_t pciDeviceId; /* 130-133 */ + uint32_t pciSubsysVendor; /* 134-137 */ + uint32_t pciSubsysId; /* 138-13B */ + + /* This validates version 1. */ + uint32_t crumbs; /* 13C-13F */ + + uint32_t enterpriseNumber; /* 140-143 */ + + uint32_t mtu; /* 144-147 */ + uint32_t reserved0; /* 148-14b */ + uint32_t crumbs2; /* 14c-14f */ + uint8_t acSerialNumber[16]; /* 150-15f */ + uint32_t crumbs3; /* 160-16f */ + + /* Leave this last in the struct so it is declared invalid if + * any new items are added. + */ + uint32_t reserved1[39]; /* 170-1ff */ +}; /* 200 */ + +struct crash_record { + uint16_t fw_major_version; /* 00 - 01 */ + uint16_t fw_minor_version; /* 02 - 03 */ + uint16_t fw_patch_version; /* 04 - 05 */ + uint16_t fw_build_version; /* 06 - 07 */ + + uint8_t build_date[16]; /* 08 - 17 */ + uint8_t build_time[16]; /* 18 - 27 */ + uint8_t build_user[16]; /* 28 - 37 */ + uint8_t card_serial_num[16]; /* 38 - 47 */ + + uint32_t time_of_crash_in_secs; /* 48 - 4B */ + uint32_t time_of_crash_in_ms; /* 4C - 4F */ + + uint16_t out_RISC_sd_num_frames; /* 50 - 51 */ + uint16_t OAP_sd_num_words; /* 52 - 53 */ + uint16_t IAP_sd_num_frames; /* 54 - 55 */ + uint16_t in_RISC_sd_num_words; /* 56 - 57 */ + + uint8_t reserved1[28]; /* 58 - 7F */ + + uint8_t out_RISC_reg_dump[256]; /* 80 -17F */ + uint8_t in_RISC_reg_dump[256]; /*180 -27F */ + uint8_t in_out_RISC_stack_dump[0]; /*280 - ??? */ +}; + +struct conn_event_log_entry { +#define MAX_CONN_EVENT_LOG_ENTRIES 100 + uint32_t timestamp_sec; /* 00 - 03 seconds since boot */ + uint32_t timestamp_ms; /* 04 - 07 milliseconds since boot */ + uint16_t device_index; /* 08 - 09 */ + uint16_t fw_conn_state; /* 0A - 0B */ + uint8_t event_type; /* 0C - 0C */ + uint8_t error_code; /* 0D - 0D */ + uint16_t error_code_detail; /* 0E - 0F */ + uint8_t num_consecutive_events; /* 10 - 10 */ + uint8_t rsvd[3]; /* 11 - 13 */ +}; + +/************************************************************************* + * + * IOCB Commands Structures and Definitions + * + *************************************************************************/ +#define IOCB_MAX_CDB_LEN 16 /* Bytes in a CBD */ +#define IOCB_MAX_SENSEDATA_LEN 32 /* Bytes of sense data */ + +/* IOCB header structure */ +struct qla4_header { + uint8_t entryType; +#define ET_STATUS 0x03 +#define ET_MARKER 0x04 +#define ET_CONT_T1 0x0A +#define ET_STATUS_CONTINUATION 0x10 +#define ET_CMND_T3 0x19 +#define ET_PASSTHRU0 0x3A +#define ET_PASSTHRU_STATUS 0x3C + + uint8_t entryStatus; + uint8_t systemDefined; + uint8_t entryCount; + + /* SyetemDefined definition */ +}; + +/* Generic queue entry structure*/ +struct queue_entry { + uint8_t data[60]; + uint32_t signature; + +}; + +/* 64 bit addressing segment counts*/ + +#define COMMAND_SEG_A64 1 +#define CONTINUE_SEG_A64 5 + +/* 64 bit addressing segment definition*/ + +struct data_seg_a64 { + struct { + uint32_t addrLow; + uint32_t addrHigh; + + } base; + + uint32_t count; + +}; + +/* Command Type 3 entry structure*/ + +struct command_t3_entry { + struct qla4_header hdr; /* 00-03 */ + + uint32_t handle; /* 04-07 */ + uint16_t target; /* 08-09 */ + uint16_t connection_id; /* 0A-0B */ + + uint8_t control_flags; /* 0C */ + + /* data direction (bits 5-6) */ +#define CF_WRITE 0x20 +#define CF_READ 0x40 +#define CF_NO_DATA 0x00 + + /* task attributes (bits 2-0) */ +#define CF_HEAD_TAG 0x03 +#define CF_ORDERED_TAG 0x02 +#define CF_SIMPLE_TAG 0x01 + + /* STATE FLAGS FIELD IS A PLACE HOLDER. THE FW WILL SET BITS + * IN THIS FIELD AS THE COMMAND IS PROCESSED. WHEN THE IOCB IS + * CHANGED TO AN IOSB THIS FIELD WILL HAVE THE STATE FLAGS SET + * PROPERLY. + */ + uint8_t state_flags; /* 0D */ + uint8_t cmdRefNum; /* 0E */ + uint8_t reserved1; /* 0F */ + uint8_t cdb[IOCB_MAX_CDB_LEN]; /* 10-1F */ + struct scsi_lun lun; /* FCP LUN (BE). */ + uint32_t cmdSeqNum; /* 28-2B */ + uint16_t timeout; /* 2C-2D */ + uint16_t dataSegCnt; /* 2E-2F */ + uint32_t ttlByteCnt; /* 30-33 */ + struct data_seg_a64 dataseg[COMMAND_SEG_A64]; /* 34-3F */ + +}; + + +/* Continuation Type 1 entry structure*/ +struct continuation_t1_entry { + struct qla4_header hdr; + + struct data_seg_a64 dataseg[CONTINUE_SEG_A64]; + +}; + +/* Parameterize for 64 or 32 bits */ +#define COMMAND_SEG COMMAND_SEG_A64 +#define CONTINUE_SEG CONTINUE_SEG_A64 + +#define ET_COMMAND ET_CMND_T3 +#define ET_CONTINUE ET_CONT_T1 + +/* Marker entry structure*/ +struct marker_entry { + struct qla4_header hdr; /* 00-03 */ + + uint32_t system_defined; /* 04-07 */ + uint16_t target; /* 08-09 */ + uint16_t modifier; /* 0A-0B */ +#define MM_LUN_RESET 0 + + uint16_t flags; /* 0C-0D */ + uint16_t reserved1; /* 0E-0F */ + struct scsi_lun lun; /* FCP LUN (BE). */ + uint64_t reserved2; /* 18-1F */ + uint64_t reserved3; /* 20-27 */ + uint64_t reserved4; /* 28-2F */ + uint64_t reserved5; /* 30-37 */ + uint64_t reserved6; /* 38-3F */ +}; + +/* Status entry structure*/ +struct status_entry { + struct qla4_header hdr; /* 00-03 */ + + uint32_t handle; /* 04-07 */ + + uint8_t scsiStatus; /* 08 */ +#define SCSI_CHECK_CONDITION 0x02 + + uint8_t iscsiFlags; /* 09 */ +#define ISCSI_FLAG_RESIDUAL_UNDER 0x02 +#define ISCSI_FLAG_RESIDUAL_OVER 0x04 + + uint8_t iscsiResponse; /* 0A */ + + uint8_t completionStatus; /* 0B */ +#define SCS_COMPLETE 0x00 +#define SCS_INCOMPLETE 0x01 +#define SCS_RESET_OCCURRED 0x04 +#define SCS_ABORTED 0x05 +#define SCS_TIMEOUT 0x06 +#define SCS_DATA_OVERRUN 0x07 +#define SCS_DATA_UNDERRUN 0x15 +#define SCS_QUEUE_FULL 0x1C +#define SCS_DEVICE_UNAVAILABLE 0x28 +#define SCS_DEVICE_LOGGED_OUT 0x29 + + uint8_t reserved1; /* 0C */ + + /* state_flags MUST be at the same location as state_flags in + * the Command_T3/4_Entry */ + uint8_t state_flags; /* 0D */ + + uint16_t senseDataByteCnt; /* 0E-0F */ + uint32_t residualByteCnt; /* 10-13 */ + uint32_t bidiResidualByteCnt; /* 14-17 */ + uint32_t expSeqNum; /* 18-1B */ + uint32_t maxCmdSeqNum; /* 1C-1F */ + uint8_t senseData[IOCB_MAX_SENSEDATA_LEN]; /* 20-3F */ + +}; + +struct passthru0 { + struct qla4_header hdr; /* 00-03 */ + uint32_t handle; /* 04-07 */ + uint16_t target; /* 08-09 */ + uint16_t connectionID; /* 0A-0B */ +#define ISNS_DEFAULT_SERVER_CONN_ID ((uint16_t)0x8000) + + uint16_t controlFlags; /* 0C-0D */ +#define PT_FLAG_ETHERNET_FRAME 0x8000 +#define PT_FLAG_ISNS_PDU 0x8000 +#define PT_FLAG_SEND_BUFFER 0x0200 +#define PT_FLAG_WAIT_4_RESPONSE 0x0100 + + uint16_t timeout; /* 0E-0F */ +#define PT_DEFAULT_TIMEOUT 30 /* seconds */ + + struct data_seg_a64 outDataSeg64; /* 10-1B */ + uint32_t res1; /* 1C-1F */ + struct data_seg_a64 inDataSeg64; /* 20-2B */ + uint8_t res2[20]; /* 2C-3F */ +}; + +struct passthru_status { + struct qla4_header hdr; /* 00-03 */ + uint32_t handle; /* 04-07 */ + uint16_t target; /* 08-09 */ + uint16_t connectionID; /* 0A-0B */ + + uint8_t completionStatus; /* 0C */ +#define PASSTHRU_STATUS_COMPLETE 0x01 + + uint8_t residualFlags; /* 0D */ + + uint16_t timeout; /* 0E-0F */ + uint16_t portNumber; /* 10-11 */ + uint8_t res1[10]; /* 12-1B */ + uint32_t outResidual; /* 1C-1F */ + uint8_t res2[12]; /* 20-2B */ + uint32_t inResidual; /* 2C-2F */ + uint8_t res4[16]; /* 30-3F */ +}; + +#endif /* _QLA4X_FW_H */ diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h new file mode 100644 index 000000000000..418fb7a13a65 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_glbl.h @@ -0,0 +1,78 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#ifndef __QLA4x_GBL_H +#define __QLA4x_GBL_H + +int qla4xxx_send_tgts(struct scsi_qla_host *ha, char *ip, uint16_t port); +int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb); +int qla4xxx_initialize_adapter(struct scsi_qla_host * ha, + uint8_t renew_ddb_list); +int qla4xxx_soft_reset(struct scsi_qla_host *ha); +irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id, struct pt_regs *regs); + +void qla4xxx_free_ddb_list(struct scsi_qla_host * ha); +void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen); + +int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha); +int qla4xxx_relogin_device(struct scsi_qla_host * ha, + struct ddb_entry * ddb_entry); +int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, + int lun); +int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, + uint32_t offset, uint32_t len); +int qla4xxx_get_firmware_status(struct scsi_qla_host * ha); +int qla4xxx_get_firmware_state(struct scsi_qla_host * ha); +int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha); + +/* FIXME: Goodness! this really wants a small struct to hold the + * parameters. On x86 the args will get passed on the stack! */ +int qla4xxx_get_fwddb_entry(struct scsi_qla_host *ha, + uint16_t fw_ddb_index, + struct dev_db_entry *fw_ddb_entry, + dma_addr_t fw_ddb_entry_dma, + uint32_t *num_valid_ddb_entries, + uint32_t *next_ddb_index, + uint32_t *fw_ddb_device_state, + uint32_t *conn_err_detail, + uint16_t *tcp_source_port_num, + uint16_t *connection_id); + +struct ddb_entry * qla4xxx_alloc_ddb(struct scsi_qla_host * ha, + uint32_t fw_ddb_index); +int qla4xxx_set_ddb_entry(struct scsi_qla_host * ha, uint16_t fw_ddb_index, + dma_addr_t fw_ddb_entry_dma); + +void qla4xxx_mark_device_missing(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry); +u16 rd_nvram_word(struct scsi_qla_host * ha, int offset); +void qla4xxx_get_crash_record(struct scsi_qla_host * ha); +struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha); +int qla4xxx_add_sess(struct ddb_entry *); +void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry); +int qla4xxx_conn_close_sess_logout(struct scsi_qla_host * ha, + uint16_t fw_ddb_index, + uint16_t connection_id, + uint16_t option); +int qla4xxx_clear_database_entry(struct scsi_qla_host * ha, + uint16_t fw_ddb_index); +int qla4xxx_is_nvram_configuration_valid(struct scsi_qla_host * ha); +int qla4xxx_get_fw_version(struct scsi_qla_host * ha); +void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, + uint32_t intr_status); +int qla4xxx_init_rings(struct scsi_qla_host * ha); +void qla4xxx_dump_buffer(void *b, uint32_t size); +struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index); +void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb); +int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host * ha); +int qla4xxx_process_ddb_changed(struct scsi_qla_host * ha, + uint32_t fw_ddb_index, uint32_t state); + +extern int extended_error_logging; +extern int ql4xdiscoverywait; +extern int ql4xdontresethba; +#endif /* _QLA4x_GBL_H */ diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c new file mode 100644 index 000000000000..bb3a1c11f44c --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_init.c @@ -0,0 +1,1340 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" + +/* + * QLogic ISP4xxx Hardware Support Function Prototypes. + */ + +static void ql4xxx_set_mac_number(struct scsi_qla_host *ha) +{ + uint32_t value; + uint8_t func_number; + unsigned long flags; + + /* Get the function number */ + spin_lock_irqsave(&ha->hardware_lock, flags); + value = readw(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + func_number = (uint8_t) ((value >> 4) & 0x30); + switch (value & ISP_CONTROL_FN_MASK) { + case ISP_CONTROL_FN0_SCSI: + ha->mac_index = 1; + break; + case ISP_CONTROL_FN1_SCSI: + ha->mac_index = 3; + break; + default: + DEBUG2(printk("scsi%ld: %s: Invalid function number, " + "ispControlStatus = 0x%x\n", ha->host_no, + __func__, value)); + break; + } + DEBUG2(printk("scsi%ld: %s: mac_index %d.\n", ha->host_no, __func__, + ha->mac_index)); +} + +/** + * qla4xxx_free_ddb - deallocate ddb + * @ha: pointer to host adapter structure. + * @ddb_entry: pointer to device database entry + * + * This routine deallocates and unlinks the specified ddb_entry from the + * adapter's + **/ +void qla4xxx_free_ddb(struct scsi_qla_host *ha, struct ddb_entry *ddb_entry) +{ + /* Remove device entry from list */ + list_del_init(&ddb_entry->list); + + /* Remove device pointer from index mapping arrays */ + ha->fw_ddb_index_map[ddb_entry->fw_ddb_index] = + (struct ddb_entry *) INVALID_ENTRY; + ha->tot_ddbs--; + + /* Free memory and scsi-ml struct for device entry */ + qla4xxx_destroy_sess(ddb_entry); +} + +/** + * qla4xxx_free_ddb_list - deallocate all ddbs + * @ha: pointer to host adapter structure. + * + * This routine deallocates and removes all devices on the sppecified adapter. + **/ +void qla4xxx_free_ddb_list(struct scsi_qla_host *ha) +{ + struct list_head *ptr; + struct ddb_entry *ddb_entry; + + while (!list_empty(&ha->ddb_list)) { + ptr = ha->ddb_list.next; + /* Free memory for device entry and remove */ + ddb_entry = list_entry(ptr, struct ddb_entry, list); + qla4xxx_free_ddb(ha, ddb_entry); + } +} + +/** + * qla4xxx_init_rings - initialize hw queues + * @ha: pointer to host adapter structure. + * + * This routine initializes the internal queues for the specified adapter. + * The QLA4010 requires us to restart the queues at index 0. + * The QLA4000 doesn't care, so just default to QLA4010's requirement. + **/ +int qla4xxx_init_rings(struct scsi_qla_host *ha) +{ + unsigned long flags = 0; + + /* Initialize request queue. */ + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->request_out = 0; + ha->request_in = 0; + ha->request_ptr = &ha->request_ring[ha->request_in]; + ha->req_q_count = REQUEST_QUEUE_DEPTH; + + /* Initialize response queue. */ + ha->response_in = 0; + ha->response_out = 0; + ha->response_ptr = &ha->response_ring[ha->response_out]; + + /* + * Initialize DMA Shadow registers. The firmware is really supposed to + * take care of this, but on some uniprocessor systems, the shadow + * registers aren't cleared-- causing the interrupt_handler to think + * there are responses to be processed when there aren't. + */ + ha->shadow_regs->req_q_out = __constant_cpu_to_le32(0); + ha->shadow_regs->rsp_q_in = __constant_cpu_to_le32(0); + wmb(); + + writel(0, &ha->reg->req_q_in); + writel(0, &ha->reg->rsp_q_out); + readl(&ha->reg->rsp_q_out); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_SUCCESS; +} + +/** + * qla4xxx_validate_mac_address - validate adapter MAC address(es) + * @ha: pointer to host adapter structure. + * + **/ +static int qla4xxx_validate_mac_address(struct scsi_qla_host *ha) +{ + struct flash_sys_info *sys_info; + dma_addr_t sys_info_dma; + int status = QLA_ERROR; + + sys_info = dma_alloc_coherent(&ha->pdev->dev, sizeof(*sys_info), + &sys_info_dma, GFP_KERNEL); + if (sys_info == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n", + ha->host_no, __func__)); + + goto exit_validate_mac_no_free; + } + memset(sys_info, 0, sizeof(*sys_info)); + + /* Get flash sys info */ + if (qla4xxx_get_flash(ha, sys_info_dma, FLASH_OFFSET_SYS_INFO, + sizeof(*sys_info)) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: get_flash FLASH_OFFSET_SYS_INFO " + "failed\n", ha->host_no, __func__)); + + goto exit_validate_mac; + } + + /* Save M.A.C. address & serial_number */ + memcpy(ha->my_mac, &sys_info->physAddr[0].address[0], + min(sizeof(ha->my_mac), + sizeof(sys_info->physAddr[0].address))); + memcpy(ha->serial_number, &sys_info->acSerialNumber, + min(sizeof(ha->serial_number), + sizeof(sys_info->acSerialNumber))); + + status = QLA_SUCCESS; + + exit_validate_mac: + dma_free_coherent(&ha->pdev->dev, sizeof(*sys_info), sys_info, + sys_info_dma); + + exit_validate_mac_no_free: + return status; +} + +/** + * qla4xxx_init_local_data - initialize adapter specific local data + * @ha: pointer to host adapter structure. + * + **/ +static int qla4xxx_init_local_data(struct scsi_qla_host *ha) +{ + /* Initilize aen queue */ + ha->aen_q_count = MAX_AEN_ENTRIES; + + return qla4xxx_get_firmware_status(ha); +} + +static int qla4xxx_fw_ready(struct scsi_qla_host *ha) +{ + uint32_t timeout_count; + int ready = 0; + + DEBUG2(dev_info(&ha->pdev->dev, "Waiting for Firmware Ready..\n")); + for (timeout_count = ADAPTER_INIT_TOV; timeout_count > 0; + timeout_count--) { + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + + /* Get firmware state. */ + if (qla4xxx_get_firmware_state(ha) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: unable to get firmware " + "state\n", ha->host_no, __func__)); + break; + + } + + if (ha->firmware_state & FW_STATE_ERROR) { + DEBUG2(printk("scsi%ld: %s: an unrecoverable error has" + " occurred\n", ha->host_no, __func__)); + break; + + } + if (ha->firmware_state & FW_STATE_CONFIG_WAIT) { + /* + * The firmware has not yet been issued an Initialize + * Firmware command, so issue it now. + */ + if (qla4xxx_initialize_fw_cb(ha) == QLA_ERROR) + break; + + /* Go back and test for ready state - no wait. */ + continue; + } + + if (ha->firmware_state == FW_STATE_READY) { + DEBUG2(dev_info(&ha->pdev->dev, "Firmware Ready..\n")); + /* The firmware is ready to process SCSI commands. */ + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: MEDIA TYPE - %s\n", + ha->host_no, + __func__, (ha->addl_fw_state & + FW_ADDSTATE_OPTICAL_MEDIA) + != 0 ? "OPTICAL" : "COPPER")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: DHCP STATE Enabled " + "%s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_DHCP_ENABLED) != 0 ? + "YES" : "NO")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: LINK %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_LINK_UP) != 0 ? + "UP" : "DOWN")); + DEBUG2(dev_info(&ha->pdev->dev, + "scsi%ld: %s: iSNS Service " + "Started %s\n", + ha->host_no, __func__, + (ha->addl_fw_state & + FW_ADDSTATE_ISNS_SVC_ENABLED) != 0 ? + "YES" : "NO")); + + ready = 1; + break; + } + DEBUG2(printk("scsi%ld: %s: waiting on fw, state=%x:%x - " + "seconds expired= %d\n", ha->host_no, __func__, + ha->firmware_state, ha->addl_fw_state, + timeout_count)); + msleep(1000); + } /* end of for */ + + if (timeout_count <= 0) + DEBUG2(printk("scsi%ld: %s: FW Initialization timed out!\n", + ha->host_no, __func__)); + + if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) { + DEBUG2(printk("scsi%ld: %s: FW is reporting its waiting to" + " grab an IP address from DHCP server\n", + ha->host_no, __func__)); + ready = 1; + } + + return ready; +} + +/** + * qla4xxx_init_firmware - initializes the firmware. + * @ha: pointer to host adapter structure. + * + **/ +static int qla4xxx_init_firmware(struct scsi_qla_host *ha) +{ + int status = QLA_ERROR; + + dev_info(&ha->pdev->dev, "Initializing firmware..\n"); + if (qla4xxx_initialize_fw_cb(ha) == QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s: Failed to initialize firmware " + "control block\n", ha->host_no, __func__)); + return status; + } + if (!qla4xxx_fw_ready(ha)) + return status; + + set_bit(AF_ONLINE, &ha->flags); + return qla4xxx_get_firmware_status(ha); +} + +static struct ddb_entry* qla4xxx_get_ddb_entry(struct scsi_qla_host *ha, + uint32_t fw_ddb_index) +{ + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + struct ddb_entry *ddb_entry = NULL; + int found = 0; + uint32_t device_state; + + /* Make sure the dma buffer is valid */ + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, + sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (fw_ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n", + ha->host_no, __func__)); + return NULL; + } + + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + fw_ddb_entry_dma, NULL, NULL, + &device_state, NULL, NULL, NULL) == + QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for " + "fw_ddb_index %d\n", ha->host_no, __func__, + fw_ddb_index)); + return NULL; + } + + /* Allocate DDB if not already allocated. */ + DEBUG2(printk("scsi%ld: %s: Looking for ddb[%d]\n", ha->host_no, + __func__, fw_ddb_index)); + list_for_each_entry(ddb_entry, &ha->ddb_list, list) { + if (memcmp(ddb_entry->iscsi_name, fw_ddb_entry->iscsiName, + ISCSI_NAME_SIZE) == 0) { + found++; + break; + } + } + + if (!found) { + DEBUG2(printk("scsi%ld: %s: ddb[%d] not found - allocating " + "new ddb\n", ha->host_no, __func__, + fw_ddb_index)); + ddb_entry = qla4xxx_alloc_ddb(ha, fw_ddb_index); + } + + /* if not found allocate new ddb */ + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), fw_ddb_entry, + fw_ddb_entry_dma); + + return ddb_entry; +} + +/** + * qla4xxx_update_ddb_entry - update driver's internal ddb + * @ha: pointer to host adapter structure. + * @ddb_entry: pointer to device database structure to be filled + * @fw_ddb_index: index of the ddb entry in fw ddb table + * + * This routine updates the driver's internal device database entry + * with information retrieved from the firmware's device database + * entry for the specified device. The ddb_entry->fw_ddb_index field + * must be initialized prior to calling this routine + * + **/ +int qla4xxx_update_ddb_entry(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, + uint32_t fw_ddb_index) +{ + struct dev_db_entry *fw_ddb_entry = NULL; + dma_addr_t fw_ddb_entry_dma; + int status = QLA_ERROR; + + if (ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: ddb_entry is NULL\n", ha->host_no, + __func__)); + goto exit_update_ddb; + } + + /* Make sure the dma buffer is valid */ + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, + sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (fw_ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n", + ha->host_no, __func__)); + + goto exit_update_ddb; + } + + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, fw_ddb_entry, + fw_ddb_entry_dma, NULL, NULL, + &ddb_entry->fw_ddb_device_state, NULL, + &ddb_entry->tcp_source_port_num, + &ddb_entry->connection_id) == + QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s: failed get_ddb_entry for " + "fw_ddb_index %d\n", ha->host_no, __func__, + fw_ddb_index)); + + goto exit_update_ddb; + } + + status = QLA_SUCCESS; + ddb_entry->target_session_id = le16_to_cpu(fw_ddb_entry->TSID); + ddb_entry->task_mgmt_timeout = + le16_to_cpu(fw_ddb_entry->taskMngmntTimeout); + ddb_entry->CmdSn = 0; + ddb_entry->exe_throttle = le16_to_cpu(fw_ddb_entry->exeThrottle); + ddb_entry->default_relogin_timeout = + le16_to_cpu(fw_ddb_entry->taskMngmntTimeout); + ddb_entry->default_time2wait = le16_to_cpu(fw_ddb_entry->minTime2Wait); + + /* Update index in case it changed */ + ddb_entry->fw_ddb_index = fw_ddb_index; + ha->fw_ddb_index_map[fw_ddb_index] = ddb_entry; + + ddb_entry->port = le16_to_cpu(fw_ddb_entry->portNumber); + ddb_entry->tpgt = le32_to_cpu(fw_ddb_entry->TargetPortalGroup); + memcpy(&ddb_entry->iscsi_name[0], &fw_ddb_entry->iscsiName[0], + min(sizeof(ddb_entry->iscsi_name), + sizeof(fw_ddb_entry->iscsiName))); + memcpy(&ddb_entry->ip_addr[0], &fw_ddb_entry->ipAddr[0], + min(sizeof(ddb_entry->ip_addr), sizeof(fw_ddb_entry->ipAddr))); + + DEBUG2(printk("scsi%ld: %s: ddb[%d] - State= %x status= %d.\n", + ha->host_no, __func__, fw_ddb_index, + ddb_entry->fw_ddb_device_state, status)); + + exit_update_ddb: + if (fw_ddb_entry) + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + fw_ddb_entry, fw_ddb_entry_dma); + + return status; +} + +/** + * qla4xxx_alloc_ddb - allocate device database entry + * @ha: Pointer to host adapter structure. + * @fw_ddb_index: Firmware's device database index + * + * This routine allocates a ddb_entry, ititializes some values, and + * inserts it into the ddb list. + **/ +struct ddb_entry * qla4xxx_alloc_ddb(struct scsi_qla_host *ha, + uint32_t fw_ddb_index) +{ + struct ddb_entry *ddb_entry; + + DEBUG2(printk("scsi%ld: %s: fw_ddb_index [%d]\n", ha->host_no, + __func__, fw_ddb_index)); + + ddb_entry = qla4xxx_alloc_sess(ha); + if (ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate memory " + "to add fw_ddb_index [%d]\n", + ha->host_no, __func__, fw_ddb_index)); + return ddb_entry; + } + + ddb_entry->fw_ddb_index = fw_ddb_index; + atomic_set(&ddb_entry->port_down_timer, ha->port_down_retry_count); + atomic_set(&ddb_entry->retry_relogin_timer, INVALID_ENTRY); + atomic_set(&ddb_entry->relogin_timer, 0); + atomic_set(&ddb_entry->relogin_retry_count, 0); + atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); + list_add_tail(&ddb_entry->list, &ha->ddb_list); + ha->fw_ddb_index_map[fw_ddb_index] = ddb_entry; + ha->tot_ddbs++; + + return ddb_entry; +} + +/** + * qla4xxx_configure_ddbs - builds driver ddb list + * @ha: Pointer to host adapter structure. + * + * This routine searches for all valid firmware ddb entries and builds + * an internal ddb list. Ddbs that are considered valid are those with + * a device state of SESSION_ACTIVE. + **/ +static int qla4xxx_build_ddb_list(struct scsi_qla_host *ha) +{ + int status = QLA_SUCCESS; + uint32_t fw_ddb_index = 0; + uint32_t next_fw_ddb_index = 0; + uint32_t ddb_state; + uint32_t conn_err, err_code; + struct ddb_entry *ddb_entry; + + dev_info(&ha->pdev->dev, "Initializing DDBs ...\n"); + for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; + fw_ddb_index = next_fw_ddb_index) { + /* First, let's see if a device exists here */ + if (qla4xxx_get_fwddb_entry(ha, fw_ddb_index, NULL, 0, NULL, + &next_fw_ddb_index, &ddb_state, + &conn_err, NULL, NULL) == + QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s: get_ddb_entry, " + "fw_ddb_index %d failed", ha->host_no, + __func__, fw_ddb_index)); + return QLA_ERROR; + } + + DEBUG2(printk("scsi%ld: %s: Getting DDB[%d] ddbstate=0x%x, " + "next_fw_ddb_index=%d.\n", ha->host_no, __func__, + fw_ddb_index, ddb_state, next_fw_ddb_index)); + + /* Issue relogin, if necessary. */ + if (ddb_state == DDB_DS_SESSION_FAILED || + ddb_state == DDB_DS_NO_CONNECTION_ACTIVE) { + /* Try and login to device */ + DEBUG2(printk("scsi%ld: %s: Login to DDB[%d]\n", + ha->host_no, __func__, fw_ddb_index)); + err_code = ((conn_err & 0x00ff0000) >> 16); + if (err_code == 0x1c || err_code == 0x06) { + DEBUG2(printk("scsi%ld: %s send target " + "completed " + "or access denied failure\n", + ha->host_no, __func__)); + } else + qla4xxx_set_ddb_entry(ha, fw_ddb_index, 0); + } + + if (ddb_state != DDB_DS_SESSION_ACTIVE) + goto next_one; + /* + * if fw_ddb with session active state found, + * add to ddb_list + */ + DEBUG2(printk("scsi%ld: %s: DDB[%d] added to list\n", + ha->host_no, __func__, fw_ddb_index)); + + /* Add DDB to internal our ddb list. */ + ddb_entry = qla4xxx_get_ddb_entry(ha, fw_ddb_index); + if (ddb_entry == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate memory " + "for device at fw_ddb_index %d\n", + ha->host_no, __func__, fw_ddb_index)); + return QLA_ERROR; + } + /* Fill in the device structure */ + if (qla4xxx_update_ddb_entry(ha, ddb_entry, fw_ddb_index) == + QLA_ERROR) { + ha->fw_ddb_index_map[fw_ddb_index] = + (struct ddb_entry *)INVALID_ENTRY; + + + DEBUG2(printk("scsi%ld: %s: update_ddb_entry failed " + "for fw_ddb_index %d.\n", + ha->host_no, __func__, fw_ddb_index)); + return QLA_ERROR; + } + +next_one: + /* We know we've reached the last device when + * next_fw_ddb_index is 0 */ + if (next_fw_ddb_index == 0) + break; + } + + dev_info(&ha->pdev->dev, "DDB list done..\n"); + + return status; +} + +struct qla4_relog_scan { + int halt_wait; + uint32_t conn_err; + uint32_t err_code; + uint32_t fw_ddb_index; + uint32_t next_fw_ddb_index; + uint32_t fw_ddb_device_state; +}; + +static int qla4_test_rdy(struct scsi_qla_host *ha, struct qla4_relog_scan *rs) +{ + struct ddb_entry *ddb_entry; + + /* + * Don't want to do a relogin if connection + * error is 0x1c. + */ + rs->err_code = ((rs->conn_err & 0x00ff0000) >> 16); + if (rs->err_code == 0x1c || rs->err_code == 0x06) { + DEBUG2(printk( + "scsi%ld: %s send target" + " completed or " + "access denied failure\n", + ha->host_no, __func__)); + } else { + /* We either have a device that is in + * the process of relogging in or a + * device that is waiting to be + * relogged in */ + rs->halt_wait = 0; + + ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, + rs->fw_ddb_index); + if (ddb_entry == NULL) + return QLA_ERROR; + + if (ddb_entry->dev_scan_wait_to_start_relogin != 0 + && time_after_eq(jiffies, + ddb_entry-> + dev_scan_wait_to_start_relogin)) + { + ddb_entry->dev_scan_wait_to_start_relogin = 0; + qla4xxx_set_ddb_entry(ha, rs->fw_ddb_index, 0); + } + } + return QLA_SUCCESS; +} + +static int qla4_scan_for_relogin(struct scsi_qla_host *ha, + struct qla4_relog_scan *rs) +{ + int error; + + /* scan for relogins + * ----------------- */ + for (rs->fw_ddb_index = 0; rs->fw_ddb_index < MAX_DDB_ENTRIES; + rs->fw_ddb_index = rs->next_fw_ddb_index) { + if (qla4xxx_get_fwddb_entry(ha, rs->fw_ddb_index, NULL, 0, + NULL, &rs->next_fw_ddb_index, + &rs->fw_ddb_device_state, + &rs->conn_err, NULL, NULL) + == QLA_ERROR) + return QLA_ERROR; + + if (rs->fw_ddb_device_state == DDB_DS_LOGIN_IN_PROCESS) + rs->halt_wait = 0; + + if (rs->fw_ddb_device_state == DDB_DS_SESSION_FAILED || + rs->fw_ddb_device_state == DDB_DS_NO_CONNECTION_ACTIVE) { + error = qla4_test_rdy(ha, rs); + if (error) + return error; + } + + /* We know we've reached the last device when + * next_fw_ddb_index is 0 */ + if (rs->next_fw_ddb_index == 0) + break; + } + return QLA_SUCCESS; +} + +/** + * qla4xxx_devices_ready - wait for target devices to be logged in + * @ha: pointer to adapter structure + * + * This routine waits up to ql4xdiscoverywait seconds + * F/W database during driver load time. + **/ +static int qla4xxx_devices_ready(struct scsi_qla_host *ha) +{ + int error; + unsigned long discovery_wtime; + struct qla4_relog_scan rs; + + discovery_wtime = jiffies + (ql4xdiscoverywait * HZ); + + DEBUG(printk("Waiting (%d) for devices ...\n", ql4xdiscoverywait)); + do { + /* poll for AEN. */ + qla4xxx_get_firmware_state(ha); + if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) { + /* Set time-between-relogin timer */ + qla4xxx_process_aen(ha, RELOGIN_DDB_CHANGED_AENS); + } + + /* if no relogins active or needed, halt discvery wait */ + rs.halt_wait = 1; + + error = qla4_scan_for_relogin(ha, &rs); + + if (rs.halt_wait) { + DEBUG2(printk("scsi%ld: %s: Delay halted. Devices " + "Ready.\n", ha->host_no, __func__)); + return QLA_SUCCESS; + } + + msleep(2000); + } while (!time_after_eq(jiffies, discovery_wtime)); + + DEBUG3(qla4xxx_get_conn_event_log(ha)); + + return QLA_SUCCESS; +} + +static void qla4xxx_flush_AENS(struct scsi_qla_host *ha) +{ + unsigned long wtime; + + /* Flush the 0x8014 AEN from the firmware as a result of + * Auto connect. We are basically doing get_firmware_ddb() + * to determine whether we need to log back in or not. + * Trying to do a set ddb before we have processed 0x8014 + * will result in another set_ddb() for the same ddb. In other + * words there will be stale entries in the aen_q. + */ + wtime = jiffies + (2 * HZ); + do { + if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) + if (ha->firmware_state & (BIT_2 | BIT_0)) + return; + + if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) + qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); + + msleep(1000); + } while (!time_after_eq(jiffies, wtime)); + +} + +static int qla4xxx_initialize_ddb_list(struct scsi_qla_host *ha) +{ + uint16_t fw_ddb_index; + int status = QLA_SUCCESS; + + /* free the ddb list if is not empty */ + if (!list_empty(&ha->ddb_list)) + qla4xxx_free_ddb_list(ha); + + for (fw_ddb_index = 0; fw_ddb_index < MAX_DDB_ENTRIES; fw_ddb_index++) + ha->fw_ddb_index_map[fw_ddb_index] = + (struct ddb_entry *)INVALID_ENTRY; + + ha->tot_ddbs = 0; + + qla4xxx_flush_AENS(ha); + + /* + * First perform device discovery for active + * fw ddb indexes and build + * ddb list. + */ + if ((status = qla4xxx_build_ddb_list(ha)) == QLA_ERROR) + return status; + + /* Wait for an AEN */ + qla4xxx_devices_ready(ha); + + /* + * Targets can come online after the inital discovery, so processing + * the aens here will catch them. + */ + if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) + qla4xxx_process_aen(ha, PROCESS_ALL_AENS); + + return status; +} + +/** + * qla4xxx_update_ddb_list - update the driver ddb list + * @ha: pointer to host adapter structure. + * + * This routine obtains device information from the F/W database after + * firmware or adapter resets. The device table is preserved. + **/ +int qla4xxx_reinitialize_ddb_list(struct scsi_qla_host *ha) +{ + int status = QLA_SUCCESS; + struct ddb_entry *ddb_entry, *detemp; + + /* Update the device information for all devices. */ + list_for_each_entry_safe(ddb_entry, detemp, &ha->ddb_list, list) { + qla4xxx_update_ddb_entry(ha, ddb_entry, + ddb_entry->fw_ddb_index); + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) { + atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); + DEBUG2(printk ("scsi%ld: %s: ddb index [%d] marked " + "ONLINE\n", ha->host_no, __func__, + ddb_entry->fw_ddb_index)); + } else if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, ddb_entry); + } + return status; +} + +/** + * qla4xxx_relogin_device - re-establish session + * @ha: Pointer to host adapter structure. + * @ddb_entry: Pointer to device database entry + * + * This routine does a session relogin with the specified device. + * The ddb entry must be assigned prior to making this call. + **/ +int qla4xxx_relogin_device(struct scsi_qla_host *ha, + struct ddb_entry * ddb_entry) +{ + uint16_t relogin_timer; + + relogin_timer = max(ddb_entry->default_relogin_timeout, + (uint16_t)RELOGIN_TOV); + atomic_set(&ddb_entry->relogin_timer, relogin_timer); + + DEBUG2(printk("scsi%ld: Relogin index [%d]. TOV=%d\n", ha->host_no, + ddb_entry->fw_ddb_index, relogin_timer)); + + qla4xxx_set_ddb_entry(ha, ddb_entry->fw_ddb_index, 0); + + return QLA_SUCCESS; +} + +/** + * qla4010_get_topcat_presence - check if it is QLA4040 TopCat Chip + * @ha: Pointer to host adapter structure. + * + **/ +static int qla4010_get_topcat_presence(struct scsi_qla_host *ha) +{ + unsigned long flags; + uint16_t topcat; + + if (ql4xxx_lock_nvram(ha) != QLA_SUCCESS) + return QLA_ERROR; + spin_lock_irqsave(&ha->hardware_lock, flags); + topcat = rd_nvram_word(ha, offsetof(struct eeprom_data, + isp4010.topcat)); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if ((topcat & TOPCAT_MASK) == TOPCAT_PRESENT) + set_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags); + else + clear_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags); + ql4xxx_unlock_nvram(ha); + return QLA_SUCCESS; +} + + +static int qla4xxx_config_nvram(struct scsi_qla_host *ha) +{ + unsigned long flags; + union external_hw_config_reg extHwConfig; + + DEBUG2(printk("scsi%ld: %s: Get EEProm parameters \n", ha->host_no, + __func__)); + if (ql4xxx_lock_flash(ha) != QLA_SUCCESS) + return (QLA_ERROR); + if (ql4xxx_lock_nvram(ha) != QLA_SUCCESS) { + ql4xxx_unlock_flash(ha); + return (QLA_ERROR); + } + + /* Get EEPRom Parameters from NVRAM and validate */ + dev_info(&ha->pdev->dev, "Configuring NVRAM ...\n"); + if (qla4xxx_is_nvram_configuration_valid(ha) == QLA_SUCCESS) { + spin_lock_irqsave(&ha->hardware_lock, flags); + extHwConfig.Asuint32_t = + rd_nvram_word(ha, eeprom_ext_hw_conf_offset(ha)); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } else { + /* + * QLogic adapters should always have a valid NVRAM. + * If not valid, do not load. + */ + dev_warn(&ha->pdev->dev, + "scsi%ld: %s: EEProm checksum invalid. " + "Please update your EEPROM\n", ha->host_no, + __func__); + + /* set defaults */ + if (is_qla4010(ha)) + extHwConfig.Asuint32_t = 0x1912; + else if (is_qla4022(ha)) + extHwConfig.Asuint32_t = 0x0023; + } + DEBUG(printk("scsi%ld: %s: Setting extHwConfig to 0xFFFF%04x\n", + ha->host_no, __func__, extHwConfig.Asuint32_t)); + + spin_lock_irqsave(&ha->hardware_lock, flags); + writel((0xFFFF << 16) | extHwConfig.Asuint32_t, isp_ext_hw_conf(ha)); + readl(isp_ext_hw_conf(ha)); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + ql4xxx_unlock_nvram(ha); + ql4xxx_unlock_flash(ha); + + return (QLA_SUCCESS); +} + +static void qla4x00_pci_config(struct scsi_qla_host *ha) +{ + uint16_t w, mwi; + + dev_info(&ha->pdev->dev, "Configuring PCI space...\n"); + + pci_set_master(ha->pdev); + mwi = 0; + if (pci_set_mwi(ha->pdev)) + mwi = PCI_COMMAND_INVALIDATE; + /* + * We want to respect framework's setting of PCI configuration space + * command register and also want to make sure that all bits of + * interest to us are properly set in command register. + */ + pci_read_config_word(ha->pdev, PCI_COMMAND, &w); + w |= mwi | (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); + w &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(ha->pdev, PCI_COMMAND, w); +} + +static int qla4xxx_start_firmware_from_flash(struct scsi_qla_host *ha) +{ + int status = QLA_ERROR; + uint32_t max_wait_time; + unsigned long flags; + uint32_t mbox_status; + + dev_info(&ha->pdev->dev, "Starting firmware ...\n"); + + /* + * Start firmware from flash ROM + * + * WORKAROUND: Stuff a non-constant value that the firmware can + * use as a seed for a random number generator in MB7 prior to + * setting BOOT_ENABLE. Fixes problem where the TCP + * connections use the same TCP ports after each reboot, + * causing some connections to not get re-established. + */ + DEBUG(printk("scsi%d: %s: Start firmware from flash ROM\n", + ha->host_no, __func__)); + + spin_lock_irqsave(&ha->hardware_lock, flags); + writel(jiffies, &ha->reg->mailbox[7]); + if (is_qla4022(ha)) + writel(set_rmask(NVR_WRITE_ENABLE), + &ha->reg->u1.isp4022.nvram); + + writel(set_rmask(CSR_BOOT_ENABLE), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait for firmware to come UP. */ + max_wait_time = FIRMWARE_UP_TOV * 4; + do { + uint32_t ctrl_status; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ctrl_status = readw(&ha->reg->ctrl_status); + mbox_status = readw(&ha->reg->mailbox[0]); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (ctrl_status & set_rmask(CSR_SCSI_PROCESSOR_INTR)) + break; + if (mbox_status == MBOX_STS_COMMAND_COMPLETE) + break; + + DEBUG2(printk("scsi%ld: %s: Waiting for boot firmware to " + "complete... ctrl_sts=0x%x, remaining=%d\n", + ha->host_no, __func__, ctrl_status, + max_wait_time)); + + msleep(250); + } while ((max_wait_time--)); + + if (mbox_status == MBOX_STS_COMMAND_COMPLETE) { + DEBUG(printk("scsi%ld: %s: Firmware has started\n", + ha->host_no, __func__)); + + spin_lock_irqsave(&ha->hardware_lock, flags); + writel(set_rmask(CSR_SCSI_PROCESSOR_INTR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + status = QLA_SUCCESS; + } else { + printk(KERN_INFO "scsi%ld: %s: Boot firmware failed " + "- mbox status 0x%x\n", ha->host_no, __func__, + mbox_status); + status = QLA_ERROR; + } + return status; +} + +static int ql4xxx_lock_drvr_wait(struct scsi_qla_host *a) +{ +#define QL4_LOCK_DRVR_WAIT 300 +#define QL4_LOCK_DRVR_SLEEP 100 + + int drvr_wait = QL4_LOCK_DRVR_WAIT; + while (drvr_wait) { + if (ql4xxx_lock_drvr(a) == 0) { + msleep(QL4_LOCK_DRVR_SLEEP); + if (drvr_wait) { + DEBUG2(printk("scsi%ld: %s: Waiting for " + "Global Init Semaphore...n", + a->host_no, + __func__)); + } + drvr_wait -= QL4_LOCK_DRVR_SLEEP; + } else { + DEBUG2(printk("scsi%ld: %s: Global Init Semaphore " + "acquired.n", a->host_no, __func__)); + return QLA_SUCCESS; + } + } + return QLA_ERROR; +} + +/** + * qla4xxx_start_firmware - starts qla4xxx firmware + * @ha: Pointer to host adapter structure. + * + * This routine performs the neccessary steps to start the firmware for + * the QLA4010 adapter. + **/ +static int qla4xxx_start_firmware(struct scsi_qla_host *ha) +{ + unsigned long flags = 0; + uint32_t mbox_status; + int status = QLA_ERROR; + int soft_reset = 1; + int config_chip = 0; + + if (is_qla4010(ha)){ + if (qla4010_get_topcat_presence(ha) != QLA_SUCCESS) + return QLA_ERROR; + } + + if (is_qla4022(ha)) + ql4xxx_set_mac_number(ha); + + if (ql4xxx_lock_drvr_wait(ha) != QLA_SUCCESS) + return QLA_ERROR; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + DEBUG2(printk("scsi%ld: %s: port_ctrl = 0x%08X\n", ha->host_no, + __func__, readw(isp_port_ctrl(ha)))); + DEBUG(printk("scsi%ld: %s: port_status = 0x%08X\n", ha->host_no, + __func__, readw(isp_port_status(ha)))); + + /* Is Hardware already initialized? */ + if ((readw(isp_port_ctrl(ha)) & 0x8000) != 0) { + DEBUG(printk("scsi%ld: %s: Hardware has already been " + "initialized\n", ha->host_no, __func__)); + + /* Receive firmware boot acknowledgement */ + mbox_status = readw(&ha->reg->mailbox[0]); + + DEBUG2(printk("scsi%ld: %s: H/W Config complete - mbox[0]= " + "0x%x\n", ha->host_no, __func__, mbox_status)); + + /* Is firmware already booted? */ + if (mbox_status == 0) { + /* F/W not running, must be config by net driver */ + config_chip = 1; + soft_reset = 0; + } else { + writel(set_rmask(CSR_SCSI_PROCESSOR_INTR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (qla4xxx_get_firmware_state(ha) == QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Get firmware " + "state -- state = 0x%x\n", + ha->host_no, + __func__, ha->firmware_state)); + /* F/W is running */ + if (ha->firmware_state & + FW_STATE_CONFIG_WAIT) { + DEBUG2(printk("scsi%ld: %s: Firmware " + "in known state -- " + "config and " + "boot, state = 0x%x\n", + ha->host_no, __func__, + ha->firmware_state)); + config_chip = 1; + soft_reset = 0; + } + } else { + DEBUG2(printk("scsi%ld: %s: Firmware in " + "unknown state -- resetting," + " state = " + "0x%x\n", ha->host_no, __func__, + ha->firmware_state)); + } + spin_lock_irqsave(&ha->hardware_lock, flags); + } + } else { + DEBUG(printk("scsi%ld: %s: H/W initialization hasn't been " + "started - resetting\n", ha->host_no, __func__)); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + DEBUG(printk("scsi%ld: %s: Flags soft_rest=%d, config= %d\n ", + ha->host_no, __func__, soft_reset, config_chip)); + if (soft_reset) { + DEBUG(printk("scsi%ld: %s: Issue Soft Reset\n", ha->host_no, + __func__)); + status = qla4xxx_soft_reset(ha); + if (status == QLA_ERROR) { + DEBUG(printk("scsi%d: %s: Soft Reset failed!\n", + ha->host_no, __func__)); + ql4xxx_unlock_drvr(ha); + return QLA_ERROR; + } + config_chip = 1; + + /* Reset clears the semaphore, so aquire again */ + if (ql4xxx_lock_drvr_wait(ha) != QLA_SUCCESS) + return QLA_ERROR; + } + + if (config_chip) { + if ((status = qla4xxx_config_nvram(ha)) == QLA_SUCCESS) + status = qla4xxx_start_firmware_from_flash(ha); + } + + ql4xxx_unlock_drvr(ha); + if (status == QLA_SUCCESS) { + qla4xxx_get_fw_version(ha); + if (test_and_clear_bit(AF_GET_CRASH_RECORD, &ha->flags)) + qla4xxx_get_crash_record(ha); + } else { + DEBUG(printk("scsi%ld: %s: Firmware has NOT started\n", + ha->host_no, __func__)); + } + return status; +} + + +/** + * qla4xxx_initialize_adapter - initiailizes hba + * @ha: Pointer to host adapter structure. + * @renew_ddb_list: Indicates what to do with the adapter's ddb list + * after adapter recovery has completed. + * 0=preserve ddb list, 1=destroy and rebuild ddb list + * + * This routine parforms all of the steps necessary to initialize the adapter. + * + **/ +int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, + uint8_t renew_ddb_list) +{ + int status = QLA_ERROR; + int8_t ip_address[IP_ADDR_LEN] = {0} ; + + ha->eeprom_cmd_data = 0; + + qla4x00_pci_config(ha); + + qla4xxx_disable_intrs(ha); + + /* Initialize the Host adapter request/response queues and firmware */ + if (qla4xxx_start_firmware(ha) == QLA_ERROR) + return status; + + if (qla4xxx_validate_mac_address(ha) == QLA_ERROR) + return status; + + if (qla4xxx_init_local_data(ha) == QLA_ERROR) + return status; + + status = qla4xxx_init_firmware(ha); + if (status == QLA_ERROR) + return status; + + /* + * FW is waiting to get an IP address from DHCP server: Skip building + * the ddb_list and wait for DHCP lease acquired aen to come in + * followed by 0x8014 aen" to trigger the tgt discovery process. + */ + if (ha->firmware_state & FW_STATE_DHCP_IN_PROGRESS) + return status; + + /* Skip device discovery if ip and subnet is zero */ + if (memcmp(ha->ip_address, ip_address, IP_ADDR_LEN) == 0 || + memcmp(ha->subnet_mask, ip_address, IP_ADDR_LEN) == 0) + return status; + + if (renew_ddb_list == PRESERVE_DDB_LIST) { + /* + * We want to preserve lun states (i.e. suspended, etc.) + * for recovery initiated by the driver. So just update + * the device states for the existing ddb_list. + */ + qla4xxx_reinitialize_ddb_list(ha); + } else if (renew_ddb_list == REBUILD_DDB_LIST) { + /* + * We want to build the ddb_list from scratch during + * driver initialization and recovery initiated by the + * INT_HBA_RESET IOCTL. + */ + status = qla4xxx_initialize_ddb_list(ha); + if (status == QLA_ERROR) { + DEBUG2(printk("%s(%ld) Error occurred during build" + "ddb list\n", __func__, ha->host_no)); + goto exit_init_hba; + } + + } + if (!ha->tot_ddbs) { + DEBUG2(printk("scsi%ld: Failed to initialize devices or none " + "present in Firmware device database\n", + ha->host_no)); + } + + exit_init_hba: + return status; + +} + +/** + * qla4xxx_add_device_dynamically - ddb addition due to an AEN + * @ha: Pointer to host adapter structure. + * @fw_ddb_index: Firmware's device database index + * + * This routine processes adds a device as a result of an 8014h AEN. + **/ +static void qla4xxx_add_device_dynamically(struct scsi_qla_host *ha, + uint32_t fw_ddb_index) +{ + struct ddb_entry * ddb_entry; + + /* First allocate a device structure */ + ddb_entry = qla4xxx_get_ddb_entry(ha, fw_ddb_index); + if (ddb_entry == NULL) { + DEBUG2(printk(KERN_WARNING + "scsi%ld: Unable to allocate memory to add " + "fw_ddb_index %d\n", ha->host_no, fw_ddb_index)); + return; + } + + if (qla4xxx_update_ddb_entry(ha, ddb_entry, fw_ddb_index) == + QLA_ERROR) { + ha->fw_ddb_index_map[fw_ddb_index] = + (struct ddb_entry *)INVALID_ENTRY; + DEBUG2(printk(KERN_WARNING + "scsi%ld: failed to add new device at index " + "[%d]\n Unable to retrieve fw ddb entry\n", + ha->host_no, fw_ddb_index)); + qla4xxx_free_ddb(ha, ddb_entry); + return; + } + + if (qla4xxx_add_sess(ddb_entry)) { + DEBUG2(printk(KERN_WARNING + "scsi%ld: failed to add new device at index " + "[%d]\n Unable to add connection and session\n", + ha->host_no, fw_ddb_index)); + qla4xxx_free_ddb(ha, ddb_entry); + } +} + +/** + * qla4xxx_process_ddb_changed - process ddb state change + * @ha - Pointer to host adapter structure. + * @fw_ddb_index - Firmware's device database index + * @state - Device state + * + * This routine processes a Decive Database Changed AEN Event. + **/ +int qla4xxx_process_ddb_changed(struct scsi_qla_host *ha, + uint32_t fw_ddb_index, uint32_t state) +{ + struct ddb_entry * ddb_entry; + uint32_t old_fw_ddb_device_state; + + /* check for out of range index */ + if (fw_ddb_index >= MAX_DDB_ENTRIES) + return QLA_ERROR; + + /* Get the corresponging ddb entry */ + ddb_entry = qla4xxx_lookup_ddb_by_fw_index(ha, fw_ddb_index); + /* Device does not currently exist in our database. */ + if (ddb_entry == NULL) { + if (state == DDB_DS_SESSION_ACTIVE) + qla4xxx_add_device_dynamically(ha, fw_ddb_index); + return QLA_SUCCESS; + } + + /* Device already exists in our database. */ + old_fw_ddb_device_state = ddb_entry->fw_ddb_device_state; + DEBUG2(printk("scsi%ld: %s DDB - old state= 0x%x, new state=0x%x for " + "index [%d]\n", ha->host_no, __func__, + ddb_entry->fw_ddb_device_state, state, fw_ddb_index)); + if (old_fw_ddb_device_state == state && + state == DDB_DS_SESSION_ACTIVE) { + /* Do nothing, state not changed. */ + return QLA_SUCCESS; + } + + ddb_entry->fw_ddb_device_state = state; + /* Device is back online. */ + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) { + atomic_set(&ddb_entry->port_down_timer, + ha->port_down_retry_count); + atomic_set(&ddb_entry->state, DDB_STATE_ONLINE); + atomic_set(&ddb_entry->relogin_retry_count, 0); + atomic_set(&ddb_entry->relogin_timer, 0); + clear_bit(DF_RELOGIN, &ddb_entry->flags); + clear_bit(DF_NO_RELOGIN, &ddb_entry->flags); + iscsi_if_create_session_done(ddb_entry->conn); + /* + * Change the lun state to READY in case the lun TIMEOUT before + * the device came back. + */ + } else { + /* Device went away, try to relogin. */ + /* Mark device missing */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, ddb_entry); + /* + * Relogin if device state changed to a not active state. + * However, do not relogin if this aen is a result of an IOCTL + * logout (DF_NO_RELOGIN) or if this is a discovered device. + */ + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_FAILED && + !test_bit(DF_RELOGIN, &ddb_entry->flags) && + !test_bit(DF_NO_RELOGIN, &ddb_entry->flags) && + !test_bit(DF_ISNS_DISCOVERED, &ddb_entry->flags)) { + /* + * This triggers a relogin. After the relogin_timer + * expires, the relogin gets scheduled. We must wait a + * minimum amount of time since receiving an 0x8014 AEN + * with failed device_state or a logout response before + * we can issue another relogin. + */ + /* Firmware padds this timeout: (time2wait +1). + * Driver retry to login should be longer than F/W. + * Otherwise F/W will fail + * set_ddb() mbx cmd with 0x4005 since it still + * counting down its time2wait. + */ + atomic_set(&ddb_entry->relogin_timer, 0); + atomic_set(&ddb_entry->retry_relogin_timer, + ddb_entry->default_time2wait + 4); + } + } + + return QLA_SUCCESS; +} + diff --git a/drivers/scsi/qla4xxx/ql4_inline.h b/drivers/scsi/qla4xxx/ql4_inline.h new file mode 100644 index 000000000000..0d61797af7da --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_inline.h @@ -0,0 +1,84 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +/* + * + * qla4xxx_lookup_ddb_by_fw_index + * This routine locates a device handle given the firmware device + * database index. If device doesn't exist, returns NULL. + * + * Input: + * ha - Pointer to host adapter structure. + * fw_ddb_index - Firmware's device database index + * + * Returns: + * Pointer to the corresponding internal device database structure + */ +static inline struct ddb_entry * +qla4xxx_lookup_ddb_by_fw_index(struct scsi_qla_host *ha, uint32_t fw_ddb_index) +{ + struct ddb_entry *ddb_entry = NULL; + + if ((fw_ddb_index < MAX_DDB_ENTRIES) && + (ha->fw_ddb_index_map[fw_ddb_index] != + (struct ddb_entry *) INVALID_ENTRY)) { + ddb_entry = ha->fw_ddb_index_map[fw_ddb_index]; + } + + DEBUG3(printk("scsi%d: %s: index [%d], ddb_entry = %p\n", + ha->host_no, __func__, fw_ddb_index, ddb_entry)); + + return ddb_entry; +} + +static inline void +__qla4xxx_enable_intrs(struct scsi_qla_host *ha) +{ + if (is_qla4022(ha)) { + writel(set_rmask(IMR_SCSI_INTR_ENABLE), + &ha->reg->u1.isp4022.intr_mask); + readl(&ha->reg->u1.isp4022.intr_mask); + } else { + writel(set_rmask(CSR_SCSI_INTR_ENABLE), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + } + set_bit(AF_INTERRUPTS_ON, &ha->flags); +} + +static inline void +__qla4xxx_disable_intrs(struct scsi_qla_host *ha) +{ + if (is_qla4022(ha)) { + writel(clr_rmask(IMR_SCSI_INTR_ENABLE), + &ha->reg->u1.isp4022.intr_mask); + readl(&ha->reg->u1.isp4022.intr_mask); + } else { + writel(clr_rmask(CSR_SCSI_INTR_ENABLE), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + } + clear_bit(AF_INTERRUPTS_ON, &ha->flags); +} + +static inline void +qla4xxx_enable_intrs(struct scsi_qla_host *ha) +{ + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + __qla4xxx_enable_intrs(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static inline void +qla4xxx_disable_intrs(struct scsi_qla_host *ha) +{ + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + __qla4xxx_disable_intrs(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c new file mode 100644 index 000000000000..c0a254b89a30 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_iocb.c @@ -0,0 +1,368 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" + +#include <scsi/scsi_tcq.h> + +/** + * qla4xxx_get_req_pkt - returns a valid entry in request queue. + * @ha: Pointer to host adapter structure. + * @queue_entry: Pointer to pointer to queue entry structure + * + * This routine performs the following tasks: + * - returns the current request_in pointer (if queue not full) + * - advances the request_in pointer + * - checks for queue full + **/ +int qla4xxx_get_req_pkt(struct scsi_qla_host *ha, + struct queue_entry **queue_entry) +{ + uint16_t request_in; + uint8_t status = QLA_SUCCESS; + + *queue_entry = ha->request_ptr; + + /* get the latest request_in and request_out index */ + request_in = ha->request_in; + ha->request_out = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out); + + /* Advance request queue pointer and check for queue full */ + if (request_in == (REQUEST_QUEUE_DEPTH - 1)) { + request_in = 0; + ha->request_ptr = ha->request_ring; + } else { + request_in++; + ha->request_ptr++; + } + + /* request queue is full, try again later */ + if ((ha->iocb_cnt + 1) >= ha->iocb_hiwat) { + /* restore request pointer */ + ha->request_ptr = *queue_entry; + status = QLA_ERROR; + } else { + ha->request_in = request_in; + memset(*queue_entry, 0, sizeof(**queue_entry)); + } + + return status; +} + +/** + * qla4xxx_send_marker_iocb - issues marker iocb to HBA + * @ha: Pointer to host adapter structure. + * @ddb_entry: Pointer to device database entry + * @lun: SCSI LUN + * @marker_type: marker identifier + * + * This routine issues a marker IOCB. + **/ +int qla4xxx_send_marker_iocb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, int lun) +{ + struct marker_entry *marker_entry; + unsigned long flags = 0; + uint8_t status = QLA_SUCCESS; + + /* Acquire hardware specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Get pointer to the queue entry for the marker */ + if (qla4xxx_get_req_pkt(ha, (struct queue_entry **) &marker_entry) != + QLA_SUCCESS) { + status = QLA_ERROR; + goto exit_send_marker; + } + + /* Put the marker in the request queue */ + marker_entry->hdr.entryType = ET_MARKER; + marker_entry->hdr.entryCount = 1; + marker_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index); + marker_entry->modifier = cpu_to_le16(MM_LUN_RESET); + int_to_scsilun(lun, &marker_entry->lun); + wmb(); + + /* Tell ISP it's got a new I/O request */ + writel(ha->request_in, &ha->reg->req_q_in); + readl(&ha->reg->req_q_in); + +exit_send_marker: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return status; +} + +struct continuation_t1_entry* qla4xxx_alloc_cont_entry( + struct scsi_qla_host *ha) +{ + struct continuation_t1_entry *cont_entry; + + cont_entry = (struct continuation_t1_entry *)ha->request_ptr; + + /* Advance request queue pointer */ + if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1)) { + ha->request_in = 0; + ha->request_ptr = ha->request_ring; + } else { + ha->request_in++; + ha->request_ptr++; + } + + /* Load packet defaults */ + cont_entry->hdr.entryType = ET_CONTINUE; + cont_entry->hdr.entryCount = 1; + cont_entry->hdr.systemDefined = (uint8_t) cpu_to_le16(ha->request_in); + + return cont_entry; +} + +uint16_t qla4xxx_calc_request_entries(uint16_t dsds) +{ + uint16_t iocbs; + + iocbs = 1; + if (dsds > COMMAND_SEG) { + iocbs += (dsds - COMMAND_SEG) / CONTINUE_SEG; + if ((dsds - COMMAND_SEG) % CONTINUE_SEG) + iocbs++; + } + return iocbs; +} + +void qla4xxx_build_scsi_iocbs(struct srb *srb, + struct command_t3_entry *cmd_entry, + uint16_t tot_dsds) +{ + struct scsi_qla_host *ha; + uint16_t avail_dsds; + struct data_seg_a64 *cur_dsd; + struct scsi_cmnd *cmd; + + cmd = srb->cmd; + ha = srb->ha; + + if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) { + /* No data being transferred */ + cmd_entry->ttlByteCnt = __constant_cpu_to_le32(0); + return; + } + + avail_dsds = COMMAND_SEG; + cur_dsd = (struct data_seg_a64 *) & (cmd_entry->dataseg[0]); + + /* Load data segments */ + if (cmd->use_sg) { + struct scatterlist *cur_seg; + struct scatterlist *end_seg; + + cur_seg = (struct scatterlist *)cmd->request_buffer; + end_seg = cur_seg + tot_dsds; + while (cur_seg < end_seg) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + struct continuation_t1_entry *cont_entry; + + cont_entry = qla4xxx_alloc_cont_entry(ha); + cur_dsd = + (struct data_seg_a64 *) + &cont_entry->dataseg[0]; + avail_dsds = CONTINUE_SEG; + } + + sle_dma = sg_dma_address(cur_seg); + cur_dsd->base.addrLow = cpu_to_le32(LSDW(sle_dma)); + cur_dsd->base.addrHigh = cpu_to_le32(MSDW(sle_dma)); + cur_dsd->count = cpu_to_le32(sg_dma_len(cur_seg)); + avail_dsds--; + + cur_dsd++; + cur_seg++; + } + } else { + cur_dsd->base.addrLow = cpu_to_le32(LSDW(srb->dma_handle)); + cur_dsd->base.addrHigh = cpu_to_le32(MSDW(srb->dma_handle)); + cur_dsd->count = cpu_to_le32(cmd->request_bufflen); + } +} + +/** + * qla4xxx_send_command_to_isp - issues command to HBA + * @ha: pointer to host adapter structure. + * @srb: pointer to SCSI Request Block to be sent to ISP + * + * This routine is called by qla4xxx_queuecommand to build an ISP + * command and pass it to the ISP for execution. + **/ +int qla4xxx_send_command_to_isp(struct scsi_qla_host *ha, struct srb * srb) +{ + struct scsi_cmnd *cmd = srb->cmd; + struct ddb_entry *ddb_entry; + struct command_t3_entry *cmd_entry; + struct scatterlist *sg = NULL; + + uint16_t tot_dsds; + uint16_t req_cnt; + + unsigned long flags; + uint16_t cnt; + uint32_t index; + char tag[2]; + + /* Get real lun and adapter */ + ddb_entry = srb->ddb; + + /* Send marker(s) if needed. */ + if (ha->marker_needed == 1) { + if (qla4xxx_send_marker_iocb(ha, ddb_entry, + cmd->device->lun) != QLA_SUCCESS) + return QLA_ERROR; + + ha->marker_needed = 0; + } + tot_dsds = 0; + + /* Acquire hardware specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + index = (uint32_t)cmd->request->tag; + + /* Calculate the number of request entries needed. */ + if (cmd->use_sg) { + sg = (struct scatterlist *)cmd->request_buffer; + tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg, + cmd->sc_data_direction); + if (tot_dsds == 0) + goto queuing_error; + } else if (cmd->request_bufflen) { + dma_addr_t req_dma; + + req_dma = pci_map_single(ha->pdev, cmd->request_buffer, + cmd->request_bufflen, + cmd->sc_data_direction); + if (dma_mapping_error(req_dma)) + goto queuing_error; + + srb->dma_handle = req_dma; + tot_dsds = 1; + } + req_cnt = qla4xxx_calc_request_entries(tot_dsds); + + if (ha->req_q_count < (req_cnt + 2)) { + cnt = (uint16_t) le32_to_cpu(ha->shadow_regs->req_q_out); + if (ha->request_in < cnt) + ha->req_q_count = cnt - ha->request_in; + else + ha->req_q_count = REQUEST_QUEUE_DEPTH - + (ha->request_in - cnt); + } + + if (ha->req_q_count < (req_cnt + 2)) + goto queuing_error; + + /* total iocbs active */ + if ((ha->iocb_cnt + req_cnt) >= REQUEST_QUEUE_DEPTH) + goto queuing_error; + + /* Build command packet */ + cmd_entry = (struct command_t3_entry *) ha->request_ptr; + memset(cmd_entry, 0, sizeof(struct command_t3_entry)); + cmd_entry->hdr.entryType = ET_COMMAND; + cmd_entry->handle = cpu_to_le32(index); + cmd_entry->target = cpu_to_le16(ddb_entry->fw_ddb_index); + cmd_entry->connection_id = cpu_to_le16(ddb_entry->connection_id); + + int_to_scsilun(cmd->device->lun, &cmd_entry->lun); + cmd_entry->cmdSeqNum = cpu_to_le32(ddb_entry->CmdSn); + cmd_entry->ttlByteCnt = cpu_to_le32(cmd->request_bufflen); + memcpy(cmd_entry->cdb, cmd->cmnd, cmd->cmd_len); + cmd_entry->dataSegCnt = cpu_to_le16(tot_dsds); + cmd_entry->hdr.entryCount = req_cnt; + + /* Set data transfer direction control flags + * NOTE: Look at data_direction bits iff there is data to be + * transferred, as the data direction bit is sometimed filled + * in when there is no data to be transferred */ + cmd_entry->control_flags = CF_NO_DATA; + if (cmd->request_bufflen) { + if (cmd->sc_data_direction == DMA_TO_DEVICE) + cmd_entry->control_flags = CF_WRITE; + else if (cmd->sc_data_direction == DMA_FROM_DEVICE) + cmd_entry->control_flags = CF_READ; + } + + /* Set tagged queueing control flags */ + cmd_entry->control_flags |= CF_SIMPLE_TAG; + if (scsi_populate_tag_msg(cmd, tag)) + switch (tag[0]) { + case MSG_HEAD_TAG: + cmd_entry->control_flags |= CF_HEAD_TAG; + break; + case MSG_ORDERED_TAG: + cmd_entry->control_flags |= CF_ORDERED_TAG; + break; + } + + + /* Advance request queue pointer */ + ha->request_in++; + if (ha->request_in == REQUEST_QUEUE_DEPTH) { + ha->request_in = 0; + ha->request_ptr = ha->request_ring; + } else + ha->request_ptr++; + + + qla4xxx_build_scsi_iocbs(srb, cmd_entry, tot_dsds); + wmb(); + + /* + * Check to see if adapter is online before placing request on + * request queue. If a reset occurs and a request is in the queue, + * the firmware will still attempt to process the request, retrieving + * garbage for pointers. + */ + if (!test_bit(AF_ONLINE, &ha->flags)) { + DEBUG2(printk("scsi%ld: %s: Adapter OFFLINE! " + "Do not issue command.\n", + ha->host_no, __func__)); + goto queuing_error; + } + + srb->cmd->host_scribble = (unsigned char *)srb; + + /* update counters */ + srb->state = SRB_ACTIVE_STATE; + srb->flags |= SRB_DMA_VALID; + + /* Track IOCB used */ + ha->iocb_cnt += req_cnt; + srb->iocb_cnt = req_cnt; + ha->req_q_count -= req_cnt; + + /* Debug print statements */ + writel(ha->request_in, &ha->reg->req_q_in); + readl(&ha->reg->req_q_in); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_SUCCESS; + +queuing_error: + + if (cmd->use_sg && tot_dsds) { + sg = (struct scatterlist *) cmd->request_buffer; + pci_unmap_sg(ha->pdev, sg, cmd->use_sg, + cmd->sc_data_direction); + } else if (tot_dsds) + pci_unmap_single(ha->pdev, srb->dma_handle, + cmd->request_bufflen, cmd->sc_data_direction); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return QLA_ERROR; +} + diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c new file mode 100644 index 000000000000..b584317608d1 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_isr.c @@ -0,0 +1,797 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" + +/** + * qla2x00_process_completed_request() - Process a Fast Post response. + * @ha: SCSI driver HA context + * @index: SRB index + **/ +static void qla4xxx_process_completed_request(struct scsi_qla_host *ha, + uint32_t index) +{ + struct srb *srb; + + srb = qla4xxx_del_from_active_array(ha, index); + if (srb) { + /* Save ISP completion status */ + srb->cmd->result = DID_OK << 16; + qla4xxx_srb_compl(ha, srb); + } else { + DEBUG2(printk("scsi%ld: Invalid ISP SCSI completion handle = " + "%d\n", ha->host_no, index)); + set_bit(DPC_RESET_HA, &ha->dpc_flags); + } +} + +/** + * qla4xxx_status_entry - processes status IOCBs + * @ha: Pointer to host adapter structure. + * @sts_entry: Pointer to status entry structure. + **/ +static void qla4xxx_status_entry(struct scsi_qla_host *ha, + struct status_entry *sts_entry) +{ + uint8_t scsi_status; + struct scsi_cmnd *cmd; + struct srb *srb; + struct ddb_entry *ddb_entry; + uint32_t residual; + uint16_t sensebytecnt; + + if (sts_entry->completionStatus == SCS_COMPLETE && + sts_entry->scsiStatus == 0) { + qla4xxx_process_completed_request(ha, + le32_to_cpu(sts_entry-> + handle)); + return; + } + + srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle)); + if (!srb) { + /* FIXMEdg: Don't we need to reset ISP in this case??? */ + DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Status Entry invalid " + "handle 0x%x, sp=%p. This cmd may have already " + "been completed.\n", ha->host_no, __func__, + le32_to_cpu(sts_entry->handle), srb)); + return; + } + + cmd = srb->cmd; + if (cmd == NULL) { + DEBUG2(printk("scsi%ld: %s: Command already returned back to " + "OS pkt->handle=%d srb=%p srb->state:%d\n", + ha->host_no, __func__, sts_entry->handle, + srb, srb->state)); + dev_warn(&ha->pdev->dev, "Command is NULL:" + " already returned to OS (srb=%p)\n", srb); + return; + } + + ddb_entry = srb->ddb; + if (ddb_entry == NULL) { + cmd->result = DID_NO_CONNECT << 16; + goto status_entry_exit; + } + + residual = le32_to_cpu(sts_entry->residualByteCnt); + + /* Translate ISP error to a Linux SCSI error. */ + scsi_status = sts_entry->scsiStatus; + switch (sts_entry->completionStatus) { + case SCS_COMPLETE: + if (scsi_status == 0) { + cmd->result = DID_OK << 16; + break; + } + + if (sts_entry->iscsiFlags & + (ISCSI_FLAG_RESIDUAL_OVER|ISCSI_FLAG_RESIDUAL_UNDER)) + cmd->resid = residual; + + cmd->result = DID_OK << 16 | scsi_status; + + if (scsi_status != SCSI_CHECK_CONDITION) + break; + + /* Copy Sense Data into sense buffer. */ + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); + + sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt); + if (sensebytecnt == 0) + break; + + memcpy(cmd->sense_buffer, sts_entry->senseData, + min(sensebytecnt, + (uint16_t) sizeof(cmd->sense_buffer))); + + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, " + "ASC/ASCQ = %02x/%02x\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, __func__, + sts_entry->senseData[2] & 0x0f, + sts_entry->senseData[12], + sts_entry->senseData[13])); + + srb->flags |= SRB_GOT_SENSE; + break; + + case SCS_INCOMPLETE: + /* Always set the status to DID_ERROR, since + * all conditions result in that status anyway */ + cmd->result = DID_ERROR << 16; + break; + + case SCS_RESET_OCCURRED: + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Device RESET occurred\n", + ha->host_no, cmd->device->channel, + cmd->device->id, cmd->device->lun, __func__)); + + cmd->result = DID_RESET << 16; + break; + + case SCS_ABORTED: + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Abort occurred\n", + ha->host_no, cmd->device->channel, + cmd->device->id, cmd->device->lun, __func__)); + + cmd->result = DID_RESET << 16; + break; + + case SCS_TIMEOUT: + DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: Timeout\n", + ha->host_no, cmd->device->channel, + cmd->device->id, cmd->device->lun)); + + cmd->result = DID_BUS_BUSY << 16; + + /* + * Mark device missing so that we won't continue to send + * I/O to this device. We should get a ddb state change + * AEN soon. + */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, ddb_entry); + break; + + case SCS_DATA_UNDERRUN: + case SCS_DATA_OVERRUN: + if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) { + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, " + "residual = 0x%x\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, __func__, residual)); + + cmd->result = DID_ERROR << 16; + break; + } + + if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) == 0) { + /* + * Firmware detected a SCSI transport underrun + * condition + */ + cmd->resid = residual; + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: UNDERRUN status " + "detected, xferlen = 0x%x, residual = " + "0x%x\n", + ha->host_no, cmd->device->channel, + cmd->device->id, + cmd->device->lun, __func__, + cmd->request_bufflen, + residual)); + } + + /* + * If there is scsi_status, it takes precedense over + * underflow condition. + */ + if (scsi_status != 0) { + cmd->result = DID_OK << 16 | scsi_status; + + if (scsi_status != SCSI_CHECK_CONDITION) + break; + + /* Copy Sense Data into sense buffer. */ + memset(cmd->sense_buffer, 0, + sizeof(cmd->sense_buffer)); + + sensebytecnt = + le16_to_cpu(sts_entry->senseDataByteCnt); + if (sensebytecnt == 0) + break; + + memcpy(cmd->sense_buffer, sts_entry->senseData, + min(sensebytecnt, + (uint16_t) sizeof(cmd->sense_buffer))); + + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, " + "ASC/ASCQ = %02x/%02x\n", ha->host_no, + cmd->device->channel, cmd->device->id, + cmd->device->lun, __func__, + sts_entry->senseData[2] & 0x0f, + sts_entry->senseData[12], + sts_entry->senseData[13])); + } else { + /* + * If RISC reports underrun and target does not + * report it then we must have a lost frame, so + * tell upper layer to retry it by reporting a + * bus busy. + */ + if ((sts_entry->iscsiFlags & + ISCSI_FLAG_RESIDUAL_UNDER) == 0) { + cmd->result = DID_BUS_BUSY << 16; + } else if ((cmd->request_bufflen - residual) < + cmd->underflow) { + /* + * Handle mid-layer underflow??? + * + * For kernels less than 2.4, the driver must + * return an error if an underflow is detected. + * For kernels equal-to and above 2.4, the + * mid-layer will appearantly handle the + * underflow by detecting the residual count -- + * unfortunately, we do not see where this is + * actually being done. In the interim, we + * will return DID_ERROR. + */ + DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " + "Mid-layer Data underrun, " + "xferlen = 0x%x, " + "residual = 0x%x\n", ha->host_no, + cmd->device->channel, + cmd->device->id, + cmd->device->lun, __func__, + cmd->request_bufflen, residual)); + + cmd->result = DID_ERROR << 16; + } else { + cmd->result = DID_OK << 16; + } + } + break; + + case SCS_DEVICE_LOGGED_OUT: + case SCS_DEVICE_UNAVAILABLE: + /* + * Mark device missing so that we won't continue to + * send I/O to this device. We should get a ddb + * state change AEN soon. + */ + if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE) + qla4xxx_mark_device_missing(ha, ddb_entry); + + cmd->result = DID_BUS_BUSY << 16; + break; + + case SCS_QUEUE_FULL: + /* + * SCSI Mid-Layer handles device queue full + */ + cmd->result = DID_OK << 16 | sts_entry->scsiStatus; + DEBUG2(printk("scsi%ld:%d:%d: %s: QUEUE FULL detected " + "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x," + " iResp=%02x\n", ha->host_no, cmd->device->id, + cmd->device->lun, __func__, + sts_entry->completionStatus, + sts_entry->scsiStatus, sts_entry->state_flags, + sts_entry->iscsiFlags, + sts_entry->iscsiResponse)); + break; + + default: + cmd->result = DID_ERROR << 16; + break; + } + +status_entry_exit: + + /* complete the request */ + srb->cc_stat = sts_entry->completionStatus; + qla4xxx_srb_compl(ha, srb); +} + +/** + * qla4xxx_process_response_queue - process response queue completions + * @ha: Pointer to host adapter structure. + * + * This routine process response queue completions in interrupt context. + * Hardware_lock locked upon entry + **/ +static void qla4xxx_process_response_queue(struct scsi_qla_host * ha) +{ + uint32_t count = 0; + struct srb *srb = NULL; + struct status_entry *sts_entry; + + /* Process all responses from response queue */ + while ((ha->response_in = + (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in)) != + ha->response_out) { + sts_entry = (struct status_entry *) ha->response_ptr; + count++; + + /* Advance pointers for next entry */ + if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1)) { + ha->response_out = 0; + ha->response_ptr = ha->response_ring; + } else { + ha->response_out++; + ha->response_ptr++; + } + + /* process entry */ + switch (sts_entry->hdr.entryType) { + case ET_STATUS: + /* + * Common status - Single completion posted in single + * IOSB. + */ + qla4xxx_status_entry(ha, sts_entry); + break; + + case ET_PASSTHRU_STATUS: + break; + + case ET_STATUS_CONTINUATION: + /* Just throw away the status continuation entries */ + DEBUG2(printk("scsi%ld: %s: Status Continuation entry " + "- ignoring\n", ha->host_no, __func__)); + break; + + case ET_COMMAND: + /* ISP device queue is full. Command not + * accepted by ISP. Queue command for + * later */ + + srb = qla4xxx_del_from_active_array(ha, + le32_to_cpu(sts_entry-> + handle)); + if (srb == NULL) + goto exit_prq_invalid_handle; + + DEBUG2(printk("scsi%ld: %s: FW device queue full, " + "srb %p\n", ha->host_no, __func__, srb)); + + /* ETRY normally by sending it back with + * DID_BUS_BUSY */ + srb->cmd->result = DID_BUS_BUSY << 16; + qla4xxx_srb_compl(ha, srb); + break; + + case ET_CONTINUE: + /* Just throw away the continuation entries */ + DEBUG2(printk("scsi%ld: %s: Continuation entry - " + "ignoring\n", ha->host_no, __func__)); + break; + + default: + /* + * Invalid entry in response queue, reset RISC + * firmware. + */ + DEBUG2(printk("scsi%ld: %s: Invalid entry %x in " + "response queue \n", ha->host_no, + __func__, + sts_entry->hdr.entryType)); + goto exit_prq_error; + } + } + + /* + * Done with responses, update the ISP For QLA4010, this also clears + * the interrupt. + */ + writel(ha->response_out, &ha->reg->rsp_q_out); + readl(&ha->reg->rsp_q_out); + + return; + +exit_prq_invalid_handle: + DEBUG2(printk("scsi%ld: %s: Invalid handle(srb)=%p type=%x IOCS=%x\n", + ha->host_no, __func__, srb, sts_entry->hdr.entryType, + sts_entry->completionStatus)); + +exit_prq_error: + writel(ha->response_out, &ha->reg->rsp_q_out); + readl(&ha->reg->rsp_q_out); + + set_bit(DPC_RESET_HA, &ha->dpc_flags); +} + +/** + * qla4xxx_isr_decode_mailbox - decodes mailbox status + * @ha: Pointer to host adapter structure. + * @mailbox_status: Mailbox status. + * + * This routine decodes the mailbox status during the ISR. + * Hardware_lock locked upon entry. runs in interrupt context. + **/ +static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha, + uint32_t mbox_status) +{ + int i; + + if ((mbox_status == MBOX_STS_BUSY) || + (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) || + (mbox_status >> 12 == MBOX_COMPLETION_STATUS)) { + ha->mbox_status[0] = mbox_status; + + if (test_bit(AF_MBOX_COMMAND, &ha->flags)) { + /* + * Copy all mailbox registers to a temporary + * location and set mailbox command done flag + */ + for (i = 1; i < ha->mbox_status_count; i++) + ha->mbox_status[i] = + readl(&ha->reg->mailbox[i]); + + set_bit(AF_MBOX_COMMAND_DONE, &ha->flags); + wake_up(&ha->mailbox_wait_queue); + } + } else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) { + /* Immediately process the AENs that don't require much work. + * Only queue the database_changed AENs */ + switch (mbox_status) { + case MBOX_ASTS_SYSTEM_ERROR: + /* Log Mailbox registers */ + if (ql4xdontresethba) { + DEBUG2(printk("%s:Dont Reset HBA\n", + __func__)); + } else { + set_bit(AF_GET_CRASH_RECORD, &ha->flags); + set_bit(DPC_RESET_HA, &ha->dpc_flags); + } + break; + + case MBOX_ASTS_REQUEST_TRANSFER_ERROR: + case MBOX_ASTS_RESPONSE_TRANSFER_ERROR: + case MBOX_ASTS_NVRAM_INVALID: + case MBOX_ASTS_IP_ADDRESS_CHANGED: + case MBOX_ASTS_DHCP_LEASE_EXPIRED: + DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, " + "Reset HA\n", ha->host_no, mbox_status)); + set_bit(DPC_RESET_HA, &ha->dpc_flags); + break; + + case MBOX_ASTS_LINK_UP: + DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n", + ha->host_no, mbox_status)); + set_bit(AF_LINK_UP, &ha->flags); + break; + + case MBOX_ASTS_LINK_DOWN: + DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n", + ha->host_no, mbox_status)); + clear_bit(AF_LINK_UP, &ha->flags); + break; + + case MBOX_ASTS_HEARTBEAT: + ha->seconds_since_last_heartbeat = 0; + break; + + case MBOX_ASTS_DHCP_LEASE_ACQUIRED: + DEBUG2(printk("scsi%ld: AEN %04x DHCP LEASE " + "ACQUIRED\n", ha->host_no, mbox_status)); + set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags); + break; + + case MBOX_ASTS_PROTOCOL_STATISTIC_ALARM: + case MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED: /* Target + * mode + * only */ + case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */ + case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR: + case MBOX_ASTS_SUBNET_STATE_CHANGE: + /* No action */ + DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no, + mbox_status)); + break; + + case MBOX_ASTS_MAC_ADDRESS_CHANGED: + case MBOX_ASTS_DNS: + /* No action */ + DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x, " + "mbox_sts[1]=%04x, mbox_sts[2]=%04x\n", + ha->host_no, mbox_status, + readl(&ha->reg->mailbox[1]), + readl(&ha->reg->mailbox[2]))); + break; + + case MBOX_ASTS_SELF_TEST_FAILED: + case MBOX_ASTS_LOGIN_FAILED: + /* No action */ + DEBUG2(printk("scsi%ld: AEN %04x, mbox_sts[1]=%04x, " + "mbox_sts[2]=%04x, mbox_sts[3]=%04x\n", + ha->host_no, mbox_status, + readl(&ha->reg->mailbox[1]), + readl(&ha->reg->mailbox[2]), + readl(&ha->reg->mailbox[3]))); + break; + + case MBOX_ASTS_DATABASE_CHANGED: + /* Queue AEN information and process it in the DPC + * routine */ + if (ha->aen_q_count > 0) { + /* advance pointer */ + if (ha->aen_in == (MAX_AEN_ENTRIES - 1)) + ha->aen_in = 0; + else + ha->aen_in++; + + /* decrement available counter */ + ha->aen_q_count--; + + for (i = 1; i < MBOX_AEN_REG_COUNT; i++) + ha->aen_q[ha->aen_in].mbox_sts[i] = + readl(&ha->reg->mailbox[i]); + + ha->aen_q[ha->aen_in].mbox_sts[0] = mbox_status; + + /* print debug message */ + DEBUG2(printk("scsi%ld: AEN[%d] %04x queued" + " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n", + ha->host_no, ha->aen_in, + mbox_status, + ha->aen_q[ha->aen_in].mbox_sts[1], + ha->aen_q[ha->aen_in].mbox_sts[2], + ha->aen_q[ha->aen_in].mbox_sts[3], + ha->aen_q[ha->aen_in]. mbox_sts[4])); + + /* The DPC routine will process the aen */ + set_bit(DPC_AEN, &ha->dpc_flags); + } else { + DEBUG2(printk("scsi%ld: %s: aen %04x, queue " + "overflowed! AEN LOST!!\n", + ha->host_no, __func__, + mbox_status)); + + DEBUG2(printk("scsi%ld: DUMP AEN QUEUE\n", + ha->host_no)); + + for (i = 0; i < MAX_AEN_ENTRIES; i++) { + DEBUG2(printk("AEN[%d] %04x %04x %04x " + "%04x\n", i, + ha->aen_q[i].mbox_sts[0], + ha->aen_q[i].mbox_sts[1], + ha->aen_q[i].mbox_sts[2], + ha->aen_q[i].mbox_sts[3])); + } + } + break; + + default: + DEBUG2(printk(KERN_WARNING + "scsi%ld: AEN %04x UNKNOWN\n", + ha->host_no, mbox_status)); + break; + } + } else { + DEBUG2(printk("scsi%ld: Unknown mailbox status %08X\n", + ha->host_no, mbox_status)); + + ha->mbox_status[0] = mbox_status; + } +} + +/** + * qla4xxx_interrupt_service_routine - isr + * @ha: pointer to host adapter structure. + * + * This is the main interrupt service routine. + * hardware_lock locked upon entry. runs in interrupt context. + **/ +void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha, + uint32_t intr_status) +{ + /* Process response queue interrupt. */ + if (intr_status & CSR_SCSI_COMPLETION_INTR) + qla4xxx_process_response_queue(ha); + + /* Process mailbox/asynch event interrupt.*/ + if (intr_status & CSR_SCSI_PROCESSOR_INTR) { + qla4xxx_isr_decode_mailbox(ha, + readl(&ha->reg->mailbox[0])); + + /* Clear Mailbox Interrupt */ + writel(set_rmask(CSR_SCSI_PROCESSOR_INTR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + } +} + +/** + * qla4xxx_intr_handler - hardware interrupt handler. + * @irq: Unused + * @dev_id: Pointer to host adapter structure + * @regs: Unused + **/ +irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct scsi_qla_host *ha; + uint32_t intr_status; + unsigned long flags = 0; + uint8_t reqs_count = 0; + + ha = (struct scsi_qla_host *) dev_id; + if (!ha) { + DEBUG2(printk(KERN_INFO + "qla4xxx: Interrupt with NULL host ptr\n")); + return IRQ_NONE; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* + * Repeatedly service interrupts up to a maximum of + * MAX_REQS_SERVICED_PER_INTR + */ + while (1) { + /* + * Read interrupt status + */ + if (le32_to_cpu(ha->shadow_regs->rsp_q_in) != + ha->response_out) + intr_status = CSR_SCSI_COMPLETION_INTR; + else + intr_status = readl(&ha->reg->ctrl_status); + + if ((intr_status & + (CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) == + 0) { + if (reqs_count == 0) + ha->spurious_int_count++; + break; + } + + if (intr_status & CSR_FATAL_ERROR) { + DEBUG2(printk(KERN_INFO "scsi%ld: Fatal Error, " + "Status 0x%04x\n", ha->host_no, + readl(isp_port_error_status (ha)))); + + /* Issue Soft Reset to clear this error condition. + * This will prevent the RISC from repeatedly + * interrupting the driver; thus, allowing the DPC to + * get scheduled to continue error recovery. + * NOTE: Disabling RISC interrupts does not work in + * this case, as CSR_FATAL_ERROR overrides + * CSR_SCSI_INTR_ENABLE */ + if ((readl(&ha->reg->ctrl_status) & + CSR_SCSI_RESET_INTR) == 0) { + writel(set_rmask(CSR_SOFT_RESET), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + } + + writel(set_rmask(CSR_FATAL_ERROR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + + __qla4xxx_disable_intrs(ha); + + set_bit(DPC_RESET_HA, &ha->dpc_flags); + + break; + } else if (intr_status & CSR_SCSI_RESET_INTR) { + clear_bit(AF_ONLINE, &ha->flags); + __qla4xxx_disable_intrs(ha); + + writel(set_rmask(CSR_SCSI_RESET_INTR), + &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + + set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags); + + break; + } else if (intr_status & INTR_PENDING) { + qla4xxx_interrupt_service_routine(ha, intr_status); + ha->total_io_count++; + if (++reqs_count == MAX_REQS_SERVICED_PER_INTR) + break; + + intr_status = 0; + } + } + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +/** + * qla4xxx_process_aen - processes AENs generated by firmware + * @ha: pointer to host adapter structure. + * @process_aen: type of AENs to process + * + * Processes specific types of Asynchronous Events generated by firmware. + * The type of AENs to process is specified by process_aen and can be + * PROCESS_ALL_AENS 0 + * FLUSH_DDB_CHANGED_AENS 1 + * RELOGIN_DDB_CHANGED_AENS 2 + **/ +void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen) +{ + uint32_t mbox_sts[MBOX_AEN_REG_COUNT]; + struct aen *aen; + int i; + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + while (ha->aen_out != ha->aen_in) { + /* Advance pointers for next entry */ + if (ha->aen_out == (MAX_AEN_ENTRIES - 1)) + ha->aen_out = 0; + else + ha->aen_out++; + + ha->aen_q_count++; + aen = &ha->aen_q[ha->aen_out]; + + /* copy aen information to local structure */ + for (i = 0; i < MBOX_AEN_REG_COUNT; i++) + mbox_sts[i] = aen->mbox_sts[i]; + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + DEBUG(printk("scsi%ld: AEN[%d] %04x, index [%d] state=%04x " + "mod=%x conerr=%08x \n", ha->host_no, ha->aen_out, + mbox_sts[0], mbox_sts[2], mbox_sts[3], + mbox_sts[1], mbox_sts[4])); + + switch (mbox_sts[0]) { + case MBOX_ASTS_DATABASE_CHANGED: + if (process_aen == FLUSH_DDB_CHANGED_AENS) { + DEBUG2(printk("scsi%ld: AEN[%d] %04x, index " + "[%d] state=%04x FLUSHED!\n", + ha->host_no, ha->aen_out, + mbox_sts[0], mbox_sts[2], + mbox_sts[3])); + break; + } else if (process_aen == RELOGIN_DDB_CHANGED_AENS) { + /* for use during init time, we only want to + * relogin non-active ddbs */ + struct ddb_entry *ddb_entry; + + ddb_entry = + /* FIXME: name length? */ + qla4xxx_lookup_ddb_by_fw_index(ha, + mbox_sts[2]); + if (!ddb_entry) + break; + + ddb_entry->dev_scan_wait_to_complete_relogin = + 0; + ddb_entry->dev_scan_wait_to_start_relogin = + jiffies + + ((ddb_entry->default_time2wait + + 4) * HZ); + + DEBUG2(printk("scsi%ld: ddb index [%d] initate" + " RELOGIN after %d seconds\n", + ha->host_no, + ddb_entry->fw_ddb_index, + ddb_entry->default_time2wait + + 4)); + break; + } + + if (mbox_sts[1] == 0) { /* Global DB change. */ + qla4xxx_reinitialize_ddb_list(ha); + } else if (mbox_sts[1] == 1) { /* Specific device. */ + qla4xxx_process_ddb_changed(ha, mbox_sts[2], + mbox_sts[3]); + } + break; + } + spin_lock_irqsave(&ha->hardware_lock, flags); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +} + diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c new file mode 100644 index 000000000000..ed977f70b2db --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_mbx.c @@ -0,0 +1,930 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#include "ql4_def.h" + + +/** + * qla4xxx_mailbox_command - issues mailbox commands + * @ha: Pointer to host adapter structure. + * @inCount: number of mailbox registers to load. + * @outCount: number of mailbox registers to return. + * @mbx_cmd: data pointer for mailbox in registers. + * @mbx_sts: data pointer for mailbox out registers. + * + * This routine sssue mailbox commands and waits for completion. + * If outCount is 0, this routine completes successfully WITHOUT waiting + * for the mailbox command to complete. + **/ +int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount, + uint8_t outCount, uint32_t *mbx_cmd, + uint32_t *mbx_sts) +{ + int status = QLA_ERROR; + uint8_t i; + u_long wait_count; + uint32_t intr_status; + unsigned long flags = 0; + DECLARE_WAITQUEUE(wait, current); + + mutex_lock(&ha->mbox_sem); + + /* Mailbox code active */ + set_bit(AF_MBOX_COMMAND, &ha->flags); + + /* Make sure that pointers are valid */ + if (!mbx_cmd || !mbx_sts) { + DEBUG2(printk("scsi%ld: %s: Invalid mbx_cmd or mbx_sts " + "pointer\n", ha->host_no, __func__)); + goto mbox_exit; + } + + /* To prevent overwriting mailbox registers for a command that has + * not yet been serviced, check to see if a previously issued + * mailbox command is interrupting. + * ----------------------------------------------------------------- + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + intr_status = readl(&ha->reg->ctrl_status); + if (intr_status & CSR_SCSI_PROCESSOR_INTR) { + /* Service existing interrupt */ + qla4xxx_interrupt_service_routine(ha, intr_status); + clear_bit(AF_MBOX_COMMAND_DONE, &ha->flags); + } + + /* Send the mailbox command to the firmware */ + ha->mbox_status_count = outCount; + for (i = 0; i < outCount; i++) + ha->mbox_status[i] = 0; + + /* Load all mailbox registers, except mailbox 0. */ + for (i = 1; i < inCount; i++) + writel(mbx_cmd[i], &ha->reg->mailbox[i]); + + /* Wakeup firmware */ + writel(mbx_cmd[0], &ha->reg->mailbox[0]); + readl(&ha->reg->mailbox[0]); + writel(set_rmask(CSR_INTR_RISC), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait for completion */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&ha->mailbox_wait_queue, &wait); + + /* + * If we don't want status, don't wait for the mailbox command to + * complete. For example, MBOX_CMD_RESET_FW doesn't return status, + * you must poll the inbound Interrupt Mask for completion. + */ + if (outCount == 0) { + status = QLA_SUCCESS; + set_current_state(TASK_RUNNING); + remove_wait_queue(&ha->mailbox_wait_queue, &wait); + goto mbox_exit; + } + /* Wait for command to complete */ + wait_count = jiffies + MBOX_TOV * HZ; + while (test_bit(AF_MBOX_COMMAND_DONE, &ha->flags) == 0) { + if (time_after_eq(jiffies, wait_count)) + break; + + spin_lock_irqsave(&ha->hardware_lock, flags); + intr_status = readl(&ha->reg->ctrl_status); + if (intr_status & INTR_PENDING) { + /* + * Service the interrupt. + * The ISR will save the mailbox status registers + * to a temporary storage location in the adapter + * structure. + */ + ha->mbox_status_count = outCount; + qla4xxx_interrupt_service_routine(ha, intr_status); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + msleep(10); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&ha->mailbox_wait_queue, &wait); + + /* Check for mailbox timeout. */ + if (!test_bit(AF_MBOX_COMMAND_DONE, &ha->flags)) { + DEBUG2(printk("scsi%ld: Mailbox Cmd 0x%08X timed out ...," + " Scheduling Adapter Reset\n", ha->host_no, + mbx_cmd[0])); + ha->mailbox_timeout_count++; + mbx_sts[0] = (-1); + set_bit(DPC_RESET_HA, &ha->dpc_flags); + goto mbox_exit; + } + + /* + * Copy the mailbox out registers to the caller's mailbox in/out + * structure. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + for (i = 0; i < outCount; i++) + mbx_sts[i] = ha->mbox_status[i]; + + /* Set return status and error flags (if applicable). */ + switch (ha->mbox_status[0]) { + case MBOX_STS_COMMAND_COMPLETE: + status = QLA_SUCCESS; + break; + + case MBOX_STS_INTERMEDIATE_COMPLETION: + status = QLA_SUCCESS; + break; + + case MBOX_STS_BUSY: + DEBUG2( printk("scsi%ld: %s: Cmd = %08X, ISP BUSY\n", + ha->host_no, __func__, mbx_cmd[0])); + ha->mailbox_timeout_count++; + break; + + default: + DEBUG2(printk("scsi%ld: %s: **** FAILED, cmd = %08X, " + "sts = %08X ****\n", ha->host_no, __func__, + mbx_cmd[0], mbx_sts[0])); + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +mbox_exit: + clear_bit(AF_MBOX_COMMAND, &ha->flags); + clear_bit(AF_MBOX_COMMAND_DONE, &ha->flags); + mutex_unlock(&ha->mbox_sem); + + return status; +} + + +/** + * qla4xxx_issue_iocb - issue mailbox iocb command + * @ha: adapter state pointer. + * @buffer: buffer pointer. + * @phys_addr: physical address of buffer. + * @size: size of buffer. + * + * Issues iocbs via mailbox commands. + * TARGET_QUEUE_LOCK must be released. + * ADAPTER_STATE_LOCK must be released. + **/ +int +qla4xxx_issue_iocb(struct scsi_qla_host * ha, void *buffer, + dma_addr_t phys_addr, size_t size) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_EXECUTE_IOCB_A64; + mbox_cmd[1] = 0; + mbox_cmd[2] = LSDW(phys_addr); + mbox_cmd[3] = MSDW(phys_addr); + status = qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]); + return status; +} + +int qla4xxx_conn_close_sess_logout(struct scsi_qla_host * ha, + uint16_t fw_ddb_index, + uint16_t connection_id, + uint16_t option) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_CONN_CLOSE_SESS_LOGOUT; + mbox_cmd[1] = fw_ddb_index; + mbox_cmd[2] = connection_id; + mbox_cmd[3] = LOGOUT_OPTION_RELOGIN; + if (qla4xxx_mailbox_command(ha, 4, 2, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_CONN_CLOSE_SESS_LOGOUT " + "option %04x failed sts %04X %04X", + ha->host_no, __func__, + option, mbox_sts[0], mbox_sts[1])); + if (mbox_sts[0] == 0x4005) + DEBUG2(printk("%s reason %04X\n", __func__, + mbox_sts[1])); + } + return QLA_SUCCESS; +} + +int qla4xxx_clear_database_entry(struct scsi_qla_host * ha, + uint16_t fw_ddb_index) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_CLEAR_DATABASE_ENTRY; + mbox_cmd[1] = fw_ddb_index; + if (qla4xxx_mailbox_command(ha, 2, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) + return QLA_ERROR; + + return QLA_SUCCESS; +} + +/** + * qla4xxx_initialize_fw_cb - initializes firmware control block. + * @ha: Pointer to host adapter structure. + **/ +int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha) +{ + struct init_fw_ctrl_blk *init_fw_cb; + dma_addr_t init_fw_cb_dma; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_ERROR; + + init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, + sizeof(struct init_fw_ctrl_blk), + &init_fw_cb_dma, GFP_KERNEL); + if (init_fw_cb == NULL) { + DEBUG2(printk("scsi%ld: %s: Unable to alloc init_cb\n", + ha->host_no, __func__)); + return 10; + } + memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); + + /* Get Initialize Firmware Control Block. */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + dma_free_coherent(&ha->pdev->dev, + sizeof(struct init_fw_ctrl_blk), + init_fw_cb, init_fw_cb_dma); + return status; + } + + /* Initialize request and response queues. */ + qla4xxx_init_rings(ha); + + /* Fill in the request and response queue information. */ + init_fw_cb->ReqQConsumerIndex = cpu_to_le16(ha->request_out); + init_fw_cb->ComplQProducerIndex = cpu_to_le16(ha->response_in); + init_fw_cb->ReqQLen = __constant_cpu_to_le16(REQUEST_QUEUE_DEPTH); + init_fw_cb->ComplQLen = __constant_cpu_to_le16(RESPONSE_QUEUE_DEPTH); + init_fw_cb->ReqQAddrLo = cpu_to_le32(LSDW(ha->request_dma)); + init_fw_cb->ReqQAddrHi = cpu_to_le32(MSDW(ha->request_dma)); + init_fw_cb->ComplQAddrLo = cpu_to_le32(LSDW(ha->response_dma)); + init_fw_cb->ComplQAddrHi = cpu_to_le32(MSDW(ha->response_dma)); + init_fw_cb->ShadowRegBufAddrLo = + cpu_to_le32(LSDW(ha->shadow_regs_dma)); + init_fw_cb->ShadowRegBufAddrHi = + cpu_to_le32(MSDW(ha->shadow_regs_dma)); + + /* Set up required options. */ + init_fw_cb->FwOptions |= + __constant_cpu_to_le16(FWOPT_SESSION_MODE | + FWOPT_INITIATOR_MODE); + init_fw_cb->FwOptions &= __constant_cpu_to_le16(~FWOPT_TARGET_MODE); + + /* Save some info in adapter structure. */ + ha->firmware_options = le16_to_cpu(init_fw_cb->FwOptions); + ha->tcp_options = le16_to_cpu(init_fw_cb->TCPOptions); + ha->heartbeat_interval = init_fw_cb->HeartbeatInterval; + memcpy(ha->ip_address, init_fw_cb->IPAddr, + min(sizeof(ha->ip_address), sizeof(init_fw_cb->IPAddr))); + memcpy(ha->subnet_mask, init_fw_cb->SubnetMask, + min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->SubnetMask))); + memcpy(ha->gateway, init_fw_cb->GatewayIPAddr, + min(sizeof(ha->gateway), sizeof(init_fw_cb->GatewayIPAddr))); + memcpy(ha->name_string, init_fw_cb->iSCSINameString, + min(sizeof(ha->name_string), + sizeof(init_fw_cb->iSCSINameString))); + memcpy(ha->alias, init_fw_cb->Alias, + min(sizeof(ha->alias), sizeof(init_fw_cb->Alias))); + + /* Save Command Line Paramater info */ + ha->port_down_retry_count = le16_to_cpu(init_fw_cb->KeepAliveTimeout); + ha->discovery_wait = ql4xdiscoverywait; + + /* Send Initialize Firmware Control Block. */ + mbox_cmd[0] = MBOX_CMD_INITIALIZE_FIRMWARE; + mbox_cmd[1] = 0; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) == + QLA_SUCCESS) + status = QLA_SUCCESS; + else { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_INITIALIZE_FIRMWARE " + "failed w/ status %04X\n", ha->host_no, __func__, + mbox_sts[0])); + } + dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), + init_fw_cb, init_fw_cb_dma); + + return status; +} + +/** + * qla4xxx_get_dhcp_ip_address - gets HBA ip address via DHCP + * @ha: Pointer to host adapter structure. + **/ +int qla4xxx_get_dhcp_ip_address(struct scsi_qla_host * ha) +{ + struct init_fw_ctrl_blk *init_fw_cb; + dma_addr_t init_fw_cb_dma; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + init_fw_cb = dma_alloc_coherent(&ha->pdev->dev, + sizeof(struct init_fw_ctrl_blk), + &init_fw_cb_dma, GFP_KERNEL); + if (init_fw_cb == NULL) { + printk("scsi%ld: %s: Unable to alloc init_cb\n", ha->host_no, + __func__); + return 10; + } + + /* Get Initialize Firmware Control Block. */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + memset(init_fw_cb, 0, sizeof(struct init_fw_ctrl_blk)); + mbox_cmd[0] = MBOX_CMD_GET_INIT_FW_CTRL_BLOCK; + mbox_cmd[2] = LSDW(init_fw_cb_dma); + mbox_cmd[3] = MSDW(init_fw_cb_dma); + + if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: Failed to get init_fw_ctrl_blk\n", + ha->host_no, __func__)); + dma_free_coherent(&ha->pdev->dev, + sizeof(struct init_fw_ctrl_blk), + init_fw_cb, init_fw_cb_dma); + return QLA_ERROR; + } + + /* Save IP Address. */ + memcpy(ha->ip_address, init_fw_cb->IPAddr, + min(sizeof(ha->ip_address), sizeof(init_fw_cb->IPAddr))); + memcpy(ha->subnet_mask, init_fw_cb->SubnetMask, + min(sizeof(ha->subnet_mask), sizeof(init_fw_cb->SubnetMask))); + memcpy(ha->gateway, init_fw_cb->GatewayIPAddr, + min(sizeof(ha->gateway), sizeof(init_fw_cb->GatewayIPAddr))); + + dma_free_coherent(&ha->pdev->dev, sizeof(struct init_fw_ctrl_blk), + init_fw_cb, init_fw_cb_dma); + + return QLA_SUCCESS; +} + +/** + * qla4xxx_get_firmware_state - gets firmware state of HBA + * @ha: Pointer to host adapter structure. + **/ +int qla4xxx_get_firmware_state(struct scsi_qla_host * ha) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Get firmware version */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_GET_FW_STATE; + if (qla4xxx_mailbox_command(ha, 1, 4, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_FW_STATE failed w/ " + "status %04X\n", ha->host_no, __func__, + mbox_sts[0])); + return QLA_ERROR; + } + ha->firmware_state = mbox_sts[1]; + ha->board_id = mbox_sts[2]; + ha->addl_fw_state = mbox_sts[3]; + DEBUG2(printk("scsi%ld: %s firmware_state=0x%x\n", + ha->host_no, __func__, ha->firmware_state);) + + return QLA_SUCCESS; +} + +/** + * qla4xxx_get_firmware_status - retrieves firmware status + * @ha: Pointer to host adapter structure. + **/ +int qla4xxx_get_firmware_status(struct scsi_qla_host * ha) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Get firmware version */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_GET_FW_STATUS; + if (qla4xxx_mailbox_command(ha, 1, 3, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_FW_STATUS failed w/ " + "status %04X\n", ha->host_no, __func__, + mbox_sts[0])); + return QLA_ERROR; + } + + /* High-water mark of IOCBs */ + ha->iocb_hiwat = mbox_sts[2]; + if (ha->iocb_hiwat > IOCB_HIWAT_CUSHION) + ha->iocb_hiwat -= IOCB_HIWAT_CUSHION; + else + dev_info(&ha->pdev->dev, "WARNING!!! You have less than %d " + "firmare IOCBs available (%d).\n", + IOCB_HIWAT_CUSHION, ha->iocb_hiwat); + + return QLA_SUCCESS; +} + +/** + * qla4xxx_get_fwddb_entry - retrieves firmware ddb entry + * @ha: Pointer to host adapter structure. + * @fw_ddb_index: Firmware's device database index + * @fw_ddb_entry: Pointer to firmware's device database entry structure + * @num_valid_ddb_entries: Pointer to number of valid ddb entries + * @next_ddb_index: Pointer to next valid device database index + * @fw_ddb_device_state: Pointer to device state + **/ +int qla4xxx_get_fwddb_entry(struct scsi_qla_host *ha, + uint16_t fw_ddb_index, + struct dev_db_entry *fw_ddb_entry, + dma_addr_t fw_ddb_entry_dma, + uint32_t *num_valid_ddb_entries, + uint32_t *next_ddb_index, + uint32_t *fw_ddb_device_state, + uint32_t *conn_err_detail, + uint16_t *tcp_source_port_num, + uint16_t *connection_id) +{ + int status = QLA_ERROR; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Make sure the device index is valid */ + if (fw_ddb_index >= MAX_DDB_ENTRIES) { + DEBUG2(printk("scsi%ld: %s: index [%d] out of range.\n", + ha->host_no, __func__, fw_ddb_index)); + goto exit_get_fwddb; + } + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_GET_DATABASE_ENTRY; + mbox_cmd[1] = (uint32_t) fw_ddb_index; + mbox_cmd[2] = LSDW(fw_ddb_entry_dma); + mbox_cmd[3] = MSDW(fw_ddb_entry_dma); + if (qla4xxx_mailbox_command(ha, 4, 7, &mbox_cmd[0], &mbox_sts[0]) == + QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_GET_DATABASE_ENTRY failed" + " with status 0x%04X\n", ha->host_no, __func__, + mbox_sts[0])); + goto exit_get_fwddb; + } + if (fw_ddb_index != mbox_sts[1]) { + DEBUG2(printk("scsi%ld: %s: index mismatch [%d] != [%d].\n", + ha->host_no, __func__, fw_ddb_index, + mbox_sts[1])); + goto exit_get_fwddb; + } + if (fw_ddb_entry) { + dev_info(&ha->pdev->dev, "DDB[%d] MB0 %04x Tot %d Next %d " + "State %04x ConnErr %08x %d.%d.%d.%d:%04d \"%s\"\n", + fw_ddb_index, mbox_sts[0], mbox_sts[2], mbox_sts[3], + mbox_sts[4], mbox_sts[5], fw_ddb_entry->ipAddr[0], + fw_ddb_entry->ipAddr[1], fw_ddb_entry->ipAddr[2], + fw_ddb_entry->ipAddr[3], + le16_to_cpu(fw_ddb_entry->portNumber), + fw_ddb_entry->iscsiName); + } + if (num_valid_ddb_entries) + *num_valid_ddb_entries = mbox_sts[2]; + if (next_ddb_index) + *next_ddb_index = mbox_sts[3]; + if (fw_ddb_device_state) + *fw_ddb_device_state = mbox_sts[4]; + + /* + * RA: This mailbox has been changed to pass connection error and + * details. Its true for ISP4010 as per Version E - Not sure when it + * was changed. Get the time2wait from the fw_dd_entry field : + * default_time2wait which we call it as minTime2Wait DEV_DB_ENTRY + * struct. + */ + if (conn_err_detail) + *conn_err_detail = mbox_sts[5]; + if (tcp_source_port_num) + *tcp_source_port_num = (uint16_t) mbox_sts[6] >> 16; + if (connection_id) + *connection_id = (uint16_t) mbox_sts[6] & 0x00FF; + status = QLA_SUCCESS; + +exit_get_fwddb: + return status; +} + +/** + * qla4xxx_set_fwddb_entry - sets a ddb entry. + * @ha: Pointer to host adapter structure. + * @fw_ddb_index: Firmware's device database index + * @fw_ddb_entry: Pointer to firmware's ddb entry structure, or NULL. + * + * This routine initializes or updates the adapter's device database + * entry for the specified device. It also triggers a login for the + * specified device. Therefore, it may also be used as a secondary + * login routine when a NULL pointer is specified for the fw_ddb_entry. + **/ +int qla4xxx_set_ddb_entry(struct scsi_qla_host * ha, uint16_t fw_ddb_index, + dma_addr_t fw_ddb_entry_dma) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Do not wait for completion. The firmware will send us an + * ASTS_DATABASE_CHANGED (0x8014) to notify us of the login status. + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_SET_DATABASE_ENTRY; + mbox_cmd[1] = (uint32_t) fw_ddb_index; + mbox_cmd[2] = LSDW(fw_ddb_entry_dma); + mbox_cmd[3] = MSDW(fw_ddb_entry_dma); + return qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]); +} + +int qla4xxx_conn_open_session_login(struct scsi_qla_host * ha, + uint16_t fw_ddb_index) +{ + int status = QLA_ERROR; + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Do not wait for completion. The firmware will send us an + * ASTS_DATABASE_CHANGED (0x8014) to notify us of the login status. + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_CONN_OPEN_SESS_LOGIN; + mbox_cmd[1] = (uint32_t) fw_ddb_index; + mbox_cmd[2] = 0; + mbox_cmd[3] = 0; + mbox_cmd[4] = 0; + status = qla4xxx_mailbox_command(ha, 4, 0, &mbox_cmd[0], &mbox_sts[0]); + DEBUG2(printk("%s fw_ddb_index=%d status=%d mbx0_1=0x%x :0x%x\n", + __func__, fw_ddb_index, status, mbox_sts[0], + mbox_sts[1]);) + + return status; +} + +/** + * qla4xxx_get_crash_record - retrieves crash record. + * @ha: Pointer to host adapter structure. + * + * This routine retrieves a crash record from the QLA4010 after an 8002h aen. + **/ +void qla4xxx_get_crash_record(struct scsi_qla_host * ha) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + struct crash_record *crash_record = NULL; + dma_addr_t crash_record_dma = 0; + uint32_t crash_record_size = 0; + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_cmd)); + + /* Get size of crash record. */ + mbox_cmd[0] = MBOX_CMD_GET_CRASH_RECORD; + if (qla4xxx_mailbox_command(ha, 5, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: ERROR: Unable to retrieve size!\n", + ha->host_no, __func__)); + goto exit_get_crash_record; + } + crash_record_size = mbox_sts[4]; + if (crash_record_size == 0) { + DEBUG2(printk("scsi%ld: %s: ERROR: Crash record size is 0!\n", + ha->host_no, __func__)); + goto exit_get_crash_record; + } + + /* Alloc Memory for Crash Record. */ + crash_record = dma_alloc_coherent(&ha->pdev->dev, crash_record_size, + &crash_record_dma, GFP_KERNEL); + if (crash_record == NULL) + goto exit_get_crash_record; + + /* Get Crash Record. */ + mbox_cmd[0] = MBOX_CMD_GET_CRASH_RECORD; + mbox_cmd[2] = LSDW(crash_record_dma); + mbox_cmd[3] = MSDW(crash_record_dma); + mbox_cmd[4] = crash_record_size; + if (qla4xxx_mailbox_command(ha, 5, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) + goto exit_get_crash_record; + + /* Dump Crash Record. */ + +exit_get_crash_record: + if (crash_record) + dma_free_coherent(&ha->pdev->dev, crash_record_size, + crash_record, crash_record_dma); +} + +/** + * qla4xxx_get_conn_event_log - retrieves connection event log + * @ha: Pointer to host adapter structure. + **/ +void qla4xxx_get_conn_event_log(struct scsi_qla_host * ha) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + struct conn_event_log_entry *event_log = NULL; + dma_addr_t event_log_dma = 0; + uint32_t event_log_size = 0; + uint32_t num_valid_entries; + uint32_t oldest_entry = 0; + uint32_t max_event_log_entries; + uint8_t i; + + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_cmd)); + + /* Get size of crash record. */ + mbox_cmd[0] = MBOX_CMD_GET_CONN_EVENT_LOG; + if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) + goto exit_get_event_log; + + event_log_size = mbox_sts[4]; + if (event_log_size == 0) + goto exit_get_event_log; + + /* Alloc Memory for Crash Record. */ + event_log = dma_alloc_coherent(&ha->pdev->dev, event_log_size, + &event_log_dma, GFP_KERNEL); + if (event_log == NULL) + goto exit_get_event_log; + + /* Get Crash Record. */ + mbox_cmd[0] = MBOX_CMD_GET_CONN_EVENT_LOG; + mbox_cmd[2] = LSDW(event_log_dma); + mbox_cmd[3] = MSDW(event_log_dma); + if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: ERROR: Unable to retrieve event " + "log!\n", ha->host_no, __func__)); + goto exit_get_event_log; + } + + /* Dump Event Log. */ + num_valid_entries = mbox_sts[1]; + + max_event_log_entries = event_log_size / + sizeof(struct conn_event_log_entry); + + if (num_valid_entries > max_event_log_entries) + oldest_entry = num_valid_entries % max_event_log_entries; + + DEBUG3(printk("scsi%ld: Connection Event Log Dump (%d entries):\n", + ha->host_no, num_valid_entries)); + + if (extended_error_logging == 3) { + if (oldest_entry == 0) { + /* Circular Buffer has not wrapped around */ + for (i=0; i < num_valid_entries; i++) { + qla4xxx_dump_buffer((uint8_t *)event_log+ + (i*sizeof(*event_log)), + sizeof(*event_log)); + } + } + else { + /* Circular Buffer has wrapped around - + * display accordingly*/ + for (i=oldest_entry; i < max_event_log_entries; i++) { + qla4xxx_dump_buffer((uint8_t *)event_log+ + (i*sizeof(*event_log)), + sizeof(*event_log)); + } + for (i=0; i < oldest_entry; i++) { + qla4xxx_dump_buffer((uint8_t *)event_log+ + (i*sizeof(*event_log)), + sizeof(*event_log)); + } + } + } + +exit_get_event_log: + if (event_log) + dma_free_coherent(&ha->pdev->dev, event_log_size, event_log, + event_log_dma); +} + +/** + * qla4xxx_reset_lun - issues LUN Reset + * @ha: Pointer to host adapter structure. + * @db_entry: Pointer to device database entry + * @un_entry: Pointer to lun entry structure + * + * This routine performs a LUN RESET on the specified target/lun. + * The caller must ensure that the ddb_entry and lun_entry pointers + * are valid before calling this routine. + **/ +int qla4xxx_reset_lun(struct scsi_qla_host * ha, struct ddb_entry * ddb_entry, + int lun) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + int status = QLA_SUCCESS; + + DEBUG2(printk("scsi%ld:%d:%d: lun reset issued\n", ha->host_no, + ddb_entry->os_target_id, lun)); + + /* + * Send lun reset command to ISP, so that the ISP will return all + * outstanding requests with RESET status + */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_LUN_RESET; + mbox_cmd[1] = ddb_entry->fw_ddb_index; + mbox_cmd[2] = lun << 8; + mbox_cmd[5] = 0x01; /* Immediate Command Enable */ + qla4xxx_mailbox_command(ha, 6, 1, &mbox_cmd[0], &mbox_sts[0]); + if (mbox_sts[0] != MBOX_STS_COMMAND_COMPLETE && + mbox_sts[0] != MBOX_STS_COMMAND_ERROR) + status = QLA_ERROR; + + return status; +} + + +int qla4xxx_get_flash(struct scsi_qla_host * ha, dma_addr_t dma_addr, + uint32_t offset, uint32_t len) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_READ_FLASH; + mbox_cmd[1] = LSDW(dma_addr); + mbox_cmd[2] = MSDW(dma_addr); + mbox_cmd[3] = offset; + mbox_cmd[4] = len; + if (qla4xxx_mailbox_command(ha, 5, 2, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_READ_FLASH, failed w/ " + "status %04X %04X, offset %08x, len %08x\n", ha->host_no, + __func__, mbox_sts[0], mbox_sts[1], offset, len)); + return QLA_ERROR; + } + return QLA_SUCCESS; +} + +/** + * qla4xxx_get_fw_version - gets firmware version + * @ha: Pointer to host adapter structure. + * + * Retrieves the firmware version on HBA. In QLA4010, mailboxes 2 & 3 may + * hold an address for data. Make sure that we write 0 to those mailboxes, + * if unused. + **/ +int qla4xxx_get_fw_version(struct scsi_qla_host * ha) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + /* Get firmware version. */ + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + mbox_cmd[0] = MBOX_CMD_ABOUT_FW; + if (qla4xxx_mailbox_command(ha, 4, 5, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: MBOX_CMD_ABOUT_FW failed w/ " + "status %04X\n", ha->host_no, __func__, mbox_sts[0])); + return QLA_ERROR; + } + + /* Save firmware version information. */ + ha->firmware_version[0] = mbox_sts[1]; + ha->firmware_version[1] = mbox_sts[2]; + ha->patch_number = mbox_sts[3]; + ha->build_number = mbox_sts[4]; + + return QLA_SUCCESS; +} + +int qla4xxx_get_default_ddb(struct scsi_qla_host *ha, dma_addr_t dma_addr) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS; + mbox_cmd[2] = LSDW(dma_addr); + mbox_cmd[3] = MSDW(dma_addr); + + if (qla4xxx_mailbox_command(ha, 4, 1, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s: failed status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + return QLA_ERROR; + } + return QLA_SUCCESS; +} + +int qla4xxx_req_ddb_entry(struct scsi_qla_host *ha, uint32_t *ddb_index) +{ + uint32_t mbox_cmd[MBOX_REG_COUNT]; + uint32_t mbox_sts[MBOX_REG_COUNT]; + + memset(&mbox_cmd, 0, sizeof(mbox_cmd)); + memset(&mbox_sts, 0, sizeof(mbox_sts)); + + mbox_cmd[0] = MBOX_CMD_REQUEST_DATABASE_ENTRY; + mbox_cmd[1] = MAX_PRST_DEV_DB_ENTRIES; + + if (qla4xxx_mailbox_command(ha, 2, 3, &mbox_cmd[0], &mbox_sts[0]) != + QLA_SUCCESS) { + if (mbox_sts[0] == MBOX_STS_COMMAND_ERROR) { + *ddb_index = mbox_sts[2]; + } else { + DEBUG2(printk("scsi%ld: %s: failed status %04X\n", + ha->host_no, __func__, mbox_sts[0])); + return QLA_ERROR; + } + } else { + *ddb_index = MAX_PRST_DEV_DB_ENTRIES; + } + + return QLA_SUCCESS; +} + + +int qla4xxx_send_tgts(struct scsi_qla_host *ha, char *ip, uint16_t port) +{ + struct dev_db_entry *fw_ddb_entry; + dma_addr_t fw_ddb_entry_dma; + uint32_t ddb_index; + int ret_val = QLA_SUCCESS; + + + fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, + sizeof(*fw_ddb_entry), + &fw_ddb_entry_dma, GFP_KERNEL); + if (!fw_ddb_entry) { + DEBUG2(printk("scsi%ld: %s: Unable to allocate dma buffer.\n", + ha->host_no, __func__)); + ret_val = QLA_ERROR; + goto qla4xxx_send_tgts_exit; + } + + ret_val = qla4xxx_get_default_ddb(ha, fw_ddb_entry_dma); + if (ret_val != QLA_SUCCESS) + goto qla4xxx_send_tgts_exit; + + ret_val = qla4xxx_req_ddb_entry(ha, &ddb_index); + if (ret_val != QLA_SUCCESS) + goto qla4xxx_send_tgts_exit; + + memset((void *)fw_ddb_entry->iSCSIAlias, 0, + sizeof(fw_ddb_entry->iSCSIAlias)); + + memset((void *)fw_ddb_entry->iscsiName, 0, + sizeof(fw_ddb_entry->iscsiName)); + + memset((void *)fw_ddb_entry->ipAddr, 0, sizeof(fw_ddb_entry->ipAddr)); + memset((void *)fw_ddb_entry->targetAddr, 0, + sizeof(fw_ddb_entry->targetAddr)); + + fw_ddb_entry->options = (DDB_OPT_DISC_SESSION | DDB_OPT_TARGET); + fw_ddb_entry->portNumber = cpu_to_le16(ntohs(port)); + + fw_ddb_entry->ipAddr[0] = *ip; + fw_ddb_entry->ipAddr[1] = *(ip + 1); + fw_ddb_entry->ipAddr[2] = *(ip + 2); + fw_ddb_entry->ipAddr[3] = *(ip + 3); + + ret_val = qla4xxx_set_ddb_entry(ha, ddb_index, fw_ddb_entry_dma); + +qla4xxx_send_tgts_exit: + dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry), + fw_ddb_entry, fw_ddb_entry_dma); + return ret_val; +} + 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; +} diff --git a/drivers/scsi/qla4xxx/ql4_nvram.h b/drivers/scsi/qla4xxx/ql4_nvram.h new file mode 100644 index 000000000000..08e2aed8c6cc --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_nvram.h @@ -0,0 +1,256 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#ifndef _QL4XNVRM_H_ +#define _QL4XNVRM_H_ + +/* + * AM29LV Flash definitions + */ +#define FM93C56A_SIZE_8 0x100 +#define FM93C56A_SIZE_16 0x80 +#define FM93C66A_SIZE_8 0x200 +#define FM93C66A_SIZE_16 0x100/* 4010 */ +#define FM93C86A_SIZE_16 0x400/* 4022 */ + +#define FM93C56A_START 0x1 + +// Commands +#define FM93C56A_READ 0x2 +#define FM93C56A_WEN 0x0 +#define FM93C56A_WRITE 0x1 +#define FM93C56A_WRITE_ALL 0x0 +#define FM93C56A_WDS 0x0 +#define FM93C56A_ERASE 0x3 +#define FM93C56A_ERASE_ALL 0x0 + +/* Command Extentions */ +#define FM93C56A_WEN_EXT 0x3 +#define FM93C56A_WRITE_ALL_EXT 0x1 +#define FM93C56A_WDS_EXT 0x0 +#define FM93C56A_ERASE_ALL_EXT 0x2 + +/* Address Bits */ +#define FM93C56A_NO_ADDR_BITS_16 8 /* 4010 */ +#define FM93C56A_NO_ADDR_BITS_8 9 /* 4010 */ +#define FM93C86A_NO_ADDR_BITS_16 10 /* 4022 */ + +/* Data Bits */ +#define FM93C56A_DATA_BITS_16 16 +#define FM93C56A_DATA_BITS_8 8 + +/* Special Bits */ +#define FM93C56A_READ_DUMMY_BITS 1 +#define FM93C56A_READY 0 +#define FM93C56A_BUSY 1 +#define FM93C56A_CMD_BITS 2 + +/* Auburn Bits */ +#define AUBURN_EEPROM_DI 0x8 +#define AUBURN_EEPROM_DI_0 0x0 +#define AUBURN_EEPROM_DI_1 0x8 +#define AUBURN_EEPROM_DO 0x4 +#define AUBURN_EEPROM_DO_0 0x0 +#define AUBURN_EEPROM_DO_1 0x4 +#define AUBURN_EEPROM_CS 0x2 +#define AUBURN_EEPROM_CS_0 0x0 +#define AUBURN_EEPROM_CS_1 0x2 +#define AUBURN_EEPROM_CLK_RISE 0x1 +#define AUBURN_EEPROM_CLK_FALL 0x0 + +/* */ +/* EEPROM format */ +/* */ +struct bios_params { + uint16_t SpinUpDelay:1; + uint16_t BIOSDisable:1; + uint16_t MMAPEnable:1; + uint16_t BootEnable:1; + uint16_t Reserved0:12; + uint8_t bootID0:7; + uint8_t bootID0Valid:1; + uint8_t bootLUN0[8]; + uint8_t bootID1:7; + uint8_t bootID1Valid:1; + uint8_t bootLUN1[8]; + uint16_t MaxLunsPerTarget; + uint8_t Reserved1[10]; +}; + +struct eeprom_port_cfg { + + /* MTU MAC 0 */ + u16 etherMtu_mac; + + /* Flow Control MAC 0 */ + u16 pauseThreshold_mac; + u16 resumeThreshold_mac; + u16 reserved[13]; +}; + +struct eeprom_function_cfg { + u8 reserved[30]; + + /* MAC ADDR */ + u8 macAddress[6]; + u8 macAddressSecondary[6]; + u16 subsysVendorId; + u16 subsysDeviceId; +}; + +struct eeprom_data { + union { + struct { /* isp4010 */ + u8 asic_id[4]; /* x00 */ + u8 version; /* x04 */ + u8 reserved; /* x05 */ + u16 board_id; /* x06 */ +#define EEPROM_BOARDID_ELDORADO 1 +#define EEPROM_BOARDID_PLACER 2 + +#define EEPROM_SERIAL_NUM_SIZE 16 + u8 serial_number[EEPROM_SERIAL_NUM_SIZE]; /* x08 */ + + /* ExtHwConfig: */ + /* Offset = 24bytes + * + * | SSRAM Size| |ST|PD|SDRAM SZ| W| B| SP | | + * |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + u16 ext_hw_conf; /* x18 */ + u8 mac0[6]; /* x1A */ + u8 mac1[6]; /* x20 */ + u8 mac2[6]; /* x26 */ + u8 mac3[6]; /* x2C */ + u16 etherMtu; /* x32 */ + u16 macConfig; /* x34 */ +#define MAC_CONFIG_ENABLE_ANEG 0x0001 +#define MAC_CONFIG_ENABLE_PAUSE 0x0002 + u16 phyConfig; /* x36 */ +#define PHY_CONFIG_PHY_ADDR_MASK 0x1f +#define PHY_CONFIG_ENABLE_FW_MANAGEMENT_MASK 0x20 + u16 topcat; /* x38 */ +#define TOPCAT_PRESENT 0x0100 +#define TOPCAT_MASK 0xFF00 + +#define EEPROM_UNUSED_1_SIZE 2 + u8 unused_1[EEPROM_UNUSED_1_SIZE]; /* x3A */ + u16 bufletSize; /* x3C */ + u16 bufletCount; /* x3E */ + u16 bufletPauseThreshold; /* x40 */ + u16 tcpWindowThreshold50; /* x42 */ + u16 tcpWindowThreshold25; /* x44 */ + u16 tcpWindowThreshold0; /* x46 */ + u16 ipHashTableBaseHi; /* x48 */ + u16 ipHashTableBaseLo; /* x4A */ + u16 ipHashTableSize; /* x4C */ + u16 tcpHashTableBaseHi; /* x4E */ + u16 tcpHashTableBaseLo; /* x50 */ + u16 tcpHashTableSize; /* x52 */ + u16 ncbTableBaseHi; /* x54 */ + u16 ncbTableBaseLo; /* x56 */ + u16 ncbTableSize; /* x58 */ + u16 drbTableBaseHi; /* x5A */ + u16 drbTableBaseLo; /* x5C */ + u16 drbTableSize; /* x5E */ + +#define EEPROM_UNUSED_2_SIZE 4 + u8 unused_2[EEPROM_UNUSED_2_SIZE]; /* x60 */ + u16 ipReassemblyTimeout; /* x64 */ + u16 tcpMaxWindowSizeHi; /* x66 */ + u16 tcpMaxWindowSizeLo; /* x68 */ + u32 net_ip_addr0; /* x6A Added for TOE + * functionality. */ + u32 net_ip_addr1; /* x6E */ + u32 scsi_ip_addr0; /* x72 */ + u32 scsi_ip_addr1; /* x76 */ +#define EEPROM_UNUSED_3_SIZE 128 /* changed from 144 to account + * for ip addresses */ + u8 unused_3[EEPROM_UNUSED_3_SIZE]; /* x7A */ + u16 subsysVendorId_f0; /* xFA */ + u16 subsysDeviceId_f0; /* xFC */ + + /* Address = 0x7F */ +#define FM93C56A_SIGNATURE 0x9356 +#define FM93C66A_SIGNATURE 0x9366 + u16 signature; /* xFE */ + +#define EEPROM_UNUSED_4_SIZE 250 + u8 unused_4[EEPROM_UNUSED_4_SIZE]; /* x100 */ + u16 subsysVendorId_f1; /* x1FA */ + u16 subsysDeviceId_f1; /* x1FC */ + u16 checksum; /* x1FE */ + } __attribute__ ((packed)) isp4010; + struct { /* isp4022 */ + u8 asicId[4]; /* x00 */ + u8 version; /* x04 */ + u8 reserved_5; /* x05 */ + u16 boardId; /* x06 */ + u8 boardIdStr[16]; /* x08 */ + u8 serialNumber[16]; /* x18 */ + + /* External Hardware Configuration */ + u16 ext_hw_conf; /* x28 */ + + /* MAC 0 CONFIGURATION */ + struct eeprom_port_cfg macCfg_port0; /* x2A */ + + /* MAC 1 CONFIGURATION */ + struct eeprom_port_cfg macCfg_port1; /* x4A */ + + /* DDR SDRAM Configuration */ + u16 bufletSize; /* x6A */ + u16 bufletCount; /* x6C */ + u16 tcpWindowThreshold50; /* x6E */ + u16 tcpWindowThreshold25; /* x70 */ + u16 tcpWindowThreshold0; /* x72 */ + u16 ipHashTableBaseHi; /* x74 */ + u16 ipHashTableBaseLo; /* x76 */ + u16 ipHashTableSize; /* x78 */ + u16 tcpHashTableBaseHi; /* x7A */ + u16 tcpHashTableBaseLo; /* x7C */ + u16 tcpHashTableSize; /* x7E */ + u16 ncbTableBaseHi; /* x80 */ + u16 ncbTableBaseLo; /* x82 */ + u16 ncbTableSize; /* x84 */ + u16 drbTableBaseHi; /* x86 */ + u16 drbTableBaseLo; /* x88 */ + u16 drbTableSize; /* x8A */ + u16 reserved_142[4]; /* x8C */ + + /* TCP/IP Parameters */ + u16 ipReassemblyTimeout; /* x94 */ + u16 tcpMaxWindowSize; /* x96 */ + u16 ipSecurity; /* x98 */ + u8 reserved_156[294]; /* x9A */ + u16 qDebug[8]; /* QLOGIC USE ONLY x1C0 */ + struct eeprom_function_cfg funcCfg_fn0; /* x1D0 */ + u16 reserved_510; /* x1FE */ + + /* Address = 512 */ + u8 oemSpace[432]; /* x200 */ + struct bios_params sBIOSParams_fn1; /* x3B0 */ + struct eeprom_function_cfg funcCfg_fn1; /* x3D0 */ + u16 reserved_1022; /* x3FE */ + + /* Address = 1024 */ + u8 reserved_1024[464]; /* x400 */ + struct eeprom_function_cfg funcCfg_fn2; /* x5D0 */ + u16 reserved_1534; /* x5FE */ + + /* Address = 1536 */ + u8 reserved_1536[432]; /* x600 */ + struct bios_params sBIOSParams_fn3; /* x7B0 */ + struct eeprom_function_cfg funcCfg_fn3; /* x7D0 */ + u16 checksum; /* x7FE */ + } __attribute__ ((packed)) isp4022; + }; +}; + + +#endif /* _QL4XNVRM_H_ */ diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c new file mode 100644 index 000000000000..5036ebf013a5 --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -0,0 +1,1755 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ +#include <linux/moduleparam.h> + +#include <scsi/scsi_tcq.h> +#include <scsi/scsicam.h> + +#include "ql4_def.h" + +/* + * Driver version + */ +char qla4xxx_version_str[40]; + +/* + * SRB allocation cache + */ +static kmem_cache_t *srb_cachep; + +/* + * Module parameter information and variables + */ +int ql4xdiscoverywait = 60; +module_param(ql4xdiscoverywait, int, S_IRUGO | S_IRUSR); +MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time"); +int ql4xdontresethba = 0; +module_param(ql4xdontresethba, int, S_IRUGO | S_IRUSR); +MODULE_PARM_DESC(ql4xdontresethba, + "Dont reset the HBA when the driver gets 0x8002 AEN " + " default it will reset hba :0" + " set to 1 to avoid resetting HBA"); + +int extended_error_logging = 0; /* 0 = off, 1 = log errors */ +module_param(extended_error_logging, int, S_IRUGO | S_IRUSR); +MODULE_PARM_DESC(extended_error_logging, + "Option to enable extended error logging, " + "Default is 0 - no logging, 1 - debug logging"); + +/* + * SCSI host template entry points + */ + +void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha); + +/* + * iSCSI template entry points + */ +static int qla4xxx_tgt_dscvr(enum iscsi_tgt_dscvr type, uint32_t host_no, + uint32_t enable, struct sockaddr *dst_addr); +static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn, + enum iscsi_param param, char *buf); +static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess, + enum iscsi_param param, char *buf); +static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag); +static int qla4xxx_conn_start(struct iscsi_cls_conn *conn); +static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session); + +/* + * SCSI host template entry points + */ +static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, + void (*done) (struct scsi_cmnd *)); +static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd); +static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd); +static int qla4xxx_slave_alloc(struct scsi_device *device); +static int qla4xxx_slave_configure(struct scsi_device *device); +static void qla4xxx_slave_destroy(struct scsi_device *sdev); + +static struct scsi_host_template qla4xxx_driver_template = { + .module = THIS_MODULE, + .name = DRIVER_NAME, + .proc_name = DRIVER_NAME, + .queuecommand = qla4xxx_queuecommand, + + .eh_device_reset_handler = qla4xxx_eh_device_reset, + .eh_host_reset_handler = qla4xxx_eh_host_reset, + + .slave_configure = qla4xxx_slave_configure, + .slave_alloc = qla4xxx_slave_alloc, + .slave_destroy = qla4xxx_slave_destroy, + + .this_id = -1, + .cmd_per_lun = 3, + .use_clustering = ENABLE_CLUSTERING, + .sg_tablesize = SG_ALL, + + .max_sectors = 0xFFFF, +}; + +static struct iscsi_transport qla4xxx_iscsi_transport = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .param_mask = ISCSI_CONN_PORT | + ISCSI_CONN_ADDRESS | + ISCSI_TARGET_NAME | + ISCSI_TPGT, + .sessiondata_size = sizeof(struct ddb_entry), + .host_template = &qla4xxx_driver_template, + + .tgt_dscvr = qla4xxx_tgt_dscvr, + .get_conn_param = qla4xxx_conn_get_param, + .get_session_param = qla4xxx_sess_get_param, + .start_conn = qla4xxx_conn_start, + .stop_conn = qla4xxx_conn_stop, + .session_recovery_timedout = qla4xxx_recovery_timedout, +}; + +static struct scsi_transport_template *qla4xxx_scsi_transport; + +static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session) +{ + struct ddb_entry *ddb_entry = session->dd_data; + struct scsi_qla_host *ha = ddb_entry->ha; + + DEBUG2(printk("scsi%ld: %s: index [%d] port down retry count of (%d) " + "secs exhausted, marking device DEAD.\n", ha->host_no, + __func__, ddb_entry->fw_ddb_index, + ha->port_down_retry_count)); + + atomic_set(&ddb_entry->state, DDB_STATE_DEAD); + + DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc flags = " + "0x%lx\n", ha->host_no, __func__, ha->dpc_flags)); + queue_work(ha->dpc_thread, &ha->dpc_work); +} + +static int qla4xxx_conn_start(struct iscsi_cls_conn *conn) +{ + struct iscsi_cls_session *session; + struct ddb_entry *ddb_entry; + + session = iscsi_dev_to_session(conn->dev.parent); + ddb_entry = session->dd_data; + + DEBUG2(printk("scsi%ld: %s: index [%d] starting conn\n", + ddb_entry->ha->host_no, __func__, + ddb_entry->fw_ddb_index)); + iscsi_unblock_session(session); + return 0; +} + +static void qla4xxx_conn_stop(struct iscsi_cls_conn *conn, int flag) +{ + struct iscsi_cls_session *session; + struct ddb_entry *ddb_entry; + + session = iscsi_dev_to_session(conn->dev.parent); + ddb_entry = session->dd_data; + + DEBUG2(printk("scsi%ld: %s: index [%d] stopping conn\n", + ddb_entry->ha->host_no, __func__, + ddb_entry->fw_ddb_index)); + if (flag == STOP_CONN_RECOVER) + iscsi_block_session(session); + else + printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); +} + +static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess, + enum iscsi_param param, char *buf) +{ + struct ddb_entry *ddb_entry = sess->dd_data; + int len; + + switch (param) { + case ISCSI_PARAM_TARGET_NAME: + len = snprintf(buf, PAGE_SIZE - 1, "%s\n", + ddb_entry->iscsi_name); + break; + case ISCSI_PARAM_TPGT: + len = sprintf(buf, "%u\n", ddb_entry->tpgt); + break; + default: + return -ENOSYS; + } + + return len; +} + +static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn, + enum iscsi_param param, char *buf) +{ + struct iscsi_cls_session *session; + struct ddb_entry *ddb_entry; + int len; + + session = iscsi_dev_to_session(conn->dev.parent); + ddb_entry = session->dd_data; + + switch (param) { + case ISCSI_PARAM_CONN_PORT: + len = sprintf(buf, "%hu\n", ddb_entry->port); + break; + case ISCSI_PARAM_CONN_ADDRESS: + /* TODO: what are the ipv6 bits */ + len = sprintf(buf, "%u.%u.%u.%u\n", + NIPQUAD(ddb_entry->ip_addr)); + break; + default: + return -ENOSYS; + } + + return len; +} + +static int qla4xxx_tgt_dscvr(enum iscsi_tgt_dscvr type, uint32_t host_no, + uint32_t enable, struct sockaddr *dst_addr) +{ + struct scsi_qla_host *ha; + struct Scsi_Host *shost; + struct sockaddr_in *addr; + struct sockaddr_in6 *addr6; + int ret = 0; + + shost = scsi_host_lookup(host_no); + if (IS_ERR(shost)) { + printk(KERN_ERR "Could not find host no %u\n", host_no); + return -ENODEV; + } + + ha = (struct scsi_qla_host *) shost->hostdata; + + switch (type) { + case ISCSI_TGT_DSCVR_SEND_TARGETS: + if (dst_addr->sa_family == AF_INET) { + addr = (struct sockaddr_in *)dst_addr; + if (qla4xxx_send_tgts(ha, (char *)&addr->sin_addr, + addr->sin_port) != QLA_SUCCESS) + ret = -EIO; + } else if (dst_addr->sa_family == AF_INET6) { + /* + * TODO: fix qla4xxx_send_tgts + */ + addr6 = (struct sockaddr_in6 *)dst_addr; + if (qla4xxx_send_tgts(ha, (char *)&addr6->sin6_addr, + addr6->sin6_port) != QLA_SUCCESS) + ret = -EIO; + } else + ret = -ENOSYS; + break; + default: + ret = -ENOSYS; + } + + scsi_host_put(shost); + return ret; +} + +void qla4xxx_destroy_sess(struct ddb_entry *ddb_entry) +{ + if (!ddb_entry->sess) + return; + + if (ddb_entry->conn) { + iscsi_if_destroy_session_done(ddb_entry->conn); + iscsi_destroy_conn(ddb_entry->conn); + iscsi_remove_session(ddb_entry->sess); + } + iscsi_free_session(ddb_entry->sess); +} + +int qla4xxx_add_sess(struct ddb_entry *ddb_entry) +{ + int err; + + err = iscsi_add_session(ddb_entry->sess, ddb_entry->fw_ddb_index); + if (err) { + DEBUG2(printk(KERN_ERR "Could not add session.\n")); + return err; + } + + ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0); + if (!ddb_entry->conn) { + iscsi_remove_session(ddb_entry->sess); + DEBUG2(printk(KERN_ERR "Could not add connection.\n")); + return -ENOMEM; + } + + ddb_entry->sess->recovery_tmo = ddb_entry->ha->port_down_retry_count; + iscsi_if_create_session_done(ddb_entry->conn); + return 0; +} + +struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha) +{ + struct ddb_entry *ddb_entry; + struct iscsi_cls_session *sess; + + sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport); + if (!sess) + return NULL; + + ddb_entry = sess->dd_data; + memset(ddb_entry, 0, sizeof(*ddb_entry)); + ddb_entry->ha = ha; + ddb_entry->sess = sess; + return ddb_entry; +} + +/* + * Timer routines + */ + +static void qla4xxx_start_timer(struct scsi_qla_host *ha, void *func, + unsigned long interval) +{ + DEBUG(printk("scsi: %s: Starting timer thread for adapter %d\n", + __func__, ha->host->host_no)); + init_timer(&ha->timer); + ha->timer.expires = jiffies + interval * HZ; + ha->timer.data = (unsigned long)ha; + ha->timer.function = (void (*)(unsigned long))func; + add_timer(&ha->timer); + ha->timer_active = 1; +} + +static void qla4xxx_stop_timer(struct scsi_qla_host *ha) +{ + del_timer_sync(&ha->timer); + ha->timer_active = 0; +} + +/*** + * qla4xxx_mark_device_missing - mark a device as missing. + * @ha: Pointer to host adapter structure. + * @ddb_entry: Pointer to device database entry + * + * This routine marks a device missing and resets the relogin retry count. + **/ +void qla4xxx_mark_device_missing(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry) +{ + atomic_set(&ddb_entry->state, DDB_STATE_MISSING); + DEBUG3(printk("scsi%d:%d:%d: index [%d] marked MISSING\n", + ha->host_no, ddb_entry->bus, ddb_entry->target, + ddb_entry->fw_ddb_index)); + iscsi_conn_error(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); +} + +static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, + struct ddb_entry *ddb_entry, + struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct srb *srb; + + srb = mempool_alloc(ha->srb_mempool, GFP_ATOMIC); + if (!srb) + return srb; + + atomic_set(&srb->ref_count, 1); + srb->ha = ha; + srb->ddb = ddb_entry; + srb->cmd = cmd; + srb->flags = 0; + cmd->SCp.ptr = (void *)srb; + cmd->scsi_done = done; + + return srb; +} + +static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb) +{ + struct scsi_cmnd *cmd = srb->cmd; + + if (srb->flags & SRB_DMA_VALID) { + if (cmd->use_sg) { + pci_unmap_sg(ha->pdev, cmd->request_buffer, + cmd->use_sg, cmd->sc_data_direction); + } else if (cmd->request_bufflen) { + pci_unmap_single(ha->pdev, srb->dma_handle, + cmd->request_bufflen, + cmd->sc_data_direction); + } + srb->flags &= ~SRB_DMA_VALID; + } + cmd->SCp.ptr = NULL; +} + +void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb) +{ + struct scsi_cmnd *cmd = srb->cmd; + + qla4xxx_srb_free_dma(ha, srb); + + mempool_free(srb, ha->srb_mempool); + + cmd->scsi_done(cmd); +} + +/** + * qla4xxx_queuecommand - scsi layer issues scsi command to driver. + * @cmd: Pointer to Linux's SCSI command structure + * @done_fn: Function that the driver calls to notify the SCSI mid-layer + * that the command has been processed. + * + * Remarks: + * This routine is invoked by Linux to send a SCSI command to the driver. + * The mid-level driver tries to ensure that queuecommand never gets + * invoked concurrently with itself or the interrupt handler (although + * the interrupt handler may call this routine as part of request- + * completion handling). Unfortunely, it sometimes calls the scheduler + * in interrupt context which is a big NO! NO!. + **/ +static int qla4xxx_queuecommand(struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + struct ddb_entry *ddb_entry = cmd->device->hostdata; + struct srb *srb; + int rval; + + if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + if (atomic_read(&ddb_entry->state) == DDB_STATE_DEAD) { + cmd->result = DID_NO_CONNECT << 16; + goto qc_fail_command; + } + goto qc_host_busy; + } + + spin_unlock_irq(ha->host->host_lock); + + srb = qla4xxx_get_new_srb(ha, ddb_entry, cmd, done); + if (!srb) + goto qc_host_busy_lock; + + rval = qla4xxx_send_command_to_isp(ha, srb); + if (rval != QLA_SUCCESS) + goto qc_host_busy_free_sp; + + spin_lock_irq(ha->host->host_lock); + return 0; + +qc_host_busy_free_sp: + qla4xxx_srb_free_dma(ha, srb); + mempool_free(srb, ha->srb_mempool); + +qc_host_busy_lock: + spin_lock_irq(ha->host->host_lock); + +qc_host_busy: + return SCSI_MLQUEUE_HOST_BUSY; + +qc_fail_command: + done(cmd); + + return 0; +} + +/** + * qla4xxx_mem_free - frees memory allocated to adapter + * @ha: Pointer to host adapter structure. + * + * Frees memory previously allocated by qla4xxx_mem_alloc + **/ +static void qla4xxx_mem_free(struct scsi_qla_host *ha) +{ + if (ha->queues) + dma_free_coherent(&ha->pdev->dev, ha->queues_len, ha->queues, + ha->queues_dma); + + ha->queues_len = 0; + ha->queues = NULL; + ha->queues_dma = 0; + ha->request_ring = NULL; + ha->request_dma = 0; + ha->response_ring = NULL; + ha->response_dma = 0; + ha->shadow_regs = NULL; + ha->shadow_regs_dma = 0; + + /* Free srb pool. */ + if (ha->srb_mempool) + mempool_destroy(ha->srb_mempool); + + ha->srb_mempool = NULL; + + /* release io space registers */ + if (ha->reg) + iounmap(ha->reg); + pci_release_regions(ha->pdev); +} + +/** + * qla4xxx_mem_alloc - allocates memory for use by adapter. + * @ha: Pointer to host adapter structure + * + * Allocates DMA memory for request and response queues. Also allocates memory + * for srbs. + **/ +static int qla4xxx_mem_alloc(struct scsi_qla_host *ha) +{ + unsigned long align; + + /* Allocate contiguous block of DMA memory for queues. */ + ha->queues_len = ((REQUEST_QUEUE_DEPTH * QUEUE_SIZE) + + (RESPONSE_QUEUE_DEPTH * QUEUE_SIZE) + + sizeof(struct shadow_regs) + + MEM_ALIGN_VALUE + + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); + ha->queues = dma_alloc_coherent(&ha->pdev->dev, ha->queues_len, + &ha->queues_dma, GFP_KERNEL); + if (ha->queues == NULL) { + dev_warn(&ha->pdev->dev, + "Memory Allocation failed - queues.\n"); + + goto mem_alloc_error_exit; + } + memset(ha->queues, 0, ha->queues_len); + + /* + * As per RISC alignment requirements -- the bus-address must be a + * multiple of the request-ring size (in bytes). + */ + align = 0; + if ((unsigned long)ha->queues_dma & (MEM_ALIGN_VALUE - 1)) + align = MEM_ALIGN_VALUE - ((unsigned long)ha->queues_dma & + (MEM_ALIGN_VALUE - 1)); + + /* Update request and response queue pointers. */ + ha->request_dma = ha->queues_dma + align; + ha->request_ring = (struct queue_entry *) (ha->queues + align); + ha->response_dma = ha->queues_dma + align + + (REQUEST_QUEUE_DEPTH * QUEUE_SIZE); + ha->response_ring = (struct queue_entry *) (ha->queues + align + + (REQUEST_QUEUE_DEPTH * + QUEUE_SIZE)); + ha->shadow_regs_dma = ha->queues_dma + align + + (REQUEST_QUEUE_DEPTH * QUEUE_SIZE) + + (RESPONSE_QUEUE_DEPTH * QUEUE_SIZE); + ha->shadow_regs = (struct shadow_regs *) (ha->queues + align + + (REQUEST_QUEUE_DEPTH * + QUEUE_SIZE) + + (RESPONSE_QUEUE_DEPTH * + QUEUE_SIZE)); + + /* Allocate memory for srb pool. */ + ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab, + mempool_free_slab, srb_cachep); + if (ha->srb_mempool == NULL) { + dev_warn(&ha->pdev->dev, + "Memory Allocation failed - SRB Pool.\n"); + + goto mem_alloc_error_exit; + } + + return QLA_SUCCESS; + +mem_alloc_error_exit: + qla4xxx_mem_free(ha); + return QLA_ERROR; +} + +/** + * qla4xxx_timer - checks every second for work to do. + * @ha: Pointer to host adapter structure. + **/ +static void qla4xxx_timer(struct scsi_qla_host *ha) +{ + struct ddb_entry *ddb_entry, *dtemp; + int start_dpc = 0; + + /* Search for relogin's to time-out and port down retry. */ + list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) { + /* Count down time between sending relogins */ + if (adapter_up(ha) && + !test_bit(DF_RELOGIN, &ddb_entry->flags) && + atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) { + if (atomic_read(&ddb_entry->retry_relogin_timer) != + INVALID_ENTRY) { + if (atomic_read(&ddb_entry->retry_relogin_timer) + == 0) { + atomic_set(&ddb_entry-> + retry_relogin_timer, + INVALID_ENTRY); + set_bit(DPC_RELOGIN_DEVICE, + &ha->dpc_flags); + set_bit(DF_RELOGIN, &ddb_entry->flags); + DEBUG2(printk("scsi%ld: %s: index [%d]" + " login device\n", + ha->host_no, __func__, + ddb_entry->fw_ddb_index)); + } else + atomic_dec(&ddb_entry-> + retry_relogin_timer); + } + } + + /* Wait for relogin to timeout */ + if (atomic_read(&ddb_entry->relogin_timer) && + (atomic_dec_and_test(&ddb_entry->relogin_timer) != 0)) { + /* + * If the relogin times out and the device is + * still NOT ONLINE then try and relogin again. + */ + if (atomic_read(&ddb_entry->state) != + DDB_STATE_ONLINE && + ddb_entry->fw_ddb_device_state == + DDB_DS_SESSION_FAILED) { + /* Reset retry relogin timer */ + atomic_inc(&ddb_entry->relogin_retry_count); + DEBUG2(printk("scsi%ld: index[%d] relogin" + " timed out-retrying" + " relogin (%d)\n", + ha->host_no, + ddb_entry->fw_ddb_index, + atomic_read(&ddb_entry-> + relogin_retry_count)) + ); + start_dpc++; + DEBUG(printk("scsi%ld:%d:%d: index [%d] " + "initate relogin after" + " %d seconds\n", + ha->host_no, ddb_entry->bus, + ddb_entry->target, + ddb_entry->fw_ddb_index, + ddb_entry->default_time2wait + 4) + ); + + atomic_set(&ddb_entry->retry_relogin_timer, + ddb_entry->default_time2wait + 4); + } + } + } + + /* Check for heartbeat interval. */ + if (ha->firmware_options & FWOPT_HEARTBEAT_ENABLE && + ha->heartbeat_interval != 0) { + ha->seconds_since_last_heartbeat++; + if (ha->seconds_since_last_heartbeat > + ha->heartbeat_interval + 2) + set_bit(DPC_RESET_HA, &ha->dpc_flags); + } + + + /* Wakeup the dpc routine for this adapter, if needed. */ + if ((start_dpc || + test_bit(DPC_RESET_HA, &ha->dpc_flags) || + test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags) || + test_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags) || + test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) || + test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || + test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) || + test_bit(DPC_AEN, &ha->dpc_flags)) && + ha->dpc_thread) { + DEBUG2(printk("scsi%ld: %s: scheduling dpc routine" + " - dpc flags = 0x%lx\n", + ha->host_no, __func__, ha->dpc_flags)); + queue_work(ha->dpc_thread, &ha->dpc_work); + } + + /* Reschedule timer thread to call us back in one second */ + mod_timer(&ha->timer, jiffies + HZ); + + DEBUG2(ha->seconds_since_last_intr++); +} + +/** + * qla4xxx_cmd_wait - waits for all outstanding commands to complete + * @ha: Pointer to host adapter structure. + * + * This routine stalls the driver until all outstanding commands are returned. + * Caller must release the Hardware Lock prior to calling this routine. + **/ +static int qla4xxx_cmd_wait(struct scsi_qla_host *ha) +{ + uint32_t index = 0; + int stat = QLA_SUCCESS; + unsigned long flags; + struct scsi_cmnd *cmd; + int wait_cnt = WAIT_CMD_TOV; /* + * Initialized for 30 seconds as we + * expect all commands to retuned + * ASAP. + */ + + while (wait_cnt) { + spin_lock_irqsave(&ha->hardware_lock, flags); + /* Find a command that hasn't completed. */ + for (index = 0; index < ha->host->can_queue; index++) { + cmd = scsi_host_find_tag(ha->host, index); + if (cmd != NULL) + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* If No Commands are pending, wait is complete */ + if (index == ha->host->can_queue) { + break; + } + + /* If we timed out on waiting for commands to come back + * return ERROR. + */ + wait_cnt--; + if (wait_cnt == 0) + stat = QLA_ERROR; + else { + msleep(1000); + } + } /* End of While (wait_cnt) */ + + return stat; +} + +/** + * qla4010_soft_reset - performs soft reset. + * @ha: Pointer to host adapter structure. + **/ +static int qla4010_soft_reset(struct scsi_qla_host *ha) +{ + uint32_t max_wait_time; + unsigned long flags = 0; + int status = QLA_ERROR; + uint32_t ctrl_status; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* + * If the SCSI Reset Interrupt bit is set, clear it. + * Otherwise, the Soft Reset won't work. + */ + ctrl_status = readw(&ha->reg->ctrl_status); + if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0) + writel(set_rmask(CSR_SCSI_RESET_INTR), &ha->reg->ctrl_status); + + /* Issue Soft Reset */ + writel(set_rmask(CSR_SOFT_RESET), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait until the Network Reset Intr bit is cleared */ + max_wait_time = RESET_INTR_TOV; + do { + spin_lock_irqsave(&ha->hardware_lock, flags); + ctrl_status = readw(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if ((ctrl_status & CSR_NET_RESET_INTR) == 0) + break; + + msleep(1000); + } while ((--max_wait_time)); + + if ((ctrl_status & CSR_NET_RESET_INTR) != 0) { + DEBUG2(printk(KERN_WARNING + "scsi%ld: Network Reset Intr not cleared by " + "Network function, clearing it now!\n", + ha->host_no)); + spin_lock_irqsave(&ha->hardware_lock, flags); + writel(set_rmask(CSR_NET_RESET_INTR), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } + + /* Wait until the firmware tells us the Soft Reset is done */ + max_wait_time = SOFT_RESET_TOV; + do { + spin_lock_irqsave(&ha->hardware_lock, flags); + ctrl_status = readw(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if ((ctrl_status & CSR_SOFT_RESET) == 0) { + status = QLA_SUCCESS; + break; + } + + msleep(1000); + } while ((--max_wait_time)); + + /* + * Also, make sure that the SCSI Reset Interrupt bit has been cleared + * after the soft reset has taken place. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + ctrl_status = readw(&ha->reg->ctrl_status); + if ((ctrl_status & CSR_SCSI_RESET_INTR) != 0) { + writel(set_rmask(CSR_SCSI_RESET_INTR), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* If soft reset fails then most probably the bios on other + * function is also enabled. + * Since the initialization is sequential the other fn + * wont be able to acknowledge the soft reset. + * Issue a force soft reset to workaround this scenario. + */ + if (max_wait_time == 0) { + /* Issue Force Soft Reset */ + spin_lock_irqsave(&ha->hardware_lock, flags); + writel(set_rmask(CSR_FORCE_SOFT_RESET), &ha->reg->ctrl_status); + readl(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + /* Wait until the firmware tells us the Soft Reset is done */ + max_wait_time = SOFT_RESET_TOV; + do { + spin_lock_irqsave(&ha->hardware_lock, flags); + ctrl_status = readw(&ha->reg->ctrl_status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if ((ctrl_status & CSR_FORCE_SOFT_RESET) == 0) { + status = QLA_SUCCESS; + break; + } + + msleep(1000); + } while ((--max_wait_time)); + } + + return status; +} + +/** + * qla4xxx_topcat_reset - performs hard reset of TopCat Chip. + * @ha: Pointer to host adapter structure. + **/ +static int qla4xxx_topcat_reset(struct scsi_qla_host *ha) +{ + unsigned long flags; + + ql4xxx_lock_nvram(ha); + spin_lock_irqsave(&ha->hardware_lock, flags); + writel(set_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha)); + readl(isp_gp_out(ha)); + mdelay(1); + + writel(clr_rmask(GPOR_TOPCAT_RESET), isp_gp_out(ha)); + readl(isp_gp_out(ha)); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + mdelay(2523); + + ql4xxx_unlock_nvram(ha); + return QLA_SUCCESS; +} + +/** + * qla4xxx_flush_active_srbs - returns all outstanding i/o requests to O.S. + * @ha: Pointer to host adapter structure. + * + * This routine is called just prior to a HARD RESET to return all + * outstanding commands back to the Operating System. + * Caller should make sure that the following locks are released + * before this calling routine: Hardware lock, and io_request_lock. + **/ +static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha) +{ + struct srb *srb; + int i; + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + for (i = 0; i < ha->host->can_queue; i++) { + srb = qla4xxx_del_from_active_array(ha, i); + if (srb != NULL) { + srb->cmd->result = DID_RESET << 16; + qla4xxx_srb_compl(ha, srb); + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + +} + +/** + * qla4xxx_hard_reset - performs HBA Hard Reset + * @ha: Pointer to host adapter structure. + **/ +static int qla4xxx_hard_reset(struct scsi_qla_host *ha) +{ + /* The QLA4010 really doesn't have an equivalent to a hard reset */ + qla4xxx_flush_active_srbs(ha); + if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) { + int status = QLA_ERROR; + + if ((qla4010_soft_reset(ha) == QLA_SUCCESS) && + (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) && + (qla4010_soft_reset(ha) == QLA_SUCCESS)) + status = QLA_SUCCESS; + return status; + } else + return qla4010_soft_reset(ha); +} + +/** + * qla4xxx_recover_adapter - recovers adapter after a fatal error + * @ha: Pointer to host adapter structure. + * @renew_ddb_list: Indicates what to do with the adapter's ddb list + * after adapter recovery has completed. + * 0=preserve ddb list, 1=destroy and rebuild ddb list + **/ +static int qla4xxx_recover_adapter(struct scsi_qla_host *ha, + uint8_t renew_ddb_list) +{ + int status; + + /* Stall incoming I/O until we are done */ + clear_bit(AF_ONLINE, &ha->flags); + DEBUG2(printk("scsi%ld: %s calling qla4xxx_cmd_wait\n", ha->host_no, + __func__)); + + /* Wait for outstanding commands to complete. + * Stalls the driver for max 30 secs + */ + status = qla4xxx_cmd_wait(ha); + + qla4xxx_disable_intrs(ha); + + /* Flush any pending ddb changed AENs */ + qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); + + /* Reset the firmware. If successful, function + * returns with ISP interrupts enabled. + */ + if (status == QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n", + ha->host_no, __func__)); + status = qla4xxx_soft_reset(ha); + } + /* FIXMEkaren: Do we want to keep interrupts enabled and process + AENs after soft reset */ + + /* If firmware (SOFT) reset failed, or if all outstanding + * commands have not returned, then do a HARD reset. + */ + if (status == QLA_ERROR) { + DEBUG2(printk("scsi%ld: %s - Performing hard reset..\n", + ha->host_no, __func__)); + status = qla4xxx_hard_reset(ha); + } + + /* Flush any pending ddb changed AENs */ + qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS); + + /* Re-initialize firmware. If successful, function returns + * with ISP interrupts enabled */ + if (status == QLA_SUCCESS) { + DEBUG2(printk("scsi%ld: %s - Initializing adapter..\n", + ha->host_no, __func__)); + + /* If successful, AF_ONLINE flag set in + * qla4xxx_initialize_adapter */ + status = qla4xxx_initialize_adapter(ha, renew_ddb_list); + } + + /* Failed adapter initialization? + * Retry reset_ha only if invoked via DPC (DPC_RESET_HA) */ + if ((test_bit(AF_ONLINE, &ha->flags) == 0) && + (test_bit(DPC_RESET_HA, &ha->dpc_flags))) { + /* Adapter initialization failed, see if we can retry + * resetting the ha */ + if (!test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags)) { + ha->retry_reset_ha_cnt = MAX_RESET_HA_RETRIES; + DEBUG2(printk("scsi%ld: recover adapter - retrying " + "(%d) more times\n", ha->host_no, + ha->retry_reset_ha_cnt)); + set_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); + status = QLA_ERROR; + } else { + if (ha->retry_reset_ha_cnt > 0) { + /* Schedule another Reset HA--DPC will retry */ + ha->retry_reset_ha_cnt--; + DEBUG2(printk("scsi%ld: recover adapter - " + "retry remaining %d\n", + ha->host_no, + ha->retry_reset_ha_cnt)); + status = QLA_ERROR; + } + + if (ha->retry_reset_ha_cnt == 0) { + /* Recover adapter retries have been exhausted. + * Adapter DEAD */ + DEBUG2(printk("scsi%ld: recover adapter " + "failed - board disabled\n", + ha->host_no)); + qla4xxx_flush_active_srbs(ha); + clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); + clear_bit(DPC_RESET_HA, &ha->dpc_flags); + clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, + &ha->dpc_flags); + status = QLA_ERROR; + } + } + } else { + clear_bit(DPC_RESET_HA, &ha->dpc_flags); + clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags); + clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags); + } + + ha->adapter_error_count++; + + if (status == QLA_SUCCESS) + qla4xxx_enable_intrs(ha); + + DEBUG2(printk("scsi%ld: recover adapter .. DONE\n", ha->host_no)); + return status; +} + +/** + * qla4xxx_do_dpc - dpc routine + * @data: in our case pointer to adapter structure + * + * This routine is a task that is schedule by the interrupt handler + * to perform the background processing for interrupts. We put it + * on a task queue that is consumed whenever the scheduler runs; that's + * so you can do anything (i.e. put the process to sleep etc). In fact, + * the mid-level tries to sleep when it reaches the driver threshold + * "host->can_queue". This can cause a panic if we were in our interrupt code. + **/ +static void qla4xxx_do_dpc(void *data) +{ + struct scsi_qla_host *ha = (struct scsi_qla_host *) data; + struct ddb_entry *ddb_entry, *dtemp; + + DEBUG2(printk("scsi%ld: %s: DPC handler waking up.\n", + ha->host_no, __func__)); + + DEBUG2(printk("scsi%ld: %s: ha->flags = 0x%08lx\n", + ha->host_no, __func__, ha->flags)); + DEBUG2(printk("scsi%ld: %s: ha->dpc_flags = 0x%08lx\n", + ha->host_no, __func__, ha->dpc_flags)); + + /* Initialization not yet finished. Don't do anything yet. */ + if (!test_bit(AF_INIT_DONE, &ha->flags)) + return; + + if (adapter_up(ha) || + test_bit(DPC_RESET_HA, &ha->dpc_flags) || + test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) || + test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) { + if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) + /* + * dg 09/23 Never initialize ddb list + * once we up and running + * qla4xxx_recover_adapter(ha, + * REBUILD_DDB_LIST); + */ + qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST); + + if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) + qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST); + + if (test_and_clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) { + uint8_t wait_time = RESET_INTR_TOV; + unsigned long flags = 0; + + qla4xxx_flush_active_srbs(ha); + + spin_lock_irqsave(&ha->hardware_lock, flags); + while ((readw(&ha->reg->ctrl_status) & + (CSR_SOFT_RESET | CSR_FORCE_SOFT_RESET)) != 0) { + if (--wait_time == 0) + break; + + spin_unlock_irqrestore(&ha->hardware_lock, + flags); + + msleep(1000); + + spin_lock_irqsave(&ha->hardware_lock, flags); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (wait_time == 0) + DEBUG2(printk("scsi%ld: %s: SR|FSR " + "bit not cleared-- resetting\n", + ha->host_no, __func__)); + } + } + + /* ---- process AEN? --- */ + if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags)) + qla4xxx_process_aen(ha, PROCESS_ALL_AENS); + + /* ---- Get DHCP IP Address? --- */ + if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags)) + qla4xxx_get_dhcp_ip_address(ha); + + /* ---- relogin device? --- */ + if (adapter_up(ha) && + test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) { + list_for_each_entry_safe(ddb_entry, dtemp, + &ha->ddb_list, list) { + if (test_and_clear_bit(DF_RELOGIN, &ddb_entry->flags) && + atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) + qla4xxx_relogin_device(ha, ddb_entry); + + /* + * If mbx cmd times out there is no point + * in continuing further. + * With large no of targets this can hang + * the system. + */ + if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) { + printk(KERN_WARNING "scsi%ld: %s: " + "need to reset hba\n", + ha->host_no, __func__); + break; + } + } + } +} + +/** + * qla4xxx_free_adapter - release the adapter + * @ha: pointer to adapter structure + **/ +static void qla4xxx_free_adapter(struct scsi_qla_host *ha) +{ + + if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) { + /* Turn-off interrupts on the card. */ + qla4xxx_disable_intrs(ha); + } + + /* Kill the kernel thread for this host */ + if (ha->dpc_thread) + destroy_workqueue(ha->dpc_thread); + + /* Issue Soft Reset to put firmware in unknown state */ + qla4xxx_soft_reset(ha); + + /* Remove timer thread, if present */ + if (ha->timer_active) + qla4xxx_stop_timer(ha); + + /* free extra memory */ + qla4xxx_mem_free(ha); + + /* Detach interrupts */ + if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags)) + free_irq(ha->pdev->irq, ha); + + pci_disable_device(ha->pdev); + +} + +/*** + * qla4xxx_iospace_config - maps registers + * @ha: pointer to adapter structure + * + * This routines maps HBA's registers from the pci address space + * into the kernel virtual address space for memory mapped i/o. + **/ +static int qla4xxx_iospace_config(struct scsi_qla_host *ha) +{ + unsigned long pio, pio_len, pio_flags; + unsigned long mmio, mmio_len, mmio_flags; + + pio = pci_resource_start(ha->pdev, 0); + pio_len = pci_resource_len(ha->pdev, 0); + pio_flags = pci_resource_flags(ha->pdev, 0); + if (pio_flags & IORESOURCE_IO) { + if (pio_len < MIN_IOBASE_LEN) { + dev_warn(&ha->pdev->dev, + "Invalid PCI I/O region size\n"); + pio = 0; + } + } else { + dev_warn(&ha->pdev->dev, "region #0 not a PIO resource\n"); + pio = 0; + } + + /* Use MMIO operations for all accesses. */ + mmio = pci_resource_start(ha->pdev, 1); + mmio_len = pci_resource_len(ha->pdev, 1); + mmio_flags = pci_resource_flags(ha->pdev, 1); + + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&ha->pdev->dev, + "region #0 not an MMIO resource, aborting\n"); + + goto iospace_error_exit; + } + if (mmio_len < MIN_IOBASE_LEN) { + dev_err(&ha->pdev->dev, + "Invalid PCI mem region size, aborting\n"); + goto iospace_error_exit; + } + + if (pci_request_regions(ha->pdev, DRIVER_NAME)) { + dev_warn(&ha->pdev->dev, + "Failed to reserve PIO/MMIO regions\n"); + + goto iospace_error_exit; + } + + ha->pio_address = pio; + ha->pio_length = pio_len; + ha->reg = ioremap(mmio, MIN_IOBASE_LEN); + if (!ha->reg) { + dev_err(&ha->pdev->dev, + "cannot remap MMIO, aborting\n"); + + goto iospace_error_exit; + } + + return 0; + +iospace_error_exit: + return -ENOMEM; +} + +/** + * qla4xxx_probe_adapter - callback function to probe HBA + * @pdev: pointer to pci_dev structure + * @pci_device_id: pointer to pci_device entry + * + * This routine will probe for Qlogic 4xxx iSCSI host adapters. + * It returns zero if successful. It also initializes all data necessary for + * the driver. + **/ +static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret = -ENODEV, status; + struct Scsi_Host *host; + struct scsi_qla_host *ha; + struct ddb_entry *ddb_entry, *ddbtemp; + uint8_t init_retry_count = 0; + char buf[34]; + + if (pci_enable_device(pdev)) + return -1; + + host = scsi_host_alloc(&qla4xxx_driver_template, sizeof(*ha)); + if (host == NULL) { + printk(KERN_WARNING + "qla4xxx: Couldn't allocate host from scsi layer!\n"); + goto probe_disable_device; + } + + /* Clear our data area */ + ha = (struct scsi_qla_host *) host->hostdata; + memset(ha, 0, sizeof(*ha)); + + /* Save the information from PCI BIOS. */ + ha->pdev = pdev; + ha->host = host; + ha->host_no = host->host_no; + + /* Configure PCI I/O space. */ + ret = qla4xxx_iospace_config(ha); + if (ret) + goto probe_failed; + + dev_info(&ha->pdev->dev, "Found an ISP%04x, irq %d, iobase 0x%p\n", + pdev->device, pdev->irq, ha->reg); + + qla4xxx_config_dma_addressing(ha); + + /* Initialize lists and spinlocks. */ + INIT_LIST_HEAD(&ha->ddb_list); + INIT_LIST_HEAD(&ha->free_srb_q); + + mutex_init(&ha->mbox_sem); + init_waitqueue_head(&ha->mailbox_wait_queue); + + spin_lock_init(&ha->hardware_lock); + spin_lock_init(&ha->list_lock); + + /* Allocate dma buffers */ + if (qla4xxx_mem_alloc(ha)) { + dev_warn(&ha->pdev->dev, + "[ERROR] Failed to allocate memory for adapter\n"); + + ret = -ENOMEM; + goto probe_failed; + } + + /* + * Initialize the Host adapter request/response queues and + * firmware + * NOTE: interrupts enabled upon successful completion + */ + status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST); + while (status == QLA_ERROR && init_retry_count++ < MAX_INIT_RETRIES) { + DEBUG2(printk("scsi: %s: retrying adapter initialization " + "(%d)\n", __func__, init_retry_count)); + qla4xxx_soft_reset(ha); + status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST); + } + if (status == QLA_ERROR) { + dev_warn(&ha->pdev->dev, "Failed to initialize adapter\n"); + + ret = -ENODEV; + goto probe_failed; + } + + host->cmd_per_lun = 3; + host->max_channel = 0; + host->max_lun = MAX_LUNS - 1; + host->max_id = MAX_TARGETS; + host->max_cmd_len = IOCB_MAX_CDB_LEN; + host->can_queue = MAX_SRBS ; + host->transportt = qla4xxx_scsi_transport; + + ret = scsi_init_shared_tag_map(host, MAX_SRBS); + if (ret) { + dev_warn(&ha->pdev->dev, "scsi_init_shared_tag_map failed"); + goto probe_failed; + } + + /* Startup the kernel thread for this host adapter. */ + DEBUG2(printk("scsi: %s: Starting kernel thread for " + "qla4xxx_dpc\n", __func__)); + sprintf(buf, "qla4xxx_%lu_dpc", ha->host_no); + ha->dpc_thread = create_singlethread_workqueue(buf); + if (!ha->dpc_thread) { + dev_warn(&ha->pdev->dev, "Unable to start DPC thread!\n"); + ret = -ENODEV; + goto probe_failed; + } + INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc, ha); + + ret = request_irq(pdev->irq, qla4xxx_intr_handler, + SA_INTERRUPT|SA_SHIRQ, "qla4xxx", ha); + if (ret) { + dev_warn(&ha->pdev->dev, "Failed to reserve interrupt %d" + " already in use.\n", pdev->irq); + goto probe_failed; + } + set_bit(AF_IRQ_ATTACHED, &ha->flags); + host->irq = pdev->irq; + DEBUG(printk("scsi%d: irq %d attached\n", ha->host_no, ha->pdev->irq)); + + qla4xxx_enable_intrs(ha); + + /* Start timer thread. */ + qla4xxx_start_timer(ha, qla4xxx_timer, 1); + + set_bit(AF_INIT_DONE, &ha->flags); + + pci_set_drvdata(pdev, ha); + + ret = scsi_add_host(host, &pdev->dev); + if (ret) + goto probe_failed; + + /* Update transport device information for all devices. */ + list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) { + if (ddb_entry->fw_ddb_device_state == DDB_DS_SESSION_ACTIVE) + if (qla4xxx_add_sess(ddb_entry)) + goto remove_host; + } + + printk(KERN_INFO + " QLogic iSCSI HBA Driver version: %s\n" + " QLogic ISP%04x @ %s, host#=%ld, fw=%02d.%02d.%02d.%02d\n", + qla4xxx_version_str, ha->pdev->device, pci_name(ha->pdev), + ha->host_no, ha->firmware_version[0], ha->firmware_version[1], + ha->patch_number, ha->build_number); + + return 0; + +remove_host: + qla4xxx_free_ddb_list(ha); + scsi_remove_host(host); + +probe_failed: + qla4xxx_free_adapter(ha); + scsi_host_put(ha->host); + +probe_disable_device: + pci_disable_device(pdev); + + return ret; +} + +/** + * qla4xxx_remove_adapter - calback function to remove adapter. + * @pci_dev: PCI device pointer + **/ +static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev) +{ + struct scsi_qla_host *ha; + + ha = pci_get_drvdata(pdev); + + /* remove devs from iscsi_sessions to scsi_devices */ + qla4xxx_free_ddb_list(ha); + + scsi_remove_host(ha->host); + + qla4xxx_free_adapter(ha); + + scsi_host_put(ha->host); + + pci_set_drvdata(pdev, NULL); +} + +/** + * qla4xxx_config_dma_addressing() - Configure OS DMA addressing method. + * @ha: HA context + * + * At exit, the @ha's flags.enable_64bit_addressing set to indicated + * supported addressing method. + */ +void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha) +{ + int retval; + + /* Update our PCI device dma_mask for full 64 bit mask */ + if (pci_set_dma_mask(ha->pdev, DMA_64BIT_MASK) == 0) { + if (pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) { + dev_dbg(&ha->pdev->dev, + "Failed to set 64 bit PCI consistent mask; " + "using 32 bit.\n"); + retval = pci_set_consistent_dma_mask(ha->pdev, + DMA_32BIT_MASK); + } + } else + retval = pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK); +} + +static int qla4xxx_slave_alloc(struct scsi_device *sdev) +{ + struct iscsi_cls_session *sess = starget_to_session(sdev->sdev_target); + struct ddb_entry *ddb = sess->dd_data; + + sdev->hostdata = ddb; + sdev->tagged_supported = 1; + scsi_activate_tcq(sdev, sdev->host->can_queue); + return 0; +} + +static int qla4xxx_slave_configure(struct scsi_device *sdev) +{ + sdev->tagged_supported = 1; + return 0; +} + +static void qla4xxx_slave_destroy(struct scsi_device *sdev) +{ + scsi_deactivate_tcq(sdev, 1); +} + +/** + * qla4xxx_del_from_active_array - returns an active srb + * @ha: Pointer to host adapter structure. + * @index: index into to the active_array + * + * This routine removes and returns the srb at the specified index + **/ +struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index) +{ + struct srb *srb = NULL; + struct scsi_cmnd *cmd; + + if (!(cmd = scsi_host_find_tag(ha->host, index))) + return srb; + + if (!(srb = (struct srb *)cmd->host_scribble)) + return srb; + + /* update counters */ + if (srb->flags & SRB_DMA_VALID) { + ha->req_q_count += srb->iocb_cnt; + ha->iocb_cnt -= srb->iocb_cnt; + if (srb->cmd) + srb->cmd->host_scribble = NULL; + } + return srb; +} + +/** + * qla4xxx_soft_reset - performs a SOFT RESET of hba. + * @ha: Pointer to host adapter structure. + **/ +int qla4xxx_soft_reset(struct scsi_qla_host *ha) +{ + + DEBUG2(printk(KERN_WARNING "scsi%ld: %s: chip reset!\n", ha->host_no, + __func__)); + if (test_bit(AF_TOPCAT_CHIP_PRESENT, &ha->flags)) { + int status = QLA_ERROR; + + if ((qla4010_soft_reset(ha) == QLA_SUCCESS) && + (qla4xxx_topcat_reset(ha) == QLA_SUCCESS) && + (qla4010_soft_reset(ha) == QLA_SUCCESS) ) + status = QLA_SUCCESS; + return status; + } else + return qla4010_soft_reset(ha); +} + +/** + * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware + * @ha: actual ha whose done queue will contain the comd returned by firmware. + * @cmd: Scsi Command to wait on. + * + * This routine waits for the command to be returned by the Firmware + * for some max time. + **/ +static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha, + struct scsi_cmnd *cmd) +{ + int done = 0; + struct srb *rp; + uint32_t max_wait_time = EH_WAIT_CMD_TOV; + + do { + /* Checking to see if its returned to OS */ + rp = (struct srb *) cmd->SCp.ptr; + if (rp == NULL) { + done++; + break; + } + + msleep(2000); + } while (max_wait_time--); + + return done; +} + +/** + * qla4xxx_wait_for_hba_online - waits for HBA to come online + * @ha: Pointer to host adapter structure + **/ +static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) +{ + unsigned long wait_online; + + wait_online = jiffies + (30 * HZ); + while (time_before(jiffies, wait_online)) { + + if (adapter_up(ha)) + return QLA_SUCCESS; + else if (ha->retry_reset_ha_cnt == 0) + return QLA_ERROR; + + msleep(2000); + } + + return QLA_ERROR; +} + +/** + * qla4xxx_eh_wait_for_active_target_commands - wait for active cmds to finish. + * @ha: pointer to to HBA + * @t: target id + * @l: lun id + * + * This function waits for all outstanding commands to a lun to complete. It + * returns 0 if all pending commands are returned and 1 otherwise. + **/ +static int qla4xxx_eh_wait_for_active_target_commands(struct scsi_qla_host *ha, + int t, int l) +{ + int cnt; + int status = 0; + struct scsi_cmnd *cmd; + + /* + * Waiting for all commands for the designated target in the active + * array + */ + for (cnt = 0; cnt < ha->host->can_queue; cnt++) { + cmd = scsi_host_find_tag(ha->host, cnt); + if (cmd && cmd->device->id == t && cmd->device->lun == l) { + if (!qla4xxx_eh_wait_on_command(ha, cmd)) { + status++; + break; + } + } + } + return status; +} + +/** + * qla4xxx_eh_device_reset - callback for target reset. + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is called by the Linux OS to reset all luns on the + * specified target. + **/ +static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd) +{ + struct scsi_qla_host *ha = to_qla_host(cmd->device->host); + struct ddb_entry *ddb_entry = cmd->device->hostdata; + struct srb *sp; + int ret = FAILED, stat; + + sp = (struct srb *) cmd->SCp.ptr; + if (!sp || !ddb_entry) + return ret; + + dev_info(&ha->pdev->dev, + "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no, + cmd->device->channel, cmd->device->id, cmd->device->lun); + + DEBUG2(printk(KERN_INFO + "scsi%ld: DEVICE_RESET cmd=%p jiffies = 0x%lx, to=%x," + "dpc_flags=%lx, status=%x allowed=%d\n", ha->host_no, + cmd, jiffies, cmd->timeout_per_command / HZ, + ha->dpc_flags, cmd->result, cmd->allowed)); + + /* FIXME: wait for hba to go online */ + stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun); + if (stat != QLA_SUCCESS) { + dev_info(&ha->pdev->dev, "DEVICE RESET FAILED. %d\n", stat); + goto eh_dev_reset_done; + } + + /* Send marker. */ + ha->marker_needed = 1; + + /* + * If we are coming down the EH path, wait for all commands to complete + * for the device. + */ + if (cmd->device->host->shost_state == SHOST_RECOVERY) { + if (qla4xxx_eh_wait_for_active_target_commands(ha, + cmd->device->id, + cmd->device->lun)){ + dev_info(&ha->pdev->dev, + "DEVICE RESET FAILED - waiting for " + "commands.\n"); + goto eh_dev_reset_done; + } + } + + dev_info(&ha->pdev->dev, + "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n", + ha->host_no, cmd->device->channel, cmd->device->id, + cmd->device->lun); + + ret = SUCCESS; + +eh_dev_reset_done: + + return ret; +} + +/** + * qla4xxx_eh_host_reset - kernel callback + * @cmd: Pointer to Linux's SCSI command structure + * + * This routine is invoked by the Linux kernel to perform fatal error + * recovery on the specified adapter. + **/ +static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd) +{ + int return_status = FAILED; + struct scsi_qla_host *ha; + + ha = (struct scsi_qla_host *) cmd->device->host->hostdata; + + dev_info(&ha->pdev->dev, + "scsi(%ld:%d:%d:%d): ADAPTER RESET ISSUED.\n", ha->host_no, + cmd->device->channel, cmd->device->id, cmd->device->lun); + + if (qla4xxx_wait_for_hba_online(ha) != QLA_SUCCESS) { + DEBUG2(printk("scsi%ld:%d: %s: Unable to reset host. Adapter " + "DEAD.\n", ha->host_no, cmd->device->channel, + __func__)); + + return FAILED; + } + + if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS) { + return_status = SUCCESS; + } + + dev_info(&ha->pdev->dev, "HOST RESET %s.\n", + return_status == FAILED ? "FAILED" : "SUCCEDED"); + + return return_status; +} + + +static struct pci_device_id qla4xxx_pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_QLOGIC, + .device = PCI_DEVICE_ID_QLOGIC_ISP4010, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { + .vendor = PCI_VENDOR_ID_QLOGIC, + .device = PCI_DEVICE_ID_QLOGIC_ISP4022, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + {0, 0}, +}; +MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl); + +struct pci_driver qla4xxx_pci_driver = { + .name = DRIVER_NAME, + .id_table = qla4xxx_pci_tbl, + .probe = qla4xxx_probe_adapter, + .remove = qla4xxx_remove_adapter, +}; + +static int __init qla4xxx_module_init(void) +{ + int ret; + + /* Allocate cache for SRBs. */ + srb_cachep = kmem_cache_create("qla4xxx_srbs", sizeof(struct srb), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (srb_cachep == NULL) { + printk(KERN_ERR + "%s: Unable to allocate SRB cache..." + "Failing load!\n", DRIVER_NAME); + ret = -ENOMEM; + goto no_srp_cache; + } + + /* Derive version string. */ + strcpy(qla4xxx_version_str, QLA4XXX_DRIVER_VERSION); + if (extended_error_logging) + strcat(qla4xxx_version_str, "-debug"); + + qla4xxx_scsi_transport = + iscsi_register_transport(&qla4xxx_iscsi_transport); + if (!qla4xxx_scsi_transport){ + ret = -ENODEV; + goto release_srb_cache; + } + + printk(KERN_INFO "QLogic iSCSI HBA Driver\n"); + ret = pci_register_driver(&qla4xxx_pci_driver); + if (ret) + goto unregister_transport; + + printk(KERN_INFO "QLogic iSCSI HBA Driver\n"); + return 0; +unregister_transport: + iscsi_unregister_transport(&qla4xxx_iscsi_transport); +release_srb_cache: + kmem_cache_destroy(srb_cachep); +no_srp_cache: + return ret; +} + +static void __exit qla4xxx_module_exit(void) +{ + pci_unregister_driver(&qla4xxx_pci_driver); + iscsi_unregister_transport(&qla4xxx_iscsi_transport); + kmem_cache_destroy(srb_cachep); +} + +module_init(qla4xxx_module_init); +module_exit(qla4xxx_module_exit); + +MODULE_AUTHOR("QLogic Corporation"); +MODULE_DESCRIPTION("QLogic iSCSI HBA Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(QLA4XXX_DRIVER_VERSION); diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h new file mode 100644 index 000000000000..b3fe7e68988e --- /dev/null +++ b/drivers/scsi/qla4xxx/ql4_version.h @@ -0,0 +1,13 @@ +/* + * QLogic iSCSI HBA Driver + * Copyright (c) 2003-2006 QLogic Corporation + * + * See LICENSE.qla4xxx for copyright and licensing details. + */ + +#define QLA4XXX_DRIVER_VERSION "5.00.05b9-k" + +#define QL4_DRIVER_MAJOR_VER 5 +#define QL4_DRIVER_MINOR_VER 0 +#define QL4_DRIVER_PATCH_VER 5 +#define QL4_DRIVER_BETA_VER 9 diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c index 327b33a57b0a..86e13183c9ba 100644 --- a/drivers/scsi/raid_class.c +++ b/drivers/scsi/raid_class.c @@ -215,18 +215,19 @@ static void raid_component_release(struct class_device *cdev) kfree(rc); } -void raid_component_add(struct raid_template *r,struct device *raid_dev, - struct device *component_dev) +int raid_component_add(struct raid_template *r,struct device *raid_dev, + struct device *component_dev) { struct class_device *cdev = attribute_container_find_class_device(&r->raid_attrs.ac, raid_dev); struct raid_component *rc; struct raid_data *rd = class_get_devdata(cdev); + int err; rc = kzalloc(sizeof(*rc), GFP_KERNEL); if (!rc) - return; + return -ENOMEM; INIT_LIST_HEAD(&rc->node); class_device_initialize(&rc->cdev); @@ -239,7 +240,18 @@ void raid_component_add(struct raid_template *r,struct device *raid_dev, list_add_tail(&rc->node, &rd->component_list); rc->cdev.parent = cdev; rc->cdev.class = &raid_class.class; - class_device_add(&rc->cdev); + err = class_device_add(&rc->cdev); + if (err) + goto err_out; + + return 0; + +err_out: + list_del(&rc->node); + rd->component_count--; + put_device(component_dev); + kfree(rc); + return err; } EXPORT_SYMBOL(raid_component_add); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index da95bce907dd..c59f31533ab4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -128,7 +128,7 @@ const char * scsi_device_type(unsigned type) return "Well-known LUN "; if (type == 0x1f) return "No Device "; - if (type > ARRAY_SIZE(scsi_device_types)) + if (type >= ARRAY_SIZE(scsi_device_types)) return "Unknown "; return scsi_device_types[type]; } diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 3d0429bc14ab..ce63044b1ec8 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -150,6 +150,7 @@ static struct { {"DELL", "PERCRAID", NULL, BLIST_FORCELUN}, {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ + {"EMC", "Invista", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, {"FSC", "CentricStor", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, @@ -161,6 +162,11 @@ static struct { {"HITACHI", "DF600", "*", BLIST_SPARSELUN}, {"HITACHI", "DISK-SUBSYSTEM", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN}, {"HITACHI", "OPEN-E", "*", BLIST_ATTACH_PQ3 | BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HITACHI", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */ {"HP", "OPEN-", "*", BLIST_REPORTLUN2}, /* HP XP Arrays */ {"HP", "NetRAID-4M", NULL, BLIST_FORCELUN}, @@ -168,6 +174,14 @@ static struct { {"HP", "C1557A", NULL, BLIST_FORCELUN}, {"HP", "C3323-300", "4269", BLIST_NOTQ}, {"HP", "C5713A", NULL, BLIST_NOREPORTLUN}, + {"HP", "DF400", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "DF500", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "DF600", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "OP-C-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "3380-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "3390-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "6586-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, + {"HP", "6588-", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN}, {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"IBM", "2105", NULL, BLIST_RETRY_HWERROR}, @@ -188,6 +202,7 @@ static struct { {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NEC", "iStorage", NULL, BLIST_REPORTLUN2}, {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, @@ -210,6 +225,7 @@ static struct { {"SUN", "T300", "*", BLIST_SPARSELUN}, {"SUN", "T4", "*", BLIST_SPARSELUN}, {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, + {"Tornado-", "F4", "*", BLIST_NOREPORTLUN}, {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, {"USB2.0", "SMARTMEDIA/XD", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36}, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 71084728eb42..743f67ed7640 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -426,7 +426,7 @@ int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd, free_req: blk_put_request(req); free_sense: - kfree(sioc); + kmem_cache_free(scsi_io_context_cache, sioc); return DRIVER_ERROR << 24; } EXPORT_SYMBOL_GPL(scsi_execute_async); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 10bc99c911fa..84ff203ffedd 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1794,7 +1794,7 @@ static void sd_shutdown(struct device *dev) **/ static int __init init_sd(void) { - int majors = 0, i; + int majors = 0, i, err; SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n")); @@ -1805,9 +1805,22 @@ static int __init init_sd(void) if (!majors) return -ENODEV; - class_register(&sd_disk_class); + err = class_register(&sd_disk_class); + if (err) + goto err_out; - return scsi_register_driver(&sd_template.gendrv); + err = scsi_register_driver(&sd_template.gendrv); + if (err) + goto err_out_class; + + return 0; + +err_out_class: + class_unregister(&sd_disk_class); +err_out: + for (i = 0; i < SD_MAJORS; i++) + unregister_blkdev(sd_major(i), "sd"); + return err; } /** @@ -1822,10 +1835,10 @@ static void __exit exit_sd(void) SCSI_LOG_HLQUEUE(3, printk("exit_sd: exiting sd driver\n")); scsi_unregister_driver(&sd_template.gendrv); + class_unregister(&sd_disk_class); + for (i = 0; i < SD_MAJORS; i++) unregister_blkdev(sd_major(i), "sd"); - - class_unregister(&sd_disk_class); } module_init(init_sd); diff --git a/drivers/scsi/seagate.c b/drivers/scsi/seagate.c index 2679ea8bff1a..4e6666ceae26 100644 --- a/drivers/scsi/seagate.c +++ b/drivers/scsi/seagate.c @@ -94,7 +94,6 @@ #include <linux/string.h> #include <linux/proc_fs.h> #include <linux/init.h> -#include <linux/delay.h> #include <linux/blkdev.h> #include <linux/stat.h> #include <linux/delay.h> @@ -103,12 +102,13 @@ #include <asm/system.h> #include <asm/uaccess.h> -#include "scsi.h" +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi.h> + #include <scsi/scsi_dbg.h> #include <scsi/scsi_host.h> -#include "seagate.h" -#include <scsi/scsi_ioctl.h> #ifdef DEBUG #define DPRINTK( when, msg... ) do { if ( (DEBUG & (when)) == (when) ) printk( msg ); } while (0) @@ -322,6 +322,7 @@ static Signature __initdata signatures[] = { static int hostno = -1; static void seagate_reconnect_intr (int, void *, struct pt_regs *); static irqreturn_t do_seagate_reconnect_intr (int, void *, struct pt_regs *); +static int seagate_st0x_bus_reset(struct scsi_cmnd *); #ifdef FAST static int fast = 1; @@ -585,8 +586,8 @@ static int linked_connected = 0; static unsigned char linked_target, linked_lun; #endif -static void (*done_fn) (Scsi_Cmnd *) = NULL; -static Scsi_Cmnd *SCint = NULL; +static void (*done_fn) (struct scsi_cmnd *) = NULL; +static struct scsi_cmnd *SCint = NULL; /* * These control whether or not disconnect / reconnect will be attempted, @@ -633,7 +634,7 @@ static irqreturn_t do_seagate_reconnect_intr(int irq, void *dev_id, static void seagate_reconnect_intr (int irq, void *dev_id, struct pt_regs *regs) { int temp; - Scsi_Cmnd *SCtmp; + struct scsi_cmnd *SCtmp; DPRINTK (PHASE_RESELECT, "scsi%d : seagate_reconnect_intr() called\n", hostno); @@ -675,10 +676,11 @@ static void seagate_reconnect_intr (int irq, void *dev_id, struct pt_regs *regs) static int recursion_depth = 0; -static int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) +static int seagate_st0x_queue_command(struct scsi_cmnd * SCpnt, + void (*done) (struct scsi_cmnd *)) { int result, reconnect; - Scsi_Cmnd *SCtmp; + struct scsi_cmnd *SCtmp; DANY ("seagate: que_command"); done_fn = done; @@ -1609,7 +1611,7 @@ connect_loop: return retcode (st0x_aborted); } /* end of internal_command */ -static int seagate_st0x_abort (Scsi_Cmnd * SCpnt) +static int seagate_st0x_abort(struct scsi_cmnd * SCpnt) { st0x_aborted = DID_ABORT; return SUCCESS; @@ -1624,7 +1626,7 @@ static int seagate_st0x_abort (Scsi_Cmnd * SCpnt) * May be called with SCpnt = NULL */ -static int seagate_st0x_bus_reset(Scsi_Cmnd * SCpnt) +static int seagate_st0x_bus_reset(struct scsi_cmnd * SCpnt) { /* No timeouts - this command is going to fail because it was reset. */ DANY ("scsi%d: Reseting bus... ", hostno); diff --git a/drivers/scsi/seagate.h b/drivers/scsi/seagate.h deleted file mode 100644 index fb5f380fa4b3..000000000000 --- a/drivers/scsi/seagate.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * seagate.h Copyright (C) 1992 Drew Eckhardt - * low level scsi driver header for ST01/ST02 by - * Drew Eckhardt - * - * <drew@colorado.edu> - */ - -#ifndef _SEAGATE_H -#define SEAGATE_H - -static int seagate_st0x_detect(struct scsi_host_template *); -static int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); - -static int seagate_st0x_abort(Scsi_Cmnd *); -static const char *seagate_st0x_info(struct Scsi_Host *); -static int seagate_st0x_bus_reset(Scsi_Cmnd *); - -#endif /* _SEAGATE_H */ diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 34f9343ed0af..3f8b93188567 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -60,7 +60,7 @@ static int sg_version_num = 30534; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include <linux/proc_fs.h> -static char *sg_version_date = "20060818"; +static char *sg_version_date = "20060920"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -94,6 +94,9 @@ int sg_big_buff = SG_DEF_RESERVED_SIZE; static int def_reserved_size = -1; /* picks up init parameter */ static int sg_allow_dio = SG_ALLOW_DIO_DEF; +static int scatter_elem_sz = SG_SCATTER_SZ; +static int scatter_elem_sz_prev = SG_SCATTER_SZ; + #define SG_SECTOR_SZ 512 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1) @@ -1537,11 +1540,9 @@ sg_remove(struct class_device *cl_dev, struct class_interface *cl_intf) msleep(10); /* dirty detach so delay device destruction */ } -/* Set 'perm' (4th argument) to 0 to disable module_param's definition - * of sysfs parameters (which module_param doesn't yet support). - * Sysfs parameters defined explicitly below. - */ -module_param_named(def_reserved_size, def_reserved_size, int, S_IRUGO); +module_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); +module_param_named(def_reserved_size, def_reserved_size, int, + S_IRUGO | S_IWUSR); module_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR); MODULE_AUTHOR("Douglas Gilbert"); @@ -1550,6 +1551,8 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(SG_VERSION_STR); MODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR); +MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " + "size (default: max(SG_SCATTER_SZ, PAGE_SIZE))"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); @@ -1558,8 +1561,14 @@ init_sg(void) { int rc; + if (scatter_elem_sz < PAGE_SIZE) { + scatter_elem_sz = PAGE_SIZE; + scatter_elem_sz_prev = scatter_elem_sz; + } if (def_reserved_size >= 0) sg_big_buff = def_reserved_size; + else + def_reserved_size = sg_big_buff; rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS, "sg"); @@ -1842,15 +1851,30 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) if (mx_sc_elems < 0) return mx_sc_elems; /* most likely -ENOMEM */ + num = scatter_elem_sz; + if (unlikely(num != scatter_elem_sz_prev)) { + if (num < PAGE_SIZE) { + scatter_elem_sz = PAGE_SIZE; + scatter_elem_sz_prev = PAGE_SIZE; + } else + scatter_elem_sz_prev = num; + } for (k = 0, sg = schp->buffer, rem_sz = blk_size; (rem_sz > 0) && (k < mx_sc_elems); ++k, rem_sz -= ret_sz, ++sg) { - num = (rem_sz > SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz; + num = (rem_sz > scatter_elem_sz_prev) ? + scatter_elem_sz_prev : rem_sz; p = sg_page_malloc(num, sfp->low_dma, &ret_sz); if (!p) return -ENOMEM; + if (num == scatter_elem_sz_prev) { + if (unlikely(ret_sz > scatter_elem_sz_prev)) { + scatter_elem_sz = ret_sz; + scatter_elem_sz_prev = ret_sz; + } + } sg->page = p; sg->length = ret_sz; @@ -2341,6 +2365,9 @@ sg_add_sfp(Sg_device * sdp, int dev) } write_unlock_irqrestore(&sg_dev_arr_lock, iflags); SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); + if (unlikely(sg_big_buff != def_reserved_size)) + sg_big_buff = def_reserved_size; + sg_build_reserve(sfp, sg_big_buff); SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", sfp->reserve.bufflen, sfp->reserve.k_use_sg)); @@ -2437,16 +2464,16 @@ sg_res_in_use(Sg_fd * sfp) return srp ? 1 : 0; } -/* If retSzp==NULL want exact size or fail */ +/* The size fetched (value output via retSzp) set when non-NULL return */ static struct page * sg_page_malloc(int rqSz, int lowDma, int *retSzp) { struct page *resp = NULL; gfp_t page_mask; int order, a_size; - int resSz = rqSz; + int resSz; - if (rqSz <= 0) + if ((rqSz <= 0) || (NULL == retSzp)) return resp; if (lowDma) @@ -2456,8 +2483,9 @@ sg_page_malloc(int rqSz, int lowDma, int *retSzp) for (order = 0, a_size = PAGE_SIZE; a_size < rqSz; order++, a_size <<= 1) ; + resSz = a_size; /* rounded up if necessary */ resp = alloc_pages(page_mask, order); - while ((!resp) && order && retSzp) { + while ((!resp) && order) { --order; a_size >>= 1; /* divide by 2, until PAGE_SIZE */ resp = alloc_pages(page_mask, order); /* try half */ @@ -2466,8 +2494,7 @@ sg_page_malloc(int rqSz, int lowDma, int *retSzp) if (resp) { if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) memset(page_address(resp), 0, resSz); - if (retSzp) - *retSzp = resSz; + *retSzp = resSz; } return resp; } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 7f669b600677..3babdc76b3fb 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -195,9 +195,9 @@ static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int); static int st_probe(struct device *); static int st_remove(struct device *); -static void do_create_driverfs_files(void); +static int do_create_driverfs_files(void); static void do_remove_driverfs_files(void); -static void do_create_class_files(struct scsi_tape *, int, int); +static int do_create_class_files(struct scsi_tape *, int, int); static struct scsi_driver st_template = { .owner = THIS_MODULE, @@ -4048,7 +4048,9 @@ static int st_probe(struct device *dev) STm->cdevs[j] = cdev; } - do_create_class_files(tpnt, dev_num, mode); + error = do_create_class_files(tpnt, dev_num, mode); + if (error) + goto out_free_tape; } sdev_printk(KERN_WARNING, SDp, @@ -4157,32 +4159,45 @@ static void scsi_tape_release(struct kref *kref) static int __init init_st(void) { + int err; + validate_options(); - printk(KERN_INFO - "st: Version %s, fixed bufsize %d, s/g segs %d\n", + printk(KERN_INFO "st: Version %s, fixed bufsize %d, s/g segs %d\n", verstr, st_fixed_buffer_size, st_max_sg_segs); st_sysfs_class = class_create(THIS_MODULE, "scsi_tape"); if (IS_ERR(st_sysfs_class)) { - st_sysfs_class = NULL; printk(KERN_ERR "Unable create sysfs class for SCSI tapes\n"); - return 1; + return PTR_ERR(st_sysfs_class); } - if (!register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), - ST_MAX_TAPE_ENTRIES, "st")) { - if (scsi_register_driver(&st_template.gendrv) == 0) { - do_create_driverfs_files(); - return 0; - } - unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), - ST_MAX_TAPE_ENTRIES); + err = register_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), + ST_MAX_TAPE_ENTRIES, "st"); + if (err) { + printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", + SCSI_TAPE_MAJOR); + goto err_class; } - class_destroy(st_sysfs_class); - printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", SCSI_TAPE_MAJOR); - return 1; + err = scsi_register_driver(&st_template.gendrv); + if (err) + goto err_chrdev; + + err = do_create_driverfs_files(); + if (err) + goto err_scsidrv; + + return 0; + +err_scsidrv: + scsi_unregister_driver(&st_template.gendrv); +err_chrdev: + unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), + ST_MAX_TAPE_ENTRIES); +err_class: + class_destroy(st_sysfs_class); + return err; } static void __exit exit_st(void) @@ -4225,14 +4240,33 @@ static ssize_t st_version_show(struct device_driver *ddd, char *buf) } static DRIVER_ATTR(version, S_IRUGO, st_version_show, NULL); -static void do_create_driverfs_files(void) +static int do_create_driverfs_files(void) { struct device_driver *driverfs = &st_template.gendrv; + int err; + + err = driver_create_file(driverfs, &driver_attr_try_direct_io); + if (err) + return err; + err = driver_create_file(driverfs, &driver_attr_fixed_buffer_size); + if (err) + goto err_try_direct_io; + err = driver_create_file(driverfs, &driver_attr_max_sg_segs); + if (err) + goto err_attr_fixed_buf; + err = driver_create_file(driverfs, &driver_attr_version); + if (err) + goto err_attr_max_sg; - driver_create_file(driverfs, &driver_attr_try_direct_io); - driver_create_file(driverfs, &driver_attr_fixed_buffer_size); - driver_create_file(driverfs, &driver_attr_max_sg_segs); - driver_create_file(driverfs, &driver_attr_version); + return 0; + +err_attr_max_sg: + driver_remove_file(driverfs, &driver_attr_max_sg_segs); +err_attr_fixed_buf: + driver_remove_file(driverfs, &driver_attr_fixed_buffer_size); +err_try_direct_io: + driver_remove_file(driverfs, &driver_attr_try_direct_io); + return err; } static void do_remove_driverfs_files(void) @@ -4293,15 +4327,12 @@ static ssize_t st_defcompression_show(struct class_device *class_dev, char *buf) CLASS_DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL); -static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) +static int do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) { int i, rew, error; char name[10]; struct class_device *st_class_member; - if (!st_sysfs_class) - return; - for (rew=0; rew < 2; rew++) { /* Make sure that the minor numbers corresponding to the four first modes always get the same names */ @@ -4316,18 +4347,24 @@ static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) if (IS_ERR(st_class_member)) { printk(KERN_WARNING "st%d: class_device_create failed\n", dev_num); + error = PTR_ERR(st_class_member); goto out; } class_set_devdata(st_class_member, &STp->modes[mode]); - class_device_create_file(st_class_member, - &class_device_attr_defined); - class_device_create_file(st_class_member, - &class_device_attr_default_blksize); - class_device_create_file(st_class_member, - &class_device_attr_default_density); - class_device_create_file(st_class_member, - &class_device_attr_default_compression); + error = class_device_create_file(st_class_member, + &class_device_attr_defined); + if (error) goto out; + error = class_device_create_file(st_class_member, + &class_device_attr_default_blksize); + if (error) goto out; + error = class_device_create_file(st_class_member, + &class_device_attr_default_density); + if (error) goto out; + error = class_device_create_file(st_class_member, + &class_device_attr_default_compression); + if (error) goto out; + if (mode == 0 && rew == 0) { error = sysfs_create_link(&STp->device->sdev_gendev.kobj, &st_class_member->kobj, @@ -4336,11 +4373,15 @@ static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) printk(KERN_ERR "st%d: Can't create sysfs link from SCSI device.\n", dev_num); + goto out; } } } - out: - return; + + return 0; + +out: + return error; } /* The following functions may be useful for a larger audience. */ diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 3cf3106a29b8..a54e6c1026b7 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -11,7 +11,7 @@ * Written By: * Ed Lin <promise_linux@promise.com> * - * Version: 2.9.0.13 + * Version: 3.0.0.1 * */ @@ -37,11 +37,11 @@ #include <scsi/scsi_tcq.h> #define DRV_NAME "stex" -#define ST_DRIVER_VERSION "2.9.0.13" -#define ST_VER_MAJOR 2 -#define ST_VER_MINOR 9 +#define ST_DRIVER_VERSION "3.0.0.1" +#define ST_VER_MAJOR 3 +#define ST_VER_MINOR 0 #define ST_OEM 0 -#define ST_BUILD_VER 13 +#define ST_BUILD_VER 1 enum { /* MU register offset */ @@ -120,12 +120,18 @@ enum { st_shasta = 0, st_vsc = 1, + st_yosemite = 2, PASSTHRU_REQ_TYPE = 0x00000001, PASSTHRU_REQ_NO_WAKEUP = 0x00000100, ST_INTERNAL_TIMEOUT = 30, + ST_TO_CMD = 0, + ST_FROM_CMD = 1, + /* vendor specific commands of Promise */ + MGT_CMD = 0xd8, + SINBAND_MGT_CMD = 0xd9, ARRAY_CMD = 0xe0, CONTROLLER_CMD = 0xe1, DEBUGGING_CMD = 0xe2, @@ -133,14 +139,48 @@ enum { PASSTHRU_GET_ADAPTER = 0x05, PASSTHRU_GET_DRVVER = 0x10, + + CTLR_CONFIG_CMD = 0x03, + CTLR_SHUTDOWN = 0x0d, + CTLR_POWER_STATE_CHANGE = 0x0e, CTLR_POWER_SAVING = 0x01, PASSTHRU_SIGNATURE = 0x4e415041, + MGT_CMD_SIGNATURE = 0xba, INQUIRY_EVPD = 0x01, }; +/* SCSI inquiry data */ +typedef struct st_inq { + u8 DeviceType :5; + u8 DeviceTypeQualifier :3; + u8 DeviceTypeModifier :7; + u8 RemovableMedia :1; + u8 Versions; + u8 ResponseDataFormat :4; + u8 HiSupport :1; + u8 NormACA :1; + u8 ReservedBit :1; + u8 AERC :1; + u8 AdditionalLength; + u8 Reserved[2]; + u8 SoftReset :1; + u8 CommandQueue :1; + u8 Reserved2 :1; + u8 LinkedCommands :1; + u8 Synchronous :1; + u8 Wide16Bit :1; + u8 Wide32Bit :1; + u8 RelativeAddressing :1; + u8 VendorId[8]; + u8 ProductId[16]; + u8 ProductRevisionLevel[4]; + u8 VendorSpecific[20]; + u8 Reserved3[40]; +} ST_INQ; + struct st_sgitem { u8 ctrl; /* SG_CF_xxx */ u8 reserved[3]; @@ -181,7 +221,7 @@ struct req_msg { u8 task_attr; u8 task_manage; u8 prd_entry; - u8 payload_sz; /* payload size in 4-byte */ + u8 payload_sz; /* payload size in 4-byte, not used */ u8 cdb[STEX_CDB_LENGTH]; u8 variable[REQ_VARIABLE_LEN]; }; @@ -242,7 +282,8 @@ struct st_drvver { #define MU_REQ_BUFFER_SIZE (MU_REQ_COUNT * sizeof(struct req_msg)) #define MU_STATUS_BUFFER_SIZE (MU_STATUS_COUNT * sizeof(struct status_msg)) #define MU_BUFFER_SIZE (MU_REQ_BUFFER_SIZE + MU_STATUS_BUFFER_SIZE) -#define STEX_BUFFER_SIZE (MU_BUFFER_SIZE + sizeof(struct st_frame)) +#define STEX_EXTRA_SIZE max(sizeof(struct st_frame), sizeof(ST_INQ)) +#define STEX_BUFFER_SIZE (MU_BUFFER_SIZE + STEX_EXTRA_SIZE) struct st_ccb { struct req_msg *req; @@ -403,7 +444,7 @@ static int stex_map_sg(struct st_hba *hba, } static void stex_internal_copy(struct scsi_cmnd *cmd, - const void *src, size_t *count, int sg_count) + const void *src, size_t *count, int sg_count, int direction) { size_t lcount; size_t len; @@ -427,7 +468,10 @@ static void stex_internal_copy(struct scsi_cmnd *cmd, } else d = cmd->request_buffer; - memcpy(d, s, len); + if (direction == ST_TO_CMD) + memcpy(d, s, len); + else + memcpy(s, d, len); lcount -= len; if (cmd->use_sg) @@ -449,7 +493,7 @@ static int stex_direct_copy(struct scsi_cmnd *cmd, return 0; } - stex_internal_copy(cmd, src, &cp_len, n_elem); + stex_internal_copy(cmd, src, &cp_len, n_elem, ST_TO_CMD); if (cmd->use_sg) pci_unmap_sg(hba->pdev, cmd->request_buffer, @@ -480,7 +524,7 @@ static void stex_controller_info(struct st_hba *hba, struct st_ccb *ccb) p->subid = hba->pdev->subsystem_vendor << 16 | hba->pdev->subsystem_device; - stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count); + stex_internal_copy(ccb->cmd, p, &count, ccb->sg_count, ST_TO_CMD); } static void @@ -489,7 +533,6 @@ stex_send_cmd(struct st_hba *hba, struct req_msg *req, u16 tag) req->tag = cpu_to_le16(tag); req->task_attr = TASK_ATTRIBUTE_SIMPLE; req->task_manage = 0; /* not supported yet */ - req->payload_sz = (u8)(sizeof(struct req_msg)/sizeof(u32)); hba->ccb[tag].req = req; hba->out_req_cnt++; @@ -595,8 +638,14 @@ stex_queuecommand(struct scsi_cmnd *cmd, void (* done)(struct scsi_cmnd *)) return SCSI_MLQUEUE_HOST_BUSY; req = stex_alloc_req(hba); - req->lun = lun; - req->target = id; + + if (hba->cardtype == st_yosemite) { + req->lun = lun * (ST_MAX_TARGET_NUM - 1) + id; + req->target = 0; + } else { + req->lun = lun; + req->target = id; + } /* cdb */ memcpy(req->cdb, cmd->cmnd, STEX_CDB_LENGTH); @@ -680,7 +729,51 @@ static void stex_copy_data(struct st_ccb *ccb, if (ccb->cmd == NULL) return; - stex_internal_copy(ccb->cmd, resp->variable, &count, ccb->sg_count); + stex_internal_copy(ccb->cmd, + resp->variable, &count, ccb->sg_count, ST_TO_CMD); +} + +static void stex_ys_commands(struct st_hba *hba, + struct st_ccb *ccb, struct status_msg *resp) +{ + size_t count; + + if (ccb->cmd->cmnd[0] == MGT_CMD && + resp->scsi_status != SAM_STAT_CHECK_CONDITION) { + ccb->cmd->request_bufflen = + le32_to_cpu(*(__le32 *)&resp->variable[0]); + return; + } + + if (resp->srb_status != 0) + return; + + /* determine inquiry command status by DeviceTypeQualifier */ + if (ccb->cmd->cmnd[0] == INQUIRY && + resp->scsi_status == SAM_STAT_GOOD) { + ST_INQ *inq_data; + + count = STEX_EXTRA_SIZE; + stex_internal_copy(ccb->cmd, hba->copy_buffer, + &count, ccb->sg_count, ST_FROM_CMD); + inq_data = (ST_INQ *)hba->copy_buffer; + if (inq_data->DeviceTypeQualifier != 0) + ccb->srb_status = SRB_STATUS_SELECTION_TIMEOUT; + else + ccb->srb_status = SRB_STATUS_SUCCESS; + } else if (ccb->cmd->cmnd[0] == REPORT_LUNS) { + u8 *report_lun_data = (u8 *)hba->copy_buffer; + + count = STEX_EXTRA_SIZE; + stex_internal_copy(ccb->cmd, report_lun_data, + &count, ccb->sg_count, ST_FROM_CMD); + if (report_lun_data[2] || report_lun_data[3]) { + report_lun_data[2] = 0x00; + report_lun_data[3] = 0x08; + stex_internal_copy(ccb->cmd, report_lun_data, + &count, ccb->sg_count, ST_TO_CMD); + } + } } static void stex_mu_intr(struct st_hba *hba, u32 doorbell) @@ -702,8 +795,17 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) return; } - if (unlikely(hba->mu_status != MU_STATE_STARTED || - hba->out_req_cnt <= 0)) { + /* + * it's not a valid status payload if: + * 1. there are no pending requests(e.g. during init stage) + * 2. there are some pending requests, but the controller is in + * reset status, and its type is not st_yosemite + * firmware of st_yosemite in reset status will return pending requests + * to driver, so we allow it to pass + */ + if (unlikely(hba->out_req_cnt <= 0 || + (hba->mu_status == MU_STATE_RESETTING && + hba->cardtype != st_yosemite))) { hba->status_tail = hba->status_head; goto update_status; } @@ -723,6 +825,7 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) if (unlikely(ccb->req == NULL)) { printk(KERN_WARNING DRV_NAME "(%s): lagging req\n", pci_name(hba->pdev)); + hba->out_req_cnt--; continue; } @@ -741,9 +844,13 @@ static void stex_mu_intr(struct st_hba *hba, u32 doorbell) ccb->scsi_status = resp->scsi_status; if (likely(ccb->cmd != NULL)) { + if (hba->cardtype == st_yosemite) + stex_ys_commands(hba, ccb, resp); + if (unlikely(ccb->cmd->cmnd[0] == PASSTHRU_CMD && ccb->cmd->cmnd[1] == PASSTHRU_GET_ADAPTER)) stex_controller_info(hba, ccb); + stex_unmap_sg(hba, ccb->cmd); stex_scsi_done(ccb); hba->out_req_cnt--; @@ -948,6 +1055,7 @@ static int stex_reset(struct scsi_cmnd *cmd) { struct st_hba *hba; unsigned long flags; + unsigned long before; hba = (struct st_hba *) &cmd->device->host->hostdata[0]; hba->mu_status = MU_STATE_RESETTING; @@ -955,20 +1063,37 @@ static int stex_reset(struct scsi_cmnd *cmd) if (hba->cardtype == st_shasta) stex_hard_reset(hba); - if (stex_handshake(hba)) { - printk(KERN_WARNING DRV_NAME - "(%s): resetting: handshake failed\n", - pci_name(hba->pdev)); - return FAILED; + if (hba->cardtype != st_yosemite) { + if (stex_handshake(hba)) { + printk(KERN_WARNING DRV_NAME + "(%s): resetting: handshake failed\n", + pci_name(hba->pdev)); + return FAILED; + } + spin_lock_irqsave(hba->host->host_lock, flags); + hba->req_head = 0; + hba->req_tail = 0; + hba->status_head = 0; + hba->status_tail = 0; + hba->out_req_cnt = 0; + spin_unlock_irqrestore(hba->host->host_lock, flags); + return SUCCESS; + } + + /* st_yosemite */ + writel(MU_INBOUND_DOORBELL_RESET, hba->mmio_base + IDBL); + readl(hba->mmio_base + IDBL); /* flush */ + before = jiffies; + while (hba->out_req_cnt > 0) { + if (time_after(jiffies, before + ST_INTERNAL_TIMEOUT * HZ)) { + printk(KERN_WARNING DRV_NAME + "(%s): reset timeout\n", pci_name(hba->pdev)); + return FAILED; + } + msleep(1); } - spin_lock_irqsave(hba->host->host_lock, flags); - hba->req_head = 0; - hba->req_tail = 0; - hba->status_head = 0; - hba->status_tail = 0; - hba->out_req_cnt = 0; - spin_unlock_irqrestore(hba->host->host_lock, flags); + hba->mu_status = MU_STATE_STARTED; return SUCCESS; } @@ -1156,9 +1281,16 @@ static void stex_hba_stop(struct st_hba *hba) req = stex_alloc_req(hba); memset(req->cdb, 0, STEX_CDB_LENGTH); - req->cdb[0] = CONTROLLER_CMD; - req->cdb[1] = CTLR_POWER_STATE_CHANGE; - req->cdb[2] = CTLR_POWER_SAVING; + if (hba->cardtype == st_yosemite) { + req->cdb[0] = MGT_CMD; + req->cdb[1] = MGT_CMD_SIGNATURE; + req->cdb[2] = CTLR_CONFIG_CMD; + req->cdb[3] = CTLR_SHUTDOWN; + } else { + req->cdb[0] = CONTROLLER_CMD; + req->cdb[1] = CTLR_POWER_STATE_CHANGE; + req->cdb[2] = CTLR_POWER_SAVING; + } hba->ccb[tag].cmd = NULL; hba->ccb[tag].sg_count = 0; @@ -1222,6 +1354,7 @@ static struct pci_device_id stex_pci_tbl[] = { { 0x105a, 0x8301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, { 0x105a, 0x8302, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_shasta }, { 0x1725, 0x7250, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_vsc }, + { 0x105a, 0x8650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, st_yosemite }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, stex_pci_tbl); diff --git a/drivers/scsi/tmscsim.c b/drivers/scsi/tmscsim.c index 9404ff3d4c79..028d5f641cc6 100644 --- a/drivers/scsi/tmscsim.c +++ b/drivers/scsi/tmscsim.c @@ -279,6 +279,10 @@ static void dc390_ResetDevParam(struct dc390_acb* pACB); static u32 dc390_laststatus = 0; static u8 dc390_adapterCnt = 0; +static int disable_clustering; +module_param(disable_clustering, int, S_IRUGO); +MODULE_PARM_DESC(disable_clustering, "If you experience problems with your devices, try setting to 1"); + /* Startup values, to be overriden on the commandline */ static int tmscsim[] = {-2, -2, -2, -2, -2, -2}; @@ -2299,7 +2303,7 @@ static struct scsi_host_template driver_template = { .this_id = 7, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, - .use_clustering = DISABLE_CLUSTERING, + .use_clustering = ENABLE_CLUSTERING, }; /*********************************************************************** @@ -2525,6 +2529,8 @@ static int __devinit dc390_probe_one(struct pci_dev *pdev, pci_set_master(pdev); error = -ENOMEM; + if (disable_clustering) + driver_template.use_clustering = DISABLE_CLUSTERING; shost = scsi_host_alloc(&driver_template, sizeof(struct dc390_acb)); if (!shost) goto out_disable_device; @@ -2660,6 +2666,10 @@ static struct pci_driver dc390_driver = { static int __init dc390_module_init(void) { + if (!disable_clustering) + printk(KERN_INFO "DC390: clustering now enabled by default. If you get problems load\n" + "\twith \"disable_clustering=1\" and report to maintainers\n"); + if (tmscsim[0] == -1 || tmscsim[0] > 15) { tmscsim[0] = 7; tmscsim[1] = 4; diff --git a/include/linux/raid_class.h b/include/linux/raid_class.h index d0dd38b3a2fd..d22ad392242a 100644 --- a/include/linux/raid_class.h +++ b/include/linux/raid_class.h @@ -77,5 +77,6 @@ DEFINE_RAID_ATTRIBUTE(enum raid_state, state) struct raid_template *raid_class_attach(struct raid_function_template *); void raid_class_release(struct raid_template *); -void raid_component_add(struct raid_template *, struct device *, - struct device *); +int __must_check raid_component_add(struct raid_template *, struct device *, + struct device *); + diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 0a487fe26d4f..519c49a0fc11 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -11,26 +11,10 @@ Original driver (sg.h): * Copyright (C) 1992 Lawrence Foard Version 2 and 3 extensions to driver: -* Copyright (C) 1998 - 2003 Douglas Gilbert - - Version: 3.5.29 (20030529) - This version is for 2.5 series kernels. - - Changes since 3.5.28 (20030308) - - fix bug introduced in version 3.1.24 (last segment of sgat list) - Changes since 3.5.27 (20020812) - - remove procfs entries: hosts, host_hdr + host_strs (now in sysfs) - - add sysfs sg driver params: def_reserved_size, allow_dio, version - - new boot option: "sg_allow_dio" and module parameter: "allow_dio" - - multiple internal changes due to scsi subsystem rework - Changes since 3.5.26 (20020708) - - re-add direct IO using Kai Makisara's work - - re-tab to 8, start using C99-isms - - simplify memory management - Changes since 3.5.25 (20020504) - - driverfs additions - - copy_to/from_user() fixes [William Stinson] - - disable kiobufs support +* Copyright (C) 1998 - 2006 Douglas Gilbert + + Version: 3.5.34 (20060920) + This version is for 2.6 series kernels. For a full changelog see http://www.torque.net/sg @@ -40,7 +24,7 @@ Map of SG verions to the Linux kernels in which they appear: 2.1.40 2.2.20 3.0.x optional version 3 sg driver for 2.2 series 3.1.17++ 2.4.0++ - 3.5.23++ 2.5.0++ + 3.5.30++ 2.6.0++ Major new features in SG 3.x driver (cf SG 2.x drivers) - SG_IO ioctl() combines function if write() and read() @@ -51,14 +35,15 @@ Major new features in SG 3.x driver (cf SG 2.x drivers) data into kernel buffers and then use the CPU to copy the data into the user space (vice versa for writes). That is called "indirect" IO due to the double handling of data. There are two methods offered to remove the - redundant copy: 1) direct IO which uses the kernel kiobuf mechanism and - 2) using the mmap() system call to map the reserve buffer (this driver has - one reserve buffer per fd) into the user space. Both have their advantages. + redundant copy: 1) direct IO and 2) using the mmap() system call to map + the reserve buffer (this driver has one reserve buffer per fd) into the + user space. Both have their advantages. In terms of absolute speed mmap() is faster. If speed is not a concern, indirect IO should be fine. Read the documentation for more information. - ** N.B. To use direct IO 'echo 1 > /proc/scsi/sg/allow_dio' may be - needed. That pseudo file's content is defaulted to 0. ** + ** N.B. To use direct IO 'echo 1 > /proc/scsi/sg/allow_dio' or + 'echo 1 > /sys/module/sg/parameters/allow_dio' is needed. + That attribute is 0 by default. ** Historical note: this SCSI pass-through driver has been known as "sg" for a decade. In broader kernel discussions "sg" is used to refer to scatter @@ -72,20 +57,17 @@ Major new features in SG 3.x driver (cf SG 2.x drivers) http://www.torque.net/sg/p/sg_v3_ho.html This is a rendering from DocBook source [change the extension to "sgml" or "xml"]. There are renderings in "ps", "pdf", "rtf" and "txt" (soon). + The SG_IO ioctl is now found in other parts kernel (e.g. the block layer). + For more information see http://www.torque.net/sg/sg_io.html The older, version 2 documents discuss the original sg interface in detail: http://www.torque.net/sg/p/scsi-generic.txt http://www.torque.net/sg/p/scsi-generic_long.txt - A version of this document (potentially out of date) may also be found in - the kernel source tree, probably at: - Documentation/scsi/scsi-generic.txt . + Also available: <kernel_source>/Documentation/scsi/scsi-generic.txt Utility and test programs are available at the sg web site. They are - bundled as sg_utils (for the lk 2.2 series) and sg3_utils (for the - lk 2.4 series). - - There is a HOWTO on the Linux SCSI subsystem in the lk 2.4 series at: - http://www.linuxdoc.org/HOWTO/SCSI-2.4-HOWTO + packaged as sg3_utils (for the lk 2.4 and 2.6 series) and sg_utils + (for the lk 2.2 series). */ @@ -238,13 +220,12 @@ typedef struct sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ #define SG_GET_ACCESS_COUNT 0x2289 -#define SG_SCATTER_SZ (8 * 4096) /* PAGE_SIZE not available to user */ +#define SG_SCATTER_SZ (8 * 4096) /* Largest size (in bytes) a single scatter-gather list element can have. - The value must be a power of 2 and <= (PAGE_SIZE * 32) [131072 bytes on - i386]. The minimum value is PAGE_SIZE. If scatter-gather not supported - by adapter then this value is the largest data block that can be - read/written by a single scsi command. The user can find the value of - PAGE_SIZE by calling getpagesize() defined in unistd.h . */ + The value used by the driver is 'max(SG_SCATTER_SZ, PAGE_SIZE)'. + This value should be a power of 2 (and may be rounded up internally). + If scatter-gather is not supported by adapter then this value is the + largest data block that can be read/written by a single scsi command. */ #define SG_DEFAULT_RETRIES 0 |