diff options
Diffstat (limited to 'drivers')
77 files changed, 9852 insertions, 733 deletions
diff --git a/drivers/message/fusion/linux_compat.h b/drivers/message/fusion/linux_compat.h index 048b5b8610e..bb2bf5aa0b6 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 5a9475e56d0..da173159ced 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 f3a5f422a8e..2d4cb6721fa 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 31fe5ea1592..bbd654a2b9b 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 c6dfb6fa13b..9540eb8efdc 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 1ef951be7a5..bcca39c3bcb 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 d7e9fab54c6..2684150917e 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 8ad3ce945b9..a3266e066c0 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 50a41eda580..4b535420180 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 2cc8a17ed8b..8e1954cdd84 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 7e42f07a27f..d20ca514e9f 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 10353379a07..3eae8062a02 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 0ed391d8ee8..c83fe751d0b 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 734adc9d520..99743ca29ca 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 7621e3fa37b..0525d672e1e 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 2142290f840..d11424b89f4 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 4cf7afc31cc..e05f0c2fc91 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 540914d6fd3..00e5f055afd 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 8caa5903ce3..cb11ccef54e 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 0c9dec4c171..3c519c9237b 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 8c2600ffc6a..3a39579bd08 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 ff2b1796fa3..c6118d99385 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 879a2665767..fa738ec8692 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 d84a281ad94..b3fa7ed71fa 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 7b3bd34faf4..b20b37661d6 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 2ad2a89b5db..28998326492 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 47eae029975..8c29eafd51c 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 c27264bed5d..00000000000 --- 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 7ed4eef8347..e1fe9494125 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 11eaff52432..6d035283af0 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 3c639286ec1..9a9ab297cf1 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 f46c382e559..34680f3dd45 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 4cdf3464267..a5723ad0a09 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 8cd0bd1d0f7..b50e27e6602 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 4cab5b534b2..977b6e8d852 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 3531a14222a..55eddcf8eb1 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 bfb4f49e125..1c624ce8189 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 5addf9fb1e1..a976e8193d1 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 4a2fed350d4..824fe080d1d 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 0d4c04e1f3d..053303d3611 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 332151e2a01..9f33e5946c0 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 87f90c4f08e..ee75a71f3c6 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 0930260aec2..c37a30aa214 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 8311ac2b93a..bef7011378c 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 2ebf259fccb..97fbc62ec66 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 859649160ca..d5d26273c04 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 de0613135f7..5fa933cda99 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 879f281e2ea..4cde76c85cb 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 65cbe2f5eea..3ba8c239f17 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 971259032ef..e57bf45a339 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 00000000000..08a07f0b8d9 --- /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 00000000000..86ea37baa0f --- /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 00000000000..752031fadfe --- /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 00000000000..56ddc227f84 --- /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 00000000000..a7f6c7b1c59 --- /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 00000000000..427489de64b --- /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 00000000000..418fb7a13a6 --- /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 00000000000..bb3a1c11f44 --- /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 00000000000..0d61797af7d --- /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 00000000000..c0a254b89a3 --- /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 00000000000..b584317608d --- /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 00000000000..ed977f70b2d --- /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 00000000000..e3957ca5b64 --- /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 00000000000..08e2aed8c6c --- /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 00000000000..5036ebf013a --- /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 00000000000..b3fe7e68988 --- /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 327b33a57b0..86e13183c9b 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 da95bce907d..c59f31533ab 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 3d0429bc14a..ce63044b1ec 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 71084728eb4..743f67ed764 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 10bc99c911f..84ff203ffed 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 2679ea8bff1..4e6666ceae2 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 fb5f380fa4b..00000000000 --- 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 34f9343ed0a..3f8b9318856 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 7f669b60067..3babdc76b3f 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 3cf3106a29b..a54e6c1026b 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 9404ff3d4c7..028d5f641cc 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; |