diff options
Diffstat (limited to 'drivers/scsi')
148 files changed, 17694 insertions, 10353 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index bc6e4627c7a1..a748fbfb6692 100644 --- a/drivers/scsi/3w-9xxx.c +++ b/drivers/scsi/3w-9xxx.c @@ -59,6 +59,8 @@ Fix 'handled=1' ISR usage, remove bogus IRQ check. Remove un-needed eh_abort handler. Add support for embedded firmware error strings. + 2.26.02.003 - Correctly handle single sgl's with use_sg=1. + 2.26.02.004 - Add support for 9550SX controllers. */ #include <linux/module.h> @@ -81,7 +83,7 @@ #include "3w-9xxx.h" /* Globals */ -#define TW_DRIVER_VERSION "2.26.02.002" +#define TW_DRIVER_VERSION "2.26.02.004" static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT]; static unsigned int twa_device_extension_count; static int twa_major = -1; @@ -891,11 +893,6 @@ static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value) writel(TW_CONTROL_CLEAR_QUEUE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); } - if (status_reg_value & TW_STATUS_SBUF_WRITE_ERROR) { - TW_PRINTK(tw_dev->host, TW_DRIVER, 0xf, "SBUF Write Error: clearing"); - writel(TW_CONTROL_CLEAR_SBUF_WRITE_ERROR, TW_CONTROL_REG_ADDR(tw_dev)); - } - if (status_reg_value & TW_STATUS_MICROCONTROLLER_ERROR) { if (tw_dev->reset_print == 0) { TW_PRINTK(tw_dev->host, TW_DRIVER, 0x10, "Microcontroller Error: clearing"); @@ -929,6 +926,36 @@ out: return retval; } /* End twa_empty_response_queue() */ +/* This function will clear the pchip/response queue on 9550SX */ +static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev) +{ + u32 status_reg_value, response_que_value; + int count = 0, retval = 1; + + if (tw_dev->tw_pci_dev->device == PCI_DEVICE_ID_3WARE_9550SX) { + status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); + + while (((status_reg_value & TW_STATUS_RESPONSE_QUEUE_EMPTY) == 0) && (count < TW_MAX_RESPONSE_DRAIN)) { + response_que_value = readl(TW_RESPONSE_QUEUE_REG_ADDR_LARGE(tw_dev)); + if ((response_que_value & TW_9550SX_DRAIN_COMPLETED) == TW_9550SX_DRAIN_COMPLETED) { + /* P-chip settle time */ + msleep(500); + retval = 0; + goto out; + } + status_reg_value = readl(TW_STATUS_REG_ADDR(tw_dev)); + count++; + } + if (count == TW_MAX_RESPONSE_DRAIN) + goto out; + + retval = 0; + } else + retval = 0; +out: + return retval; +} /* End twa_empty_response_queue_large() */ + /* This function passes sense keys from firmware to scsi layer */ static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host) { @@ -1612,8 +1639,16 @@ static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset) int tries = 0, retval = 1, flashed = 0, do_soft_reset = soft_reset; while (tries < TW_MAX_RESET_TRIES) { - if (do_soft_reset) + if (do_soft_reset) { TW_SOFT_RESET(tw_dev); + /* Clear pchip/response queue on 9550SX */ + if (twa_empty_response_queue_large(tw_dev)) { + TW_PRINTK(tw_dev->host, TW_DRIVER, 0x36, "Response queue (large) empty failed during reset sequence"); + do_soft_reset = 1; + tries++; + continue; + } + } /* Make sure controller is in a good state */ if (twa_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY | (do_soft_reset == 1 ? TW_STATUS_ATTENTION_INTERRUPT : 0), 60)) { @@ -1805,6 +1840,8 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH) { command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; + if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) + memcpy(tw_dev->generic_buffer_virt[request_id], tw_dev->srb[request_id]->request_buffer, tw_dev->srb[request_id]->request_bufflen); } else { buffaddr = twa_map_scsi_single_data(tw_dev, request_id); if (buffaddr == 0) @@ -1823,6 +1860,12 @@ static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, if (tw_dev->srb[request_id]->use_sg > 0) { if ((tw_dev->srb[request_id]->use_sg == 1) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { + if (tw_dev->srb[request_id]->sc_data_direction == DMA_TO_DEVICE || tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL) { + struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + memcpy(tw_dev->generic_buffer_virt[request_id], buf, sg->length); + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } command_packet->sg_list[0].address = tw_dev->generic_buffer_phys[request_id]; command_packet->sg_list[0].length = TW_MIN_SGL_LENGTH; } else { @@ -1888,11 +1931,20 @@ out: /* This function completes an execute scsi operation */ static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id) { - /* Copy the response if too small */ - if ((tw_dev->srb[request_id]->request_buffer) && (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH)) { - memcpy(tw_dev->srb[request_id]->request_buffer, - tw_dev->generic_buffer_virt[request_id], - tw_dev->srb[request_id]->request_bufflen); + if (tw_dev->srb[request_id]->request_bufflen < TW_MIN_SGL_LENGTH && + (tw_dev->srb[request_id]->sc_data_direction == DMA_FROM_DEVICE || + tw_dev->srb[request_id]->sc_data_direction == DMA_BIDIRECTIONAL)) { + if (tw_dev->srb[request_id]->use_sg == 0) { + memcpy(tw_dev->srb[request_id]->request_buffer, + tw_dev->generic_buffer_virt[request_id], + tw_dev->srb[request_id]->request_bufflen); + } + if (tw_dev->srb[request_id]->use_sg == 1) { + struct scatterlist *sg = (struct scatterlist *)tw_dev->srb[request_id]->request_buffer; + char *buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + memcpy(buf, tw_dev->generic_buffer_virt[request_id], sg->length); + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } } } /* End twa_scsiop_execute_scsi_complete() */ @@ -2016,7 +2068,10 @@ static int __devinit twa_probe(struct pci_dev *pdev, const struct pci_device_id goto out_free_device_extension; } - mem_addr = pci_resource_start(pdev, 1); + if (pdev->device == PCI_DEVICE_ID_3WARE_9000) + mem_addr = pci_resource_start(pdev, 1); + else + mem_addr = pci_resource_start(pdev, 2); /* Save base address */ tw_dev->base_addr = ioremap(mem_addr, PAGE_SIZE); @@ -2130,6 +2185,8 @@ static void twa_remove(struct pci_dev *pdev) static struct pci_device_id twa_pci_tbl[] __devinitdata = { { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { PCI_VENDOR_ID_3WARE, PCI_DEVICE_ID_3WARE_9550SX, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { } }; MODULE_DEVICE_TABLE(pci, twa_pci_tbl); diff --git a/drivers/scsi/3w-9xxx.h b/drivers/scsi/3w-9xxx.h index 8c8ecbed3b58..46f22cdc8298 100644 --- a/drivers/scsi/3w-9xxx.h +++ b/drivers/scsi/3w-9xxx.h @@ -267,7 +267,6 @@ static twa_message_type twa_error_table[] = { #define TW_CONTROL_CLEAR_PARITY_ERROR 0x00800000 #define TW_CONTROL_CLEAR_QUEUE_ERROR 0x00400000 #define TW_CONTROL_CLEAR_PCI_ABORT 0x00100000 -#define TW_CONTROL_CLEAR_SBUF_WRITE_ERROR 0x00000008 /* Status register bit definitions */ #define TW_STATUS_MAJOR_VERSION_MASK 0xF0000000 @@ -285,9 +284,8 @@ static twa_message_type twa_error_table[] = { #define TW_STATUS_MICROCONTROLLER_READY 0x00002000 #define TW_STATUS_COMMAND_QUEUE_EMPTY 0x00001000 #define TW_STATUS_EXPECTED_BITS 0x00002000 -#define TW_STATUS_UNEXPECTED_BITS 0x00F00008 -#define TW_STATUS_SBUF_WRITE_ERROR 0x00000008 -#define TW_STATUS_VALID_INTERRUPT 0x00DF0008 +#define TW_STATUS_UNEXPECTED_BITS 0x00F00000 +#define TW_STATUS_VALID_INTERRUPT 0x00DF0000 /* RESPONSE QUEUE BIT DEFINITIONS */ #define TW_RESPONSE_ID_MASK 0x00000FF0 @@ -324,9 +322,9 @@ static twa_message_type twa_error_table[] = { /* Compatibility defines */ #define TW_9000_ARCH_ID 0x5 -#define TW_CURRENT_DRIVER_SRL 28 -#define TW_CURRENT_DRIVER_BUILD 9 -#define TW_CURRENT_DRIVER_BRANCH 4 +#define TW_CURRENT_DRIVER_SRL 30 +#define TW_CURRENT_DRIVER_BUILD 80 +#define TW_CURRENT_DRIVER_BRANCH 0 /* Phase defines */ #define TW_PHASE_INITIAL 0 @@ -334,6 +332,7 @@ static twa_message_type twa_error_table[] = { #define TW_PHASE_SGLIST 2 /* Misc defines */ +#define TW_9550SX_DRAIN_COMPLETED 0xFFFF #define TW_SECTOR_SIZE 512 #define TW_ALIGNMENT_9000 4 /* 4 bytes */ #define TW_ALIGNMENT_9000_SGL 0x3 @@ -417,6 +416,9 @@ static twa_message_type twa_error_table[] = { #ifndef PCI_DEVICE_ID_3WARE_9000 #define PCI_DEVICE_ID_3WARE_9000 0x1002 #endif +#ifndef PCI_DEVICE_ID_3WARE_9550SX +#define PCI_DEVICE_ID_3WARE_9550SX 0x1003 +#endif /* Bitmask macros to eliminate bitfields */ @@ -443,6 +445,7 @@ static twa_message_type twa_error_table[] = { #define TW_STATUS_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0x4) #define TW_COMMAND_QUEUE_REG_ADDR(x) (sizeof(dma_addr_t) > 4 ? ((unsigned char __iomem *)x->base_addr + 0x20) : ((unsigned char __iomem *)x->base_addr + 0x8)) #define TW_RESPONSE_QUEUE_REG_ADDR(x) ((unsigned char __iomem *)x->base_addr + 0xC) +#define TW_RESPONSE_QUEUE_REG_ADDR_LARGE(x) ((unsigned char __iomem *)x->base_addr + 0x30) #define TW_CLEAR_ALL_INTERRUPTS(x) (writel(TW_STATUS_VALID_INTERRUPT, TW_CONTROL_REG_ADDR(x))) #define TW_CLEAR_ATTENTION_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_ATTENTION_INTERRUPT, TW_CONTROL_REG_ADDR(x))) #define TW_CLEAR_HOST_INTERRUPT(x) (writel(TW_CONTROL_CLEAR_HOST_INTERRUPT, TW_CONTROL_REG_ADDR(x))) diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index 973c51fb0fe2..ae9e0203e9de 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1499,22 +1499,43 @@ static int tw_scsiop_inquiry(TW_Device_Extension *tw_dev, int request_id) return 0; } /* End tw_scsiop_inquiry() */ +static void tw_transfer_internal(TW_Device_Extension *tw_dev, int request_id, + void *data, unsigned int len) +{ + struct scsi_cmnd *cmd = tw_dev->srb[request_id]; + void *buf; + unsigned int transfer_len; + + if (cmd->use_sg) { + struct scatterlist *sg = + (struct scatterlist *)cmd->request_buffer; + buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + transfer_len = min(sg->length, len); + } else { + buf = cmd->request_buffer; + transfer_len = min(cmd->request_bufflen, len); + } + + memcpy(buf, data, transfer_len); + + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *)cmd->request_buffer; + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } +} + /* This function is called by the isr to complete an inquiry command */ static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_id) { unsigned char *is_unit_present; - unsigned char *request_buffer; + unsigned char request_buffer[36]; TW_Param *param; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_inquiry_complete()\n"); - /* Fill request buffer */ - if (tw_dev->srb[request_id]->request_buffer == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsiop_inquiry_complete(): Request buffer NULL.\n"); - return 1; - } - request_buffer = tw_dev->srb[request_id]->request_buffer; - memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); request_buffer[0] = TYPE_DISK; /* Peripheral device type */ request_buffer[1] = 0; /* Device type modifier */ request_buffer[2] = 0; /* No ansi/iso compliance */ @@ -1522,6 +1543,8 @@ static int tw_scsiop_inquiry_complete(TW_Device_Extension *tw_dev, int request_i memcpy(&request_buffer[8], "3ware ", 8); /* Vendor ID */ sprintf(&request_buffer[16], "Logical Disk %-2d ", tw_dev->srb[request_id]->device->id); memcpy(&request_buffer[32], TW_DRIVER_VERSION, 3); + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; if (param == NULL) { @@ -1612,7 +1635,7 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques { TW_Param *param; unsigned char *flags; - unsigned char *request_buffer; + unsigned char request_buffer[8]; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_mode_sense_complete()\n"); @@ -1622,8 +1645,7 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques return 1; } flags = (char *)&(param->data[0]); - request_buffer = tw_dev->srb[request_id]->buffer; - memset(request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); request_buffer[0] = 0xf; /* mode data length */ request_buffer[1] = 0; /* default medium type */ @@ -1635,6 +1657,8 @@ static int tw_scsiop_mode_sense_complete(TW_Device_Extension *tw_dev, int reques request_buffer[6] = 0x4; /* WCE on */ else request_buffer[6] = 0x0; /* WCE off */ + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); return 0; } /* End tw_scsiop_mode_sense_complete() */ @@ -1701,17 +1725,12 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req { unsigned char *param_data; u32 capacity; - char *buff; + char buff[8]; TW_Param *param; dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete()\n"); - buff = tw_dev->srb[request_id]->request_buffer; - if (buff == NULL) { - printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Request buffer NULL.\n"); - return 1; - } - memset(buff, 0, tw_dev->srb[request_id]->request_bufflen); + memset(buff, 0, sizeof(buff)); param = (TW_Param *)tw_dev->alignment_virtual_address[request_id]; if (param == NULL) { printk(KERN_WARNING "3w-xxxx: tw_scsiop_read_capacity_complete(): Bad alignment virtual address.\n"); @@ -1739,6 +1758,8 @@ static int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int req buff[6] = (TW_BLOCK_SIZE >> 8) & 0xff; buff[7] = TW_BLOCK_SIZE & 0xff; + tw_transfer_internal(tw_dev, request_id, buff, sizeof(buff)); + return 0; } /* End tw_scsiop_read_capacity_complete() */ diff --git a/drivers/scsi/53c7xx.c b/drivers/scsi/53c7xx.c index 2341d27ceed7..7a33c708f5b3 100644 --- a/drivers/scsi/53c7xx.c +++ b/drivers/scsi/53c7xx.c @@ -6090,8 +6090,8 @@ NCR53c7x0_release(struct Scsi_Host *host) { if (hostdata->num_cmds) printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n", host->host_no, hostdata->num_cmds); - if (hostdata->events) - vfree ((void *)hostdata->events); + + vfree(hostdata->events); /* XXX This assumes default cache mode to be IOMAP_FULL_CACHING, which * XXX may be invalid (CONFIG_060_WRITETHROUGH) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index f1e8c4223ed1..9c9f162bd6ed 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1,5 +1,11 @@ menu "SCSI device support" +config RAID_ATTRS + tristate "RAID Transport Class" + default n + ---help--- + Provides RAID + config SCSI tristate "SCSI device support" ---help--- @@ -229,6 +235,13 @@ config SCSI_ISCSI_ATTRS each attached iSCSI device to sysfs, say Y. Otherwise, say N. +config SCSI_SAS_ATTRS + tristate "SAS Transport Attributes" + depends on SCSI + help + If you wish to export transport-specific information about + each attached SAS device to sysfs, say Y. + endmenu menu "SCSI low-level drivers" @@ -250,7 +263,7 @@ config SCSI_DECNCR config SCSI_DECSII tristate "DEC SII Scsi Driver" - depends on MACH_DECSTATION && SCSI && MIPS32 + depends on MACH_DECSTATION && SCSI && 32BIT config BLK_DEV_3W_XXXX_RAID tristate "3ware 5/6/7/8xxx ATA-RAID support" @@ -459,6 +472,15 @@ config SCSI_ATA_PIIX If unsure, say N. +config SCSI_SATA_MV + tristate "Marvell SATA support" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for the Marvell Serial ATA family. + Currently supports 88SX[56]0[48][01] chips. + + If unsure, say N. + config SCSI_SATA_NV tristate "NVIDIA SATA support" depends on SCSI_SATA && PCI && EXPERIMENTAL @@ -467,11 +489,11 @@ config SCSI_SATA_NV If unsure, say N. -config SCSI_SATA_PROMISE - tristate "Promise SATA TX2/TX4 support" +config SCSI_PDC_ADMA + tristate "Pacific Digital ADMA support" depends on SCSI_SATA && PCI help - This option enables support for Promise Serial ATA TX2/TX4. + This option enables support for Pacific Digital ADMA controllers If unsure, say N. @@ -483,6 +505,14 @@ config SCSI_SATA_QSTOR If unsure, say N. +config SCSI_SATA_PROMISE + tristate "Promise SATA TX2/TX4 support" + depends on SCSI_SATA && PCI + help + This option enables support for Promise Serial ATA TX2/TX4. + + If unsure, say N. + config SCSI_SATA_SX4 tristate "Promise SATA SX4 support" depends on SCSI_SATA && PCI && EXPERIMENTAL @@ -499,6 +529,14 @@ config SCSI_SATA_SIL If unsure, say N. +config SCSI_SATA_SIL24 + tristate "Silicon Image 3124/3132 SATA support" + depends on SCSI_SATA && PCI && EXPERIMENTAL + help + This option enables support for Silicon Image 3124/3132 Serial ATA. + + If unsure, say N. + config SCSI_SATA_SIS tristate "SiS 964/180 SATA support" depends on SCSI_SATA && PCI && EXPERIMENTAL @@ -531,6 +569,11 @@ config SCSI_SATA_VITESSE If unsure, say N. +config SCSI_SATA_INTEL_COMBINED + bool + depends on IDE=y && !BLK_DEV_IDE_SATA && (SCSI_SATA_AHCI || SCSI_ATA_PIIX) + default y + config SCSI_BUSLOGIC tristate "BusLogic SCSI support" depends on (PCI || ISA || MCA) && SCSI && ISA_DMA_API @@ -1696,7 +1739,7 @@ config TT_DMA_EMUL config MAC_SCSI bool "Macintosh NCR5380 SCSI" - depends on MAC && SCSI + depends on MAC && SCSI=y help This is the NCR 5380 SCSI controller included on most of the 68030 based Macintoshes. If you have one of these say Y and read the @@ -1717,7 +1760,7 @@ config SCSI_MAC_ESP config MVME147_SCSI bool "WD33C93 SCSI driver for MVME147" - depends on MVME147 && SCSI + depends on MVME147 && SCSI=y help Support for the on-board SCSI controller on the Motorola MVME147 single-board computer. @@ -1758,7 +1801,7 @@ config SUN3_SCSI config SUN3X_ESP bool "Sun3x ESP SCSI" - depends on SUN3X && SCSI + depends on SUN3X && SCSI=y help The ESP was an on-board SCSI controller used on Sun 3/80 machines. Say Y here to compile in support for it. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 3746fb9fa2f5..2d4439826c08 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -22,6 +22,8 @@ subdir-$(CONFIG_PCMCIA) += pcmcia obj-$(CONFIG_SCSI) += scsi_mod.o +obj-$(CONFIG_RAID_ATTRS) += raid_class.o + # --- NOTE ORDERING HERE --- # For kernel non-modular link, transport attributes need to # be initialised before drivers @@ -29,6 +31,7 @@ obj-$(CONFIG_SCSI) += scsi_mod.o obj-$(CONFIG_SCSI_SPI_ATTRS) += scsi_transport_spi.o obj-$(CONFIG_SCSI_FC_ATTRS) += scsi_transport_fc.o obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o +obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o @@ -96,6 +99,7 @@ obj-$(CONFIG_SCSI_DC395x) += dc395x.o obj-$(CONFIG_SCSI_DC390T) += tmscsim.o obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ +obj-$(CONFIG_MEGARAID_SAS) += megaraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o obj-$(CONFIG_SCSI_SUNESP) += esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o @@ -126,12 +130,15 @@ obj-$(CONFIG_SCSI_ATA_PIIX) += libata.o ata_piix.o obj-$(CONFIG_SCSI_SATA_PROMISE) += libata.o sata_promise.o obj-$(CONFIG_SCSI_SATA_QSTOR) += libata.o sata_qstor.o obj-$(CONFIG_SCSI_SATA_SIL) += libata.o sata_sil.o +obj-$(CONFIG_SCSI_SATA_SIL24) += libata.o sata_sil24.o obj-$(CONFIG_SCSI_SATA_VIA) += libata.o sata_via.o obj-$(CONFIG_SCSI_SATA_VITESSE) += libata.o sata_vsc.o obj-$(CONFIG_SCSI_SATA_SIS) += libata.o sata_sis.o obj-$(CONFIG_SCSI_SATA_SX4) += libata.o sata_sx4.o obj-$(CONFIG_SCSI_SATA_NV) += libata.o sata_nv.o obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o +obj-$(CONFIG_SCSI_SATA_MV) += libata.o sata_mv.o +obj-$(CONFIG_SCSI_PDC_ADMA) += libata.o pdc_adma.o obj-$(CONFIG_ARM) += arm/ diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index f8ec6fe7d858..23392ae7df8b 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -88,6 +88,13 @@ */ #include <scsi/scsi_dbg.h> +#ifndef NDEBUG +#define NDEBUG 0 +#endif +#ifndef NDEBUG_ABORT +#define NDEBUG_ABORT 0 +#endif + #if (NDEBUG & NDEBUG_LISTS) #define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); } #define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); } @@ -359,7 +366,7 @@ static struct { {PHASE_UNKNOWN, "UNKNOWN"} }; -#ifdef NDEBUG +#if NDEBUG static struct { unsigned char mask; const char *name; diff --git a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c index 79ae73b23680..e1f2246ee7cd 100644 --- a/drivers/scsi/NCR53c406a.c +++ b/drivers/scsi/NCR53c406a.c @@ -62,7 +62,7 @@ #define SYNC_MODE 0 /* Synchronous transfer mode */ -#if DEBUG +#ifdef DEBUG #undef NCR53C406A_DEBUG #define NCR53C406A_DEBUG 1 #endif diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index ccdf440021fb..93416f760e5a 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -133,6 +133,7 @@ struct inquiry_data { static unsigned long aac_build_sg(struct scsi_cmnd* scsicmd, struct sgmap* sgmap); static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* psg); +static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg); static int aac_send_srb_fib(struct scsi_cmnd* scsicmd); #ifdef AAC_DETAILED_STATUS_INFO static char *aac_get_status_string(u32 status); @@ -312,18 +313,37 @@ int aac_get_containers(struct aac_dev *dev) } dresp = (struct aac_mount *)fib_data(fibptr); + if ((le32_to_cpu(dresp->status) == ST_OK) && + (le32_to_cpu(dresp->mnt[0].vol) == CT_NONE)) { + dinfo->command = cpu_to_le32(VM_NameServe64); + dinfo->count = cpu_to_le32(index); + dinfo->type = cpu_to_le32(FT_FILESYS); + + if (fib_send(ContainerCommand, + fibptr, + sizeof(struct aac_query_mount), + FsaNormal, + 1, 1, + NULL, NULL) < 0) + continue; + } else + dresp->mnt[0].capacityhigh = 0; + dprintk ((KERN_DEBUG - "VM_NameServe cid=%d status=%d vol=%d state=%d cap=%u\n", + "VM_NameServe cid=%d status=%d vol=%d state=%d cap=%llu\n", (int)index, (int)le32_to_cpu(dresp->status), (int)le32_to_cpu(dresp->mnt[0].vol), (int)le32_to_cpu(dresp->mnt[0].state), - (unsigned)le32_to_cpu(dresp->mnt[0].capacity))); + ((u64)le32_to_cpu(dresp->mnt[0].capacity)) + + (((u64)le32_to_cpu(dresp->mnt[0].capacityhigh)) << 32))); if ((le32_to_cpu(dresp->status) == ST_OK) && (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) && (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) { fsa_dev_ptr[index].valid = 1; fsa_dev_ptr[index].type = le32_to_cpu(dresp->mnt[0].vol); - fsa_dev_ptr[index].size = le32_to_cpu(dresp->mnt[0].capacity); + fsa_dev_ptr[index].size + = ((u64)le32_to_cpu(dresp->mnt[0].capacity)) + + (((u64)le32_to_cpu(dresp->mnt[0].capacityhigh)) << 32); if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY) fsa_dev_ptr[index].ro = 1; } @@ -348,6 +368,27 @@ static void aac_io_done(struct scsi_cmnd * scsicmd) spin_unlock_irqrestore(host->host_lock, cpu_flags); } +static void aac_internal_transfer(struct scsi_cmnd *scsicmd, void *data, unsigned int offset, unsigned int len) +{ + void *buf; + unsigned int transfer_len; + struct scatterlist *sg = scsicmd->request_buffer; + + if (scsicmd->use_sg) { + buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + transfer_len = min(sg->length, len + offset); + } else { + buf = scsicmd->request_buffer; + transfer_len = min(scsicmd->request_bufflen, len + offset); + } + + memcpy(buf + offset, data, transfer_len - offset); + + if (scsicmd->use_sg) + kunmap_atomic(buf - sg->offset, KM_IRQ0); + +} + static void get_container_name_callback(void *context, struct fib * fibptr) { struct aac_get_name_resp * get_name_reply; @@ -363,18 +404,22 @@ static void get_container_name_callback(void *context, struct fib * fibptr) /* Failure is irrelevant, using default value instead */ if ((le32_to_cpu(get_name_reply->status) == CT_OK) && (get_name_reply->data[0] != '\0')) { - int count; - char * dp; - char * sp = get_name_reply->data; + char *sp = get_name_reply->data; sp[sizeof(((struct aac_get_name_resp *)NULL)->data)-1] = '\0'; while (*sp == ' ') ++sp; - count = sizeof(((struct inquiry_data *)NULL)->inqd_pid); - dp = ((struct inquiry_data *)scsicmd->request_buffer)->inqd_pid; - if (*sp) do { - *dp++ = (*sp) ? *sp++ : ' '; - } while (--count > 0); + if (*sp) { + char d[sizeof(((struct inquiry_data *)NULL)->inqd_pid)]; + int count = sizeof(d); + char *dp = d; + do { + *dp++ = (*sp) ? *sp++ : ' '; + } while (--count > 0); + aac_internal_transfer(scsicmd, d, + offsetof(struct inquiry_data, inqd_pid), sizeof(d)); + } } + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; fib_complete(fibptr); @@ -434,7 +479,7 @@ static int aac_get_container_name(struct scsi_cmnd * scsicmd, int cid) * is updated in the struct fsa_dev_info structure rather than returned. */ -static int probe_container(struct aac_dev *dev, int cid) +int probe_container(struct aac_dev *dev, int cid) { struct fsa_dev_info *fsa_dev_ptr; int status; @@ -471,11 +516,29 @@ static int probe_container(struct aac_dev *dev, int cid) dresp = (struct aac_mount *) fib_data(fibptr); if ((le32_to_cpu(dresp->status) == ST_OK) && + (le32_to_cpu(dresp->mnt[0].vol) == CT_NONE)) { + dinfo->command = cpu_to_le32(VM_NameServe64); + dinfo->count = cpu_to_le32(cid); + dinfo->type = cpu_to_le32(FT_FILESYS); + + if (fib_send(ContainerCommand, + fibptr, + sizeof(struct aac_query_mount), + FsaNormal, + 1, 1, + NULL, NULL) < 0) + goto error; + } else + dresp->mnt[0].capacityhigh = 0; + + if ((le32_to_cpu(dresp->status) == ST_OK) && (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE) && (le32_to_cpu(dresp->mnt[0].state) != FSCS_HIDDEN)) { fsa_dev_ptr[cid].valid = 1; fsa_dev_ptr[cid].type = le32_to_cpu(dresp->mnt[0].vol); - fsa_dev_ptr[cid].size = le32_to_cpu(dresp->mnt[0].capacity); + fsa_dev_ptr[cid].size + = ((u64)le32_to_cpu(dresp->mnt[0].capacity)) + + (((u64)le32_to_cpu(dresp->mnt[0].capacityhigh)) << 32); if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY) fsa_dev_ptr[cid].ro = 1; } @@ -629,7 +692,7 @@ int aac_get_adapter_info(struct aac_dev* dev) fibptr, sizeof(*info), FsaNormal, - 1, 1, + -1, 1, /* First `interrupt' command uses special wait */ NULL, NULL); @@ -777,34 +840,36 @@ int aac_get_adapter_info(struct aac_dev* dev) /* * 57 scatter gather elements */ - dev->scsi_host_ptr->sg_tablesize = (dev->max_fib_size - - sizeof(struct aac_fibhdr) - - sizeof(struct aac_write) + sizeof(struct sgmap)) / - sizeof(struct sgmap); - if (dev->dac_support) { - /* - * 38 scatter gather elements - */ - dev->scsi_host_ptr->sg_tablesize = - (dev->max_fib_size - + if (!(dev->raw_io_interface)) { + dev->scsi_host_ptr->sg_tablesize = (dev->max_fib_size - sizeof(struct aac_fibhdr) - - sizeof(struct aac_write64) + - sizeof(struct sgmap64)) / - sizeof(struct sgmap64); - } - dev->scsi_host_ptr->max_sectors = AAC_MAX_32BIT_SGBCOUNT; - if(!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) { - /* - * Worst case size that could cause sg overflow when - * we break up SG elements that are larger than 64KB. - * Would be nice if we could tell the SCSI layer what - * the maximum SG element size can be. Worst case is - * (sg_tablesize-1) 4KB elements with one 64KB - * element. - * 32bit -> 468 or 238KB 64bit -> 424 or 212KB - */ - dev->scsi_host_ptr->max_sectors = - (dev->scsi_host_ptr->sg_tablesize * 8) + 112; + sizeof(struct aac_write) + sizeof(struct sgentry)) / + sizeof(struct sgentry); + if (dev->dac_support) { + /* + * 38 scatter gather elements + */ + dev->scsi_host_ptr->sg_tablesize = + (dev->max_fib_size - + sizeof(struct aac_fibhdr) - + sizeof(struct aac_write64) + + sizeof(struct sgentry64)) / + sizeof(struct sgentry64); + } + dev->scsi_host_ptr->max_sectors = AAC_MAX_32BIT_SGBCOUNT; + if(!(dev->adapter_info.options & AAC_OPT_NEW_COMM)) { + /* + * Worst case size that could cause sg overflow when + * we break up SG elements that are larger than 64KB. + * Would be nice if we could tell the SCSI layer what + * the maximum SG element size can be. Worst case is + * (sg_tablesize-1) 4KB elements with one 64KB + * element. + * 32bit -> 468 or 238KB 64bit -> 424 or 212KB + */ + dev->scsi_host_ptr->max_sectors = + (dev->scsi_host_ptr->sg_tablesize * 8) + 112; + } } fib_complete(fibptr); @@ -814,12 +879,11 @@ int aac_get_adapter_info(struct aac_dev* dev) } -static void read_callback(void *context, struct fib * fibptr) +static void io_callback(void *context, struct fib * fibptr) { struct aac_dev *dev; struct aac_read_reply *readreply; struct scsi_cmnd *scsicmd; - u32 lba; u32 cid; scsicmd = (struct scsi_cmnd *) context; @@ -827,8 +891,40 @@ static void read_callback(void *context, struct fib * fibptr) dev = (struct aac_dev *)scsicmd->device->host->hostdata; cid = ID_LUN_TO_CONTAINER(scsicmd->device->id, scsicmd->device->lun); - lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3]; - dprintk((KERN_DEBUG "read_callback[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies)); + if (nblank(dprintk(x))) { + u64 lba; + switch (scsicmd->cmnd[0]) { + case WRITE_6: + case READ_6: + lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | + (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3]; + break; + case WRITE_16: + case READ_16: + lba = ((u64)scsicmd->cmnd[2] << 56) | + ((u64)scsicmd->cmnd[3] << 48) | + ((u64)scsicmd->cmnd[4] << 40) | + ((u64)scsicmd->cmnd[5] << 32) | + ((u64)scsicmd->cmnd[6] << 24) | + (scsicmd->cmnd[7] << 16) | + (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9]; + break; + case WRITE_12: + case READ_12: + lba = ((u64)scsicmd->cmnd[2] << 24) | + (scsicmd->cmnd[3] << 16) | + (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + break; + default: + lba = ((u64)scsicmd->cmnd[2] << 24) | + (scsicmd->cmnd[3] << 16) | + (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + break; + } + printk(KERN_DEBUG + "io_callback[cpu %d]: lba = %llu, t = %ld.\n", + smp_processor_id(), (unsigned long long)lba, jiffies); + } if (fibptr == NULL) BUG(); @@ -847,7 +943,7 @@ static void read_callback(void *context, struct fib * fibptr) scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; else { #ifdef AAC_DETAILED_STATUS_INFO - printk(KERN_WARNING "read_callback: io failed, status = %d\n", + printk(KERN_WARNING "io_callback: io failed, status = %d\n", le32_to_cpu(readreply->status)); #endif scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION; @@ -867,56 +963,9 @@ static void read_callback(void *context, struct fib * fibptr) aac_io_done(scsicmd); } -static void write_callback(void *context, struct fib * fibptr) -{ - struct aac_dev *dev; - struct aac_write_reply *writereply; - struct scsi_cmnd *scsicmd; - u32 lba; - u32 cid; - - scsicmd = (struct scsi_cmnd *) context; - dev = (struct aac_dev *)scsicmd->device->host->hostdata; - cid = ID_LUN_TO_CONTAINER(scsicmd->device->id, scsicmd->device->lun); - - lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3]; - dprintk((KERN_DEBUG "write_callback[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies)); - if (fibptr == NULL) - BUG(); - - if(scsicmd->use_sg) - pci_unmap_sg(dev->pdev, - (struct scatterlist *)scsicmd->buffer, - scsicmd->use_sg, - scsicmd->sc_data_direction); - else if(scsicmd->request_bufflen) - pci_unmap_single(dev->pdev, scsicmd->SCp.dma_handle, - scsicmd->request_bufflen, - scsicmd->sc_data_direction); - - writereply = (struct aac_write_reply *) fib_data(fibptr); - if (le32_to_cpu(writereply->status) == ST_OK) - scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; - else { - printk(KERN_WARNING "write_callback: write failed, status = %d\n", writereply->status); - scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION; - set_sense((u8 *) &dev->fsa_dev[cid].sense_data, - HARDWARE_ERROR, - SENCODE_INTERNAL_TARGET_FAILURE, - ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0, - 0, 0); - memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, - sizeof(struct sense_data)); - } - - fib_complete(fibptr); - fib_free(fibptr); - aac_io_done(scsicmd); -} - static int aac_read(struct scsi_cmnd * scsicmd, int cid) { - u32 lba; + u64 lba; u32 count; int status; @@ -928,23 +977,69 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) /* * Get block address and transfer length */ - if (scsicmd->cmnd[0] == READ_6) /* 6 byte command */ - { + switch (scsicmd->cmnd[0]) { + case READ_6: dprintk((KERN_DEBUG "aachba: received a read(6) command on id %d.\n", cid)); - lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3]; + lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | + (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3]; count = scsicmd->cmnd[4]; if (count == 0) count = 256; - } else { + break; + case READ_16: + dprintk((KERN_DEBUG "aachba: received a read(16) command on id %d.\n", cid)); + + lba = ((u64)scsicmd->cmnd[2] << 56) | + ((u64)scsicmd->cmnd[3] << 48) | + ((u64)scsicmd->cmnd[4] << 40) | + ((u64)scsicmd->cmnd[5] << 32) | + ((u64)scsicmd->cmnd[6] << 24) | + (scsicmd->cmnd[7] << 16) | + (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9]; + count = (scsicmd->cmnd[10] << 24) | + (scsicmd->cmnd[11] << 16) | + (scsicmd->cmnd[12] << 8) | scsicmd->cmnd[13]; + break; + case READ_12: + dprintk((KERN_DEBUG "aachba: received a read(12) command on id %d.\n", cid)); + + lba = ((u64)scsicmd->cmnd[2] << 24) | + (scsicmd->cmnd[3] << 16) | + (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + count = (scsicmd->cmnd[6] << 24) | + (scsicmd->cmnd[7] << 16) | + (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9]; + break; + default: dprintk((KERN_DEBUG "aachba: received a read(10) command on id %d.\n", cid)); - lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + lba = ((u64)scsicmd->cmnd[2] << 24) | + (scsicmd->cmnd[3] << 16) | + (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8]; + break; } - dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %u, t = %ld.\n", + dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %llu, t = %ld.\n", smp_processor_id(), (unsigned long long)lba, jiffies)); + if ((!(dev->raw_io_interface) || !(dev->raw_io_64)) && + (lba & 0xffffffff00000000LL)) { + dprintk((KERN_DEBUG "aac_read: Illegal lba\n")); + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | + SAM_STAT_CHECK_CONDITION; + set_sense((u8 *) &dev->fsa_dev[cid].sense_data, + HARDWARE_ERROR, + SENCODE_INTERNAL_TARGET_FAILURE, + ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0, + 0, 0); + memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, + (sizeof(dev->fsa_dev[cid].sense_data) > sizeof(scsicmd->sense_buffer)) + ? sizeof(scsicmd->sense_buffer) + : sizeof(dev->fsa_dev[cid].sense_data)); + scsicmd->scsi_done(scsicmd); + return 0; + } /* * Alocate and initialize a Fib */ @@ -954,13 +1049,38 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) fib_init(cmd_fibcontext); - if (dev->dac_support == 1) { + if (dev->raw_io_interface) { + struct aac_raw_io *readcmd; + readcmd = (struct aac_raw_io *) fib_data(cmd_fibcontext); + readcmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff)); + readcmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32)); + readcmd->count = cpu_to_le32(count<<9); + readcmd->cid = cpu_to_le16(cid); + readcmd->flags = cpu_to_le16(1); + readcmd->bpTotal = 0; + readcmd->bpComplete = 0; + + aac_build_sgraw(scsicmd, &readcmd->sg); + fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(readcmd->sg.count) - 1) * sizeof (struct sgentryraw)); + if (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))) + BUG(); + /* + * Now send the Fib to the adapter + */ + status = fib_send(ContainerRawIo, + cmd_fibcontext, + fibsize, + FsaNormal, + 0, 1, + (fib_callback) io_callback, + (void *) scsicmd); + } else if (dev->dac_support == 1) { struct aac_read64 *readcmd; readcmd = (struct aac_read64 *) fib_data(cmd_fibcontext); readcmd->command = cpu_to_le32(VM_CtHostRead64); readcmd->cid = cpu_to_le16(cid); readcmd->sector_count = cpu_to_le16(count); - readcmd->block = cpu_to_le32(lba); + readcmd->block = cpu_to_le32((u32)(lba&0xffffffff)); readcmd->pad = 0; readcmd->flags = 0; @@ -968,7 +1088,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) fibsize = sizeof(struct aac_read64) + ((le32_to_cpu(readcmd->sg.count) - 1) * sizeof (struct sgentry64)); - BUG_ON (fibsize > (sizeof(struct hw_fib) - + BUG_ON (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))); /* * Now send the Fib to the adapter @@ -978,14 +1098,14 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) fibsize, FsaNormal, 0, 1, - (fib_callback) read_callback, + (fib_callback) io_callback, (void *) scsicmd); } else { struct aac_read *readcmd; readcmd = (struct aac_read *) fib_data(cmd_fibcontext); readcmd->command = cpu_to_le32(VM_CtBlockRead); readcmd->cid = cpu_to_le32(cid); - readcmd->block = cpu_to_le32(lba); + readcmd->block = cpu_to_le32((u32)(lba&0xffffffff)); readcmd->count = cpu_to_le32(count * 512); aac_build_sg(scsicmd, &readcmd->sg); @@ -1002,7 +1122,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) fibsize, FsaNormal, 0, 1, - (fib_callback) read_callback, + (fib_callback) io_callback, (void *) scsicmd); } @@ -1027,7 +1147,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) static int aac_write(struct scsi_cmnd * scsicmd, int cid) { - u32 lba; + u64 lba; u32 count; int status; u16 fibsize; @@ -1044,13 +1164,48 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) count = scsicmd->cmnd[4]; if (count == 0) count = 256; + } else if (scsicmd->cmnd[0] == WRITE_16) { /* 16 byte command */ + dprintk((KERN_DEBUG "aachba: received a write(16) command on id %d.\n", cid)); + + lba = ((u64)scsicmd->cmnd[2] << 56) | + ((u64)scsicmd->cmnd[3] << 48) | + ((u64)scsicmd->cmnd[4] << 40) | + ((u64)scsicmd->cmnd[5] << 32) | + ((u64)scsicmd->cmnd[6] << 24) | + (scsicmd->cmnd[7] << 16) | + (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9]; + count = (scsicmd->cmnd[10] << 24) | (scsicmd->cmnd[11] << 16) | + (scsicmd->cmnd[12] << 8) | scsicmd->cmnd[13]; + } else if (scsicmd->cmnd[0] == WRITE_12) { /* 12 byte command */ + dprintk((KERN_DEBUG "aachba: received a write(12) command on id %d.\n", cid)); + + lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) + | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + count = (scsicmd->cmnd[6] << 24) | (scsicmd->cmnd[7] << 16) + | (scsicmd->cmnd[8] << 8) | scsicmd->cmnd[9]; } else { dprintk((KERN_DEBUG "aachba: received a write(10) command on id %d.\n", cid)); - lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; + lba = ((u64)scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5]; count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8]; } - dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %u, t = %ld.\n", + dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %llu, t = %ld.\n", smp_processor_id(), (unsigned long long)lba, jiffies)); + if ((!(dev->raw_io_interface) || !(dev->raw_io_64)) + && (lba & 0xffffffff00000000LL)) { + dprintk((KERN_DEBUG "aac_write: Illegal lba\n")); + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_CHECK_CONDITION; + set_sense((u8 *) &dev->fsa_dev[cid].sense_data, + HARDWARE_ERROR, + SENCODE_INTERNAL_TARGET_FAILURE, + ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0, + 0, 0); + memcpy(scsicmd->sense_buffer, &dev->fsa_dev[cid].sense_data, + (sizeof(dev->fsa_dev[cid].sense_data) > sizeof(scsicmd->sense_buffer)) + ? sizeof(scsicmd->sense_buffer) + : sizeof(dev->fsa_dev[cid].sense_data)); + scsicmd->scsi_done(scsicmd); + return 0; + } /* * Allocate and initialize a Fib then setup a BlockWrite command */ @@ -1061,13 +1216,38 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) } fib_init(cmd_fibcontext); - if(dev->dac_support == 1) { + if (dev->raw_io_interface) { + struct aac_raw_io *writecmd; + writecmd = (struct aac_raw_io *) fib_data(cmd_fibcontext); + writecmd->block[0] = cpu_to_le32((u32)(lba&0xffffffff)); + writecmd->block[1] = cpu_to_le32((u32)((lba&0xffffffff00000000LL)>>32)); + writecmd->count = cpu_to_le32(count<<9); + writecmd->cid = cpu_to_le16(cid); + writecmd->flags = 0; + writecmd->bpTotal = 0; + writecmd->bpComplete = 0; + + aac_build_sgraw(scsicmd, &writecmd->sg); + fibsize = sizeof(struct aac_raw_io) + ((le32_to_cpu(writecmd->sg.count) - 1) * sizeof (struct sgentryraw)); + if (fibsize > (dev->max_fib_size - sizeof(struct aac_fibhdr))) + BUG(); + /* + * Now send the Fib to the adapter + */ + status = fib_send(ContainerRawIo, + cmd_fibcontext, + fibsize, + FsaNormal, + 0, 1, + (fib_callback) io_callback, + (void *) scsicmd); + } else if (dev->dac_support == 1) { struct aac_write64 *writecmd; writecmd = (struct aac_write64 *) fib_data(cmd_fibcontext); writecmd->command = cpu_to_le32(VM_CtHostWrite64); writecmd->cid = cpu_to_le16(cid); writecmd->sector_count = cpu_to_le16(count); - writecmd->block = cpu_to_le32(lba); + writecmd->block = cpu_to_le32((u32)(lba&0xffffffff)); writecmd->pad = 0; writecmd->flags = 0; @@ -1085,14 +1265,14 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) fibsize, FsaNormal, 0, 1, - (fib_callback) write_callback, + (fib_callback) io_callback, (void *) scsicmd); } else { struct aac_write *writecmd; writecmd = (struct aac_write *) fib_data(cmd_fibcontext); writecmd->command = cpu_to_le32(VM_CtBlockWrite); writecmd->cid = cpu_to_le32(cid); - writecmd->block = cpu_to_le32(lba); + writecmd->block = cpu_to_le32((u32)(lba&0xffffffff)); writecmd->count = cpu_to_le32(count * 512); writecmd->sg.count = cpu_to_le32(1); /* ->stable is not used - it did mean which type of write */ @@ -1111,7 +1291,7 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) fibsize, FsaNormal, 0, 1, - (fib_callback) write_callback, + (fib_callback) io_callback, (void *) scsicmd); } @@ -1281,11 +1461,18 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) */ if ((fsa_dev_ptr[cid].valid & 1) == 0) { switch (scsicmd->cmnd[0]) { + case SERVICE_ACTION_IN: + if (!(dev->raw_io_interface) || + !(dev->raw_io_64) || + ((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16)) + break; case INQUIRY: case READ_CAPACITY: case TEST_UNIT_READY: spin_unlock_irq(host->host_lock); probe_container(dev, cid); + if ((fsa_dev_ptr[cid].valid & 1) == 0) + fsa_dev_ptr[cid].valid = 0; spin_lock_irq(host->host_lock); if (fsa_dev_ptr[cid].valid == 0) { scsicmd->result = DID_NO_CONNECT << 16; @@ -1340,44 +1527,86 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) switch (scsicmd->cmnd[0]) { case INQUIRY: { - struct inquiry_data *inq_data_ptr; + struct inquiry_data inq_data; dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", scsicmd->device->id)); - inq_data_ptr = (struct inquiry_data *)scsicmd->request_buffer; - memset(inq_data_ptr, 0, sizeof (struct inquiry_data)); + memset(&inq_data, 0, sizeof (struct inquiry_data)); - inq_data_ptr->inqd_ver = 2; /* claim compliance to SCSI-2 */ - inq_data_ptr->inqd_dtq = 0x80; /* set RMB bit to one indicating that the medium is removable */ - inq_data_ptr->inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */ - inq_data_ptr->inqd_len = 31; + inq_data.inqd_ver = 2; /* claim compliance to SCSI-2 */ + inq_data.inqd_rdf = 2; /* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */ + inq_data.inqd_len = 31; /*Format for "pad2" is RelAdr | WBus32 | WBus16 | Sync | Linked |Reserved| CmdQue | SftRe */ - inq_data_ptr->inqd_pad2= 0x32 ; /*WBus16|Sync|CmdQue */ + inq_data.inqd_pad2= 0x32 ; /*WBus16|Sync|CmdQue */ /* * Set the Vendor, Product, and Revision Level * see: <vendor>.c i.e. aac.c */ if (scsicmd->device->id == host->this_id) { - setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), (sizeof(container_types)/sizeof(char *))); - inq_data_ptr->inqd_pdt = INQD_PDT_PROC; /* Processor device */ + setinqstr(cardtype, (void *) (inq_data.inqd_vid), (sizeof(container_types)/sizeof(char *))); + inq_data.inqd_pdt = INQD_PDT_PROC; /* Processor device */ + aac_internal_transfer(scsicmd, &inq_data, 0, sizeof(inq_data)); scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; scsicmd->scsi_done(scsicmd); return 0; } - setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), fsa_dev_ptr[cid].type); - inq_data_ptr->inqd_pdt = INQD_PDT_DA; /* Direct/random access device */ + setinqstr(cardtype, (void *) (inq_data.inqd_vid), fsa_dev_ptr[cid].type); + inq_data.inqd_pdt = INQD_PDT_DA; /* Direct/random access device */ + aac_internal_transfer(scsicmd, &inq_data, 0, sizeof(inq_data)); return aac_get_container_name(scsicmd, cid); } + case SERVICE_ACTION_IN: + if (!(dev->raw_io_interface) || + !(dev->raw_io_64) || + ((scsicmd->cmnd[1] & 0x1f) != SAI_READ_CAPACITY_16)) + break; + { + u64 capacity; + char cp[12]; + unsigned int offset = 0; + + dprintk((KERN_DEBUG "READ CAPACITY_16 command.\n")); + capacity = fsa_dev_ptr[cid].size - 1; + if (scsicmd->cmnd[13] > 12) { + offset = scsicmd->cmnd[13] - 12; + if (offset > sizeof(cp)) + break; + memset(cp, 0, offset); + aac_internal_transfer(scsicmd, cp, 0, offset); + } + cp[0] = (capacity >> 56) & 0xff; + cp[1] = (capacity >> 48) & 0xff; + cp[2] = (capacity >> 40) & 0xff; + cp[3] = (capacity >> 32) & 0xff; + cp[4] = (capacity >> 24) & 0xff; + cp[5] = (capacity >> 16) & 0xff; + cp[6] = (capacity >> 8) & 0xff; + cp[7] = (capacity >> 0) & 0xff; + cp[8] = 0; + cp[9] = 0; + cp[10] = 2; + cp[11] = 0; + aac_internal_transfer(scsicmd, cp, offset, sizeof(cp)); + + /* Do not cache partition table for arrays */ + scsicmd->device->removable = 1; + + scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; + scsicmd->scsi_done(scsicmd); + + return 0; + } + case READ_CAPACITY: { u32 capacity; - char *cp; + char cp[8]; dprintk((KERN_DEBUG "READ CAPACITY command.\n")); - if (fsa_dev_ptr[cid].size <= 0x100000000LL) + if (fsa_dev_ptr[cid].size <= 0x100000000ULL) capacity = fsa_dev_ptr[cid].size - 1; else capacity = (u32)-1; - cp = scsicmd->request_buffer; + cp[0] = (capacity >> 24) & 0xff; cp[1] = (capacity >> 16) & 0xff; cp[2] = (capacity >> 8) & 0xff; @@ -1386,6 +1615,9 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) cp[5] = 0; cp[6] = 2; cp[7] = 0; + aac_internal_transfer(scsicmd, cp, 0, sizeof(cp)); + /* Do not cache partition table for arrays */ + scsicmd->device->removable = 1; scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; scsicmd->scsi_done(scsicmd); @@ -1395,15 +1627,15 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) case MODE_SENSE: { - char *mode_buf; + char mode_buf[4]; dprintk((KERN_DEBUG "MODE SENSE command.\n")); - mode_buf = scsicmd->request_buffer; mode_buf[0] = 3; /* Mode data length */ mode_buf[1] = 0; /* Medium type - default */ mode_buf[2] = 0; /* Device-specific param, bit 8: 0/1 = write enabled/protected */ mode_buf[3] = 0; /* Block descriptor length */ + aac_internal_transfer(scsicmd, mode_buf, 0, sizeof(mode_buf)); scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; scsicmd->scsi_done(scsicmd); @@ -1411,10 +1643,9 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) } case MODE_SENSE_10: { - char *mode_buf; + char mode_buf[8]; dprintk((KERN_DEBUG "MODE SENSE 10 byte command.\n")); - mode_buf = scsicmd->request_buffer; mode_buf[0] = 0; /* Mode data length (MSB) */ mode_buf[1] = 6; /* Mode data length (LSB) */ mode_buf[2] = 0; /* Medium type - default */ @@ -1423,6 +1654,7 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) mode_buf[5] = 0; /* reserved */ mode_buf[6] = 0; /* Block descriptor length (MSB) */ mode_buf[7] = 0; /* Block descriptor length (LSB) */ + aac_internal_transfer(scsicmd, mode_buf, 0, sizeof(mode_buf)); scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | SAM_STAT_GOOD; scsicmd->scsi_done(scsicmd); @@ -1466,6 +1698,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) { case READ_6: case READ_10: + case READ_12: + case READ_16: /* * Hack to keep track of ordinal number of the device that * corresponds to a container. Needed to convert @@ -1473,17 +1707,19 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) */ spin_unlock_irq(host->host_lock); - if (scsicmd->request->rq_disk) - memcpy(fsa_dev_ptr[cid].devname, - scsicmd->request->rq_disk->disk_name, - 8); - + if (scsicmd->request->rq_disk) + strlcpy(fsa_dev_ptr[cid].devname, + scsicmd->request->rq_disk->disk_name, + min(sizeof(fsa_dev_ptr[cid].devname), + sizeof(scsicmd->request->rq_disk->disk_name) + 1)); ret = aac_read(scsicmd, cid); spin_lock_irq(host->host_lock); return ret; case WRITE_6: case WRITE_10: + case WRITE_12: + case WRITE_16: spin_unlock_irq(host->host_lock); ret = aac_write(scsicmd, cid); spin_lock_irq(host->host_lock); @@ -1714,6 +1950,8 @@ static void aac_srb_callback(void *context, struct fib * fibptr) case WRITE_10: case READ_12: case WRITE_12: + case READ_16: + case WRITE_16: if(le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow ) { printk(KERN_WARNING"aacraid: SCSI CMD underflow\n"); } else { @@ -1819,8 +2057,8 @@ static void aac_srb_callback(void *context, struct fib * fibptr) sizeof(scsicmd->sense_buffer) : le32_to_cpu(srbreply->sense_data_size); #ifdef AAC_DETAILED_STATUS_INFO - dprintk((KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", - le32_to_cpu(srbreply->status), len)); + printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", + le32_to_cpu(srbreply->status), len); #endif memcpy(scsicmd->sense_buffer, srbreply->sense_data, len); @@ -1894,7 +2132,7 @@ static int aac_send_srb_fib(struct scsi_cmnd* scsicmd) srbcmd->id = cpu_to_le32(scsicmd->device->id); srbcmd->lun = cpu_to_le32(scsicmd->device->lun); srbcmd->flags = cpu_to_le32(flag); - timeout = (scsicmd->timeout-jiffies)/HZ; + timeout = scsicmd->timeout_per_command/HZ; if(timeout == 0){ timeout = 1; } @@ -2077,6 +2315,76 @@ static unsigned long aac_build_sg64(struct scsi_cmnd* scsicmd, struct sgmap64* p return byte_count; } +static unsigned long aac_build_sgraw(struct scsi_cmnd* scsicmd, struct sgmapraw* psg) +{ + struct Scsi_Host *host = scsicmd->device->host; + struct aac_dev *dev = (struct aac_dev *)host->hostdata; + unsigned long byte_count = 0; + + // Get rid of old data + psg->count = 0; + psg->sg[0].next = 0; + psg->sg[0].prev = 0; + psg->sg[0].addr[0] = 0; + psg->sg[0].addr[1] = 0; + psg->sg[0].count = 0; + psg->sg[0].flags = 0; + if (scsicmd->use_sg) { + struct scatterlist *sg; + int i; + int sg_count; + sg = (struct scatterlist *) scsicmd->request_buffer; + + sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg, + scsicmd->sc_data_direction); + + for (i = 0; i < sg_count; i++) { + int count = sg_dma_len(sg); + u64 addr = sg_dma_address(sg); + psg->sg[i].next = 0; + psg->sg[i].prev = 0; + psg->sg[i].addr[1] = cpu_to_le32((u32)(addr>>32)); + psg->sg[i].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff)); + psg->sg[i].count = cpu_to_le32(count); + psg->sg[i].flags = 0; + byte_count += count; + sg++; + } + psg->count = cpu_to_le32(sg_count); + /* hba wants the size to be exact */ + if(byte_count > scsicmd->request_bufflen){ + u32 temp = le32_to_cpu(psg->sg[i-1].count) - + (byte_count - scsicmd->request_bufflen); + psg->sg[i-1].count = cpu_to_le32(temp); + byte_count = scsicmd->request_bufflen; + } + /* Check for command underflow */ + if(scsicmd->underflow && (byte_count < scsicmd->underflow)){ + printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n", + byte_count, scsicmd->underflow); + } + } + else if(scsicmd->request_bufflen) { + int count; + u64 addr; + scsicmd->SCp.dma_handle = pci_map_single(dev->pdev, + scsicmd->request_buffer, + scsicmd->request_bufflen, + scsicmd->sc_data_direction); + addr = scsicmd->SCp.dma_handle; + count = scsicmd->request_bufflen; + psg->count = cpu_to_le32(1); + psg->sg[0].next = 0; + psg->sg[0].prev = 0; + psg->sg[0].addr[1] = cpu_to_le32((u32)(addr>>32)); + psg->sg[0].addr[0] = cpu_to_le32((u32)(addr & 0xffffffff)); + psg->sg[0].count = cpu_to_le32(count); + psg->sg[0].flags = 0; + byte_count = scsicmd->request_bufflen; + } + return byte_count; +} + #ifdef AAC_DETAILED_STATUS_INFO struct aac_srb_status_info { diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index 4ab07861b457..d54b1cc88d0d 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -1,6 +1,10 @@ #if (!defined(dprintk)) # define dprintk(x) #endif +/* eg: if (nblank(dprintk(x))) */ +#define _nblank(x) #x +#define nblank(x) _nblank(x)[0] + /*------------------------------------------------------------------------------ * D E F I N E S @@ -15,7 +19,7 @@ #define AAC_MAX_LUN (8) #define AAC_MAX_HOSTPHYSMEMPAGES (0xfffff) -#define AAC_MAX_32BIT_SGBCOUNT ((unsigned short)512) +#define AAC_MAX_32BIT_SGBCOUNT ((unsigned short)256) /* * These macros convert from physical channels to virtual channels @@ -110,6 +114,22 @@ struct user_sgentry64 { u32 count; /* Length. */ }; +struct sgentryraw { + __le32 next; /* reserved for F/W use */ + __le32 prev; /* reserved for F/W use */ + __le32 addr[2]; + __le32 count; + __le32 flags; /* reserved for F/W use */ +}; + +struct user_sgentryraw { + u32 next; /* reserved for F/W use */ + u32 prev; /* reserved for F/W use */ + u32 addr[2]; + u32 count; + u32 flags; /* reserved for F/W use */ +}; + /* * SGMAP * @@ -137,6 +157,16 @@ struct user_sgmap64 { struct user_sgentry64 sg[1]; }; +struct sgmapraw { + __le32 count; + struct sgentryraw sg[1]; +}; + +struct user_sgmapraw { + u32 count; + struct user_sgentryraw sg[1]; +}; + struct creation_info { u8 buildnum; /* e.g., 588 */ @@ -276,7 +306,6 @@ enum aac_queue_types { */ #define FsaNormal 1 -#define FsaHigh 2 /* * Define the FIB. The FIB is the where all the requested data and @@ -351,6 +380,7 @@ struct hw_fib { */ #define ContainerCommand 500 #define ContainerCommand64 501 +#define ContainerRawIo 502 /* * Cluster Commands */ @@ -456,6 +486,7 @@ struct adapter_ops { void (*adapter_interrupt)(struct aac_dev *dev); void (*adapter_notify)(struct aac_dev *dev, u32 event); + void (*adapter_disable_int)(struct aac_dev *dev); int (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 p2, u32 p3, u32 p4, u32 p5, u32 p6, u32 *status, u32 *r1, u32 *r2, u32 *r3, u32 *r4); int (*adapter_check_health)(struct aac_dev *dev); }; @@ -518,8 +549,6 @@ struct aac_queue { /* This is only valid for adapter to host command queues. */ spinlock_t *lock; /* Spinlock for this queue must take this lock before accessing the lock */ spinlock_t lockdata; /* Actual lock (used only on one side of the lock) */ - unsigned long SavedIrql; /* Previous IRQL when the spin lock is taken */ - u32 padding; /* Padding - FIXME - can remove I believe */ struct list_head cmdq; /* A queue of FIBs which need to be prcessed by the FS thread. This is */ /* only valid for command queues which receive entries from the adapter. */ struct list_head pendingq; /* A queue of outstanding fib's to the adapter. */ @@ -748,7 +777,9 @@ struct fsa_dev_info { u64 last; u64 size; u32 type; + u32 config_waiting_on; u16 queue_depth; + u8 config_needed; u8 valid; u8 ro; u8 locked; @@ -981,6 +1012,10 @@ struct aac_dev u8 nondasd_support; u8 dac_support; u8 raid_scsi_mode; + /* macro side-effects BEWARE */ +# define raw_io_interface \ + init->InitStructRevision==cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4) + u8 raw_io_64; u8 printf_enabled; }; @@ -990,6 +1025,9 @@ struct aac_dev #define aac_adapter_notify(dev, event) \ (dev)->a_ops.adapter_notify(dev, event) +#define aac_adapter_disable_int(dev) \ + (dev)->a_ops.adapter_disable_int(dev) + #define aac_adapter_sync_cmd(dev, command, p1, p2, p3, p4, p5, p6, status, r1, r2, r3, r4) \ (dev)->a_ops.adapter_sync_cmd(dev, command, p1, p2, p3, p4, p5, p6, status, r1, r2, r3, r4) @@ -1156,6 +1194,17 @@ struct aac_write_reply __le32 committed; }; +struct aac_raw_io +{ + __le32 block[2]; + __le32 count; + __le16 cid; + __le16 flags; /* 00 W, 01 R */ + __le16 bpTotal; /* reserved for F/W use */ + __le16 bpComplete; /* reserved for F/W use */ + struct sgmapraw sg; +}; + #define CT_FLUSH_CACHE 129 struct aac_synchronize { __le32 command; /* VM_ContainerConfig */ @@ -1196,7 +1245,7 @@ struct aac_srb }; /* - * This and assocated data structs are used by the + * This and associated data structs are used by the * ioctl caller and are in cpu order. */ struct user_aac_srb @@ -1317,8 +1366,10 @@ struct aac_srb_reply #define VM_CtBlockVerify64 18 #define VM_CtHostRead64 19 #define VM_CtHostWrite64 20 +#define VM_DrvErrTblLog 21 +#define VM_NameServe64 22 -#define MAX_VMCOMMAND_NUM 21 /* used for sizing stats array - leave last */ +#define MAX_VMCOMMAND_NUM 23 /* used for sizing stats array - leave last */ /* * Descriptive information (eg, vital stats) @@ -1427,6 +1478,7 @@ struct aac_mntent { manager (eg, filesystem) */ __le32 altoid; /* != oid <==> snapshot or broken mirror exists */ + __le32 capacityhigh; }; #define FSCS_NOTCLEAN 0x0001 /* fsck is neccessary before mounting */ @@ -1508,11 +1560,12 @@ struct fib_ioctl struct revision { - u32 compat; - u32 version; - u32 build; + __le32 compat; + __le32 version; + __le32 build; }; + /* * Ugly - non Linux like ioctl coding for back compat. */ @@ -1661,6 +1714,7 @@ extern struct aac_common aac_config; #define AifCmdJobProgress 2 /* Progress report */ #define AifJobCtrZero 101 /* Array Zero progress */ #define AifJobStsSuccess 1 /* Job completes */ +#define AifJobStsRunning 102 /* Job running */ #define AifCmdAPIReport 3 /* Report from other user of API */ #define AifCmdDriverNotify 4 /* Notify host driver of event */ #define AifDenMorphComplete 200 /* A morph operation completed */ @@ -1731,5 +1785,7 @@ int fib_adapter_complete(struct fib * fibptr, unsigned short size); struct aac_driver_ident* aac_get_driver_ident(int devtype); int aac_get_adapter_info(struct aac_dev* dev); int aac_send_shutdown(struct aac_dev *dev); +int probe_container(struct aac_dev *dev, int cid); extern int numacb; extern int acbsize; +extern char aac_driver_version[]; diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c index 85387099aab2..71f1cad9b5f0 100644 --- a/drivers/scsi/aacraid/commctrl.c +++ b/drivers/scsi/aacraid/commctrl.c @@ -287,7 +287,6 @@ return_fib: kfree(fib->hw_fib); kfree(fib); status = 0; - fibctx->jiffies = jiffies/HZ; } else { spin_unlock_irqrestore(&dev->fib_lock, flags); if (f.wait) { @@ -302,6 +301,7 @@ return_fib: status = -EAGAIN; } } + fibctx->jiffies = jiffies/HZ; return status; } @@ -405,10 +405,20 @@ static int close_getadapter_fib(struct aac_dev * dev, void __user *arg) static int check_revision(struct aac_dev *dev, void __user *arg) { struct revision response; - - response.compat = 1; - response.version = le32_to_cpu(dev->adapter_info.kernelrev); - response.build = le32_to_cpu(dev->adapter_info.kernelbuild); + char *driver_version = aac_driver_version; + u32 version; + + response.compat = cpu_to_le32(1); + version = (simple_strtol(driver_version, + &driver_version, 10) << 24) | 0x00000400; + version += simple_strtol(driver_version + 1, &driver_version, 10) << 16; + version += simple_strtol(driver_version + 1, NULL, 10); + response.version = cpu_to_le32(version); +# if (defined(AAC_DRIVER_BUILD)) + response.build = cpu_to_le32(AAC_DRIVER_BUILD); +# else + response.build = cpu_to_le32(9999); +# endif if (copy_to_user(arg, &response, sizeof(response))) return -EFAULT; diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 43557bf661f6..59a341b2aedc 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -44,7 +44,9 @@ #include "aacraid.h" -struct aac_common aac_config; +struct aac_common aac_config = { + .irq_mod = 1 +}; static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign) { @@ -193,7 +195,7 @@ int aac_send_shutdown(struct aac_dev * dev) fibctx, sizeof(struct aac_close), FsaNormal, - 1, 1, + -2 /* Timeout silently */, 1, NULL, NULL); if (status == 0) @@ -311,8 +313,15 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) dev->max_fib_size = sizeof(struct hw_fib); dev->sg_tablesize = host->sg_tablesize = (dev->max_fib_size - sizeof(struct aac_fibhdr) - - sizeof(struct aac_write) + sizeof(struct sgmap)) - / sizeof(struct sgmap); + - sizeof(struct aac_write) + sizeof(struct sgentry)) + / sizeof(struct sgentry); + dev->raw_io_64 = 0; + if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, + 0, 0, 0, 0, 0, 0, status+0, status+1, status+2, NULL, NULL)) && + (status[0] == 0x00000001)) { + if (status[1] & AAC_OPT_NEW_COMM_64) + dev->raw_io_64 = 1; + } if ((!aac_adapter_sync_cmd(dev, GET_COMM_PREFERRED_SETTINGS, 0, 0, 0, 0, 0, 0, status+0, status+1, status+2, status+3, status+4)) @@ -340,8 +349,8 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) dev->max_fib_size = 512; dev->sg_tablesize = host->sg_tablesize = (512 - sizeof(struct aac_fibhdr) - - sizeof(struct aac_write) + sizeof(struct sgmap)) - / sizeof(struct sgmap); + - sizeof(struct aac_write) + sizeof(struct sgentry)) + / sizeof(struct sgentry); host->can_queue = AAC_NUM_IO_FIB; } else if (acbsize == 2048) { host->max_sectors = 512; diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 5322865942e2..e4d543a474ae 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -39,7 +39,9 @@ #include <linux/completion.h> #include <linux/blkdev.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> #include <asm/semaphore.h> +#include <asm/delay.h> #include "aacraid.h" @@ -254,6 +256,7 @@ static void fib_dealloc(struct fib * fibptr) static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entry, u32 * index, unsigned long *nonotify) { struct aac_queue * q; + unsigned long idx; /* * All of the queues wrap when they reach the end, so we check @@ -263,32 +266,27 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr */ q = &dev->queues->queue[qid]; - - *index = le32_to_cpu(*(q->headers.producer)); - if ((*index - 2) == le32_to_cpu(*(q->headers.consumer))) + + idx = *index = le32_to_cpu(*(q->headers.producer)); + /* Interrupt Moderation, only interrupt for first two entries */ + if (idx != le32_to_cpu(*(q->headers.consumer))) { + if (--idx == 0) { + if (qid == AdapNormCmdQueue) + idx = ADAP_NORM_CMD_ENTRIES; + else + idx = ADAP_NORM_RESP_ENTRIES; + } + if (idx != le32_to_cpu(*(q->headers.consumer))) *nonotify = 1; + } - if (qid == AdapHighCmdQueue) { - if (*index >= ADAP_HIGH_CMD_ENTRIES) - *index = 0; - } else if (qid == AdapNormCmdQueue) { + if (qid == AdapNormCmdQueue) { if (*index >= ADAP_NORM_CMD_ENTRIES) *index = 0; /* Wrap to front of the Producer Queue. */ - } - else if (qid == AdapHighRespQueue) - { - if (*index >= ADAP_HIGH_RESP_ENTRIES) - *index = 0; - } - else if (qid == AdapNormRespQueue) - { + } else { if (*index >= ADAP_NORM_RESP_ENTRIES) *index = 0; /* Wrap to front of the Producer Queue. */ } - else { - printk("aacraid: invalid qid\n"); - BUG(); - } if ((*index + 1) == le32_to_cpu(*(q->headers.consumer))) { /* Queue is full */ printk(KERN_WARNING "Queue %d full, %u outstanding.\n", @@ -320,12 +318,8 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f { struct aac_entry * entry = NULL; int map = 0; - struct aac_queue * q = &dev->queues->queue[qid]; - - spin_lock_irqsave(q->lock, q->SavedIrql); - if (qid == AdapHighCmdQueue || qid == AdapNormCmdQueue) - { + if (qid == AdapNormCmdQueue) { /* if no entries wait for some if caller wants to */ while (!aac_get_entry(dev, qid, &entry, index, nonotify)) { @@ -336,9 +330,7 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f */ entry->size = cpu_to_le32(le16_to_cpu(hw_fib->header.Size)); map = 1; - } - else if (qid == AdapHighRespQueue || qid == AdapNormRespQueue) - { + } else { while(!aac_get_entry(dev, qid, &entry, index, nonotify)) { /* if no entries wait for some if caller wants to */ @@ -361,42 +353,6 @@ static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_f return 0; } - -/** - * aac_insert_entry - insert a queue entry - * @dev: Adapter - * @index: Index of entry to insert - * @qid: Queue number - * @nonotify: Suppress adapter notification - * - * Gets the next free QE off the requested priorty adapter command - * queue and associates the Fib with the QE. The QE represented by - * index is ready to insert on the queue when this routine returns - * success. - */ - -static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned long nonotify) -{ - struct aac_queue * q = &dev->queues->queue[qid]; - - if(q == NULL) - BUG(); - *(q->headers.producer) = cpu_to_le32(index + 1); - spin_unlock_irqrestore(q->lock, q->SavedIrql); - - if (qid == AdapHighCmdQueue || - qid == AdapNormCmdQueue || - qid == AdapHighRespQueue || - qid == AdapNormRespQueue) - { - if (!nonotify) - aac_adapter_notify(dev, qid); - } - else - printk("Suprise insert!\n"); - return 0; -} - /* * Define the highest level of host to adapter communication routines. * These routines will support host to adapter FS commuication. These @@ -425,12 +381,13 @@ static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned l int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority, int wait, int reply, fib_callback callback, void * callback_data) { u32 index; - u32 qid; struct aac_dev * dev = fibptr->dev; unsigned long nointr = 0; struct hw_fib * hw_fib = fibptr->hw_fib; struct aac_queue * q; unsigned long flags = 0; + unsigned long qflags; + if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned))) return -EBUSY; /* @@ -483,26 +440,8 @@ int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority * Get a queue entry connect the FIB to it and send an notify * the adapter a command is ready. */ - if (priority == FsaHigh) { - hw_fib->header.XferState |= cpu_to_le32(HighPriority); - qid = AdapHighCmdQueue; - } else { - hw_fib->header.XferState |= cpu_to_le32(NormalPriority); - qid = AdapNormCmdQueue; - } - q = &dev->queues->queue[qid]; + hw_fib->header.XferState |= cpu_to_le32(NormalPriority); - if(wait) - spin_lock_irqsave(&fibptr->event_lock, flags); - if(aac_queue_get( dev, &index, qid, hw_fib, 1, fibptr, &nointr)<0) - return -EWOULDBLOCK; - dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index)); - dprintk((KERN_DEBUG "Fib contents:.\n")); - dprintk((KERN_DEBUG " Command = %d.\n", hw_fib->header.Command)); - dprintk((KERN_DEBUG " XferState = %x.\n", hw_fib->header.XferState)); - dprintk((KERN_DEBUG " hw_fib va being sent=%p\n",fibptr->hw_fib)); - dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa)); - dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); /* * Fill in the Callback and CallbackContext if we are not * going to wait. @@ -511,22 +450,67 @@ int fib_send(u16 command, struct fib * fibptr, unsigned long size, int priority fibptr->callback = callback; fibptr->callback_data = callback_data; } - FIB_COUNTER_INCREMENT(aac_config.FibsSent); - list_add_tail(&fibptr->queue, &q->pendingq); - q->numpending++; fibptr->done = 0; fibptr->flags = 0; - if(aac_insert_entry(dev, index, qid, (nointr & aac_config.irq_mod)) < 0) - return -EWOULDBLOCK; + FIB_COUNTER_INCREMENT(aac_config.FibsSent); + + dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index)); + dprintk((KERN_DEBUG "Fib contents:.\n")); + dprintk((KERN_DEBUG " Command = %d.\n", hw_fib->header.Command)); + dprintk((KERN_DEBUG " XferState = %x.\n", hw_fib->header.XferState)); + dprintk((KERN_DEBUG " hw_fib va being sent=%p\n",fibptr->hw_fib)); + dprintk((KERN_DEBUG " hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa)); + dprintk((KERN_DEBUG " fib being sent=%p\n",fibptr)); + + q = &dev->queues->queue[AdapNormCmdQueue]; + + if(wait) + spin_lock_irqsave(&fibptr->event_lock, flags); + spin_lock_irqsave(q->lock, qflags); + aac_queue_get( dev, &index, AdapNormCmdQueue, hw_fib, 1, fibptr, &nointr); + + list_add_tail(&fibptr->queue, &q->pendingq); + q->numpending++; + *(q->headers.producer) = cpu_to_le32(index + 1); + spin_unlock_irqrestore(q->lock, qflags); + if (!(nointr & aac_config.irq_mod)) + aac_adapter_notify(dev, AdapNormCmdQueue); /* * If the caller wanted us to wait for response wait now. */ if (wait) { spin_unlock_irqrestore(&fibptr->event_lock, flags); - down(&fibptr->event_wait); + /* Only set for first known interruptable command */ + if (wait < 0) { + /* + * *VERY* Dangerous to time out a command, the + * assumption is made that we have no hope of + * functioning because an interrupt routing or other + * hardware failure has occurred. + */ + unsigned long count = 36000000L; /* 3 minutes */ + unsigned long qflags; + while (down_trylock(&fibptr->event_wait)) { + if (--count == 0) { + spin_lock_irqsave(q->lock, qflags); + q->numpending--; + list_del(&fibptr->queue); + spin_unlock_irqrestore(q->lock, qflags); + if (wait == -1) { + printk(KERN_ERR "aacraid: fib_send: first asynchronous command timed out.\n" + "Usually a result of a PCI interrupt routing problem;\n" + "update mother board BIOS or consider utilizing one of\n" + "the SAFE mode kernel options (acpi, apic etc)\n"); + } + return -ETIMEDOUT; + } + udelay(5); + } + } else + down(&fibptr->event_wait); if(fibptr->done == 0) BUG(); @@ -608,15 +592,9 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid) case HostNormCmdQueue: notify = HostNormCmdNotFull; break; - case HostHighCmdQueue: - notify = HostHighCmdNotFull; - break; case HostNormRespQueue: notify = HostNormRespNotFull; break; - case HostHighRespQueue: - notify = HostHighRespNotFull; - break; default: BUG(); return; @@ -638,9 +616,13 @@ int fib_adapter_complete(struct fib * fibptr, unsigned short size) { struct hw_fib * hw_fib = fibptr->hw_fib; struct aac_dev * dev = fibptr->dev; + struct aac_queue * q; unsigned long nointr = 0; - if (hw_fib->header.XferState == 0) + unsigned long qflags; + + if (hw_fib->header.XferState == 0) { return 0; + } /* * If we plan to do anything check the structure type first. */ @@ -655,37 +637,21 @@ int fib_adapter_complete(struct fib * fibptr, unsigned short size) * send the completed cdb to the adapter. */ if (hw_fib->header.XferState & cpu_to_le32(SentFromAdapter)) { + u32 index; hw_fib->header.XferState |= cpu_to_le32(HostProcessed); - if (hw_fib->header.XferState & cpu_to_le32(HighPriority)) { - u32 index; - if (size) - { - size += sizeof(struct aac_fibhdr); - if (size > le16_to_cpu(hw_fib->header.SenderSize)) - return -EMSGSIZE; - hw_fib->header.Size = cpu_to_le16(size); - } - if(aac_queue_get(dev, &index, AdapHighRespQueue, hw_fib, 1, NULL, &nointr) < 0) { - return -EWOULDBLOCK; - } - if (aac_insert_entry(dev, index, AdapHighRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) { - } - } else if (hw_fib->header.XferState & - cpu_to_le32(NormalPriority)) { - u32 index; - - if (size) { - size += sizeof(struct aac_fibhdr); - if (size > le16_to_cpu(hw_fib->header.SenderSize)) - return -EMSGSIZE; - hw_fib->header.Size = cpu_to_le16(size); - } - if (aac_queue_get(dev, &index, AdapNormRespQueue, hw_fib, 1, NULL, &nointr) < 0) - return -EWOULDBLOCK; - if (aac_insert_entry(dev, index, AdapNormRespQueue, (nointr & (int)aac_config.irq_mod)) != 0) - { - } + if (size) { + size += sizeof(struct aac_fibhdr); + if (size > le16_to_cpu(hw_fib->header.SenderSize)) + return -EMSGSIZE; + hw_fib->header.Size = cpu_to_le16(size); } + q = &dev->queues->queue[AdapNormRespQueue]; + spin_lock_irqsave(q->lock, qflags); + aac_queue_get(dev, &index, AdapNormRespQueue, hw_fib, 1, NULL, &nointr); + *(q->headers.producer) = cpu_to_le32(index + 1); + spin_unlock_irqrestore(q->lock, qflags); + if (!(nointr & (int)aac_config.irq_mod)) + aac_adapter_notify(dev, AdapNormRespQueue); } else { @@ -777,6 +743,268 @@ void aac_printf(struct aac_dev *dev, u32 val) memset(cp, 0, 256); } + +/** + * aac_handle_aif - Handle a message from the firmware + * @dev: Which adapter this fib is from + * @fibptr: Pointer to fibptr from adapter + * + * This routine handles a driver notify fib from the adapter and + * dispatches it to the appropriate routine for handling. + */ + +static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr) +{ + struct hw_fib * hw_fib = fibptr->hw_fib; + struct aac_aifcmd * aifcmd = (struct aac_aifcmd *)hw_fib->data; + int busy; + u32 container; + struct scsi_device *device; + enum { + NOTHING, + DELETE, + ADD, + CHANGE + } device_config_needed; + + /* Sniff for container changes */ + + if (!dev) + return; + container = (u32)-1; + + /* + * We have set this up to try and minimize the number of + * re-configures that take place. As a result of this when + * certain AIF's come in we will set a flag waiting for another + * type of AIF before setting the re-config flag. + */ + switch (le32_to_cpu(aifcmd->command)) { + case AifCmdDriverNotify: + switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) { + /* + * Morph or Expand complete + */ + case AifDenMorphComplete: + case AifDenVolumeExtendComplete: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + + /* + * Find the Scsi_Device associated with the SCSI + * address. Make sure we have the right array, and if + * so set the flag to initiate a new re-config once we + * see an AifEnConfigChange AIF come through. + */ + + if ((dev != NULL) && (dev->scsi_host_ptr != NULL)) { + device = scsi_device_lookup(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + if (device) { + dev->fsa_dev[container].config_needed = CHANGE; + dev->fsa_dev[container].config_waiting_on = AifEnConfigChange; + scsi_device_put(device); + } + } + } + + /* + * If we are waiting on something and this happens to be + * that thing then set the re-configure flag. + */ + if (container != (u32)-1) { + if (container >= dev->maximum_num_containers) + break; + if (dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) + dev->fsa_dev[container].config_waiting_on = 0; + } else for (container = 0; + container < dev->maximum_num_containers; ++container) { + if (dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) + dev->fsa_dev[container].config_waiting_on = 0; + } + break; + + case AifCmdEventNotify: + switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) { + /* + * Add an Array. + */ + case AifEnAddContainer: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + dev->fsa_dev[container].config_needed = ADD; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + break; + + /* + * Delete an Array. + */ + case AifEnDeleteContainer: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + dev->fsa_dev[container].config_needed = DELETE; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + break; + + /* + * Container change detected. If we currently are not + * waiting on something else, setup to wait on a Config Change. + */ + case AifEnContainerChange: + container = le32_to_cpu(((u32 *)aifcmd->data)[1]); + if (container >= dev->maximum_num_containers) + break; + if (dev->fsa_dev[container].config_waiting_on) + break; + dev->fsa_dev[container].config_needed = CHANGE; + dev->fsa_dev[container].config_waiting_on = + AifEnConfigChange; + break; + + case AifEnConfigChange: + break; + + } + + /* + * If we are waiting on something and this happens to be + * that thing then set the re-configure flag. + */ + if (container != (u32)-1) { + if (container >= dev->maximum_num_containers) + break; + if (dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) + dev->fsa_dev[container].config_waiting_on = 0; + } else for (container = 0; + container < dev->maximum_num_containers; ++container) { + if (dev->fsa_dev[container].config_waiting_on == + le32_to_cpu(*(u32 *)aifcmd->data)) + dev->fsa_dev[container].config_waiting_on = 0; + } + break; + + case AifCmdJobProgress: + /* + * These are job progress AIF's. When a Clear is being + * done on a container it is initially created then hidden from + * the OS. When the clear completes we don't get a config + * change so we monitor the job status complete on a clear then + * wait for a container change. + */ + + if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero)) + && ((((u32 *)aifcmd->data)[6] == ((u32 *)aifcmd->data)[5]) + || (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsSuccess)))) { + for (container = 0; + container < dev->maximum_num_containers; + ++container) { + /* + * Stomp on all config sequencing for all + * containers? + */ + dev->fsa_dev[container].config_waiting_on = + AifEnContainerChange; + dev->fsa_dev[container].config_needed = ADD; + } + } + if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero)) + && (((u32 *)aifcmd->data)[6] == 0) + && (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsRunning))) { + for (container = 0; + container < dev->maximum_num_containers; + ++container) { + /* + * Stomp on all config sequencing for all + * containers? + */ + dev->fsa_dev[container].config_waiting_on = + AifEnContainerChange; + dev->fsa_dev[container].config_needed = DELETE; + } + } + break; + } + + device_config_needed = NOTHING; + for (container = 0; container < dev->maximum_num_containers; + ++container) { + if ((dev->fsa_dev[container].config_waiting_on == 0) + && (dev->fsa_dev[container].config_needed != NOTHING)) { + device_config_needed = + dev->fsa_dev[container].config_needed; + dev->fsa_dev[container].config_needed = NOTHING; + break; + } + } + if (device_config_needed == NOTHING) + return; + + /* + * If we decided that a re-configuration needs to be done, + * schedule it here on the way out the door, please close the door + * behind you. + */ + + busy = 0; + + + /* + * Find the Scsi_Device associated with the SCSI address, + * and mark it as changed, invalidating the cache. This deals + * with changes to existing device IDs. + */ + + if (!dev || !dev->scsi_host_ptr) + return; + /* + * force reload of disk info via probe_container + */ + if ((device_config_needed == CHANGE) + && (dev->fsa_dev[container].valid == 1)) + dev->fsa_dev[container].valid = 2; + if ((device_config_needed == CHANGE) || + (device_config_needed == ADD)) + probe_container(dev, container); + device = scsi_device_lookup(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + if (device) { + switch (device_config_needed) { + case DELETE: + scsi_remove_device(device); + break; + case CHANGE: + if (!dev->fsa_dev[container].valid) { + scsi_remove_device(device); + break; + } + scsi_rescan_device(&device->sdev_gendev); + + default: + break; + } + scsi_device_put(device); + } + if (device_config_needed == ADD) { + scsi_add_device(dev->scsi_host_ptr, + CONTAINER_TO_CHANNEL(container), + CONTAINER_TO_ID(container), + CONTAINER_TO_LUN(container)); + } + +} + /** * aac_command_thread - command processing thread * @dev: Adapter to monitor @@ -791,7 +1019,6 @@ int aac_command_thread(struct aac_dev * dev) { struct hw_fib *hw_fib, *hw_newfib; struct fib *fib, *newfib; - struct aac_queue_block *queues = dev->queues; struct aac_fib_context *fibctx; unsigned long flags; DECLARE_WAITQUEUE(wait, current); @@ -811,21 +1038,22 @@ int aac_command_thread(struct aac_dev * dev) * Let the DPC know it has a place to send the AIF's to. */ dev->aif_thread = 1; - add_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait); + add_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait); set_current_state(TASK_INTERRUPTIBLE); + dprintk ((KERN_INFO "aac_command_thread start\n")); while(1) { - spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags); - while(!list_empty(&(queues->queue[HostNormCmdQueue].cmdq))) { + spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags); + while(!list_empty(&(dev->queues->queue[HostNormCmdQueue].cmdq))) { struct list_head *entry; struct aac_aifcmd * aifcmd; set_current_state(TASK_RUNNING); - - entry = queues->queue[HostNormCmdQueue].cmdq.next; + + entry = dev->queues->queue[HostNormCmdQueue].cmdq.next; list_del(entry); - - spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags); + + spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags); fib = list_entry(entry, struct fib, fiblink); /* * We will process the FIB here or pass it to a @@ -846,6 +1074,7 @@ int aac_command_thread(struct aac_dev * dev) aifcmd = (struct aac_aifcmd *) hw_fib->data; if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) { /* Handle Driver Notify Events */ + aac_handle_aif(dev, fib); *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK); fib_adapter_complete(fib, (u16)sizeof(u32)); } else { @@ -855,9 +1084,62 @@ int aac_command_thread(struct aac_dev * dev) u32 time_now, time_last; unsigned long flagv; - + unsigned num; + struct hw_fib ** hw_fib_pool, ** hw_fib_p; + struct fib ** fib_pool, ** fib_p; + + /* Sniff events */ + if ((aifcmd->command == + cpu_to_le32(AifCmdEventNotify)) || + (aifcmd->command == + cpu_to_le32(AifCmdJobProgress))) { + aac_handle_aif(dev, fib); + } + time_now = jiffies/HZ; + /* + * Warning: no sleep allowed while + * holding spinlock. We take the estimate + * and pre-allocate a set of fibs outside the + * lock. + */ + num = le32_to_cpu(dev->init->AdapterFibsSize) + / sizeof(struct hw_fib); /* some extra */ + spin_lock_irqsave(&dev->fib_lock, flagv); + entry = dev->fib_list.next; + while (entry != &dev->fib_list) { + entry = entry->next; + ++num; + } + spin_unlock_irqrestore(&dev->fib_lock, flagv); + hw_fib_pool = NULL; + fib_pool = NULL; + if (num + && ((hw_fib_pool = kmalloc(sizeof(struct hw_fib *) * num, GFP_KERNEL))) + && ((fib_pool = kmalloc(sizeof(struct fib *) * num, GFP_KERNEL)))) { + hw_fib_p = hw_fib_pool; + fib_p = fib_pool; + while (hw_fib_p < &hw_fib_pool[num]) { + if (!(*(hw_fib_p++) = kmalloc(sizeof(struct hw_fib), GFP_KERNEL))) { + --hw_fib_p; + break; + } + if (!(*(fib_p++) = kmalloc(sizeof(struct fib), GFP_KERNEL))) { + kfree(*(--hw_fib_p)); + break; + } + } + if ((num = hw_fib_p - hw_fib_pool) == 0) { + kfree(fib_pool); + fib_pool = NULL; + kfree(hw_fib_pool); + hw_fib_pool = NULL; + } + } else if (hw_fib_pool) { + kfree(hw_fib_pool); + hw_fib_pool = NULL; + } spin_lock_irqsave(&dev->fib_lock, flagv); entry = dev->fib_list.next; /* @@ -866,6 +1148,8 @@ int aac_command_thread(struct aac_dev * dev) * fib, and then set the event to wake up the * thread that is waiting for it. */ + hw_fib_p = hw_fib_pool; + fib_p = fib_pool; while (entry != &dev->fib_list) { /* * Extract the fibctx @@ -898,9 +1182,11 @@ int aac_command_thread(struct aac_dev * dev) * Warning: no sleep allowed while * holding spinlock */ - hw_newfib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC); - newfib = kmalloc(sizeof(struct fib), GFP_ATOMIC); - if (newfib && hw_newfib) { + if (hw_fib_p < &hw_fib_pool[num]) { + hw_newfib = *hw_fib_p; + *(hw_fib_p++) = NULL; + newfib = *fib_p; + *(fib_p++) = NULL; /* * Make the copy of the FIB */ @@ -915,15 +1201,11 @@ int aac_command_thread(struct aac_dev * dev) fibctx->count++; /* * Set the event to wake up the - * thread that will waiting. + * thread that is waiting. */ up(&fibctx->wait_sem); } else { printk(KERN_WARNING "aifd: didn't allocate NewFib.\n"); - if(newfib) - kfree(newfib); - if(hw_newfib) - kfree(hw_newfib); } entry = entry->next; } @@ -933,21 +1215,38 @@ int aac_command_thread(struct aac_dev * dev) *(__le32 *)hw_fib->data = cpu_to_le32(ST_OK); fib_adapter_complete(fib, sizeof(u32)); spin_unlock_irqrestore(&dev->fib_lock, flagv); + /* Free up the remaining resources */ + hw_fib_p = hw_fib_pool; + fib_p = fib_pool; + while (hw_fib_p < &hw_fib_pool[num]) { + if (*hw_fib_p) + kfree(*hw_fib_p); + if (*fib_p) + kfree(*fib_p); + ++fib_p; + ++hw_fib_p; + } + if (hw_fib_pool) + kfree(hw_fib_pool); + if (fib_pool) + kfree(fib_pool); } - spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags); kfree(fib); + spin_lock_irqsave(dev->queues->queue[HostNormCmdQueue].lock, flags); } /* * There are no more AIF's */ - spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags); + spin_unlock_irqrestore(dev->queues->queue[HostNormCmdQueue].lock, flags); schedule(); if(signal_pending(current)) break; set_current_state(TASK_INTERRUPTIBLE); } - remove_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait); + if (dev->queues) + remove_wait_queue(&dev->queues->queue[HostNormCmdQueue].cmdready, &wait); dev->aif_thread = 0; complete_and_exit(&dev->aif_completion, 0); + return 0; } diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c index 562da90480a1..a1f9ceef0ac9 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -27,8 +27,11 @@ * Abstract: Linux Driver entry module for Adaptec RAID Array Controller */ -#define AAC_DRIVER_VERSION "1.1.2-lk2" -#define AAC_DRIVER_BUILD_DATE __DATE__ +#define AAC_DRIVER_VERSION "1.1-4" +#ifndef AAC_DRIVER_BRANCH +#define AAC_DRIVER_BRANCH "" +#endif +#define AAC_DRIVER_BUILD_DATE __DATE__ " " __TIME__ #define AAC_DRIVERNAME "aacraid" #include <linux/compat.h> @@ -58,16 +61,24 @@ #include "aacraid.h" +#ifdef AAC_DRIVER_BUILD +#define _str(x) #x +#define str(x) _str(x) +#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION "[" str(AAC_DRIVER_BUILD) "]" AAC_DRIVER_BRANCH +#else +#define AAC_DRIVER_FULL_VERSION AAC_DRIVER_VERSION AAC_DRIVER_BRANCH " " AAC_DRIVER_BUILD_DATE +#endif MODULE_AUTHOR("Red Hat Inc and Adaptec"); MODULE_DESCRIPTION("Dell PERC2, 2/Si, 3/Si, 3/Di, " "Adaptec Advanced Raid Products, " "and HP NetRAID-4M SCSI driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION(AAC_DRIVER_VERSION); +MODULE_VERSION(AAC_DRIVER_FULL_VERSION); static LIST_HEAD(aac_devices); static int aac_cfg_major = -1; +char aac_driver_version[] = AAC_DRIVER_FULL_VERSION; /* * Because of the way Linux names scsi devices, the order in this table has @@ -109,36 +120,39 @@ static struct pci_device_id aac_pci_tbl[] = { { 0x9005, 0x0286, 0x9005, 0x02a3, 0, 0, 29 }, /* ICP5085AU (Hurricane) */ { 0x9005, 0x0285, 0x9005, 0x02a4, 0, 0, 30 }, /* ICP9085LI (Marauder-X) */ { 0x9005, 0x0285, 0x9005, 0x02a5, 0, 0, 31 }, /* ICP5085BR (Marauder-E) */ - { 0x9005, 0x0287, 0x9005, 0x0800, 0, 0, 32 }, /* Themisto Jupiter Platform */ - { 0x9005, 0x0200, 0x9005, 0x0200, 0, 0, 32 }, /* Themisto Jupiter Platform */ - { 0x9005, 0x0286, 0x9005, 0x0800, 0, 0, 33 }, /* Callisto Jupiter Platform */ - { 0x9005, 0x0285, 0x9005, 0x028e, 0, 0, 34 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */ - { 0x9005, 0x0285, 0x9005, 0x028f, 0, 0, 35 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */ - { 0x9005, 0x0285, 0x9005, 0x0290, 0, 0, 36 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */ - { 0x9005, 0x0285, 0x1028, 0x0291, 0, 0, 37 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */ - { 0x9005, 0x0285, 0x9005, 0x0292, 0, 0, 38 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */ - { 0x9005, 0x0285, 0x9005, 0x0293, 0, 0, 39 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */ - { 0x9005, 0x0285, 0x9005, 0x0294, 0, 0, 40 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */ - { 0x9005, 0x0285, 0x103C, 0x3227, 0, 0, 41 }, /* AAR-2610SA PCI SATA 6ch */ - { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 42 }, /* ASR-2240S (SabreExpress) */ - { 0x9005, 0x0285, 0x9005, 0x0297, 0, 0, 43 }, /* ASR-4005SAS */ - { 0x9005, 0x0285, 0x1014, 0x02F2, 0, 0, 44 }, /* IBM 8i (AvonPark) */ - { 0x9005, 0x0285, 0x1014, 0x0312, 0, 0, 44 }, /* IBM 8i (AvonPark Lite) */ - { 0x9005, 0x0285, 0x9005, 0x0298, 0, 0, 45 }, /* ASR-4000SAS (BlackBird) */ - { 0x9005, 0x0285, 0x9005, 0x0299, 0, 0, 46 }, /* ASR-4800SAS (Marauder-X) */ - { 0x9005, 0x0285, 0x9005, 0x029a, 0, 0, 47 }, /* ASR-4805SAS (Marauder-E) */ - { 0x9005, 0x0286, 0x9005, 0x02a2, 0, 0, 48 }, /* ASR-4810SAS (Hurricane */ - - { 0x9005, 0x0285, 0x1028, 0x0287, 0, 0, 49 }, /* Perc 320/DC*/ - { 0x1011, 0x0046, 0x9005, 0x0365, 0, 0, 50 }, /* Adaptec 5400S (Mustang)*/ - { 0x1011, 0x0046, 0x9005, 0x0364, 0, 0, 51 }, /* Adaptec 5400S (Mustang)*/ - { 0x1011, 0x0046, 0x9005, 0x1364, 0, 0, 52 }, /* Dell PERC2/QC */ - { 0x1011, 0x0046, 0x103c, 0x10c2, 0, 0, 53 }, /* HP NetRAID-4M */ - - { 0x9005, 0x0285, 0x1028, PCI_ANY_ID, 0, 0, 54 }, /* Dell Catchall */ - { 0x9005, 0x0285, 0x17aa, PCI_ANY_ID, 0, 0, 55 }, /* Legend Catchall */ - { 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 56 }, /* Adaptec Catch All */ - { 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 57 }, /* Adaptec Rocket Catch All */ + { 0x9005, 0x0286, 0x9005, 0x02a6, 0, 0, 32 }, /* ICP9067MA (Intruder-6) */ + { 0x9005, 0x0287, 0x9005, 0x0800, 0, 0, 33 }, /* Themisto Jupiter Platform */ + { 0x9005, 0x0200, 0x9005, 0x0200, 0, 0, 33 }, /* Themisto Jupiter Platform */ + { 0x9005, 0x0286, 0x9005, 0x0800, 0, 0, 34 }, /* Callisto Jupiter Platform */ + { 0x9005, 0x0285, 0x9005, 0x028e, 0, 0, 35 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */ + { 0x9005, 0x0285, 0x9005, 0x028f, 0, 0, 36 }, /* ASR-2025SA SATA SO-DIMM PCI-X ZCR (Terminator) */ + { 0x9005, 0x0285, 0x9005, 0x0290, 0, 0, 37 }, /* AAR-2410SA PCI SATA 4ch (Jaguar II) */ + { 0x9005, 0x0285, 0x1028, 0x0291, 0, 0, 38 }, /* CERC SATA RAID 2 PCI SATA 6ch (DellCorsair) */ + { 0x9005, 0x0285, 0x9005, 0x0292, 0, 0, 39 }, /* AAR-2810SA PCI SATA 8ch (Corsair-8) */ + { 0x9005, 0x0285, 0x9005, 0x0293, 0, 0, 40 }, /* AAR-21610SA PCI SATA 16ch (Corsair-16) */ + { 0x9005, 0x0285, 0x9005, 0x0294, 0, 0, 41 }, /* ESD SO-DIMM PCI-X SATA ZCR (Prowler) */ + { 0x9005, 0x0285, 0x103C, 0x3227, 0, 0, 42 }, /* AAR-2610SA PCI SATA 6ch */ + { 0x9005, 0x0285, 0x9005, 0x0296, 0, 0, 43 }, /* ASR-2240S (SabreExpress) */ + { 0x9005, 0x0285, 0x9005, 0x0297, 0, 0, 44 }, /* ASR-4005SAS */ + { 0x9005, 0x0285, 0x1014, 0x02F2, 0, 0, 45 }, /* IBM 8i (AvonPark) */ + { 0x9005, 0x0285, 0x1014, 0x0312, 0, 0, 45 }, /* IBM 8i (AvonPark Lite) */ + { 0x9005, 0x0286, 0x1014, 0x9580, 0, 0, 46 }, /* IBM 8k/8k-l8 (Aurora) */ + { 0x9005, 0x0286, 0x1014, 0x9540, 0, 0, 47 }, /* IBM 8k/8k-l4 (Aurora Lite) */ + { 0x9005, 0x0285, 0x9005, 0x0298, 0, 0, 48 }, /* ASR-4000SAS (BlackBird) */ + { 0x9005, 0x0285, 0x9005, 0x0299, 0, 0, 49 }, /* ASR-4800SAS (Marauder-X) */ + { 0x9005, 0x0285, 0x9005, 0x029a, 0, 0, 50 }, /* ASR-4805SAS (Marauder-E) */ + { 0x9005, 0x0286, 0x9005, 0x02a2, 0, 0, 51 }, /* ASR-4810SAS (Hurricane */ + + { 0x9005, 0x0285, 0x1028, 0x0287, 0, 0, 52 }, /* Perc 320/DC*/ + { 0x1011, 0x0046, 0x9005, 0x0365, 0, 0, 53 }, /* Adaptec 5400S (Mustang)*/ + { 0x1011, 0x0046, 0x9005, 0x0364, 0, 0, 54 }, /* Adaptec 5400S (Mustang)*/ + { 0x1011, 0x0046, 0x9005, 0x1364, 0, 0, 55 }, /* Dell PERC2/QC */ + { 0x1011, 0x0046, 0x103c, 0x10c2, 0, 0, 56 }, /* HP NetRAID-4M */ + + { 0x9005, 0x0285, 0x1028, PCI_ANY_ID, 0, 0, 57 }, /* Dell Catchall */ + { 0x9005, 0x0285, 0x17aa, PCI_ANY_ID, 0, 0, 58 }, /* Legend Catchall */ + { 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 59 }, /* Adaptec Catch All */ + { 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 60 }, /* Adaptec Rocket Catch All */ { 0,} }; MODULE_DEVICE_TABLE(pci, aac_pci_tbl); @@ -180,8 +194,9 @@ static struct aac_driver_ident aac_drivers[] = { { aac_rkt_init, "aacraid", "ICP ", "ICP9047MA ", 1 }, /* ICP9047MA (Lancer) */ { aac_rkt_init, "aacraid", "ICP ", "ICP9087MA ", 1 }, /* ICP9087MA (Lancer) */ { aac_rkt_init, "aacraid", "ICP ", "ICP5085AU ", 1 }, /* ICP5085AU (Hurricane) */ - { aac_rkt_init, "aacraid", "ICP ", "ICP9085LI ", 1 }, /* ICP9085LI (Marauder-X) */ - { aac_rkt_init, "aacraid", "ICP ", "ICP5085BR ", 1 }, /* ICP5085BR (Marauder-E) */ + { aac_rx_init, "aacraid", "ICP ", "ICP9085LI ", 1 }, /* ICP9085LI (Marauder-X) */ + { aac_rx_init, "aacraid", "ICP ", "ICP5085BR ", 1 }, /* ICP5085BR (Marauder-E) */ + { aac_rkt_init, "aacraid", "ICP ", "ICP9067MA ", 1 }, /* ICP9067MA (Intruder-6) */ { NULL , "aacraid", "ADAPTEC ", "Themisto ", 0, AAC_QUIRK_SLAVE }, /* Jupiter Platform */ { aac_rkt_init, "aacraid", "ADAPTEC ", "Callisto ", 2, AAC_QUIRK_MASTER }, /* Jupiter Platform */ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2020SA ", 1 }, /* ASR-2020SA SATA PCI-X ZCR (Skyhawk) */ @@ -195,10 +210,12 @@ static struct aac_driver_ident aac_drivers[] = { { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-2240S ", 1 }, /* ASR-2240S (SabreExpress) */ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4005SAS ", 1 }, /* ASR-4005SAS */ { aac_rx_init, "ServeRAID","IBM ", "ServeRAID 8i ", 1 }, /* IBM 8i (AvonPark) */ + { aac_rkt_init, "ServeRAID","IBM ", "ServeRAID 8k-l8 ", 1 }, /* IBM 8k/8k-l8 (Aurora) */ + { aac_rkt_init, "ServeRAID","IBM ", "ServeRAID 8k-l4 ", 1 }, /* IBM 8k/8k-l4 (Aurora Lite) */ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4000SAS ", 1 }, /* ASR-4000SAS (BlackBird & AvonPark) */ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4800SAS ", 1 }, /* ASR-4800SAS (Marauder-X) */ { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4805SAS ", 1 }, /* ASR-4805SAS (Marauder-E) */ - { aac_rx_init, "aacraid", "ADAPTEC ", "ASR-4810SAS ", 1 }, /* ASR-4810SAS (Hurricane) */ + { aac_rkt_init, "aacraid", "ADAPTEC ", "ASR-4810SAS ", 1 }, /* ASR-4810SAS (Hurricane) */ { aac_rx_init, "percraid", "DELL ", "PERC 320/DC ", 2, AAC_QUIRK_31BIT | AAC_QUIRK_34SG }, /* Perc 320/DC*/ { aac_sa_init, "aacraid", "ADAPTEC ", "Adaptec 5400S ", 4, AAC_QUIRK_34SG }, /* Adaptec 5400S (Mustang)*/ @@ -436,9 +453,9 @@ static int aac_eh_reset(struct scsi_cmnd* cmd) /* * We can exit If all the commands are complete */ + spin_unlock_irq(host->host_lock); if (active == 0) return SUCCESS; - spin_unlock_irq(host->host_lock); ssleep(1); spin_lock_irq(host->host_lock); } @@ -731,7 +748,8 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, unique_id++; } - if (pci_enable_device(pdev)) + error = pci_enable_device(pdev); + if (error) goto out; if (pci_set_dma_mask(pdev, 0xFFFFFFFFULL) || @@ -755,6 +773,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, shost->irq = pdev->irq; shost->base = pci_resource_start(pdev, 0); shost->unique_id = unique_id; + shost->max_cmd_len = 16; aac = (struct aac_dev *)shost->hostdata; aac->scsi_host_ptr = shost; @@ -782,7 +801,9 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, goto out_free_fibs; aac->maximum_num_channels = aac_drivers[index].channels; - aac_get_adapter_info(aac); + error = aac_get_adapter_info(aac); + if (error < 0) + goto out_deinit; /* * Lets override negotiations and drop the maximum SG limit to 34 @@ -839,11 +860,12 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, return 0; -out_deinit: + out_deinit: kill_proc(aac->thread_pid, SIGKILL, 0); wait_for_completion(&aac->aif_completion); aac_send_shutdown(aac); + aac_adapter_disable_int(aac); fib_map_free(aac); pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); kfree(aac->queues); @@ -860,6 +882,13 @@ out_deinit: return error; } +static void aac_shutdown(struct pci_dev *dev) +{ + struct Scsi_Host *shost = pci_get_drvdata(dev); + struct aac_dev *aac = (struct aac_dev *)shost->hostdata; + aac_send_shutdown(aac); +} + static void __devexit aac_remove_one(struct pci_dev *pdev) { struct Scsi_Host *shost = pci_get_drvdata(pdev); @@ -871,6 +900,7 @@ static void __devexit aac_remove_one(struct pci_dev *pdev) wait_for_completion(&aac->aif_completion); aac_send_shutdown(aac); + aac_adapter_disable_int(aac); fib_map_free(aac); pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys); @@ -891,17 +921,18 @@ static struct pci_driver aac_pci_driver = { .id_table = aac_pci_tbl, .probe = aac_probe_one, .remove = __devexit_p(aac_remove_one), + .shutdown = aac_shutdown, }; static int __init aac_init(void) { int error; - printk(KERN_INFO "Red Hat/Adaptec aacraid driver (%s %s)\n", - AAC_DRIVER_VERSION, AAC_DRIVER_BUILD_DATE); + printk(KERN_INFO "Adaptec %s driver (%s)\n", + AAC_DRIVERNAME, aac_driver_version); - error = pci_module_init(&aac_pci_driver); - if (error) + error = pci_register_driver(&aac_pci_driver); + if (error < 0) return error; aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops); @@ -909,6 +940,7 @@ static int __init aac_init(void) printk(KERN_WARNING "aacraid: unable to register \"aac\" device.\n"); } + return 0; } diff --git a/drivers/scsi/aacraid/rkt.c b/drivers/scsi/aacraid/rkt.c index 7d68b7825137..557287a0b80b 100644 --- a/drivers/scsi/aacraid/rkt.c +++ b/drivers/scsi/aacraid/rkt.c @@ -88,6 +88,16 @@ static irqreturn_t aac_rkt_intr(int irq, void *dev_id, struct pt_regs *regs) } /** + * aac_rkt_disable_interrupt - Disable interrupts + * @dev: Adapter + */ + +static void aac_rkt_disable_interrupt(struct aac_dev *dev) +{ + rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); +} + +/** * rkt_sync_cmd - send a command and wait * @dev: Adapter * @command: Command to execute @@ -412,10 +422,19 @@ int aac_rkt_init(struct aac_dev *dev) * Fill in the function dispatch table. */ dev->a_ops.adapter_interrupt = aac_rkt_interrupt_adapter; + dev->a_ops.adapter_disable_int = aac_rkt_disable_interrupt; dev->a_ops.adapter_notify = aac_rkt_notify_adapter; dev->a_ops.adapter_sync_cmd = rkt_sync_cmd; dev->a_ops.adapter_check_health = aac_rkt_check_health; + /* + * First clear out all interrupts. Then enable the one's that we + * can handle. + */ + rkt_writeb(dev, MUnit.OIMR, 0xff); + rkt_writel(dev, MUnit.ODR, 0xffffffff); + rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); + if (aac_init_adapter(dev) == NULL) goto error_irq; /* @@ -438,6 +457,7 @@ error_kfree: kfree(dev->queues); error_irq: + rkt_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); free_irq(dev->scsi_host_ptr->irq, (void *)dev); error_iounmap: diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c index 1ff25f49fada..a8459faf87ca 100644 --- a/drivers/scsi/aacraid/rx.c +++ b/drivers/scsi/aacraid/rx.c @@ -88,6 +88,16 @@ static irqreturn_t aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs) } /** + * aac_rx_disable_interrupt - Disable interrupts + * @dev: Adapter + */ + +static void aac_rx_disable_interrupt(struct aac_dev *dev) +{ + rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); +} + +/** * rx_sync_cmd - send a command and wait * @dev: Adapter * @command: Command to execute @@ -412,10 +422,19 @@ int aac_rx_init(struct aac_dev *dev) * Fill in the function dispatch table. */ dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter; + dev->a_ops.adapter_disable_int = aac_rx_disable_interrupt; dev->a_ops.adapter_notify = aac_rx_notify_adapter; dev->a_ops.adapter_sync_cmd = rx_sync_cmd; dev->a_ops.adapter_check_health = aac_rx_check_health; + /* + * First clear out all interrupts. Then enable the one's that we + * can handle. + */ + rx_writeb(dev, MUnit.OIMR, 0xff); + rx_writel(dev, MUnit.ODR, 0xffffffff); + rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xfb); + if (aac_init_adapter(dev) == NULL) goto error_irq; /* @@ -438,6 +457,7 @@ error_kfree: kfree(dev->queues); error_irq: + rx_writeb(dev, MUnit.OIMR, dev->OIMR = 0xff); free_irq(dev->scsi_host_ptr->irq, (void *)dev); error_iounmap: diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c index 0680249ab861..3900abc5850d 100644 --- a/drivers/scsi/aacraid/sa.c +++ b/drivers/scsi/aacraid/sa.c @@ -82,6 +82,16 @@ static irqreturn_t aac_sa_intr(int irq, void *dev_id, struct pt_regs *regs) } /** + * aac_sa_disable_interrupt - disable interrupt + * @dev: Which adapter to enable. + */ + +static void aac_sa_disable_interrupt (struct aac_dev *dev) +{ + sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff); +} + +/** * aac_sa_notify_adapter - handle adapter notification * @dev: Adapter that notification is for * @event: Event to notidy @@ -214,9 +224,8 @@ static int sa_sync_cmd(struct aac_dev *dev, u32 command, static void aac_sa_interrupt_adapter (struct aac_dev *dev) { - u32 ret; sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, 0, 0, 0, 0, 0, - &ret, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); } /** @@ -352,10 +361,18 @@ int aac_sa_init(struct aac_dev *dev) */ dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter; + dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt; dev->a_ops.adapter_notify = aac_sa_notify_adapter; dev->a_ops.adapter_sync_cmd = sa_sync_cmd; dev->a_ops.adapter_check_health = aac_sa_check_health; + /* + * First clear out all interrupts. Then enable the one's that + * we can handle. + */ + sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff); + sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | + DOORBELL_2 | DOORBELL_3 | DOORBELL_4)); if(aac_init_adapter(dev) == NULL) goto error_irq; @@ -381,6 +398,7 @@ error_kfree: kfree(dev->queues); error_irq: + sa_writew(dev, SaDbCSR.PRISETIRQMASK, 0xffff); free_irq(dev->scsi_host_ptr->irq, (void *)dev); error_iounmap: diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c index 0fb93363eb22..37ec5411e325 100644 --- a/drivers/scsi/advansys.c +++ b/drivers/scsi/advansys.c @@ -9200,8 +9200,8 @@ asc_prt_scsi_cmnd(struct scsi_cmnd *s) (unsigned) s->serial_number, s->retries, s->allowed); printk( -" timeout_per_command %d, timeout_total %d, timeout %d\n", - s->timeout_per_command, s->timeout_total, s->timeout); +" timeout_per_command %d\n", + s->timeout_per_command); printk( " scsi_done 0x%lx, done 0x%lx, host_scribble 0x%lx, result 0x%x\n", diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index c5623694d10f..e2a5657d5fdb 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -1,26 +1,34 @@ /* * ahci.c - AHCI SATA support * - * Copyright 2004 Red Hat, Inc. + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. + * Copyright 2004-2005 Red Hat, Inc. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. * - * Version 1.0 of the AHCI specification: + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * AHCI hardware documentation: * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf + * http://www.intel.com/technology/serialata/pdf/rev1_1.pdf * */ @@ -33,6 +41,7 @@ #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/dma-mapping.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -181,11 +190,9 @@ static void ahci_irq_clear(struct ata_port *ap); static void ahci_eng_timeout(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); -static void ahci_host_stop(struct ata_host_set *host_set); static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf); static void ahci_qc_prep(struct ata_queued_cmd *qc); static u8 ahci_check_status(struct ata_port *ap); -static u8 ahci_check_err(struct ata_port *ap); static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc); static void ahci_remove_one (struct pci_dev *pdev); @@ -209,12 +216,11 @@ static Scsi_Host_Template ahci_sht = { .ordered_flush = 1, }; -static struct ata_port_operations ahci_ops = { +static const struct ata_port_operations ahci_ops = { .port_disable = ata_port_disable, .check_status = ahci_check_status, .check_altstatus = ahci_check_status, - .check_err = ahci_check_err, .dev_select = ata_noop_dev_select, .tf_read = ahci_tf_read, @@ -234,7 +240,6 @@ static struct ata_port_operations ahci_ops = { .port_start = ahci_port_start, .port_stop = ahci_port_stop, - .host_stop = ahci_host_stop, }; static struct ata_port_info ahci_port_info[] = { @@ -244,7 +249,7 @@ static struct ata_port_info ahci_port_info[] = { .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, - .pio_mask = 0x03, /* pio3-4 */ + .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, @@ -269,6 +274,8 @@ static struct pci_device_id ahci_pci_tbl[] = { board_ahci }, /* ESB2 */ { PCI_VENDOR_ID_INTEL, 0x2683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_ahci }, /* ESB2 */ + { PCI_VENDOR_ID_INTEL, 0x27c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_ahci }, /* ICH7-M DH */ { } /* terminate list */ }; @@ -286,17 +293,9 @@ static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int return base + 0x100 + (port * 0x80); } -static inline void *ahci_port_base (void *base, unsigned int port) -{ - return (void *) ahci_port_base_ul((unsigned long)base, port); -} - -static void ahci_host_stop(struct ata_host_set *host_set) +static inline void __iomem *ahci_port_base (void __iomem *base, unsigned int port) { - struct ahci_host_priv *hpriv = host_set->private_data; - kfree(hpriv); - - ata_host_stop(host_set); + return (void __iomem *) ahci_port_base_ul((unsigned long)base, port); } static int ahci_port_start(struct ata_port *ap) @@ -304,8 +303,9 @@ static int ahci_port_start(struct ata_port *ap) struct device *dev = ap->host_set->dev; struct ahci_host_priv *hpriv = ap->host_set->private_data; struct ahci_port_priv *pp; - void *mem, *mmio = ap->host_set->mmio_base; - void *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void *mem; dma_addr_t mem_dma; pp = kmalloc(sizeof(*pp), GFP_KERNEL); @@ -373,8 +373,8 @@ static void ahci_port_stop(struct ata_port *ap) { struct device *dev = ap->host_set->dev; struct ahci_port_priv *pp = ap->private_data; - void *mmio = ap->host_set->mmio_base; - void *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); u32 tmp; tmp = readl(port_mmio + PORT_CMD); @@ -406,7 +406,7 @@ static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) return 0xffffffffU; } - return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -424,7 +424,7 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, return; } - writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } static void ahci_phy_reset(struct ata_port *ap) @@ -452,18 +452,11 @@ static void ahci_phy_reset(struct ata_port *ap) static u8 ahci_check_status(struct ata_port *ap) { - void *mmio = (void *) ap->ioaddr.cmd_addr; + void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr; return readl(mmio + PORT_TFDATA) & 0xFF; } -static u8 ahci_check_err(struct ata_port *ap) -{ - void *mmio = (void *) ap->ioaddr.cmd_addr; - - return (readl(mmio + PORT_TFDATA) >> 8) & 0xFF; -} - static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf) { struct ahci_port_priv *pp = ap->private_data; @@ -536,8 +529,8 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc) static void ahci_intr_error(struct ata_port *ap, u32 irq_stat) { - void *mmio = ap->host_set->mmio_base; - void *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); u32 tmp; int work; @@ -584,12 +577,16 @@ static void ahci_intr_error(struct ata_port *ap, u32 irq_stat) static void ahci_eng_timeout(struct ata_port *ap) { - void *mmio = ap->host_set->mmio_base; - void *port_mmio = ahci_port_base(mmio, ap->port_no); + struct ata_host_set *host_set = ap->host_set; + void __iomem *mmio = host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); struct ata_queued_cmd *qc; + unsigned long flags; DPRINTK("ENTER\n"); + spin_lock_irqsave(&host_set->lock, flags); + ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT)); qc = ata_qc_from_tag(ap, ap->active_tag); @@ -604,15 +601,16 @@ static void ahci_eng_timeout(struct ata_port *ap) * not being called from the SCSI EH. */ qc->scsidone = scsi_finish_command; - ata_qc_complete(qc, ATA_ERR); + ata_qc_complete(qc, AC_ERR_OTHER); } + spin_unlock_irqrestore(&host_set->lock, flags); } static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) { - void *mmio = ap->host_set->mmio_base; - void *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *mmio = ap->host_set->mmio_base; + void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); u32 status, serr, ci; serr = readl(port_mmio + PORT_SCR_ERR); @@ -632,7 +630,7 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) if (status & PORT_IRQ_FATAL) { ahci_intr_error(ap, status); if (qc) - ata_qc_complete(qc, ATA_ERR); + ata_qc_complete(qc, AC_ERR_OTHER); } return 1; @@ -648,7 +646,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * struct ata_host_set *host_set = dev_instance; struct ahci_host_priv *hpriv; unsigned int i, handled = 0; - void *mmio; + void __iomem *mmio; u32 irq_stat, irq_ack = 0; VPRINTK("ENTER\n"); @@ -666,17 +664,35 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * for (i = 0; i < host_set->n_ports; i++) { struct ata_port *ap; - u32 tmp; - VPRINTK("port %u\n", i); + if (!(irq_stat & (1 << i))) + continue; + ap = host_set->ports[i]; - tmp = irq_stat & (1 << i); - if (tmp && ap) { + if (ap) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); - if (ahci_host_intr(ap, qc)) - irq_ack |= (1 << i); + if (!ahci_host_intr(ap, qc)) + if (ata_ratelimit()) { + struct pci_dev *pdev = + to_pci_dev(ap->host_set->dev); + dev_printk(KERN_WARNING, &pdev->dev, + "unhandled interrupt on port %u\n", + i); + } + + VPRINTK("port %u\n", i); + } else { + VPRINTK("port %u (no irq)\n", i); + if (ata_ratelimit()) { + struct pci_dev *pdev = + to_pci_dev(ap->host_set->dev); + dev_printk(KERN_WARNING, &pdev->dev, + "interrupt on disabled port %u\n", i); + } } + + irq_ack |= (1 << i); } if (irq_ack) { @@ -694,10 +710,7 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * static int ahci_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void *port_mmio = (void *) ap->ioaddr.cmd_addr; - - writel(1, port_mmio + PORT_SCR_ACT); - readl(port_mmio + PORT_SCR_ACT); /* flush */ + void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr; writel(1, port_mmio + PORT_CMD_ISSUE); readl(port_mmio + PORT_CMD_ISSUE); /* flush */ @@ -747,8 +760,8 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) tmp = readl(mmio + HOST_CTL); if (tmp & HOST_RESET) { - printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n", - pci_name(pdev), tmp); + dev_printk(KERN_ERR, &pdev->dev, + "controller reset failed (0x%x)\n", tmp); return -EIO; } @@ -776,22 +789,22 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) if (rc) { rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); return rc; } } } else { rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); return rc; } rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); return rc; } } @@ -862,27 +875,11 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) return 0; } -/* move to PCI layer, integrate w/ MSI stuff */ -static void pci_intx(struct pci_dev *pdev, int enable) -{ - u16 pci_command, new; - - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - - if (enable) - new = pci_command & ~PCI_COMMAND_INTX_DISABLE; - else - new = pci_command | PCI_COMMAND_INTX_DISABLE; - - if (new != pci_command) - pci_write_config_word(pdev, PCI_COMMAND, pci_command); -} - static void ahci_print_info(struct ata_probe_ent *probe_ent) { struct ahci_host_priv *hpriv = probe_ent->private_data; struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void *mmio = probe_ent->mmio_base; + void __iomem *mmio = probe_ent->mmio_base; u32 vers, cap, impl, speed; const char *speed_s; u16 cc; @@ -910,10 +907,10 @@ static void ahci_print_info(struct ata_probe_ent *probe_ent) else scc_s = "unknown"; - printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x " + dev_printk(KERN_INFO, &pdev->dev, + "AHCI %02x%02x.%02x%02x " "%u slots %u ports %s Gbps 0x%x impl %s mode\n" , - pci_name(pdev), (vers >> 24) & 0xff, (vers >> 16) & 0xff, @@ -926,11 +923,11 @@ static void ahci_print_info(struct ata_probe_ent *probe_ent) impl, scc_s); - printk(KERN_INFO DRV_NAME "(%s) flags: " + dev_printk(KERN_INFO, &pdev->dev, + "flags: " "%s%s%s%s%s%s" "%s%s%s%s%s%s%s\n" , - pci_name(pdev), cap & (1 << 31) ? "64bit " : "", cap & (1 << 30) ? "ncq " : "", @@ -955,7 +952,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) struct ata_probe_ent *probe_ent = NULL; struct ahci_host_priv *hpriv; unsigned long base; - void *mmio_base; + void __iomem *mmio_base; unsigned int board_idx = (unsigned int) ent->driver_data; int have_msi, pci_dev_busy = 0; int rc; @@ -963,7 +960,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) VPRINTK("ENTER\n"); if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -992,8 +989,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->dev = pci_dev_to_dev(pdev); INIT_LIST_HEAD(&probe_ent->node); - mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR), - pci_resource_len(pdev, AHCI_PCI_BAR)); + mmio_base = pci_iomap(pdev, AHCI_PCI_BAR, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; @@ -1037,7 +1033,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) err_out_hpriv: kfree(hpriv); err_out_iounmap: - iounmap(mmio_base); + pci_iounmap(pdev, mmio_base); err_out_free_ent: kfree(probe_ent); err_out_msi: @@ -1077,7 +1073,8 @@ static void ahci_remove_one (struct pci_dev *pdev) scsi_host_put(ap->host); } - host_set->ops->host_stop(host_set); + kfree(hpriv); + pci_iounmap(pdev, host_set->mmio_base); kfree(host_set); if (have_msi) @@ -1094,7 +1091,6 @@ static int __init ahci_init(void) return pci_module_init(&ahci_pci_driver); } - static void __exit ahci_exit(void) { pci_unregister_driver(&ahci_pci_driver); @@ -1105,6 +1101,7 @@ MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("AHCI SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, ahci_pci_tbl); +MODULE_VERSION(DRV_VERSION); module_init(ahci_init); module_exit(ahci_exit); diff --git a/drivers/scsi/aic7xxx/Kconfig.aic79xx b/drivers/scsi/aic7xxx/Kconfig.aic79xx index c2523a30a7f5..69ed77fcb71f 100644 --- a/drivers/scsi/aic7xxx/Kconfig.aic79xx +++ b/drivers/scsi/aic7xxx/Kconfig.aic79xx @@ -5,6 +5,7 @@ config SCSI_AIC79XX tristate "Adaptec AIC79xx U320 support" depends on PCI && SCSI + select SCSI_SPI_ATTRS help This driver supports all of Adaptec's Ultra 320 PCI-X based SCSI controllers. diff --git a/drivers/scsi/aic7xxx/aic7770.c b/drivers/scsi/aic7xxx/aic7770.c index 00f3bd1e181e..527efd36f5c1 100644 --- a/drivers/scsi/aic7xxx/aic7770.c +++ b/drivers/scsi/aic7xxx/aic7770.c @@ -126,7 +126,6 @@ aic7770_find_device(uint32_t id) int aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry, u_int io) { - u_long l; int error; int have_seeprom; u_int hostconf; diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c index 70c5fb59c9ea..d754b3267863 100644 --- a/drivers/scsi/aic7xxx/aic7770_osm.c +++ b/drivers/scsi/aic7xxx/aic7770_osm.c @@ -112,6 +112,9 @@ aic7770_remove(struct device *dev) struct ahc_softc *ahc = dev_get_drvdata(dev); u_long s; + if (ahc->platform_data && ahc->platform_data->host) + scsi_remove_host(ahc->platform_data->host); + ahc_lock(ahc, &s); ahc_intr_enable(ahc, FALSE); ahc_unlock(ahc, &s); diff --git a/drivers/scsi/aic7xxx/aic79xx.h b/drivers/scsi/aic7xxx/aic79xx.h index fd4b2f3eb0c2..653fb0b42aea 100644 --- a/drivers/scsi/aic7xxx/aic79xx.h +++ b/drivers/scsi/aic7xxx/aic79xx.h @@ -1247,9 +1247,6 @@ struct ahd_softc { uint16_t user_tagenable;/* Tagged Queuing allowed */ }; -TAILQ_HEAD(ahd_softc_tailq, ahd_softc); -extern struct ahd_softc_tailq ahd_tailq; - /*************************** IO Cell Configuration ****************************/ #define AHD_PRECOMP_SLEW_INDEX \ (AHD_ANNEXCOL_PRECOMP_SLEW - AHD_ANNEXCOL_PER_DEV0) @@ -1374,8 +1371,6 @@ void ahd_enable_coalescing(struct ahd_softc *ahd, void ahd_pause_and_flushwork(struct ahd_softc *ahd); int ahd_suspend(struct ahd_softc *ahd); int ahd_resume(struct ahd_softc *ahd); -void ahd_softc_insert(struct ahd_softc *); -struct ahd_softc *ahd_find_softc(struct ahd_softc *ahd); void ahd_set_unit(struct ahd_softc *, int); void ahd_set_name(struct ahd_softc *, char *); struct scb *ahd_get_scb(struct ahd_softc *ahd, u_int col_idx); @@ -1524,7 +1519,6 @@ void ahd_print_scb(struct scb *scb); void ahd_print_devinfo(struct ahd_softc *ahd, struct ahd_devinfo *devinfo); void ahd_dump_sglist(struct scb *scb); -void ahd_dump_all_cards_state(void); void ahd_dump_card_state(struct ahd_softc *ahd); int ahd_print_register(ahd_reg_parse_entry_t *table, u_int num_entries, diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index 137fb1a37dd1..4e8f00df978d 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -52,8 +52,6 @@ #include <dev/aic7xxx/aicasm/aicasm_insformat.h> #endif -/******************************** Globals *************************************/ -struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq); /***************************** Lookup Tables **********************************/ char *ahd_chip_names[] = @@ -5180,74 +5178,6 @@ ahd_softc_init(struct ahd_softc *ahd) } void -ahd_softc_insert(struct ahd_softc *ahd) -{ - struct ahd_softc *list_ahd; - -#if AHD_PCI_CONFIG > 0 - /* - * Second Function PCI devices need to inherit some - * settings from function 0. - */ - if ((ahd->features & AHD_MULTI_FUNC) != 0) { - TAILQ_FOREACH(list_ahd, &ahd_tailq, links) { - ahd_dev_softc_t list_pci; - ahd_dev_softc_t pci; - - list_pci = list_ahd->dev_softc; - pci = ahd->dev_softc; - if (ahd_get_pci_slot(list_pci) == ahd_get_pci_slot(pci) - && ahd_get_pci_bus(list_pci) == ahd_get_pci_bus(pci)) { - struct ahd_softc *master; - struct ahd_softc *slave; - - if (ahd_get_pci_function(list_pci) == 0) { - master = list_ahd; - slave = ahd; - } else { - master = ahd; - slave = list_ahd; - } - slave->flags &= ~AHD_BIOS_ENABLED; - slave->flags |= - master->flags & AHD_BIOS_ENABLED; - break; - } - } - } -#endif - - /* - * Insertion sort into our list of softcs. - */ - list_ahd = TAILQ_FIRST(&ahd_tailq); - while (list_ahd != NULL - && ahd_softc_comp(ahd, list_ahd) <= 0) - list_ahd = TAILQ_NEXT(list_ahd, links); - if (list_ahd != NULL) - TAILQ_INSERT_BEFORE(list_ahd, ahd, links); - else - TAILQ_INSERT_TAIL(&ahd_tailq, ahd, links); - ahd->init_level++; -} - -/* - * Verify that the passed in softc pointer is for a - * controller that is still configured. - */ -struct ahd_softc * -ahd_find_softc(struct ahd_softc *ahd) -{ - struct ahd_softc *list_ahd; - - TAILQ_FOREACH(list_ahd, &ahd_tailq, links) { - if (list_ahd == ahd) - return (ahd); - } - return (NULL); -} - -void ahd_set_unit(struct ahd_softc *ahd, int unit) { ahd->unit = unit; @@ -7902,18 +7832,10 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset) static void ahd_reset_poll(void *arg) { - struct ahd_softc *ahd; + struct ahd_softc *ahd = arg; u_int scsiseq1; - u_long l; u_long s; - ahd_list_lock(&l); - ahd = ahd_find_softc((struct ahd_softc *)arg); - if (ahd == NULL) { - printf("ahd_reset_poll: Instance %p no longer exists\n", arg); - ahd_list_unlock(&l); - return; - } ahd_lock(ahd, &s); ahd_pause(ahd); ahd_update_modes(ahd); @@ -7924,7 +7846,6 @@ ahd_reset_poll(void *arg) ahd_reset_poll, ahd); ahd_unpause(ahd); ahd_unlock(ahd, &s); - ahd_list_unlock(&l); return; } @@ -7936,25 +7857,16 @@ ahd_reset_poll(void *arg) ahd->flags &= ~AHD_RESET_POLL_ACTIVE; ahd_unlock(ahd, &s); ahd_release_simq(ahd); - ahd_list_unlock(&l); } /**************************** Statistics Processing ***************************/ static void ahd_stat_timer(void *arg) { - struct ahd_softc *ahd; - u_long l; + struct ahd_softc *ahd = arg; u_long s; int enint_coal; - ahd_list_lock(&l); - ahd = ahd_find_softc((struct ahd_softc *)arg); - if (ahd == NULL) { - printf("ahd_stat_timer: Instance %p no longer exists\n", arg); - ahd_list_unlock(&l); - return; - } ahd_lock(ahd, &s); enint_coal = ahd->hs_mailbox & ENINT_COALESCE; @@ -7981,7 +7893,6 @@ ahd_stat_timer(void *arg) ahd_timer_reset(&ahd->stat_timer, AHD_STAT_UPDATE_US, ahd_stat_timer, ahd); ahd_unlock(ahd, &s); - ahd_list_unlock(&l); } /****************************** Status Processing *****************************/ @@ -8745,16 +8656,6 @@ sized: return (last_probe); } -void -ahd_dump_all_cards_state(void) -{ - struct ahd_softc *list_ahd; - - TAILQ_FOREACH(list_ahd, &ahd_tailq, links) { - ahd_dump_card_state(list_ahd); - } -} - int ahd_print_register(ahd_reg_parse_entry_t *table, u_int num_entries, const char *name, u_int address, u_int value, @@ -9039,7 +8940,6 @@ ahd_dump_card_state(struct ahd_softc *ahd) ahd_outb(ahd, STACK, (ahd->saved_stack[i] >> 8) & 0xFF); } printf("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n"); - ahd_platform_dump_card_state(ahd); ahd_restore_modes(ahd, saved_modes); if (paused == 0) ahd_unpause(ahd); diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 329cb2331339..95c285cc83e4 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -46,32 +46,14 @@ #include "aic79xx_inline.h" #include <scsi/scsicam.h> -/* - * Include aiclib.c as part of our - * "module dependencies are hard" work around. - */ -#include "aiclib.c" +static struct scsi_transport_template *ahd_linux_transport_template = NULL; #include <linux/init.h> /* __setup */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -#include "sd.h" /* For geometry detection */ -#endif - #include <linux/mm.h> /* For fetching system memory size */ +#include <linux/blkdev.h> /* For block_size() */ #include <linux/delay.h> /* For ssleep/msleep */ /* - * Lock protecting manipulation of the ahd softc list. - */ -spinlock_t ahd_list_spinlock; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -/* For dynamic sglist size calculation. */ -u_int ahd_linux_nseg; -#endif - -/* * Bucket size for counting good commands in between bad ones. */ #define AHD_LINUX_ERR_THRESH 1000 @@ -188,71 +170,6 @@ static adapter_tag_info_t aic79xx_tag_info[] = }; /* - * By default, read streaming is disabled. In theory, - * read streaming should enhance performance, but early - * U320 drive firmware actually performs slower with - * read streaming enabled. - */ -#ifdef CONFIG_AIC79XX_ENABLE_RD_STRM -#define AIC79XX_CONFIGED_RD_STRM 0xFFFF -#else -#define AIC79XX_CONFIGED_RD_STRM 0 -#endif - -static uint16_t aic79xx_rd_strm_info[] = -{ - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM, - AIC79XX_CONFIGED_RD_STRM -}; - -/* - * DV option: - * - * positive value = DV Enabled - * zero = DV Disabled - * negative value = DV Default for adapter type/seeprom - */ -#ifdef CONFIG_AIC79XX_DV_SETTING -#define AIC79XX_CONFIGED_DV CONFIG_AIC79XX_DV_SETTING -#else -#define AIC79XX_CONFIGED_DV -1 -#endif - -static int8_t aic79xx_dv_settings[] = -{ - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV, - AIC79XX_CONFIGED_DV -}; - -/* * The I/O cell on the chip is very configurable in respect to its analog * characteristics. Set the defaults here; they can be overriden with * the proper insmod parameters. @@ -375,13 +292,6 @@ static uint32_t aic79xx_pci_parity = ~0; uint32_t aic79xx_allow_memio = ~0; /* - * aic79xx_detect() has been run, so register all device arrivals - * immediately with the system rather than deferring to the sorted - * attachment performed by aic79xx_detect(). - */ -int aic79xx_detect_complete; - -/* * So that we can set how long each device is given as a selection timeout. * The table of values goes like this: * 0 - 256ms @@ -412,7 +322,7 @@ MODULE_AUTHOR("Maintainer: Justin T. Gibbs <gibbs@scsiguy.com>"); MODULE_DESCRIPTION("Adaptec Aic790X U320 SCSI Host Bus Adapter driver"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_VERSION(AIC79XX_DRIVER_VERSION); -module_param(aic79xx, charp, 0); +module_param(aic79xx, charp, 0444); MODULE_PARM_DESC(aic79xx, "period delimited, options string.\n" " verbose Enable verbose/diagnostic logging\n" @@ -427,8 +337,6 @@ MODULE_PARM_DESC(aic79xx, " reverse_scan Sort PCI devices highest Bus/Slot to lowest\n" " tag_info:<tag_str> Set per-target tag depth\n" " global_tag_depth:<int> Global tag depth for all targets on all buses\n" -" rd_strm:<rd_strm_masks> Set per-target read streaming setting.\n" -" dv:<dv_settings> Set per-controller Domain Validation Setting.\n" " slewrate:<slewrate_list>Set the signal slew rate (0-15).\n" " precomp:<pcomp_list> Set the signal precompensation (0-7).\n" " amplitude:<int> Set the signal amplitude (0-7).\n" @@ -441,249 +349,35 @@ MODULE_PARM_DESC(aic79xx, " Shorten the selection timeout to 128ms\n" "\n" " options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n" -"\n" -" Sample /etc/modprobe.conf line:\n" -" Change Read Streaming for Controller's 2 and 3\n" -"\n" -" options aic79xx 'aic79xx=rd_strm:{..0xFFF0.0xC0F0}'"); +"\n"); static void ahd_linux_handle_scsi_status(struct ahd_softc *, - struct ahd_linux_device *, + struct scsi_device *, struct scb *); static void ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, - Scsi_Cmnd *cmd); -static void ahd_linux_filter_inquiry(struct ahd_softc *ahd, - struct ahd_devinfo *devinfo); -static void ahd_linux_dev_timed_unfreeze(u_long arg); + struct scsi_cmnd *cmd); static void ahd_linux_sem_timeout(u_long arg); +static int ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag); static void ahd_linux_initialize_scsi_bus(struct ahd_softc *ahd); -static void ahd_linux_size_nseg(void); -static void ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd); -static void ahd_linux_start_dv(struct ahd_softc *ahd); -static void ahd_linux_dv_timeout(struct scsi_cmnd *cmd); -static int ahd_linux_dv_thread(void *data); -static void ahd_linux_kill_dv_thread(struct ahd_softc *ahd); -static void ahd_linux_dv_target(struct ahd_softc *ahd, u_int target); -static void ahd_linux_dv_transition(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ); -static void ahd_linux_dv_fill_cmd(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo); -static void ahd_linux_dv_inq(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ, - u_int request_length); -static void ahd_linux_dv_tur(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo); -static void ahd_linux_dv_rebd(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ); -static void ahd_linux_dv_web(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ); -static void ahd_linux_dv_reb(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ); -static void ahd_linux_dv_su(struct ahd_softc *ahd, - struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ); -static int ahd_linux_fallback(struct ahd_softc *ahd, - struct ahd_devinfo *devinfo); -static __inline int ahd_linux_dv_fallback(struct ahd_softc *ahd, - struct ahd_devinfo *devinfo); -static void ahd_linux_dv_complete(Scsi_Cmnd *cmd); -static void ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ); static u_int ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo); -static u_int ahd_linux_user_dv_setting(struct ahd_softc *ahd); -static void ahd_linux_setup_user_rd_strm_settings(struct ahd_softc *ahd); -static void ahd_linux_device_queue_depth(struct ahd_softc *ahd, - struct ahd_linux_device *dev); -static struct ahd_linux_target* ahd_linux_alloc_target(struct ahd_softc*, - u_int, u_int); -static void ahd_linux_free_target(struct ahd_softc*, - struct ahd_linux_target*); -static struct ahd_linux_device* ahd_linux_alloc_device(struct ahd_softc*, - struct ahd_linux_target*, - u_int); -static void ahd_linux_free_device(struct ahd_softc*, - struct ahd_linux_device*); -static void ahd_linux_run_device_queue(struct ahd_softc*, - struct ahd_linux_device*); +static void ahd_linux_device_queue_depth(struct scsi_device *); +static int ahd_linux_run_command(struct ahd_softc*, + struct ahd_linux_device *, + struct scsi_cmnd *); static void ahd_linux_setup_tag_info_global(char *p); -static aic_option_callback_t ahd_linux_setup_tag_info; -static aic_option_callback_t ahd_linux_setup_rd_strm_info; -static aic_option_callback_t ahd_linux_setup_dv; -static aic_option_callback_t ahd_linux_setup_iocell_info; -static int ahd_linux_next_unit(void); -static void ahd_runq_tasklet(unsigned long data); -static int aic79xx_setup(char *c); - -/****************************** Inlines ***************************************/ -static __inline void ahd_schedule_completeq(struct ahd_softc *ahd); -static __inline void ahd_schedule_runq(struct ahd_softc *ahd); -static __inline void ahd_setup_runq_tasklet(struct ahd_softc *ahd); -static __inline void ahd_teardown_runq_tasklet(struct ahd_softc *ahd); -static __inline struct ahd_linux_device* - ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, - u_int target, u_int lun, int alloc); -static struct ahd_cmd *ahd_linux_run_complete_queue(struct ahd_softc *ahd); -static __inline void ahd_linux_check_device_queue(struct ahd_softc *ahd, - struct ahd_linux_device *dev); -static __inline struct ahd_linux_device * - ahd_linux_next_device_to_run(struct ahd_softc *ahd); -static __inline void ahd_linux_run_device_queues(struct ahd_softc *ahd); -static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*); - -static __inline void -ahd_schedule_completeq(struct ahd_softc *ahd) -{ - if ((ahd->platform_data->flags & AHD_RUN_CMPLT_Q_TIMER) == 0) { - ahd->platform_data->flags |= AHD_RUN_CMPLT_Q_TIMER; - ahd->platform_data->completeq_timer.expires = jiffies; - add_timer(&ahd->platform_data->completeq_timer); - } -} - -/* - * Must be called with our lock held. - */ -static __inline void -ahd_schedule_runq(struct ahd_softc *ahd) -{ - tasklet_schedule(&ahd->platform_data->runq_tasklet); -} - -static __inline -void ahd_setup_runq_tasklet(struct ahd_softc *ahd) -{ - tasklet_init(&ahd->platform_data->runq_tasklet, ahd_runq_tasklet, - (unsigned long)ahd); -} +static int aic79xx_setup(char *c); -static __inline void -ahd_teardown_runq_tasklet(struct ahd_softc *ahd) -{ - tasklet_kill(&ahd->platform_data->runq_tasklet); -} +static int ahd_linux_unit; -static __inline struct ahd_linux_device* -ahd_linux_get_device(struct ahd_softc *ahd, u_int channel, u_int target, - u_int lun, int alloc) -{ - struct ahd_linux_target *targ; - struct ahd_linux_device *dev; - u_int target_offset; - target_offset = target; - if (channel != 0) - target_offset += 8; - targ = ahd->platform_data->targets[target_offset]; - if (targ == NULL) { - if (alloc != 0) { - targ = ahd_linux_alloc_target(ahd, channel, target); - if (targ == NULL) - return (NULL); - } else - return (NULL); - } - dev = targ->devices[lun]; - if (dev == NULL && alloc != 0) - dev = ahd_linux_alloc_device(ahd, targ, lun); - return (dev); -} - -#define AHD_LINUX_MAX_RETURNED_ERRORS 4 -static struct ahd_cmd * -ahd_linux_run_complete_queue(struct ahd_softc *ahd) -{ - struct ahd_cmd *acmd; - u_long done_flags; - int with_errors; - - with_errors = 0; - ahd_done_lock(ahd, &done_flags); - while ((acmd = TAILQ_FIRST(&ahd->platform_data->completeq)) != NULL) { - Scsi_Cmnd *cmd; - - if (with_errors > AHD_LINUX_MAX_RETURNED_ERRORS) { - /* - * Linux uses stack recursion to requeue - * commands that need to be retried. Avoid - * blowing out the stack by "spoon feeding" - * commands that completed with error back - * the operating system in case they are going - * to be retried. "ick" - */ - ahd_schedule_completeq(ahd); - break; - } - TAILQ_REMOVE(&ahd->platform_data->completeq, - acmd, acmd_links.tqe); - cmd = &acmd_scsi_cmd(acmd); - cmd->host_scribble = NULL; - if (ahd_cmd_get_transaction_status(cmd) != DID_OK - || (cmd->result & 0xFF) != SCSI_STATUS_OK) - with_errors++; - - cmd->scsi_done(cmd); - } - ahd_done_unlock(ahd, &done_flags); - return (acmd); -} - -static __inline void -ahd_linux_check_device_queue(struct ahd_softc *ahd, - struct ahd_linux_device *dev) -{ - if ((dev->flags & AHD_DEV_FREEZE_TIL_EMPTY) != 0 - && dev->active == 0) { - dev->flags &= ~AHD_DEV_FREEZE_TIL_EMPTY; - dev->qfrozen--; - } - - if (TAILQ_FIRST(&dev->busyq) == NULL - || dev->openings == 0 || dev->qfrozen != 0) - return; - - ahd_linux_run_device_queue(ahd, dev); -} - -static __inline struct ahd_linux_device * -ahd_linux_next_device_to_run(struct ahd_softc *ahd) -{ - - if ((ahd->flags & AHD_RESOURCE_SHORTAGE) != 0 - || (ahd->platform_data->qfrozen != 0 - && AHD_DV_SIMQ_FROZEN(ahd) == 0)) - return (NULL); - return (TAILQ_FIRST(&ahd->platform_data->device_runq)); -} - -static __inline void -ahd_linux_run_device_queues(struct ahd_softc *ahd) -{ - struct ahd_linux_device *dev; - - while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) { - TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links); - dev->flags &= ~AHD_DEV_ON_RUN_LIST; - ahd_linux_check_device_queue(ahd, dev); - } -} +/****************************** Inlines ***************************************/ +static __inline void ahd_linux_unmap_scb(struct ahd_softc*, struct scb*); static __inline void ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb) { - Scsi_Cmnd *cmd; + struct scsi_cmnd *cmd; int direction; cmd = scb->io_ctx; @@ -705,197 +399,6 @@ ahd_linux_unmap_scb(struct ahd_softc *ahd, struct scb *scb) #define BUILD_SCSIID(ahd, cmd) \ ((((cmd)->device->id << TID_SHIFT) & TID) | (ahd)->our_id) -/************************ Host template entry points *************************/ -static int ahd_linux_detect(Scsi_Host_Template *); -static const char *ahd_linux_info(struct Scsi_Host *); -static int ahd_linux_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) -static int ahd_linux_slave_alloc(Scsi_Device *); -static int ahd_linux_slave_configure(Scsi_Device *); -static void ahd_linux_slave_destroy(Scsi_Device *); -#if defined(__i386__) -static int ahd_linux_biosparam(struct scsi_device*, - struct block_device*, sector_t, int[]); -#endif -#else -static int ahd_linux_release(struct Scsi_Host *); -static void ahd_linux_select_queue_depth(struct Scsi_Host *host, - Scsi_Device *scsi_devs); -#if defined(__i386__) -static int ahd_linux_biosparam(Disk *, kdev_t, int[]); -#endif -#endif -static int ahd_linux_bus_reset(Scsi_Cmnd *); -static int ahd_linux_dev_reset(Scsi_Cmnd *); -static int ahd_linux_abort(Scsi_Cmnd *); - -/* - * Calculate a safe value for AHD_NSEG (as expressed through ahd_linux_nseg). - * - * In pre-2.5.X... - * The midlayer allocates an S/G array dynamically when a command is issued - * using SCSI malloc. This array, which is in an OS dependent format that - * must later be copied to our private S/G list, is sized to house just the - * number of segments needed for the current transfer. Since the code that - * sizes the SCSI malloc pool does not take into consideration fragmentation - * of the pool, executing transactions numbering just a fraction of our - * concurrent transaction limit with SG list lengths aproaching AHC_NSEG will - * quickly depleat the SCSI malloc pool of usable space. Unfortunately, the - * mid-layer does not properly handle this scsi malloc failures for the S/G - * array and the result can be a lockup of the I/O subsystem. We try to size - * our S/G list so that it satisfies our drivers allocation requirements in - * addition to avoiding fragmentation of the SCSI malloc pool. - */ -static void -ahd_linux_size_nseg(void) -{ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - u_int cur_size; - u_int best_size; - - /* - * The SCSI allocator rounds to the nearest 512 bytes - * an cannot allocate across a page boundary. Our algorithm - * is to start at 1K of scsi malloc space per-command and - * loop through all factors of the PAGE_SIZE and pick the best. - */ - best_size = 0; - for (cur_size = 1024; cur_size <= PAGE_SIZE; cur_size *= 2) { - u_int nseg; - - nseg = cur_size / sizeof(struct scatterlist); - if (nseg < AHD_LINUX_MIN_NSEG) - continue; - - if (best_size == 0) { - best_size = cur_size; - ahd_linux_nseg = nseg; - } else { - u_int best_rem; - u_int cur_rem; - - /* - * Compare the traits of the current "best_size" - * with the current size to determine if the - * current size is a better size. - */ - best_rem = best_size % sizeof(struct scatterlist); - cur_rem = cur_size % sizeof(struct scatterlist); - if (cur_rem < best_rem) { - best_size = cur_size; - ahd_linux_nseg = nseg; - } - } - } -#endif -} - -/* - * Try to detect an Adaptec 79XX controller. - */ -static int -ahd_linux_detect(Scsi_Host_Template *template) -{ - struct ahd_softc *ahd; - int found; - int error = 0; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - /* - * It is a bug that the upper layer takes - * this lock just prior to calling us. - */ - spin_unlock_irq(&io_request_lock); -#endif - - /* - * Sanity checking of Linux SCSI data structures so - * that some of our hacks^H^H^H^H^Hassumptions aren't - * violated. - */ - if (offsetof(struct ahd_cmd_internal, end) - > offsetof(struct scsi_cmnd, host_scribble)) { - printf("ahd_linux_detect: SCSI data structures changed.\n"); - printf("ahd_linux_detect: Unable to attach\n"); - return (0); - } - /* - * Determine an appropriate size for our Scatter Gatther lists. - */ - ahd_linux_size_nseg(); -#ifdef MODULE - /* - * If we've been passed any parameters, process them now. - */ - if (aic79xx) - aic79xx_setup(aic79xx); -#endif - - template->proc_name = "aic79xx"; - - /* - * Initialize our softc list lock prior to - * probing for any adapters. - */ - ahd_list_lockinit(); - -#ifdef CONFIG_PCI - error = ahd_linux_pci_init(); - if (error) - return error; -#endif - - /* - * Register with the SCSI layer all - * controllers we've found. - */ - found = 0; - TAILQ_FOREACH(ahd, &ahd_tailq, links) { - - if (ahd_linux_register_host(ahd, template) == 0) - found++; - } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - spin_lock_irq(&io_request_lock); -#endif - aic79xx_detect_complete++; - return 0; -} - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -/* - * Free the passed in Scsi_Host memory structures prior to unloading the - * module. - */ -static int -ahd_linux_release(struct Scsi_Host * host) -{ - struct ahd_softc *ahd; - u_long l; - - ahd_list_lock(&l); - if (host != NULL) { - - /* - * We should be able to just perform - * the free directly, but check our - * list for extra sanity. - */ - ahd = ahd_find_softc(*(struct ahd_softc **)host->hostdata); - if (ahd != NULL) { - u_long s; - - ahd_lock(ahd, &s); - ahd_intr_enable(ahd, FALSE); - ahd_unlock(ahd, &s); - ahd_free(ahd); - } - } - ahd_list_unlock(&l); - return (0); -} -#endif - /* * Return a string describing the driver. */ @@ -928,220 +431,177 @@ ahd_linux_info(struct Scsi_Host *host) * Queue an SCB to the controller. */ static int -ahd_linux_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *)) +ahd_linux_queue(struct scsi_cmnd * cmd, void (*scsi_done) (struct scsi_cmnd *)) { struct ahd_softc *ahd; - struct ahd_linux_device *dev; - u_long flags; + struct ahd_linux_device *dev = scsi_transport_device_data(cmd->device); ahd = *(struct ahd_softc **)cmd->device->host->hostdata; /* - * Save the callback on completion function. - */ - cmd->scsi_done = scsi_done; - - ahd_midlayer_entrypoint_lock(ahd, &flags); - - /* * Close the race of a command that was in the process of * being queued to us just as our simq was frozen. Let * DV commands through so long as we are only frozen to * perform DV. */ - if (ahd->platform_data->qfrozen != 0 - && AHD_DV_CMD(cmd) == 0) { - - ahd_cmd_set_transaction_status(cmd, CAM_REQUEUE_REQ); - ahd_linux_queue_cmd_complete(ahd, cmd); - ahd_schedule_completeq(ahd); - ahd_midlayer_entrypoint_unlock(ahd, &flags); - return (0); - } - dev = ahd_linux_get_device(ahd, cmd->device->channel, - cmd->device->id, cmd->device->lun, - /*alloc*/TRUE); - if (dev == NULL) { - ahd_cmd_set_transaction_status(cmd, CAM_RESRC_UNAVAIL); - ahd_linux_queue_cmd_complete(ahd, cmd); - ahd_schedule_completeq(ahd); - ahd_midlayer_entrypoint_unlock(ahd, &flags); - printf("%s: aic79xx_linux_queue - Unable to allocate device!\n", - ahd_name(ahd)); - return (0); + if (ahd->platform_data->qfrozen != 0) { + printf("%s: queue frozen\n", ahd_name(ahd)); + + return SCSI_MLQUEUE_HOST_BUSY; } - if (cmd->cmd_len > MAX_CDB_LEN) - return (-EINVAL); + + /* + * Save the callback on completion function. + */ + cmd->scsi_done = scsi_done; + cmd->result = CAM_REQ_INPROG << 16; - TAILQ_INSERT_TAIL(&dev->busyq, (struct ahd_cmd *)cmd, acmd_links.tqe); - if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) { - TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links); - dev->flags |= AHD_DEV_ON_RUN_LIST; - ahd_linux_run_device_queues(ahd); - } - ahd_midlayer_entrypoint_unlock(ahd, &flags); - return (0); + + return ahd_linux_run_command(ahd, dev, cmd); +} + +static inline struct scsi_target ** +ahd_linux_target_in_softc(struct scsi_target *starget) +{ + struct ahd_softc *ahd = + *((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata); + unsigned int target_offset; + + target_offset = starget->id; + if (starget->channel != 0) + target_offset += 8; + + return &ahd->platform_data->starget[target_offset]; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) static int -ahd_linux_slave_alloc(Scsi_Device *device) +ahd_linux_target_alloc(struct scsi_target *starget) { - struct ahd_softc *ahd; + struct ahd_softc *ahd = + *((struct ahd_softc **)dev_to_shost(&starget->dev)->hostdata); + unsigned long flags; + struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget); + struct ahd_linux_target *targ = scsi_transport_target_data(starget); + struct ahd_devinfo devinfo; + struct ahd_initiator_tinfo *tinfo; + struct ahd_tmode_tstate *tstate; + char channel = starget->channel + 'A'; - ahd = *((struct ahd_softc **)device->host->hostdata); - if (bootverbose) - printf("%s: Slave Alloc %d\n", ahd_name(ahd), device->id); - return (0); + ahd_lock(ahd, &flags); + + BUG_ON(*ahd_targp != NULL); + + *ahd_targp = starget; + memset(targ, 0, sizeof(*targ)); + + tinfo = ahd_fetch_transinfo(ahd, channel, ahd->our_id, + starget->id, &tstate); + ahd_compile_devinfo(&devinfo, ahd->our_id, starget->id, + CAM_LUN_WILDCARD, channel, + ROLE_INITIATOR); + spi_min_period(starget) = AHD_SYNCRATE_MAX; /* We can do U320 */ + if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0) + spi_max_offset(starget) = MAX_OFFSET_PACED_BUG; + else + spi_max_offset(starget) = MAX_OFFSET_PACED; + spi_max_width(starget) = ahd->features & AHD_WIDE; + + ahd_set_syncrate(ahd, &devinfo, 0, 0, 0, + AHD_TRANS_GOAL, /*paused*/FALSE); + ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, + AHD_TRANS_GOAL, /*paused*/FALSE); + ahd_unlock(ahd, &flags); + + return 0; +} + +static void +ahd_linux_target_destroy(struct scsi_target *starget) +{ + struct scsi_target **ahd_targp = ahd_linux_target_in_softc(starget); + + *ahd_targp = NULL; } static int -ahd_linux_slave_configure(Scsi_Device *device) +ahd_linux_slave_alloc(struct scsi_device *sdev) { - struct ahd_softc *ahd; - struct ahd_linux_device *dev; - u_long flags; + struct ahd_softc *ahd = + *((struct ahd_softc **)sdev->host->hostdata); + struct scsi_target *starget = sdev->sdev_target; + struct ahd_linux_target *targ = scsi_transport_target_data(starget); + struct ahd_linux_device *dev; - ahd = *((struct ahd_softc **)device->host->hostdata); if (bootverbose) - printf("%s: Slave Configure %d\n", ahd_name(ahd), device->id); - ahd_midlayer_entrypoint_lock(ahd, &flags); + printf("%s: Slave Alloc %d\n", ahd_name(ahd), sdev->id); + + BUG_ON(targ->sdev[sdev->lun] != NULL); + + dev = scsi_transport_device_data(sdev); + memset(dev, 0, sizeof(*dev)); + /* - * Since Linux has attached to the device, configure - * it so we don't free and allocate the device - * structure on every command. + * We start out life using untagged + * transactions of which we allow one. */ - dev = ahd_linux_get_device(ahd, device->channel, - device->id, device->lun, - /*alloc*/TRUE); - if (dev != NULL) { - dev->flags &= ~AHD_DEV_UNCONFIGURED; - dev->flags |= AHD_DEV_SLAVE_CONFIGURED; - dev->scsi_device = device; - ahd_linux_device_queue_depth(ahd, dev); - } - ahd_midlayer_entrypoint_unlock(ahd, &flags); + dev->openings = 1; + + /* + * Set maxtags to 0. This will be changed if we + * later determine that we are dealing with + * a tagged queuing capable device. + */ + dev->maxtags = 0; + + targ->sdev[sdev->lun] = sdev; + return (0); } -static void -ahd_linux_slave_destroy(Scsi_Device *device) +static int +ahd_linux_slave_configure(struct scsi_device *sdev) { struct ahd_softc *ahd; - struct ahd_linux_device *dev; - u_long flags; - ahd = *((struct ahd_softc **)device->host->hostdata); + ahd = *((struct ahd_softc **)sdev->host->hostdata); if (bootverbose) - printf("%s: Slave Destroy %d\n", ahd_name(ahd), device->id); - ahd_midlayer_entrypoint_lock(ahd, &flags); - dev = ahd_linux_get_device(ahd, device->channel, - device->id, device->lun, - /*alloc*/FALSE); + printf("%s: Slave Configure %d\n", ahd_name(ahd), sdev->id); - /* - * Filter out "silly" deletions of real devices by only - * deleting devices that have had slave_configure() - * called on them. All other devices that have not - * been configured will automatically be deleted by - * the refcounting process. - */ - if (dev != NULL - && (dev->flags & AHD_DEV_SLAVE_CONFIGURED) != 0) { - dev->flags |= AHD_DEV_UNCONFIGURED; - if (TAILQ_EMPTY(&dev->busyq) - && dev->active == 0 - && (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0) - ahd_linux_free_device(ahd, dev); - } - ahd_midlayer_entrypoint_unlock(ahd, &flags); + ahd_linux_device_queue_depth(sdev); + + /* Initial Domain Validation */ + if (!spi_initial_dv(sdev->sdev_target)) + spi_dv_device(sdev); + + return 0; } -#else -/* - * Sets the queue depth for each SCSI device hanging - * off the input host adapter. - */ + static void -ahd_linux_select_queue_depth(struct Scsi_Host * host, - Scsi_Device * scsi_devs) +ahd_linux_slave_destroy(struct scsi_device *sdev) { - Scsi_Device *device; - Scsi_Device *ldev; struct ahd_softc *ahd; - u_long flags; + struct ahd_linux_device *dev = scsi_transport_device_data(sdev); + struct ahd_linux_target *targ = scsi_transport_target_data(sdev->sdev_target); - ahd = *((struct ahd_softc **)host->hostdata); - ahd_lock(ahd, &flags); - for (device = scsi_devs; device != NULL; device = device->next) { + ahd = *((struct ahd_softc **)sdev->host->hostdata); + if (bootverbose) + printf("%s: Slave Destroy %d\n", ahd_name(ahd), sdev->id); - /* - * Watch out for duplicate devices. This works around - * some quirks in how the SCSI scanning code does its - * device management. - */ - for (ldev = scsi_devs; ldev != device; ldev = ldev->next) { - if (ldev->host == device->host - && ldev->channel == device->channel - && ldev->id == device->id - && ldev->lun == device->lun) - break; - } - /* Skip duplicate. */ - if (ldev != device) - continue; + BUG_ON(dev->active); - if (device->host == host) { - struct ahd_linux_device *dev; + targ->sdev[sdev->lun] = NULL; - /* - * Since Linux has attached to the device, configure - * it so we don't free and allocate the device - * structure on every command. - */ - dev = ahd_linux_get_device(ahd, device->channel, - device->id, device->lun, - /*alloc*/TRUE); - if (dev != NULL) { - dev->flags &= ~AHD_DEV_UNCONFIGURED; - dev->scsi_device = device; - ahd_linux_device_queue_depth(ahd, dev); - device->queue_depth = dev->openings - + dev->active; - if ((dev->flags & (AHD_DEV_Q_BASIC - | AHD_DEV_Q_TAGGED)) == 0) { - /* - * We allow the OS to queue 2 untagged - * transactions to us at any time even - * though we can only execute them - * serially on the controller/device. - * This should remove some latency. - */ - device->queue_depth = 2; - } - } - } - } - ahd_unlock(ahd, &flags); } -#endif #if defined(__i386__) /* * Return the disk geometry for the given SCSI device. */ static int -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) ahd_linux_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) { uint8_t *bh; -#else -ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) -{ - struct scsi_device *sdev = disk->device; - u_long capacity = disk->capacity; - struct buffer_head *bh; -#endif int heads; int sectors; int cylinders; @@ -1151,22 +611,11 @@ ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) ahd = *((struct ahd_softc **)sdev->host->hostdata); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) bh = scsi_bios_ptable(bdev); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,17) - bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, block_size(dev)); -#else - bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024); -#endif - if (bh) { ret = scsi_partsize(bh, capacity, &geom[2], &geom[0], &geom[1]); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) kfree(bh); -#else - brelse(bh); -#endif if (ret != -1) return (ret); } @@ -1194,392 +643,35 @@ ahd_linux_biosparam(Disk *disk, kdev_t dev, int geom[]) * Abort the current SCSI command(s). */ static int -ahd_linux_abort(Scsi_Cmnd *cmd) +ahd_linux_abort(struct scsi_cmnd *cmd) { - struct ahd_softc *ahd; - struct ahd_cmd *acmd; - struct ahd_cmd *list_acmd; - struct ahd_linux_device *dev; - struct scb *pending_scb; - u_long s; - u_int saved_scbptr; - u_int active_scbptr; - u_int last_phase; - u_int cdb_byte; - int retval; - int was_paused; - int paused; - int wait; - int disconnected; - ahd_mode_state saved_modes; - - pending_scb = NULL; - paused = FALSE; - wait = FALSE; - ahd = *(struct ahd_softc **)cmd->device->host->hostdata; - acmd = (struct ahd_cmd *)cmd; - - printf("%s:%d:%d:%d: Attempting to abort cmd %p:", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun, cmd); - for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) - printf(" 0x%x", cmd->cmnd[cdb_byte]); - printf("\n"); - - /* - * In all versions of Linux, we have to work around - * a major flaw in how the mid-layer is locked down - * if we are to sleep successfully in our error handler - * while allowing our interrupt handler to run. Since - * the midlayer acquires either the io_request_lock or - * our lock prior to calling us, we must use the - * spin_unlock_irq() method for unlocking our lock. - * This will force interrupts to be enabled on the - * current CPU. Since the EH thread should not have - * been running with CPU interrupts disabled other than - * by acquiring either the io_request_lock or our own - * lock, this *should* be safe. - */ - ahd_midlayer_entrypoint_lock(ahd, &s); - - /* - * First determine if we currently own this command. - * Start by searching the device queue. If not found - * there, check the pending_scb list. If not found - * at all, and the system wanted us to just abort the - * command, return success. - */ - dev = ahd_linux_get_device(ahd, cmd->device->channel, - cmd->device->id, cmd->device->lun, - /*alloc*/FALSE); - - if (dev == NULL) { - /* - * No target device for this command exists, - * so we must not still own the command. - */ - printf("%s:%d:%d:%d: Is not an active device\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun); - retval = SUCCESS; - goto no_cmd; - } - - TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) { - if (list_acmd == acmd) - break; - } - - if (list_acmd != NULL) { - printf("%s:%d:%d:%d: Command found on device queue\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun); - TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe); - cmd->result = DID_ABORT << 16; - ahd_linux_queue_cmd_complete(ahd, cmd); - retval = SUCCESS; - goto done; - } - - /* - * See if we can find a matching cmd in the pending list. - */ - LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { - if (pending_scb->io_ctx == cmd) - break; - } - - if (pending_scb == NULL) { - printf("%s:%d:%d:%d: Command not found\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun); - goto no_cmd; - } - - if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) { - /* - * We can't queue two recovery actions using the same SCB - */ - retval = FAILED; - goto done; - } - - /* - * Ensure that the card doesn't do anything - * behind our back. Also make sure that we - * didn't "just" miss an interrupt that would - * affect this cmd. - */ - was_paused = ahd_is_paused(ahd); - ahd_pause_and_flushwork(ahd); - paused = TRUE; - - if ((pending_scb->flags & SCB_ACTIVE) == 0) { - printf("%s:%d:%d:%d: Command already completed\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun); - goto no_cmd; - } - - printf("%s: At time of recovery, card was %spaused\n", - ahd_name(ahd), was_paused ? "" : "not "); - ahd_dump_card_state(ahd); - - disconnected = TRUE; - if (ahd_search_qinfifo(ahd, cmd->device->id, cmd->device->channel + 'A', - cmd->device->lun, SCB_GET_TAG(pending_scb), - ROLE_INITIATOR, CAM_REQ_ABORTED, - SEARCH_COMPLETE) > 0) { - printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun); - retval = SUCCESS; - goto done; - } - - saved_modes = ahd_save_modes(ahd); - ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); - last_phase = ahd_inb(ahd, LASTPHASE); - saved_scbptr = ahd_get_scbptr(ahd); - active_scbptr = saved_scbptr; - if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { - struct scb *bus_scb; - - bus_scb = ahd_lookup_scb(ahd, active_scbptr); - if (bus_scb == pending_scb) - disconnected = FALSE; - } - - /* - * At this point, pending_scb is the scb associated with the - * passed in command. That command is currently active on the - * bus or is in the disconnected state. - */ - if (last_phase != P_BUSFREE - && SCB_GET_TAG(pending_scb) == active_scbptr) { - - /* - * We're active on the bus, so assert ATN - * and hope that the target responds. - */ - pending_scb = ahd_lookup_scb(ahd, active_scbptr); - pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; - ahd_outb(ahd, MSG_OUT, HOST_MSG); - ahd_outb(ahd, SCSISIGO, last_phase|ATNO); - printf("%s:%d:%d:%d: Device is active, asserting ATN\n", - ahd_name(ahd), cmd->device->channel, - cmd->device->id, cmd->device->lun); - wait = TRUE; - } else if (disconnected) { - - /* - * Actually re-queue this SCB in an attempt - * to select the device before it reconnects. - */ - pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; - ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb)); - pending_scb->hscb->cdb_len = 0; - pending_scb->hscb->task_attribute = 0; - pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK; - - if ((pending_scb->flags & SCB_PACKETIZED) != 0) { - /* - * Mark the SCB has having an outstanding - * task management function. Should the command - * complete normally before the task management - * function can be sent, the host will be notified - * to abort our requeued SCB. - */ - ahd_outb(ahd, SCB_TASK_MANAGEMENT, - pending_scb->hscb->task_management); - } else { - /* - * If non-packetized, set the MK_MESSAGE control - * bit indicating that we desire to send a message. - * We also set the disconnected flag since there is - * no guarantee that our SCB control byte matches - * the version on the card. We don't want the - * sequencer to abort the command thinking an - * unsolicited reselection occurred. - */ - pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; + int error; - /* - * The sequencer will never re-reference the - * in-core SCB. To make sure we are notified - * during reslection, set the MK_MESSAGE flag in - * the card's copy of the SCB. - */ - ahd_outb(ahd, SCB_CONTROL, - ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); - } - - /* - * Clear out any entries in the QINFIFO first - * so we are the next SCB for this target - * to run. - */ - ahd_search_qinfifo(ahd, cmd->device->id, - cmd->device->channel + 'A', cmd->device->lun, - SCB_LIST_NULL, ROLE_INITIATOR, - CAM_REQUEUE_REQ, SEARCH_COMPLETE); - ahd_qinfifo_requeue_tail(ahd, pending_scb); - ahd_set_scbptr(ahd, saved_scbptr); - ahd_print_path(ahd, pending_scb); - printf("Device is disconnected, re-queuing SCB\n"); - wait = TRUE; - } else { - printf("%s:%d:%d:%d: Unable to deliver message\n", - ahd_name(ahd), cmd->device->channel, - cmd->device->id, cmd->device->lun); - retval = FAILED; - goto done; - } - -no_cmd: - /* - * Our assumption is that if we don't have the command, no - * recovery action was required, so we return success. Again, - * the semantics of the mid-layer recovery engine are not - * well defined, so this may change in time. - */ - retval = SUCCESS; -done: - if (paused) - ahd_unpause(ahd); - if (wait) { - struct timer_list timer; - int ret; - - pending_scb->platform_data->flags |= AHD_SCB_UP_EH_SEM; - spin_unlock_irq(&ahd->platform_data->spin_lock); - init_timer(&timer); - timer.data = (u_long)pending_scb; - timer.expires = jiffies + (5 * HZ); - timer.function = ahd_linux_sem_timeout; - add_timer(&timer); - printf("Recovery code sleeping\n"); - down(&ahd->platform_data->eh_sem); - printf("Recovery code awake\n"); - ret = del_timer_sync(&timer); - if (ret == 0) { - printf("Timer Expired\n"); - retval = FAILED; - } - spin_lock_irq(&ahd->platform_data->spin_lock); - } - ahd_schedule_runq(ahd); - ahd_linux_run_complete_queue(ahd); - ahd_midlayer_entrypoint_unlock(ahd, &s); - return (retval); -} - - -static void -ahd_linux_dev_reset_complete(Scsi_Cmnd *cmd) -{ - free(cmd, M_DEVBUF); + error = ahd_linux_queue_recovery_cmd(cmd, SCB_ABORT); + if (error != 0) + printf("aic79xx_abort returns 0x%x\n", error); + return error; } /* * Attempt to send a target reset message to the device that timed out. */ static int -ahd_linux_dev_reset(Scsi_Cmnd *cmd) +ahd_linux_dev_reset(struct scsi_cmnd *cmd) { - struct ahd_softc *ahd; - struct scsi_cmnd *recovery_cmd; - struct ahd_linux_device *dev; - struct ahd_initiator_tinfo *tinfo; - struct ahd_tmode_tstate *tstate; - struct scb *scb; - struct hardware_scb *hscb; - u_long s; - struct timer_list timer; - int retval; - - ahd = *(struct ahd_softc **)cmd->device->host->hostdata; - recovery_cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK); - if (!recovery_cmd) - return (FAILED); - memset(recovery_cmd, 0, sizeof(struct scsi_cmnd)); - recovery_cmd->device = cmd->device; - recovery_cmd->scsi_done = ahd_linux_dev_reset_complete; -#ifdef AHD_DEBUG - if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) - printf("%s:%d:%d:%d: Device reset called for cmd %p\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->device->lun, cmd); -#endif - ahd_lock(ahd, &s); - - dev = ahd_linux_get_device(ahd, cmd->device->channel, cmd->device->id, - cmd->device->lun, /*alloc*/FALSE); - if (dev == NULL) { - ahd_unlock(ahd, &s); - kfree(recovery_cmd); - return (FAILED); - } - if ((scb = ahd_get_scb(ahd, AHD_NEVER_COL_IDX)) == NULL) { - ahd_unlock(ahd, &s); - kfree(recovery_cmd); - return (FAILED); - } - tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, - cmd->device->id, &tstate); - recovery_cmd->result = CAM_REQ_INPROG << 16; - recovery_cmd->host_scribble = (char *)scb; - scb->io_ctx = recovery_cmd; - scb->platform_data->dev = dev; - scb->sg_count = 0; - ahd_set_residual(scb, 0); - ahd_set_sense_residual(scb, 0); - hscb = scb->hscb; - hscb->control = 0; - hscb->scsiid = BUILD_SCSIID(ahd, cmd); - hscb->lun = cmd->device->lun; - hscb->cdb_len = 0; - hscb->task_management = SIU_TASKMGMT_LUN_RESET; - scb->flags |= SCB_DEVICE_RESET|SCB_RECOVERY_SCB|SCB_ACTIVE; - if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - scb->flags |= SCB_PACKETIZED; - } else { - hscb->control |= MK_MESSAGE; - } - dev->openings--; - dev->active++; - dev->commands_issued++; - LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); - ahd_queue_scb(ahd, scb); + int error; - scb->platform_data->flags |= AHD_SCB_UP_EH_SEM; - ahd_unlock(ahd, &s); - init_timer(&timer); - timer.data = (u_long)scb; - timer.expires = jiffies + (5 * HZ); - timer.function = ahd_linux_sem_timeout; - add_timer(&timer); - printf("Recovery code sleeping\n"); - down(&ahd->platform_data->eh_sem); - printf("Recovery code awake\n"); - retval = SUCCESS; - if (del_timer_sync(&timer) == 0) { - printf("Timer Expired\n"); - retval = FAILED; - } - ahd_lock(ahd, &s); - ahd_schedule_runq(ahd); - ahd_linux_run_complete_queue(ahd); - ahd_unlock(ahd, &s); - printf("%s: Device reset returning 0x%x\n", ahd_name(ahd), retval); - return (retval); + error = ahd_linux_queue_recovery_cmd(cmd, SCB_DEVICE_RESET); + if (error != 0) + printf("aic79xx_dev_reset returns 0x%x\n", error); + return error; } /* * Reset the SCSI bus. */ static int -ahd_linux_bus_reset(Scsi_Cmnd *cmd) +ahd_linux_bus_reset(struct scsi_cmnd *cmd) { struct ahd_softc *ahd; u_long s; @@ -1594,7 +686,6 @@ ahd_linux_bus_reset(Scsi_Cmnd *cmd) ahd_lock(ahd, &s); found = ahd_reset_channel(ahd, cmd->device->channel + 'A', /*initiate reset*/TRUE); - ahd_linux_run_complete_queue(ahd); ahd_unlock(ahd, &s); if (bootverbose) @@ -1604,9 +695,10 @@ ahd_linux_bus_reset(Scsi_Cmnd *cmd) return (SUCCESS); } -Scsi_Host_Template aic79xx_driver_template = { +struct scsi_host_template aic79xx_driver_template = { .module = THIS_MODULE, .name = "aic79xx", + .proc_name = "aic79xx", .proc_info = ahd_linux_proc_info, .info = ahd_linux_info, .queuecommand = ahd_linux_queue, @@ -1623,37 +715,10 @@ Scsi_Host_Template aic79xx_driver_template = { .slave_alloc = ahd_linux_slave_alloc, .slave_configure = ahd_linux_slave_configure, .slave_destroy = ahd_linux_slave_destroy, + .target_alloc = ahd_linux_target_alloc, + .target_destroy = ahd_linux_target_destroy, }; -/**************************** Tasklet Handler *********************************/ - -/* - * In 2.4.X and above, this routine is called from a tasklet, - * so we must re-acquire our lock prior to executing this code. - * In all prior kernels, ahd_schedule_runq() calls this routine - * directly and ahd_schedule_runq() is called with our lock held. - */ -static void -ahd_runq_tasklet(unsigned long data) -{ - struct ahd_softc* ahd; - struct ahd_linux_device *dev; - u_long flags; - - ahd = (struct ahd_softc *)data; - ahd_lock(ahd, &flags); - while ((dev = ahd_linux_next_device_to_run(ahd)) != NULL) { - - TAILQ_REMOVE(&ahd->platform_data->device_runq, dev, links); - dev->flags &= ~AHD_DEV_ON_RUN_LIST; - ahd_linux_check_device_queue(ahd, dev); - /* Yeild to our interrupt handler */ - ahd_unlock(ahd, &flags); - ahd_lock(ahd, &flags); - } - ahd_unlock(ahd, &flags); -} - /******************************** Bus DMA *************************************/ int ahd_dma_tag_create(struct ahd_softc *ahd, bus_dma_tag_t parent, @@ -1693,36 +758,10 @@ int ahd_dmamem_alloc(struct ahd_softc *ahd, bus_dma_tag_t dmat, void** vaddr, int flags, bus_dmamap_t *mapp) { - bus_dmamap_t map; - - map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT); - if (map == NULL) - return (ENOMEM); - /* - * Although we can dma data above 4GB, our - * "consistent" memory is below 4GB for - * space efficiency reasons (only need a 4byte - * address). For this reason, we have to reset - * our dma mask when doing allocations. - */ - if (ahd->dev_softc != NULL) - if (pci_set_dma_mask(ahd->dev_softc, 0xFFFFFFFF)) { - printk(KERN_WARNING "aic79xx: No suitable DMA available.\n"); - kfree(map); - return (ENODEV); - } *vaddr = pci_alloc_consistent(ahd->dev_softc, - dmat->maxsize, &map->bus_addr); - if (ahd->dev_softc != NULL) - if (pci_set_dma_mask(ahd->dev_softc, - ahd->platform_data->hw_dma_mask)) { - printk(KERN_WARNING "aic79xx: No suitable DMA available.\n"); - kfree(map); - return (ENODEV); - } + dmat->maxsize, mapp); if (*vaddr == NULL) return (ENOMEM); - *mapp = map; return(0); } @@ -1731,7 +770,7 @@ ahd_dmamem_free(struct ahd_softc *ahd, bus_dma_tag_t dmat, void* vaddr, bus_dmamap_t map) { pci_free_consistent(ahd->dev_softc, dmat->maxsize, - vaddr, map->bus_addr); + vaddr, map); } int @@ -1745,7 +784,7 @@ ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map, */ bus_dma_segment_t stack_sg; - stack_sg.ds_addr = map->bus_addr; + stack_sg.ds_addr = map; stack_sg.ds_len = dmat->maxsize; cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0); return (0); @@ -1754,11 +793,6 @@ ahd_dmamap_load(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map, void ahd_dmamap_destroy(struct ahd_softc *ahd, bus_dma_tag_t dmat, bus_dmamap_t map) { - /* - * The map may is NULL in our < 2.3.X implementation. - */ - if (map != NULL) - free(map, M_DEVBUF); } int @@ -1823,41 +857,6 @@ ahd_softc_comp(struct ahd_softc *lahd, struct ahd_softc *rahd) } static void -ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) -{ - - if ((instance >= 0) && (targ >= 0) - && (instance < NUM_ELEMENTS(aic79xx_tag_info)) - && (targ < AHD_NUM_TARGETS)) { - aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF; - if (bootverbose) - printf("tag_info[%d:%d] = %d\n", instance, targ, value); - } -} - -static void -ahd_linux_setup_rd_strm_info(u_long arg, int instance, int targ, int32_t value) -{ - if ((instance >= 0) - && (instance < NUM_ELEMENTS(aic79xx_rd_strm_info))) { - aic79xx_rd_strm_info[instance] = value & 0xFFFF; - if (bootverbose) - printf("rd_strm[%d] = 0x%x\n", instance, value); - } -} - -static void -ahd_linux_setup_dv(u_long arg, int instance, int targ, int32_t value) -{ - if ((instance >= 0) - && (instance < NUM_ELEMENTS(aic79xx_dv_settings))) { - aic79xx_dv_settings[instance] = value; - if (bootverbose) - printf("dv[%d] = %d\n", instance, value); - } -} - -static void ahd_linux_setup_iocell_info(u_long index, int instance, int targ, int32_t value) { @@ -1887,6 +886,99 @@ ahd_linux_setup_tag_info_global(char *p) } } +static void +ahd_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) +{ + + if ((instance >= 0) && (targ >= 0) + && (instance < NUM_ELEMENTS(aic79xx_tag_info)) + && (targ < AHD_NUM_TARGETS)) { + aic79xx_tag_info[instance].tag_commands[targ] = value & 0x1FF; + if (bootverbose) + printf("tag_info[%d:%d] = %d\n", instance, targ, value); + } +} + +static char * +ahd_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, + void (*callback)(u_long, int, int, int32_t), + u_long callback_arg) +{ + char *tok_end; + char *tok_end2; + int i; + int instance; + int targ; + int done; + char tok_list[] = {'.', ',', '{', '}', '\0'}; + + /* All options use a ':' name/arg separator */ + if (*opt_arg != ':') + return (opt_arg); + opt_arg++; + instance = -1; + targ = -1; + done = FALSE; + /* + * Restore separator that may be in + * the middle of our option argument. + */ + tok_end = strchr(opt_arg, '\0'); + if (tok_end < end) + *tok_end = ','; + while (!done) { + switch (*opt_arg) { + case '{': + if (instance == -1) { + instance = 0; + } else { + if (depth > 1) { + if (targ == -1) + targ = 0; + } else { + printf("Malformed Option %s\n", + opt_name); + done = TRUE; + } + } + opt_arg++; + break; + case '}': + if (targ != -1) + targ = -1; + else if (instance != -1) + instance = -1; + opt_arg++; + break; + case ',': + case '.': + if (instance == -1) + done = TRUE; + else if (targ >= 0) + targ++; + else if (instance >= 0) + instance++; + opt_arg++; + break; + case '\0': + done = TRUE; + break; + default: + tok_end = end; + for (i = 0; tok_list[i]; i++) { + tok_end2 = strchr(opt_arg, tok_list[i]); + if ((tok_end2) && (tok_end2 < tok_end)) + tok_end = tok_end2; + } + callback(callback_arg, instance, targ, + simple_strtol(opt_arg, NULL, 0)); + opt_arg = tok_end; + break; + } + } + return (opt_arg); +} + /* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. @@ -1916,8 +1008,6 @@ aic79xx_setup(char *s) { "seltime", &aic79xx_seltime }, { "tag_info", NULL }, { "global_tag_depth", NULL}, - { "rd_strm", NULL }, - { "dv", NULL }, { "slewrate", NULL }, { "precomp", NULL }, { "amplitude", NULL }, @@ -1946,24 +1036,18 @@ aic79xx_setup(char *s) if (strncmp(p, "global_tag_depth", n) == 0) { ahd_linux_setup_tag_info_global(p + n); } else if (strncmp(p, "tag_info", n) == 0) { - s = aic_parse_brace_option("tag_info", p + n, end, + s = ahd_parse_brace_option("tag_info", p + n, end, 2, ahd_linux_setup_tag_info, 0); - } else if (strncmp(p, "rd_strm", n) == 0) { - s = aic_parse_brace_option("rd_strm", p + n, end, - 1, ahd_linux_setup_rd_strm_info, 0); - } else if (strncmp(p, "dv", n) == 0) { - s = aic_parse_brace_option("dv", p + n, end, 1, - ahd_linux_setup_dv, 0); } else if (strncmp(p, "slewrate", n) == 0) { - s = aic_parse_brace_option("slewrate", + s = ahd_parse_brace_option("slewrate", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_SLEWRATE_INDEX); } else if (strncmp(p, "precomp", n) == 0) { - s = aic_parse_brace_option("precomp", + s = ahd_parse_brace_option("precomp", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_PRECOMP_INDEX); } else if (strncmp(p, "amplitude", n) == 0) { - s = aic_parse_brace_option("amplitude", + s = ahd_parse_brace_option("amplitude", p + n, end, 1, ahd_linux_setup_iocell_info, AIC79XX_AMPLITUDE_INDEX); } else if (p[n] == ':') { @@ -1982,13 +1066,12 @@ __setup("aic79xx=", aic79xx_setup); uint32_t aic79xx_verbose; int -ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template) +ahd_linux_register_host(struct ahd_softc *ahd, struct scsi_host_template *template) { char buf[80]; struct Scsi_Host *host; char *new_name; u_long s; - u_long target; template->name = ahd->description; host = scsi_host_alloc(template, sizeof(struct ahd_softc *)); @@ -1997,11 +1080,7 @@ ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template) *((struct ahd_softc **)host->hostdata) = ahd; ahd_lock(ahd, &s); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) scsi_assign_lock(host, &ahd->platform_data->spin_lock); -#elif AHD_SCSI_HAS_HOST_LOCK != 0 - host->lock = &ahd->platform_data->spin_lock; -#endif ahd->platform_data->host = host; host->can_queue = AHD_MAX_QUEUE; host->cmd_per_lun = 2; @@ -2012,7 +1091,7 @@ ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template) host->max_lun = AHD_NUM_LUNS; host->max_channel = 0; host->sg_tablesize = AHD_NSEG; - ahd_set_unit(ahd, ahd_linux_next_unit()); + ahd_set_unit(ahd, ahd_linux_unit++); sprintf(buf, "scsi%d", host->host_no); new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT); if (new_name != NULL) { @@ -2020,54 +1099,14 @@ ahd_linux_register_host(struct ahd_softc *ahd, Scsi_Host_Template *template) ahd_set_name(ahd, new_name); } host->unique_id = ahd->unit; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - scsi_set_pci_device(host, ahd->dev_softc); -#endif - ahd_linux_setup_user_rd_strm_settings(ahd); ahd_linux_initialize_scsi_bus(ahd); - ahd_unlock(ahd, &s); - ahd->platform_data->dv_pid = kernel_thread(ahd_linux_dv_thread, ahd, 0); - ahd_lock(ahd, &s); - if (ahd->platform_data->dv_pid < 0) { - printf("%s: Failed to create DV thread, error= %d\n", - ahd_name(ahd), ahd->platform_data->dv_pid); - return (-ahd->platform_data->dv_pid); - } - /* - * Initially allocate *all* of our linux target objects - * so that the DV thread will scan them all in parallel - * just after driver initialization. Any device that - * does not exist will have its target object destroyed - * by the selection timeout handler. In the case of a - * device that appears after the initial DV scan, async - * negotiation will occur for the first command, and DV - * will comence should that first command be successful. - */ - for (target = 0; target < host->max_id; target++) { - - /* - * Skip our own ID. Some Compaq/HP storage devices - * have enclosure management devices that respond to - * single bit selection (i.e. selecting ourselves). - * It is expected that either an external application - * or a modified kernel will be used to probe this - * ID if it is appropriate. To accommodate these - * installations, ahc_linux_alloc_target() will allocate - * for our ID if asked to do so. - */ - if (target == ahd->our_id) - continue; - - ahd_linux_alloc_target(ahd, 0, target); - } ahd_intr_enable(ahd, TRUE); - ahd_linux_start_dv(ahd); ahd_unlock(ahd, &s); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) + host->transportt = ahd_linux_transport_template; + scsi_add_host(host, &ahd->dev_softc->dev); /* XXX handle failure */ scsi_scan_host(host); -#endif return (0); } @@ -2081,29 +1120,6 @@ ahd_linux_get_memsize(void) } /* - * Find the smallest available unit number to use - * for a new device. We don't just use a static - * count to handle the "repeated hot-(un)plug" - * scenario. - */ -static int -ahd_linux_next_unit(void) -{ - struct ahd_softc *ahd; - int unit; - - unit = 0; -retry: - TAILQ_FOREACH(ahd, &ahd_tailq, links) { - if (ahd->unit == unit) { - unit++; - goto retry; - } - } - return (unit); -} - -/* * Place the SCSI bus into a known state by either resetting it, * or forcing transfer negotiations on the next command to any * target. @@ -2162,20 +1178,9 @@ ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg) if (ahd->platform_data == NULL) return (ENOMEM); memset(ahd->platform_data, 0, sizeof(struct ahd_platform_data)); - TAILQ_INIT(&ahd->platform_data->completeq); - TAILQ_INIT(&ahd->platform_data->device_runq); ahd->platform_data->irq = AHD_LINUX_NOIRQ; - ahd->platform_data->hw_dma_mask = 0xFFFFFFFF; ahd_lockinit(ahd); - ahd_done_lockinit(ahd); - init_timer(&ahd->platform_data->completeq_timer); - ahd->platform_data->completeq_timer.data = (u_long)ahd; - ahd->platform_data->completeq_timer.function = - (ahd_linux_callback_t *)ahd_linux_thread_run_complete_queue; init_MUTEX_LOCKED(&ahd->platform_data->eh_sem); - init_MUTEX_LOCKED(&ahd->platform_data->dv_sem); - init_MUTEX_LOCKED(&ahd->platform_data->dv_cmd_sem); - ahd_setup_runq_tasklet(ahd); ahd->seltime = (aic79xx_seltime & 0x3) << 4; return (0); } @@ -2183,39 +1188,22 @@ ahd_platform_alloc(struct ahd_softc *ahd, void *platform_arg) void ahd_platform_free(struct ahd_softc *ahd) { - struct ahd_linux_target *targ; - struct ahd_linux_device *dev; + struct scsi_target *starget; int i, j; if (ahd->platform_data != NULL) { - del_timer_sync(&ahd->platform_data->completeq_timer); - ahd_linux_kill_dv_thread(ahd); - ahd_teardown_runq_tasklet(ahd); - if (ahd->platform_data->host != NULL) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - scsi_remove_host(ahd->platform_data->host); -#endif - scsi_host_put(ahd->platform_data->host); - } - /* destroy all of the device and target objects */ for (i = 0; i < AHD_NUM_TARGETS; i++) { - targ = ahd->platform_data->targets[i]; - if (targ != NULL) { - /* Keep target around through the loop. */ - targ->refcount++; + starget = ahd->platform_data->starget[i]; + if (starget != NULL) { for (j = 0; j < AHD_NUM_LUNS; j++) { - - if (targ->devices[j] == NULL) + struct ahd_linux_target *targ = + scsi_transport_target_data(starget); + if (targ->sdev[j] == NULL) continue; - dev = targ->devices[j]; - ahd_linux_free_device(ahd, dev); + targ->sdev[j] = NULL; } - /* - * Forcibly free the target now that - * all devices are gone. - */ - ahd_linux_free_target(ahd, targ); + ahd->platform_data->starget[i] = NULL; } } @@ -2233,16 +1221,9 @@ ahd_platform_free(struct ahd_softc *ahd) release_mem_region(ahd->platform_data->mem_busaddr, 0x1000); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - /* - * In 2.4 we detach from the scsi midlayer before the PCI - * layer invokes our remove callback. No per-instance - * detach is provided, so we must reach inside the PCI - * subsystem's internals and detach our driver manually. - */ - if (ahd->dev_softc != NULL) - ahd->dev_softc->driver = NULL; -#endif + if (ahd->platform_data->host) + scsi_host_put(ahd->platform_data->host); + free(ahd->platform_data, M_DEVBUF); } } @@ -2280,13 +1261,22 @@ void ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, ahd_queue_alg alg) { + struct scsi_target *starget; + struct ahd_linux_target *targ; struct ahd_linux_device *dev; + struct scsi_device *sdev; int was_queuing; int now_queuing; - dev = ahd_linux_get_device(ahd, devinfo->channel - 'A', - devinfo->target, - devinfo->lun, /*alloc*/FALSE); + starget = ahd->platform_data->starget[devinfo->target]; + targ = scsi_transport_target_data(starget); + BUG_ON(targ == NULL); + sdev = targ->sdev[devinfo->lun]; + if (sdev == NULL) + return; + + dev = scsi_transport_device_data(sdev); + if (dev == NULL) return; was_queuing = dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED); @@ -2339,1434 +1329,37 @@ ahd_platform_set_tags(struct ahd_softc *ahd, struct ahd_devinfo *devinfo, dev->maxtags = 0; dev->openings = 1 - dev->active; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - if (dev->scsi_device != NULL) { - switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) { - case AHD_DEV_Q_BASIC: - scsi_adjust_queue_depth(dev->scsi_device, - MSG_SIMPLE_TASK, - dev->openings + dev->active); - break; - case AHD_DEV_Q_TAGGED: - scsi_adjust_queue_depth(dev->scsi_device, - MSG_ORDERED_TASK, - dev->openings + dev->active); - break; - default: - /* - * We allow the OS to queue 2 untagged transactions to - * us at any time even though we can only execute them - * serially on the controller/device. This should - * remove some latency. - */ - scsi_adjust_queue_depth(dev->scsi_device, - /*NON-TAGGED*/0, - /*queue depth*/2); - break; - } - } -#endif -} - -int -ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel, - int lun, u_int tag, role_t role, uint32_t status) -{ - int targ; - int maxtarg; - int maxlun; - int clun; - int count; - - if (tag != SCB_LIST_NULL) - return (0); - - targ = 0; - if (target != CAM_TARGET_WILDCARD) { - targ = target; - maxtarg = targ + 1; - } else { - maxtarg = (ahd->features & AHD_WIDE) ? 16 : 8; - } - clun = 0; - if (lun != CAM_LUN_WILDCARD) { - clun = lun; - maxlun = clun + 1; - } else { - maxlun = AHD_NUM_LUNS; - } - - count = 0; - for (; targ < maxtarg; targ++) { - - for (; clun < maxlun; clun++) { - struct ahd_linux_device *dev; - struct ahd_busyq *busyq; - struct ahd_cmd *acmd; - - dev = ahd_linux_get_device(ahd, /*chan*/0, targ, - clun, /*alloc*/FALSE); - if (dev == NULL) - continue; - - busyq = &dev->busyq; - while ((acmd = TAILQ_FIRST(busyq)) != NULL) { - Scsi_Cmnd *cmd; - - cmd = &acmd_scsi_cmd(acmd); - TAILQ_REMOVE(busyq, acmd, - acmd_links.tqe); - count++; - cmd->result = status << 16; - ahd_linux_queue_cmd_complete(ahd, cmd); - } - } - } - - return (count); -} - -static void -ahd_linux_thread_run_complete_queue(struct ahd_softc *ahd) -{ - u_long flags; - - ahd_lock(ahd, &flags); - del_timer(&ahd->platform_data->completeq_timer); - ahd->platform_data->flags &= ~AHD_RUN_CMPLT_Q_TIMER; - ahd_linux_run_complete_queue(ahd); - ahd_unlock(ahd, &flags); -} - -static void -ahd_linux_start_dv(struct ahd_softc *ahd) -{ - - /* - * Freeze the simq and signal ahd_linux_queue to not let any - * more commands through - */ - if ((ahd->platform_data->flags & AHD_DV_ACTIVE) == 0) { -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) - printf("%s: Starting DV\n", ahd_name(ahd)); -#endif - - ahd->platform_data->flags |= AHD_DV_ACTIVE; - ahd_freeze_simq(ahd); - - /* Wake up the DV kthread */ - up(&ahd->platform_data->dv_sem); - } -} - -static int -ahd_linux_dv_thread(void *data) -{ - struct ahd_softc *ahd; - int target; - u_long s; - - ahd = (struct ahd_softc *)data; - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) - printf("In DV Thread\n"); -#endif - - /* - * Complete thread creation. - */ - lock_kernel(); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) - /* - * Don't care about any signals. - */ - siginitsetinv(¤t->blocked, 0); - - daemonize(); - sprintf(current->comm, "ahd_dv_%d", ahd->unit); -#else - daemonize("ahd_dv_%d", ahd->unit); - current->flags |= PF_NOFREEZE; -#endif - unlock_kernel(); - - while (1) { - /* - * Use down_interruptible() rather than down() to - * avoid inclusion in the load average. - */ - down_interruptible(&ahd->platform_data->dv_sem); - - /* Check to see if we've been signaled to exit */ - ahd_lock(ahd, &s); - if ((ahd->platform_data->flags & AHD_DV_SHUTDOWN) != 0) { - ahd_unlock(ahd, &s); - break; - } - ahd_unlock(ahd, &s); - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) - printf("%s: Beginning Domain Validation\n", - ahd_name(ahd)); -#endif - - /* - * Wait for any pending commands to drain before proceeding. - */ - ahd_lock(ahd, &s); - while (LIST_FIRST(&ahd->pending_scbs) != NULL) { - ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_EMPTY; - ahd_unlock(ahd, &s); - down_interruptible(&ahd->platform_data->dv_sem); - ahd_lock(ahd, &s); - } - - /* - * Wait for the SIMQ to be released so that DV is the - * only reason the queue is frozen. - */ - while (AHD_DV_SIMQ_FROZEN(ahd) == 0) { - ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE; - ahd_unlock(ahd, &s); - down_interruptible(&ahd->platform_data->dv_sem); - ahd_lock(ahd, &s); - } - ahd_unlock(ahd, &s); - - for (target = 0; target < AHD_NUM_TARGETS; target++) - ahd_linux_dv_target(ahd, target); - - ahd_lock(ahd, &s); - ahd->platform_data->flags &= ~AHD_DV_ACTIVE; - ahd_unlock(ahd, &s); - - /* - * Release the SIMQ so that normal commands are - * allowed to continue on the bus. - */ - ahd_release_simq(ahd); - } - up(&ahd->platform_data->eh_sem); - return (0); -} - -static void -ahd_linux_kill_dv_thread(struct ahd_softc *ahd) -{ - u_long s; - - ahd_lock(ahd, &s); - if (ahd->platform_data->dv_pid != 0) { - ahd->platform_data->flags |= AHD_DV_SHUTDOWN; - ahd_unlock(ahd, &s); - up(&ahd->platform_data->dv_sem); - - /* - * Use the eh_sem as an indicator that the - * dv thread is exiting. Note that the dv - * thread must still return after performing - * the up on our semaphore before it has - * completely exited this module. Unfortunately, - * there seems to be no easy way to wait for the - * exit of a thread for which you are not the - * parent (dv threads are parented by init). - * Cross your fingers... - */ - down(&ahd->platform_data->eh_sem); - - /* - * Mark the dv thread as already dead. This - * avoids attempting to kill it a second time. - * This is necessary because we must kill the - * DV thread before calling ahd_free() in the - * module shutdown case to avoid bogus locking - * in the SCSI mid-layer, but we ahd_free() is - * called without killing the DV thread in the - * instance detach case, so ahd_platform_free() - * calls us again to verify that the DV thread - * is dead. - */ - ahd->platform_data->dv_pid = 0; - } else { - ahd_unlock(ahd, &s); - } -} - -#define AHD_LINUX_DV_INQ_SHORT_LEN 36 -#define AHD_LINUX_DV_INQ_LEN 256 -#define AHD_LINUX_DV_TIMEOUT (HZ / 4) - -#define AHD_SET_DV_STATE(ahd, targ, newstate) \ - ahd_set_dv_state(ahd, targ, newstate, __LINE__) - -static __inline void -ahd_set_dv_state(struct ahd_softc *ahd, struct ahd_linux_target *targ, - ahd_dv_state newstate, u_int line) -{ - ahd_dv_state oldstate; - oldstate = targ->dv_state; -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) - printf("%s:%d: Going from state %d to state %d\n", - ahd_name(ahd), line, oldstate, newstate); -#endif - - if (oldstate == newstate) - targ->dv_state_retry++; - else - targ->dv_state_retry = 0; - targ->dv_state = newstate; -} - -static void -ahd_linux_dv_target(struct ahd_softc *ahd, u_int target_offset) -{ - struct ahd_devinfo devinfo; - struct ahd_linux_target *targ; - struct scsi_cmnd *cmd; - struct scsi_device *scsi_dev; - struct scsi_sense_data *sense; - uint8_t *buffer; - u_long s; - u_int timeout; - int echo_size; - - sense = NULL; - buffer = NULL; - echo_size = 0; - ahd_lock(ahd, &s); - targ = ahd->platform_data->targets[target_offset]; - if (targ == NULL || (targ->flags & AHD_DV_REQUIRED) == 0) { - ahd_unlock(ahd, &s); - return; - } - ahd_compile_devinfo(&devinfo, ahd->our_id, targ->target, /*lun*/0, - targ->channel + 'A', ROLE_INITIATOR); -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, &devinfo); - printf("Performing DV\n"); - } -#endif - - ahd_unlock(ahd, &s); - - cmd = malloc(sizeof(struct scsi_cmnd), M_DEVBUF, M_WAITOK); - scsi_dev = malloc(sizeof(struct scsi_device), M_DEVBUF, M_WAITOK); - scsi_dev->host = ahd->platform_data->host; - scsi_dev->id = devinfo.target; - scsi_dev->lun = devinfo.lun; - scsi_dev->channel = devinfo.channel - 'A'; - ahd->platform_data->dv_scsi_dev = scsi_dev; - - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_SHORT_ASYNC); - - while (targ->dv_state != AHD_DV_STATE_EXIT) { - timeout = AHD_LINUX_DV_TIMEOUT; - switch (targ->dv_state) { - case AHD_DV_STATE_INQ_SHORT_ASYNC: - case AHD_DV_STATE_INQ_ASYNC: - case AHD_DV_STATE_INQ_ASYNC_VERIFY: - /* - * Set things to async narrow to reduce the - * chance that the INQ will fail. - */ - ahd_lock(ahd, &s); - ahd_set_syncrate(ahd, &devinfo, 0, 0, 0, - AHD_TRANS_GOAL, /*paused*/FALSE); - ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, - AHD_TRANS_GOAL, /*paused*/FALSE); - ahd_unlock(ahd, &s); - timeout = 10 * HZ; - targ->flags &= ~AHD_INQ_VALID; - /* FALLTHROUGH */ - case AHD_DV_STATE_INQ_VERIFY: - { - u_int inq_len; - - if (targ->dv_state == AHD_DV_STATE_INQ_SHORT_ASYNC) - inq_len = AHD_LINUX_DV_INQ_SHORT_LEN; - else - inq_len = targ->inq_data->additional_length + 5; - ahd_linux_dv_inq(ahd, cmd, &devinfo, targ, inq_len); - break; - } - case AHD_DV_STATE_TUR: - case AHD_DV_STATE_BUSY: - timeout = 5 * HZ; - ahd_linux_dv_tur(ahd, cmd, &devinfo); - break; - case AHD_DV_STATE_REBD: - ahd_linux_dv_rebd(ahd, cmd, &devinfo, targ); - break; - case AHD_DV_STATE_WEB: - ahd_linux_dv_web(ahd, cmd, &devinfo, targ); - break; - - case AHD_DV_STATE_REB: - ahd_linux_dv_reb(ahd, cmd, &devinfo, targ); - break; - - case AHD_DV_STATE_SU: - ahd_linux_dv_su(ahd, cmd, &devinfo, targ); - timeout = 50 * HZ; - break; - - default: - ahd_print_devinfo(ahd, &devinfo); - printf("Unknown DV state %d\n", targ->dv_state); - goto out; - } - - /* Queue the command and wait for it to complete */ - /* Abuse eh_timeout in the scsi_cmnd struct for our purposes */ - init_timer(&cmd->eh_timeout); -#ifdef AHD_DEBUG - if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) - /* - * All of the printfs during negotiation - * really slow down the negotiation. - * Add a bit of time just to be safe. - */ - timeout += HZ; -#endif - scsi_add_timer(cmd, timeout, ahd_linux_dv_timeout); - /* - * In 2.5.X, it is assumed that all calls from the - * "midlayer" (which we are emulating) will have the - * ahd host lock held. For other kernels, the - * io_request_lock must be held. - */ -#if AHD_SCSI_HAS_HOST_LOCK != 0 - ahd_lock(ahd, &s); -#else - spin_lock_irqsave(&io_request_lock, s); -#endif - ahd_linux_queue(cmd, ahd_linux_dv_complete); -#if AHD_SCSI_HAS_HOST_LOCK != 0 - ahd_unlock(ahd, &s); -#else - spin_unlock_irqrestore(&io_request_lock, s); -#endif - down_interruptible(&ahd->platform_data->dv_cmd_sem); - /* - * Wait for the SIMQ to be released so that DV is the - * only reason the queue is frozen. - */ - ahd_lock(ahd, &s); - while (AHD_DV_SIMQ_FROZEN(ahd) == 0) { - ahd->platform_data->flags |= AHD_DV_WAIT_SIMQ_RELEASE; - ahd_unlock(ahd, &s); - down_interruptible(&ahd->platform_data->dv_sem); - ahd_lock(ahd, &s); - } - ahd_unlock(ahd, &s); - - ahd_linux_dv_transition(ahd, cmd, &devinfo, targ); - } - -out: - if ((targ->flags & AHD_INQ_VALID) != 0 - && ahd_linux_get_device(ahd, devinfo.channel - 'A', - devinfo.target, devinfo.lun, - /*alloc*/FALSE) == NULL) { - /* - * The DV state machine failed to configure this device. - * This is normal if DV is disabled. Since we have inquiry - * data, filter it and use the "optimistic" negotiation - * parameters found in the inquiry string. - */ - ahd_linux_filter_inquiry(ahd, &devinfo); - if ((targ->flags & (AHD_BASIC_DV|AHD_ENHANCED_DV)) != 0) { - ahd_print_devinfo(ahd, &devinfo); - printf("DV failed to configure device. " - "Please file a bug report against " - "this driver.\n"); - } - } - - if (cmd != NULL) - free(cmd, M_DEVBUF); - - if (ahd->platform_data->dv_scsi_dev != NULL) { - free(ahd->platform_data->dv_scsi_dev, M_DEVBUF); - ahd->platform_data->dv_scsi_dev = NULL; - } - - ahd_lock(ahd, &s); - if (targ->dv_buffer != NULL) { - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = NULL; - } - if (targ->dv_buffer1 != NULL) { - free(targ->dv_buffer1, M_DEVBUF); - targ->dv_buffer1 = NULL; - } - targ->flags &= ~AHD_DV_REQUIRED; - if (targ->refcount == 0) - ahd_linux_free_target(ahd, targ); - ahd_unlock(ahd, &s); -} - -static __inline int -ahd_linux_dv_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) -{ - u_long s; - int retval; - - ahd_lock(ahd, &s); - retval = ahd_linux_fallback(ahd, devinfo); - ahd_unlock(ahd, &s); - - return (retval); -} - -static void -ahd_linux_dv_transition(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ) -{ - u_int32_t status; - - status = aic_error_action(cmd, targ->inq_data, - ahd_cmd_get_transaction_status(cmd), - ahd_cmd_get_scsi_status(cmd)); - - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Entering ahd_linux_dv_transition, state= %d, " - "status= 0x%x, cmd->result= 0x%x\n", targ->dv_state, - status, cmd->result); - } -#endif - - switch (targ->dv_state) { - case AHD_DV_STATE_INQ_SHORT_ASYNC: - case AHD_DV_STATE_INQ_ASYNC: - switch (status & SS_MASK) { - case SS_NOP: - { - AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1); - break; - } - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_TUR: - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) - targ->dv_state_retry--; - if ((status & SS_ERRMASK) == EBUSY) - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY); - if (targ->dv_state_retry < 10) - break; - /* FALLTHROUGH */ - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Failed DV inquiry, skipping\n"); - } -#endif - break; - } + switch ((dev->flags & (AHD_DEV_Q_BASIC|AHD_DEV_Q_TAGGED))) { + case AHD_DEV_Q_BASIC: + scsi_adjust_queue_depth(sdev, + MSG_SIMPLE_TASK, + dev->openings + dev->active); break; - case AHD_DV_STATE_INQ_ASYNC_VERIFY: - switch (status & SS_MASK) { - case SS_NOP: - { - u_int xportflags; - u_int spi3data; - - if (memcmp(targ->inq_data, targ->dv_buffer, - AHD_LINUX_DV_INQ_LEN) != 0) { - /* - * Inquiry data must have changed. - * Try from the top again. - */ - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - } - - AHD_SET_DV_STATE(ahd, targ, targ->dv_state+1); - targ->flags |= AHD_INQ_VALID; - if (ahd_linux_user_dv_setting(ahd) == 0) - break; - - xportflags = targ->inq_data->flags; - if ((xportflags & (SID_Sync|SID_WBus16)) == 0) - break; - - spi3data = targ->inq_data->spi3data; - switch (spi3data & SID_SPI_CLOCK_DT_ST) { - default: - case SID_SPI_CLOCK_ST: - /* Assume only basic DV is supported. */ - targ->flags |= AHD_BASIC_DV; - break; - case SID_SPI_CLOCK_DT: - case SID_SPI_CLOCK_DT_ST: - targ->flags |= AHD_ENHANCED_DV; - break; - } - break; - } - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_TUR: - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) - targ->dv_state_retry--; - - if ((status & SS_ERRMASK) == EBUSY) - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY); - if (targ->dv_state_retry < 10) - break; - /* FALLTHROUGH */ - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Failed DV inquiry, skipping\n"); - } -#endif - break; - } - break; - case AHD_DV_STATE_INQ_VERIFY: - switch (status & SS_MASK) { - case SS_NOP: - { - - if (memcmp(targ->inq_data, targ->dv_buffer, - AHD_LINUX_DV_INQ_LEN) == 0) { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - int i; - - ahd_print_devinfo(ahd, devinfo); - printf("Inquiry buffer mismatch:"); - for (i = 0; i < AHD_LINUX_DV_INQ_LEN; i++) { - if ((i & 0xF) == 0) - printf("\n "); - printf("0x%x:0x0%x ", - ((uint8_t *)targ->inq_data)[i], - targ->dv_buffer[i]); - } - printf("\n"); - } -#endif - - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - /* - * Do not count "falling back" - * against our retries. - */ - targ->dv_state_retry = 0; - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - break; - } - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_TUR: - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) { - targ->dv_state_retry--; - } else if ((status & SSQ_FALLBACK) != 0) { - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) { - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_EXIT); - break; - } - /* - * Do not count "falling back" - * against our retries. - */ - targ->dv_state_retry = 0; - } else if ((status & SS_ERRMASK) == EBUSY) - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY); - if (targ->dv_state_retry < 10) - break; - /* FALLTHROUGH */ - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Failed DV inquiry, skipping\n"); - } -#endif - break; - } - break; - - case AHD_DV_STATE_TUR: - switch (status & SS_MASK) { - case SS_NOP: - if ((targ->flags & AHD_BASIC_DV) != 0) { - ahd_linux_filter_inquiry(ahd, devinfo); - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_VERIFY); - } else if ((targ->flags & AHD_ENHANCED_DV) != 0) { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REBD); - } else { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - } - break; - case SS_RETRY: - case SS_TUR: - if ((status & SS_ERRMASK) == EBUSY) { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_BUSY); - break; - } - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) { - targ->dv_state_retry--; - } else if ((status & SSQ_FALLBACK) != 0) { - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) { - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_EXIT); - break; - } - /* - * Do not count "falling back" - * against our retries. - */ - targ->dv_state_retry = 0; - } - if (targ->dv_state_retry >= 10) { -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("DV TUR reties exhausted\n"); - } -#endif - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - if (status & SSQ_DELAY) - ssleep(1); - - break; - case SS_START: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_SU); - break; - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - break; - - case AHD_DV_STATE_REBD: - switch (status & SS_MASK) { - case SS_NOP: - { - uint32_t echo_size; - - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB); - echo_size = scsi_3btoul(&targ->dv_buffer[1]); - echo_size &= 0x1FFF; -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Echo buffer size= %d\n", echo_size); - } -#endif - if (echo_size == 0) { - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - - /* Generate the buffer pattern */ - targ->dv_echo_size = echo_size; - ahd_linux_generate_dv_pattern(targ); - /* - * Setup initial negotiation values. - */ - ahd_linux_filter_inquiry(ahd, devinfo); - break; - } - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) - targ->dv_state_retry--; - if (targ->dv_state_retry <= 10) - break; -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("DV REBD reties exhausted\n"); - } -#endif - /* FALLTHROUGH */ - case SS_FATAL: - default: - /* - * Setup initial negotiation values - * and try level 1 DV. - */ - ahd_linux_filter_inquiry(ahd, devinfo); - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_INQ_VERIFY); - targ->dv_echo_size = 0; - break; - } - break; - - case AHD_DV_STATE_WEB: - switch (status & SS_MASK) { - case SS_NOP: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_REB); - break; - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) { - targ->dv_state_retry--; - } else if ((status & SSQ_FALLBACK) != 0) { - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) { - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_EXIT); - break; - } - /* - * Do not count "falling back" - * against our retries. - */ - targ->dv_state_retry = 0; - } - if (targ->dv_state_retry <= 10) - break; - /* FALLTHROUGH */ -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("DV WEB reties exhausted\n"); - } -#endif - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - break; - - case AHD_DV_STATE_REB: - switch (status & SS_MASK) { - case SS_NOP: - if (memcmp(targ->dv_buffer, targ->dv_buffer1, - targ->dv_echo_size) != 0) { - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_EXIT); - else - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_WEB); - break; - } - - if (targ->dv_buffer != NULL) { - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = NULL; - } - if (targ->dv_buffer1 != NULL) { - free(targ->dv_buffer1, M_DEVBUF); - targ->dv_buffer1 = NULL; - } - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) { - targ->dv_state_retry--; - } else if ((status & SSQ_FALLBACK) != 0) { - if (ahd_linux_dv_fallback(ahd, devinfo) != 0) { - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_EXIT); - break; - } - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_WEB); - } - if (targ->dv_state_retry <= 10) { - if ((status & (SSQ_DELAY_RANDOM|SSQ_DELAY))!= 0) - msleep(ahd->our_id*1000/10); - break; - } -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("DV REB reties exhausted\n"); - } -#endif - /* FALLTHROUGH */ - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - break; - - case AHD_DV_STATE_SU: - switch (status & SS_MASK) { - case SS_NOP: - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } - break; - - case AHD_DV_STATE_BUSY: - switch (status & SS_MASK) { - case SS_NOP: - case SS_INQ_REFRESH: - AHD_SET_DV_STATE(ahd, targ, - AHD_DV_STATE_INQ_SHORT_ASYNC); - break; - case SS_TUR: - case SS_RETRY: - AHD_SET_DV_STATE(ahd, targ, targ->dv_state); - if (ahd_cmd_get_transaction_status(cmd) - == CAM_REQUEUE_REQ) { - targ->dv_state_retry--; - } else if (targ->dv_state_retry < 60) { - if ((status & SSQ_DELAY) != 0) - ssleep(1); - } else { -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("DV BUSY reties exhausted\n"); - } -#endif - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - } - break; - default: - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } + case AHD_DEV_Q_TAGGED: + scsi_adjust_queue_depth(sdev, + MSG_ORDERED_TASK, + dev->openings + dev->active); break; - default: - printf("%s: Invalid DV completion state %d\n", ahd_name(ahd), - targ->dv_state); - AHD_SET_DV_STATE(ahd, targ, AHD_DV_STATE_EXIT); - break; - } -} - -static void -ahd_linux_dv_fill_cmd(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo) -{ - memset(cmd, 0, sizeof(struct scsi_cmnd)); - cmd->device = ahd->platform_data->dv_scsi_dev; - cmd->scsi_done = ahd_linux_dv_complete; -} - -/* - * Synthesize an inquiry command. On the return trip, it'll be - * sniffed and the device transfer settings set for us. - */ -static void -ahd_linux_dv_inq(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, struct ahd_linux_target *targ, - u_int request_length) -{ - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending INQ\n"); - } -#endif - if (targ->inq_data == NULL) - targ->inq_data = malloc(AHD_LINUX_DV_INQ_LEN, - M_DEVBUF, M_WAITOK); - if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC) { - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(AHD_LINUX_DV_INQ_LEN, - M_DEVBUF, M_WAITOK); - } - - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 6; - cmd->cmnd[0] = INQUIRY; - cmd->cmnd[4] = request_length; - cmd->request_bufflen = request_length; - if (targ->dv_state > AHD_DV_STATE_INQ_ASYNC) - cmd->request_buffer = targ->dv_buffer; - else - cmd->request_buffer = targ->inq_data; - memset(cmd->request_buffer, 0, AHD_LINUX_DV_INQ_LEN); -} - -static void -ahd_linux_dv_tur(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo) -{ - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending TUR\n"); - } -#endif - /* Do a TUR to clear out any non-fatal transitional state */ - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_NONE; - cmd->cmd_len = 6; - cmd->cmnd[0] = TEST_UNIT_READY; -} - -#define AHD_REBD_LEN 4 - -static void -ahd_linux_dv_rebd(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, struct ahd_linux_target *targ) -{ - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending REBD\n"); - } -#endif - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(AHD_REBD_LEN, M_DEVBUF, M_WAITOK); - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = READ_BUFFER; - cmd->cmnd[1] = 0x0b; - scsi_ulto3b(AHD_REBD_LEN, &cmd->cmnd[6]); - cmd->request_bufflen = AHD_REBD_LEN; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer; -} - -static void -ahd_linux_dv_web(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, struct ahd_linux_target *targ) -{ - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending WEB\n"); - } -#endif - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_TO_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = WRITE_BUFFER; - cmd->cmnd[1] = 0x0a; - scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]); - cmd->request_bufflen = targ->dv_echo_size; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer; -} - -static void -ahd_linux_dv_reb(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, struct ahd_linux_target *targ) -{ - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending REB\n"); - } -#endif - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_FROM_DEVICE; - cmd->cmd_len = 10; - cmd->cmnd[0] = READ_BUFFER; - cmd->cmnd[1] = 0x0a; - scsi_ulto3b(targ->dv_echo_size, &cmd->cmnd[6]); - cmd->request_bufflen = targ->dv_echo_size; - cmd->underflow = cmd->request_bufflen; - cmd->request_buffer = targ->dv_buffer1; -} - -static void -ahd_linux_dv_su(struct ahd_softc *ahd, struct scsi_cmnd *cmd, - struct ahd_devinfo *devinfo, - struct ahd_linux_target *targ) -{ - u_int le; - - le = SID_IS_REMOVABLE(targ->inq_data) ? SSS_LOEJ : 0; - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Sending SU\n"); - } -#endif - ahd_linux_dv_fill_cmd(ahd, cmd, devinfo); - cmd->sc_data_direction = DMA_NONE; - cmd->cmd_len = 6; - cmd->cmnd[0] = START_STOP_UNIT; - cmd->cmnd[4] = le | SSS_START; -} - -static int -ahd_linux_fallback(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) -{ - struct ahd_linux_target *targ; - struct ahd_initiator_tinfo *tinfo; - struct ahd_transinfo *goal; - struct ahd_tmode_tstate *tstate; - u_int width; - u_int period; - u_int offset; - u_int ppr_options; - u_int cur_speed; - u_int wide_speed; - u_int narrow_speed; - u_int fallback_speed; - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - ahd_print_devinfo(ahd, devinfo); - printf("Trying to fallback\n"); - } -#endif - targ = ahd->platform_data->targets[devinfo->target_offset]; - tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, - devinfo->our_scsiid, - devinfo->target, &tstate); - goal = &tinfo->goal; - width = goal->width; - period = goal->period; - offset = goal->offset; - ppr_options = goal->ppr_options; - if (offset == 0) - period = AHD_ASYNC_XFER_PERIOD; - if (targ->dv_next_narrow_period == 0) - targ->dv_next_narrow_period = MAX(period, AHD_SYNCRATE_ULTRA2); - if (targ->dv_next_wide_period == 0) - targ->dv_next_wide_period = period; - if (targ->dv_max_width == 0) - targ->dv_max_width = width; - if (targ->dv_max_ppr_options == 0) - targ->dv_max_ppr_options = ppr_options; - if (targ->dv_last_ppr_options == 0) - targ->dv_last_ppr_options = ppr_options; - - cur_speed = aic_calc_speed(width, period, offset, AHD_SYNCRATE_MIN); - wide_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_16_BIT, - targ->dv_next_wide_period, - MAX_OFFSET, AHD_SYNCRATE_MIN); - narrow_speed = aic_calc_speed(MSG_EXT_WDTR_BUS_8_BIT, - targ->dv_next_narrow_period, - MAX_OFFSET, AHD_SYNCRATE_MIN); - fallback_speed = aic_calc_speed(width, period+1, offset, - AHD_SYNCRATE_MIN); -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - printf("cur_speed= %d, wide_speed= %d, narrow_speed= %d, " - "fallback_speed= %d\n", cur_speed, wide_speed, - narrow_speed, fallback_speed); - } -#endif - - if (cur_speed > 160000) { - /* - * Paced/DT/IU_REQ only transfer speeds. All we - * can do is fallback in terms of syncrate. - */ - period++; - } else if (cur_speed > 80000) { - if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - /* - * Try without IU_REQ as it may be confusing - * an expander. - */ - ppr_options &= ~MSG_EXT_PPR_IU_REQ; - } else { - /* - * Paced/DT only transfer speeds. All we - * can do is fallback in terms of syncrate. - */ - period++; - ppr_options = targ->dv_max_ppr_options; - } - } else if (cur_speed > 3300) { - /* - * In this range we the following - * options ordered from highest to - * lowest desireability: - * - * o Wide/DT - * o Wide/non-DT - * o Narrow at a potentally higher sync rate. - * - * All modes are tested with and without IU_REQ - * set since using IUs may confuse an expander. + * We allow the OS to queue 2 untagged transactions to + * us at any time even though we can only execute them + * serially on the controller/device. This should + * remove some latency. */ - if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - - ppr_options &= ~MSG_EXT_PPR_IU_REQ; - } else if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) { - /* - * Try going non-DT. - */ - ppr_options = targ->dv_max_ppr_options; - ppr_options &= ~MSG_EXT_PPR_DT_REQ; - } else if (targ->dv_last_ppr_options != 0) { - /* - * Try without QAS or any other PPR options. - * We may need a non-PPR message to work with - * an expander. We look at the "last PPR options" - * so we will perform this fallback even if the - * target responded to our PPR negotiation with - * no option bits set. - */ - ppr_options = 0; - } else if (width == MSG_EXT_WDTR_BUS_16_BIT) { - /* - * If the next narrow speed is greater than - * the next wide speed, fallback to narrow. - * Otherwise fallback to the next DT/Wide setting. - * The narrow async speed will always be smaller - * than the wide async speed, so handle this case - * specifically. - */ - ppr_options = targ->dv_max_ppr_options; - if (narrow_speed > fallback_speed - || period >= AHD_ASYNC_XFER_PERIOD) { - targ->dv_next_wide_period = period+1; - width = MSG_EXT_WDTR_BUS_8_BIT; - period = targ->dv_next_narrow_period; - } else { - period++; - } - } else if ((ahd->features & AHD_WIDE) != 0 - && targ->dv_max_width != 0 - && wide_speed >= fallback_speed - && (targ->dv_next_wide_period <= AHD_ASYNC_XFER_PERIOD - || period >= AHD_ASYNC_XFER_PERIOD)) { - - /* - * We are narrow. Try falling back - * to the next wide speed with - * all supported ppr options set. - */ - targ->dv_next_narrow_period = period+1; - width = MSG_EXT_WDTR_BUS_16_BIT; - period = targ->dv_next_wide_period; - ppr_options = targ->dv_max_ppr_options; - } else { - /* Only narrow fallback is allowed. */ - period++; - ppr_options = targ->dv_max_ppr_options; - } - } else { - return (-1); - } - offset = MAX_OFFSET; - ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_PACED); - ahd_set_width(ahd, devinfo, width, AHD_TRANS_GOAL, FALSE); - if (period == 0) { - period = 0; - offset = 0; - ppr_options = 0; - if (width == MSG_EXT_WDTR_BUS_8_BIT) - targ->dv_next_narrow_period = AHD_ASYNC_XFER_PERIOD; - else - targ->dv_next_wide_period = AHD_ASYNC_XFER_PERIOD; + scsi_adjust_queue_depth(sdev, + /*NON-TAGGED*/0, + /*queue depth*/2); + break; } - ahd_set_syncrate(ahd, devinfo, period, offset, - ppr_options, AHD_TRANS_GOAL, FALSE); - targ->dv_last_ppr_options = ppr_options; - return (0); } -static void -ahd_linux_dv_timeout(struct scsi_cmnd *cmd) -{ - struct ahd_softc *ahd; - struct scb *scb; - u_long flags; - - ahd = *((struct ahd_softc **)cmd->device->host->hostdata); - ahd_lock(ahd, &flags); - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) { - printf("%s: Timeout while doing DV command %x.\n", - ahd_name(ahd), cmd->cmnd[0]); - ahd_dump_card_state(ahd); - } -#endif - - /* - * Guard against "done race". No action is - * required if we just completed. - */ - if ((scb = (struct scb *)cmd->host_scribble) == NULL) { - ahd_unlock(ahd, &flags); - return; - } - - /* - * Command has not completed. Mark this - * SCB as having failing status prior to - * resetting the bus, so we get the correct - * error code. - */ - if ((scb->flags & SCB_SENSE) != 0) - ahd_set_transaction_status(scb, CAM_AUTOSENSE_FAIL); - else - ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT); - ahd_reset_channel(ahd, cmd->device->channel + 'A', /*initiate*/TRUE); - - /* - * Add a minimal bus settle delay for devices that are slow to - * respond after bus resets. - */ - ahd_freeze_simq(ahd); - init_timer(&ahd->platform_data->reset_timer); - ahd->platform_data->reset_timer.data = (u_long)ahd; - ahd->platform_data->reset_timer.expires = jiffies + HZ / 2; - ahd->platform_data->reset_timer.function = - (ahd_linux_callback_t *)ahd_release_simq; - add_timer(&ahd->platform_data->reset_timer); - if (ahd_linux_next_device_to_run(ahd) != NULL) - ahd_schedule_runq(ahd); - ahd_linux_run_complete_queue(ahd); - ahd_unlock(ahd, &flags); -} - -static void -ahd_linux_dv_complete(struct scsi_cmnd *cmd) -{ - struct ahd_softc *ahd; - - ahd = *((struct ahd_softc **)cmd->device->host->hostdata); - - /* Delete the DV timer before it goes off! */ - scsi_delete_timer(cmd); - -#ifdef AHD_DEBUG - if (ahd_debug & AHD_SHOW_DV) - printf("%s:%c:%d: Command completed, status= 0x%x\n", - ahd_name(ahd), cmd->device->channel, cmd->device->id, - cmd->result); -#endif - - /* Wake up the state machine */ - up(&ahd->platform_data->dv_cmd_sem); -} - -static void -ahd_linux_generate_dv_pattern(struct ahd_linux_target *targ) +int +ahd_platform_abort_scbs(struct ahd_softc *ahd, int target, char channel, + int lun, u_int tag, role_t role, uint32_t status) { - uint16_t b; - u_int i; - u_int j; - - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - targ->dv_buffer = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK); - if (targ->dv_buffer1 != NULL) - free(targ->dv_buffer1, M_DEVBUF); - targ->dv_buffer1 = malloc(targ->dv_echo_size, M_DEVBUF, M_WAITOK); - - i = 0; - - b = 0x0001; - for (j = 0 ; i < targ->dv_echo_size; j++) { - if (j < 32) { - /* - * 32bytes of sequential numbers. - */ - targ->dv_buffer[i++] = j & 0xff; - } else if (j < 48) { - /* - * 32bytes of repeating 0x0000, 0xffff. - */ - targ->dv_buffer[i++] = (j & 0x02) ? 0xff : 0x00; - } else if (j < 64) { - /* - * 32bytes of repeating 0x5555, 0xaaaa. - */ - targ->dv_buffer[i++] = (j & 0x02) ? 0xaa : 0x55; - } else { - /* - * Remaining buffer is filled with a repeating - * patter of: - * - * 0xffff - * ~0x0001 << shifted once in each loop. - */ - if (j & 0x02) { - if (j & 0x01) { - targ->dv_buffer[i++] = ~(b >> 8) & 0xff; - b <<= 1; - if (b == 0x0000) - b = 0x0001; - } else { - targ->dv_buffer[i++] = (~b & 0xff); - } - } else { - targ->dv_buffer[i++] = 0xff; - } - } - } + return 0; } static u_int @@ -3800,100 +1393,23 @@ ahd_linux_user_tagdepth(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) return (tags); } -static u_int -ahd_linux_user_dv_setting(struct ahd_softc *ahd) -{ - static int warned_user; - int dv; - - if (ahd->unit >= NUM_ELEMENTS(aic79xx_dv_settings)) { - - if (warned_user == 0) { - printf(KERN_WARNING -"aic79xx: WARNING: Insufficient dv settings instances\n" -"aic79xx: for installed controllers. Using defaults\n" -"aic79xx: Please update the aic79xx_dv_settings array in" -"aic79xx: the aic79xx_osm.c source file.\n"); - warned_user++; - } - dv = -1; - } else { - - dv = aic79xx_dv_settings[ahd->unit]; - } - - if (dv < 0) { - /* - * Apply the default. - */ - dv = 1; - if (ahd->seep_config != 0) - dv = (ahd->seep_config->bios_control & CFENABLEDV); - } - return (dv); -} - -static void -ahd_linux_setup_user_rd_strm_settings(struct ahd_softc *ahd) -{ - static int warned_user; - u_int rd_strm_mask; - u_int target_id; - - /* - * If we have specific read streaming info for this controller, - * apply it. Otherwise use the defaults. - */ - if (ahd->unit >= NUM_ELEMENTS(aic79xx_rd_strm_info)) { - - if (warned_user == 0) { - - printf(KERN_WARNING -"aic79xx: WARNING: Insufficient rd_strm instances\n" -"aic79xx: for installed controllers. Using defaults\n" -"aic79xx: Please update the aic79xx_rd_strm_info array\n" -"aic79xx: in the aic79xx_osm.c source file.\n"); - warned_user++; - } - rd_strm_mask = AIC79XX_CONFIGED_RD_STRM; - } else { - - rd_strm_mask = aic79xx_rd_strm_info[ahd->unit]; - } - for (target_id = 0; target_id < 16; target_id++) { - struct ahd_devinfo devinfo; - struct ahd_initiator_tinfo *tinfo; - struct ahd_tmode_tstate *tstate; - - tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, - target_id, &tstate); - ahd_compile_devinfo(&devinfo, ahd->our_id, target_id, - CAM_LUN_WILDCARD, 'A', ROLE_INITIATOR); - tinfo->user.ppr_options &= ~MSG_EXT_PPR_RD_STRM; - if ((rd_strm_mask & devinfo.target_mask) != 0) - tinfo->user.ppr_options |= MSG_EXT_PPR_RD_STRM; - } -} - /* * Determines the queue depth for a given device. */ static void -ahd_linux_device_queue_depth(struct ahd_softc *ahd, - struct ahd_linux_device *dev) +ahd_linux_device_queue_depth(struct scsi_device *sdev) { struct ahd_devinfo devinfo; u_int tags; + struct ahd_softc *ahd = *((struct ahd_softc **)sdev->host->hostdata); ahd_compile_devinfo(&devinfo, ahd->our_id, - dev->target->target, dev->lun, - dev->target->channel == 0 ? 'A' : 'B', + sdev->sdev_target->id, sdev->lun, + sdev->sdev_target->channel == 0 ? 'A' : 'B', ROLE_INITIATOR); tags = ahd_linux_user_tagdepth(ahd, &devinfo); - if (tags != 0 - && dev->scsi_device != NULL - && dev->scsi_device->tagged_supported != 0) { + if (tags != 0 && sdev->tagged_supported != 0) { ahd_set_tags(ahd, &devinfo, AHD_QUEUE_TAGGED); ahd_print_devinfo(ahd, &devinfo); @@ -3903,11 +1419,10 @@ ahd_linux_device_queue_depth(struct ahd_softc *ahd, } } -static void -ahd_linux_run_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev) +static int +ahd_linux_run_command(struct ahd_softc *ahd, struct ahd_linux_device *dev, + struct scsi_cmnd *cmd) { - struct ahd_cmd *acmd; - struct scsi_cmnd *cmd; struct scb *scb; struct hardware_scb *hscb; struct ahd_initiator_tinfo *tinfo; @@ -3915,157 +1430,122 @@ ahd_linux_run_device_queue(struct ahd_softc *ahd, struct ahd_linux_device *dev) u_int col_idx; uint16_t mask; - if ((dev->flags & AHD_DEV_ON_RUN_LIST) != 0) - panic("running device on run list"); - - while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL - && dev->openings > 0 && dev->qfrozen == 0) { - - /* - * Schedule us to run later. The only reason we are not - * running is because the whole controller Q is frozen. - */ - if (ahd->platform_data->qfrozen != 0 - && AHD_DV_SIMQ_FROZEN(ahd) == 0) { - - TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, - dev, links); - dev->flags |= AHD_DEV_ON_RUN_LIST; - return; - } - - cmd = &acmd_scsi_cmd(acmd); + /* + * Get an scb to use. + */ + tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, + cmd->device->id, &tstate); + if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0 + || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { + col_idx = AHD_NEVER_COL_IDX; + } else { + col_idx = AHD_BUILD_COL_IDX(cmd->device->id, + cmd->device->lun); + } + if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) { + ahd->flags |= AHD_RESOURCE_SHORTAGE; + return SCSI_MLQUEUE_HOST_BUSY; + } - /* - * Get an scb to use. - */ - tinfo = ahd_fetch_transinfo(ahd, 'A', ahd->our_id, - cmd->device->id, &tstate); - if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) == 0 - || (tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) { - col_idx = AHD_NEVER_COL_IDX; - } else { - col_idx = AHD_BUILD_COL_IDX(cmd->device->id, - cmd->device->lun); - } - if ((scb = ahd_get_scb(ahd, col_idx)) == NULL) { - TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, - dev, links); - dev->flags |= AHD_DEV_ON_RUN_LIST; - ahd->flags |= AHD_RESOURCE_SHORTAGE; - return; - } - TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe); - scb->io_ctx = cmd; - scb->platform_data->dev = dev; - hscb = scb->hscb; - cmd->host_scribble = (char *)scb; + scb->io_ctx = cmd; + scb->platform_data->dev = dev; + hscb = scb->hscb; + cmd->host_scribble = (char *)scb; - /* - * Fill out basics of the HSCB. - */ - hscb->control = 0; - hscb->scsiid = BUILD_SCSIID(ahd, cmd); - hscb->lun = cmd->device->lun; - scb->hscb->task_management = 0; - mask = SCB_GET_TARGET_MASK(ahd, scb); + /* + * Fill out basics of the HSCB. + */ + hscb->control = 0; + hscb->scsiid = BUILD_SCSIID(ahd, cmd); + hscb->lun = cmd->device->lun; + scb->hscb->task_management = 0; + mask = SCB_GET_TARGET_MASK(ahd, scb); - if ((ahd->user_discenable & mask) != 0) - hscb->control |= DISCENB; + if ((ahd->user_discenable & mask) != 0) + hscb->control |= DISCENB; - if (AHD_DV_CMD(cmd) != 0) - scb->flags |= SCB_SILENT; + if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) + scb->flags |= SCB_PACKETIZED; - if ((tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ) != 0) - scb->flags |= SCB_PACKETIZED; + if ((tstate->auto_negotiate & mask) != 0) { + scb->flags |= SCB_AUTO_NEGOTIATE; + scb->hscb->control |= MK_MESSAGE; + } - if ((tstate->auto_negotiate & mask) != 0) { - scb->flags |= SCB_AUTO_NEGOTIATE; - scb->hscb->control |= MK_MESSAGE; - } + if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) { + int msg_bytes; + uint8_t tag_msgs[2]; - if ((dev->flags & (AHD_DEV_Q_TAGGED|AHD_DEV_Q_BASIC)) != 0) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - int msg_bytes; - uint8_t tag_msgs[2]; - - msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs); - if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) { - hscb->control |= tag_msgs[0]; - if (tag_msgs[0] == MSG_ORDERED_TASK) - dev->commands_since_idle_or_otag = 0; - } else -#endif - if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH - && (dev->flags & AHD_DEV_Q_TAGGED) != 0) { - hscb->control |= MSG_ORDERED_TASK; + msg_bytes = scsi_populate_tag_msg(cmd, tag_msgs); + if (msg_bytes && tag_msgs[0] != MSG_SIMPLE_TASK) { + hscb->control |= tag_msgs[0]; + if (tag_msgs[0] == MSG_ORDERED_TASK) dev->commands_since_idle_or_otag = 0; - } else { - hscb->control |= MSG_SIMPLE_TASK; - } + } else + if (dev->commands_since_idle_or_otag == AHD_OTAG_THRESH + && (dev->flags & AHD_DEV_Q_TAGGED) != 0) { + hscb->control |= MSG_ORDERED_TASK; + dev->commands_since_idle_or_otag = 0; + } else { + hscb->control |= MSG_SIMPLE_TASK; } + } - hscb->cdb_len = cmd->cmd_len; - memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len); - - scb->sg_count = 0; - ahd_set_residual(scb, 0); - ahd_set_sense_residual(scb, 0); - if (cmd->use_sg != 0) { - void *sg; - struct scatterlist *cur_seg; - u_int nseg; - int dir; - - cur_seg = (struct scatterlist *)cmd->request_buffer; - dir = cmd->sc_data_direction; - nseg = pci_map_sg(ahd->dev_softc, cur_seg, - cmd->use_sg, dir); - scb->platform_data->xfer_len = 0; - for (sg = scb->sg_list; nseg > 0; nseg--, cur_seg++) { - dma_addr_t addr; - bus_size_t len; - - addr = sg_dma_address(cur_seg); - len = sg_dma_len(cur_seg); - scb->platform_data->xfer_len += len; - sg = ahd_sg_setup(ahd, scb, sg, addr, len, - /*last*/nseg == 1); - } - } else if (cmd->request_bufflen != 0) { - void *sg; + hscb->cdb_len = cmd->cmd_len; + memcpy(hscb->shared_data.idata.cdb, cmd->cmnd, hscb->cdb_len); + + scb->platform_data->xfer_len = 0; + ahd_set_residual(scb, 0); + ahd_set_sense_residual(scb, 0); + scb->sg_count = 0; + if (cmd->use_sg != 0) { + void *sg; + struct scatterlist *cur_seg; + u_int nseg; + int dir; + + cur_seg = (struct scatterlist *)cmd->request_buffer; + dir = cmd->sc_data_direction; + nseg = pci_map_sg(ahd->dev_softc, cur_seg, + cmd->use_sg, dir); + scb->platform_data->xfer_len = 0; + for (sg = scb->sg_list; nseg > 0; nseg--, cur_seg++) { dma_addr_t addr; - int dir; - - sg = scb->sg_list; - dir = cmd->sc_data_direction; - addr = pci_map_single(ahd->dev_softc, - cmd->request_buffer, - cmd->request_bufflen, dir); - scb->platform_data->xfer_len = cmd->request_bufflen; - scb->platform_data->buf_busaddr = addr; - sg = ahd_sg_setup(ahd, scb, sg, addr, - cmd->request_bufflen, /*last*/TRUE); - } + bus_size_t len; - LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); - dev->openings--; - dev->active++; - dev->commands_issued++; - - /* Update the error counting bucket and dump if needed */ - if (dev->target->cmds_since_error) { - dev->target->cmds_since_error++; - if (dev->target->cmds_since_error > - AHD_LINUX_ERR_THRESH) - dev->target->cmds_since_error = 0; + addr = sg_dma_address(cur_seg); + len = sg_dma_len(cur_seg); + scb->platform_data->xfer_len += len; + sg = ahd_sg_setup(ahd, scb, sg, addr, len, + /*last*/nseg == 1); } + } else if (cmd->request_bufflen != 0) { + void *sg; + dma_addr_t addr; + int dir; - if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0) - dev->commands_since_idle_or_otag++; - scb->flags |= SCB_ACTIVE; - ahd_queue_scb(ahd, scb); + sg = scb->sg_list; + dir = cmd->sc_data_direction; + addr = pci_map_single(ahd->dev_softc, + cmd->request_buffer, + cmd->request_bufflen, dir); + scb->platform_data->xfer_len = cmd->request_bufflen; + scb->platform_data->buf_busaddr = addr; + sg = ahd_sg_setup(ahd, scb, sg, addr, + cmd->request_bufflen, /*last*/TRUE); } + + LIST_INSERT_HEAD(&ahd->pending_scbs, scb, pending_links); + dev->openings--; + dev->active++; + dev->commands_issued++; + + if ((dev->flags & AHD_DEV_PERIODIC_OTAG) != 0) + dev->commands_since_idle_or_otag++; + scb->flags |= SCB_ACTIVE; + ahd_queue_scb(ahd, scb); + + return 0; } /* @@ -4081,9 +1561,6 @@ ahd_linux_isr(int irq, void *dev_id, struct pt_regs * regs) ahd = (struct ahd_softc *) dev_id; ahd_lock(ahd, &flags); ours = ahd_intr(ahd); - if (ahd_linux_next_device_to_run(ahd) != NULL) - ahd_schedule_runq(ahd); - ahd_linux_run_complete_queue(ahd); ahd_unlock(ahd, &flags); return IRQ_RETVAL(ours); } @@ -4092,111 +1569,6 @@ void ahd_platform_flushwork(struct ahd_softc *ahd) { - while (ahd_linux_run_complete_queue(ahd) != NULL) - ; -} - -static struct ahd_linux_target* -ahd_linux_alloc_target(struct ahd_softc *ahd, u_int channel, u_int target) -{ - struct ahd_linux_target *targ; - - targ = malloc(sizeof(*targ), M_DEVBUF, M_NOWAIT); - if (targ == NULL) - return (NULL); - memset(targ, 0, sizeof(*targ)); - targ->channel = channel; - targ->target = target; - targ->ahd = ahd; - targ->flags = AHD_DV_REQUIRED; - ahd->platform_data->targets[target] = targ; - return (targ); -} - -static void -ahd_linux_free_target(struct ahd_softc *ahd, struct ahd_linux_target *targ) -{ - struct ahd_devinfo devinfo; - struct ahd_initiator_tinfo *tinfo; - struct ahd_tmode_tstate *tstate; - u_int our_id; - u_int target_offset; - char channel; - - /* - * Force a negotiation to async/narrow on any - * future command to this device unless a bus - * reset occurs between now and that command. - */ - channel = 'A' + targ->channel; - our_id = ahd->our_id; - target_offset = targ->target; - tinfo = ahd_fetch_transinfo(ahd, channel, our_id, - targ->target, &tstate); - ahd_compile_devinfo(&devinfo, our_id, targ->target, CAM_LUN_WILDCARD, - channel, ROLE_INITIATOR); - ahd_set_syncrate(ahd, &devinfo, 0, 0, 0, - AHD_TRANS_GOAL, /*paused*/FALSE); - ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT, - AHD_TRANS_GOAL, /*paused*/FALSE); - ahd_update_neg_request(ahd, &devinfo, tstate, tinfo, AHD_NEG_ALWAYS); - ahd->platform_data->targets[target_offset] = NULL; - if (targ->inq_data != NULL) - free(targ->inq_data, M_DEVBUF); - if (targ->dv_buffer != NULL) - free(targ->dv_buffer, M_DEVBUF); - if (targ->dv_buffer1 != NULL) - free(targ->dv_buffer1, M_DEVBUF); - free(targ, M_DEVBUF); -} - -static struct ahd_linux_device* -ahd_linux_alloc_device(struct ahd_softc *ahd, - struct ahd_linux_target *targ, u_int lun) -{ - struct ahd_linux_device *dev; - - dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT); - if (dev == NULL) - return (NULL); - memset(dev, 0, sizeof(*dev)); - init_timer(&dev->timer); - TAILQ_INIT(&dev->busyq); - dev->flags = AHD_DEV_UNCONFIGURED; - dev->lun = lun; - dev->target = targ; - - /* - * We start out life using untagged - * transactions of which we allow one. - */ - dev->openings = 1; - - /* - * Set maxtags to 0. This will be changed if we - * later determine that we are dealing with - * a tagged queuing capable device. - */ - dev->maxtags = 0; - - targ->refcount++; - targ->devices[lun] = dev; - return (dev); -} - -static void -ahd_linux_free_device(struct ahd_softc *ahd, struct ahd_linux_device *dev) -{ - struct ahd_linux_target *targ; - - del_timer(&dev->timer); - targ = dev->target; - targ->devices[dev->lun] = NULL; - free(dev, M_DEVBUF); - targ->refcount--; - if (targ->refcount == 0 - && (targ->flags & AHD_DV_REQUIRED) == 0) - ahd_linux_free_target(ahd, targ); } void @@ -4207,10 +1579,14 @@ ahd_send_async(struct ahd_softc *ahd, char channel, case AC_TRANSFER_NEG: { char buf[80]; + struct scsi_target *starget; struct ahd_linux_target *targ; struct info_str info; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; + unsigned int target_ppr_options; + + BUG_ON(target == CAM_TARGET_WILDCARD); info.buffer = buf; info.length = sizeof(buf); @@ -4234,58 +1610,47 @@ ahd_send_async(struct ahd_softc *ahd, char channel, * Don't bother reporting results that * are identical to those last reported. */ - targ = ahd->platform_data->targets[target]; - if (targ == NULL) + starget = ahd->platform_data->starget[target]; + if (starget == NULL) break; - if (tinfo->curr.period == targ->last_tinfo.period - && tinfo->curr.width == targ->last_tinfo.width - && tinfo->curr.offset == targ->last_tinfo.offset - && tinfo->curr.ppr_options == targ->last_tinfo.ppr_options) + targ = scsi_transport_target_data(starget); + + target_ppr_options = + (spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0) + + (spi_qas(starget) ? MSG_EXT_PPR_QAS_REQ : 0) + + (spi_iu(starget) ? MSG_EXT_PPR_IU_REQ : 0) + + (spi_rd_strm(starget) ? MSG_EXT_PPR_RD_STRM : 0) + + (spi_pcomp_en(starget) ? MSG_EXT_PPR_PCOMP_EN : 0) + + (spi_rti(starget) ? MSG_EXT_PPR_RTI : 0) + + (spi_wr_flow(starget) ? MSG_EXT_PPR_WR_FLOW : 0) + + (spi_hold_mcs(starget) ? MSG_EXT_PPR_HOLD_MCS : 0); + + if (tinfo->curr.period == spi_period(starget) + && tinfo->curr.width == spi_width(starget) + && tinfo->curr.offset == spi_offset(starget) + && tinfo->curr.ppr_options == target_ppr_options) if (bootverbose == 0) break; - targ->last_tinfo.period = tinfo->curr.period; - targ->last_tinfo.width = tinfo->curr.width; - targ->last_tinfo.offset = tinfo->curr.offset; - targ->last_tinfo.ppr_options = tinfo->curr.ppr_options; - - printf("(%s:%c:", ahd_name(ahd), channel); - if (target == CAM_TARGET_WILDCARD) - printf("*): "); - else - printf("%d): ", target); - ahd_format_transinfo(&info, &tinfo->curr); - if (info.pos < info.length) - *info.buffer = '\0'; - else - buf[info.length - 1] = '\0'; - printf("%s", buf); + spi_period(starget) = tinfo->curr.period; + spi_width(starget) = tinfo->curr.width; + spi_offset(starget) = tinfo->curr.offset; + spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0; + spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0; + spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0; + spi_rd_strm(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RD_STRM ? 1 : 0; + spi_pcomp_en(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_PCOMP_EN ? 1 : 0; + spi_rti(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_RTI ? 1 : 0; + spi_wr_flow(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_WR_FLOW ? 1 : 0; + spi_hold_mcs(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_HOLD_MCS ? 1 : 0; + spi_display_xfer_agreement(starget); break; } case AC_SENT_BDR: { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) WARN_ON(lun != CAM_LUN_WILDCARD); scsi_report_device_reset(ahd->platform_data->host, channel - 'A', target); -#else - Scsi_Device *scsi_dev; - - /* - * Find the SCSI device associated with this - * request and indicate that a UA is expected. - */ - for (scsi_dev = ahd->platform_data->host->host_queue; - scsi_dev != NULL; scsi_dev = scsi_dev->next) { - if (channel - 'A' == scsi_dev->channel - && target == scsi_dev->id - && (lun == CAM_LUN_WILDCARD - || lun == scsi_dev->lun)) { - scsi_dev->was_reset = 1; - scsi_dev->expecting_cc_ua = 1; - } - } -#endif break; } case AC_BUS_RESET: @@ -4305,7 +1670,7 @@ ahd_send_async(struct ahd_softc *ahd, char channel, void ahd_done(struct ahd_softc *ahd, struct scb *scb) { - Scsi_Cmnd *cmd; + struct scsi_cmnd *cmd; struct ahd_linux_device *dev; if ((scb->flags & SCB_ACTIVE) == 0) { @@ -4373,19 +1738,8 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb) ahd_set_transaction_status(scb, CAM_REQ_CMP); } } else if (ahd_get_transaction_status(scb) == CAM_SCSI_STATUS_ERROR) { - ahd_linux_handle_scsi_status(ahd, dev, scb); - } else if (ahd_get_transaction_status(scb) == CAM_SEL_TIMEOUT) { - dev->flags |= AHD_DEV_UNCONFIGURED; - if (AHD_DV_CMD(cmd) == FALSE) - dev->target->flags &= ~AHD_DV_REQUIRED; + ahd_linux_handle_scsi_status(ahd, cmd->device, scb); } - /* - * Start DV for devices that require it assuming the first command - * sent does not result in a selection timeout. - */ - if (ahd_get_transaction_status(scb) != CAM_SEL_TIMEOUT - && (dev->target->flags & AHD_DV_REQUIRED) != 0) - ahd_linux_start_dv(ahd); if (dev->openings == 1 && ahd_get_transaction_status(scb) == CAM_REQ_CMP @@ -4406,47 +1760,32 @@ ahd_done(struct ahd_softc *ahd, struct scb *scb) if (dev->active == 0) dev->commands_since_idle_or_otag = 0; - if (TAILQ_EMPTY(&dev->busyq)) { - if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0 - && dev->active == 0 - && (dev->flags & AHD_DEV_TIMER_ACTIVE) == 0) - ahd_linux_free_device(ahd, dev); - } else if ((dev->flags & AHD_DEV_ON_RUN_LIST) == 0) { - TAILQ_INSERT_TAIL(&ahd->platform_data->device_runq, dev, links); - dev->flags |= AHD_DEV_ON_RUN_LIST; - } - if ((scb->flags & SCB_RECOVERY_SCB) != 0) { printf("Recovery SCB completes\n"); if (ahd_get_transaction_status(scb) == CAM_BDR_SENT || ahd_get_transaction_status(scb) == CAM_REQ_ABORTED) ahd_set_transaction_status(scb, CAM_CMD_TIMEOUT); - if ((scb->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) { - scb->platform_data->flags &= ~AHD_SCB_UP_EH_SEM; + if ((ahd->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) { + ahd->platform_data->flags &= ~AHD_SCB_UP_EH_SEM; up(&ahd->platform_data->eh_sem); } } ahd_free_scb(ahd, scb); ahd_linux_queue_cmd_complete(ahd, cmd); - - if ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_EMPTY) != 0 - && LIST_FIRST(&ahd->pending_scbs) == NULL) { - ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_EMPTY; - up(&ahd->platform_data->dv_sem); - } } static void ahd_linux_handle_scsi_status(struct ahd_softc *ahd, - struct ahd_linux_device *dev, struct scb *scb) + struct scsi_device *sdev, struct scb *scb) { struct ahd_devinfo devinfo; + struct ahd_linux_device *dev = scsi_transport_device_data(sdev); ahd_compile_devinfo(&devinfo, ahd->our_id, - dev->target->target, dev->lun, - dev->target->channel == 0 ? 'A' : 'B', + sdev->sdev_target->id, sdev->lun, + sdev->sdev_target->channel == 0 ? 'A' : 'B', ROLE_INITIATOR); /* @@ -4465,7 +1804,7 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd, case SCSI_STATUS_CHECK_COND: case SCSI_STATUS_CMD_TERMINATED: { - Scsi_Cmnd *cmd; + struct scsi_cmnd *cmd; /* * Copy sense information to the OS's cmd @@ -4518,7 +1857,6 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd, break; } case SCSI_STATUS_QUEUE_FULL: - { /* * By the time the core driver has returned this * command, all other commands that were queued @@ -4579,98 +1917,23 @@ ahd_linux_handle_scsi_status(struct ahd_softc *ahd, (dev->flags & AHD_DEV_Q_BASIC) ? AHD_QUEUE_BASIC : AHD_QUEUE_TAGGED); ahd_set_scsi_status(scb, SCSI_STATUS_BUSY); - /* FALLTHROUGH */ - } - case SCSI_STATUS_BUSY: - /* - * Set a short timer to defer sending commands for - * a bit since Linux will not delay in this case. - */ - if ((dev->flags & AHD_DEV_TIMER_ACTIVE) != 0) { - printf("%s:%c:%d: Device Timer still active during " - "busy processing\n", ahd_name(ahd), - dev->target->channel, dev->target->target); - break; - } - dev->flags |= AHD_DEV_TIMER_ACTIVE; - dev->qfrozen++; - init_timer(&dev->timer); - dev->timer.data = (u_long)dev; - dev->timer.expires = jiffies + (HZ/2); - dev->timer.function = ahd_linux_dev_timed_unfreeze; - add_timer(&dev->timer); - break; } } static void -ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, Scsi_Cmnd *cmd) +ahd_linux_queue_cmd_complete(struct ahd_softc *ahd, struct scsi_cmnd *cmd) { /* - * Typically, the complete queue has very few entries - * queued to it before the queue is emptied by - * ahd_linux_run_complete_queue, so sorting the entries - * by generation number should be inexpensive. - * We perform the sort so that commands that complete - * with an error are retuned in the order origionally - * queued to the controller so that any subsequent retries - * are performed in order. The underlying ahd routines do - * not guarantee the order that aborted commands will be - * returned to us. - */ - struct ahd_completeq *completeq; - struct ahd_cmd *list_cmd; - struct ahd_cmd *acmd; - - /* * Map CAM error codes into Linux Error codes. We * avoid the conversion so that the DV code has the * full error information available when making * state change decisions. */ - if (AHD_DV_CMD(cmd) == FALSE) { + { uint32_t status; u_int new_status; status = ahd_cmd_get_transaction_status(cmd); - if (status != CAM_REQ_CMP) { - struct ahd_linux_device *dev; - struct ahd_devinfo devinfo; - cam_status cam_status; - uint32_t action; - u_int scsi_status; - - dev = ahd_linux_get_device(ahd, cmd->device->channel, - cmd->device->id, - cmd->device->lun, - /*alloc*/FALSE); - - if (dev == NULL) - goto no_fallback; - - ahd_compile_devinfo(&devinfo, - ahd->our_id, - dev->target->target, dev->lun, - dev->target->channel == 0 ? 'A':'B', - ROLE_INITIATOR); - - scsi_status = ahd_cmd_get_scsi_status(cmd); - cam_status = ahd_cmd_get_transaction_status(cmd); - action = aic_error_action(cmd, dev->target->inq_data, - cam_status, scsi_status); - if ((action & SSQ_FALLBACK) != 0) { - - /* Update stats */ - dev->target->errors_detected++; - if (dev->target->cmds_since_error == 0) - dev->target->cmds_since_error++; - else { - dev->target->cmds_since_error = 0; - ahd_linux_fallback(ahd, &devinfo); - } - } - } -no_fallback: switch (status) { case CAM_REQ_INPROG: case CAM_REQ_CMP: @@ -4715,26 +1978,7 @@ no_fallback: new_status = DID_ERROR; break; case CAM_REQUEUE_REQ: - /* - * If we want the request requeued, make sure there - * are sufficent retries. In the old scsi error code, - * we used to be able to specify a result code that - * bypassed the retry count. Now we must use this - * hack. We also "fake" a check condition with - * a sense code of ABORTED COMMAND. This seems to - * evoke a retry even if this command is being sent - * via the eh thread. Ick! Ick! Ick! - */ - if (cmd->retries > 0) - cmd->retries--; - new_status = DID_OK; - ahd_cmd_set_scsi_status(cmd, SCSI_STATUS_CHECK_COND); - cmd->result |= (DRIVER_SENSE << 24); - memset(cmd->sense_buffer, 0, - sizeof(cmd->sense_buffer)); - cmd->sense_buffer[0] = SSD_ERRCODE_VALID - | SSD_CURRENT_ERROR; - cmd->sense_buffer[2] = SSD_KEY_ABORTED_COMMAND; + new_status = DID_REQUEUE; break; default: /* We should never get here */ @@ -4745,116 +1989,23 @@ no_fallback: ahd_cmd_set_transaction_status(cmd, new_status); } - completeq = &ahd->platform_data->completeq; - list_cmd = TAILQ_FIRST(completeq); - acmd = (struct ahd_cmd *)cmd; - while (list_cmd != NULL - && acmd_scsi_cmd(list_cmd).serial_number - < acmd_scsi_cmd(acmd).serial_number) - list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe); - if (list_cmd != NULL) - TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe); - else - TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe); + cmd->scsi_done(cmd); } static void -ahd_linux_filter_inquiry(struct ahd_softc *ahd, struct ahd_devinfo *devinfo) +ahd_linux_sem_timeout(u_long arg) { - struct scsi_inquiry_data *sid; - struct ahd_initiator_tinfo *tinfo; - struct ahd_transinfo *user; - struct ahd_transinfo *goal; - struct ahd_transinfo *curr; - struct ahd_tmode_tstate *tstate; - struct ahd_linux_device *dev; - u_int width; - u_int period; - u_int offset; - u_int ppr_options; - u_int trans_version; - u_int prot_version; - - /* - * Determine if this lun actually exists. If so, - * hold on to its corresponding device structure. - * If not, make sure we release the device and - * don't bother processing the rest of this inquiry - * command. - */ - dev = ahd_linux_get_device(ahd, devinfo->channel - 'A', - devinfo->target, devinfo->lun, - /*alloc*/TRUE); - - sid = (struct scsi_inquiry_data *)dev->target->inq_data; - if (SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) { - - dev->flags &= ~AHD_DEV_UNCONFIGURED; - } else { - dev->flags |= AHD_DEV_UNCONFIGURED; - return; - } + struct ahd_softc *ahd; + u_long s; - /* - * Update our notion of this device's transfer - * negotiation capabilities. - */ - tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, - devinfo->our_scsiid, - devinfo->target, &tstate); - user = &tinfo->user; - goal = &tinfo->goal; - curr = &tinfo->curr; - width = user->width; - period = user->period; - offset = user->offset; - ppr_options = user->ppr_options; - trans_version = user->transport_version; - prot_version = MIN(user->protocol_version, SID_ANSI_REV(sid)); + ahd = (struct ahd_softc *)arg; - /* - * Only attempt SPI3/4 once we've verified that - * the device claims to support SPI3/4 features. - */ - if (prot_version < SCSI_REV_2) - trans_version = SID_ANSI_REV(sid); - else - trans_version = SCSI_REV_2; - - if ((sid->flags & SID_WBus16) == 0) - width = MSG_EXT_WDTR_BUS_8_BIT; - if ((sid->flags & SID_Sync) == 0) { - period = 0; - offset = 0; - ppr_options = 0; - } - if ((sid->spi3data & SID_SPI_QAS) == 0) - ppr_options &= ~MSG_EXT_PPR_QAS_REQ; - if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0) - ppr_options &= MSG_EXT_PPR_QAS_REQ; - if ((sid->spi3data & SID_SPI_IUS) == 0) - ppr_options &= (MSG_EXT_PPR_DT_REQ - | MSG_EXT_PPR_QAS_REQ); - - if (prot_version > SCSI_REV_2 - && ppr_options != 0) - trans_version = user->transport_version; - - ahd_validate_width(ahd, /*tinfo limit*/NULL, &width, ROLE_UNKNOWN); - ahd_find_syncrate(ahd, &period, &ppr_options, AHD_SYNCRATE_MAX); - ahd_validate_offset(ahd, /*tinfo limit*/NULL, period, - &offset, width, ROLE_UNKNOWN); - if (offset == 0 || period == 0) { - period = 0; - offset = 0; - ppr_options = 0; + ahd_lock(ahd, &s); + if ((ahd->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) { + ahd->platform_data->flags &= ~AHD_SCB_UP_EH_SEM; + up(&ahd->platform_data->eh_sem); } - /* Apply our filtered user settings. */ - curr->transport_version = trans_version; - curr->protocol_version = prot_version; - ahd_set_width(ahd, devinfo, width, AHD_TRANS_GOAL, /*paused*/FALSE); - ahd_set_syncrate(ahd, devinfo, period, offset, ppr_options, - AHD_TRANS_GOAL, /*paused*/FALSE); + ahd_unlock(ahd, &s); } void @@ -4882,12 +2033,6 @@ ahd_release_simq(struct ahd_softc *ahd) if (ahd->platform_data->qfrozen == 0) { unblock_reqs = 1; } - if (AHD_DV_SIMQ_FROZEN(ahd) - && ((ahd->platform_data->flags & AHD_DV_WAIT_SIMQ_RELEASE) != 0)) { - ahd->platform_data->flags &= ~AHD_DV_WAIT_SIMQ_RELEASE; - up(&ahd->platform_data->dv_sem); - } - ahd_schedule_runq(ahd); ahd_unlock(ahd, &s); /* * There is still a race here. The mid-layer @@ -4899,118 +2044,743 @@ ahd_release_simq(struct ahd_softc *ahd) scsi_unblock_requests(ahd->platform_data->host); } -static void -ahd_linux_sem_timeout(u_long arg) +static int +ahd_linux_queue_recovery_cmd(struct scsi_cmnd *cmd, scb_flag flag) { - struct scb *scb; - struct ahd_softc *ahd; - u_long s; + struct ahd_softc *ahd; + struct ahd_linux_device *dev; + struct scb *pending_scb; + u_int saved_scbptr; + u_int active_scbptr; + u_int last_phase; + u_int saved_scsiid; + u_int cdb_byte; + int retval; + int was_paused; + int paused; + int wait; + int disconnected; + ahd_mode_state saved_modes; - scb = (struct scb *)arg; - ahd = scb->ahd_softc; - ahd_lock(ahd, &s); - if ((scb->platform_data->flags & AHD_SCB_UP_EH_SEM) != 0) { - scb->platform_data->flags &= ~AHD_SCB_UP_EH_SEM; - up(&ahd->platform_data->eh_sem); + pending_scb = NULL; + paused = FALSE; + wait = FALSE; + ahd = *(struct ahd_softc **)cmd->device->host->hostdata; + + printf("%s:%d:%d:%d: Attempting to queue a%s message:", + ahd_name(ahd), cmd->device->channel, + cmd->device->id, cmd->device->lun, + flag == SCB_ABORT ? "n ABORT" : " TARGET RESET"); + + printf("CDB:"); + for (cdb_byte = 0; cdb_byte < cmd->cmd_len; cdb_byte++) + printf(" 0x%x", cmd->cmnd[cdb_byte]); + printf("\n"); + + spin_lock_irq(&ahd->platform_data->spin_lock); + + /* + * First determine if we currently own this command. + * Start by searching the device queue. If not found + * there, check the pending_scb list. If not found + * at all, and the system wanted us to just abort the + * command, return success. + */ + dev = scsi_transport_device_data(cmd->device); + + if (dev == NULL) { + /* + * No target device for this command exists, + * so we must not still own the command. + */ + printf("%s:%d:%d:%d: Is not an active device\n", + ahd_name(ahd), cmd->device->channel, cmd->device->id, + cmd->device->lun); + retval = SUCCESS; + goto no_cmd; } - ahd_unlock(ahd, &s); + + /* + * See if we can find a matching cmd in the pending list. + */ + LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { + if (pending_scb->io_ctx == cmd) + break; + } + + if (pending_scb == NULL && flag == SCB_DEVICE_RESET) { + + /* Any SCB for this device will do for a target reset */ + LIST_FOREACH(pending_scb, &ahd->pending_scbs, pending_links) { + if (ahd_match_scb(ahd, pending_scb, cmd->device->id, + cmd->device->channel + 'A', + CAM_LUN_WILDCARD, + SCB_LIST_NULL, ROLE_INITIATOR) == 0) + break; + } + } + + if (pending_scb == NULL) { + printf("%s:%d:%d:%d: Command not found\n", + ahd_name(ahd), cmd->device->channel, cmd->device->id, + cmd->device->lun); + goto no_cmd; + } + + if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) { + /* + * We can't queue two recovery actions using the same SCB + */ + retval = FAILED; + goto done; + } + + /* + * Ensure that the card doesn't do anything + * behind our back. Also make sure that we + * didn't "just" miss an interrupt that would + * affect this cmd. + */ + was_paused = ahd_is_paused(ahd); + ahd_pause_and_flushwork(ahd); + paused = TRUE; + + if ((pending_scb->flags & SCB_ACTIVE) == 0) { + printf("%s:%d:%d:%d: Command already completed\n", + ahd_name(ahd), cmd->device->channel, cmd->device->id, + cmd->device->lun); + goto no_cmd; + } + + printf("%s: At time of recovery, card was %spaused\n", + ahd_name(ahd), was_paused ? "" : "not "); + ahd_dump_card_state(ahd); + + disconnected = TRUE; + if (flag == SCB_ABORT) { + if (ahd_search_qinfifo(ahd, cmd->device->id, + cmd->device->channel + 'A', + cmd->device->lun, + pending_scb->hscb->tag, + ROLE_INITIATOR, CAM_REQ_ABORTED, + SEARCH_COMPLETE) > 0) { + printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n", + ahd_name(ahd), cmd->device->channel, + cmd->device->id, cmd->device->lun); + retval = SUCCESS; + goto done; + } + } else if (ahd_search_qinfifo(ahd, cmd->device->id, + cmd->device->channel + 'A', + cmd->device->lun, pending_scb->hscb->tag, + ROLE_INITIATOR, /*status*/0, + SEARCH_COUNT) > 0) { + disconnected = FALSE; + } + + saved_modes = ahd_save_modes(ahd); + ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI); + last_phase = ahd_inb(ahd, LASTPHASE); + saved_scbptr = ahd_get_scbptr(ahd); + active_scbptr = saved_scbptr; + if (disconnected && (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) == 0) { + struct scb *bus_scb; + + bus_scb = ahd_lookup_scb(ahd, active_scbptr); + if (bus_scb == pending_scb) + disconnected = FALSE; + else if (flag != SCB_ABORT + && ahd_inb(ahd, SAVED_SCSIID) == pending_scb->hscb->scsiid + && ahd_inb(ahd, SAVED_LUN) == SCB_GET_LUN(pending_scb)) + disconnected = FALSE; + } + + /* + * At this point, pending_scb is the scb associated with the + * passed in command. That command is currently active on the + * bus or is in the disconnected state. + */ + saved_scsiid = ahd_inb(ahd, SAVED_SCSIID); + if (last_phase != P_BUSFREE + && (SCB_GET_TAG(pending_scb) == active_scbptr + || (flag == SCB_DEVICE_RESET + && SCSIID_TARGET(ahd, saved_scsiid) == cmd->device->id))) { + + /* + * We're active on the bus, so assert ATN + * and hope that the target responds. + */ + pending_scb = ahd_lookup_scb(ahd, active_scbptr); + pending_scb->flags |= SCB_RECOVERY_SCB|flag; + ahd_outb(ahd, MSG_OUT, HOST_MSG); + ahd_outb(ahd, SCSISIGO, last_phase|ATNO); + printf("%s:%d:%d:%d: Device is active, asserting ATN\n", + ahd_name(ahd), cmd->device->channel, + cmd->device->id, cmd->device->lun); + wait = TRUE; + } else if (disconnected) { + + /* + * Actually re-queue this SCB in an attempt + * to select the device before it reconnects. + */ + pending_scb->flags |= SCB_RECOVERY_SCB|SCB_ABORT; + ahd_set_scbptr(ahd, SCB_GET_TAG(pending_scb)); + pending_scb->hscb->cdb_len = 0; + pending_scb->hscb->task_attribute = 0; + pending_scb->hscb->task_management = SIU_TASKMGMT_ABORT_TASK; + + if ((pending_scb->flags & SCB_PACKETIZED) != 0) { + /* + * Mark the SCB has having an outstanding + * task management function. Should the command + * complete normally before the task management + * function can be sent, the host will be notified + * to abort our requeued SCB. + */ + ahd_outb(ahd, SCB_TASK_MANAGEMENT, + pending_scb->hscb->task_management); + } else { + /* + * If non-packetized, set the MK_MESSAGE control + * bit indicating that we desire to send a message. + * We also set the disconnected flag since there is + * no guarantee that our SCB control byte matches + * the version on the card. We don't want the + * sequencer to abort the command thinking an + * unsolicited reselection occurred. + */ + pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED; + + /* + * The sequencer will never re-reference the + * in-core SCB. To make sure we are notified + * during reslection, set the MK_MESSAGE flag in + * the card's copy of the SCB. + */ + ahd_outb(ahd, SCB_CONTROL, + ahd_inb(ahd, SCB_CONTROL)|MK_MESSAGE); + } + + /* + * Clear out any entries in the QINFIFO first + * so we are the next SCB for this target + * to run. + */ + ahd_search_qinfifo(ahd, cmd->device->id, + cmd->device->channel + 'A', cmd->device->lun, + SCB_LIST_NULL, ROLE_INITIATOR, + CAM_REQUEUE_REQ, SEARCH_COMPLETE); + ahd_qinfifo_requeue_tail(ahd, pending_scb); + ahd_set_scbptr(ahd, saved_scbptr); + ahd_print_path(ahd, pending_scb); + printf("Device is disconnected, re-queuing SCB\n"); + wait = TRUE; + } else { + printf("%s:%d:%d:%d: Unable to deliver message\n", + ahd_name(ahd), cmd->device->channel, + cmd->device->id, cmd->device->lun); + retval = FAILED; + goto done; + } + +no_cmd: + /* + * Our assumption is that if we don't have the command, no + * recovery action was required, so we return success. Again, + * the semantics of the mid-layer recovery engine are not + * well defined, so this may change in time. + */ + retval = SUCCESS; +done: + if (paused) + ahd_unpause(ahd); + if (wait) { + struct timer_list timer; + int ret; + + ahd->platform_data->flags |= AHD_SCB_UP_EH_SEM; + spin_unlock_irq(&ahd->platform_data->spin_lock); + init_timer(&timer); + timer.data = (u_long)ahd; + timer.expires = jiffies + (5 * HZ); + timer.function = ahd_linux_sem_timeout; + add_timer(&timer); + printf("Recovery code sleeping\n"); + down(&ahd->platform_data->eh_sem); + printf("Recovery code awake\n"); + ret = del_timer_sync(&timer); + if (ret == 0) { + printf("Timer Expired\n"); + retval = FAILED; + } + spin_lock_irq(&ahd->platform_data->spin_lock); + } + spin_unlock_irq(&ahd->platform_data->spin_lock); + return (retval); } -static void -ahd_linux_dev_timed_unfreeze(u_long arg) +static void ahd_linux_set_width(struct scsi_target *starget, int width) { - struct ahd_linux_device *dev; - struct ahd_softc *ahd; - u_long s; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_devinfo devinfo; + unsigned long flags; - dev = (struct ahd_linux_device *)arg; - ahd = dev->target->ahd; - ahd_lock(ahd, &s); - dev->flags &= ~AHD_DEV_TIMER_ACTIVE; - if (dev->qfrozen > 0) - dev->qfrozen--; - if (dev->qfrozen == 0 - && (dev->flags & AHD_DEV_ON_RUN_LIST) == 0) - ahd_linux_run_device_queue(ahd, dev); - if ((dev->flags & AHD_DEV_UNCONFIGURED) != 0 - && dev->active == 0) - ahd_linux_free_device(ahd, dev); - ahd_unlock(ahd, &s); + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_lock(ahd, &flags); + ahd_set_width(ahd, &devinfo, width, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); } -void -ahd_platform_dump_card_state(struct ahd_softc *ahd) +static void ahd_linux_set_period(struct scsi_target *starget, int period) { - struct ahd_linux_device *dev; - int target; - int maxtarget; - int lun; - int i; - - maxtarget = (ahd->features & AHD_WIDE) ? 15 : 7; - for (target = 0; target <=maxtarget; target++) { - - for (lun = 0; lun < AHD_NUM_LUNS; lun++) { - struct ahd_cmd *acmd; - - dev = ahd_linux_get_device(ahd, 0, target, - lun, /*alloc*/FALSE); - if (dev == NULL) - continue; - - printf("DevQ(%d:%d:%d): ", 0, target, lun); - i = 0; - TAILQ_FOREACH(acmd, &dev->busyq, acmd_links.tqe) { - if (i++ > AHD_SCB_MAX) - break; - } - printf("%d waiting\n", i); - } + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options; + unsigned int dt; + unsigned long flags; + unsigned long offset = tinfo->goal.offset; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: set period to %d\n", ahd_name(ahd), period); +#endif + if (offset == 0) + offset = MAX_OFFSET; + + if (period < 8) + period = 8; + if (period < 10) { + ppr_options |= MSG_EXT_PPR_DT_REQ; + if (period == 8) + ppr_options |= MSG_EXT_PPR_IU_REQ; } + + dt = ppr_options & MSG_EXT_PPR_DT_REQ; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + + /* all PPR requests apart from QAS require wide transfers */ + if (ppr_options & ~MSG_EXT_PPR_QAS_REQ) { + if (spi_width(starget) == 0) + ppr_options &= MSG_EXT_PPR_QAS_REQ; + } + + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); } -static int __init -ahd_linux_init(void) +static void ahd_linux_set_offset(struct scsi_target *starget, int offset) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - return ahd_linux_detect(&aic79xx_driver_template); -#else - scsi_register_module(MODULE_SCSI_HA, &aic79xx_driver_template); - if (aic79xx_driver_template.present == 0) { - scsi_unregister_module(MODULE_SCSI_HA, - &aic79xx_driver_template); - return (-ENODEV); + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = 0; + unsigned int period = 0; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: set offset to %d\n", ahd_name(ahd), offset); +#endif + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + if (offset != 0) { + period = tinfo->goal.period; + ppr_options = tinfo->goal.ppr_options; + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); } - return (0); + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, offset, ppr_options, + AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_dt(struct scsi_target *starget, int dt) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_DT_REQ; + unsigned int period = tinfo->goal.period; + unsigned int width = tinfo->goal.width; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s DT\n", ahd_name(ahd), + dt ? "enabling" : "disabling"); +#endif + if (dt) { + ppr_options |= MSG_EXT_PPR_DT_REQ; + if (!width) + ahd_linux_set_width(starget, 1); + } else { + if (period <= 9) + period = 10; /* If resetting DT, period must be >= 25ns */ + /* IU is invalid without DT set */ + ppr_options &= ~MSG_EXT_PPR_IU_REQ; + } + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_qas(struct scsi_target *starget, int qas) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_QAS_REQ; + unsigned int period = tinfo->goal.period; + unsigned int dt; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s QAS\n", ahd_name(ahd), + qas ? "enabling" : "disabling"); #endif + + if (qas) { + ppr_options |= MSG_EXT_PPR_QAS_REQ; + } + + dt = ppr_options & MSG_EXT_PPR_DT_REQ; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); } -static void __exit -ahd_linux_exit(void) +static void ahd_linux_set_iu(struct scsi_target *starget, int iu) { - struct ahd_softc *ahd; + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_IU_REQ; + unsigned int period = tinfo->goal.period; + unsigned int dt; + unsigned long flags; - /* - * Shutdown DV threads before going into the SCSI mid-layer. - * This avoids situations where the mid-layer locks the entire - * kernel so that waiting for our DV threads to exit leads - * to deadlock. - */ - TAILQ_FOREACH(ahd, &ahd_tailq, links) { +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s IU\n", ahd_name(ahd), + iu ? "enabling" : "disabling"); +#endif + + if (iu) { + ppr_options |= MSG_EXT_PPR_IU_REQ; + ppr_options |= MSG_EXT_PPR_DT_REQ; /* IU requires DT */ + } + + dt = ppr_options & MSG_EXT_PPR_DT_REQ; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_rd_strm(struct scsi_target *starget, int rdstrm) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_RD_STRM; + unsigned int period = tinfo->goal.period; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s Read Streaming\n", ahd_name(ahd), + rdstrm ? "enabling" : "disabling"); +#endif + + if (rdstrm) + ppr_options |= MSG_EXT_PPR_RD_STRM; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); - ahd_linux_kill_dv_thread(ahd); + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_wr_flow(struct scsi_target *starget, int wrflow) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_WR_FLOW; + unsigned int period = tinfo->goal.period; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s Write Flow Control\n", ahd_name(ahd), + wrflow ? "enabling" : "disabling"); +#endif + + if (wrflow) + ppr_options |= MSG_EXT_PPR_WR_FLOW; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_rti(struct scsi_target *starget, int rti) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_RTI; + unsigned int period = tinfo->goal.period; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + + if ((ahd->features & AHD_RTI) == 0) { +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: RTI not available\n", ahd_name(ahd)); +#endif + return; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s RTI\n", ahd_name(ahd), + rti ? "enabling" : "disabling"); +#endif + + if (rti) + ppr_options |= MSG_EXT_PPR_RTI; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_pcomp_en(struct scsi_target *starget, int pcomp) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_PCOMP_EN; + unsigned int period = tinfo->goal.period; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_DV) != 0) + printf("%s: %s Precompensation\n", ahd_name(ahd), + pcomp ? "Enable" : "Disable"); +#endif + + if (pcomp) + ppr_options |= MSG_EXT_PPR_PCOMP_EN; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + +static void ahd_linux_set_hold_mcs(struct scsi_target *starget, int hold) +{ + struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); + struct ahd_softc *ahd = *((struct ahd_softc **)shost->hostdata); + struct ahd_tmode_tstate *tstate; + struct ahd_initiator_tinfo *tinfo + = ahd_fetch_transinfo(ahd, + starget->channel + 'A', + shost->this_id, starget->id, &tstate); + struct ahd_devinfo devinfo; + unsigned int ppr_options = tinfo->goal.ppr_options + & ~MSG_EXT_PPR_HOLD_MCS; + unsigned int period = tinfo->goal.period; + unsigned int dt = ppr_options & MSG_EXT_PPR_DT_REQ; + unsigned long flags; + + if (hold) + ppr_options |= MSG_EXT_PPR_HOLD_MCS; + + ahd_compile_devinfo(&devinfo, shost->this_id, starget->id, 0, + starget->channel + 'A', ROLE_INITIATOR); + ahd_find_syncrate(ahd, &period, &ppr_options, + dt ? AHD_SYNCRATE_MAX : AHD_SYNCRATE_ULTRA2); + + ahd_lock(ahd, &flags); + ahd_set_syncrate(ahd, &devinfo, period, tinfo->goal.offset, + ppr_options, AHD_TRANS_GOAL, FALSE); + ahd_unlock(ahd, &flags); +} + + + +static struct spi_function_template ahd_linux_transport_functions = { + .set_offset = ahd_linux_set_offset, + .show_offset = 1, + .set_period = ahd_linux_set_period, + .show_period = 1, + .set_width = ahd_linux_set_width, + .show_width = 1, + .set_dt = ahd_linux_set_dt, + .show_dt = 1, + .set_iu = ahd_linux_set_iu, + .show_iu = 1, + .set_qas = ahd_linux_set_qas, + .show_qas = 1, + .set_rd_strm = ahd_linux_set_rd_strm, + .show_rd_strm = 1, + .set_wr_flow = ahd_linux_set_wr_flow, + .show_wr_flow = 1, + .set_rti = ahd_linux_set_rti, + .show_rti = 1, + .set_pcomp_en = ahd_linux_set_pcomp_en, + .show_pcomp_en = 1, + .set_hold_mcs = ahd_linux_set_hold_mcs, + .show_hold_mcs = 1, +}; + +static int __init +ahd_linux_init(void) +{ + int error = 0; + /* - * In 2.4 we have to unregister from the PCI core _after_ - * unregistering from the scsi midlayer to avoid dangling - * references. + * If we've been passed any parameters, process them now. */ - scsi_unregister_module(MODULE_SCSI_HA, &aic79xx_driver_template); -#endif + if (aic79xx) + aic79xx_setup(aic79xx); + + ahd_linux_transport_template = + spi_attach_transport(&ahd_linux_transport_functions); + if (!ahd_linux_transport_template) + return -ENODEV; + + scsi_transport_reserve_target(ahd_linux_transport_template, + sizeof(struct ahd_linux_target)); + scsi_transport_reserve_device(ahd_linux_transport_template, + sizeof(struct ahd_linux_device)); + + error = ahd_linux_pci_init(); + if (error) + spi_release_transport(ahd_linux_transport_template); + return error; +} + +static void __exit +ahd_linux_exit(void) +{ ahd_linux_pci_exit(); + spi_release_transport(ahd_linux_transport_template); } module_init(ahd_linux_init); diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.h b/drivers/scsi/aic7xxx/aic79xx_osm.h index 7823e52e99ab..052c6619accc 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.h +++ b/drivers/scsi/aic7xxx/aic79xx_osm.h @@ -42,6 +42,7 @@ #ifndef _AIC79XX_LINUX_H_ #define _AIC79XX_LINUX_H_ +#include <linux/config.h> #include <linux/types.h> #include <linux/blkdev.h> #include <linux/delay.h> @@ -49,18 +50,23 @@ #include <linux/pci.h> #include <linux/smp_lock.h> #include <linux/version.h> +#include <linux/interrupt.h> #include <linux/module.h> +#include <linux/slab.h> #include <asm/byteorder.h> #include <asm/io.h> -#include <linux/interrupt.h> /* For tasklet support. */ -#include <linux/config.h> -#include <linux/slab.h> +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_eh.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_transport_spi.h> /* Core SCSI definitions */ #define AIC_LIB_PREFIX ahd -#include "scsi.h" -#include <scsi/scsi_host.h> /* Name space conflict with BSD queue macros */ #ifdef LIST_HEAD @@ -95,7 +101,7 @@ /************************* Forward Declarations *******************************/ struct ahd_softc; typedef struct pci_dev *ahd_dev_softc_t; -typedef Scsi_Cmnd *ahd_io_ctx_t; +typedef struct scsi_cmnd *ahd_io_ctx_t; /******************************* Byte Order ***********************************/ #define ahd_htobe16(x) cpu_to_be16(x) @@ -114,8 +120,7 @@ typedef Scsi_Cmnd *ahd_io_ctx_t; /************************* Configuration Data *********************************/ extern uint32_t aic79xx_allow_memio; -extern int aic79xx_detect_complete; -extern Scsi_Host_Template aic79xx_driver_template; +extern struct scsi_host_template aic79xx_driver_template; /***************************** Bus Space/DMA **********************************/ @@ -145,11 +150,7 @@ struct ahd_linux_dma_tag }; typedef struct ahd_linux_dma_tag* bus_dma_tag_t; -struct ahd_linux_dmamap -{ - dma_addr_t bus_addr; -}; -typedef struct ahd_linux_dmamap* bus_dmamap_t; +typedef dma_addr_t bus_dmamap_t; typedef int bus_dma_filter_t(void*, dma_addr_t); typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int); @@ -226,12 +227,12 @@ typedef struct timer_list ahd_timer_t; #define ahd_timer_init init_timer #define ahd_timer_stop del_timer_sync typedef void ahd_linux_callback_t (u_long); -static __inline void ahd_timer_reset(ahd_timer_t *timer, u_int usec, +static __inline void ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg); static __inline void ahd_scb_timer_reset(struct scb *scb, u_int usec); static __inline void -ahd_timer_reset(ahd_timer_t *timer, u_int usec, ahd_callback_t *func, void *arg) +ahd_timer_reset(ahd_timer_t *timer, int usec, ahd_callback_t *func, void *arg) { struct ahd_softc *ahd; @@ -252,43 +253,8 @@ ahd_scb_timer_reset(struct scb *scb, u_int usec) /***************************** SMP support ************************************/ #include <linux/spinlock.h> -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) || defined(SCSI_HAS_HOST_LOCK)) -#define AHD_SCSI_HAS_HOST_LOCK 1 -#else -#define AHD_SCSI_HAS_HOST_LOCK 0 -#endif - #define AIC79XX_DRIVER_VERSION "1.3.11" -/**************************** Front End Queues ********************************/ -/* - * Data structure used to cast the Linux struct scsi_cmnd to something - * that allows us to use the queue macros. The linux structure has - * plenty of space to hold the links fields as required by the queue - * macros, but the queue macors require them to have the correct type. - */ -struct ahd_cmd_internal { - /* Area owned by the Linux scsi layer. */ - uint8_t private[offsetof(struct scsi_cmnd, SCp.Status)]; - union { - STAILQ_ENTRY(ahd_cmd) ste; - LIST_ENTRY(ahd_cmd) le; - TAILQ_ENTRY(ahd_cmd) tqe; - } links; - uint32_t end; -}; - -struct ahd_cmd { - union { - struct ahd_cmd_internal icmd; - struct scsi_cmnd scsi_cmd; - } un; -}; - -#define acmd_icmd(cmd) ((cmd)->un.icmd) -#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd) -#define acmd_links un.icmd.links - /*************************** Device Data Structures ***************************/ /* * A per probed device structure used to deal with some error recovery @@ -297,22 +263,17 @@ struct ahd_cmd { * after a successfully completed inquiry command to the target when * that inquiry data indicates a lun is present. */ -TAILQ_HEAD(ahd_busyq, ahd_cmd); + typedef enum { - AHD_DEV_UNCONFIGURED = 0x01, AHD_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */ - AHD_DEV_TIMER_ACTIVE = 0x04, /* Our timer is active */ - AHD_DEV_ON_RUN_LIST = 0x08, /* Queued to be run later */ AHD_DEV_Q_BASIC = 0x10, /* Allow basic device queuing */ AHD_DEV_Q_TAGGED = 0x20, /* Allow full SCSI2 command queueing */ AHD_DEV_PERIODIC_OTAG = 0x40, /* Send OTAG to prevent starvation */ - AHD_DEV_SLAVE_CONFIGURED = 0x80 /* slave_configure() has been called */ } ahd_linux_dev_flags; struct ahd_linux_target; struct ahd_linux_device { TAILQ_ENTRY(ahd_linux_device) links; - struct ahd_busyq busyq; /* * The number of transactions currently @@ -388,62 +349,12 @@ struct ahd_linux_device { */ u_int commands_since_idle_or_otag; #define AHD_OTAG_THRESH 500 - - int lun; - Scsi_Device *scsi_device; - struct ahd_linux_target *target; }; -typedef enum { - AHD_DV_REQUIRED = 0x01, - AHD_INQ_VALID = 0x02, - AHD_BASIC_DV = 0x04, - AHD_ENHANCED_DV = 0x08 -} ahd_linux_targ_flags; - -/* DV States */ -typedef enum { - AHD_DV_STATE_EXIT = 0, - AHD_DV_STATE_INQ_SHORT_ASYNC, - AHD_DV_STATE_INQ_ASYNC, - AHD_DV_STATE_INQ_ASYNC_VERIFY, - AHD_DV_STATE_TUR, - AHD_DV_STATE_REBD, - AHD_DV_STATE_INQ_VERIFY, - AHD_DV_STATE_WEB, - AHD_DV_STATE_REB, - AHD_DV_STATE_SU, - AHD_DV_STATE_BUSY -} ahd_dv_state; - struct ahd_linux_target { - struct ahd_linux_device *devices[AHD_NUM_LUNS]; - int channel; - int target; - int refcount; + struct scsi_device *sdev[AHD_NUM_LUNS]; struct ahd_transinfo last_tinfo; struct ahd_softc *ahd; - ahd_linux_targ_flags flags; - struct scsi_inquiry_data *inq_data; - /* - * The next "fallback" period to use for narrow/wide transfers. - */ - uint8_t dv_next_narrow_period; - uint8_t dv_next_wide_period; - uint8_t dv_max_width; - uint8_t dv_max_ppr_options; - uint8_t dv_last_ppr_options; - u_int dv_echo_size; - ahd_dv_state dv_state; - u_int dv_state_retry; - uint8_t *dv_buffer; - uint8_t *dv_buffer1; - - /* - * Cumulative counter of errors. - */ - u_long errors_detected; - u_long cmds_since_error; }; /********************* Definitions Required by the Core ***********************/ @@ -453,32 +364,16 @@ struct ahd_linux_target { * manner and are allocated below 4GB, the number of S/G segments is * unrestricted. */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -/* - * We dynamically adjust the number of segments in pre-2.5 kernels to - * avoid fragmentation issues in the SCSI mid-layer's private memory - * allocator. See aic79xx_osm.c ahd_linux_size_nseg() for details. - */ -extern u_int ahd_linux_nseg; -#define AHD_NSEG ahd_linux_nseg -#define AHD_LINUX_MIN_NSEG 64 -#else #define AHD_NSEG 128 -#endif /* * Per-SCB OSM storage. */ -typedef enum { - AHD_SCB_UP_EH_SEM = 0x1 -} ahd_linux_scb_flags; - struct scb_platform_data { struct ahd_linux_device *dev; dma_addr_t buf_busaddr; uint32_t xfer_len; uint32_t sense_resid; /* Auto-Sense residual */ - ahd_linux_scb_flags flags; }; /* @@ -487,44 +382,23 @@ struct scb_platform_data { * alignment restrictions of the various platforms supported by * this driver. */ -typedef enum { - AHD_DV_WAIT_SIMQ_EMPTY = 0x01, - AHD_DV_WAIT_SIMQ_RELEASE = 0x02, - AHD_DV_ACTIVE = 0x04, - AHD_DV_SHUTDOWN = 0x08, - AHD_RUN_CMPLT_Q_TIMER = 0x10 -} ahd_linux_softc_flags; - -TAILQ_HEAD(ahd_completeq, ahd_cmd); - struct ahd_platform_data { /* * Fields accessed from interrupt context. */ - struct ahd_linux_target *targets[AHD_NUM_TARGETS]; - TAILQ_HEAD(, ahd_linux_device) device_runq; - struct ahd_completeq completeq; + struct scsi_target *starget[AHD_NUM_TARGETS]; spinlock_t spin_lock; - struct tasklet_struct runq_tasklet; u_int qfrozen; - pid_t dv_pid; - struct timer_list completeq_timer; struct timer_list reset_timer; - struct timer_list stats_timer; struct semaphore eh_sem; - struct semaphore dv_sem; - struct semaphore dv_cmd_sem; /* XXX This needs to be in - * the target struct - */ - struct scsi_device *dv_scsi_dev; struct Scsi_Host *host; /* pointer to scsi host */ #define AHD_LINUX_NOIRQ ((uint32_t)~0) uint32_t irq; /* IRQ for this adapter */ uint32_t bios_address; uint32_t mem_busaddr; /* Mem Base Addr */ - uint64_t hw_dma_mask; - ahd_linux_softc_flags flags; +#define AHD_SCB_UP_EH_SEM 0x1 + uint32_t flags; }; /************************** OS Utility Wrappers *******************************/ @@ -641,7 +515,7 @@ ahd_insb(struct ahd_softc * ahd, long port, uint8_t *array, int count) /**************************** Initialization **********************************/ int ahd_linux_register_host(struct ahd_softc *, - Scsi_Host_Template *); + struct scsi_host_template *); uint64_t ahd_linux_get_memsize(void); @@ -657,28 +531,6 @@ void ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo); /******************************** Locking *************************************/ -/* Lock protecting internal data structures */ -static __inline void ahd_lockinit(struct ahd_softc *); -static __inline void ahd_lock(struct ahd_softc *, unsigned long *flags); -static __inline void ahd_unlock(struct ahd_softc *, unsigned long *flags); - -/* Lock acquisition and release of the above lock in midlayer entry points. */ -static __inline void ahd_midlayer_entrypoint_lock(struct ahd_softc *, - unsigned long *flags); -static __inline void ahd_midlayer_entrypoint_unlock(struct ahd_softc *, - unsigned long *flags); - -/* Lock held during command compeletion to the upper layer */ -static __inline void ahd_done_lockinit(struct ahd_softc *); -static __inline void ahd_done_lock(struct ahd_softc *, unsigned long *flags); -static __inline void ahd_done_unlock(struct ahd_softc *, unsigned long *flags); - -/* Lock held during ahd_list manipulation and ahd softc frees */ -extern spinlock_t ahd_list_spinlock; -static __inline void ahd_list_lockinit(void); -static __inline void ahd_list_lock(unsigned long *flags); -static __inline void ahd_list_unlock(unsigned long *flags); - static __inline void ahd_lockinit(struct ahd_softc *ahd) { @@ -697,75 +549,6 @@ ahd_unlock(struct ahd_softc *ahd, unsigned long *flags) spin_unlock_irqrestore(&ahd->platform_data->spin_lock, *flags); } -static __inline void -ahd_midlayer_entrypoint_lock(struct ahd_softc *ahd, unsigned long *flags) -{ - /* - * In 2.5.X and some 2.4.X versions, the midlayer takes our - * lock just before calling us, so we avoid locking again. - * For other kernel versions, the io_request_lock is taken - * just before our entry point is called. In this case, we - * trade the io_request_lock for our per-softc lock. - */ -#if AHD_SCSI_HAS_HOST_LOCK == 0 - spin_unlock(&io_request_lock); - spin_lock(&ahd->platform_data->spin_lock); -#endif -} - -static __inline void -ahd_midlayer_entrypoint_unlock(struct ahd_softc *ahd, unsigned long *flags) -{ -#if AHD_SCSI_HAS_HOST_LOCK == 0 - spin_unlock(&ahd->platform_data->spin_lock); - spin_lock(&io_request_lock); -#endif -} - -static __inline void -ahd_done_lockinit(struct ahd_softc *ahd) -{ - /* - * In 2.5.X, our own lock is held during completions. - * In previous versions, the io_request_lock is used. - * In either case, we can't initialize this lock again. - */ -} - -static __inline void -ahd_done_lock(struct ahd_softc *ahd, unsigned long *flags) -{ -#if AHD_SCSI_HAS_HOST_LOCK == 0 - spin_lock(&io_request_lock); -#endif -} - -static __inline void -ahd_done_unlock(struct ahd_softc *ahd, unsigned long *flags) -{ -#if AHD_SCSI_HAS_HOST_LOCK == 0 - spin_unlock(&io_request_lock); -#endif -} - -static __inline void -ahd_list_lockinit(void) -{ - spin_lock_init(&ahd_list_spinlock); -} - -static __inline void -ahd_list_lock(unsigned long *flags) -{ - spin_lock_irqsave(&ahd_list_spinlock, *flags); -} - -static __inline void -ahd_list_unlock(unsigned long *flags) -{ - spin_unlock_irqrestore(&ahd_list_spinlock, *flags); -} - /******************************* PCI Definitions ******************************/ /* * PCIM_xxx: mask to locate subfield in register @@ -925,27 +708,17 @@ ahd_flush_device_writes(struct ahd_softc *ahd) } /**************************** Proc FS Support *********************************/ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -int ahd_linux_proc_info(char *, char **, off_t, int, int, int); -#else int ahd_linux_proc_info(struct Scsi_Host *, char *, char **, off_t, int, int); -#endif - -/*************************** Domain Validation ********************************/ -#define AHD_DV_CMD(cmd) ((cmd)->scsi_done == ahd_linux_dv_complete) -#define AHD_DV_SIMQ_FROZEN(ahd) \ - ((((ahd)->platform_data->flags & AHD_DV_ACTIVE) != 0) \ - && (ahd)->platform_data->qfrozen == 1) /*********************** Transaction Access Wrappers **************************/ -static __inline void ahd_cmd_set_transaction_status(Scsi_Cmnd *, uint32_t); +static __inline void ahd_cmd_set_transaction_status(struct scsi_cmnd *, uint32_t); static __inline void ahd_set_transaction_status(struct scb *, uint32_t); -static __inline void ahd_cmd_set_scsi_status(Scsi_Cmnd *, uint32_t); +static __inline void ahd_cmd_set_scsi_status(struct scsi_cmnd *, uint32_t); static __inline void ahd_set_scsi_status(struct scb *, uint32_t); -static __inline uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd); +static __inline uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd); static __inline uint32_t ahd_get_transaction_status(struct scb *); -static __inline uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd); +static __inline uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd); static __inline uint32_t ahd_get_scsi_status(struct scb *); static __inline void ahd_set_transaction_tag(struct scb *, int, u_int); static __inline u_long ahd_get_transfer_length(struct scb *); @@ -964,7 +737,7 @@ static __inline void ahd_platform_scb_free(struct ahd_softc *ahd, static __inline void ahd_freeze_scb(struct scb *scb); static __inline -void ahd_cmd_set_transaction_status(Scsi_Cmnd *cmd, uint32_t status) +void ahd_cmd_set_transaction_status(struct scsi_cmnd *cmd, uint32_t status) { cmd->result &= ~(CAM_STATUS_MASK << 16); cmd->result |= status << 16; @@ -977,7 +750,7 @@ void ahd_set_transaction_status(struct scb *scb, uint32_t status) } static __inline -void ahd_cmd_set_scsi_status(Scsi_Cmnd *cmd, uint32_t status) +void ahd_cmd_set_scsi_status(struct scsi_cmnd *cmd, uint32_t status) { cmd->result &= ~0xFFFF; cmd->result |= status; @@ -990,7 +763,7 @@ void ahd_set_scsi_status(struct scb *scb, uint32_t status) } static __inline -uint32_t ahd_cmd_get_transaction_status(Scsi_Cmnd *cmd) +uint32_t ahd_cmd_get_transaction_status(struct scsi_cmnd *cmd) { return ((cmd->result >> 16) & CAM_STATUS_MASK); } @@ -1002,7 +775,7 @@ uint32_t ahd_get_transaction_status(struct scb *scb) } static __inline -uint32_t ahd_cmd_get_scsi_status(Scsi_Cmnd *cmd) +uint32_t ahd_cmd_get_scsi_status(struct scsi_cmnd *cmd) { return (cmd->result & 0xFFFF); } @@ -1117,7 +890,6 @@ void ahd_done(struct ahd_softc*, struct scb*); void ahd_send_async(struct ahd_softc *, char channel, u_int target, u_int lun, ac_code, void *); void ahd_print_path(struct ahd_softc *, struct scb *); -void ahd_platform_dump_card_state(struct ahd_softc *ahd); #ifdef CONFIG_PCI #define AHD_PCI_CONFIG 1 diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 91daf0c7fb10..bf360ae021ab 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -92,27 +92,34 @@ struct pci_driver aic79xx_pci_driver = { static void ahd_linux_pci_dev_remove(struct pci_dev *pdev) { - struct ahd_softc *ahd; - u_long l; + struct ahd_softc *ahd = pci_get_drvdata(pdev); + u_long s; - /* - * We should be able to just perform - * the free directly, but check our - * list for extra sanity. - */ - ahd_list_lock(&l); - ahd = ahd_find_softc((struct ahd_softc *)pci_get_drvdata(pdev)); - if (ahd != NULL) { - u_long s; - - TAILQ_REMOVE(&ahd_tailq, ahd, links); - ahd_list_unlock(&l); - ahd_lock(ahd, &s); - ahd_intr_enable(ahd, FALSE); - ahd_unlock(ahd, &s); - ahd_free(ahd); - } else - ahd_list_unlock(&l); + if (ahd->platform_data && ahd->platform_data->host) + scsi_remove_host(ahd->platform_data->host); + + ahd_lock(ahd, &s); + ahd_intr_enable(ahd, FALSE); + ahd_unlock(ahd, &s); + ahd_free(ahd); +} + +static void +ahd_linux_pci_inherit_flags(struct ahd_softc *ahd) +{ + struct pci_dev *pdev = ahd->dev_softc, *master_pdev; + unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + + master_pdev = pci_get_slot(pdev->bus, master_devfn); + if (master_pdev) { + struct ahd_softc *master = pci_get_drvdata(master_pdev); + if (master) { + ahd->flags &= ~AHD_BIOS_ENABLED; + ahd->flags |= master->flags & AHD_BIOS_ENABLED; + } else + printk(KERN_ERR "aic79xx: no multichannel peer found!\n"); + pci_dev_put(master_pdev); + } } static int @@ -125,22 +132,6 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) char *name; int error; - /* - * Some BIOSen report the same device multiple times. - */ - TAILQ_FOREACH(ahd, &ahd_tailq, links) { - struct pci_dev *probed_pdev; - - probed_pdev = ahd->dev_softc; - if (probed_pdev->bus->number == pdev->bus->number - && probed_pdev->devfn == pdev->devfn) - break; - } - if (ahd != NULL) { - /* Skip duplicate. */ - return (-ENODEV); - } - pci = pdev; entry = ahd_find_pci_device(pci); if (entry == NULL) @@ -177,15 +168,12 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (memsize >= 0x8000000000ULL && pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) { ahd->flags |= AHD_64BIT_ADDRESSING; - ahd->platform_data->hw_dma_mask = DMA_64BIT_MASK; } else if (memsize > 0x80000000 && pci_set_dma_mask(pdev, mask_39bit) == 0) { ahd->flags |= AHD_39BIT_ADDRESSING; - ahd->platform_data->hw_dma_mask = mask_39bit; } } else { pci_set_dma_mask(pdev, DMA_32BIT_MASK); - ahd->platform_data->hw_dma_mask = DMA_32BIT_MASK; } ahd->dev_softc = pci; error = ahd_pci_config(ahd, entry); @@ -193,16 +181,17 @@ ahd_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ahd_free(ahd); return (-error); } + + /* + * Second Function PCI devices need to inherit some + * * settings from function 0. + */ + if ((ahd->features & AHD_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0) + ahd_linux_pci_inherit_flags(ahd); + pci_set_drvdata(pdev, ahd); - if (aic79xx_detect_complete) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) - ahd_linux_register_host(ahd, &aic79xx_driver_template); -#else - printf("aic79xx: ignoring PCI device found after " - "initialization\n"); - return (-ENODEV); -#endif - } + + ahd_linux_register_host(ahd, &aic79xx_driver_template); return (0); } diff --git a/drivers/scsi/aic7xxx/aic79xx_pci.c b/drivers/scsi/aic7xxx/aic79xx_pci.c index 703f6e44889d..2131db60018a 100644 --- a/drivers/scsi/aic7xxx/aic79xx_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_pci.c @@ -283,7 +283,6 @@ int ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry) { struct scb_data *shared_scb_data; - u_long l; u_int command; uint32_t devconfig; uint16_t subvendor; @@ -373,16 +372,9 @@ ahd_pci_config(struct ahd_softc *ahd, struct ahd_pci_identity *entry) * Allow interrupts now that we are completely setup. */ error = ahd_pci_map_int(ahd); - if (error != 0) - return (error); - - ahd_list_lock(&l); - /* - * Link this softc in with all other ahd instances. - */ - ahd_softc_insert(ahd); - ahd_list_unlock(&l); - return (0); + if (!error) + ahd->init_level++; + return error; } /* diff --git a/drivers/scsi/aic7xxx/aic79xx_proc.c b/drivers/scsi/aic7xxx/aic79xx_proc.c index e01cd6175e34..39a27840fce6 100644 --- a/drivers/scsi/aic7xxx/aic79xx_proc.c +++ b/drivers/scsi/aic7xxx/aic79xx_proc.c @@ -49,10 +49,53 @@ static void ahd_dump_target_state(struct ahd_softc *ahd, u_int our_id, char channel, u_int target_id, u_int target_offset); static void ahd_dump_device_state(struct info_str *info, - struct ahd_linux_device *dev); + struct scsi_device *sdev); static int ahd_proc_write_seeprom(struct ahd_softc *ahd, char *buffer, int length); +/* + * Table of syncrates that don't follow the "divisible by 4" + * rule. This table will be expanded in future SCSI specs. + */ +static struct { + u_int period_factor; + u_int period; /* in 100ths of ns */ +} scsi_syncrates[] = { + { 0x08, 625 }, /* FAST-160 */ + { 0x09, 1250 }, /* FAST-80 */ + { 0x0a, 2500 }, /* FAST-40 40MHz */ + { 0x0b, 3030 }, /* FAST-40 33MHz */ + { 0x0c, 5000 } /* FAST-20 */ +}; + +/* + * Return the frequency in kHz corresponding to the given + * sync period factor. + */ +static u_int +ahd_calc_syncsrate(u_int period_factor) +{ + int i; + int num_syncrates; + + num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); + /* See if the period is in the "exception" table */ + for (i = 0; i < num_syncrates; i++) { + + if (period_factor == scsi_syncrates[i].period_factor) { + /* Period in kHz */ + return (100000000 / scsi_syncrates[i].period); + } + } + + /* + * Wasn't in the table, so use the standard + * 4 times conversion. + */ + return (10000000 / (period_factor * 4 * 10)); +} + + static void copy_mem_info(struct info_str *info, char *data, int len) { @@ -109,7 +152,7 @@ ahd_format_transinfo(struct info_str *info, struct ahd_transinfo *tinfo) speed = 3300; freq = 0; if (tinfo->offset != 0) { - freq = aic_calc_syncsrate(tinfo->period); + freq = ahd_calc_syncsrate(tinfo->period); speed = freq; } speed *= (0x01 << tinfo->width); @@ -167,6 +210,7 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info, u_int target_offset) { struct ahd_linux_target *targ; + struct scsi_target *starget; struct ahd_initiator_tinfo *tinfo; struct ahd_tmode_tstate *tstate; int lun; @@ -176,20 +220,20 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info, copy_info(info, "Target %d Negotiation Settings\n", target_id); copy_info(info, "\tUser: "); ahd_format_transinfo(info, &tinfo->user); - targ = ahd->platform_data->targets[target_offset]; - if (targ == NULL) + starget = ahd->platform_data->starget[target_offset]; + if (starget == NULL) return; + targ = scsi_transport_target_data(starget); copy_info(info, "\tGoal: "); ahd_format_transinfo(info, &tinfo->goal); copy_info(info, "\tCurr: "); ahd_format_transinfo(info, &tinfo->curr); - copy_info(info, "\tTransmission Errors %ld\n", targ->errors_detected); for (lun = 0; lun < AHD_NUM_LUNS; lun++) { - struct ahd_linux_device *dev; + struct scsi_device *dev; - dev = targ->devices[lun]; + dev = targ->sdev[lun]; if (dev == NULL) continue; @@ -199,10 +243,13 @@ ahd_dump_target_state(struct ahd_softc *ahd, struct info_str *info, } static void -ahd_dump_device_state(struct info_str *info, struct ahd_linux_device *dev) +ahd_dump_device_state(struct info_str *info, struct scsi_device *sdev) { + struct ahd_linux_device *dev = scsi_transport_device_data(sdev); + copy_info(info, "\tChannel %c Target %d Lun %d Settings\n", - dev->target->channel + 'A', dev->target->target, dev->lun); + sdev->sdev_target->channel + 'A', + sdev->sdev_target->id, sdev->lun); copy_info(info, "\t\tCommands Queued %ld\n", dev->commands_issued); copy_info(info, "\t\tCommands Active %d\n", dev->active); @@ -278,36 +325,16 @@ done: * Return information to handle /proc support for the driver. */ int -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -ahd_linux_proc_info(char *buffer, char **start, off_t offset, - int length, int hostno, int inout) -#else ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start, off_t offset, int length, int inout) -#endif { - struct ahd_softc *ahd; + struct ahd_softc *ahd = *(struct ahd_softc **)shost->hostdata; struct info_str info; char ahd_info[256]; - u_long l; u_int max_targ; u_int i; int retval; - retval = -EINVAL; - ahd_list_lock(&l); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - TAILQ_FOREACH(ahd, &ahd_tailq, links) { - if (ahd->platform_data->host->host_no == hostno) - break; - } -#else - ahd = ahd_find_softc(*(struct ahd_softc **)shost->hostdata); -#endif - - if (ahd == NULL) - goto done; - /* Has data been written to the file? */ if (inout == TRUE) { retval = ahd_proc_write_seeprom(ahd, buffer, length); @@ -357,6 +384,5 @@ ahd_linux_proc_info(struct Scsi_Host *shost, char *buffer, char **start, } retval = info.pos > info.offset ? info.pos - info.offset : 0; done: - ahd_list_unlock(&l); return (retval); } diff --git a/drivers/scsi/aic7xxx/aic7xxx.h b/drivers/scsi/aic7xxx/aic7xxx.h index 088cbc23743d..91d294c6334e 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.h +++ b/drivers/scsi/aic7xxx/aic7xxx.h @@ -37,7 +37,7 @@ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#79 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.h#85 $ * * $FreeBSD$ */ @@ -243,7 +243,7 @@ typedef enum { */ AHC_AIC7850_FE = AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA, AHC_AIC7860_FE = AHC_AIC7850_FE, - AHC_AIC7870_FE = AHC_TARGETMODE, + AHC_AIC7870_FE = AHC_TARGETMODE|AHC_AUTOPAUSE, AHC_AIC7880_FE = AHC_AIC7870_FE|AHC_ULTRA, /* * Although we have space for both the initiator and diff --git a/drivers/scsi/aic7xxx/aic7xxx.reg b/drivers/scsi/aic7xxx/aic7xxx.reg index 810ec700d9fc..e196d83b93c7 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.reg +++ b/drivers/scsi/aic7xxx/aic7xxx.reg @@ -39,7 +39,7 @@ * * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $" /* * This file is processed by the aic7xxx_asm utility for use in assembling @@ -1306,7 +1306,6 @@ scratch_ram { */ MWI_RESIDUAL { size 1 - alias TARG_IMMEDIATE_SCB } /* * SCBID of the next SCB to be started by the controller. @@ -1461,6 +1460,7 @@ scratch_ram { */ LAST_MSG { size 1 + alias TARG_IMMEDIATE_SCB } /* diff --git a/drivers/scsi/aic7xxx/aic7xxx.seq b/drivers/scsi/aic7xxx/aic7xxx.seq index d84b741fbab5..15196390e28d 100644 --- a/drivers/scsi/aic7xxx/aic7xxx.seq +++ b/drivers/scsi/aic7xxx/aic7xxx.seq @@ -40,7 +40,7 @@ * $FreeBSD$ */ -VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $" +VERSION = "$Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $" PATCH_ARG_LIST = "struct ahc_softc *ahc" PREFIX = "ahc_" @@ -679,6 +679,7 @@ await_busfree: clr SCSIBUSL; /* Prevent bit leakage durint SELTO */ } and SXFRCTL0, ~SPIOEN; + mvi SEQ_FLAGS, NOT_IDENTIFIED|NO_CDB_SENT; test SSTAT1,REQINIT|BUSFREE jz .; test SSTAT1, BUSFREE jnz poll_for_work; mvi MISSED_BUSFREE call set_seqint; @@ -1097,7 +1098,7 @@ ultra2_dmahalt: test SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg; if ((ahc->flags & AHC_TARGETROLE) != 0) { test SSTAT0, TARGET jz dma_last_sg; - if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) { + if ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0) { test DMAPARAMS, DIRECTION jz dma_mid_sg; } } diff --git a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c index 468d612a44f6..3cb07e114e89 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_93cx6.c +++ b/drivers/scsi/aic7xxx/aic7xxx_93cx6.c @@ -28,9 +28,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#17 $ - * - * $FreeBSD$ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx_93cx6.c#19 $ */ /* @@ -64,7 +62,6 @@ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB * first). The clock cycling from low to high initiates the next data * bit to be sent from the chip. - * */ #ifdef __linux__ @@ -81,14 +78,22 @@ * Right now, we only have to read the SEEPROM. But we make it easier to * add other 93Cx6 functions. */ -static struct seeprom_cmd { +struct seeprom_cmd { uint8_t len; - uint8_t bits[9]; -} seeprom_read = {3, {1, 1, 0}}; + uint8_t bits[11]; +}; +/* Short opcodes for the c46 */ static struct seeprom_cmd seeprom_ewen = {9, {1, 0, 0, 1, 1, 0, 0, 0, 0}}; static struct seeprom_cmd seeprom_ewds = {9, {1, 0, 0, 0, 0, 0, 0, 0, 0}}; + +/* Long opcodes for the C56/C66 */ +static struct seeprom_cmd seeprom_long_ewen = {11, {1, 0, 0, 1, 1, 0, 0, 0, 0}}; +static struct seeprom_cmd seeprom_long_ewds = {11, {1, 0, 0, 0, 0, 0, 0, 0, 0}}; + +/* Common opcodes */ static struct seeprom_cmd seeprom_write = {3, {1, 0, 1}}; +static struct seeprom_cmd seeprom_read = {3, {1, 1, 0}}; /* * Wait for the SEERDY to go high; about 800 ns. @@ -222,12 +227,25 @@ int ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf, u_int start_addr, u_int count) { + struct seeprom_cmd *ewen, *ewds; uint16_t v; uint8_t temp; int i, k; /* Place the chip into write-enable mode */ - send_seeprom_cmd(sd, &seeprom_ewen); + if (sd->sd_chip == C46) { + ewen = &seeprom_ewen; + ewds = &seeprom_ewds; + } else if (sd->sd_chip == C56_66) { + ewen = &seeprom_long_ewen; + ewds = &seeprom_long_ewds; + } else { + printf("ahc_write_seeprom: unsupported seeprom type %d\n", + sd->sd_chip); + return (0); + } + + send_seeprom_cmd(sd, ewen); reset_seeprom(sd); /* Write all requested data out to the seeprom. */ @@ -277,7 +295,7 @@ ahc_write_seeprom(struct seeprom_descriptor *sd, uint16_t *buf, } /* Put the chip back into write-protect mode */ - send_seeprom_cmd(sd, &seeprom_ewds); + send_seeprom_cmd(sd, ewds); reset_seeprom(sd); return (1); diff --git a/drivers/scsi/aic7xxx/aic7xxx_core.c b/drivers/scsi/aic7xxx/aic7xxx_core.c index 7bc01e41bcce..58ac46103eb6 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_core.c +++ b/drivers/scsi/aic7xxx/aic7xxx_core.c @@ -37,9 +37,7 @@ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#134 $ - * - * $FreeBSD$ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.c#155 $ */ #ifdef __linux__ @@ -287,10 +285,19 @@ ahc_restart(struct ahc_softc *ahc) ahc_outb(ahc, SEQ_FLAGS2, ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA); } + + /* + * Clear any pending sequencer interrupt. It is no + * longer relevant since we're resetting the Program + * Counter. + */ + ahc_outb(ahc, CLRINT, CLRSEQINT); + ahc_outb(ahc, MWI_RESIDUAL, 0); ahc_outb(ahc, SEQCTL, ahc->seqctl); ahc_outb(ahc, SEQADDR0, 0); ahc_outb(ahc, SEQADDR1, 0); + ahc_unpause(ahc); } @@ -1174,19 +1181,20 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) scb_index); } #endif - /* - * Force a renegotiation with this target just in - * case the cable was pulled and will later be - * re-attached. The target may forget its negotiation - * settings with us should it attempt to reselect - * during the interruption. The target will not issue - * a unit attention in this case, so we must always - * renegotiate. - */ ahc_scb_devinfo(ahc, &devinfo, scb); - ahc_force_renegotiation(ahc, &devinfo); ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT); ahc_freeze_devq(ahc, scb); + + /* + * Cancel any pending transactions on the device + * now that it seems to be missing. This will + * also revert us to async/narrow transfers until + * we can renegotiate with the device. + */ + ahc_handle_devreset(ahc, &devinfo, + CAM_SEL_TIMEOUT, + "Selection Timeout", + /*verbose_level*/1); } ahc_outb(ahc, CLRINT, CLRSCSIINT); ahc_restart(ahc); @@ -3763,8 +3771,9 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, /*period*/0, /*offset*/0, /*ppr_options*/0, AHC_TRANS_CUR, /*paused*/TRUE); - ahc_send_async(ahc, devinfo->channel, devinfo->target, - CAM_LUN_WILDCARD, AC_SENT_BDR, NULL); + if (status != CAM_SEL_TIMEOUT) + ahc_send_async(ahc, devinfo->channel, devinfo->target, + CAM_LUN_WILDCARD, AC_SENT_BDR, NULL); if (message != NULL && (verbose_level <= bootverbose)) @@ -4003,14 +4012,6 @@ ahc_reset(struct ahc_softc *ahc, int reinit) * to disturb the integrity of the bus. */ ahc_pause(ahc); - if ((ahc_inb(ahc, HCNTRL) & CHIPRST) != 0) { - /* - * The chip has not been initialized since - * PCI/EISA/VLB bus reset. Don't trust - * "left over BIOS data". - */ - ahc->flags |= AHC_NO_BIOS_INIT; - } sxfrctl1_b = 0; if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) { u_int sblkctl; @@ -5036,14 +5037,23 @@ ahc_pause_and_flushwork(struct ahc_softc *ahc) ahc->flags |= AHC_ALL_INTERRUPTS; paused = FALSE; do { - if (paused) + if (paused) { ahc_unpause(ahc); + /* + * Give the sequencer some time to service + * any active selections. + */ + ahc_delay(500); + } ahc_intr(ahc); ahc_pause(ahc); paused = TRUE; ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO); - ahc_clear_critical_section(ahc); intstat = ahc_inb(ahc, INTSTAT); + if ((intstat & INT_PEND) == 0) { + ahc_clear_critical_section(ahc); + intstat = ahc_inb(ahc, INTSTAT); + } } while (--maxloops && (intstat != 0xFF || (ahc->features & AHC_REMOVABLE) == 0) && ((intstat & INT_PEND) != 0 diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index 116d0f51ca2c..6ee1435d37fa 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -125,12 +125,6 @@ static struct scsi_transport_template *ahc_linux_transport_template = NULL; -/* - * Include aiclib.c as part of our - * "module dependencies are hard" work around. - */ -#include "aiclib.c" - #include <linux/init.h> /* __setup */ #include <linux/mm.h> /* For fetching system memory size */ #include <linux/blkdev.h> /* For block_size() */ @@ -391,7 +385,6 @@ static int ahc_linux_run_command(struct ahc_softc*, struct ahc_linux_device *, struct scsi_cmnd *); static void ahc_linux_setup_tag_info_global(char *p); -static aic_option_callback_t ahc_linux_setup_tag_info; static int aic7xxx_setup(char *s); static int ahc_linux_unit; @@ -635,6 +628,8 @@ ahc_linux_slave_alloc(struct scsi_device *sdev) targ->sdev[sdev->lun] = sdev; + spi_period(starget) = 0; + return 0; } @@ -918,6 +913,86 @@ ahc_linux_setup_tag_info(u_long arg, int instance, int targ, int32_t value) } } +static char * +ahc_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, + void (*callback)(u_long, int, int, int32_t), + u_long callback_arg) +{ + char *tok_end; + char *tok_end2; + int i; + int instance; + int targ; + int done; + char tok_list[] = {'.', ',', '{', '}', '\0'}; + + /* All options use a ':' name/arg separator */ + if (*opt_arg != ':') + return (opt_arg); + opt_arg++; + instance = -1; + targ = -1; + done = FALSE; + /* + * Restore separator that may be in + * the middle of our option argument. + */ + tok_end = strchr(opt_arg, '\0'); + if (tok_end < end) + *tok_end = ','; + while (!done) { + switch (*opt_arg) { + case '{': + if (instance == -1) { + instance = 0; + } else { + if (depth > 1) { + if (targ == -1) + targ = 0; + } else { + printf("Malformed Option %s\n", + opt_name); + done = TRUE; + } + } + opt_arg++; + break; + case '}': + if (targ != -1) + targ = -1; + else if (instance != -1) + instance = -1; + opt_arg++; + break; + case ',': + case '.': + if (instance == -1) + done = TRUE; + else if (targ >= 0) + targ++; + else if (instance >= 0) + instance++; + opt_arg++; + break; + case '\0': + done = TRUE; + break; + default: + tok_end = end; + for (i = 0; tok_list[i]; i++) { + tok_end2 = strchr(opt_arg, tok_list[i]); + if ((tok_end2) && (tok_end2 < tok_end)) + tok_end = tok_end2; + } + callback(callback_arg, instance, targ, + simple_strtol(opt_arg, NULL, 0)); + opt_arg = tok_end; + break; + } + } + return (opt_arg); +} + /* * Handle Linux boot parameters. This routine allows for assigning a value * to a parameter with a ':' between the parameter and the value. @@ -972,7 +1047,7 @@ aic7xxx_setup(char *s) if (strncmp(p, "global_tag_depth", n) == 0) { ahc_linux_setup_tag_info_global(p + n); } else if (strncmp(p, "tag_info", n) == 0) { - s = aic_parse_brace_option("tag_info", p + n, end, + s = ahc_parse_brace_option("tag_info", p + n, end, 2, ahc_linux_setup_tag_info, 0); } else if (p[n] == ':') { *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0); @@ -1034,15 +1109,6 @@ ahc_linux_register_host(struct ahc_softc *ahc, struct scsi_host_template *templa return (0); } -uint64_t -ahc_linux_get_memsize(void) -{ - struct sysinfo si; - - si_meminfo(&si); - return ((uint64_t)si.totalram << PAGE_SHIFT); -} - /* * Place the SCSI bus into a known state by either resetting it, * or forcing transfer negotiations on the next command to any @@ -1143,11 +1209,6 @@ ahc_platform_free(struct ahc_softc *ahc) int i, j; if (ahc->platform_data != NULL) { - if (ahc->platform_data->host != NULL) { - scsi_remove_host(ahc->platform_data->host); - scsi_host_put(ahc->platform_data->host); - } - /* destroy all of the device and target objects */ for (i = 0; i < AHC_NUM_TARGETS; i++) { starget = ahc->platform_data->starget[i]; @@ -1176,6 +1237,9 @@ ahc_platform_free(struct ahc_softc *ahc) 0x1000); } + if (ahc->platform_data->host) + scsi_host_put(ahc->platform_data->host); + free(ahc->platform_data, M_DEVBUF); } } @@ -1264,14 +1328,12 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, } switch ((dev->flags & (AHC_DEV_Q_BASIC|AHC_DEV_Q_TAGGED))) { case AHC_DEV_Q_BASIC: - scsi_adjust_queue_depth(sdev, - MSG_SIMPLE_TASK, - dev->openings + dev->active); + scsi_set_tag_type(sdev, MSG_SIMPLE_TAG); + scsi_activate_tcq(sdev, dev->openings + dev->active); break; case AHC_DEV_Q_TAGGED: - scsi_adjust_queue_depth(sdev, - MSG_ORDERED_TASK, - dev->openings + dev->active); + scsi_set_tag_type(sdev, MSG_ORDERED_TAG); + scsi_activate_tcq(sdev, dev->openings + dev->active); break; default: /* @@ -1280,9 +1342,7 @@ ahc_platform_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, * serially on the controller/device. This should * remove some latency. */ - scsi_adjust_queue_depth(sdev, - /*NON-TAGGED*/0, - /*queue depth*/2); + scsi_deactivate_tcq(sdev, 2); break; } } @@ -1616,9 +1676,9 @@ ahc_send_async(struct ahc_softc *ahc, char channel, if (channel == 'B') target_offset += 8; starget = ahc->platform_data->starget[target_offset]; - targ = scsi_transport_target_data(starget); - if (targ == NULL) + if (starget == NULL) break; + targ = scsi_transport_target_data(starget); target_ppr_options = (spi_dt(starget) ? MSG_EXT_PPR_DT_REQ : 0) @@ -1635,9 +1695,9 @@ ahc_send_async(struct ahc_softc *ahc, char channel, spi_period(starget) = tinfo->curr.period; spi_width(starget) = tinfo->curr.width; spi_offset(starget) = tinfo->curr.offset; - spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ; - spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ; - spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ; + spi_dt(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_DT_REQ ? 1 : 0; + spi_qas(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_QAS_REQ ? 1 : 0; + spi_iu(starget) = tinfo->curr.ppr_options & MSG_EXT_PPR_IU_REQ ? 1 : 0; spi_display_xfer_agreement(starget); break; } @@ -2333,8 +2393,6 @@ ahc_platform_dump_card_state(struct ahc_softc *ahc) { } -static void ahc_linux_exit(void); - static void ahc_linux_set_width(struct scsi_target *starget, int width) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); @@ -2429,12 +2487,14 @@ static void ahc_linux_set_dt(struct scsi_target *starget, int dt) unsigned int ppr_options = tinfo->goal.ppr_options & ~MSG_EXT_PPR_DT_REQ; unsigned int period = tinfo->goal.period; + unsigned int width = tinfo->goal.width; unsigned long flags; struct ahc_syncrate *syncrate; if (dt) { - period = 9; /* 12.5ns is the only period valid for DT */ ppr_options |= MSG_EXT_PPR_DT_REQ; + if (!width) + ahc_linux_set_width(starget, 1); } else if (period == 9) period = 10; /* if resetting DT, period must be >= 25ns */ diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index 0e47ac217549..be9edbe26dbe 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -265,7 +265,7 @@ ahc_scb_timer_reset(struct scb *scb, u_int usec) /***************************** SMP support ************************************/ #include <linux/spinlock.h> -#define AIC7XXX_DRIVER_VERSION "6.2.36" +#define AIC7XXX_DRIVER_VERSION "7.0" /*************************** Device Data Structures ***************************/ /* @@ -494,8 +494,6 @@ ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count) int ahc_linux_register_host(struct ahc_softc *, struct scsi_host_template *); -uint64_t ahc_linux_get_memsize(void); - /*************************** Pretty Printing **********************************/ struct info_str { char *buffer; diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c index 9d318ce2c993..cb30d9c1153d 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -143,12 +143,36 @@ ahc_linux_pci_dev_remove(struct pci_dev *pdev) struct ahc_softc *ahc = pci_get_drvdata(pdev); u_long s; + if (ahc->platform_data && ahc->platform_data->host) + scsi_remove_host(ahc->platform_data->host); + ahc_lock(ahc, &s); ahc_intr_enable(ahc, FALSE); ahc_unlock(ahc, &s); ahc_free(ahc); } +static void +ahc_linux_pci_inherit_flags(struct ahc_softc *ahc) +{ + struct pci_dev *pdev = ahc->dev_softc, *master_pdev; + unsigned int master_devfn = PCI_DEVFN(PCI_SLOT(pdev->devfn), 0); + + master_pdev = pci_get_slot(pdev->bus, master_devfn); + if (master_pdev) { + struct ahc_softc *master = pci_get_drvdata(master_pdev); + if (master) { + ahc->flags &= ~AHC_BIOS_ENABLED; + ahc->flags |= master->flags & AHC_BIOS_ENABLED; + + ahc->flags &= ~AHC_PRIMARY_CHANNEL; + ahc->flags |= master->flags & AHC_PRIMARY_CHANNEL; + } else + printk(KERN_ERR "aic7xxx: no multichannel peer found!\n"); + pci_dev_put(master_pdev); + } +} + static int ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -159,6 +183,7 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct ahc_pci_identity *entry; char *name; int error; + struct device *dev = &pdev->dev; pci = pdev; entry = ahc_find_pci_device(pci); @@ -188,11 +213,12 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); if (sizeof(dma_addr_t) > 4 - && ahc_linux_get_memsize() > 0x80000000 - && pci_set_dma_mask(pdev, mask_39bit) == 0) { + && ahc->features & AHC_LARGE_SCBS + && dma_set_mask(dev, mask_39bit) == 0 + && dma_get_required_mask(dev) > DMA_32BIT_MASK) { ahc->flags |= AHC_39BIT_ADDRESSING; } else { - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + if (dma_set_mask(dev, DMA_32BIT_MASK)) { printk(KERN_WARNING "aic7xxx: No suitable DMA available.\n"); return (-ENODEV); } @@ -203,6 +229,14 @@ ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ahc_free(ahc); return (-error); } + + /* + * Second Function PCI devices need to inherit some + * settings from function 0. + */ + if ((ahc->features & AHC_MULTI_FUNC) && PCI_FUNC(pdev->devfn) != 0) + ahc_linux_pci_inherit_flags(ahc); + pci_set_drvdata(pdev, ahc); ahc_linux_register_host(ahc, &aic7xxx_driver_template); return (0); diff --git a/drivers/scsi/aic7xxx/aic7xxx_proc.c b/drivers/scsi/aic7xxx/aic7xxx_proc.c index 3802c91f0b07..04a3506cf340 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_proc.c +++ b/drivers/scsi/aic7xxx/aic7xxx_proc.c @@ -54,6 +54,49 @@ static void ahc_dump_device_state(struct info_str *info, static int ahc_proc_write_seeprom(struct ahc_softc *ahc, char *buffer, int length); +/* + * Table of syncrates that don't follow the "divisible by 4" + * rule. This table will be expanded in future SCSI specs. + */ +static struct { + u_int period_factor; + u_int period; /* in 100ths of ns */ +} scsi_syncrates[] = { + { 0x08, 625 }, /* FAST-160 */ + { 0x09, 1250 }, /* FAST-80 */ + { 0x0a, 2500 }, /* FAST-40 40MHz */ + { 0x0b, 3030 }, /* FAST-40 33MHz */ + { 0x0c, 5000 } /* FAST-20 */ +}; + +/* + * Return the frequency in kHz corresponding to the given + * sync period factor. + */ +static u_int +ahc_calc_syncsrate(u_int period_factor) +{ + int i; + int num_syncrates; + + num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); + /* See if the period is in the "exception" table */ + for (i = 0; i < num_syncrates; i++) { + + if (period_factor == scsi_syncrates[i].period_factor) { + /* Period in kHz */ + return (100000000 / scsi_syncrates[i].period); + } + } + + /* + * Wasn't in the table, so use the standard + * 4 times conversion. + */ + return (10000000 / (period_factor * 4 * 10)); +} + + static void copy_mem_info(struct info_str *info, char *data, int len) { @@ -106,7 +149,7 @@ ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo) speed = 3300; freq = 0; if (tinfo->offset != 0) { - freq = aic_calc_syncsrate(tinfo->period); + freq = ahc_calc_syncsrate(tinfo->period); speed = freq; } speed *= (0x01 << tinfo->width); diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped index 7c1390ed1179..2ce1febca207 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped +++ b/drivers/scsi/aic7xxx/aic7xxx_reg.h_shipped @@ -2,8 +2,8 @@ * DO NOT EDIT - This file is automatically generated * from the following source files: * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $ - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $ */ typedef int (ahc_reg_print_t)(u_int, u_int *, u_int); typedef struct ahc_reg_parse_entry { @@ -1298,7 +1298,6 @@ ahc_reg_print_t ahc_sg_cache_pre_print; #define CMDSIZE_TABLE_TAIL 0x34 #define MWI_RESIDUAL 0x38 -#define TARG_IMMEDIATE_SCB 0x38 #define NEXT_QUEUED_SCB 0x39 @@ -1380,6 +1379,7 @@ ahc_reg_print_t ahc_sg_cache_pre_print; #define RETURN_2 0x52 #define LAST_MSG 0x53 +#define TARG_IMMEDIATE_SCB 0x53 #define SCSISEQ_TEMPLATE 0x54 #define ENSELO 0x40 diff --git a/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped index 9c713775d44a..88bfd767c51c 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped +++ b/drivers/scsi/aic7xxx/aic7xxx_reg_print.c_shipped @@ -2,8 +2,8 @@ * DO NOT EDIT - This file is automatically generated * from the following source files: * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $ - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $ */ #include "aic7xxx_osm.h" diff --git a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped index cf411368a871..4cee08521e75 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped +++ b/drivers/scsi/aic7xxx/aic7xxx_seq.h_shipped @@ -2,13 +2,13 @@ * DO NOT EDIT - This file is automatically generated * from the following source files: * - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#56 $ - * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#39 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.seq#58 $ + * $Id: //depot/aic7xxx/aic7xxx/aic7xxx.reg#40 $ */ static uint8_t seqprog[] = { 0xb2, 0x00, 0x00, 0x08, 0xf7, 0x11, 0x22, 0x08, - 0x00, 0x65, 0xec, 0x59, + 0x00, 0x65, 0xee, 0x59, 0xf7, 0x01, 0x02, 0x08, 0xff, 0x6a, 0x24, 0x08, 0x40, 0x00, 0x40, 0x68, @@ -21,15 +21,15 @@ static uint8_t seqprog[] = { 0x01, 0x4d, 0xc8, 0x30, 0x00, 0x4c, 0x12, 0x70, 0x01, 0x39, 0xa2, 0x30, - 0x00, 0x6a, 0xc0, 0x5e, + 0x00, 0x6a, 0xc2, 0x5e, 0x01, 0x51, 0x20, 0x31, 0x01, 0x57, 0xae, 0x00, 0x0d, 0x6a, 0x76, 0x00, - 0x00, 0x51, 0x12, 0x5e, + 0x00, 0x51, 0x14, 0x5e, 0x01, 0x51, 0xc8, 0x30, 0x00, 0x39, 0xc8, 0x60, 0x00, 0xbb, 0x30, 0x70, - 0xc1, 0x6a, 0xd8, 0x5e, + 0xc1, 0x6a, 0xda, 0x5e, 0x01, 0xbf, 0x72, 0x30, 0x01, 0x40, 0x7e, 0x31, 0x01, 0x90, 0x80, 0x30, @@ -49,10 +49,10 @@ static uint8_t seqprog[] = { 0x08, 0x6a, 0x78, 0x00, 0x01, 0x50, 0xc8, 0x30, 0xe0, 0x6a, 0xcc, 0x00, - 0x48, 0x6a, 0xfc, 0x5d, + 0x48, 0x6a, 0xfe, 0x5d, 0x01, 0x6a, 0xdc, 0x01, 0x88, 0x6a, 0xcc, 0x00, - 0x48, 0x6a, 0xfc, 0x5d, + 0x48, 0x6a, 0xfe, 0x5d, 0x01, 0x6a, 0x26, 0x01, 0xf0, 0x19, 0x7a, 0x08, 0x0f, 0x18, 0xc8, 0x08, @@ -93,7 +93,7 @@ static uint8_t seqprog[] = { 0x00, 0x65, 0x20, 0x41, 0x02, 0x57, 0xae, 0x00, 0x00, 0x65, 0x9e, 0x40, - 0x61, 0x6a, 0xd8, 0x5e, + 0x61, 0x6a, 0xda, 0x5e, 0x08, 0x51, 0x20, 0x71, 0x02, 0x0b, 0xb2, 0x78, 0x00, 0x65, 0xae, 0x40, @@ -106,7 +106,7 @@ static uint8_t seqprog[] = { 0x80, 0x3d, 0x7a, 0x00, 0x20, 0x6a, 0x16, 0x00, 0x00, 0x65, 0xcc, 0x41, - 0x00, 0x65, 0xb2, 0x5e, + 0x00, 0x65, 0xb4, 0x5e, 0x00, 0x65, 0x12, 0x40, 0x20, 0x11, 0xd2, 0x68, 0x20, 0x6a, 0x18, 0x00, @@ -140,27 +140,27 @@ static uint8_t seqprog[] = { 0x80, 0x0b, 0xc4, 0x79, 0x12, 0x01, 0x02, 0x00, 0x01, 0xab, 0xac, 0x30, - 0xe4, 0x6a, 0x6e, 0x5d, + 0xe4, 0x6a, 0x70, 0x5d, 0x40, 0x6a, 0x16, 0x00, - 0x80, 0x3e, 0x84, 0x5d, + 0x80, 0x3e, 0x86, 0x5d, 0x20, 0xb8, 0x18, 0x79, - 0x20, 0x6a, 0x84, 0x5d, - 0x00, 0xab, 0x84, 0x5d, + 0x20, 0x6a, 0x86, 0x5d, + 0x00, 0xab, 0x86, 0x5d, 0x01, 0xa9, 0x78, 0x30, 0x10, 0xb8, 0x20, 0x79, - 0xe4, 0x6a, 0x6e, 0x5d, + 0xe4, 0x6a, 0x70, 0x5d, 0x00, 0x65, 0xae, 0x40, 0x10, 0x03, 0x3c, 0x69, 0x08, 0x3c, 0x5a, 0x69, 0x04, 0x3c, 0x92, 0x69, 0x02, 0x3c, 0x98, 0x69, 0x01, 0x3c, 0x44, 0x79, - 0xff, 0x6a, 0x70, 0x00, + 0xff, 0x6a, 0xa6, 0x00, 0x00, 0x65, 0xa4, 0x59, - 0x00, 0x6a, 0xc0, 0x5e, - 0xff, 0x38, 0x30, 0x71, + 0x00, 0x6a, 0xc2, 0x5e, + 0xff, 0x53, 0x30, 0x71, 0x0d, 0x6a, 0x76, 0x00, - 0x00, 0x38, 0x12, 0x5e, + 0x00, 0x53, 0x14, 0x5e, 0x00, 0x65, 0xea, 0x58, 0x12, 0x01, 0x02, 0x00, 0x00, 0x65, 0x18, 0x41, @@ -168,10 +168,10 @@ static uint8_t seqprog[] = { 0x00, 0x65, 0xf2, 0x58, 0xfd, 0x57, 0xae, 0x08, 0x00, 0x65, 0xae, 0x40, - 0xe4, 0x6a, 0x6e, 0x5d, + 0xe4, 0x6a, 0x70, 0x5d, 0x20, 0x3c, 0x4a, 0x79, - 0x02, 0x6a, 0x84, 0x5d, - 0x04, 0x6a, 0x84, 0x5d, + 0x02, 0x6a, 0x86, 0x5d, + 0x04, 0x6a, 0x86, 0x5d, 0x01, 0x03, 0x4c, 0x69, 0xf7, 0x11, 0x22, 0x08, 0xff, 0x6a, 0x24, 0x08, @@ -182,13 +182,13 @@ static uint8_t seqprog[] = { 0x80, 0x86, 0xc8, 0x08, 0x01, 0x4f, 0xc8, 0x30, 0x00, 0x50, 0x6c, 0x61, - 0xc4, 0x6a, 0x6e, 0x5d, + 0xc4, 0x6a, 0x70, 0x5d, 0x40, 0x3c, 0x68, 0x79, - 0x28, 0x6a, 0x84, 0x5d, + 0x28, 0x6a, 0x86, 0x5d, 0x00, 0x65, 0x4c, 0x41, - 0x08, 0x6a, 0x84, 0x5d, + 0x08, 0x6a, 0x86, 0x5d, 0x00, 0x65, 0x4c, 0x41, - 0x84, 0x6a, 0x6e, 0x5d, + 0x84, 0x6a, 0x70, 0x5d, 0x00, 0x65, 0xf2, 0x58, 0x01, 0x66, 0xc8, 0x30, 0x01, 0x64, 0xd8, 0x31, @@ -208,16 +208,16 @@ static uint8_t seqprog[] = { 0xf7, 0x3c, 0x78, 0x08, 0x00, 0x65, 0x20, 0x41, 0x40, 0xaa, 0x7e, 0x10, - 0x04, 0xaa, 0x6e, 0x5d, - 0x00, 0x65, 0x56, 0x42, - 0xc4, 0x6a, 0x6e, 0x5d, + 0x04, 0xaa, 0x70, 0x5d, + 0x00, 0x65, 0x58, 0x42, + 0xc4, 0x6a, 0x70, 0x5d, 0xc0, 0x6a, 0x7e, 0x00, - 0x00, 0xa8, 0x84, 0x5d, + 0x00, 0xa8, 0x86, 0x5d, 0xe4, 0x6a, 0x06, 0x00, - 0x00, 0x6a, 0x84, 0x5d, + 0x00, 0x6a, 0x86, 0x5d, 0x00, 0x65, 0x4c, 0x41, 0x10, 0x3c, 0xa8, 0x69, - 0x00, 0xbb, 0x8a, 0x44, + 0x00, 0xbb, 0x8c, 0x44, 0x18, 0x6a, 0xda, 0x01, 0x01, 0x69, 0xd8, 0x31, 0x1c, 0x6a, 0xd0, 0x01, @@ -227,31 +227,32 @@ static uint8_t seqprog[] = { 0x01, 0x93, 0x26, 0x01, 0x03, 0x6a, 0x2a, 0x01, 0x01, 0x69, 0x32, 0x31, - 0x1c, 0x6a, 0xe0, 0x5d, + 0x1c, 0x6a, 0xe2, 0x5d, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xa8, 0x5e, + 0x00, 0x65, 0xaa, 0x5e, 0x01, 0x50, 0xa0, 0x18, 0x02, 0x6a, 0x22, 0x05, 0x1a, 0x01, 0x02, 0x00, 0x80, 0x6a, 0x74, 0x00, 0x40, 0x6a, 0x78, 0x00, 0x40, 0x6a, 0x16, 0x00, - 0x00, 0x65, 0xd8, 0x5d, + 0x00, 0x65, 0xda, 0x5d, 0x01, 0x3f, 0xc8, 0x30, - 0xbf, 0x64, 0x56, 0x7a, - 0x80, 0x64, 0x9e, 0x73, - 0xa0, 0x64, 0x00, 0x74, - 0xc0, 0x64, 0xf4, 0x73, - 0xe0, 0x64, 0x30, 0x74, - 0x01, 0x6a, 0xd8, 0x5e, + 0xbf, 0x64, 0x58, 0x7a, + 0x80, 0x64, 0xa0, 0x73, + 0xa0, 0x64, 0x02, 0x74, + 0xc0, 0x64, 0xf6, 0x73, + 0xe0, 0x64, 0x32, 0x74, + 0x01, 0x6a, 0xda, 0x5e, 0x00, 0x65, 0xcc, 0x41, 0xf7, 0x11, 0x22, 0x08, 0x01, 0x06, 0xd4, 0x30, 0xff, 0x6a, 0x24, 0x08, 0xf7, 0x01, 0x02, 0x08, - 0x09, 0x0c, 0xe6, 0x79, + 0xc0, 0x6a, 0x78, 0x00, + 0x09, 0x0c, 0xe8, 0x79, 0x08, 0x0c, 0x04, 0x68, - 0xb1, 0x6a, 0xd8, 0x5e, + 0xb1, 0x6a, 0xda, 0x5e, 0xff, 0x6a, 0x26, 0x09, 0x12, 0x01, 0x02, 0x00, 0x02, 0x6a, 0x08, 0x30, @@ -264,29 +265,29 @@ static uint8_t seqprog[] = { 0x00, 0xa5, 0x4a, 0x21, 0x00, 0xa6, 0x4c, 0x21, 0x00, 0xa7, 0x4e, 0x25, - 0x08, 0xeb, 0xdc, 0x7e, - 0x80, 0xeb, 0x06, 0x7a, + 0x08, 0xeb, 0xde, 0x7e, + 0x80, 0xeb, 0x08, 0x7a, 0xff, 0x6a, 0xd6, 0x09, - 0x08, 0xeb, 0x0a, 0x6a, + 0x08, 0xeb, 0x0c, 0x6a, 0xff, 0x6a, 0xd4, 0x0c, - 0x80, 0xa3, 0xdc, 0x6e, - 0x88, 0xeb, 0x20, 0x72, - 0x08, 0xeb, 0xdc, 0x6e, - 0x04, 0xea, 0x24, 0xe2, - 0x08, 0xee, 0xdc, 0x6e, + 0x80, 0xa3, 0xde, 0x6e, + 0x88, 0xeb, 0x22, 0x72, + 0x08, 0xeb, 0xde, 0x6e, + 0x04, 0xea, 0x26, 0xe2, + 0x08, 0xee, 0xde, 0x6e, 0x04, 0x6a, 0xd0, 0x81, 0x05, 0xa4, 0xc0, 0x89, 0x03, 0xa5, 0xc2, 0x31, 0x09, 0x6a, 0xd6, 0x05, - 0x00, 0x65, 0x08, 0x5a, + 0x00, 0x65, 0x0a, 0x5a, 0x06, 0xa4, 0xd4, 0x89, - 0x80, 0x94, 0xdc, 0x7e, + 0x80, 0x94, 0xde, 0x7e, 0x07, 0xe9, 0x10, 0x31, 0x01, 0xe9, 0x46, 0x31, - 0x00, 0xa3, 0xba, 0x5e, - 0x00, 0x65, 0xfa, 0x59, + 0x00, 0xa3, 0xbc, 0x5e, + 0x00, 0x65, 0xfc, 0x59, 0x01, 0xa4, 0xca, 0x30, - 0x80, 0xa3, 0x34, 0x7a, + 0x80, 0xa3, 0x36, 0x7a, 0x02, 0x65, 0xca, 0x00, 0x01, 0x65, 0xf8, 0x31, 0x80, 0x93, 0x26, 0x01, @@ -294,162 +295,162 @@ static uint8_t seqprog[] = { 0x01, 0x8c, 0xc8, 0x30, 0x00, 0x88, 0xc8, 0x18, 0x02, 0x64, 0xc8, 0x88, - 0xff, 0x64, 0xdc, 0x7e, - 0xff, 0x8d, 0x4a, 0x6a, - 0xff, 0x8e, 0x4a, 0x6a, + 0xff, 0x64, 0xde, 0x7e, + 0xff, 0x8d, 0x4c, 0x6a, + 0xff, 0x8e, 0x4c, 0x6a, 0x03, 0x8c, 0xd4, 0x98, - 0x00, 0x65, 0xdc, 0x56, + 0x00, 0x65, 0xde, 0x56, 0x01, 0x64, 0x70, 0x30, 0xff, 0x64, 0xc8, 0x10, 0x01, 0x64, 0xc8, 0x18, 0x00, 0x8c, 0x18, 0x19, 0xff, 0x8d, 0x1a, 0x21, 0xff, 0x8e, 0x1c, 0x25, - 0xc0, 0x3c, 0x5a, 0x7a, - 0x21, 0x6a, 0xd8, 0x5e, + 0xc0, 0x3c, 0x5c, 0x7a, + 0x21, 0x6a, 0xda, 0x5e, 0xa8, 0x6a, 0x76, 0x00, 0x79, 0x6a, 0x76, 0x00, - 0x40, 0x3f, 0x62, 0x6a, + 0x40, 0x3f, 0x64, 0x6a, 0x04, 0x3b, 0x76, 0x00, 0x04, 0x6a, 0xd4, 0x81, - 0x20, 0x3c, 0x6a, 0x7a, - 0x51, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0x82, 0x42, + 0x20, 0x3c, 0x6c, 0x7a, + 0x51, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0x84, 0x42, 0x20, 0x3c, 0x78, 0x00, - 0x00, 0xb3, 0xba, 0x5e, + 0x00, 0xb3, 0xbc, 0x5e, 0x07, 0xac, 0x10, 0x31, 0x05, 0xb3, 0x46, 0x31, 0x88, 0x6a, 0xcc, 0x00, - 0xac, 0x6a, 0xee, 0x5d, + 0xac, 0x6a, 0xf0, 0x5d, 0xa3, 0x6a, 0xcc, 0x00, - 0xb3, 0x6a, 0xf2, 0x5d, - 0x00, 0x65, 0x3a, 0x5a, + 0xb3, 0x6a, 0xf4, 0x5d, + 0x00, 0x65, 0x3c, 0x5a, 0xfd, 0xa4, 0x48, 0x09, 0x03, 0x8c, 0x10, 0x30, - 0x00, 0x65, 0xe6, 0x5d, - 0x01, 0xa4, 0x94, 0x7a, + 0x00, 0x65, 0xe8, 0x5d, + 0x01, 0xa4, 0x96, 0x7a, 0x04, 0x3b, 0x76, 0x08, 0x01, 0x3b, 0x26, 0x31, 0x80, 0x02, 0x04, 0x00, - 0x10, 0x0c, 0x8a, 0x7a, - 0x03, 0x9e, 0x8c, 0x6a, + 0x10, 0x0c, 0x8c, 0x7a, + 0x03, 0x9e, 0x8e, 0x6a, 0x7f, 0x02, 0x04, 0x08, - 0x91, 0x6a, 0xd8, 0x5e, + 0x91, 0x6a, 0xda, 0x5e, 0x00, 0x65, 0xcc, 0x41, 0x01, 0xa4, 0xca, 0x30, - 0x80, 0xa3, 0x9a, 0x7a, + 0x80, 0xa3, 0x9c, 0x7a, 0x02, 0x65, 0xca, 0x00, 0x01, 0x65, 0xf8, 0x31, 0x01, 0x3b, 0x26, 0x31, - 0x00, 0x65, 0x0e, 0x5a, - 0x01, 0xfc, 0xa8, 0x6a, - 0x80, 0x0b, 0x9e, 0x6a, - 0x10, 0x0c, 0x9e, 0x7a, - 0x20, 0x93, 0x9e, 0x6a, + 0x00, 0x65, 0x10, 0x5a, + 0x01, 0xfc, 0xaa, 0x6a, + 0x80, 0x0b, 0xa0, 0x6a, + 0x10, 0x0c, 0xa0, 0x7a, + 0x20, 0x93, 0xa0, 0x6a, 0x02, 0x93, 0x26, 0x01, - 0x02, 0xfc, 0xb2, 0x7a, - 0x40, 0x0d, 0xc6, 0x6a, + 0x02, 0xfc, 0xb4, 0x7a, + 0x40, 0x0d, 0xc8, 0x6a, 0x01, 0xa4, 0x48, 0x01, - 0x00, 0x65, 0xc6, 0x42, - 0x40, 0x0d, 0xb8, 0x6a, - 0x00, 0x65, 0x0e, 0x5a, - 0x00, 0x65, 0xaa, 0x42, - 0x80, 0xfc, 0xc2, 0x7a, - 0x80, 0xa4, 0xc2, 0x6a, + 0x00, 0x65, 0xc8, 0x42, + 0x40, 0x0d, 0xba, 0x6a, + 0x00, 0x65, 0x10, 0x5a, + 0x00, 0x65, 0xac, 0x42, + 0x80, 0xfc, 0xc4, 0x7a, + 0x80, 0xa4, 0xc4, 0x6a, 0xff, 0xa5, 0x4a, 0x19, 0xff, 0xa6, 0x4c, 0x21, 0xff, 0xa7, 0x4e, 0x21, 0xf8, 0xfc, 0x48, 0x09, 0x7f, 0xa3, 0x46, 0x09, - 0x04, 0x3b, 0xe2, 0x6a, + 0x04, 0x3b, 0xe4, 0x6a, 0x02, 0x93, 0x26, 0x01, - 0x01, 0x94, 0xc8, 0x7a, - 0x01, 0x94, 0xc8, 0x7a, - 0x01, 0x94, 0xc8, 0x7a, - 0x01, 0x94, 0xc8, 0x7a, - 0x01, 0x94, 0xc8, 0x7a, - 0x01, 0xa4, 0xe0, 0x7a, - 0x01, 0xfc, 0xd6, 0x7a, - 0x01, 0x94, 0xe2, 0x6a, - 0x01, 0x94, 0xe2, 0x6a, - 0x01, 0x94, 0xe2, 0x6a, - 0x00, 0x65, 0x82, 0x42, - 0x01, 0x94, 0xe0, 0x7a, - 0x10, 0x94, 0xe2, 0x6a, + 0x01, 0x94, 0xca, 0x7a, + 0x01, 0x94, 0xca, 0x7a, + 0x01, 0x94, 0xca, 0x7a, + 0x01, 0x94, 0xca, 0x7a, + 0x01, 0x94, 0xca, 0x7a, + 0x01, 0xa4, 0xe2, 0x7a, + 0x01, 0xfc, 0xd8, 0x7a, + 0x01, 0x94, 0xe4, 0x6a, + 0x01, 0x94, 0xe4, 0x6a, + 0x01, 0x94, 0xe4, 0x6a, + 0x00, 0x65, 0x84, 0x42, + 0x01, 0x94, 0xe2, 0x7a, + 0x10, 0x94, 0xe4, 0x6a, 0xd7, 0x93, 0x26, 0x09, - 0x28, 0x93, 0xe6, 0x6a, + 0x28, 0x93, 0xe8, 0x6a, 0x01, 0x85, 0x0a, 0x01, - 0x02, 0xfc, 0xee, 0x6a, + 0x02, 0xfc, 0xf0, 0x6a, 0x01, 0x14, 0x46, 0x31, 0xff, 0x6a, 0x10, 0x09, 0xfe, 0x85, 0x0a, 0x09, - 0xff, 0x38, 0xfc, 0x6a, - 0x80, 0xa3, 0xfc, 0x7a, - 0x80, 0x0b, 0xfa, 0x7a, - 0x04, 0x3b, 0xfc, 0x7a, + 0xff, 0x38, 0xfe, 0x6a, + 0x80, 0xa3, 0xfe, 0x7a, + 0x80, 0x0b, 0xfc, 0x7a, + 0x04, 0x3b, 0xfe, 0x7a, 0xbf, 0x3b, 0x76, 0x08, 0x01, 0x3b, 0x26, 0x31, - 0x00, 0x65, 0x0e, 0x5a, - 0x01, 0x0b, 0x0a, 0x6b, - 0x10, 0x0c, 0xfe, 0x7a, - 0x04, 0x93, 0x08, 0x6b, - 0x01, 0x94, 0x06, 0x7b, - 0x10, 0x94, 0x08, 0x6b, + 0x00, 0x65, 0x10, 0x5a, + 0x01, 0x0b, 0x0c, 0x6b, + 0x10, 0x0c, 0x00, 0x7b, + 0x04, 0x93, 0x0a, 0x6b, + 0x01, 0x94, 0x08, 0x7b, + 0x10, 0x94, 0x0a, 0x6b, 0xc7, 0x93, 0x26, 0x09, 0x01, 0x99, 0xd4, 0x30, - 0x38, 0x93, 0x0c, 0x6b, - 0xff, 0x08, 0x5a, 0x6b, - 0xff, 0x09, 0x5a, 0x6b, - 0xff, 0x0a, 0x5a, 0x6b, - 0xff, 0x38, 0x28, 0x7b, + 0x38, 0x93, 0x0e, 0x6b, + 0xff, 0x08, 0x5c, 0x6b, + 0xff, 0x09, 0x5c, 0x6b, + 0xff, 0x0a, 0x5c, 0x6b, + 0xff, 0x38, 0x2a, 0x7b, 0x04, 0x14, 0x10, 0x31, 0x01, 0x38, 0x18, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x88, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0xf4, 0x5d, - 0x00, 0x38, 0xe0, 0x5d, + 0x14, 0x6a, 0xf6, 0x5d, + 0x00, 0x38, 0xe2, 0x5d, 0xff, 0x6a, 0x70, 0x08, - 0x00, 0x65, 0x54, 0x43, - 0x80, 0xa3, 0x2e, 0x7b, + 0x00, 0x65, 0x56, 0x43, + 0x80, 0xa3, 0x30, 0x7b, 0x01, 0xa4, 0x48, 0x01, - 0x00, 0x65, 0x5a, 0x43, - 0x08, 0xeb, 0x34, 0x7b, - 0x00, 0x65, 0x0e, 0x5a, - 0x08, 0xeb, 0x30, 0x6b, + 0x00, 0x65, 0x5c, 0x43, + 0x08, 0xeb, 0x36, 0x7b, + 0x00, 0x65, 0x10, 0x5a, + 0x08, 0xeb, 0x32, 0x6b, 0x07, 0xe9, 0x10, 0x31, 0x01, 0xe9, 0xca, 0x30, 0x01, 0x65, 0x46, 0x31, - 0x00, 0x6a, 0xba, 0x5e, + 0x00, 0x6a, 0xbc, 0x5e, 0x88, 0x6a, 0xcc, 0x00, - 0xa4, 0x6a, 0xf4, 0x5d, - 0x08, 0x6a, 0xe0, 0x5d, + 0xa4, 0x6a, 0xf6, 0x5d, + 0x08, 0x6a, 0xe2, 0x5d, 0x0d, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xa8, 0x5e, + 0x00, 0x65, 0xaa, 0x5e, 0x88, 0x6a, 0xcc, 0x00, - 0x00, 0x65, 0x8a, 0x5e, + 0x00, 0x65, 0x8c, 0x5e, 0x01, 0x99, 0x46, 0x31, - 0x00, 0xa3, 0xba, 0x5e, + 0x00, 0xa3, 0xbc, 0x5e, 0x01, 0x88, 0x10, 0x31, - 0x00, 0x65, 0x3a, 0x5a, - 0x00, 0x65, 0xfa, 0x59, + 0x00, 0x65, 0x3c, 0x5a, + 0x00, 0x65, 0xfc, 0x59, 0x03, 0x8c, 0x10, 0x30, - 0x00, 0x65, 0xe6, 0x5d, - 0x80, 0x0b, 0x82, 0x6a, - 0x80, 0x0b, 0x62, 0x6b, - 0x01, 0x0c, 0x5c, 0x7b, - 0x10, 0x0c, 0x82, 0x7a, - 0x03, 0x9e, 0x82, 0x6a, - 0x00, 0x65, 0x04, 0x5a, - 0x00, 0x6a, 0xba, 0x5e, - 0x01, 0xa4, 0x82, 0x6b, - 0xff, 0x38, 0x78, 0x7b, + 0x00, 0x65, 0xe8, 0x5d, + 0x80, 0x0b, 0x84, 0x6a, + 0x80, 0x0b, 0x64, 0x6b, + 0x01, 0x0c, 0x5e, 0x7b, + 0x10, 0x0c, 0x84, 0x7a, + 0x03, 0x9e, 0x84, 0x6a, + 0x00, 0x65, 0x06, 0x5a, + 0x00, 0x6a, 0xbc, 0x5e, + 0x01, 0xa4, 0x84, 0x6b, + 0xff, 0x38, 0x7a, 0x7b, 0x01, 0x38, 0xc8, 0x30, 0x00, 0x08, 0x40, 0x19, 0xff, 0x6a, 0xc8, 0x08, 0x00, 0x09, 0x42, 0x21, 0x00, 0x0a, 0x44, 0x21, 0xff, 0x6a, 0x70, 0x08, - 0x00, 0x65, 0x7a, 0x43, + 0x00, 0x65, 0x7c, 0x43, 0x03, 0x08, 0x40, 0x31, 0x03, 0x08, 0x40, 0x31, 0x01, 0x08, 0x40, 0x31, @@ -461,16 +462,16 @@ static uint8_t seqprog[] = { 0x04, 0x3c, 0xcc, 0x79, 0xfb, 0x3c, 0x78, 0x08, 0x04, 0x93, 0x20, 0x79, - 0x01, 0x0c, 0x8e, 0x6b, + 0x01, 0x0c, 0x90, 0x6b, 0x80, 0xba, 0x20, 0x79, 0x80, 0x04, 0x20, 0x79, - 0xe4, 0x6a, 0x6e, 0x5d, - 0x23, 0x6a, 0x84, 0x5d, - 0x01, 0x6a, 0x84, 0x5d, + 0xe4, 0x6a, 0x70, 0x5d, + 0x23, 0x6a, 0x86, 0x5d, + 0x01, 0x6a, 0x86, 0x5d, 0x00, 0x65, 0x20, 0x41, 0x00, 0x65, 0xcc, 0x41, - 0x80, 0x3c, 0xa2, 0x7b, - 0x21, 0x6a, 0xd8, 0x5e, + 0x80, 0x3c, 0xa4, 0x7b, + 0x21, 0x6a, 0xda, 0x5e, 0x01, 0xbc, 0x18, 0x31, 0x02, 0x6a, 0x1a, 0x31, 0x02, 0x6a, 0xf8, 0x01, @@ -480,16 +481,16 @@ static uint8_t seqprog[] = { 0xff, 0x6a, 0x12, 0x08, 0xff, 0x6a, 0x14, 0x08, 0xf3, 0xbc, 0xd4, 0x18, - 0xa0, 0x6a, 0xc8, 0x53, + 0xa0, 0x6a, 0xca, 0x53, 0x04, 0xa0, 0x10, 0x31, 0xac, 0x6a, 0x26, 0x01, 0x04, 0xa0, 0x10, 0x31, 0x03, 0x08, 0x18, 0x31, 0x88, 0x6a, 0xcc, 0x00, - 0xa0, 0x6a, 0xf4, 0x5d, - 0x00, 0xbc, 0xe0, 0x5d, + 0xa0, 0x6a, 0xf6, 0x5d, + 0x00, 0xbc, 0xe2, 0x5d, 0x3d, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0xe0, 0x43, + 0x00, 0x65, 0xe2, 0x43, 0xff, 0x6a, 0x10, 0x09, 0xa4, 0x6a, 0x26, 0x01, 0x0c, 0xa0, 0x32, 0x31, @@ -499,128 +500,128 @@ static uint8_t seqprog[] = { 0x36, 0x6a, 0x26, 0x01, 0x02, 0x93, 0x26, 0x01, 0x35, 0x6a, 0x26, 0x01, - 0x00, 0x65, 0x9c, 0x5e, - 0x00, 0x65, 0x9c, 0x5e, + 0x00, 0x65, 0x9e, 0x5e, + 0x00, 0x65, 0x9e, 0x5e, 0x02, 0x93, 0x26, 0x01, 0xbf, 0x3c, 0x78, 0x08, - 0x04, 0x0b, 0xe6, 0x6b, - 0x10, 0x0c, 0xe2, 0x7b, - 0x01, 0x03, 0xe6, 0x6b, - 0x20, 0x93, 0xe8, 0x6b, - 0x04, 0x0b, 0xee, 0x6b, + 0x04, 0x0b, 0xe8, 0x6b, + 0x10, 0x0c, 0xe4, 0x7b, + 0x01, 0x03, 0xe8, 0x6b, + 0x20, 0x93, 0xea, 0x6b, + 0x04, 0x0b, 0xf0, 0x6b, 0x40, 0x3c, 0x78, 0x00, 0xc7, 0x93, 0x26, 0x09, - 0x38, 0x93, 0xf0, 0x6b, + 0x38, 0x93, 0xf2, 0x6b, 0x00, 0x65, 0xcc, 0x41, - 0x80, 0x3c, 0x56, 0x6c, + 0x80, 0x3c, 0x58, 0x6c, 0x01, 0x06, 0x50, 0x31, 0x80, 0xb8, 0x70, 0x01, 0x00, 0x65, 0xcc, 0x41, 0x10, 0x3f, 0x06, 0x00, 0x10, 0x6a, 0x06, 0x00, 0x01, 0x3a, 0xca, 0x30, - 0x80, 0x65, 0x1c, 0x64, - 0x10, 0xb8, 0x40, 0x6c, + 0x80, 0x65, 0x1e, 0x64, + 0x10, 0xb8, 0x42, 0x6c, 0xc0, 0x3e, 0xca, 0x00, - 0x40, 0xb8, 0x0c, 0x6c, + 0x40, 0xb8, 0x0e, 0x6c, 0xbf, 0x65, 0xca, 0x08, - 0x20, 0xb8, 0x20, 0x7c, + 0x20, 0xb8, 0x22, 0x7c, 0x01, 0x65, 0x0c, 0x30, - 0x00, 0x65, 0xd8, 0x5d, - 0xa0, 0x3f, 0x28, 0x64, + 0x00, 0x65, 0xda, 0x5d, + 0xa0, 0x3f, 0x2a, 0x64, 0x23, 0xb8, 0x0c, 0x08, - 0x00, 0x65, 0xd8, 0x5d, - 0xa0, 0x3f, 0x28, 0x64, - 0x00, 0xbb, 0x20, 0x44, - 0xff, 0x65, 0x20, 0x64, - 0x00, 0x65, 0x40, 0x44, + 0x00, 0x65, 0xda, 0x5d, + 0xa0, 0x3f, 0x2a, 0x64, + 0x00, 0xbb, 0x22, 0x44, + 0xff, 0x65, 0x22, 0x64, + 0x00, 0x65, 0x42, 0x44, 0x40, 0x6a, 0x18, 0x00, 0x01, 0x65, 0x0c, 0x30, - 0x00, 0x65, 0xd8, 0x5d, - 0xa0, 0x3f, 0xfc, 0x73, + 0x00, 0x65, 0xda, 0x5d, + 0xa0, 0x3f, 0xfe, 0x73, 0x40, 0x6a, 0x18, 0x00, 0x01, 0x3a, 0xa6, 0x30, 0x08, 0x6a, 0x74, 0x00, 0x00, 0x65, 0xcc, 0x41, - 0x64, 0x6a, 0x68, 0x5d, - 0x80, 0x64, 0xd8, 0x6c, - 0x04, 0x64, 0x9a, 0x74, - 0x02, 0x64, 0xaa, 0x74, - 0x00, 0x6a, 0x60, 0x74, - 0x03, 0x64, 0xc8, 0x74, - 0x23, 0x64, 0x48, 0x74, - 0x08, 0x64, 0x5c, 0x74, - 0x61, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0xd8, 0x5d, + 0x64, 0x6a, 0x6a, 0x5d, + 0x80, 0x64, 0xda, 0x6c, + 0x04, 0x64, 0x9c, 0x74, + 0x02, 0x64, 0xac, 0x74, + 0x00, 0x6a, 0x62, 0x74, + 0x03, 0x64, 0xca, 0x74, + 0x23, 0x64, 0x4a, 0x74, + 0x08, 0x64, 0x5e, 0x74, + 0x61, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0xda, 0x5d, 0x08, 0x51, 0xce, 0x71, - 0x00, 0x65, 0x40, 0x44, - 0x80, 0x04, 0x5a, 0x7c, - 0x51, 0x6a, 0x5e, 0x5d, - 0x01, 0x51, 0x5a, 0x64, - 0x01, 0xa4, 0x52, 0x7c, - 0x80, 0xba, 0x5c, 0x6c, - 0x41, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0x5c, 0x44, - 0x21, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0x5c, 0x44, - 0x07, 0x6a, 0x54, 0x5d, + 0x00, 0x65, 0x42, 0x44, + 0x80, 0x04, 0x5c, 0x7c, + 0x51, 0x6a, 0x60, 0x5d, + 0x01, 0x51, 0x5c, 0x64, + 0x01, 0xa4, 0x54, 0x7c, + 0x80, 0xba, 0x5e, 0x6c, + 0x41, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0x5e, 0x44, + 0x21, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0x5e, 0x44, + 0x07, 0x6a, 0x56, 0x5d, 0x01, 0x06, 0xd4, 0x30, 0x00, 0x65, 0xcc, 0x41, - 0x80, 0xb8, 0x56, 0x7c, - 0xc0, 0x3c, 0x6a, 0x7c, - 0x80, 0x3c, 0x56, 0x6c, - 0xff, 0xa8, 0x6a, 0x6c, - 0x40, 0x3c, 0x56, 0x6c, - 0x10, 0xb8, 0x6e, 0x7c, - 0xa1, 0x6a, 0xd8, 0x5e, - 0x01, 0xb4, 0x74, 0x6c, - 0x02, 0xb4, 0x76, 0x6c, - 0x01, 0xa4, 0x76, 0x7c, - 0xff, 0xa8, 0x86, 0x7c, + 0x80, 0xb8, 0x58, 0x7c, + 0xc0, 0x3c, 0x6c, 0x7c, + 0x80, 0x3c, 0x58, 0x6c, + 0xff, 0xa8, 0x6c, 0x6c, + 0x40, 0x3c, 0x58, 0x6c, + 0x10, 0xb8, 0x70, 0x7c, + 0xa1, 0x6a, 0xda, 0x5e, + 0x01, 0xb4, 0x76, 0x6c, + 0x02, 0xb4, 0x78, 0x6c, + 0x01, 0xa4, 0x78, 0x7c, + 0xff, 0xa8, 0x88, 0x7c, 0x04, 0xb4, 0x68, 0x01, 0x01, 0x6a, 0x76, 0x00, - 0x00, 0xbb, 0x12, 0x5e, - 0xff, 0xa8, 0x86, 0x7c, - 0x71, 0x6a, 0xd8, 0x5e, - 0x40, 0x51, 0x86, 0x64, - 0x00, 0x65, 0xb2, 0x5e, + 0x00, 0xbb, 0x14, 0x5e, + 0xff, 0xa8, 0x88, 0x7c, + 0x71, 0x6a, 0xda, 0x5e, + 0x40, 0x51, 0x88, 0x64, + 0x00, 0x65, 0xb4, 0x5e, 0x00, 0x65, 0xde, 0x41, - 0x00, 0xbb, 0x8a, 0x5c, + 0x00, 0xbb, 0x8c, 0x5c, 0x00, 0x65, 0xde, 0x41, - 0x00, 0x65, 0xb2, 0x5e, + 0x00, 0x65, 0xb4, 0x5e, 0x01, 0x65, 0xa2, 0x30, 0x01, 0xf8, 0xc8, 0x30, 0x01, 0x4e, 0xc8, 0x30, - 0x00, 0x6a, 0xb6, 0xdd, - 0x00, 0x51, 0xc8, 0x5d, + 0x00, 0x6a, 0xb8, 0xdd, + 0x00, 0x51, 0xca, 0x5d, 0x01, 0x4e, 0x9c, 0x18, 0x02, 0x6a, 0x22, 0x05, - 0xc0, 0x3c, 0x56, 0x6c, + 0xc0, 0x3c, 0x58, 0x6c, 0x04, 0xb8, 0x70, 0x01, - 0x00, 0x65, 0xd4, 0x5e, + 0x00, 0x65, 0xd6, 0x5e, 0x20, 0xb8, 0xde, 0x69, 0x01, 0xbb, 0xa2, 0x30, 0x3f, 0xba, 0x7c, 0x08, - 0x00, 0xb9, 0xce, 0x5c, + 0x00, 0xb9, 0xd0, 0x5c, 0x00, 0x65, 0xde, 0x41, 0x01, 0x06, 0xd4, 0x30, 0x20, 0x3c, 0xcc, 0x79, - 0x20, 0x3c, 0x5c, 0x7c, - 0x01, 0xa4, 0xb8, 0x7c, + 0x20, 0x3c, 0x5e, 0x7c, + 0x01, 0xa4, 0xba, 0x7c, 0x01, 0xb4, 0x68, 0x01, 0x00, 0x65, 0xcc, 0x41, - 0x00, 0x65, 0x5c, 0x44, + 0x00, 0x65, 0x5e, 0x44, 0x04, 0x14, 0x58, 0x31, 0x01, 0x06, 0xd4, 0x30, 0x08, 0xa0, 0x60, 0x31, 0xac, 0x6a, 0xcc, 0x00, - 0x14, 0x6a, 0xf4, 0x5d, + 0x14, 0x6a, 0xf6, 0x5d, 0x01, 0x06, 0xd4, 0x30, - 0xa0, 0x6a, 0xec, 0x5d, + 0xa0, 0x6a, 0xee, 0x5d, 0x00, 0x65, 0xcc, 0x41, 0xdf, 0x3c, 0x78, 0x08, 0x12, 0x01, 0x02, 0x00, - 0x00, 0x65, 0x5c, 0x44, + 0x00, 0x65, 0x5e, 0x44, 0x4c, 0x65, 0xcc, 0x28, 0x01, 0x3e, 0x20, 0x31, 0xd0, 0x66, 0xcc, 0x18, @@ -631,102 +632,102 @@ static uint8_t seqprog[] = { 0xd0, 0x65, 0xca, 0x18, 0x01, 0x3e, 0x20, 0x31, 0x30, 0x65, 0xd4, 0x18, - 0x00, 0x65, 0xe6, 0x4c, + 0x00, 0x65, 0xe8, 0x4c, 0xe1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, 0x20, 0x65, 0xd4, 0x18, - 0x00, 0x65, 0xee, 0x54, + 0x00, 0x65, 0xf0, 0x54, 0xe1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, 0x20, 0x65, 0xca, 0x18, 0xe0, 0x65, 0xd4, 0x18, - 0x00, 0x65, 0xf8, 0x4c, + 0x00, 0x65, 0xfa, 0x4c, 0xe1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, 0xd0, 0x65, 0xd4, 0x18, - 0x00, 0x65, 0x00, 0x55, + 0x00, 0x65, 0x02, 0x55, 0xe1, 0x6a, 0x22, 0x01, 0xff, 0x6a, 0xd4, 0x08, 0x01, 0x6c, 0xa2, 0x30, - 0xff, 0x51, 0x12, 0x75, - 0x00, 0x51, 0x8e, 0x5d, + 0xff, 0x51, 0x14, 0x75, + 0x00, 0x51, 0x90, 0x5d, 0x01, 0x51, 0x20, 0x31, - 0x00, 0x65, 0x34, 0x45, + 0x00, 0x65, 0x36, 0x45, 0x3f, 0xba, 0xc8, 0x08, - 0x00, 0x3e, 0x34, 0x75, - 0x00, 0x65, 0xb0, 0x5e, + 0x00, 0x3e, 0x36, 0x75, + 0x00, 0x65, 0xb2, 0x5e, 0x80, 0x3c, 0x78, 0x00, 0x01, 0x06, 0xd4, 0x30, - 0x00, 0x65, 0xd8, 0x5d, + 0x00, 0x65, 0xda, 0x5d, 0x01, 0x3c, 0x78, 0x00, - 0xe0, 0x3f, 0x50, 0x65, + 0xe0, 0x3f, 0x52, 0x65, 0x02, 0x3c, 0x78, 0x00, - 0x20, 0x12, 0x50, 0x65, - 0x51, 0x6a, 0x5e, 0x5d, - 0x00, 0x51, 0x8e, 0x5d, - 0x51, 0x6a, 0x5e, 0x5d, + 0x20, 0x12, 0x52, 0x65, + 0x51, 0x6a, 0x60, 0x5d, + 0x00, 0x51, 0x90, 0x5d, + 0x51, 0x6a, 0x60, 0x5d, 0x01, 0x51, 0x20, 0x31, 0x04, 0x3c, 0x78, 0x00, 0x01, 0xb9, 0xc8, 0x30, - 0x00, 0x3d, 0x4e, 0x65, + 0x00, 0x3d, 0x50, 0x65, 0x08, 0x3c, 0x78, 0x00, 0x3f, 0xba, 0xc8, 0x08, - 0x00, 0x3e, 0x4e, 0x65, + 0x00, 0x3e, 0x50, 0x65, 0x10, 0x3c, 0x78, 0x00, - 0x04, 0xb8, 0x4e, 0x7d, + 0x04, 0xb8, 0x50, 0x7d, 0xfb, 0xb8, 0x70, 0x09, - 0x20, 0xb8, 0x44, 0x6d, + 0x20, 0xb8, 0x46, 0x6d, 0x01, 0x90, 0xc8, 0x30, 0xff, 0x6a, 0xa2, 0x00, - 0x00, 0x3d, 0xce, 0x5c, + 0x00, 0x3d, 0xd0, 0x5c, 0x01, 0x64, 0x20, 0x31, 0xff, 0x6a, 0x78, 0x08, 0x00, 0x65, 0xea, 0x58, - 0x10, 0xb8, 0x5c, 0x7c, - 0xff, 0x6a, 0x54, 0x5d, - 0x00, 0x65, 0x5c, 0x44, - 0x00, 0x65, 0xb0, 0x5e, - 0x31, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0x5c, 0x44, + 0x10, 0xb8, 0x5e, 0x7c, + 0xff, 0x6a, 0x56, 0x5d, + 0x00, 0x65, 0x5e, 0x44, + 0x00, 0x65, 0xb2, 0x5e, + 0x31, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0x5e, 0x44, 0x10, 0x3f, 0x06, 0x00, 0x10, 0x6a, 0x06, 0x00, 0x01, 0x65, 0x74, 0x34, - 0x81, 0x6a, 0xd8, 0x5e, - 0x00, 0x65, 0x60, 0x45, + 0x81, 0x6a, 0xda, 0x5e, + 0x00, 0x65, 0x62, 0x45, 0x01, 0x06, 0xd4, 0x30, - 0x01, 0x0c, 0x60, 0x7d, - 0x04, 0x0c, 0x5a, 0x6d, + 0x01, 0x0c, 0x62, 0x7d, + 0x04, 0x0c, 0x5c, 0x6d, 0xe0, 0x03, 0x7e, 0x08, 0xe0, 0x3f, 0xcc, 0x61, 0x01, 0x65, 0xcc, 0x30, 0x01, 0x12, 0xda, 0x34, 0x01, 0x06, 0xd4, 0x34, - 0x01, 0x03, 0x6e, 0x6d, + 0x01, 0x03, 0x70, 0x6d, 0x40, 0x03, 0xcc, 0x08, 0x01, 0x65, 0x06, 0x30, 0x40, 0x65, 0xc8, 0x08, - 0x00, 0x66, 0x7c, 0x75, - 0x40, 0x65, 0x7c, 0x7d, - 0x00, 0x65, 0x7c, 0x5d, + 0x00, 0x66, 0x7e, 0x75, + 0x40, 0x65, 0x7e, 0x7d, + 0x00, 0x65, 0x7e, 0x5d, 0xff, 0x6a, 0xd4, 0x08, 0xff, 0x6a, 0xd4, 0x08, 0xff, 0x6a, 0xd4, 0x08, 0xff, 0x6a, 0xd4, 0x0c, 0x08, 0x01, 0x02, 0x00, - 0x02, 0x0b, 0x86, 0x7d, + 0x02, 0x0b, 0x88, 0x7d, 0x01, 0x65, 0x0c, 0x30, - 0x02, 0x0b, 0x8a, 0x7d, + 0x02, 0x0b, 0x8c, 0x7d, 0xf7, 0x01, 0x02, 0x0c, 0x01, 0x65, 0xc8, 0x30, - 0xff, 0x41, 0xae, 0x75, + 0xff, 0x41, 0xb0, 0x75, 0x01, 0x41, 0x20, 0x31, 0xff, 0x6a, 0xa4, 0x00, - 0x00, 0x65, 0x9e, 0x45, - 0xff, 0xbf, 0xae, 0x75, + 0x00, 0x65, 0xa0, 0x45, + 0xff, 0xbf, 0xb0, 0x75, 0x01, 0x90, 0xa4, 0x30, 0x01, 0xbf, 0x20, 0x31, - 0x00, 0xbb, 0x98, 0x65, - 0xff, 0x52, 0xac, 0x75, + 0x00, 0xbb, 0x9a, 0x65, + 0xff, 0x52, 0xae, 0x75, 0x01, 0xbf, 0xcc, 0x30, 0x01, 0x90, 0xca, 0x30, 0x01, 0x52, 0x20, 0x31, @@ -734,28 +735,28 @@ static uint8_t seqprog[] = { 0x01, 0x65, 0x20, 0x35, 0x01, 0xbf, 0x82, 0x34, 0x01, 0x64, 0xa2, 0x30, - 0x00, 0x6a, 0xc0, 0x5e, + 0x00, 0x6a, 0xc2, 0x5e, 0x0d, 0x6a, 0x76, 0x00, - 0x00, 0x51, 0x12, 0x46, + 0x00, 0x51, 0x14, 0x46, 0x01, 0x65, 0xa4, 0x30, 0xe0, 0x6a, 0xcc, 0x00, - 0x48, 0x6a, 0x06, 0x5e, + 0x48, 0x6a, 0x08, 0x5e, 0x01, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x05, 0x88, 0x6a, 0xcc, 0x00, - 0x48, 0x6a, 0x06, 0x5e, - 0x01, 0x6a, 0xe0, 0x5d, + 0x48, 0x6a, 0x08, 0x5e, + 0x01, 0x6a, 0xe2, 0x5d, 0x01, 0x6a, 0x26, 0x05, 0x01, 0x65, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0xcc, 0x7d, + 0x80, 0xee, 0xce, 0x7d, 0xff, 0x6a, 0xdc, 0x0d, 0x01, 0x65, 0x32, 0x31, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xa8, 0x46, - 0x81, 0x6a, 0xd8, 0x5e, - 0x01, 0x0c, 0xd8, 0x7d, - 0x04, 0x0c, 0xd6, 0x6d, + 0x00, 0x65, 0xaa, 0x46, + 0x81, 0x6a, 0xda, 0x5e, + 0x01, 0x0c, 0xda, 0x7d, + 0x04, 0x0c, 0xd8, 0x6d, 0xe0, 0x03, 0x06, 0x08, 0xe0, 0x03, 0x7e, 0x0c, 0x01, 0x65, 0x18, 0x31, @@ -774,7 +775,7 @@ static uint8_t seqprog[] = { 0x01, 0x6c, 0xda, 0x34, 0x3d, 0x64, 0xa4, 0x28, 0x55, 0x64, 0xc8, 0x28, - 0x00, 0x65, 0x06, 0x46, + 0x00, 0x65, 0x08, 0x46, 0x2e, 0x64, 0xa4, 0x28, 0x66, 0x64, 0xc8, 0x28, 0x00, 0x6c, 0xda, 0x18, @@ -785,63 +786,63 @@ static uint8_t seqprog[] = { 0x00, 0x6c, 0xda, 0x24, 0x01, 0x65, 0xc8, 0x30, 0xe0, 0x6a, 0xcc, 0x00, - 0x44, 0x6a, 0x02, 0x5e, + 0x44, 0x6a, 0x04, 0x5e, 0x01, 0x90, 0xe2, 0x31, - 0x04, 0x3b, 0x26, 0x7e, + 0x04, 0x3b, 0x28, 0x7e, 0x30, 0x6a, 0xd0, 0x01, 0x20, 0x6a, 0xd0, 0x01, 0x1d, 0x6a, 0xdc, 0x01, - 0xdc, 0xee, 0x22, 0x66, - 0x00, 0x65, 0x3e, 0x46, + 0xdc, 0xee, 0x24, 0x66, + 0x00, 0x65, 0x40, 0x46, 0x20, 0x6a, 0xd0, 0x01, 0x01, 0x6a, 0xdc, 0x01, 0x20, 0xa0, 0xd8, 0x31, 0x09, 0xee, 0xdc, 0x01, - 0x80, 0xee, 0x2e, 0x7e, + 0x80, 0xee, 0x30, 0x7e, 0x11, 0x6a, 0xdc, 0x01, - 0x50, 0xee, 0x32, 0x66, + 0x50, 0xee, 0x34, 0x66, 0x20, 0x6a, 0xd0, 0x01, 0x09, 0x6a, 0xdc, 0x01, - 0x88, 0xee, 0x38, 0x66, + 0x88, 0xee, 0x3a, 0x66, 0x19, 0x6a, 0xdc, 0x01, - 0xd8, 0xee, 0x3c, 0x66, + 0xd8, 0xee, 0x3e, 0x66, 0xff, 0x6a, 0xdc, 0x09, - 0x18, 0xee, 0x40, 0x6e, + 0x18, 0xee, 0x42, 0x6e, 0xff, 0x6a, 0xd4, 0x0c, 0x88, 0x6a, 0xcc, 0x00, - 0x44, 0x6a, 0x02, 0x5e, - 0x20, 0x6a, 0xe0, 0x5d, + 0x44, 0x6a, 0x04, 0x5e, + 0x20, 0x6a, 0xe2, 0x5d, 0x01, 0x3b, 0x26, 0x31, - 0x04, 0x3b, 0x5a, 0x6e, + 0x04, 0x3b, 0x5c, 0x6e, 0xa0, 0x6a, 0xca, 0x00, 0x20, 0x65, 0xc8, 0x18, - 0x00, 0x65, 0x98, 0x5e, - 0x00, 0x65, 0x52, 0x66, + 0x00, 0x65, 0x9a, 0x5e, + 0x00, 0x65, 0x54, 0x66, 0x0a, 0x93, 0x26, 0x01, - 0x00, 0x65, 0xa8, 0x46, + 0x00, 0x65, 0xaa, 0x46, 0xa0, 0x6a, 0xcc, 0x00, 0xff, 0x6a, 0xc8, 0x08, - 0x20, 0x94, 0x5e, 0x6e, - 0x10, 0x94, 0x60, 0x6e, - 0x08, 0x94, 0x7a, 0x6e, - 0x08, 0x94, 0x7a, 0x6e, - 0x08, 0x94, 0x7a, 0x6e, + 0x20, 0x94, 0x60, 0x6e, + 0x10, 0x94, 0x62, 0x6e, + 0x08, 0x94, 0x7c, 0x6e, + 0x08, 0x94, 0x7c, 0x6e, + 0x08, 0x94, 0x7c, 0x6e, 0xff, 0x8c, 0xc8, 0x10, 0xc1, 0x64, 0xc8, 0x18, 0xf8, 0x64, 0xc8, 0x08, 0x01, 0x99, 0xda, 0x30, - 0x00, 0x66, 0x6e, 0x66, - 0xc0, 0x66, 0xaa, 0x76, + 0x00, 0x66, 0x70, 0x66, + 0xc0, 0x66, 0xac, 0x76, 0x60, 0x66, 0xc8, 0x18, 0x3d, 0x64, 0xc8, 0x28, - 0x00, 0x65, 0x5e, 0x46, + 0x00, 0x65, 0x60, 0x46, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0x7c, 0x6e, + 0x08, 0x93, 0x7e, 0x6e, 0x00, 0x62, 0xc4, 0x18, - 0x00, 0x65, 0xa8, 0x5e, - 0x00, 0x65, 0x88, 0x5e, - 0x00, 0x65, 0x88, 0x5e, - 0x00, 0x65, 0x88, 0x5e, + 0x00, 0x65, 0xaa, 0x5e, + 0x00, 0x65, 0x8a, 0x5e, + 0x00, 0x65, 0x8a, 0x5e, + 0x00, 0x65, 0x8a, 0x5e, 0x01, 0x99, 0xda, 0x30, 0x01, 0x99, 0xda, 0x30, 0x01, 0x99, 0xda, 0x30, @@ -858,11 +859,11 @@ static uint8_t seqprog[] = { 0x01, 0x6c, 0x32, 0x31, 0x01, 0x6c, 0x32, 0x31, 0x01, 0x6c, 0x32, 0x35, - 0x08, 0x94, 0xa8, 0x7e, + 0x08, 0x94, 0xaa, 0x7e, 0xf7, 0x93, 0x26, 0x09, - 0x08, 0x93, 0xac, 0x6e, + 0x08, 0x93, 0xae, 0x6e, 0xff, 0x6a, 0xd4, 0x0c, - 0x04, 0xb8, 0xd4, 0x6e, + 0x04, 0xb8, 0xd6, 0x6e, 0x01, 0x42, 0x7e, 0x31, 0xff, 0x6a, 0x76, 0x01, 0x01, 0x90, 0x84, 0x34, @@ -870,14 +871,14 @@ static uint8_t seqprog[] = { 0x01, 0x85, 0x0a, 0x01, 0x7f, 0x65, 0x10, 0x09, 0xfe, 0x85, 0x0a, 0x0d, - 0xff, 0x42, 0xd0, 0x66, - 0xff, 0x41, 0xc8, 0x66, - 0xd1, 0x6a, 0xd8, 0x5e, + 0xff, 0x42, 0xd2, 0x66, + 0xff, 0x41, 0xca, 0x66, + 0xd1, 0x6a, 0xda, 0x5e, 0xff, 0x6a, 0xca, 0x04, 0x01, 0x41, 0x20, 0x31, 0x01, 0xbf, 0x82, 0x30, 0x01, 0x6a, 0x76, 0x00, - 0x00, 0xbb, 0x12, 0x46, + 0x00, 0xbb, 0x14, 0x46, 0x01, 0x42, 0x20, 0x31, 0x01, 0xbf, 0x84, 0x34, 0x01, 0x41, 0x7e, 0x31, @@ -941,7 +942,7 @@ static ahc_patch_func_t ahc_patch17_func; static int ahc_patch17_func(struct ahc_softc *ahc) { - return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0); + return ((ahc->bugs & AHC_TMODE_WIDEODD_BUG) != 0); } static ahc_patch_func_t ahc_patch16_func; @@ -1142,152 +1143,152 @@ static struct patch { { ahc_patch0_func, 196, 1, 1 }, { ahc_patch9_func, 212, 6, 2 }, { ahc_patch0_func, 218, 6, 1 }, - { ahc_patch8_func, 226, 20, 2 }, + { ahc_patch8_func, 226, 21, 2 }, { ahc_patch1_func, 241, 1, 1 }, - { ahc_patch1_func, 248, 1, 2 }, - { ahc_patch0_func, 249, 2, 2 }, - { ahc_patch11_func, 250, 1, 1 }, - { ahc_patch9_func, 258, 27, 3 }, - { ahc_patch1_func, 274, 10, 2 }, - { ahc_patch13_func, 277, 1, 1 }, - { ahc_patch14_func, 285, 14, 1 }, - { ahc_patch1_func, 301, 1, 2 }, - { ahc_patch0_func, 302, 1, 1 }, - { ahc_patch9_func, 305, 1, 1 }, - { ahc_patch13_func, 310, 1, 1 }, - { ahc_patch9_func, 311, 2, 2 }, - { ahc_patch0_func, 313, 4, 1 }, - { ahc_patch14_func, 317, 1, 1 }, - { ahc_patch15_func, 319, 2, 3 }, - { ahc_patch9_func, 319, 1, 2 }, - { ahc_patch0_func, 320, 1, 1 }, - { ahc_patch6_func, 325, 1, 2 }, - { ahc_patch0_func, 326, 1, 1 }, - { ahc_patch1_func, 330, 47, 11 }, - { ahc_patch6_func, 337, 2, 4 }, - { ahc_patch7_func, 337, 1, 1 }, - { ahc_patch8_func, 338, 1, 1 }, - { ahc_patch0_func, 339, 1, 1 }, - { ahc_patch16_func, 340, 1, 1 }, - { ahc_patch6_func, 356, 6, 3 }, - { ahc_patch16_func, 356, 5, 1 }, - { ahc_patch0_func, 362, 7, 1 }, - { ahc_patch13_func, 372, 5, 1 }, - { ahc_patch0_func, 377, 52, 17 }, - { ahc_patch14_func, 377, 1, 1 }, - { ahc_patch7_func, 379, 2, 2 }, - { ahc_patch17_func, 380, 1, 1 }, - { ahc_patch9_func, 383, 1, 1 }, - { ahc_patch18_func, 390, 1, 1 }, - { ahc_patch14_func, 395, 9, 3 }, - { ahc_patch9_func, 396, 3, 2 }, - { ahc_patch0_func, 399, 3, 1 }, - { ahc_patch9_func, 407, 6, 2 }, - { ahc_patch0_func, 413, 9, 2 }, - { ahc_patch13_func, 413, 1, 1 }, - { ahc_patch13_func, 422, 2, 1 }, - { ahc_patch14_func, 424, 1, 1 }, - { ahc_patch9_func, 426, 1, 2 }, - { ahc_patch0_func, 427, 1, 1 }, - { ahc_patch7_func, 428, 1, 1 }, + { ahc_patch1_func, 249, 1, 2 }, + { ahc_patch0_func, 250, 2, 2 }, + { ahc_patch11_func, 251, 1, 1 }, + { ahc_patch9_func, 259, 27, 3 }, + { ahc_patch1_func, 275, 10, 2 }, + { ahc_patch13_func, 278, 1, 1 }, + { ahc_patch14_func, 286, 14, 1 }, + { ahc_patch1_func, 302, 1, 2 }, + { ahc_patch0_func, 303, 1, 1 }, + { ahc_patch9_func, 306, 1, 1 }, + { ahc_patch13_func, 311, 1, 1 }, + { ahc_patch9_func, 312, 2, 2 }, + { ahc_patch0_func, 314, 4, 1 }, + { ahc_patch14_func, 318, 1, 1 }, + { ahc_patch15_func, 320, 2, 3 }, + { ahc_patch9_func, 320, 1, 2 }, + { ahc_patch0_func, 321, 1, 1 }, + { ahc_patch6_func, 326, 1, 2 }, + { ahc_patch0_func, 327, 1, 1 }, + { ahc_patch1_func, 331, 47, 11 }, + { ahc_patch6_func, 338, 2, 4 }, + { ahc_patch7_func, 338, 1, 1 }, + { ahc_patch8_func, 339, 1, 1 }, + { ahc_patch0_func, 340, 1, 1 }, + { ahc_patch16_func, 341, 1, 1 }, + { ahc_patch6_func, 357, 6, 3 }, + { ahc_patch16_func, 357, 5, 1 }, + { ahc_patch0_func, 363, 7, 1 }, + { ahc_patch13_func, 373, 5, 1 }, + { ahc_patch0_func, 378, 52, 17 }, + { ahc_patch14_func, 378, 1, 1 }, + { ahc_patch7_func, 380, 2, 2 }, + { ahc_patch17_func, 381, 1, 1 }, + { ahc_patch9_func, 384, 1, 1 }, + { ahc_patch18_func, 391, 1, 1 }, + { ahc_patch14_func, 396, 9, 3 }, + { ahc_patch9_func, 397, 3, 2 }, + { ahc_patch0_func, 400, 3, 1 }, + { ahc_patch9_func, 408, 6, 2 }, + { ahc_patch0_func, 414, 9, 2 }, + { ahc_patch13_func, 414, 1, 1 }, + { ahc_patch13_func, 423, 2, 1 }, + { ahc_patch14_func, 425, 1, 1 }, + { ahc_patch9_func, 427, 1, 2 }, + { ahc_patch0_func, 428, 1, 1 }, { ahc_patch7_func, 429, 1, 1 }, - { ahc_patch8_func, 430, 3, 3 }, - { ahc_patch6_func, 431, 1, 2 }, - { ahc_patch0_func, 432, 1, 1 }, - { ahc_patch9_func, 433, 1, 1 }, - { ahc_patch15_func, 434, 1, 2 }, - { ahc_patch13_func, 434, 1, 1 }, - { ahc_patch14_func, 436, 9, 4 }, - { ahc_patch9_func, 436, 1, 1 }, - { ahc_patch9_func, 443, 2, 1 }, - { ahc_patch0_func, 445, 4, 3 }, - { ahc_patch9_func, 445, 1, 2 }, - { ahc_patch0_func, 446, 3, 1 }, - { ahc_patch1_func, 450, 2, 1 }, - { ahc_patch7_func, 452, 10, 2 }, - { ahc_patch0_func, 462, 1, 1 }, - { ahc_patch8_func, 463, 118, 22 }, - { ahc_patch1_func, 465, 3, 2 }, - { ahc_patch0_func, 468, 5, 3 }, - { ahc_patch9_func, 468, 2, 2 }, - { ahc_patch0_func, 470, 3, 1 }, - { ahc_patch1_func, 475, 2, 2 }, - { ahc_patch0_func, 477, 6, 3 }, - { ahc_patch9_func, 477, 2, 2 }, - { ahc_patch0_func, 479, 3, 1 }, - { ahc_patch1_func, 485, 2, 2 }, - { ahc_patch0_func, 487, 9, 7 }, - { ahc_patch9_func, 487, 5, 6 }, - { ahc_patch19_func, 487, 1, 2 }, - { ahc_patch0_func, 488, 1, 1 }, - { ahc_patch19_func, 490, 1, 2 }, - { ahc_patch0_func, 491, 1, 1 }, - { ahc_patch0_func, 492, 4, 1 }, - { ahc_patch6_func, 497, 3, 2 }, - { ahc_patch0_func, 500, 1, 1 }, - { ahc_patch6_func, 510, 1, 2 }, - { ahc_patch0_func, 511, 1, 1 }, - { ahc_patch20_func, 548, 7, 1 }, - { ahc_patch3_func, 583, 1, 2 }, - { ahc_patch0_func, 584, 1, 1 }, - { ahc_patch21_func, 587, 1, 1 }, - { ahc_patch8_func, 589, 106, 33 }, - { ahc_patch4_func, 591, 1, 1 }, - { ahc_patch1_func, 597, 2, 2 }, - { ahc_patch0_func, 599, 1, 1 }, - { ahc_patch1_func, 602, 1, 2 }, - { ahc_patch0_func, 603, 1, 1 }, - { ahc_patch9_func, 604, 3, 3 }, - { ahc_patch15_func, 605, 1, 1 }, - { ahc_patch0_func, 607, 4, 1 }, - { ahc_patch19_func, 616, 2, 2 }, - { ahc_patch0_func, 618, 1, 1 }, - { ahc_patch19_func, 622, 10, 3 }, - { ahc_patch5_func, 624, 8, 1 }, - { ahc_patch0_func, 632, 9, 2 }, - { ahc_patch5_func, 633, 8, 1 }, - { ahc_patch4_func, 643, 1, 2 }, - { ahc_patch0_func, 644, 1, 1 }, - { ahc_patch19_func, 645, 1, 2 }, - { ahc_patch0_func, 646, 3, 2 }, - { ahc_patch4_func, 648, 1, 1 }, - { ahc_patch5_func, 649, 1, 1 }, - { ahc_patch5_func, 652, 1, 1 }, - { ahc_patch5_func, 654, 1, 1 }, - { ahc_patch4_func, 656, 2, 2 }, - { ahc_patch0_func, 658, 2, 1 }, - { ahc_patch5_func, 660, 1, 1 }, - { ahc_patch5_func, 663, 1, 1 }, - { ahc_patch5_func, 666, 1, 1 }, - { ahc_patch19_func, 670, 1, 1 }, - { ahc_patch19_func, 673, 1, 1 }, - { ahc_patch4_func, 679, 1, 1 }, - { ahc_patch6_func, 682, 1, 2 }, - { ahc_patch0_func, 683, 1, 1 }, - { ahc_patch7_func, 695, 16, 1 }, - { ahc_patch4_func, 711, 20, 1 }, - { ahc_patch9_func, 732, 4, 2 }, - { ahc_patch0_func, 736, 4, 1 }, - { ahc_patch9_func, 740, 4, 2 }, - { ahc_patch0_func, 744, 3, 1 }, - { ahc_patch6_func, 750, 1, 1 }, - { ahc_patch22_func, 752, 14, 1 }, - { ahc_patch7_func, 766, 3, 1 }, - { ahc_patch9_func, 778, 24, 8 }, - { ahc_patch19_func, 782, 1, 2 }, - { ahc_patch0_func, 783, 1, 1 }, - { ahc_patch15_func, 788, 4, 2 }, - { ahc_patch0_func, 792, 7, 3 }, - { ahc_patch23_func, 792, 5, 2 }, - { ahc_patch0_func, 797, 2, 1 }, - { ahc_patch0_func, 802, 42, 3 }, - { ahc_patch18_func, 814, 18, 2 }, - { ahc_patch0_func, 832, 1, 1 }, - { ahc_patch4_func, 856, 1, 1 }, - { ahc_patch4_func, 857, 3, 2 }, - { ahc_patch0_func, 860, 1, 1 }, - { ahc_patch13_func, 861, 3, 1 }, - { ahc_patch4_func, 864, 12, 1 } + { ahc_patch7_func, 430, 1, 1 }, + { ahc_patch8_func, 431, 3, 3 }, + { ahc_patch6_func, 432, 1, 2 }, + { ahc_patch0_func, 433, 1, 1 }, + { ahc_patch9_func, 434, 1, 1 }, + { ahc_patch15_func, 435, 1, 2 }, + { ahc_patch13_func, 435, 1, 1 }, + { ahc_patch14_func, 437, 9, 4 }, + { ahc_patch9_func, 437, 1, 1 }, + { ahc_patch9_func, 444, 2, 1 }, + { ahc_patch0_func, 446, 4, 3 }, + { ahc_patch9_func, 446, 1, 2 }, + { ahc_patch0_func, 447, 3, 1 }, + { ahc_patch1_func, 451, 2, 1 }, + { ahc_patch7_func, 453, 10, 2 }, + { ahc_patch0_func, 463, 1, 1 }, + { ahc_patch8_func, 464, 118, 22 }, + { ahc_patch1_func, 466, 3, 2 }, + { ahc_patch0_func, 469, 5, 3 }, + { ahc_patch9_func, 469, 2, 2 }, + { ahc_patch0_func, 471, 3, 1 }, + { ahc_patch1_func, 476, 2, 2 }, + { ahc_patch0_func, 478, 6, 3 }, + { ahc_patch9_func, 478, 2, 2 }, + { ahc_patch0_func, 480, 3, 1 }, + { ahc_patch1_func, 486, 2, 2 }, + { ahc_patch0_func, 488, 9, 7 }, + { ahc_patch9_func, 488, 5, 6 }, + { ahc_patch19_func, 488, 1, 2 }, + { ahc_patch0_func, 489, 1, 1 }, + { ahc_patch19_func, 491, 1, 2 }, + { ahc_patch0_func, 492, 1, 1 }, + { ahc_patch0_func, 493, 4, 1 }, + { ahc_patch6_func, 498, 3, 2 }, + { ahc_patch0_func, 501, 1, 1 }, + { ahc_patch6_func, 511, 1, 2 }, + { ahc_patch0_func, 512, 1, 1 }, + { ahc_patch20_func, 549, 7, 1 }, + { ahc_patch3_func, 584, 1, 2 }, + { ahc_patch0_func, 585, 1, 1 }, + { ahc_patch21_func, 588, 1, 1 }, + { ahc_patch8_func, 590, 106, 33 }, + { ahc_patch4_func, 592, 1, 1 }, + { ahc_patch1_func, 598, 2, 2 }, + { ahc_patch0_func, 600, 1, 1 }, + { ahc_patch1_func, 603, 1, 2 }, + { ahc_patch0_func, 604, 1, 1 }, + { ahc_patch9_func, 605, 3, 3 }, + { ahc_patch15_func, 606, 1, 1 }, + { ahc_patch0_func, 608, 4, 1 }, + { ahc_patch19_func, 617, 2, 2 }, + { ahc_patch0_func, 619, 1, 1 }, + { ahc_patch19_func, 623, 10, 3 }, + { ahc_patch5_func, 625, 8, 1 }, + { ahc_patch0_func, 633, 9, 2 }, + { ahc_patch5_func, 634, 8, 1 }, + { ahc_patch4_func, 644, 1, 2 }, + { ahc_patch0_func, 645, 1, 1 }, + { ahc_patch19_func, 646, 1, 2 }, + { ahc_patch0_func, 647, 3, 2 }, + { ahc_patch4_func, 649, 1, 1 }, + { ahc_patch5_func, 650, 1, 1 }, + { ahc_patch5_func, 653, 1, 1 }, + { ahc_patch5_func, 655, 1, 1 }, + { ahc_patch4_func, 657, 2, 2 }, + { ahc_patch0_func, 659, 2, 1 }, + { ahc_patch5_func, 661, 1, 1 }, + { ahc_patch5_func, 664, 1, 1 }, + { ahc_patch5_func, 667, 1, 1 }, + { ahc_patch19_func, 671, 1, 1 }, + { ahc_patch19_func, 674, 1, 1 }, + { ahc_patch4_func, 680, 1, 1 }, + { ahc_patch6_func, 683, 1, 2 }, + { ahc_patch0_func, 684, 1, 1 }, + { ahc_patch7_func, 696, 16, 1 }, + { ahc_patch4_func, 712, 20, 1 }, + { ahc_patch9_func, 733, 4, 2 }, + { ahc_patch0_func, 737, 4, 1 }, + { ahc_patch9_func, 741, 4, 2 }, + { ahc_patch0_func, 745, 3, 1 }, + { ahc_patch6_func, 751, 1, 1 }, + { ahc_patch22_func, 753, 14, 1 }, + { ahc_patch7_func, 767, 3, 1 }, + { ahc_patch9_func, 779, 24, 8 }, + { ahc_patch19_func, 783, 1, 2 }, + { ahc_patch0_func, 784, 1, 1 }, + { ahc_patch15_func, 789, 4, 2 }, + { ahc_patch0_func, 793, 7, 3 }, + { ahc_patch23_func, 793, 5, 2 }, + { ahc_patch0_func, 798, 2, 1 }, + { ahc_patch0_func, 803, 42, 3 }, + { ahc_patch18_func, 815, 18, 2 }, + { ahc_patch0_func, 833, 1, 1 }, + { ahc_patch4_func, 857, 1, 1 }, + { ahc_patch4_func, 858, 3, 2 }, + { ahc_patch0_func, 861, 1, 1 }, + { ahc_patch13_func, 862, 3, 1 }, + { ahc_patch4_func, 865, 12, 1 } }; static struct cs { @@ -1296,11 +1297,11 @@ static struct cs { } critical_sections[] = { { 11, 18 }, { 21, 30 }, - { 711, 727 }, - { 857, 860 }, - { 864, 870 }, - { 872, 874 }, - { 874, 876 } + { 712, 728 }, + { 858, 861 }, + { 865, 871 }, + { 873, 875 }, + { 875, 877 } }; static const int num_critical_sections = sizeof(critical_sections) diff --git a/drivers/scsi/aic7xxx/aiclib.c b/drivers/scsi/aic7xxx/aiclib.c index 7c5a6db0e672..828ae3d9a510 100644 --- a/drivers/scsi/aic7xxx/aiclib.c +++ b/drivers/scsi/aic7xxx/aiclib.c @@ -30,1382 +30,5 @@ * $Id$ */ -#include <linux/blkdev.h> -#include <linux/delay.h> -#include <linux/version.h> - -/* Core SCSI definitions */ -#include <scsi/scsi_host.h> #include "aiclib.h" -#include "cam.h" - -#ifndef FALSE -#define FALSE 0 -#endif /* FALSE */ -#ifndef TRUE -#define TRUE 1 -#endif /* TRUE */ -#ifndef ERESTART -#define ERESTART -1 /* restart syscall */ -#endif -#ifndef EJUSTRETURN -#define EJUSTRETURN -2 /* don't modify regs, just return */ -#endif - -static int ascentrycomp(const void *key, const void *member); -static int senseentrycomp(const void *key, const void *member); -static void fetchtableentries(int sense_key, int asc, int ascq, - struct scsi_inquiry_data *, - const struct sense_key_table_entry **, - const struct asc_table_entry **); -static void * scsibsearch(const void *key, const void *base, size_t nmemb, - size_t size, - int (*compar)(const void *, const void *)); -typedef int (cam_quirkmatch_t)(caddr_t, caddr_t); -static int cam_strmatch(const u_int8_t *str, const u_int8_t *pattern, - int str_len); -static caddr_t cam_quirkmatch(caddr_t target, caddr_t quirk_table, - int num_entries, int entry_size, - cam_quirkmatch_t *comp_func); - -#define SCSI_NO_SENSE_STRINGS 1 -#if !defined(SCSI_NO_SENSE_STRINGS) -#define SST(asc, ascq, action, desc) \ - asc, ascq, action, desc -#else -static const char empty_string[] = ""; - -#define SST(asc, ascq, action, desc) \ - asc, ascq, action, empty_string -#endif - -static const struct sense_key_table_entry sense_key_table[] = -{ - { SSD_KEY_NO_SENSE, SS_NOP, "NO SENSE" }, - { SSD_KEY_RECOVERED_ERROR, SS_NOP|SSQ_PRINT_SENSE, "RECOVERED ERROR" }, - { - SSD_KEY_NOT_READY, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, - "NOT READY" - }, - { SSD_KEY_MEDIUM_ERROR, SS_RDEF, "MEDIUM ERROR" }, - { SSD_KEY_HARDWARE_ERROR, SS_RDEF, "HARDWARE FAILURE" }, - { SSD_KEY_ILLEGAL_REQUEST, SS_FATAL|EINVAL, "ILLEGAL REQUEST" }, - { SSD_KEY_UNIT_ATTENTION, SS_FATAL|ENXIO, "UNIT ATTENTION" }, - { SSD_KEY_DATA_PROTECT, SS_FATAL|EACCES, "DATA PROTECT" }, - { SSD_KEY_BLANK_CHECK, SS_FATAL|ENOSPC, "BLANK CHECK" }, - { SSD_KEY_Vendor_Specific, SS_FATAL|EIO, "Vendor Specific" }, - { SSD_KEY_COPY_ABORTED, SS_FATAL|EIO, "COPY ABORTED" }, - { SSD_KEY_ABORTED_COMMAND, SS_RDEF, "ABORTED COMMAND" }, - { SSD_KEY_EQUAL, SS_NOP, "EQUAL" }, - { SSD_KEY_VOLUME_OVERFLOW, SS_FATAL|EIO, "VOLUME OVERFLOW" }, - { SSD_KEY_MISCOMPARE, SS_NOP, "MISCOMPARE" }, - { SSD_KEY_RESERVED, SS_FATAL|EIO, "RESERVED" } -}; - -static const int sense_key_table_size = - sizeof(sense_key_table)/sizeof(sense_key_table[0]); - -static struct asc_table_entry quantum_fireball_entries[] = { - {SST(0x04, 0x0b, SS_START|SSQ_DECREMENT_COUNT|ENXIO, - "Logical unit not ready, initializing cmd. required")} -}; - -static struct asc_table_entry sony_mo_entries[] = { - {SST(0x04, 0x00, SS_START|SSQ_DECREMENT_COUNT|ENXIO, - "Logical unit not ready, cause not reportable")} -}; - -static struct scsi_sense_quirk_entry sense_quirk_table[] = { - { - /* - * The Quantum Fireball ST and SE like to return 0x04 0x0b when - * they really should return 0x04 0x02. 0x04,0x0b isn't - * defined in any SCSI spec, and it isn't mentioned in the - * hardware manual for these drives. - */ - {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"}, - /*num_sense_keys*/0, - sizeof(quantum_fireball_entries)/sizeof(struct asc_table_entry), - /*sense key entries*/NULL, - quantum_fireball_entries - }, - { - /* - * This Sony MO drive likes to return 0x04, 0x00 when it - * isn't spun up. - */ - {T_DIRECT, SIP_MEDIA_REMOVABLE, "SONY", "SMO-*", "*"}, - /*num_sense_keys*/0, - sizeof(sony_mo_entries)/sizeof(struct asc_table_entry), - /*sense key entries*/NULL, - sony_mo_entries - } -}; - -static const int sense_quirk_table_size = - sizeof(sense_quirk_table)/sizeof(sense_quirk_table[0]); - -static struct asc_table_entry asc_table[] = { -/* - * From File: ASC-NUM.TXT - * SCSI ASC/ASCQ Assignments - * Numeric Sorted Listing - * as of 5/12/97 - * - * D - DIRECT ACCESS DEVICE (SBC) device column key - * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- - * . L - PRINTER DEVICE (SSC) blank = reserved - * . P - PROCESSOR DEVICE (SPC) not blank = allowed - * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) - * . . R - CD DEVICE (MMC) - * . . S - SCANNER DEVICE (SGC) - * . . .O - OPTICAL MEMORY DEVICE (SBC) - * . . . M - MEDIA CHANGER DEVICE (SMC) - * . . . C - COMMUNICATION DEVICE (SSC) - * . . . .A - STORAGE ARRAY DEVICE (SCC) - * . . . . E - ENCLOSURE SERVICES DEVICE (SES) - * DTLPWRSOMCAE ASC ASCQ Action Description - * ------------ ---- ---- ------ -----------------------------------*/ -/* DTLPWRSOMCAE */{SST(0x00, 0x00, SS_NOP, - "No additional sense information") }, -/* T S */{SST(0x00, 0x01, SS_RDEF, - "Filemark detected") }, -/* T S */{SST(0x00, 0x02, SS_RDEF, - "End-of-partition/medium detected") }, -/* T */{SST(0x00, 0x03, SS_RDEF, - "Setmark detected") }, -/* T S */{SST(0x00, 0x04, SS_RDEF, - "Beginning-of-partition/medium detected") }, -/* T S */{SST(0x00, 0x05, SS_RDEF, - "End-of-data detected") }, -/* DTLPWRSOMCAE */{SST(0x00, 0x06, SS_RDEF, - "I/O process terminated") }, -/* R */{SST(0x00, 0x11, SS_FATAL|EBUSY, - "Audio play operation in progress") }, -/* R */{SST(0x00, 0x12, SS_NOP, - "Audio play operation paused") }, -/* R */{SST(0x00, 0x13, SS_NOP, - "Audio play operation successfully completed") }, -/* R */{SST(0x00, 0x14, SS_RDEF, - "Audio play operation stopped due to error") }, -/* R */{SST(0x00, 0x15, SS_NOP, - "No current audio status to return") }, -/* DTLPWRSOMCAE */{SST(0x00, 0x16, SS_FATAL|EBUSY, - "Operation in progress") }, -/* DTL WRSOM AE */{SST(0x00, 0x17, SS_RDEF, - "Cleaning requested") }, -/* D W O */{SST(0x01, 0x00, SS_RDEF, - "No index/sector signal") }, -/* D WR OM */{SST(0x02, 0x00, SS_RDEF, - "No seek complete") }, -/* DTL W SO */{SST(0x03, 0x00, SS_RDEF, - "Peripheral device write fault") }, -/* T */{SST(0x03, 0x01, SS_RDEF, - "No write current") }, -/* T */{SST(0x03, 0x02, SS_RDEF, - "Excessive write errors") }, -/* DTLPWRSOMCAE */{SST(0x04, 0x00, - SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO, - "Logical unit not ready, cause not reportable") }, -/* DTLPWRSOMCAE */{SST(0x04, 0x01, - SS_TUR|SSQ_DELAY|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, - "Logical unit is in process of becoming ready") }, -/* DTLPWRSOMCAE */{SST(0x04, 0x02, SS_START|SSQ_DECREMENT_COUNT|ENXIO, - "Logical unit not ready, initializing cmd. required") }, -/* DTLPWRSOMCAE */{SST(0x04, 0x03, SS_FATAL|ENXIO, - "Logical unit not ready, manual intervention required")}, -/* DTL O */{SST(0x04, 0x04, SS_FATAL|EBUSY, - "Logical unit not ready, format in progress") }, -/* DT W OMCA */{SST(0x04, 0x05, SS_FATAL|EBUSY, - "Logical unit not ready, rebuild in progress") }, -/* DT W OMCA */{SST(0x04, 0x06, SS_FATAL|EBUSY, - "Logical unit not ready, recalculation in progress") }, -/* DTLPWRSOMCAE */{SST(0x04, 0x07, SS_FATAL|EBUSY, - "Logical unit not ready, operation in progress") }, -/* R */{SST(0x04, 0x08, SS_FATAL|EBUSY, - "Logical unit not ready, long write in progress") }, -/* DTL WRSOMCAE */{SST(0x05, 0x00, SS_RDEF, - "Logical unit does not respond to selection") }, -/* D WR OM */{SST(0x06, 0x00, SS_RDEF, - "No reference position found") }, -/* DTL WRSOM */{SST(0x07, 0x00, SS_RDEF, - "Multiple peripheral devices selected") }, -/* DTL WRSOMCAE */{SST(0x08, 0x00, SS_RDEF, - "Logical unit communication failure") }, -/* DTL WRSOMCAE */{SST(0x08, 0x01, SS_RDEF, - "Logical unit communication time-out") }, -/* DTL WRSOMCAE */{SST(0x08, 0x02, SS_RDEF, - "Logical unit communication parity error") }, -/* DT R OM */{SST(0x08, 0x03, SS_RDEF, - "Logical unit communication crc error (ultra-dma/32)")}, -/* DT WR O */{SST(0x09, 0x00, SS_RDEF, - "Track following error") }, -/* WR O */{SST(0x09, 0x01, SS_RDEF, - "Tracking servo failure") }, -/* WR O */{SST(0x09, 0x02, SS_RDEF, - "Focus servo failure") }, -/* WR O */{SST(0x09, 0x03, SS_RDEF, - "Spindle servo failure") }, -/* DT WR O */{SST(0x09, 0x04, SS_RDEF, - "Head select fault") }, -/* DTLPWRSOMCAE */{SST(0x0A, 0x00, SS_FATAL|ENOSPC, - "Error log overflow") }, -/* DTLPWRSOMCAE */{SST(0x0B, 0x00, SS_RDEF, - "Warning") }, -/* DTLPWRSOMCAE */{SST(0x0B, 0x01, SS_RDEF, - "Specified temperature exceeded") }, -/* DTLPWRSOMCAE */{SST(0x0B, 0x02, SS_RDEF, - "Enclosure degraded") }, -/* T RS */{SST(0x0C, 0x00, SS_RDEF, - "Write error") }, -/* D W O */{SST(0x0C, 0x01, SS_NOP|SSQ_PRINT_SENSE, - "Write error - recovered with auto reallocation") }, -/* D W O */{SST(0x0C, 0x02, SS_RDEF, - "Write error - auto reallocation failed") }, -/* D W O */{SST(0x0C, 0x03, SS_RDEF, - "Write error - recommend reassignment") }, -/* DT W O */{SST(0x0C, 0x04, SS_RDEF, - "Compression check miscompare error") }, -/* DT W O */{SST(0x0C, 0x05, SS_RDEF, - "Data expansion occurred during compression") }, -/* DT W O */{SST(0x0C, 0x06, SS_RDEF, - "Block not compressible") }, -/* R */{SST(0x0C, 0x07, SS_RDEF, - "Write error - recovery needed") }, -/* R */{SST(0x0C, 0x08, SS_RDEF, - "Write error - recovery failed") }, -/* R */{SST(0x0C, 0x09, SS_RDEF, - "Write error - loss of streaming") }, -/* R */{SST(0x0C, 0x0A, SS_RDEF, - "Write error - padding blocks added") }, -/* D W O */{SST(0x10, 0x00, SS_RDEF, - "ID CRC or ECC error") }, -/* DT WRSO */{SST(0x11, 0x00, SS_RDEF, - "Unrecovered read error") }, -/* DT W SO */{SST(0x11, 0x01, SS_RDEF, - "Read retries exhausted") }, -/* DT W SO */{SST(0x11, 0x02, SS_RDEF, - "Error too long to correct") }, -/* DT W SO */{SST(0x11, 0x03, SS_RDEF, - "Multiple read errors") }, -/* D W O */{SST(0x11, 0x04, SS_RDEF, - "Unrecovered read error - auto reallocate failed") }, -/* WR O */{SST(0x11, 0x05, SS_RDEF, - "L-EC uncorrectable error") }, -/* WR O */{SST(0x11, 0x06, SS_RDEF, - "CIRC unrecovered error") }, -/* W O */{SST(0x11, 0x07, SS_RDEF, - "Data re-synchronization error") }, -/* T */{SST(0x11, 0x08, SS_RDEF, - "Incomplete block read") }, -/* T */{SST(0x11, 0x09, SS_RDEF, - "No gap found") }, -/* DT O */{SST(0x11, 0x0A, SS_RDEF, - "Miscorrected error") }, -/* D W O */{SST(0x11, 0x0B, SS_RDEF, - "Unrecovered read error - recommend reassignment") }, -/* D W O */{SST(0x11, 0x0C, SS_RDEF, - "Unrecovered read error - recommend rewrite the data")}, -/* DT WR O */{SST(0x11, 0x0D, SS_RDEF, - "De-compression CRC error") }, -/* DT WR O */{SST(0x11, 0x0E, SS_RDEF, - "Cannot decompress using declared algorithm") }, -/* R */{SST(0x11, 0x0F, SS_RDEF, - "Error reading UPC/EAN number") }, -/* R */{SST(0x11, 0x10, SS_RDEF, - "Error reading ISRC number") }, -/* R */{SST(0x11, 0x11, SS_RDEF, - "Read error - loss of streaming") }, -/* D W O */{SST(0x12, 0x00, SS_RDEF, - "Address mark not found for id field") }, -/* D W O */{SST(0x13, 0x00, SS_RDEF, - "Address mark not found for data field") }, -/* DTL WRSO */{SST(0x14, 0x00, SS_RDEF, - "Recorded entity not found") }, -/* DT WR O */{SST(0x14, 0x01, SS_RDEF, - "Record not found") }, -/* T */{SST(0x14, 0x02, SS_RDEF, - "Filemark or setmark not found") }, -/* T */{SST(0x14, 0x03, SS_RDEF, - "End-of-data not found") }, -/* T */{SST(0x14, 0x04, SS_RDEF, - "Block sequence error") }, -/* DT W O */{SST(0x14, 0x05, SS_RDEF, - "Record not found - recommend reassignment") }, -/* DT W O */{SST(0x14, 0x06, SS_RDEF, - "Record not found - data auto-reallocated") }, -/* DTL WRSOM */{SST(0x15, 0x00, SS_RDEF, - "Random positioning error") }, -/* DTL WRSOM */{SST(0x15, 0x01, SS_RDEF, - "Mechanical positioning error") }, -/* DT WR O */{SST(0x15, 0x02, SS_RDEF, - "Positioning error detected by read of medium") }, -/* D W O */{SST(0x16, 0x00, SS_RDEF, - "Data synchronization mark error") }, -/* D W O */{SST(0x16, 0x01, SS_RDEF, - "Data sync error - data rewritten") }, -/* D W O */{SST(0x16, 0x02, SS_RDEF, - "Data sync error - recommend rewrite") }, -/* D W O */{SST(0x16, 0x03, SS_NOP|SSQ_PRINT_SENSE, - "Data sync error - data auto-reallocated") }, -/* D W O */{SST(0x16, 0x04, SS_RDEF, - "Data sync error - recommend reassignment") }, -/* DT WRSO */{SST(0x17, 0x00, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with no error correction applied") }, -/* DT WRSO */{SST(0x17, 0x01, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with retries") }, -/* DT WR O */{SST(0x17, 0x02, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with positive head offset") }, -/* DT WR O */{SST(0x17, 0x03, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with negative head offset") }, -/* WR O */{SST(0x17, 0x04, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with retries and/or CIRC applied") }, -/* D WR O */{SST(0x17, 0x05, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data using previous sector id") }, -/* D W O */{SST(0x17, 0x06, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data without ECC - data auto-reallocated") }, -/* D W O */{SST(0x17, 0x07, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data without ECC - recommend reassignment")}, -/* D W O */{SST(0x17, 0x08, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data without ECC - recommend rewrite") }, -/* D W O */{SST(0x17, 0x09, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data without ECC - data rewritten") }, -/* D W O */{SST(0x18, 0x00, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with error correction applied") }, -/* D WR O */{SST(0x18, 0x01, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with error corr. & retries applied") }, -/* D WR O */{SST(0x18, 0x02, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data - data auto-reallocated") }, -/* R */{SST(0x18, 0x03, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with CIRC") }, -/* R */{SST(0x18, 0x04, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with L-EC") }, -/* D WR O */{SST(0x18, 0x05, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data - recommend reassignment") }, -/* D WR O */{SST(0x18, 0x06, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data - recommend rewrite") }, -/* D W O */{SST(0x18, 0x07, SS_NOP|SSQ_PRINT_SENSE, - "Recovered data with ECC - data rewritten") }, -/* D O */{SST(0x19, 0x00, SS_RDEF, - "Defect list error") }, -/* D O */{SST(0x19, 0x01, SS_RDEF, - "Defect list not available") }, -/* D O */{SST(0x19, 0x02, SS_RDEF, - "Defect list error in primary list") }, -/* D O */{SST(0x19, 0x03, SS_RDEF, - "Defect list error in grown list") }, -/* DTLPWRSOMCAE */{SST(0x1A, 0x00, SS_RDEF, - "Parameter list length error") }, -/* DTLPWRSOMCAE */{SST(0x1B, 0x00, SS_RDEF, - "Synchronous data transfer error") }, -/* D O */{SST(0x1C, 0x00, SS_RDEF, - "Defect list not found") }, -/* D O */{SST(0x1C, 0x01, SS_RDEF, - "Primary defect list not found") }, -/* D O */{SST(0x1C, 0x02, SS_RDEF, - "Grown defect list not found") }, -/* D W O */{SST(0x1D, 0x00, SS_FATAL, - "Miscompare during verify operation" )}, -/* D W O */{SST(0x1E, 0x00, SS_NOP|SSQ_PRINT_SENSE, - "Recovered id with ecc correction") }, -/* D O */{SST(0x1F, 0x00, SS_RDEF, - "Partial defect list transfer") }, -/* DTLPWRSOMCAE */{SST(0x20, 0x00, SS_FATAL|EINVAL, - "Invalid command operation code") }, -/* DT WR OM */{SST(0x21, 0x00, SS_FATAL|EINVAL, - "Logical block address out of range" )}, -/* DT WR OM */{SST(0x21, 0x01, SS_FATAL|EINVAL, - "Invalid element address") }, -/* D */{SST(0x22, 0x00, SS_FATAL|EINVAL, - "Illegal function") }, /* Deprecated. Use 20 00, 24 00, or 26 00 instead */ -/* DTLPWRSOMCAE */{SST(0x24, 0x00, SS_FATAL|EINVAL, - "Invalid field in CDB") }, -/* DTLPWRSOMCAE */{SST(0x25, 0x00, SS_FATAL|ENXIO, - "Logical unit not supported") }, -/* DTLPWRSOMCAE */{SST(0x26, 0x00, SS_FATAL|EINVAL, - "Invalid field in parameter list") }, -/* DTLPWRSOMCAE */{SST(0x26, 0x01, SS_FATAL|EINVAL, - "Parameter not supported") }, -/* DTLPWRSOMCAE */{SST(0x26, 0x02, SS_FATAL|EINVAL, - "Parameter value invalid") }, -/* DTLPWRSOMCAE */{SST(0x26, 0x03, SS_FATAL|EINVAL, - "Threshold parameters not supported") }, -/* DTLPWRSOMCAE */{SST(0x26, 0x04, SS_FATAL|EINVAL, - "Invalid release of active persistent reservation") }, -/* DT W O */{SST(0x27, 0x00, SS_FATAL|EACCES, - "Write protected") }, -/* DT W O */{SST(0x27, 0x01, SS_FATAL|EACCES, - "Hardware write protected") }, -/* DT W O */{SST(0x27, 0x02, SS_FATAL|EACCES, - "Logical unit software write protected") }, -/* T */{SST(0x27, 0x03, SS_FATAL|EACCES, - "Associated write protect") }, -/* T */{SST(0x27, 0x04, SS_FATAL|EACCES, - "Persistent write protect") }, -/* T */{SST(0x27, 0x05, SS_FATAL|EACCES, - "Permanent write protect") }, -/* DTLPWRSOMCAE */{SST(0x28, 0x00, SS_RDEF, - "Not ready to ready change, medium may have changed") }, -/* DTLPWRSOMCAE */{SST(0x28, 0x01, SS_FATAL|ENXIO, - "Import or export element accessed") }, -/* - * XXX JGibbs - All of these should use the same errno, but I don't think - * ENXIO is the correct choice. Should we borrow from the networking - * errnos? ECONNRESET anyone? - */ -/* DTLPWRSOMCAE */{SST(0x29, 0x00, SS_RDEF, - "Power on, reset, or bus device reset occurred") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x01, SS_RDEF, - "Power on occurred") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x02, SS_RDEF, - "Scsi bus reset occurred") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x03, SS_RDEF, - "Bus device reset function occurred") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x04, SS_RDEF, - "Device internal reset") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x05, SS_RDEF, - "Transceiver mode changed to single-ended") }, -/* DTLPWRSOMCAE */{SST(0x29, 0x06, SS_RDEF, - "Transceiver mode changed to LVD") }, -/* DTL WRSOMCAE */{SST(0x2A, 0x00, SS_RDEF, - "Parameters changed") }, -/* DTL WRSOMCAE */{SST(0x2A, 0x01, SS_RDEF, - "Mode parameters changed") }, -/* DTL WRSOMCAE */{SST(0x2A, 0x02, SS_RDEF, - "Log parameters changed") }, -/* DTLPWRSOMCAE */{SST(0x2A, 0x03, SS_RDEF, - "Reservations preempted") }, -/* DTLPWRSO C */{SST(0x2B, 0x00, SS_RDEF, - "Copy cannot execute since host cannot disconnect") }, -/* DTLPWRSOMCAE */{SST(0x2C, 0x00, SS_RDEF, - "Command sequence error") }, -/* S */{SST(0x2C, 0x01, SS_RDEF, - "Too many windows specified") }, -/* S */{SST(0x2C, 0x02, SS_RDEF, - "Invalid combination of windows specified") }, -/* R */{SST(0x2C, 0x03, SS_RDEF, - "Current program area is not empty") }, -/* R */{SST(0x2C, 0x04, SS_RDEF, - "Current program area is empty") }, -/* T */{SST(0x2D, 0x00, SS_RDEF, - "Overwrite error on update in place") }, -/* DTLPWRSOMCAE */{SST(0x2F, 0x00, SS_RDEF, - "Commands cleared by another initiator") }, -/* DT WR OM */{SST(0x30, 0x00, SS_RDEF, - "Incompatible medium installed") }, -/* DT WR O */{SST(0x30, 0x01, SS_RDEF, - "Cannot read medium - unknown format") }, -/* DT WR O */{SST(0x30, 0x02, SS_RDEF, - "Cannot read medium - incompatible format") }, -/* DT */{SST(0x30, 0x03, SS_RDEF, - "Cleaning cartridge installed") }, -/* DT WR O */{SST(0x30, 0x04, SS_RDEF, - "Cannot write medium - unknown format") }, -/* DT WR O */{SST(0x30, 0x05, SS_RDEF, - "Cannot write medium - incompatible format") }, -/* DT W O */{SST(0x30, 0x06, SS_RDEF, - "Cannot format medium - incompatible medium") }, -/* DTL WRSOM AE */{SST(0x30, 0x07, SS_RDEF, - "Cleaning failure") }, -/* R */{SST(0x30, 0x08, SS_RDEF, - "Cannot write - application code mismatch") }, -/* R */{SST(0x30, 0x09, SS_RDEF, - "Current session not fixated for append") }, -/* DT WR O */{SST(0x31, 0x00, SS_RDEF, - "Medium format corrupted") }, -/* D L R O */{SST(0x31, 0x01, SS_RDEF, - "Format command failed") }, -/* D W O */{SST(0x32, 0x00, SS_RDEF, - "No defect spare location available") }, -/* D W O */{SST(0x32, 0x01, SS_RDEF, - "Defect list update failure") }, -/* T */{SST(0x33, 0x00, SS_RDEF, - "Tape length error") }, -/* DTLPWRSOMCAE */{SST(0x34, 0x00, SS_RDEF, - "Enclosure failure") }, -/* DTLPWRSOMCAE */{SST(0x35, 0x00, SS_RDEF, - "Enclosure services failure") }, -/* DTLPWRSOMCAE */{SST(0x35, 0x01, SS_RDEF, - "Unsupported enclosure function") }, -/* DTLPWRSOMCAE */{SST(0x35, 0x02, SS_RDEF, - "Enclosure services unavailable") }, -/* DTLPWRSOMCAE */{SST(0x35, 0x03, SS_RDEF, - "Enclosure services transfer failure") }, -/* DTLPWRSOMCAE */{SST(0x35, 0x04, SS_RDEF, - "Enclosure services transfer refused") }, -/* L */{SST(0x36, 0x00, SS_RDEF, - "Ribbon, ink, or toner failure") }, -/* DTL WRSOMCAE */{SST(0x37, 0x00, SS_RDEF, - "Rounded parameter") }, -/* DTL WRSOMCAE */{SST(0x39, 0x00, SS_RDEF, - "Saving parameters not supported") }, -/* DTL WRSOM */{SST(0x3A, 0x00, SS_NOP, - "Medium not present") }, -/* DT WR OM */{SST(0x3A, 0x01, SS_NOP, - "Medium not present - tray closed") }, -/* DT WR OM */{SST(0x3A, 0x01, SS_NOP, - "Medium not present - tray open") }, -/* DT WR OM */{SST(0x3A, 0x03, SS_NOP, - "Medium not present - Loadable") }, -/* DT WR OM */{SST(0x3A, 0x04, SS_NOP, - "Medium not present - medium auxiliary " - "memory accessible") }, -/* DT WR OM */{SST(0x3A, 0xFF, SS_NOP, NULL) },/* Range 0x05->0xFF */ -/* TL */{SST(0x3B, 0x00, SS_RDEF, - "Sequential positioning error") }, -/* T */{SST(0x3B, 0x01, SS_RDEF, - "Tape position error at beginning-of-medium") }, -/* T */{SST(0x3B, 0x02, SS_RDEF, - "Tape position error at end-of-medium") }, -/* L */{SST(0x3B, 0x03, SS_RDEF, - "Tape or electronic vertical forms unit not ready") }, -/* L */{SST(0x3B, 0x04, SS_RDEF, - "Slew failure") }, -/* L */{SST(0x3B, 0x05, SS_RDEF, - "Paper jam") }, -/* L */{SST(0x3B, 0x06, SS_RDEF, - "Failed to sense top-of-form") }, -/* L */{SST(0x3B, 0x07, SS_RDEF, - "Failed to sense bottom-of-form") }, -/* T */{SST(0x3B, 0x08, SS_RDEF, - "Reposition error") }, -/* S */{SST(0x3B, 0x09, SS_RDEF, - "Read past end of medium") }, -/* S */{SST(0x3B, 0x0A, SS_RDEF, - "Read past beginning of medium") }, -/* S */{SST(0x3B, 0x0B, SS_RDEF, - "Position past end of medium") }, -/* T S */{SST(0x3B, 0x0C, SS_RDEF, - "Position past beginning of medium") }, -/* DT WR OM */{SST(0x3B, 0x0D, SS_FATAL|ENOSPC, - "Medium destination element full") }, -/* DT WR OM */{SST(0x3B, 0x0E, SS_RDEF, - "Medium source element empty") }, -/* R */{SST(0x3B, 0x0F, SS_RDEF, - "End of medium reached") }, -/* DT WR OM */{SST(0x3B, 0x11, SS_RDEF, - "Medium magazine not accessible") }, -/* DT WR OM */{SST(0x3B, 0x12, SS_RDEF, - "Medium magazine removed") }, -/* DT WR OM */{SST(0x3B, 0x13, SS_RDEF, - "Medium magazine inserted") }, -/* DT WR OM */{SST(0x3B, 0x14, SS_RDEF, - "Medium magazine locked") }, -/* DT WR OM */{SST(0x3B, 0x15, SS_RDEF, - "Medium magazine unlocked") }, -/* DTLPWRSOMCAE */{SST(0x3D, 0x00, SS_RDEF, - "Invalid bits in identify message") }, -/* DTLPWRSOMCAE */{SST(0x3E, 0x00, SS_RDEF, - "Logical unit has not self-configured yet") }, -/* DTLPWRSOMCAE */{SST(0x3E, 0x01, SS_RDEF, - "Logical unit failure") }, -/* DTLPWRSOMCAE */{SST(0x3E, 0x02, SS_RDEF, - "Timeout on logical unit") }, -/* DTLPWRSOMCAE */{SST(0x3F, 0x00, SS_RDEF, - "Target operating conditions have changed") }, -/* DTLPWRSOMCAE */{SST(0x3F, 0x01, SS_RDEF, - "Microcode has been changed") }, -/* DTLPWRSOMC */{SST(0x3F, 0x02, SS_RDEF, - "Changed operating definition") }, -/* DTLPWRSOMCAE */{SST(0x3F, 0x03, SS_INQ_REFRESH|SSQ_DECREMENT_COUNT, - "Inquiry data has changed") }, -/* DT WR OMCAE */{SST(0x3F, 0x04, SS_RDEF, - "Component device attached") }, -/* DT WR OMCAE */{SST(0x3F, 0x05, SS_RDEF, - "Device identifier changed") }, -/* DT WR OMCAE */{SST(0x3F, 0x06, SS_RDEF, - "Redundancy group created or modified") }, -/* DT WR OMCAE */{SST(0x3F, 0x07, SS_RDEF, - "Redundancy group deleted") }, -/* DT WR OMCAE */{SST(0x3F, 0x08, SS_RDEF, - "Spare created or modified") }, -/* DT WR OMCAE */{SST(0x3F, 0x09, SS_RDEF, - "Spare deleted") }, -/* DT WR OMCAE */{SST(0x3F, 0x0A, SS_RDEF, - "Volume set created or modified") }, -/* DT WR OMCAE */{SST(0x3F, 0x0B, SS_RDEF, - "Volume set deleted") }, -/* DT WR OMCAE */{SST(0x3F, 0x0C, SS_RDEF, - "Volume set deassigned") }, -/* DT WR OMCAE */{SST(0x3F, 0x0D, SS_RDEF, - "Volume set reassigned") }, -/* DTLPWRSOMCAE */{SST(0x3F, 0x0E, SS_RDEF, - "Reported luns data has changed") }, -/* DTLPWRSOMCAE */{SST(0x3F, 0x0F, SS_RETRY|SSQ_DECREMENT_COUNT - | SSQ_DELAY_RANDOM|EBUSY, - "Echo buffer overwritten") }, -/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, "Medium Loadable") }, -/* DT WR OM B*/{SST(0x3F, 0x0F, SS_RDEF, - "Medium auxiliary memory accessible") }, -/* D */{SST(0x40, 0x00, SS_RDEF, - "Ram failure") }, /* deprecated - use 40 NN instead */ -/* DTLPWRSOMCAE */{SST(0x40, 0x80, SS_RDEF, - "Diagnostic failure: ASCQ = Component ID") }, -/* DTLPWRSOMCAE */{SST(0x40, 0xFF, SS_RDEF|SSQ_RANGE, - NULL) },/* Range 0x80->0xFF */ -/* D */{SST(0x41, 0x00, SS_RDEF, - "Data path failure") }, /* deprecated - use 40 NN instead */ -/* D */{SST(0x42, 0x00, SS_RDEF, - "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */ -/* DTLPWRSOMCAE */{SST(0x43, 0x00, SS_RDEF, - "Message error") }, -/* DTLPWRSOMCAE */{SST(0x44, 0x00, SS_RDEF, - "Internal target failure") }, -/* DTLPWRSOMCAE */{SST(0x45, 0x00, SS_RDEF, - "Select or reselect failure") }, -/* DTLPWRSOMC */{SST(0x46, 0x00, SS_RDEF, - "Unsuccessful soft reset") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x00, SS_RDEF|SSQ_FALLBACK, - "SCSI parity error") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x01, SS_RDEF|SSQ_FALLBACK, - "Data Phase CRC error detected") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x02, SS_RDEF|SSQ_FALLBACK, - "SCSI parity error detected during ST data phase") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x03, SS_RDEF|SSQ_FALLBACK, - "Information Unit iuCRC error") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x04, SS_RDEF|SSQ_FALLBACK, - "Asynchronous information protection error detected") }, -/* DTLPWRSOMCAE */{SST(0x47, 0x05, SS_RDEF|SSQ_FALLBACK, - "Protocol server CRC error") }, -/* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_RDEF|SSQ_FALLBACK, - "Initiator detected error message received") }, -/* DTLPWRSOMCAE */{SST(0x49, 0x00, SS_RDEF, - "Invalid message error") }, -/* DTLPWRSOMCAE */{SST(0x4A, 0x00, SS_RDEF, - "Command phase error") }, -/* DTLPWRSOMCAE */{SST(0x4B, 0x00, SS_RDEF, - "Data phase error") }, -/* DTLPWRSOMCAE */{SST(0x4C, 0x00, SS_RDEF, - "Logical unit failed self-configuration") }, -/* DTLPWRSOMCAE */{SST(0x4D, 0x00, SS_RDEF, - "Tagged overlapped commands: ASCQ = Queue tag ID") }, -/* DTLPWRSOMCAE */{SST(0x4D, 0xFF, SS_RDEF|SSQ_RANGE, - NULL)}, /* Range 0x00->0xFF */ -/* DTLPWRSOMCAE */{SST(0x4E, 0x00, SS_RDEF, - "Overlapped commands attempted") }, -/* T */{SST(0x50, 0x00, SS_RDEF, - "Write append error") }, -/* T */{SST(0x50, 0x01, SS_RDEF, - "Write append position error") }, -/* T */{SST(0x50, 0x02, SS_RDEF, - "Position error related to timing") }, -/* T O */{SST(0x51, 0x00, SS_RDEF, - "Erase failure") }, -/* T */{SST(0x52, 0x00, SS_RDEF, - "Cartridge fault") }, -/* DTL WRSOM */{SST(0x53, 0x00, SS_RDEF, - "Media load or eject failed") }, -/* T */{SST(0x53, 0x01, SS_RDEF, - "Unload tape failure") }, -/* DT WR OM */{SST(0x53, 0x02, SS_RDEF, - "Medium removal prevented") }, -/* P */{SST(0x54, 0x00, SS_RDEF, - "Scsi to host system interface failure") }, -/* P */{SST(0x55, 0x00, SS_RDEF, - "System resource failure") }, -/* D O */{SST(0x55, 0x01, SS_FATAL|ENOSPC, - "System buffer full") }, -/* R */{SST(0x57, 0x00, SS_RDEF, - "Unable to recover table-of-contents") }, -/* O */{SST(0x58, 0x00, SS_RDEF, - "Generation does not exist") }, -/* O */{SST(0x59, 0x00, SS_RDEF, - "Updated block read") }, -/* DTLPWRSOM */{SST(0x5A, 0x00, SS_RDEF, - "Operator request or state change input") }, -/* DT WR OM */{SST(0x5A, 0x01, SS_RDEF, - "Operator medium removal request") }, -/* DT W O */{SST(0x5A, 0x02, SS_RDEF, - "Operator selected write protect") }, -/* DT W O */{SST(0x5A, 0x03, SS_RDEF, - "Operator selected write permit") }, -/* DTLPWRSOM */{SST(0x5B, 0x00, SS_RDEF, - "Log exception") }, -/* DTLPWRSOM */{SST(0x5B, 0x01, SS_RDEF, - "Threshold condition met") }, -/* DTLPWRSOM */{SST(0x5B, 0x02, SS_RDEF, - "Log counter at maximum") }, -/* DTLPWRSOM */{SST(0x5B, 0x03, SS_RDEF, - "Log list codes exhausted") }, -/* D O */{SST(0x5C, 0x00, SS_RDEF, - "RPL status change") }, -/* D O */{SST(0x5C, 0x01, SS_NOP|SSQ_PRINT_SENSE, - "Spindles synchronized") }, -/* D O */{SST(0x5C, 0x02, SS_RDEF, - "Spindles not synchronized") }, -/* DTLPWRSOMCAE */{SST(0x5D, 0x00, SS_RDEF, - "Failure prediction threshold exceeded") }, -/* DTLPWRSOMCAE */{SST(0x5D, 0xFF, SS_RDEF, - "Failure prediction threshold exceeded (false)") }, -/* DTLPWRSO CA */{SST(0x5E, 0x00, SS_RDEF, - "Low power condition on") }, -/* DTLPWRSO CA */{SST(0x5E, 0x01, SS_RDEF, - "Idle condition activated by timer") }, -/* DTLPWRSO CA */{SST(0x5E, 0x02, SS_RDEF, - "Standby condition activated by timer") }, -/* DTLPWRSO CA */{SST(0x5E, 0x03, SS_RDEF, - "Idle condition activated by command") }, -/* DTLPWRSO CA */{SST(0x5E, 0x04, SS_RDEF, - "Standby condition activated by command") }, -/* S */{SST(0x60, 0x00, SS_RDEF, - "Lamp failure") }, -/* S */{SST(0x61, 0x00, SS_RDEF, - "Video acquisition error") }, -/* S */{SST(0x61, 0x01, SS_RDEF, - "Unable to acquire video") }, -/* S */{SST(0x61, 0x02, SS_RDEF, - "Out of focus") }, -/* S */{SST(0x62, 0x00, SS_RDEF, - "Scan head positioning error") }, -/* R */{SST(0x63, 0x00, SS_RDEF, - "End of user area encountered on this track") }, -/* R */{SST(0x63, 0x01, SS_FATAL|ENOSPC, - "Packet does not fit in available space") }, -/* R */{SST(0x64, 0x00, SS_RDEF, - "Illegal mode for this track") }, -/* R */{SST(0x64, 0x01, SS_RDEF, - "Invalid packet size") }, -/* DTLPWRSOMCAE */{SST(0x65, 0x00, SS_RDEF, - "Voltage fault") }, -/* S */{SST(0x66, 0x00, SS_RDEF, - "Automatic document feeder cover up") }, -/* S */{SST(0x66, 0x01, SS_RDEF, - "Automatic document feeder lift up") }, -/* S */{SST(0x66, 0x02, SS_RDEF, - "Document jam in automatic document feeder") }, -/* S */{SST(0x66, 0x03, SS_RDEF, - "Document miss feed automatic in document feeder") }, -/* A */{SST(0x67, 0x00, SS_RDEF, - "Configuration failure") }, -/* A */{SST(0x67, 0x01, SS_RDEF, - "Configuration of incapable logical units failed") }, -/* A */{SST(0x67, 0x02, SS_RDEF, - "Add logical unit failed") }, -/* A */{SST(0x67, 0x03, SS_RDEF, - "Modification of logical unit failed") }, -/* A */{SST(0x67, 0x04, SS_RDEF, - "Exchange of logical unit failed") }, -/* A */{SST(0x67, 0x05, SS_RDEF, - "Remove of logical unit failed") }, -/* A */{SST(0x67, 0x06, SS_RDEF, - "Attachment of logical unit failed") }, -/* A */{SST(0x67, 0x07, SS_RDEF, - "Creation of logical unit failed") }, -/* A */{SST(0x68, 0x00, SS_RDEF, - "Logical unit not configured") }, -/* A */{SST(0x69, 0x00, SS_RDEF, - "Data loss on logical unit") }, -/* A */{SST(0x69, 0x01, SS_RDEF, - "Multiple logical unit failures") }, -/* A */{SST(0x69, 0x02, SS_RDEF, - "Parity/data mismatch") }, -/* A */{SST(0x6A, 0x00, SS_RDEF, - "Informational, refer to log") }, -/* A */{SST(0x6B, 0x00, SS_RDEF, - "State change has occurred") }, -/* A */{SST(0x6B, 0x01, SS_RDEF, - "Redundancy level got better") }, -/* A */{SST(0x6B, 0x02, SS_RDEF, - "Redundancy level got worse") }, -/* A */{SST(0x6C, 0x00, SS_RDEF, - "Rebuild failure occurred") }, -/* A */{SST(0x6D, 0x00, SS_RDEF, - "Recalculate failure occurred") }, -/* A */{SST(0x6E, 0x00, SS_RDEF, - "Command to logical unit failed") }, -/* T */{SST(0x70, 0x00, SS_RDEF, - "Decompression exception short: ASCQ = Algorithm ID") }, -/* T */{SST(0x70, 0xFF, SS_RDEF|SSQ_RANGE, - NULL) }, /* Range 0x00 -> 0xFF */ -/* T */{SST(0x71, 0x00, SS_RDEF, - "Decompression exception long: ASCQ = Algorithm ID") }, -/* T */{SST(0x71, 0xFF, SS_RDEF|SSQ_RANGE, - NULL) }, /* Range 0x00 -> 0xFF */ -/* R */{SST(0x72, 0x00, SS_RDEF, - "Session fixation error") }, -/* R */{SST(0x72, 0x01, SS_RDEF, - "Session fixation error writing lead-in") }, -/* R */{SST(0x72, 0x02, SS_RDEF, - "Session fixation error writing lead-out") }, -/* R */{SST(0x72, 0x03, SS_RDEF, - "Session fixation error - incomplete track in session") }, -/* R */{SST(0x72, 0x04, SS_RDEF, - "Empty or partially written reserved track") }, -/* R */{SST(0x73, 0x00, SS_RDEF, - "CD control error") }, -/* R */{SST(0x73, 0x01, SS_RDEF, - "Power calibration area almost full") }, -/* R */{SST(0x73, 0x02, SS_FATAL|ENOSPC, - "Power calibration area is full") }, -/* R */{SST(0x73, 0x03, SS_RDEF, - "Power calibration area error") }, -/* R */{SST(0x73, 0x04, SS_RDEF, - "Program memory area update failure") }, -/* R */{SST(0x73, 0x05, SS_RDEF, - "program memory area is full") } -}; - -static const int asc_table_size = sizeof(asc_table)/sizeof(asc_table[0]); - -struct asc_key -{ - int asc; - int ascq; -}; - -static int -ascentrycomp(const void *key, const void *member) -{ - int asc; - int ascq; - const struct asc_table_entry *table_entry; - - asc = ((const struct asc_key *)key)->asc; - ascq = ((const struct asc_key *)key)->ascq; - table_entry = (const struct asc_table_entry *)member; - - if (asc >= table_entry->asc) { - - if (asc > table_entry->asc) - return (1); - - if (ascq <= table_entry->ascq) { - /* Check for ranges */ - if (ascq == table_entry->ascq - || ((table_entry->action & SSQ_RANGE) != 0 - && ascq >= (table_entry - 1)->ascq)) - return (0); - return (-1); - } - return (1); - } - return (-1); -} - -static int -senseentrycomp(const void *key, const void *member) -{ - int sense_key; - const struct sense_key_table_entry *table_entry; - - sense_key = *((const int *)key); - table_entry = (const struct sense_key_table_entry *)member; - - if (sense_key >= table_entry->sense_key) { - if (sense_key == table_entry->sense_key) - return (0); - return (1); - } - return (-1); -} - -static void -fetchtableentries(int sense_key, int asc, int ascq, - struct scsi_inquiry_data *inq_data, - const struct sense_key_table_entry **sense_entry, - const struct asc_table_entry **asc_entry) -{ - void *match; - const struct asc_table_entry *asc_tables[2]; - const struct sense_key_table_entry *sense_tables[2]; - struct asc_key asc_ascq; - size_t asc_tables_size[2]; - size_t sense_tables_size[2]; - int num_asc_tables; - int num_sense_tables; - int i; - - /* Default to failure */ - *sense_entry = NULL; - *asc_entry = NULL; - match = NULL; - if (inq_data != NULL) - match = cam_quirkmatch((void *)inq_data, - (void *)sense_quirk_table, - sense_quirk_table_size, - sizeof(*sense_quirk_table), - aic_inquiry_match); - - if (match != NULL) { - struct scsi_sense_quirk_entry *quirk; - - quirk = (struct scsi_sense_quirk_entry *)match; - asc_tables[0] = quirk->asc_info; - asc_tables_size[0] = quirk->num_ascs; - asc_tables[1] = asc_table; - asc_tables_size[1] = asc_table_size; - num_asc_tables = 2; - sense_tables[0] = quirk->sense_key_info; - sense_tables_size[0] = quirk->num_sense_keys; - sense_tables[1] = sense_key_table; - sense_tables_size[1] = sense_key_table_size; - num_sense_tables = 2; - } else { - asc_tables[0] = asc_table; - asc_tables_size[0] = asc_table_size; - num_asc_tables = 1; - sense_tables[0] = sense_key_table; - sense_tables_size[0] = sense_key_table_size; - num_sense_tables = 1; - } - - asc_ascq.asc = asc; - asc_ascq.ascq = ascq; - for (i = 0; i < num_asc_tables; i++) { - void *found_entry; - - found_entry = scsibsearch(&asc_ascq, asc_tables[i], - asc_tables_size[i], - sizeof(**asc_tables), - ascentrycomp); - - if (found_entry) { - *asc_entry = (struct asc_table_entry *)found_entry; - break; - } - } - - for (i = 0; i < num_sense_tables; i++) { - void *found_entry; - - found_entry = scsibsearch(&sense_key, sense_tables[i], - sense_tables_size[i], - sizeof(**sense_tables), - senseentrycomp); - - if (found_entry) { - *sense_entry = - (struct sense_key_table_entry *)found_entry; - break; - } - } -} - -static void * -scsibsearch(const void *key, const void *base, size_t nmemb, size_t size, - int (*compar)(const void *, const void *)) -{ - const void *entry; - u_int l; - u_int u; - u_int m; - - l = -1; - u = nmemb; - while (l + 1 != u) { - m = (l + u) / 2; - entry = base + m * size; - if (compar(key, entry) > 0) - l = m; - else - u = m; - } - - entry = base + u * size; - if (u == nmemb - || compar(key, entry) != 0) - return (NULL); - - return ((void *)entry); -} - -/* - * Compare string with pattern, returning 0 on match. - * Short pattern matches trailing blanks in name, - * wildcard '*' in pattern matches rest of name, - * wildcard '?' matches a single non-space character. - */ -static int -cam_strmatch(const uint8_t *str, const uint8_t *pattern, int str_len) -{ - - while (*pattern != '\0'&& str_len > 0) { - - if (*pattern == '*') { - return (0); - } - if ((*pattern != *str) - && (*pattern != '?' || *str == ' ')) { - return (1); - } - pattern++; - str++; - str_len--; - } - while (str_len > 0 && *str++ == ' ') - str_len--; - - return (str_len); -} - -static caddr_t -cam_quirkmatch(caddr_t target, caddr_t quirk_table, int num_entries, - int entry_size, cam_quirkmatch_t *comp_func) -{ - for (; num_entries > 0; num_entries--, quirk_table += entry_size) { - if ((*comp_func)(target, quirk_table) == 0) - return (quirk_table); - } - return (NULL); -} - -void -aic_sense_desc(int sense_key, int asc, int ascq, - struct scsi_inquiry_data *inq_data, - const char **sense_key_desc, const char **asc_desc) -{ - const struct asc_table_entry *asc_entry; - const struct sense_key_table_entry *sense_entry; - - fetchtableentries(sense_key, asc, ascq, - inq_data, - &sense_entry, - &asc_entry); - - *sense_key_desc = sense_entry->desc; - - if (asc_entry != NULL) - *asc_desc = asc_entry->desc; - else if (asc >= 0x80 && asc <= 0xff) - *asc_desc = "Vendor Specific ASC"; - else if (ascq >= 0x80 && ascq <= 0xff) - *asc_desc = "Vendor Specific ASCQ"; - else - *asc_desc = "Reserved ASC/ASCQ pair"; -} - -/* - * Given sense and device type information, return the appropriate action. - * If we do not understand the specific error as identified by the ASC/ASCQ - * pair, fall back on the more generic actions derived from the sense key. - */ -aic_sense_action -aic_sense_error_action(struct scsi_sense_data *sense_data, - struct scsi_inquiry_data *inq_data, uint32_t sense_flags) -{ - const struct asc_table_entry *asc_entry; - const struct sense_key_table_entry *sense_entry; - int error_code, sense_key, asc, ascq; - aic_sense_action action; - - scsi_extract_sense(sense_data, &error_code, &sense_key, &asc, &ascq); - - if (error_code == SSD_DEFERRED_ERROR) { - /* - * XXX dufault@FreeBSD.org - * This error doesn't relate to the command associated - * with this request sense. A deferred error is an error - * for a command that has already returned GOOD status - * (see SCSI2 8.2.14.2). - * - * By my reading of that section, it looks like the current - * command has been cancelled, we should now clean things up - * (hopefully recovering any lost data) and then retry the - * current command. There are two easy choices, both wrong: - * - * 1. Drop through (like we had been doing), thus treating - * this as if the error were for the current command and - * return and stop the current command. - * - * 2. Issue a retry (like I made it do) thus hopefully - * recovering the current transfer, and ignoring the - * fact that we've dropped a command. - * - * These should probably be handled in a device specific - * sense handler or punted back up to a user mode daemon - */ - action = SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE; - } else { - fetchtableentries(sense_key, asc, ascq, - inq_data, - &sense_entry, - &asc_entry); - - /* - * Override the 'No additional Sense' entry (0,0) - * with the error action of the sense key. - */ - if (asc_entry != NULL - && (asc != 0 || ascq != 0)) - action = asc_entry->action; - else - action = sense_entry->action; - - if (sense_key == SSD_KEY_RECOVERED_ERROR) { - /* - * The action succeeded but the device wants - * the user to know that some recovery action - * was required. - */ - action &= ~(SS_MASK|SSQ_MASK|SS_ERRMASK); - action |= SS_NOP|SSQ_PRINT_SENSE; - } else if (sense_key == SSD_KEY_ILLEGAL_REQUEST) { - if ((sense_flags & SF_QUIET_IR) != 0) - action &= ~SSQ_PRINT_SENSE; - } else if (sense_key == SSD_KEY_UNIT_ATTENTION) { - if ((sense_flags & SF_RETRY_UA) != 0 - && (action & SS_MASK) == SS_FAIL) { - action &= ~(SS_MASK|SSQ_MASK); - action |= SS_RETRY|SSQ_DECREMENT_COUNT| - SSQ_PRINT_SENSE; - } - } - } - - if ((sense_flags & SF_PRINT_ALWAYS) != 0) - action |= SSQ_PRINT_SENSE; - else if ((sense_flags & SF_NO_PRINT) != 0) - action &= ~SSQ_PRINT_SENSE; - - return (action); -} - -/* - * Try make as good a match as possible with - * available sub drivers - */ -int -aic_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) -{ - struct scsi_inquiry_pattern *entry; - struct scsi_inquiry_data *inq; - - entry = (struct scsi_inquiry_pattern *)table_entry; - inq = (struct scsi_inquiry_data *)inqbuffer; - - if (((SID_TYPE(inq) == entry->type) - || (entry->type == T_ANY)) - && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE - : entry->media_type & SIP_MEDIA_FIXED) - && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) - && (cam_strmatch(inq->product, entry->product, - sizeof(inq->product)) == 0) - && (cam_strmatch(inq->revision, entry->revision, - sizeof(inq->revision)) == 0)) { - return (0); - } - return (-1); -} - -/* - * Table of syncrates that don't follow the "divisible by 4" - * rule. This table will be expanded in future SCSI specs. - */ -static struct { - u_int period_factor; - u_int period; /* in 100ths of ns */ -} scsi_syncrates[] = { - { 0x08, 625 }, /* FAST-160 */ - { 0x09, 1250 }, /* FAST-80 */ - { 0x0a, 2500 }, /* FAST-40 40MHz */ - { 0x0b, 3030 }, /* FAST-40 33MHz */ - { 0x0c, 5000 } /* FAST-20 */ -}; - -/* - * Return the frequency in kHz corresponding to the given - * sync period factor. - */ -u_int -aic_calc_syncsrate(u_int period_factor) -{ - int i; - int num_syncrates; - - num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); - /* See if the period is in the "exception" table */ - for (i = 0; i < num_syncrates; i++) { - - if (period_factor == scsi_syncrates[i].period_factor) { - /* Period in kHz */ - return (100000000 / scsi_syncrates[i].period); - } - } - - /* - * Wasn't in the table, so use the standard - * 4 times conversion. - */ - return (10000000 / (period_factor * 4 * 10)); -} - -/* - * Return speed in KB/s. - */ -u_int -aic_calc_speed(u_int width, u_int period, u_int offset, u_int min_rate) -{ - u_int freq; - - if (offset != 0 && period < min_rate) - freq = aic_calc_syncsrate(period); - else - /* Roughly 3.3MB/s for async */ - freq = 3300; - freq <<= width; - return (freq); -} - -uint32_t -aic_error_action(struct scsi_cmnd *cmd, struct scsi_inquiry_data *inq_data, - cam_status status, u_int scsi_status) -{ - aic_sense_action err_action; - int sense; - - sense = (cmd->result >> 24) == DRIVER_SENSE; - - switch (status) { - case CAM_REQ_CMP: - err_action = SS_NOP; - break; - case CAM_AUTOSENSE_FAIL: - case CAM_SCSI_STATUS_ERROR: - - switch (scsi_status) { - case SCSI_STATUS_OK: - case SCSI_STATUS_COND_MET: - case SCSI_STATUS_INTERMED: - case SCSI_STATUS_INTERMED_COND_MET: - err_action = SS_NOP; - break; - case SCSI_STATUS_CMD_TERMINATED: - case SCSI_STATUS_CHECK_COND: - if (sense != 0) { - struct scsi_sense_data *sense; - - sense = (struct scsi_sense_data *) - &cmd->sense_buffer; - err_action = - aic_sense_error_action(sense, inq_data, 0); - - } else { - err_action = SS_RETRY|SSQ_FALLBACK - | SSQ_DECREMENT_COUNT|EIO; - } - break; - case SCSI_STATUS_QUEUE_FULL: - case SCSI_STATUS_BUSY: - err_action = SS_RETRY|SSQ_DELAY|SSQ_MANY - | SSQ_DECREMENT_COUNT|EBUSY; - break; - case SCSI_STATUS_RESERV_CONFLICT: - default: - err_action = SS_FAIL|EBUSY; - break; - } - break; - case CAM_CMD_TIMEOUT: - case CAM_REQ_CMP_ERR: - case CAM_UNEXP_BUSFREE: - case CAM_UNCOR_PARITY: - case CAM_DATA_RUN_ERR: - err_action = SS_RETRY|SSQ_FALLBACK|EIO; - break; - case CAM_UA_ABORT: - case CAM_UA_TERMIO: - case CAM_MSG_REJECT_REC: - case CAM_SEL_TIMEOUT: - err_action = SS_FAIL|EIO; - break; - case CAM_REQ_INVALID: - case CAM_PATH_INVALID: - case CAM_DEV_NOT_THERE: - case CAM_NO_HBA: - case CAM_PROVIDE_FAIL: - case CAM_REQ_TOO_BIG: - case CAM_RESRC_UNAVAIL: - case CAM_BUSY: - default: - /* panic?? These should never occur in our application. */ - err_action = SS_FAIL|EIO; - break; - case CAM_SCSI_BUS_RESET: - case CAM_BDR_SENT: - case CAM_REQUEUE_REQ: - /* Unconditional requeue */ - err_action = SS_RETRY; - break; - } - - return (err_action); -} - -char * -aic_parse_brace_option(char *opt_name, char *opt_arg, char *end, int depth, - aic_option_callback_t *callback, u_long callback_arg) -{ - char *tok_end; - char *tok_end2; - int i; - int instance; - int targ; - int done; - char tok_list[] = {'.', ',', '{', '}', '\0'}; - /* All options use a ':' name/arg separator */ - if (*opt_arg != ':') - return (opt_arg); - opt_arg++; - instance = -1; - targ = -1; - done = FALSE; - /* - * Restore separator that may be in - * the middle of our option argument. - */ - tok_end = strchr(opt_arg, '\0'); - if (tok_end < end) - *tok_end = ','; - while (!done) { - switch (*opt_arg) { - case '{': - if (instance == -1) { - instance = 0; - } else { - if (depth > 1) { - if (targ == -1) - targ = 0; - } else { - printf("Malformed Option %s\n", - opt_name); - done = TRUE; - } - } - opt_arg++; - break; - case '}': - if (targ != -1) - targ = -1; - else if (instance != -1) - instance = -1; - opt_arg++; - break; - case ',': - case '.': - if (instance == -1) - done = TRUE; - else if (targ >= 0) - targ++; - else if (instance >= 0) - instance++; - opt_arg++; - break; - case '\0': - done = TRUE; - break; - default: - tok_end = end; - for (i = 0; tok_list[i]; i++) { - tok_end2 = strchr(opt_arg, tok_list[i]); - if ((tok_end2) && (tok_end2 < tok_end)) - tok_end = tok_end2; - } - callback(callback_arg, instance, targ, - simple_strtol(opt_arg, NULL, 0)); - opt_arg = tok_end; - break; - } - } - return (opt_arg); -} diff --git a/drivers/scsi/aic7xxx/aiclib.h b/drivers/scsi/aic7xxx/aiclib.h index bfe6f954d3c4..3bfbf0fe1ec2 100644 --- a/drivers/scsi/aic7xxx/aiclib.h +++ b/drivers/scsi/aic7xxx/aiclib.h @@ -57,121 +57,6 @@ #ifndef _AICLIB_H #define _AICLIB_H -/* - * Linux Interrupt Support. - */ -#ifndef IRQ_RETVAL -typedef void irqreturn_t; -#define IRQ_RETVAL(x) -#endif - -/* - * SCSI command format - */ - -/* - * Define dome bits that are in ALL (or a lot of) scsi commands - */ -#define SCSI_CTL_LINK 0x01 -#define SCSI_CTL_FLAG 0x02 -#define SCSI_CTL_VENDOR 0xC0 -#define SCSI_CMD_LUN 0xA0 /* these two should not be needed */ -#define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */ - -#define SCSI_MAX_CDBLEN 16 /* - * 16 byte commands are in the - * SCSI-3 spec - */ -/* 6byte CDBs special case 0 length to be 256 */ -#define SCSI_CDB6_LEN(len) ((len) == 0 ? 256 : len) - -/* - * This type defines actions to be taken when a particular sense code is - * received. Right now, these flags are only defined to take up 16 bits, - * but can be expanded in the future if necessary. - */ -typedef enum { - SS_NOP = 0x000000, /* Do nothing */ - SS_RETRY = 0x010000, /* Retry the command */ - SS_FAIL = 0x020000, /* Bail out */ - SS_START = 0x030000, /* Send a Start Unit command to the device, - * then retry the original command. - */ - SS_TUR = 0x040000, /* Send a Test Unit Ready command to the - * device, then retry the original command. - */ - SS_REQSENSE = 0x050000, /* Send a RequestSense command to the - * device, then retry the original command. - */ - SS_INQ_REFRESH = 0x060000, - SS_MASK = 0xff0000 -} aic_sense_action; - -typedef enum { - SSQ_NONE = 0x0000, - SSQ_DECREMENT_COUNT = 0x0100, /* Decrement the retry count */ - SSQ_MANY = 0x0200, /* send lots of recovery commands */ - SSQ_RANGE = 0x0400, /* - * This table entry represents the - * end of a range of ASCQs that - * have identical error actions - * and text. - */ - SSQ_PRINT_SENSE = 0x0800, - SSQ_DELAY = 0x1000, /* Delay before retry. */ - SSQ_DELAY_RANDOM = 0x2000, /* Randomized delay before retry. */ - SSQ_FALLBACK = 0x4000, /* Do a speed fallback to recover */ - SSQ_MASK = 0xff00 -} aic_sense_action_qualifier; - -/* Mask for error status values */ -#define SS_ERRMASK 0xff - -/* The default, retyable, error action */ -#define SS_RDEF SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE|EIO - -/* The retyable, error action, with table specified error code */ -#define SS_RET SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE - -/* Fatal error action, with table specified error code */ -#define SS_FATAL SS_FAIL|SSQ_PRINT_SENSE - -struct scsi_generic -{ - uint8_t opcode; - uint8_t bytes[11]; -}; - -struct scsi_request_sense -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[2]; - uint8_t length; - uint8_t control; -}; - -struct scsi_test_unit_ready -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[3]; - uint8_t control; -}; - -struct scsi_send_diag -{ - uint8_t opcode; - uint8_t byte2; -#define SSD_UOL 0x01 -#define SSD_DOL 0x02 -#define SSD_SELFTEST 0x04 -#define SSD_PF 0x10 - uint8_t unused[1]; - uint8_t paramlen[2]; - uint8_t control; -}; - struct scsi_sense { uint8_t opcode; @@ -181,537 +66,12 @@ struct scsi_sense uint8_t control; }; -struct scsi_inquiry -{ - uint8_t opcode; - uint8_t byte2; -#define SI_EVPD 0x01 - uint8_t page_code; - uint8_t reserved; - uint8_t length; - uint8_t control; -}; - -struct scsi_mode_sense_6 -{ - uint8_t opcode; - uint8_t byte2; -#define SMS_DBD 0x08 - uint8_t page; -#define SMS_PAGE_CODE 0x3F -#define SMS_VENDOR_SPECIFIC_PAGE 0x00 -#define SMS_DISCONNECT_RECONNECT_PAGE 0x02 -#define SMS_PERIPHERAL_DEVICE_PAGE 0x09 -#define SMS_CONTROL_MODE_PAGE 0x0A -#define SMS_ALL_PAGES_PAGE 0x3F -#define SMS_PAGE_CTRL_MASK 0xC0 -#define SMS_PAGE_CTRL_CURRENT 0x00 -#define SMS_PAGE_CTRL_CHANGEABLE 0x40 -#define SMS_PAGE_CTRL_DEFAULT 0x80 -#define SMS_PAGE_CTRL_SAVED 0xC0 - uint8_t unused; - uint8_t length; - uint8_t control; -}; - -struct scsi_mode_sense_10 -{ - uint8_t opcode; - uint8_t byte2; /* same bits as small version */ - uint8_t page; /* same bits as small version */ - uint8_t unused[4]; - uint8_t length[2]; - uint8_t control; -}; - -struct scsi_mode_select_6 -{ - uint8_t opcode; - uint8_t byte2; -#define SMS_SP 0x01 -#define SMS_PF 0x10 - uint8_t unused[2]; - uint8_t length; - uint8_t control; -}; - -struct scsi_mode_select_10 -{ - uint8_t opcode; - uint8_t byte2; /* same bits as small version */ - uint8_t unused[5]; - uint8_t length[2]; - uint8_t control; -}; - -/* - * When sending a mode select to a tape drive, the medium type must be 0. - */ -struct scsi_mode_hdr_6 -{ - uint8_t datalen; - uint8_t medium_type; - uint8_t dev_specific; - uint8_t block_descr_len; -}; - -struct scsi_mode_hdr_10 -{ - uint8_t datalen[2]; - uint8_t medium_type; - uint8_t dev_specific; - uint8_t reserved[2]; - uint8_t block_descr_len[2]; -}; - -struct scsi_mode_block_descr -{ - uint8_t density_code; - uint8_t num_blocks[3]; - uint8_t reserved; - uint8_t block_len[3]; -}; - -struct scsi_log_sense -{ - uint8_t opcode; - uint8_t byte2; -#define SLS_SP 0x01 -#define SLS_PPC 0x02 - uint8_t page; -#define SLS_PAGE_CODE 0x3F -#define SLS_ALL_PAGES_PAGE 0x00 -#define SLS_OVERRUN_PAGE 0x01 -#define SLS_ERROR_WRITE_PAGE 0x02 -#define SLS_ERROR_READ_PAGE 0x03 -#define SLS_ERROR_READREVERSE_PAGE 0x04 -#define SLS_ERROR_VERIFY_PAGE 0x05 -#define SLS_ERROR_NONMEDIUM_PAGE 0x06 -#define SLS_ERROR_LASTN_PAGE 0x07 -#define SLS_PAGE_CTRL_MASK 0xC0 -#define SLS_PAGE_CTRL_THRESHOLD 0x00 -#define SLS_PAGE_CTRL_CUMULATIVE 0x40 -#define SLS_PAGE_CTRL_THRESH_DEFAULT 0x80 -#define SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0 - uint8_t reserved[2]; - uint8_t paramptr[2]; - uint8_t length[2]; - uint8_t control; -}; - -struct scsi_log_select -{ - uint8_t opcode; - uint8_t byte2; -/* SLS_SP 0x01 */ -#define SLS_PCR 0x02 - uint8_t page; -/* SLS_PAGE_CTRL_MASK 0xC0 */ -/* SLS_PAGE_CTRL_THRESHOLD 0x00 */ -/* SLS_PAGE_CTRL_CUMULATIVE 0x40 */ -/* SLS_PAGE_CTRL_THRESH_DEFAULT 0x80 */ -/* SLS_PAGE_CTRL_CUMUL_DEFAULT 0xC0 */ - uint8_t reserved[4]; - uint8_t length[2]; - uint8_t control; -}; - -struct scsi_log_header -{ - uint8_t page; - uint8_t reserved; - uint8_t datalen[2]; -}; - -struct scsi_log_param_header { - uint8_t param_code[2]; - uint8_t param_control; -#define SLP_LP 0x01 -#define SLP_LBIN 0x02 -#define SLP_TMC_MASK 0x0C -#define SLP_TMC_ALWAYS 0x00 -#define SLP_TMC_EQUAL 0x04 -#define SLP_TMC_NOTEQUAL 0x08 -#define SLP_TMC_GREATER 0x0C -#define SLP_ETC 0x10 -#define SLP_TSD 0x20 -#define SLP_DS 0x40 -#define SLP_DU 0x80 - uint8_t param_len; -}; - -struct scsi_control_page { - uint8_t page_code; - uint8_t page_length; - uint8_t rlec; -#define SCB_RLEC 0x01 /*Report Log Exception Cond*/ - uint8_t queue_flags; -#define SCP_QUEUE_ALG_MASK 0xF0 -#define SCP_QUEUE_ALG_RESTRICTED 0x00 -#define SCP_QUEUE_ALG_UNRESTRICTED 0x10 -#define SCP_QUEUE_ERR 0x02 /*Queued I/O aborted for CACs*/ -#define SCP_QUEUE_DQUE 0x01 /*Queued I/O disabled*/ - uint8_t eca_and_aen; -#define SCP_EECA 0x80 /*Enable Extended CA*/ -#define SCP_RAENP 0x04 /*Ready AEN Permission*/ -#define SCP_UAAENP 0x02 /*UA AEN Permission*/ -#define SCP_EAENP 0x01 /*Error AEN Permission*/ - uint8_t reserved; - uint8_t aen_holdoff_period[2]; -}; - -struct scsi_reserve -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[2]; - uint8_t length; - uint8_t control; -}; - -struct scsi_release -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[2]; - uint8_t length; - uint8_t control; -}; - -struct scsi_prevent -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[2]; - uint8_t how; - uint8_t control; -}; -#define PR_PREVENT 0x01 -#define PR_ALLOW 0x00 - -struct scsi_sync_cache -{ - uint8_t opcode; - uint8_t byte2; - uint8_t begin_lba[4]; - uint8_t reserved; - uint8_t lb_count[2]; - uint8_t control; -}; - - -struct scsi_changedef -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused1; - uint8_t how; - uint8_t unused[4]; - uint8_t datalen; - uint8_t control; -}; - -struct scsi_read_buffer -{ - uint8_t opcode; - uint8_t byte2; -#define RWB_MODE 0x07 -#define RWB_MODE_HDR_DATA 0x00 -#define RWB_MODE_DATA 0x02 -#define RWB_MODE_DOWNLOAD 0x04 -#define RWB_MODE_DOWNLOAD_SAVE 0x05 - uint8_t buffer_id; - uint8_t offset[3]; - uint8_t length[3]; - uint8_t control; -}; - -struct scsi_write_buffer -{ - uint8_t opcode; - uint8_t byte2; - uint8_t buffer_id; - uint8_t offset[3]; - uint8_t length[3]; - uint8_t control; -}; - -struct scsi_rw_6 -{ - uint8_t opcode; - uint8_t addr[3]; -/* only 5 bits are valid in the MSB address byte */ -#define SRW_TOPADDR 0x1F - uint8_t length; - uint8_t control; -}; - -struct scsi_rw_10 -{ - uint8_t opcode; -#define SRW10_RELADDR 0x01 -#define SRW10_FUA 0x08 -#define SRW10_DPO 0x10 - uint8_t byte2; - uint8_t addr[4]; - uint8_t reserved; - uint8_t length[2]; - uint8_t control; -}; - -struct scsi_rw_12 -{ - uint8_t opcode; -#define SRW12_RELADDR 0x01 -#define SRW12_FUA 0x08 -#define SRW12_DPO 0x10 - uint8_t byte2; - uint8_t addr[4]; - uint8_t length[4]; - uint8_t reserved; - uint8_t control; -}; - -struct scsi_start_stop_unit -{ - uint8_t opcode; - uint8_t byte2; -#define SSS_IMMED 0x01 - uint8_t reserved[2]; - uint8_t how; -#define SSS_START 0x01 -#define SSS_LOEJ 0x02 - uint8_t control; -}; - -#define SC_SCSI_1 0x01 -#define SC_SCSI_2 0x03 - -/* - * Opcodes - */ - -#define TEST_UNIT_READY 0x00 -#define REQUEST_SENSE 0x03 -#define READ_6 0x08 -#define WRITE_6 0x0a -#define INQUIRY 0x12 -#define MODE_SELECT_6 0x15 -#define MODE_SENSE_6 0x1a -#define START_STOP_UNIT 0x1b -#define START_STOP 0x1b -#define RESERVE 0x16 -#define RELEASE 0x17 -#define RECEIVE_DIAGNOSTIC 0x1c -#define SEND_DIAGNOSTIC 0x1d -#define PREVENT_ALLOW 0x1e -#define READ_CAPACITY 0x25 -#define READ_10 0x28 -#define WRITE_10 0x2a -#define POSITION_TO_ELEMENT 0x2b -#define SYNCHRONIZE_CACHE 0x35 -#define WRITE_BUFFER 0x3b -#define READ_BUFFER 0x3c -#define CHANGE_DEFINITION 0x40 -#define LOG_SELECT 0x4c -#define LOG_SENSE 0x4d -#ifdef XXXCAM -#define MODE_SENSE_10 0x5A -#endif -#define MODE_SELECT_10 0x55 -#define MOVE_MEDIUM 0xa5 -#define READ_12 0xa8 -#define WRITE_12 0xaa -#define READ_ELEMENT_STATUS 0xb8 - - -/* - * Device Types - */ -#define T_DIRECT 0x00 -#define T_SEQUENTIAL 0x01 -#define T_PRINTER 0x02 -#define T_PROCESSOR 0x03 -#define T_WORM 0x04 -#define T_CDROM 0x05 -#define T_SCANNER 0x06 -#define T_OPTICAL 0x07 -#define T_CHANGER 0x08 -#define T_COMM 0x09 -#define T_ASC0 0x0a -#define T_ASC1 0x0b -#define T_STORARRAY 0x0c -#define T_ENCLOSURE 0x0d -#define T_RBC 0x0e -#define T_OCRW 0x0f -#define T_NODEVICE 0x1F -#define T_ANY 0xFF /* Used in Quirk table matches */ - -#define T_REMOV 1 -#define T_FIXED 0 - -/* - * This length is the initial inquiry length used by the probe code, as - * well as the legnth necessary for aic_print_inquiry() to function - * correctly. If either use requires a different length in the future, - * the two values should be de-coupled. - */ -#define SHORT_INQUIRY_LENGTH 36 - -struct scsi_inquiry_data -{ - uint8_t device; -#define SID_TYPE(inq_data) ((inq_data)->device & 0x1f) -#define SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5) -#define SID_QUAL_LU_CONNECTED 0x00 /* - * The specified peripheral device - * type is currently connected to - * logical unit. If the target cannot - * determine whether or not a physical - * device is currently connected, it - * shall also use this peripheral - * qualifier when returning the INQUIRY - * data. This peripheral qualifier - * does not mean that the device is - * ready for access by the initiator. - */ -#define SID_QUAL_LU_OFFLINE 0x01 /* - * The target is capable of supporting - * the specified peripheral device type - * on this logical unit; however, the - * physical device is not currently - * connected to this logical unit. - */ -#define SID_QUAL_RSVD 0x02 -#define SID_QUAL_BAD_LU 0x03 /* - * The target is not capable of - * supporting a physical device on - * this logical unit. For this - * peripheral qualifier the peripheral - * device type shall be set to 1Fh to - * provide compatibility with previous - * versions of SCSI. All other - * peripheral device type values are - * reserved for this peripheral - * qualifier. - */ -#define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0) - uint8_t dev_qual2; -#define SID_QUAL2 0x7F -#define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0) - uint8_t version; -#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07) #define SCSI_REV_0 0 #define SCSI_REV_CCS 1 #define SCSI_REV_2 2 #define SCSI_REV_SPC 3 #define SCSI_REV_SPC2 4 -#define SID_ECMA 0x38 -#define SID_ISO 0xC0 - uint8_t response_format; -#define SID_AENC 0x80 -#define SID_TrmIOP 0x40 - uint8_t additional_length; - uint8_t reserved[2]; - uint8_t flags; -#define SID_SftRe 0x01 -#define SID_CmdQue 0x02 -#define SID_Linked 0x08 -#define SID_Sync 0x10 -#define SID_WBus16 0x20 -#define SID_WBus32 0x40 -#define SID_RelAdr 0x80 -#define SID_VENDOR_SIZE 8 - char vendor[SID_VENDOR_SIZE]; -#define SID_PRODUCT_SIZE 16 - char product[SID_PRODUCT_SIZE]; -#define SID_REVISION_SIZE 4 - char revision[SID_REVISION_SIZE]; - /* - * The following fields were taken from SCSI Primary Commands - 2 - * (SPC-2) Revision 14, Dated 11 November 1999 - */ -#define SID_VENDOR_SPECIFIC_0_SIZE 20 - uint8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE]; - /* - * An extension of SCSI Parallel Specific Values - */ -#define SID_SPI_IUS 0x01 -#define SID_SPI_QAS 0x02 -#define SID_SPI_CLOCK_ST 0x00 -#define SID_SPI_CLOCK_DT 0x04 -#define SID_SPI_CLOCK_DT_ST 0x0C -#define SID_SPI_MASK 0x0F - uint8_t spi3data; - uint8_t reserved2; - /* - * Version Descriptors, stored 2 byte values. - */ - uint8_t version1[2]; - uint8_t version2[2]; - uint8_t version3[2]; - uint8_t version4[2]; - uint8_t version5[2]; - uint8_t version6[2]; - uint8_t version7[2]; - uint8_t version8[2]; - - uint8_t reserved3[22]; - -#define SID_VENDOR_SPECIFIC_1_SIZE 160 - uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE]; -}; - -struct scsi_vpd_unit_serial_number -{ - uint8_t device; - uint8_t page_code; -#define SVPD_UNIT_SERIAL_NUMBER 0x80 - uint8_t reserved; - uint8_t length; /* serial number length */ -#define SVPD_SERIAL_NUM_SIZE 251 - uint8_t serial_num[SVPD_SERIAL_NUM_SIZE]; -}; - -struct scsi_read_capacity -{ - uint8_t opcode; - uint8_t byte2; - uint8_t addr[4]; - uint8_t unused[3]; - uint8_t control; -}; - -struct scsi_read_capacity_data -{ - uint8_t addr[4]; - uint8_t length[4]; -}; - -struct scsi_report_luns -{ - uint8_t opcode; - uint8_t byte2; - uint8_t unused[3]; - uint8_t addr[4]; - uint8_t control; -}; - -struct scsi_report_luns_data { - uint8_t length[4]; /* length of LUN inventory, in bytes */ - uint8_t reserved[4]; /* unused */ - /* - * LUN inventory- we only support the type zero form for now. - */ - struct { - uint8_t lundata[8]; - } luns[1]; -}; -#define RPL_LUNDATA_ATYP_MASK 0xc0 /* MBZ for type 0 lun */ -#define RPL_LUNDATA_T0LUN 1 /* @ lundata[1] */ - - struct scsi_sense_data { uint8_t error_code; @@ -757,41 +117,6 @@ struct scsi_sense_data #define SSD_FULL_SIZE sizeof(struct scsi_sense_data) }; -struct scsi_mode_header_6 -{ - uint8_t data_length; /* Sense data length */ - uint8_t medium_type; - uint8_t dev_spec; - uint8_t blk_desc_len; -}; - -struct scsi_mode_header_10 -{ - uint8_t data_length[2];/* Sense data length */ - uint8_t medium_type; - uint8_t dev_spec; - uint8_t unused[2]; - uint8_t blk_desc_len[2]; -}; - -struct scsi_mode_page_header -{ - uint8_t page_code; - uint8_t page_length; -}; - -struct scsi_mode_blk_desc -{ - uint8_t density; - uint8_t nblocks[3]; - uint8_t reserved; - uint8_t blklen[3]; -}; - -#define SCSI_DEFAULT_DENSITY 0x00 /* use 'default' density */ -#define SCSI_SAME_DENSITY 0x7f /* use 'same' density- >= SCSI-2 only */ - - /* * Status Byte */ @@ -807,76 +132,7 @@ struct scsi_mode_blk_desc #define SCSI_STATUS_ACA_ACTIVE 0x30 #define SCSI_STATUS_TASK_ABORTED 0x40 -struct scsi_inquiry_pattern { - uint8_t type; - uint8_t media_type; -#define SIP_MEDIA_REMOVABLE 0x01 -#define SIP_MEDIA_FIXED 0x02 - const char *vendor; - const char *product; - const char *revision; -}; - -struct scsi_static_inquiry_pattern { - uint8_t type; - uint8_t media_type; - char vendor[SID_VENDOR_SIZE+1]; - char product[SID_PRODUCT_SIZE+1]; - char revision[SID_REVISION_SIZE+1]; -}; - -struct scsi_sense_quirk_entry { - struct scsi_inquiry_pattern inq_pat; - int num_sense_keys; - int num_ascs; - struct sense_key_table_entry *sense_key_info; - struct asc_table_entry *asc_info; -}; - -struct sense_key_table_entry { - uint8_t sense_key; - uint32_t action; - const char *desc; -}; - -struct asc_table_entry { - uint8_t asc; - uint8_t ascq; - uint32_t action; - const char *desc; -}; - -struct op_table_entry { - uint8_t opcode; - uint16_t opmask; - const char *desc; -}; - -struct scsi_op_quirk_entry { - struct scsi_inquiry_pattern inq_pat; - int num_ops; - struct op_table_entry *op_table; -}; - -typedef enum { - SSS_FLAG_NONE = 0x00, - SSS_FLAG_PRINT_COMMAND = 0x01 -} scsi_sense_string_flags; - -extern const char *scsi_sense_key_text[]; - /************************* Large Disk Handling ********************************/ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -static __inline int aic_sector_div(u_long capacity, int heads, int sectors); - -static __inline int -aic_sector_div(u_long capacity, int heads, int sectors) -{ - return (capacity / (heads * sectors)); -} -#else -static __inline int aic_sector_div(sector_t capacity, int heads, int sectors); - static __inline int aic_sector_div(sector_t capacity, int heads, int sectors) { @@ -884,152 +140,6 @@ aic_sector_div(sector_t capacity, int heads, int sectors) sector_div(capacity, (heads * sectors)); return (int)capacity; } -#endif - -/**************************** Module Library Hack *****************************/ -/* - * What we'd like to do is have a single "scsi library" module that both the - * aic7xxx and aic79xx drivers could load and depend on. A cursory examination - * of implementing module dependencies in Linux (handling the install and - * initrd cases) does not look promissing. For now, we just duplicate this - * code in both drivers using a simple symbol renaming scheme that hides this - * hack from the drivers. - */ -#define AIC_LIB_ENTRY_CONCAT(x, prefix) prefix ## x -#define AIC_LIB_ENTRY_EXPAND(x, prefix) AIC_LIB_ENTRY_CONCAT(x, prefix) -#define AIC_LIB_ENTRY(x) AIC_LIB_ENTRY_EXPAND(x, AIC_LIB_PREFIX) - -#define aic_sense_desc AIC_LIB_ENTRY(_sense_desc) -#define aic_sense_error_action AIC_LIB_ENTRY(_sense_error_action) -#define aic_error_action AIC_LIB_ENTRY(_error_action) -#define aic_op_desc AIC_LIB_ENTRY(_op_desc) -#define aic_cdb_string AIC_LIB_ENTRY(_cdb_string) -#define aic_print_inquiry AIC_LIB_ENTRY(_print_inquiry) -#define aic_calc_syncsrate AIC_LIB_ENTRY(_calc_syncrate) -#define aic_calc_syncparam AIC_LIB_ENTRY(_calc_syncparam) -#define aic_calc_speed AIC_LIB_ENTRY(_calc_speed) -#define aic_inquiry_match AIC_LIB_ENTRY(_inquiry_match) -#define aic_static_inquiry_match AIC_LIB_ENTRY(_static_inquiry_match) -#define aic_parse_brace_option AIC_LIB_ENTRY(_parse_brace_option) - -/******************************************************************************/ - -void aic_sense_desc(int /*sense_key*/, int /*asc*/, - int /*ascq*/, struct scsi_inquiry_data*, - const char** /*sense_key_desc*/, - const char** /*asc_desc*/); -aic_sense_action aic_sense_error_action(struct scsi_sense_data*, - struct scsi_inquiry_data*, - uint32_t /*sense_flags*/); -uint32_t aic_error_action(struct scsi_cmnd *, - struct scsi_inquiry_data *, - cam_status, u_int); - -#define SF_RETRY_UA 0x01 -#define SF_NO_PRINT 0x02 -#define SF_QUIET_IR 0x04 /* Be quiet about Illegal Request reponses */ -#define SF_PRINT_ALWAYS 0x08 - - -const char * aic_op_desc(uint16_t /*opcode*/, struct scsi_inquiry_data*); -char * aic_cdb_string(uint8_t* /*cdb_ptr*/, char* /*cdb_string*/, - size_t /*len*/); -void aic_print_inquiry(struct scsi_inquiry_data*); - -u_int aic_calc_syncsrate(u_int /*period_factor*/); -u_int aic_calc_syncparam(u_int /*period*/); -u_int aic_calc_speed(u_int width, u_int period, u_int offset, - u_int min_rate); - -int aic_inquiry_match(caddr_t /*inqbuffer*/, - caddr_t /*table_entry*/); -int aic_static_inquiry_match(caddr_t /*inqbuffer*/, - caddr_t /*table_entry*/); - -typedef void aic_option_callback_t(u_long, int, int, int32_t); -char * aic_parse_brace_option(char *opt_name, char *opt_arg, - char *end, int depth, - aic_option_callback_t *, u_long); - -static __inline void scsi_extract_sense(struct scsi_sense_data *sense, - int *error_code, int *sense_key, - int *asc, int *ascq); -static __inline void scsi_ulto2b(uint32_t val, uint8_t *bytes); -static __inline void scsi_ulto3b(uint32_t val, uint8_t *bytes); -static __inline void scsi_ulto4b(uint32_t val, uint8_t *bytes); -static __inline uint32_t scsi_2btoul(uint8_t *bytes); -static __inline uint32_t scsi_3btoul(uint8_t *bytes); -static __inline int32_t scsi_3btol(uint8_t *bytes); -static __inline uint32_t scsi_4btoul(uint8_t *bytes); - -static __inline void scsi_extract_sense(struct scsi_sense_data *sense, - int *error_code, int *sense_key, - int *asc, int *ascq) -{ - *error_code = sense->error_code & SSD_ERRCODE; - *sense_key = sense->flags & SSD_KEY; - *asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; - *ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; -} - -static __inline void -scsi_ulto2b(uint32_t val, uint8_t *bytes) -{ - - bytes[0] = (val >> 8) & 0xff; - bytes[1] = val & 0xff; -} - -static __inline void -scsi_ulto3b(uint32_t val, uint8_t *bytes) -{ - - bytes[0] = (val >> 16) & 0xff; - bytes[1] = (val >> 8) & 0xff; - bytes[2] = val & 0xff; -} - -static __inline void -scsi_ulto4b(uint32_t val, uint8_t *bytes) -{ - - bytes[0] = (val >> 24) & 0xff; - bytes[1] = (val >> 16) & 0xff; - bytes[2] = (val >> 8) & 0xff; - bytes[3] = val & 0xff; -} - -static __inline uint32_t -scsi_2btoul(uint8_t *bytes) -{ - uint32_t rv; - - rv = (bytes[0] << 8) | - bytes[1]; - return (rv); -} - -static __inline uint32_t -scsi_3btoul(uint8_t *bytes) -{ - uint32_t rv; - - rv = (bytes[0] << 16) | - (bytes[1] << 8) | - bytes[2]; - return (rv); -} - -static __inline int32_t -scsi_3btol(uint8_t *bytes) -{ - uint32_t rc = scsi_3btoul(bytes); - - if (rc & 0x00800000) - rc |= 0xff000000; - - return (int32_t) rc; -} static __inline uint32_t scsi_4btoul(uint8_t *bytes) diff --git a/drivers/scsi/arm/Kconfig b/drivers/scsi/arm/Kconfig index 54b32868aaf7..13f23043c8a3 100644 --- a/drivers/scsi/arm/Kconfig +++ b/drivers/scsi/arm/Kconfig @@ -3,7 +3,7 @@ # config SCSI_ACORNSCSI_3 tristate "Acorn SCSI card (aka30) support" - depends on ARCH_ACORN && SCSI + depends on ARCH_ACORN && SCSI && BROKEN help This enables support for the Acorn SCSI card (aka30). If you have an Acorn system with one of these, say Y. If unsure, say N. diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/scsi.h index 48e1c4d9738b..19937640e2e7 100644 --- a/drivers/scsi/arm/scsi.h +++ b/drivers/scsi/arm/scsi.h @@ -10,6 +10,8 @@ * Commonly used scsi driver functions. */ +#include <linux/scatterlist.h> + #define BELT_AND_BRACES /* @@ -22,9 +24,7 @@ static inline int copy_SCp_to_sg(struct scatterlist *sg, Scsi_Pointer *SCp, int BUG_ON(bufs + 1 > max); - sg->page = virt_to_page(SCp->ptr); - sg->offset = offset_in_page(SCp->ptr); - sg->length = SCp->this_residual; + sg_set_buf(sg, SCp->ptr, SCp->this_residual); if (bufs) memcpy(sg + 1, SCp->buffer + 1, diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index a2cfade2c1c6..7f8aa1b552ce 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -1,24 +1,42 @@ /* - - ata_piix.c - Intel PATA/SATA controllers - - Maintained by: Jeff Garzik <jgarzik@pobox.com> - Please ALWAYS copy linux-ide@vger.kernel.org - on emails. - - - Copyright 2003-2004 Red Hat Inc - Copyright 2003-2004 Jeff Garzik - - - Copyright header from piix.c: - - Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer - Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> - Copyright (C) 2003 Red Hat Inc <alan@redhat.com> - - May be copied or modified under the terms of the GNU General Public License - + * ata_piix.c - Intel PATA/SATA controllers + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * + * Copyright 2003-2005 Red Hat Inc + * Copyright 2003-2005 Jeff Garzik + * + * + * Copyright header from piix.c: + * + * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat Inc <alan@redhat.com> + * + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available at http://developer.intel.com/ + * */ #include <linux/kernel.h> @@ -27,12 +45,13 @@ #include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> #define DRV_NAME "ata_piix" -#define DRV_VERSION "1.03" +#define DRV_VERSION "1.04" enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ @@ -50,8 +69,8 @@ enum { PIIX_COMB_PATA_P0 = (1 << 1), PIIX_COMB = (1 << 2), /* combined mode enabled? */ - PIIX_PORT_PRESENT = (1 << 0), - PIIX_PORT_ENABLED = (1 << 4), + PIIX_PORT_ENABLED = (1 << 0), + PIIX_PORT_PRESENT = (1 << 4), PIIX_80C_PRI = (1 << 5) | (1 << 4), PIIX_80C_SEC = (1 << 7) | (1 << 6), @@ -129,7 +148,7 @@ static Scsi_Host_Template piix_sht = { .ordered_flush = 1, }; -static struct ata_port_operations piix_pata_ops = { +static const struct ata_port_operations piix_pata_ops = { .port_disable = ata_port_disable, .set_piomode = piix_set_piomode, .set_dmamode = piix_set_dmamode, @@ -159,7 +178,7 @@ static struct ata_port_operations piix_pata_ops = { .host_stop = ata_host_stop, }; -static struct ata_port_operations piix_sata_ops = { +static const struct ata_port_operations piix_sata_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, @@ -359,7 +378,9 @@ static void piix_pata_phy_reset(struct ata_port *ap) * None (inherited from caller). * * RETURNS: - * Non-zero if device detected, zero otherwise. + * Non-zero if port is enabled, it may or may not have a device + * attached in that case (PRESENT bit would only be set if BIOS probe + * was done). Zero is returned if port is disabled. */ static int piix_sata_probe (struct ata_port *ap) { @@ -383,7 +404,7 @@ static int piix_sata_probe (struct ata_port *ap) */ for (i = 0; i < 4; i++) { - mask = (PIIX_PORT_PRESENT << i) | (PIIX_PORT_ENABLED << i); + mask = (PIIX_PORT_ENABLED << i); if ((orig_mask & mask) == mask) if (combined || (i == ap->hard_port_no)) @@ -422,7 +443,6 @@ static void piix_sata_phy_reset(struct ata_port *ap) * piix_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring * @adev: um - * @pio: PIO mode, 0 - 4 * * Set PIO mode for device, in host controller PCI config space. * @@ -548,25 +568,12 @@ static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev) } } -/* move to PCI layer, integrate w/ MSI stuff */ -static void pci_enable_intx(struct pci_dev *pdev) -{ - u16 pci_command; - - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_INTX_DISABLE) { - pci_command &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); - } -} - #define AHCI_PCI_BAR 5 #define AHCI_GLOBAL_CTL 0x04 #define AHCI_ENABLE (1 << 31) static int piix_disable_ahci(struct pci_dev *pdev) { - void *mmio; - unsigned long addr; + void __iomem *mmio; u32 tmp; int rc = 0; @@ -574,11 +581,11 @@ static int piix_disable_ahci(struct pci_dev *pdev) * works because this device is usually set up by BIOS. */ - addr = pci_resource_start(pdev, AHCI_PCI_BAR); - if (!addr || !pci_resource_len(pdev, AHCI_PCI_BAR)) + if (!pci_resource_start(pdev, AHCI_PCI_BAR) || + !pci_resource_len(pdev, AHCI_PCI_BAR)) return 0; - mmio = ioremap(addr, 64); + mmio = pci_iomap(pdev, AHCI_PCI_BAR, 64); if (!mmio) return -ENOMEM; @@ -592,7 +599,7 @@ static int piix_disable_ahci(struct pci_dev *pdev) rc = -EIO; } - iounmap(mmio); + pci_iounmap(pdev, mmio); return rc; } @@ -615,27 +622,28 @@ static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; struct ata_port_info *port_info[2]; - unsigned int combined = 0, n_ports = 1; + unsigned int combined = 0; unsigned int pata_chan = 0, sata_chan = 0; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, + "version " DRV_VERSION "\n"); /* no hotplugging support (FIXME) */ if (!in_module_init) return -ENODEV; port_info[0] = &piix_port_info[ent->driver_data]; - port_info[1] = NULL; + port_info[1] = &piix_port_info[ent->driver_data]; if (port_info[0]->host_flags & PIIX_FLAG_AHCI) { - u8 tmp; - pci_read_config_byte(pdev, PIIX_SCC, &tmp); - if (tmp == PIIX_AHCI_DEVICE) { - int rc = piix_disable_ahci(pdev); - if (rc) - return rc; - } + u8 tmp; + pci_read_config_byte(pdev, PIIX_SCC, &tmp); + if (tmp == PIIX_AHCI_DEVICE) { + int rc = piix_disable_ahci(pdev); + if (rc) + return rc; + } } if (port_info[0]->host_flags & PIIX_FLAG_COMBINED) { @@ -658,18 +666,19 @@ static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) * message-signalled interrupts currently). */ if (port_info[0]->host_flags & PIIX_FLAG_CHECKINTR) - pci_enable_intx(pdev); + pci_intx(pdev, 1); if (combined) { port_info[sata_chan] = &piix_port_info[ent->driver_data]; port_info[sata_chan]->host_flags |= ATA_FLAG_SLAVE_POSS; port_info[pata_chan] = &piix_port_info[ich5_pata]; - n_ports++; - printk(KERN_WARNING DRV_NAME ": combined mode detected\n"); + dev_printk(KERN_WARNING, &pdev->dev, + "combined mode detected (p=%u, s=%u)\n", + pata_chan, sata_chan); } - return ata_pci_init_one(pdev, port_info, n_ports); + return ata_pci_init_one(pdev, port_info, 2); } static int __init piix_init(void) diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c index e6153fe5842a..a8cfbef304b5 100644 --- a/drivers/scsi/atp870u.c +++ b/drivers/scsi/atp870u.c @@ -996,6 +996,7 @@ oktosend: #ifdef ED_DBGP printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id); #endif + dev->id[c][target_id].prdaddr = dev->id[c][target_id].prd_bus; outl(dev->id[c][target_id].prdaddr, tmpcip); tmpcip = tmpcip - 2; outb(0x06, tmpcip); @@ -2572,7 +2573,7 @@ static void atp870u_free_tables(struct Scsi_Host *host) for (k = 0; k < 16; k++) { if (!atp_dev->id[j][k].prd_table) continue; - pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prdaddr); + pci_free_consistent(atp_dev->pdev, 1024, atp_dev->id[j][k].prd_table, atp_dev->id[j][k].prd_bus); atp_dev->id[j][k].prd_table = NULL; } } @@ -2584,12 +2585,13 @@ static int atp870u_init_tables(struct Scsi_Host *host) int c,k; for(c=0;c < 2;c++) { for(k=0;k<16;k++) { - atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prdaddr)); + atp_dev->id[c][k].prd_table = pci_alloc_consistent(atp_dev->pdev, 1024, &(atp_dev->id[c][k].prd_bus)); if (!atp_dev->id[c][k].prd_table) { printk("atp870u_init_tables fail\n"); atp870u_free_tables(host); return -ENOMEM; } + atp_dev->id[c][k].prdaddr = atp_dev->id[c][k].prd_bus; atp_dev->id[c][k].devsp=0x20; atp_dev->id[c][k].devtype = 0x7f; atp_dev->id[c][k].curr_req = NULL; diff --git a/drivers/scsi/atp870u.h b/drivers/scsi/atp870u.h index 89f43af39cf2..62bae64a01c1 100644 --- a/drivers/scsi/atp870u.h +++ b/drivers/scsi/atp870u.h @@ -54,8 +54,9 @@ struct atp_unit unsigned long tran_len; unsigned long last_len; unsigned char *prd_pos; - unsigned char *prd_table; - dma_addr_t prdaddr; + unsigned char *prd_table; /* Kernel address of PRD table */ + dma_addr_t prd_bus; /* Bus address of PRD */ + dma_addr_t prdaddr; /* Dynamically updated in driver */ struct scsi_cmnd *curr_req; } id[2][16]; struct Scsi_Host *host; diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 3900e28ac7d6..540147cb51ce 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/blkdev.h> #include <linux/completion.h> -#include <linux/devfs_fs_kernel.h> #include <linux/ioctl32.h> #include <linux/compat.h> #include <linux/chio.h> /* here are all the ioctls */ @@ -31,7 +30,7 @@ #include <scsi/scsi_ioctl.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> -#include <scsi/scsi_request.h> +#include <scsi/scsi_eh.h> #include <scsi/scsi_dbg.h> #define CH_DT_MAX 16 @@ -117,7 +116,7 @@ typedef struct { } scsi_changer; static LIST_HEAD(ch_devlist); -static spinlock_t ch_devlist_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(ch_devlist_lock); static int ch_devcount; static struct scsi_driver ch_template = @@ -181,17 +180,17 @@ static struct { /* ------------------------------------------------------------------- */ -static int ch_find_errno(unsigned char *sense_buffer) +static int ch_find_errno(struct scsi_sense_hdr *sshdr) { int i,errno = 0; /* Check to see if additional sense information is available */ - if (sense_buffer[7] > 5 && - sense_buffer[12] != 0) { + if (scsi_sense_valid(sshdr) && + sshdr->asc != 0) { for (i = 0; err[i].errno != 0; i++) { - if (err[i].sense == sense_buffer[ 2] && - err[i].asc == sense_buffer[12] && - err[i].ascq == sense_buffer[13]) { + if (err[i].sense == sshdr->sense_key && + err[i].asc == sshdr->asc && + err[i].ascq == sshdr->ascq) { errno = -err[i].errno; break; } @@ -207,13 +206,9 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, void *buffer, unsigned buflength, enum dma_data_direction direction) { - int errno, retries = 0, timeout; - struct scsi_request *sr; + int errno, retries = 0, timeout, result; + struct scsi_sense_hdr sshdr; - sr = scsi_allocate_request(ch->device, GFP_KERNEL); - if (NULL == sr) - return -ENOMEM; - timeout = (cmd[0] == INITIALIZE_ELEMENT_STATUS) ? timeout_init : timeout_move; @@ -224,16 +219,17 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, __scsi_print_command(cmd); } - scsi_wait_req(sr, cmd, buffer, buflength, - timeout * HZ, MAX_RETRIES); + result = scsi_execute_req(ch->device, cmd, direction, buffer, + buflength, &sshdr, timeout * HZ, + MAX_RETRIES); - dprintk("result: 0x%x\n",sr->sr_result); - if (driver_byte(sr->sr_result) & DRIVER_SENSE) { + dprintk("result: 0x%x\n",result); + if (driver_byte(result) & DRIVER_SENSE) { if (debug) - scsi_print_req_sense(ch->name, sr); - errno = ch_find_errno(sr->sr_sense_buffer); + scsi_print_sense_hdr(ch->name, &sshdr); + errno = ch_find_errno(&sshdr); - switch(sr->sr_sense_buffer[2] & 0xf) { + switch(sshdr.sense_key) { case UNIT_ATTENTION: ch->unit_attention = 1; if (retries++ < 3) @@ -241,7 +237,6 @@ ch_do_scsi(scsi_changer *ch, unsigned char *cmd, break; } } - scsi_release_request(sr); return errno; } @@ -565,7 +560,7 @@ ch_set_voltag(scsi_changer *ch, u_int elem, return result; } -static int ch_gstatus(scsi_changer *ch, int type, unsigned char *dest) +static int ch_gstatus(scsi_changer *ch, int type, unsigned char __user *dest) { int retval = 0; u_char data[16]; @@ -639,6 +634,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, { scsi_changer *ch = file->private_data; int retval; + void __user *argp = (void __user *)arg; switch (cmd) { case CHIOGPARAMS: @@ -651,7 +647,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, params.cp_nportals = ch->counts[CHET_IE]; params.cp_ndrives = ch->counts[CHET_DT]; - if (copy_to_user((void *) arg, ¶ms, sizeof(params))) + if (copy_to_user(argp, ¶ms, sizeof(params))) return -EFAULT; return 0; } @@ -676,7 +672,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, vparams.cvp_n4 = ch->counts[CHET_V4]; strncpy(vparams.cvp_label4,vendor_labels[3],16); } - if (copy_to_user((void *) arg, &vparams, sizeof(vparams))) + if (copy_to_user(argp, &vparams, sizeof(vparams))) return -EFAULT; return 0; } @@ -685,7 +681,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, { struct changer_position pos; - if (copy_from_user(&pos, (void*)arg, sizeof (pos))) + if (copy_from_user(&pos, argp, sizeof (pos))) return -EFAULT; if (0 != ch_checkrange(ch, pos.cp_type, pos.cp_unit)) { @@ -704,7 +700,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, { struct changer_move mv; - if (copy_from_user(&mv, (void*)arg, sizeof (mv))) + if (copy_from_user(&mv, argp, sizeof (mv))) return -EFAULT; if (0 != ch_checkrange(ch, mv.cm_fromtype, mv.cm_fromunit) || @@ -726,7 +722,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, { struct changer_exchange mv; - if (copy_from_user(&mv, (void*)arg, sizeof (mv))) + if (copy_from_user(&mv, argp, sizeof (mv))) return -EFAULT; if (0 != ch_checkrange(ch, mv.ce_srctype, mv.ce_srcunit ) || @@ -751,7 +747,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, { struct changer_element_status ces; - if (copy_from_user(&ces, (void*)arg, sizeof (ces))) + if (copy_from_user(&ces, argp, sizeof (ces))) return -EFAULT; if (ces.ces_type < 0 || ces.ces_type >= CH_TYPES) return -EINVAL; @@ -767,7 +763,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, unsigned int elem; int result,i; - if (copy_from_user(&cge, (void*)arg, sizeof (cge))) + if (copy_from_user(&cge, argp, sizeof (cge))) return -EFAULT; if (0 != ch_checkrange(ch, cge.cge_type, cge.cge_unit)) @@ -830,7 +826,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, kfree(buffer); up(&ch->lock); - if (copy_to_user((void*)arg, &cge, sizeof (cge))) + if (copy_to_user(argp, &cge, sizeof (cge))) return -EFAULT; return result; } @@ -848,7 +844,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, struct changer_set_voltag csv; int elem; - if (copy_from_user(&csv, (void*)arg, sizeof(csv))) + if (copy_from_user(&csv, argp, sizeof(csv))) return -EFAULT; if (0 != ch_checkrange(ch, csv.csv_type, csv.csv_unit)) { @@ -866,7 +862,7 @@ static int ch_ioctl(struct inode * inode, struct file * file, } default: - return scsi_ioctl(ch->device, cmd, (void*)arg); + return scsi_ioctl(ch->device, cmd, argp); } } @@ -899,9 +895,9 @@ static long ch_ioctl_compat(struct file * file, case CHIOGSTATUS32: { struct changer_element_status32 ces32; - unsigned char *data; + unsigned char __user *data; - if (copy_from_user(&ces32, (void*)arg, sizeof (ces32))) + if (copy_from_user(&ces32, (void __user *)arg, sizeof (ces32))) return -EFAULT; if (ces32.ces_type < 0 || ces32.ces_type >= CH_TYPES) return -EINVAL; @@ -940,9 +936,7 @@ static int ch_probe(struct device *dev) if (init) ch_init_elem(ch); - devfs_mk_cdev(MKDEV(SCSI_CHANGER_MAJOR,ch->minor), - S_IFCHR | S_IRUGO | S_IWUGO, ch->name); - class_device_create(ch_sysfs_class, + class_device_create(ch_sysfs_class, NULL, MKDEV(SCSI_CHANGER_MAJOR,ch->minor), dev, "s%s", ch->name); @@ -974,7 +968,6 @@ static int ch_remove(struct device *dev) class_device_destroy(ch_sysfs_class, MKDEV(SCSI_CHANGER_MAJOR,ch->minor)); - devfs_remove(ch->name); kfree(ch->dt); kfree(ch); ch_devcount--; diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c index ec161733a82b..f6be2c1c3942 100644 --- a/drivers/scsi/constants.c +++ b/drivers/scsi/constants.c @@ -17,6 +17,7 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_request.h> #include <scsi/scsi_eh.h> +#include <scsi/scsi_dbg.h> @@ -1155,6 +1156,31 @@ scsi_show_extd_sense(unsigned char asc, unsigned char ascq) } } +void +scsi_print_sense_hdr(const char *name, struct scsi_sense_hdr *sshdr) +{ + const char *sense_txt; + /* An example of deferred is when an earlier write to disk cache + * succeeded, but now the disk discovers that it cannot write the + * data to the magnetic media. + */ + const char *error = scsi_sense_is_deferred(sshdr) ? + "<<DEFERRED>>" : "Current"; + printk(KERN_INFO "%s: %s", name, error); + if (sshdr->response_code >= 0x72) + printk(" [descriptor]"); + + sense_txt = scsi_sense_key_string(sshdr->sense_key); + if (sense_txt) + printk(": sense key: %s\n", sense_txt); + else + printk(": sense key=0x%x\n", sshdr->sense_key); + printk(KERN_INFO " "); + scsi_show_extd_sense(sshdr->asc, sshdr->ascq); + printk("\n"); +} +EXPORT_SYMBOL(scsi_print_sense_hdr); + /* Print sense information */ void __scsi_print_sense(const char *name, const unsigned char *sense_buffer, @@ -1162,8 +1188,6 @@ __scsi_print_sense(const char *name, const unsigned char *sense_buffer, { int k, num, res; unsigned int info; - const char *error; - const char *sense_txt; struct scsi_sense_hdr ssh; res = scsi_normalize_sense(sense_buffer, sense_len, &ssh); @@ -1181,26 +1205,7 @@ __scsi_print_sense(const char *name, const unsigned char *sense_buffer, printk("\n"); return; } - - /* An example of deferred is when an earlier write to disk cache - * succeeded, but now the disk discovers that it cannot write the - * data to the magnetic media. - */ - error = scsi_sense_is_deferred(&ssh) ? - "<<DEFERRED>>" : "Current"; - printk(KERN_INFO "%s: %s", name, error); - if (ssh.response_code >= 0x72) - printk(" [descriptor]"); - - sense_txt = scsi_sense_key_string(ssh.sense_key); - if (sense_txt) - printk(": sense key: %s\n", sense_txt); - else - printk(": sense key=0x%x\n", ssh.sense_key); - printk(KERN_INFO " "); - scsi_show_extd_sense(ssh.asc, ssh.ascq); - printk("\n"); - + scsi_print_sense_hdr(name, &ssh); if (ssh.response_code < 0x72) { /* only decode extras for "fixed" format now */ char buff[80]; diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c index d72be0ce89c8..3fda8d455c5b 100644 --- a/drivers/scsi/cpqfcTSinit.c +++ b/drivers/scsi/cpqfcTSinit.c @@ -691,8 +691,7 @@ int cpqfcTS_ioctl( struct scsi_device *ScsiDev, int Cmnd, void *arg) if( copy_to_user( vendor_cmd->bufp, buf, vendor_cmd->len)) result = -EFAULT; - if( buf) - kfree( buf); + kfree(buf); return result; } diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c index 929170dcd3cb..600ba1202864 100644 --- a/drivers/scsi/dc395x.c +++ b/drivers/scsi/dc395x.c @@ -183,7 +183,7 @@ * cross a page boundy. */ #define SEGMENTX_LEN (sizeof(struct SGentry)*DC395x_MAX_SG_LISTENTRY) -#define VIRTX_LEN (sizeof(void *) * DC395x_MAX_SG_LISTENTRY) + struct SGentry { u32 address; /* bus! address */ @@ -235,7 +235,6 @@ struct ScsiReqBlk { u8 sg_count; /* No of HW sg entries for this request */ u8 sg_index; /* Index of HW sg entry for this request */ u32 total_xfer_length; /* Total number of bytes remaining to be transfered */ - void **virt_map; unsigned char *virt_addr; /* Virtual address of current transfer position */ /* @@ -1022,14 +1021,14 @@ static void build_srb(struct scsi_cmnd *cmd, struct DeviceCtlBlk *dcb, reqlen, cmd->request_buffer, cmd->use_sg, srb->sg_count); + srb->virt_addr = page_address(sl->page); for (i = 0; i < srb->sg_count; i++) { - u32 seglen = (u32)sg_dma_len(sl + i); - sgp[i].address = (u32)sg_dma_address(sl + i); + u32 busaddr = (u32)sg_dma_address(&sl[i]); + u32 seglen = (u32)sl[i].length; + sgp[i].address = busaddr; sgp[i].length = seglen; srb->total_xfer_length += seglen; - srb->virt_map[i] = kmap(sl[i].page); } - srb->virt_addr = srb->virt_map[0]; sgp += srb->sg_count - 1; /* @@ -1976,7 +1975,6 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left) int segment = cmd->use_sg; u32 xferred = srb->total_xfer_length - left; /* bytes transfered */ struct SGentry *psge = srb->segment_x + srb->sg_index; - void **virt = srb->virt_map; dprintkdbg(DBG_0, "sg_update_list: Transfered %i of %i bytes, %i remain\n", @@ -2016,16 +2014,16 @@ static void sg_update_list(struct ScsiReqBlk *srb, u32 left) /* We have to walk the scatterlist to find it */ sg = (struct scatterlist *)cmd->request_buffer; - idx = 0; while (segment--) { unsigned long mask = ~((unsigned long)sg->length - 1) & PAGE_MASK; if ((sg_dma_address(sg) & mask) == (psge->address & mask)) { - srb->virt_addr = virt[idx] + (psge->address & ~PAGE_MASK); + srb->virt_addr = (page_address(sg->page) + + psge->address - + (psge->address & PAGE_MASK)); return; } ++sg; - ++idx; } dprintkl(KERN_ERR, "sg_update_list: sg_to_virt failed\n"); @@ -2151,7 +2149,7 @@ static void data_out_phase0(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb, DC395x_read32(acb, TRM_S1040_DMA_CXCNT)); } /* - * calculate all the residue data that not yet transfered + * calculate all the residue data that not yet tranfered * SCSI transfer counter + left in SCSI FIFO data * * .....TRM_S1040_SCSI_COUNTER (24bits) @@ -3269,7 +3267,6 @@ static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb) struct scsi_cmnd *cmd = srb->cmd; enum dma_data_direction dir = cmd->sc_data_direction; if (cmd->use_sg && dir != PCI_DMA_NONE) { - int i; /* unmap DC395x SG list */ dprintkdbg(DBG_SG, "pci_unmap_srb: list=%08x(%05x)\n", srb->sg_bus_addr, SEGMENTX_LEN); @@ -3279,8 +3276,6 @@ static void pci_unmap_srb(struct AdapterCtlBlk *acb, struct ScsiReqBlk *srb) dprintkdbg(DBG_SG, "pci_unmap_srb: segs=%i buffer=%p\n", cmd->use_sg, cmd->request_buffer); /* unmap the sg segments */ - for (i = 0; i < srb->sg_count; i++) - kunmap(virt_to_page(srb->virt_map[i])); pci_unmap_sg(acb->dev, (struct scatterlist *)cmd->request_buffer, cmd->use_sg, dir); @@ -3327,7 +3322,7 @@ static void srb_done(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb, if (cmd->use_sg) { struct scatterlist* sg = (struct scatterlist *)cmd->request_buffer; - ptr = (struct ScsiInqData *)(srb->virt_map[0] + sg->offset); + ptr = (struct ScsiInqData *)(page_address(sg->page) + sg->offset); } else { ptr = (struct ScsiInqData *)(cmd->request_buffer); } @@ -4262,9 +4257,8 @@ static void adapter_sg_tables_free(struct AdapterCtlBlk *acb) const unsigned srbs_per_page = PAGE_SIZE/SEGMENTX_LEN; for (i = 0; i < DC395x_MAX_SRB_CNT; i += srbs_per_page) - kfree(acb->srb_array[i].segment_x); - - vfree(acb->srb_array[0].virt_map); + if (acb->srb_array[i].segment_x) + kfree(acb->srb_array[i].segment_x); } @@ -4280,12 +4274,9 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb) int srb_idx = 0; unsigned i = 0; struct SGentry *ptr; - void **virt_array; - for (i = 0; i < DC395x_MAX_SRB_CNT; i++) { + for (i = 0; i < DC395x_MAX_SRB_CNT; i++) acb->srb_array[i].segment_x = NULL; - acb->srb_array[i].virt_map = NULL; - } dprintkdbg(DBG_1, "Allocate %i pages for SG tables\n", pages); while (pages--) { @@ -4306,19 +4297,6 @@ static int __devinit adapter_sg_tables_alloc(struct AdapterCtlBlk *acb) ptr + (i * DC395x_MAX_SG_LISTENTRY); else dprintkl(KERN_DEBUG, "No space for tmsrb SG table reserved?!\n"); - - virt_array = vmalloc((DC395x_MAX_SRB_CNT + 1) * DC395x_MAX_SG_LISTENTRY * sizeof(void*)); - - if (!virt_array) { - adapter_sg_tables_free(acb); - return 1; - } - - for (i = 0; i < DC395x_MAX_SRB_CNT + 1; i++) { - acb->srb_array[i].virt_map = virt_array; - virt_array += DC395x_MAX_SG_LISTENTRY; - } - return 0; } diff --git a/drivers/scsi/dec_esp.c b/drivers/scsi/dec_esp.c index 315f95a0d6c0..4f39890b44ac 100644 --- a/drivers/scsi/dec_esp.c +++ b/drivers/scsi/dec_esp.c @@ -228,7 +228,7 @@ static int dec_esp_detect(Scsi_Host_Template * tpnt) mem_start = get_tc_base_addr(slot); /* Store base addr into esp struct */ - esp->slot = PHYSADDR(mem_start); + esp->slot = CPHYSADDR(mem_start); esp->dregs = 0; esp->eregs = (struct ESP_regs *) (mem_start + DEC_SCSI_SREG); diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index e2370529c632..7235f94f1191 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -907,9 +907,13 @@ static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev raptorFlag = TRUE; } - + if (pci_request_regions(pDev, "dpt_i2o")) { + PERROR("dpti: adpt_config_hba: pci request region failed\n"); + return -EINVAL; + } base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size); if (!base_addr_virt) { + pci_release_regions(pDev); PERROR("dpti: adpt_config_hba: io remap failed\n"); return -EINVAL; } @@ -919,6 +923,7 @@ static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev if (!msg_addr_virt) { PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n"); iounmap(base_addr_virt); + pci_release_regions(pDev); return -EINVAL; } } else { @@ -932,6 +937,7 @@ static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev iounmap(msg_addr_virt); } iounmap(base_addr_virt); + pci_release_regions(pDev); return -ENOMEM; } memset(pHba, 0, sizeof(adpt_hba)); @@ -1027,6 +1033,7 @@ static void adpt_i2o_delete_hba(adpt_hba* pHba) up(&adpt_configuration_lock); iounmap(pHba->base_addr_virt); + pci_release_regions(pHba->pDev); if(pHba->msg_addr_virt != pHba->base_addr_virt){ iounmap(pHba->msg_addr_virt); } diff --git a/drivers/scsi/eata.c b/drivers/scsi/eata.c index c10e45b94b62..3d13fdee4fc2 100644 --- a/drivers/scsi/eata.c +++ b/drivers/scsi/eata.c @@ -1357,7 +1357,7 @@ static int port_detect(unsigned long port_base, unsigned int j, for (i = 0; i < shost->can_queue; i++) { size_t sz = shost->sg_tablesize *sizeof(struct sg_list); - unsigned int gfp_mask = (shost->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC; + gfp_t gfp_mask = (shost->unchecked_isa_dma ? GFP_DMA : 0) | GFP_ATOMIC; ha->cp[i].sglist = kmalloc(sz, gfp_mask); if (!ha->cp[i].sglist) { printk diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c index fa652f8aa643..d59d449a9e4d 100644 --- a/drivers/scsi/fd_mcs.c +++ b/drivers/scsi/fd_mcs.c @@ -1360,3 +1360,5 @@ static Scsi_Host_Template driver_template = { .use_clustering = DISABLE_CLUSTERING, }; #include "scsi_module.c" + +MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 5feb886c3392..71dd1ebbe58f 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -24,11 +24,13 @@ #include <linux/module.h> #include <linux/blkdev.h> #include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/string.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/completion.h> #include <linux/transport_class.h> +#include <linux/platform_device.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> @@ -52,21 +54,100 @@ static struct class shost_class = { }; /** - * scsi_host_cancel - cancel outstanding IO to this host - * @shost: pointer to struct Scsi_Host - * recovery: recovery requested to run. + * scsi_host_set_state - Take the given host through the host + * state model. + * @shost: scsi host to change the state of. + * @state: state to change to. + * + * Returns zero if unsuccessful or an error if the requested + * transition is illegal. **/ -static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) +int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) { - struct scsi_device *sdev; + enum scsi_host_state oldstate = shost->shost_state; + + if (state == oldstate) + return 0; + + switch (state) { + case SHOST_CREATED: + /* There are no legal states that come back to + * created. This is the manually initialised start + * state */ + goto illegal; + + case SHOST_RUNNING: + switch (oldstate) { + case SHOST_CREATED: + case SHOST_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_RECOVERY: + switch (oldstate) { + case SHOST_RUNNING: + break; + default: + goto illegal; + } + break; + + case SHOST_CANCEL: + switch (oldstate) { + case SHOST_CREATED: + case SHOST_RUNNING: + case SHOST_CANCEL_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_DEL: + switch (oldstate) { + case SHOST_CANCEL: + case SHOST_DEL_RECOVERY: + break; + default: + goto illegal; + } + break; - set_bit(SHOST_CANCEL, &shost->shost_state); - shost_for_each_device(sdev, shost) { - scsi_device_cancel(sdev, recovery); + case SHOST_CANCEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL: + case SHOST_RECOVERY: + break; + default: + goto illegal; + } + break; + + case SHOST_DEL_RECOVERY: + switch (oldstate) { + case SHOST_CANCEL_RECOVERY: + break; + default: + goto illegal; + } + break; } - wait_event(shost->host_wait, (!test_bit(SHOST_RECOVERY, - &shost->shost_state))); + shost->shost_state = state; + return 0; + + illegal: + SCSI_LOG_ERROR_RECOVERY(1, + dev_printk(KERN_ERR, &shost->shost_gendev, + "Illegal host state transition" + "%s->%s\n", + scsi_host_state_name(oldstate), + scsi_host_state_name(state))); + return -EINVAL; } +EXPORT_SYMBOL(scsi_host_set_state); /** * scsi_remove_host - remove a scsi host @@ -74,15 +155,29 @@ static void scsi_host_cancel(struct Scsi_Host *shost, int recovery) **/ void scsi_remove_host(struct Scsi_Host *shost) { + unsigned long flags; + down(&shost->scan_mutex); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_CANCEL)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) { + spin_unlock_irqrestore(shost->host_lock, flags); + up(&shost->scan_mutex); + return; + } + spin_unlock_irqrestore(shost->host_lock, flags); + up(&shost->scan_mutex); scsi_forget_host(shost); - scsi_host_cancel(shost, 0); scsi_proc_host_rm(shost); - set_bit(SHOST_DEL, &shost->shost_state); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_DEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL_RECOVERY)); + spin_unlock_irqrestore(shost->host_lock, flags); transport_unregister_device(&shost->shost_gendev); class_device_unregister(&shost->shost_classdev); device_del(&shost->shost_gendev); + scsi_proc_hostdir_rm(shost->hostt); } EXPORT_SYMBOL(scsi_remove_host); @@ -115,7 +210,7 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev) if (error) goto out; - set_bit(SHOST_ADD, &shost->shost_state); + scsi_host_set_state(shost, SHOST_RUNNING); get_device(shost->shost_gendev.parent); error = class_device_add(&shost->shost_classdev); @@ -164,19 +259,11 @@ static void scsi_host_dev_release(struct device *dev) struct Scsi_Host *shost = dev_to_shost(dev); struct device *parent = dev->parent; - if (shost->ehandler) { - DECLARE_COMPLETION(sem); - shost->eh_notify = &sem; - shost->eh_kill = 1; - up(shost->eh_wait); - wait_for_completion(&sem); - shost->eh_notify = NULL; - } - + if (shost->ehandler) + kthread_stop(shost->ehandler); if (shost->work_q) destroy_workqueue(shost->work_q); - scsi_proc_hostdir_rm(shost->hostt); scsi_destroy_command_freelist(shost); kfree(shost->shost_data); @@ -201,8 +288,8 @@ static void scsi_host_dev_release(struct device *dev) struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) { struct Scsi_Host *shost; - int gfp_mask = GFP_KERNEL, rval; - DECLARE_COMPLETION(complete); + gfp_t gfp_mask = GFP_KERNEL; + int rval; if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; @@ -226,6 +313,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) spin_lock_init(&shost->default_lock); scsi_assign_lock(shost, &shost->default_lock); + shost->shost_state = SHOST_CREATED; INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__targets); INIT_LIST_HEAD(&shost->eh_cmd_q); @@ -307,12 +395,12 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) snprintf(shost->shost_classdev.class_id, BUS_ID_SIZE, "host%d", shost->host_no); - shost->eh_notify = &complete; - rval = kernel_thread(scsi_error_handler, shost, 0); - if (rval < 0) + shost->ehandler = kthread_run(scsi_error_handler, shost, + "scsi_eh_%d", shost->host_no); + if (IS_ERR(shost->ehandler)) { + rval = PTR_ERR(shost->ehandler); goto fail_destroy_freelist; - wait_for_completion(&complete); - shost->eh_notify = NULL; + } scsi_proc_hostdir_add(shost->hostt); return shost; @@ -382,7 +470,7 @@ EXPORT_SYMBOL(scsi_host_lookup); **/ struct Scsi_Host *scsi_host_get(struct Scsi_Host *shost) { - if (test_bit(SHOST_DEL, &shost->shost_state) || + if ((shost->shost_state == SHOST_DEL) || !get_device(&shost->shost_gendev)) return NULL; return shost; diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index b5dc35355570..19392f651272 100644 --- a/drivers/scsi/ibmmca.c +++ b/drivers/scsi/ibmmca.c @@ -36,7 +36,6 @@ #include <linux/proc_fs.h> #include <linux/stat.h> #include <linux/mca.h> -#include <linux/string.h> #include <linux/spinlock.h> #include <linux/init.h> #include <linux/mca-legacy.h> @@ -461,6 +460,8 @@ MODULE_PARM(adisplay, "1i"); MODULE_PARM(normal, "1i"); MODULE_PARM(ansi, "1i"); #endif + +MODULE_LICENSE("GPL"); #endif /*counter of concurrent disk read/writes, to turn on/off disk led */ static int disk_rw_in_progress = 0; diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.c b/drivers/scsi/ibmvscsi/ibmvscsi.c index fe09d145542a..822b9fa706f3 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -87,7 +87,7 @@ static int max_channel = 3; static int init_timeout = 5; static int max_requests = 50; -#define IBMVSCSI_VERSION "1.5.6" +#define IBMVSCSI_VERSION "1.5.7" MODULE_DESCRIPTION("IBM Virtual SCSI"); MODULE_AUTHOR("Dave Boutcher"); @@ -145,6 +145,8 @@ static int initialize_event_pool(struct event_pool *pool, sizeof(*evt->xfer_iu) * i; evt->xfer_iu = pool->iu_storage + i; evt->hostdata = hostdata; + evt->ext_list = NULL; + evt->ext_list_token = 0; } return 0; @@ -161,9 +163,16 @@ static void release_event_pool(struct event_pool *pool, struct ibmvscsi_host_data *hostdata) { int i, in_use = 0; - for (i = 0; i < pool->size; ++i) + for (i = 0; i < pool->size; ++i) { if (atomic_read(&pool->events[i].free) != 1) ++in_use; + if (pool->events[i].ext_list) { + dma_free_coherent(hostdata->dev, + SG_ALL * sizeof(struct memory_descriptor), + pool->events[i].ext_list, + pool->events[i].ext_list_token); + } + } if (in_use) printk(KERN_WARNING "ibmvscsi: releasing event pool with %d " @@ -286,24 +295,41 @@ static void set_srp_direction(struct scsi_cmnd *cmd, } else { if (cmd->sc_data_direction == DMA_TO_DEVICE) { srp_cmd->data_out_format = SRP_INDIRECT_BUFFER; - srp_cmd->data_out_count = numbuf; + srp_cmd->data_out_count = + numbuf < MAX_INDIRECT_BUFS ? + numbuf: MAX_INDIRECT_BUFS; } else { srp_cmd->data_in_format = SRP_INDIRECT_BUFFER; - srp_cmd->data_in_count = numbuf; + srp_cmd->data_in_count = + numbuf < MAX_INDIRECT_BUFS ? + numbuf: MAX_INDIRECT_BUFS; } } } +static void unmap_sg_list(int num_entries, + struct device *dev, + struct memory_descriptor *md) +{ + int i; + + for (i = 0; i < num_entries; ++i) { + dma_unmap_single(dev, + md[i].virtual_address, + md[i].length, DMA_BIDIRECTIONAL); + } +} + /** * unmap_cmd_data: - Unmap data pointed in srp_cmd based on the format * @cmd: srp_cmd whose additional_data member will be unmapped * @dev: device for which the memory is mapped * */ -static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) +static void unmap_cmd_data(struct srp_cmd *cmd, + struct srp_event_struct *evt_struct, + struct device *dev) { - int i; - if ((cmd->data_out_format == SRP_NO_BUFFER) && (cmd->data_in_format == SRP_NO_BUFFER)) return; @@ -318,15 +344,34 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) (struct indirect_descriptor *)cmd->additional_data; int num_mapped = indirect->head.length / sizeof(indirect->list[0]); - for (i = 0; i < num_mapped; ++i) { - struct memory_descriptor *data = &indirect->list[i]; - dma_unmap_single(dev, - data->virtual_address, - data->length, DMA_BIDIRECTIONAL); + + if (num_mapped <= MAX_INDIRECT_BUFS) { + unmap_sg_list(num_mapped, dev, &indirect->list[0]); + return; } + + unmap_sg_list(num_mapped, dev, evt_struct->ext_list); } } +static int map_sg_list(int num_entries, + struct scatterlist *sg, + struct memory_descriptor *md) +{ + int i; + u64 total_length = 0; + + for (i = 0; i < num_entries; ++i) { + struct memory_descriptor *descr = md + i; + struct scatterlist *sg_entry = &sg[i]; + descr->virtual_address = sg_dma_address(sg_entry); + descr->length = sg_dma_len(sg_entry); + descr->memory_handle = 0; + total_length += sg_dma_len(sg_entry); + } + return total_length; +} + /** * map_sg_data: - Maps dma for a scatterlist and initializes decriptor fields * @cmd: Scsi_Cmnd with the scatterlist @@ -337,10 +382,11 @@ static void unmap_cmd_data(struct srp_cmd *cmd, struct device *dev) * Returns 1 on success. */ static int map_sg_data(struct scsi_cmnd *cmd, + struct srp_event_struct *evt_struct, struct srp_cmd *srp_cmd, struct device *dev) { - int i, sg_mapped; + int sg_mapped; u64 total_length = 0; struct scatterlist *sg = cmd->request_buffer; struct memory_descriptor *data = @@ -363,27 +409,46 @@ static int map_sg_data(struct scsi_cmnd *cmd, return 1; } - if (sg_mapped > MAX_INDIRECT_BUFS) { + if (sg_mapped > SG_ALL) { printk(KERN_ERR "ibmvscsi: More than %d mapped sg entries, got %d\n", - MAX_INDIRECT_BUFS, sg_mapped); + SG_ALL, sg_mapped); return 0; } indirect->head.virtual_address = 0; indirect->head.length = sg_mapped * sizeof(indirect->list[0]); indirect->head.memory_handle = 0; - for (i = 0; i < sg_mapped; ++i) { - struct memory_descriptor *descr = &indirect->list[i]; - struct scatterlist *sg_entry = &sg[i]; - descr->virtual_address = sg_dma_address(sg_entry); - descr->length = sg_dma_len(sg_entry); - descr->memory_handle = 0; - total_length += sg_dma_len(sg_entry); + + if (sg_mapped <= MAX_INDIRECT_BUFS) { + total_length = map_sg_list(sg_mapped, sg, &indirect->list[0]); + indirect->total_length = total_length; + return 1; } - indirect->total_length = total_length; - return 1; + /* get indirect table */ + if (!evt_struct->ext_list) { + evt_struct->ext_list =(struct memory_descriptor*) + dma_alloc_coherent(dev, + SG_ALL * sizeof(struct memory_descriptor), + &evt_struct->ext_list_token, 0); + if (!evt_struct->ext_list) { + printk(KERN_ERR + "ibmvscsi: Can't allocate memory for indirect table\n"); + return 0; + + } + } + + total_length = map_sg_list(sg_mapped, sg, evt_struct->ext_list); + + indirect->total_length = total_length; + indirect->head.virtual_address = evt_struct->ext_list_token; + indirect->head.length = sg_mapped * sizeof(indirect->list[0]); + memcpy(indirect->list, evt_struct->ext_list, + MAX_INDIRECT_BUFS * sizeof(struct memory_descriptor)); + + return 1; } /** @@ -428,6 +493,7 @@ static int map_single_data(struct scsi_cmnd *cmd, * Returns 1 on success. */ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, + struct srp_event_struct *evt_struct, struct srp_cmd *srp_cmd, struct device *dev) { switch (cmd->sc_data_direction) { @@ -450,7 +516,7 @@ static int map_data_for_srp_cmd(struct scsi_cmnd *cmd, if (!cmd->request_buffer) return 1; if (cmd->use_sg) - return map_sg_data(cmd, srp_cmd, dev); + return map_sg_data(cmd, evt_struct, srp_cmd, dev); return map_single_data(cmd, srp_cmd, dev); } @@ -486,6 +552,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, printk(KERN_WARNING "ibmvscsi: Warning, request_limit exceeded\n"); unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct, hostdata->dev); free_event_struct(&hostdata->pool, evt_struct); return SCSI_MLQUEUE_HOST_BUSY; @@ -513,7 +580,7 @@ static int ibmvscsi_send_srp_event(struct srp_event_struct *evt_struct, return 0; send_error: - unmap_cmd_data(&evt_struct->iu.srp.cmd, hostdata->dev); + unmap_cmd_data(&evt_struct->iu.srp.cmd, evt_struct, hostdata->dev); if ((cmnd = evt_struct->cmnd) != NULL) { cmnd->result = DID_ERROR << 16; @@ -551,6 +618,7 @@ static void handle_cmd_rsp(struct srp_event_struct *evt_struct) rsp->sense_and_response_data, rsp->sense_data_list_length); unmap_cmd_data(&evt_struct->iu.srp.cmd, + evt_struct, evt_struct->hostdata->dev); if (rsp->doover) @@ -583,6 +651,7 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, { struct srp_cmd *srp_cmd; struct srp_event_struct *evt_struct; + struct indirect_descriptor *indirect; struct ibmvscsi_host_data *hostdata = (struct ibmvscsi_host_data *)&cmnd->device->host->hostdata; u16 lun = lun_from_dev(cmnd->device); @@ -591,14 +660,6 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, if (!evt_struct) return SCSI_MLQUEUE_HOST_BUSY; - init_event_struct(evt_struct, - handle_cmd_rsp, - VIOSRP_SRP_FORMAT, - cmnd->timeout); - - evt_struct->cmnd = cmnd; - evt_struct->cmnd_done = done; - /* Set up the actual SRP IU */ srp_cmd = &evt_struct->iu.srp.cmd; memset(srp_cmd, 0x00, sizeof(*srp_cmd)); @@ -606,17 +667,25 @@ static int ibmvscsi_queuecommand(struct scsi_cmnd *cmnd, memcpy(srp_cmd->cdb, cmnd->cmnd, sizeof(cmnd->cmnd)); srp_cmd->lun = ((u64) lun) << 48; - if (!map_data_for_srp_cmd(cmnd, srp_cmd, hostdata->dev)) { + if (!map_data_for_srp_cmd(cmnd, evt_struct, srp_cmd, hostdata->dev)) { printk(KERN_ERR "ibmvscsi: couldn't convert cmd to srp_cmd\n"); free_event_struct(&hostdata->pool, evt_struct); return SCSI_MLQUEUE_HOST_BUSY; } + init_event_struct(evt_struct, + handle_cmd_rsp, + VIOSRP_SRP_FORMAT, + cmnd->timeout_per_command/HZ); + + evt_struct->cmnd = cmnd; + evt_struct->cmnd_done = done; + /* Fix up dma address of the buffer itself */ - if ((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) || - (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) { - struct indirect_descriptor *indirect = - (struct indirect_descriptor *)srp_cmd->additional_data; + indirect = (struct indirect_descriptor *)srp_cmd->additional_data; + if (((srp_cmd->data_out_format == SRP_INDIRECT_BUFFER) || + (srp_cmd->data_in_format == SRP_INDIRECT_BUFFER)) && + (indirect->head.virtual_address == 0)) { indirect->head.virtual_address = evt_struct->crq.IU_data_ptr + offsetof(struct srp_cmd, additional_data) + offsetof(struct indirect_descriptor, list); @@ -658,6 +727,16 @@ static void adapter_info_rsp(struct srp_event_struct *evt_struct) if (hostdata->madapter_info.port_max_txu[0]) hostdata->host->max_sectors = hostdata->madapter_info.port_max_txu[0] >> 9; + + if (hostdata->madapter_info.os_type == 3 && + strcmp(hostdata->madapter_info.srp_version, "1.6a") <= 0) { + printk("ibmvscsi: host (Ver. %s) doesn't support large" + "transfers\n", + hostdata->madapter_info.srp_version); + printk("ibmvscsi: limiting scatterlists to %d\n", + MAX_INDIRECT_BUFS); + hostdata->host->sg_tablesize = MAX_INDIRECT_BUFS; + } } } @@ -826,11 +905,13 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) struct srp_event_struct *tmp_evt, *found_evt; union viosrp_iu srp_rsp; int rsp_rc; + unsigned long flags; u16 lun = lun_from_dev(cmd->device); /* First, find this command in our sent list so we can figure * out the correct tag */ + spin_lock_irqsave(hostdata->host->host_lock, flags); found_evt = NULL; list_for_each_entry(tmp_evt, &hostdata->sent, list) { if (tmp_evt->cmnd == cmd) { @@ -839,11 +920,14 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) } } - if (!found_evt) + if (!found_evt) { + spin_unlock_irqrestore(hostdata->host->host_lock, flags); return FAILED; + } evt = get_event_struct(&hostdata->pool); if (evt == NULL) { + spin_unlock_irqrestore(hostdata->host->host_lock, flags); printk(KERN_ERR "ibmvscsi: failed to allocate abort event\n"); return FAILED; } @@ -867,7 +951,9 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) evt->sync_srp = &srp_rsp; init_completion(&evt->comp); - if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + rsp_rc = ibmvscsi_send_srp_event(evt, hostdata); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + if (rsp_rc != 0) { printk(KERN_ERR "ibmvscsi: failed to send abort() event\n"); return FAILED; } @@ -901,6 +987,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) * The event is no longer in our list. Make sure it didn't * complete while we were aborting */ + spin_lock_irqsave(hostdata->host->host_lock, flags); found_evt = NULL; list_for_each_entry(tmp_evt, &hostdata->sent, list) { if (tmp_evt->cmnd == cmd) { @@ -910,6 +997,7 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) } if (found_evt == NULL) { + spin_unlock_irqrestore(hostdata->host->host_lock, flags); printk(KERN_INFO "ibmvscsi: aborted task tag 0x%lx completed\n", tsk_mgmt->managed_task_tag); @@ -922,8 +1010,10 @@ static int ibmvscsi_eh_abort_handler(struct scsi_cmnd *cmd) cmd->result = (DID_ABORT << 16); list_del(&found_evt->list); - unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt->hostdata->dev); + unmap_cmd_data(&found_evt->iu.srp.cmd, found_evt, + found_evt->hostdata->dev); free_event_struct(&found_evt->hostdata->pool, found_evt); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); atomic_inc(&hostdata->request_limit); return SUCCESS; } @@ -943,10 +1033,13 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) struct srp_event_struct *tmp_evt, *pos; union viosrp_iu srp_rsp; int rsp_rc; + unsigned long flags; u16 lun = lun_from_dev(cmd->device); + spin_lock_irqsave(hostdata->host->host_lock, flags); evt = get_event_struct(&hostdata->pool); if (evt == NULL) { + spin_unlock_irqrestore(hostdata->host->host_lock, flags); printk(KERN_ERR "ibmvscsi: failed to allocate reset event\n"); return FAILED; } @@ -969,7 +1062,9 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) evt->sync_srp = &srp_rsp; init_completion(&evt->comp); - if (ibmvscsi_send_srp_event(evt, hostdata) != 0) { + rsp_rc = ibmvscsi_send_srp_event(evt, hostdata); + spin_unlock_irqrestore(hostdata->host->host_lock, flags); + if (rsp_rc != 0) { printk(KERN_ERR "ibmvscsi: failed to send reset event\n"); return FAILED; } @@ -1002,12 +1097,14 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) /* We need to find all commands for this LUN that have not yet been * responded to, and fail them with DID_RESET */ + spin_lock_irqsave(hostdata->host->host_lock, flags); list_for_each_entry_safe(tmp_evt, pos, &hostdata->sent, list) { if ((tmp_evt->cmnd) && (tmp_evt->cmnd->device == cmd->device)) { if (tmp_evt->cmnd) tmp_evt->cmnd->result = (DID_RESET << 16); list_del(&tmp_evt->list); - unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt->hostdata->dev); + unmap_cmd_data(&tmp_evt->iu.srp.cmd, tmp_evt, + tmp_evt->hostdata->dev); free_event_struct(&tmp_evt->hostdata->pool, tmp_evt); atomic_inc(&hostdata->request_limit); @@ -1017,6 +1114,7 @@ static int ibmvscsi_eh_device_reset_handler(struct scsi_cmnd *cmd) tmp_evt->done(tmp_evt); } } + spin_unlock_irqrestore(hostdata->host->host_lock, flags); return SUCCESS; } @@ -1035,6 +1133,7 @@ static void purge_requests(struct ibmvscsi_host_data *hostdata) if (tmp_evt->cmnd) { tmp_evt->cmnd->result = (DID_ERROR << 16); unmap_cmd_data(&tmp_evt->iu.srp.cmd, + tmp_evt, tmp_evt->hostdata->dev); if (tmp_evt->cmnd_done) tmp_evt->cmnd_done(tmp_evt->cmnd); @@ -1339,7 +1438,7 @@ static struct scsi_host_template driver_template = { .cmd_per_lun = 16, .can_queue = 1, /* Updated after SRP_LOGIN */ .this_id = -1, - .sg_tablesize = MAX_INDIRECT_BUFS, + .sg_tablesize = SG_ALL, .use_clustering = ENABLE_CLUSTERING, .shost_attrs = ibmvscsi_attrs, }; @@ -1442,15 +1541,18 @@ static int ibmvscsi_remove(struct vio_dev *vdev) */ static struct vio_device_id ibmvscsi_device_table[] __devinitdata = { {"vscsi", "IBM,v-scsi"}, - {0,} + { "", "" } }; - MODULE_DEVICE_TABLE(vio, ibmvscsi_device_table); + static struct vio_driver ibmvscsi_driver = { - .name = "ibmvscsi", .id_table = ibmvscsi_device_table, .probe = ibmvscsi_probe, - .remove = ibmvscsi_remove + .remove = ibmvscsi_remove, + .driver = { + .name = "ibmvscsi", + .owner = THIS_MODULE, + } }; int __init ibmvscsi_module_init(void) diff --git a/drivers/scsi/ibmvscsi/ibmvscsi.h b/drivers/scsi/ibmvscsi/ibmvscsi.h index 1030b703c30e..8bec0438dc8a 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.h +++ b/drivers/scsi/ibmvscsi/ibmvscsi.h @@ -68,6 +68,8 @@ struct srp_event_struct { void (*cmnd_done) (struct scsi_cmnd *); struct completion comp; union viosrp_iu *sync_srp; + struct memory_descriptor *ext_list; + dma_addr_t ext_list_token; }; /* a pool of event structs for use */ diff --git a/drivers/scsi/ibmvscsi/iseries_vscsi.c b/drivers/scsi/ibmvscsi/iseries_vscsi.c index e9202f2a8276..1045872b0175 100644 --- a/drivers/scsi/ibmvscsi/iseries_vscsi.c +++ b/drivers/scsi/ibmvscsi/iseries_vscsi.c @@ -28,10 +28,10 @@ * hypervisor system or a converged hypervisor system. */ -#include <asm/iSeries/vio.h> -#include <asm/iSeries/HvLpEvent.h> -#include <asm/iSeries/HvTypes.h> -#include <asm/iSeries/HvLpConfig.h> +#include <asm/iseries/vio.h> +#include <asm/iseries/hv_lp_event.h> +#include <asm/iseries/hv_types.h> +#include <asm/iseries/hv_lp_config.h> #include <asm/vio.h> #include <linux/device.h> #include "ibmvscsi.h" diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 035f615817d7..8bf5652f1060 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -28,6 +28,7 @@ */ #include <asm/vio.h> +#include <asm/prom.h> #include <asm/iommu.h> #include <asm/hvcall.h> #include <linux/dma-mapping.h> diff --git a/drivers/scsi/ibmvscsi/srp.h b/drivers/scsi/ibmvscsi/srp.h index 2ae5154fd89c..7d8e4c4accb9 100644 --- a/drivers/scsi/ibmvscsi/srp.h +++ b/drivers/scsi/ibmvscsi/srp.h @@ -35,7 +35,7 @@ enum srp_types { SRP_LOGIN_REQ_TYPE = 0x00, SRP_LOGIN_RSP_TYPE = 0xC0, - SRP_LOGIN_REJ_TYPE = 0x80, + SRP_LOGIN_REJ_TYPE = 0xC2, SRP_I_LOGOUT_TYPE = 0x03, SRP_T_LOGOUT_TYPE = 0x80, SRP_TSK_MGMT_TYPE = 0x01, diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 3d62c9bcbff7..a440ea38efaa 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -184,13 +184,16 @@ static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigne unsigned long flags; local_irq_save(flags); - buf = kmap_atomic(pc->sg->page, KM_IRQ0) + pc->sg->offset; - drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count); + buf = kmap_atomic(pc->sg->page, KM_IRQ0) + + pc->sg->offset; + drive->hwif->atapi_input_bytes(drive, + buf + pc->b_count, count); kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(pc->sg->page) + pc->sg->offset; - drive->hwif->atapi_input_bytes(drive, buf + pc->b_count, count); + drive->hwif->atapi_input_bytes(drive, + buf + pc->b_count, count); } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { @@ -216,13 +219,16 @@ static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsign unsigned long flags; local_irq_save(flags); - buf = kmap_atomic(pc->sg->page, KM_IRQ0) + pc->sg->offset; - drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count); + buf = kmap_atomic(pc->sg->page, KM_IRQ0) + + pc->sg->offset; + drive->hwif->atapi_output_bytes(drive, + buf + pc->b_count, count); kunmap_atomic(buf - pc->sg->offset, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(pc->sg->page) + pc->sg->offset; - drive->hwif->atapi_output_bytes(drive, buf + pc->b_count, count); + drive->hwif->atapi_output_bytes(drive, + buf + pc->b_count, count); } bcount -= count; pc->b_count += count; if (pc->b_count == pc->sg->length) { diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index babd48363402..e0039dfae8e5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -4944,6 +4944,7 @@ static int ipr_reset_restore_cfg_space(struct ipr_cmnd *ipr_cmd) int rc; ENTER; + pci_unblock_user_cfg_access(ioa_cfg->pdev); rc = pci_restore_state(ioa_cfg->pdev); if (rc != PCIBIOS_SUCCESSFUL) { @@ -4998,6 +4999,7 @@ static int ipr_reset_start_bist(struct ipr_cmnd *ipr_cmd) int rc; ENTER; + pci_block_user_cfg_access(ioa_cfg->pdev); rc = pci_write_config_byte(ioa_cfg->pdev, PCI_BIST, PCI_BIST_START); if (rc != PCIBIOS_SUCCESSFUL) { diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c index 6dfcb4fbccdd..4cdd891781b1 100644 --- a/drivers/scsi/ips.c +++ b/drivers/scsi/ips.c @@ -133,10 +133,12 @@ /* 6.10.00 - Remove 1G Addressing Limitations */ /* 6.11.xx - Get VersionInfo buffer off the stack ! DDTS 60401 */ /* 6.11.xx - Make Logical Drive Info structure safe for DMA DDTS 60639 */ -/* 7.10.xx - Add highmem_io flag in SCSI Templete for 2.4 kernels */ +/* 7.10.18 - Add highmem_io flag in SCSI Templete for 2.4 kernels */ /* - Fix path/name for scsi_hosts.h include for 2.6 kernels */ /* - Fix sort order of 7k */ /* - Remove 3 unused "inline" functions */ +/* 7.12.xx - Use STATIC functions whereever possible */ +/* - Clean up deprecated MODULE_PARM calls */ /*****************************************************************************/ /* @@ -207,8 +209,8 @@ module_param(ips, charp, 0); /* * DRIVER_VER */ -#define IPS_VERSION_HIGH "7.10" -#define IPS_VERSION_LOW ".18 " +#define IPS_VERSION_HIGH "7.12" +#define IPS_VERSION_LOW ".02 " #if !defined(__i386__) && !defined(__ia64__) && !defined(__x86_64__) #warning "This driver has only been tested on the x86/ia64/x86_64 platforms" diff --git a/drivers/scsi/ips.h b/drivers/scsi/ips.h index 480e06f4d6ae..505e967013de 100644 --- a/drivers/scsi/ips.h +++ b/drivers/scsi/ips.h @@ -87,15 +87,14 @@ #define scsi_set_pci_device(sh,dev) (0) #endif - #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) - - #ifndef irqreturn_t - typedef void irqreturn_t; - #endif - + #ifndef IRQ_NONE + typedef void irqreturn_t; #define IRQ_NONE #define IRQ_HANDLED #define IRQ_RETVAL(x) + #endif + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) #define IPS_REGISTER_HOSTS(SHT) scsi_register_module(MODULE_SCSI_HA,SHT) #define IPS_UNREGISTER_HOSTS(SHT) scsi_unregister_module(MODULE_SCSI_HA,SHT) #define IPS_ADD_HOST(shost,device) @@ -123,6 +122,10 @@ #ifndef min #define min(x,y) ((x) < (y) ? x : y) #endif + + #ifndef __iomem /* For clean compiles in earlier kernels without __iomem annotations */ + #define __iomem + #endif #define pci_dma_hi32(a) ((a >> 16) >> 16) #define pci_dma_lo32(a) (a & 0xffffffff) @@ -1206,13 +1209,13 @@ typedef struct { #define IPS_VER_MAJOR 7 #define IPS_VER_MAJOR_STRING "7" -#define IPS_VER_MINOR 10 -#define IPS_VER_MINOR_STRING "10" -#define IPS_VER_BUILD 18 -#define IPS_VER_BUILD_STRING "18" -#define IPS_VER_STRING "7.10.18" +#define IPS_VER_MINOR 12 +#define IPS_VER_MINOR_STRING "12" +#define IPS_VER_BUILD 02 +#define IPS_VER_BUILD_STRING "02" +#define IPS_VER_STRING "7.12.02" #define IPS_RELEASE_ID 0x00020000 -#define IPS_BUILD_IDENT 731 +#define IPS_BUILD_IDENT 761 #define IPS_LEGALCOPYRIGHT_STRING "(C) Copyright IBM Corp. 1994, 2002. All Rights Reserved." #define IPS_ADAPTECCOPYRIGHT_STRING "(c) Copyright Adaptec, Inc. 2002 to 2004. All Rights Reserved." #define IPS_DELLCOPYRIGHT_STRING "(c) Copyright Dell 2004. All Rights Reserved." @@ -1223,12 +1226,12 @@ typedef struct { #define IPS_VER_SERVERAID2 "2.88.13" #define IPS_VER_NAVAJO "2.88.13" #define IPS_VER_SERVERAID3 "6.10.24" -#define IPS_VER_SERVERAID4H "7.10.11" -#define IPS_VER_SERVERAID4MLx "7.10.18" -#define IPS_VER_SARASOTA "7.10.18" -#define IPS_VER_MARCO "7.10.18" -#define IPS_VER_SEBRING "7.10.18" -#define IPS_VER_KEYWEST "7.10.18" +#define IPS_VER_SERVERAID4H "7.12.02" +#define IPS_VER_SERVERAID4MLx "7.12.02" +#define IPS_VER_SARASOTA "7.12.02" +#define IPS_VER_MARCO "7.12.02" +#define IPS_VER_SEBRING "7.12.02" +#define IPS_VER_KEYWEST "7.12.02" /* Compatability IDs for various adapters */ #define IPS_COMPAT_UNKNOWN "" diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c index 4cbb6187cc44..459a4daebece 100644 --- a/drivers/scsi/lasi700.c +++ b/drivers/scsi/lasi700.c @@ -98,7 +98,7 @@ MODULE_DEVICE_TABLE(parisc, lasi700_ids); static int __init lasi700_probe(struct parisc_device *dev) { - unsigned long base = dev->hpa + LASI_SCSI_CORE_OFFSET; + unsigned long base = dev->hpa.start + LASI_SCSI_CORE_OFFSET; struct NCR_700_Host_Parameters *hostdata; struct Scsi_Host *host; @@ -125,8 +125,6 @@ lasi700_probe(struct parisc_device *dev) hostdata->dmode_extra = DMODE_FC2; } - NCR_700_set_mem_mapped(hostdata); - host = NCR_700_detect(&lasi700_template, hostdata, &dev->dev); if (!host) goto out_kfree; @@ -168,7 +166,7 @@ lasi700_driver_remove(struct parisc_device *dev) } static struct parisc_driver lasi700_driver = { - .name = "Lasi SCSI", + .name = "lasi_scsi", .id_table = lasi700_ids, .probe = lasi700_probe, .remove = __devexit_p(lasi700_driver_remove), diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 73b1f72b7e43..ff18fa7044c5 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -1,25 +1,35 @@ /* - libata-core.c - helper library for ATA - - Copyright 2003-2004 Red Hat, Inc. All rights reserved. - Copyright 2003-2004 Jeff Garzik - - The contents of this file are subject to the Open - Software License version 1.1 that can be found at - http://www.opensource.org/licenses/osl-1.1.txt and is included herein - by reference. - - Alternatively, the contents of this file may be used under the terms - of the GNU General Public License version 2 (the "GPL") as distributed - in the kernel source COPYING file, in which case the provisions of - the GPL are applicable instead of the above. If you wish to allow - the use of your version of this file only under the terms of the - GPL and not to allow others to use your version of this file under - the OSL, indicate your decision by deleting the provisions above and - replace them with the notice and other provisions required by the GPL. - If you do not delete the provisions above, a recipient may use your - version of this file under either the OSL or the GPL. - + * libata-core.c - helper library for ATA + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003-2004 Red Hat, Inc. All rights reserved. + * Copyright 2003-2004 Jeff Garzik + * + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available from http://www.t13.org/ and + * http://www.sata-io.org/ + * */ #include <linux/config.h> @@ -38,6 +48,8 @@ #include <linux/completion.h> #include <linux/suspend.h> #include <linux/workqueue.h> +#include <linux/jiffies.h> +#include <linux/scatterlist.h> #include <scsi/scsi.h> #include "scsi.h" #include "scsi_priv.h" @@ -52,26 +64,31 @@ static unsigned int ata_busy_sleep (struct ata_port *ap, unsigned long tmout_pat, unsigned long tmout); +static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev); +static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev); static void ata_set_mode(struct ata_port *ap); static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev); -static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift); +static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift); static int fgb(u32 bitmap); -static int ata_choose_xfer_mode(struct ata_port *ap, +static int ata_choose_xfer_mode(const struct ata_port *ap, u8 *xfer_mode_out, unsigned int *xfer_shift_out); -static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat); static void __ata_qc_complete(struct ata_queued_cmd *qc); static unsigned int ata_unique_id = 1; static struct workqueue_struct *ata_wq; +int atapi_enabled = 0; +module_param(atapi_enabled, int, 0444); +MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Library module for ATA devices"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); /** - * ata_tf_load - send taskfile registers to host controller + * ata_tf_load_pio - send taskfile registers to host controller * @ap: Port to which output is sent * @tf: ATA taskfile register set * @@ -81,7 +98,7 @@ MODULE_VERSION(DRV_VERSION); * Inherited from caller. */ -static void ata_tf_load_pio(struct ata_port *ap, struct ata_taskfile *tf) +static void ata_tf_load_pio(struct ata_port *ap, const struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; @@ -139,7 +156,7 @@ static void ata_tf_load_pio(struct ata_port *ap, struct ata_taskfile *tf) * Inherited from caller. */ -static void ata_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void ata_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; @@ -208,7 +225,7 @@ static void ata_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) * LOCKING: * Inherited from caller. */ -void ata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) +void ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) { if (ap->flags & ATA_FLAG_MMIO) ata_tf_load_mmio(ap, tf); @@ -228,7 +245,7 @@ void ata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) * spin_lock_irqsave(host_set lock) */ -static void ata_exec_command_pio(struct ata_port *ap, struct ata_taskfile *tf) +static void ata_exec_command_pio(struct ata_port *ap, const struct ata_taskfile *tf) { DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); @@ -249,7 +266,7 @@ static void ata_exec_command_pio(struct ata_port *ap, struct ata_taskfile *tf) * spin_lock_irqsave(host_set lock) */ -static void ata_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void ata_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); @@ -269,7 +286,7 @@ static void ata_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) * LOCKING: * spin_lock_irqsave(host_set lock) */ -void ata_exec_command(struct ata_port *ap, struct ata_taskfile *tf) +void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) { if (ap->flags & ATA_FLAG_MMIO) ata_exec_command_mmio(ap, tf); @@ -278,28 +295,6 @@ void ata_exec_command(struct ata_port *ap, struct ata_taskfile *tf) } /** - * ata_exec - issue ATA command to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues PIO/MMIO write to ATA command register, with proper - * synchronization with interrupt handler / other threads. - * - * LOCKING: - * Obtains host_set lock. - */ - -static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf) -{ - unsigned long flags; - - DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); - spin_lock_irqsave(&ap->host_set->lock, flags); - ap->ops->exec_command(ap, tf); - spin_unlock_irqrestore(&ap->host_set->lock, flags); -} - -/** * ata_tf_to_host - issue ATA taskfile to host controller * @ap: port to which command is being issued * @tf: ATA taskfile register set @@ -309,30 +304,11 @@ static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf) * other threads. * * LOCKING: - * Obtains host_set lock. - */ - -static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf) -{ - ap->ops->tf_load(ap, tf); - - ata_exec(ap, tf); -} - -/** - * ata_tf_to_host_nolock - issue ATA taskfile to host controller - * @ap: port to which command is being issued - * @tf: ATA taskfile register set - * - * Issues ATA taskfile register set to ATA host controller, - * with proper synchronization with interrupt handler and - * other threads. - * - * LOCKING: * spin_lock_irqsave(host_set lock) */ -void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf) +static inline void ata_tf_to_host(struct ata_port *ap, + const struct ata_taskfile *tf) { ap->ops->tf_load(ap, tf); ap->ops->exec_command(ap, tf); @@ -354,6 +330,8 @@ static void ata_tf_read_pio(struct ata_port *ap, struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; + tf->command = ata_check_status(ap); + tf->feature = inb(ioaddr->error_addr); tf->nsect = inb(ioaddr->nsect_addr); tf->lbal = inb(ioaddr->lbal_addr); tf->lbam = inb(ioaddr->lbam_addr); @@ -386,6 +364,8 @@ static void ata_tf_read_mmio(struct ata_port *ap, struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; + tf->command = ata_check_status(ap); + tf->feature = readb((void __iomem *)ioaddr->error_addr); tf->nsect = readb((void __iomem *)ioaddr->nsect_addr); tf->lbal = readb((void __iomem *)ioaddr->lbal_addr); tf->lbam = readb((void __iomem *)ioaddr->lbam_addr); @@ -506,30 +486,6 @@ u8 ata_altstatus(struct ata_port *ap) /** - * ata_chk_err - Read device error reg - * @ap: port where the device is - * - * Reads ATA taskfile error register for - * currently-selected device and return its value. - * - * Note: may NOT be used as the check_err() entry in - * ata_port_operations. - * - * LOCKING: - * Inherited from caller. - */ -u8 ata_chk_err(struct ata_port *ap) -{ - if (ap->ops->check_err) - return ap->ops->check_err(ap); - - if (ap->flags & ATA_FLAG_MMIO) { - return readb((void __iomem *) ap->ioaddr.error_addr); - } - return inb(ap->ioaddr.error_addr); -} - -/** * ata_tf_to_fis - Convert ATA taskfile to SATA FIS structure * @tf: Taskfile to convert * @fis: Buffer into which data will output @@ -542,7 +498,7 @@ u8 ata_chk_err(struct ata_port *ap) * Inherited from caller. */ -void ata_tf_to_fis(struct ata_taskfile *tf, u8 *fis, u8 pmp) +void ata_tf_to_fis(const struct ata_taskfile *tf, u8 *fis, u8 pmp) { fis[0] = 0x27; /* Register - Host to Device FIS */ fis[1] = (pmp & 0xf) | (1 << 7); /* Port multiplier number, @@ -583,7 +539,7 @@ void ata_tf_to_fis(struct ata_taskfile *tf, u8 *fis, u8 pmp) * Inherited from caller. */ -void ata_tf_from_fis(u8 *fis, struct ata_taskfile *tf) +void ata_tf_from_fis(const u8 *fis, struct ata_taskfile *tf) { tf->command = fis[2]; /* status */ tf->feature = fis[3]; /* error */ @@ -601,79 +557,53 @@ void ata_tf_from_fis(u8 *fis, struct ata_taskfile *tf) tf->hob_nsect = fis[13]; } -/** - * ata_prot_to_cmd - determine which read/write opcodes to use - * @protocol: ATA_PROT_xxx taskfile protocol - * @lba48: true is lba48 is present - * - * Given necessary input, determine which read/write commands - * to use to transfer data. - * - * LOCKING: - * None. - */ -static int ata_prot_to_cmd(int protocol, int lba48) -{ - int rcmd = 0, wcmd = 0; - - switch (protocol) { - case ATA_PROT_PIO: - if (lba48) { - rcmd = ATA_CMD_PIO_READ_EXT; - wcmd = ATA_CMD_PIO_WRITE_EXT; - } else { - rcmd = ATA_CMD_PIO_READ; - wcmd = ATA_CMD_PIO_WRITE; - } - break; - - case ATA_PROT_DMA: - if (lba48) { - rcmd = ATA_CMD_READ_EXT; - wcmd = ATA_CMD_WRITE_EXT; - } else { - rcmd = ATA_CMD_READ; - wcmd = ATA_CMD_WRITE; - } - break; - - default: - return -1; - } - - return rcmd | (wcmd << 8); -} +static const u8 ata_rw_cmds[] = { + /* pio multi */ + ATA_CMD_READ_MULTI, + ATA_CMD_WRITE_MULTI, + ATA_CMD_READ_MULTI_EXT, + ATA_CMD_WRITE_MULTI_EXT, + /* pio */ + ATA_CMD_PIO_READ, + ATA_CMD_PIO_WRITE, + ATA_CMD_PIO_READ_EXT, + ATA_CMD_PIO_WRITE_EXT, + /* dma */ + ATA_CMD_READ, + ATA_CMD_WRITE, + ATA_CMD_READ_EXT, + ATA_CMD_WRITE_EXT +}; /** - * ata_dev_set_protocol - set taskfile protocol and r/w commands - * @dev: device to examine and configure + * ata_rwcmd_protocol - set taskfile r/w commands and protocol + * @qc: command to examine and configure * - * Examine the device configuration, after we have - * read the identify-device page and configured the - * data transfer mode. Set internal state related to - * the ATA taskfile protocol (pio, pio mult, dma, etc.) - * and calculate the proper read/write commands to use. + * Examine the device configuration and tf->flags to calculate + * the proper read/write commands and protocol to use. * * LOCKING: * caller. */ -static void ata_dev_set_protocol(struct ata_device *dev) +void ata_rwcmd_protocol(struct ata_queued_cmd *qc) { - int pio = (dev->flags & ATA_DFLAG_PIO); - int lba48 = (dev->flags & ATA_DFLAG_LBA48); - int proto, cmd; + struct ata_taskfile *tf = &qc->tf; + struct ata_device *dev = qc->dev; - if (pio) - proto = dev->xfer_protocol = ATA_PROT_PIO; - else - proto = dev->xfer_protocol = ATA_PROT_DMA; + int index, lba48, write; + + lba48 = (tf->flags & ATA_TFLAG_LBA48) ? 2 : 0; + write = (tf->flags & ATA_TFLAG_WRITE) ? 1 : 0; - cmd = ata_prot_to_cmd(proto, lba48); - if (cmd < 0) - BUG(); + if (dev->flags & ATA_DFLAG_PIO) { + tf->protocol = ATA_PROT_PIO; + index = dev->multi_count ? 0 : 4; + } else { + tf->protocol = ATA_PROT_DMA; + index = 8; + } - dev->read_cmd = cmd & 0xff; - dev->write_cmd = (cmd >> 8) & 0xff; + tf->command = ata_rw_cmds[index + lba48 + write]; } static const char * xfer_mode_str[] = { @@ -855,7 +785,7 @@ static unsigned int ata_devchk(struct ata_port *ap, * the event of failure. */ -unsigned int ata_dev_classify(struct ata_taskfile *tf) +unsigned int ata_dev_classify(const struct ata_taskfile *tf) { /* Apple's open source Darwin code hints that some devices only * put a proper signature into the LBA mid/high registers, @@ -907,8 +837,8 @@ static u8 ata_dev_try_classify(struct ata_port *ap, unsigned int device) memset(&tf, 0, sizeof(tf)); - err = ata_chk_err(ap); ap->ops->tf_read(ap, &tf); + err = tf.feature; dev->class = ATA_DEV_NONE; @@ -947,7 +877,7 @@ static u8 ata_dev_try_classify(struct ata_port *ap, unsigned int device) * caller. */ -void ata_dev_id_string(u16 *id, unsigned char *s, +void ata_dev_id_string(const u16 *id, unsigned char *s, unsigned int ofs, unsigned int len) { unsigned int c; @@ -1064,7 +994,7 @@ void ata_dev_select(struct ata_port *ap, unsigned int device, * caller. */ -static inline void ata_dump_id(struct ata_device *dev) +static inline void ata_dump_id(const struct ata_device *dev) { DPRINTK("49==0x%04x " "53==0x%04x " @@ -1092,6 +1022,31 @@ static inline void ata_dump_id(struct ata_device *dev) dev->id[93]); } +/* + * Compute the PIO modes available for this device. This is not as + * trivial as it seems if we must consider early devices correctly. + * + * FIXME: pre IDE drive timing (do we care ?). + */ + +static unsigned int ata_pio_modes(const struct ata_device *adev) +{ + u16 modes; + + /* Usual case. Word 53 indicates word 88 is valid */ + if (adev->id[ATA_ID_FIELD_VALID] & (1 << 2)) { + modes = adev->id[ATA_ID_PIO_MODES] & 0x03; + modes <<= 3; + modes |= 0x7; + return modes; + } + + /* If word 88 isn't valid then Word 51 holds the PIO timing number + for the maximum. Turn it into a mask and return it */ + modes = (2 << (adev->id[ATA_ID_OLD_PIO_MODES] & 0xFF)) - 1 ; + return modes; +} + /** * ata_dev_identify - obtain IDENTIFY x DEVICE page * @ap: port on which device we wish to probe resides @@ -1117,10 +1072,9 @@ static inline void ata_dump_id(struct ata_device *dev) static void ata_dev_identify(struct ata_port *ap, unsigned int device) { struct ata_device *dev = &ap->device[device]; - unsigned int i; + unsigned int major_version; u16 tmp; unsigned long xfer_modes; - u8 status; unsigned int using_edd; DECLARE_COMPLETION(wait); struct ata_queued_cmd *qc; @@ -1174,8 +1128,11 @@ retry: else wait_for_completion(&wait); - status = ata_chk_status(ap); - if (status & ATA_ERR) { + spin_lock_irqsave(&ap->host_set->lock, flags); + ap->ops->tf_read(ap, &qc->tf); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + if (qc->tf.command & ATA_ERR) { /* * arg! EDD works for all test cases, but seems to return * the ATA signature for some ATAPI devices. Until the @@ -1188,7 +1145,7 @@ retry: * to have this problem. */ if ((using_edd) && (qc->tf.command == ATA_CMD_ID_ATA)) { - u8 err = ata_chk_err(ap); + u8 err = qc->tf.feature; if (err & ATA_ABORTED) { dev->class = ATA_DEV_ATAPI; qc->cursg = 0; @@ -1215,9 +1172,9 @@ retry: * common ATA, ATAPI feature tests */ - /* we require LBA and DMA support (bits 8 & 9 of word 49) */ - if (!ata_id_has_dma(dev->id) || !ata_id_has_lba(dev->id)) { - printk(KERN_DEBUG "ata%u: no dma/lba\n", ap->id); + /* we require DMA support (bits 8 of word 49) */ + if (!ata_id_has_dma(dev->id)) { + printk(KERN_DEBUG "ata%u: no dma\n", ap->id); goto err_out_nosup; } @@ -1225,10 +1182,8 @@ retry: xfer_modes = dev->id[ATA_ID_UDMA_MODES]; if (!xfer_modes) xfer_modes = (dev->id[ATA_ID_MWDMA_MODES]) << ATA_SHIFT_MWDMA; - if (!xfer_modes) { - xfer_modes = (dev->id[ATA_ID_PIO_MODES]) << (ATA_SHIFT_PIO + 3); - xfer_modes |= (0x7 << ATA_SHIFT_PIO); - } + if (!xfer_modes) + xfer_modes = ata_pio_modes(dev); ata_dump_id(dev); @@ -1237,32 +1192,75 @@ retry: if (!ata_id_is_ata(dev->id)) /* sanity check */ goto err_out_nosup; + /* get major version */ tmp = dev->id[ATA_ID_MAJOR_VER]; - for (i = 14; i >= 1; i--) - if (tmp & (1 << i)) + for (major_version = 14; major_version >= 1; major_version--) + if (tmp & (1 << major_version)) break; - /* we require at least ATA-3 */ - if (i < 3) { - printk(KERN_DEBUG "ata%u: no ATA-3\n", ap->id); - goto err_out_nosup; + /* + * The exact sequence expected by certain pre-ATA4 drives is: + * SRST RESET + * IDENTIFY + * INITIALIZE DEVICE PARAMETERS + * anything else.. + * Some drives were very specific about that exact sequence. + */ + if (major_version < 4 || (!ata_id_has_lba(dev->id))) { + ata_dev_init_params(ap, dev); + + /* current CHS translation info (id[53-58]) might be + * changed. reread the identify device info. + */ + ata_dev_reread_id(ap, dev); } - if (ata_id_has_lba48(dev->id)) { - dev->flags |= ATA_DFLAG_LBA48; - dev->n_sectors = ata_id_u64(dev->id, 100); - } else { - dev->n_sectors = ata_id_u32(dev->id, 60); + if (ata_id_has_lba(dev->id)) { + dev->flags |= ATA_DFLAG_LBA; + + if (ata_id_has_lba48(dev->id)) { + dev->flags |= ATA_DFLAG_LBA48; + dev->n_sectors = ata_id_u64(dev->id, 100); + } else { + dev->n_sectors = ata_id_u32(dev->id, 60); + } + + /* print device info to dmesg */ + printk(KERN_INFO "ata%u: dev %u ATA-%d, max %s, %Lu sectors:%s\n", + ap->id, device, + major_version, + ata_mode_string(xfer_modes), + (unsigned long long)dev->n_sectors, + dev->flags & ATA_DFLAG_LBA48 ? " LBA48" : " LBA"); + } else { + /* CHS */ + + /* Default translation */ + dev->cylinders = dev->id[1]; + dev->heads = dev->id[3]; + dev->sectors = dev->id[6]; + dev->n_sectors = dev->cylinders * dev->heads * dev->sectors; + + if (ata_id_current_chs_valid(dev->id)) { + /* Current CHS translation is valid. */ + dev->cylinders = dev->id[54]; + dev->heads = dev->id[55]; + dev->sectors = dev->id[56]; + + dev->n_sectors = ata_id_u32(dev->id, 57); + } + + /* print device info to dmesg */ + printk(KERN_INFO "ata%u: dev %u ATA-%d, max %s, %Lu sectors: CHS %d/%d/%d\n", + ap->id, device, + major_version, + ata_mode_string(xfer_modes), + (unsigned long long)dev->n_sectors, + (int)dev->cylinders, (int)dev->heads, (int)dev->sectors); + } ap->host->max_cmd_len = 16; - - /* print device info to dmesg */ - printk(KERN_INFO "ata%u: dev %u ATA, max %s, %Lu sectors:%s\n", - ap->id, device, - ata_mode_string(xfer_modes), - (unsigned long long)dev->n_sectors, - dev->flags & ATA_DFLAG_LBA48 ? " lba48" : ""); } /* ATAPI-specific feature tests */ @@ -1296,7 +1294,7 @@ err_out: } -static inline u8 ata_dev_knobble(struct ata_port *ap) +static inline u8 ata_dev_knobble(const struct ata_port *ap) { return ((ap->cbl == ATA_CBL_SATA) && (!ata_id_is_sata(ap->device->id))); } @@ -1304,12 +1302,12 @@ static inline u8 ata_dev_knobble(struct ata_port *ap) /** * ata_dev_config - Run device specific handlers and check for * SATA->PATA bridges - * @ap: Bus + * @ap: Bus * @i: Device * * LOCKING: */ - + void ata_dev_config(struct ata_port *ap, unsigned int i) { /* limit bridge transfers to udma5, 200 sectors */ @@ -1482,7 +1480,153 @@ void ata_port_disable(struct ata_port *ap) ap->flags |= ATA_FLAG_PORT_DISABLED; } -static struct { +/* + * This mode timing computation functionality is ported over from + * drivers/ide/ide-timing.h and was originally written by Vojtech Pavlik + */ +/* + * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). + * These were taken from ATA/ATAPI-6 standard, rev 0a, except + * for PIO 5, which is a nonstandard extension and UDMA6, which + * is currently supported only by Maxtor drives. + */ + +static const struct ata_timing ata_timing[] = { + + { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, + { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, + { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, + { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, + + { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, + { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, + { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, + +/* { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 }, */ + + { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, + { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, + { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, + + { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, + { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, + { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, + +/* { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, */ + { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, + { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, + + { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, + { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, + { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, + +/* { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, */ + + { 0xFF } +}; + +#define ENOUGH(v,unit) (((v)-1)/(unit)+1) +#define EZ(v,unit) ((v)?ENOUGH(v,unit):0) + +static void ata_timing_quantize(const struct ata_timing *t, struct ata_timing *q, int T, int UT) +{ + q->setup = EZ(t->setup * 1000, T); + q->act8b = EZ(t->act8b * 1000, T); + q->rec8b = EZ(t->rec8b * 1000, T); + q->cyc8b = EZ(t->cyc8b * 1000, T); + q->active = EZ(t->active * 1000, T); + q->recover = EZ(t->recover * 1000, T); + q->cycle = EZ(t->cycle * 1000, T); + q->udma = EZ(t->udma * 1000, UT); +} + +void ata_timing_merge(const struct ata_timing *a, const struct ata_timing *b, + struct ata_timing *m, unsigned int what) +{ + if (what & ATA_TIMING_SETUP ) m->setup = max(a->setup, b->setup); + if (what & ATA_TIMING_ACT8B ) m->act8b = max(a->act8b, b->act8b); + if (what & ATA_TIMING_REC8B ) m->rec8b = max(a->rec8b, b->rec8b); + if (what & ATA_TIMING_CYC8B ) m->cyc8b = max(a->cyc8b, b->cyc8b); + if (what & ATA_TIMING_ACTIVE ) m->active = max(a->active, b->active); + if (what & ATA_TIMING_RECOVER) m->recover = max(a->recover, b->recover); + if (what & ATA_TIMING_CYCLE ) m->cycle = max(a->cycle, b->cycle); + if (what & ATA_TIMING_UDMA ) m->udma = max(a->udma, b->udma); +} + +static const struct ata_timing* ata_timing_find_mode(unsigned short speed) +{ + const struct ata_timing *t; + + for (t = ata_timing; t->mode != speed; t++) + if (t->mode == 0xFF) + return NULL; + return t; +} + +int ata_timing_compute(struct ata_device *adev, unsigned short speed, + struct ata_timing *t, int T, int UT) +{ + const struct ata_timing *s; + struct ata_timing p; + + /* + * Find the mode. + */ + + if (!(s = ata_timing_find_mode(speed))) + return -EINVAL; + + /* + * If the drive is an EIDE drive, it can tell us it needs extended + * PIO/MW_DMA cycle timing. + */ + + if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE drive */ + memset(&p, 0, sizeof(p)); + if(speed >= XFER_PIO_0 && speed <= XFER_SW_DMA_0) { + if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO]; + else p.cycle = p.cyc8b = adev->id[ATA_ID_EIDE_PIO_IORDY]; + } else if(speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2) { + p.cycle = adev->id[ATA_ID_EIDE_DMA_MIN]; + } + ata_timing_merge(&p, t, t, ATA_TIMING_CYCLE | ATA_TIMING_CYC8B); + } + + /* + * Convert the timing to bus clock counts. + */ + + ata_timing_quantize(s, t, T, UT); + + /* + * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T + * and some other commands. We have to ensure that the DMA cycle timing is + * slower/equal than the fastest PIO timing. + */ + + if (speed > XFER_PIO_4) { + ata_timing_compute(adev, adev->pio_mode, &p, T, UT); + ata_timing_merge(&p, t, t, ATA_TIMING_ALL); + } + + /* + * Lenghten active & recovery time so that cycle time is correct. + */ + + if (t->act8b + t->rec8b < t->cyc8b) { + t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; + t->rec8b = t->cyc8b - t->act8b; + } + + if (t->active + t->recover < t->cycle) { + t->active += (t->cycle - (t->active + t->recover)) / 2; + t->recover = t->cycle - t->active; + } + + return 0; +} + +static const struct { unsigned int shift; u8 base; } xfer_mode_classes[] = { @@ -1589,7 +1733,7 @@ static void ata_host_set_dma(struct ata_port *ap, u8 xfer_mode, */ static void ata_set_mode(struct ata_port *ap) { - unsigned int i, xfer_shift; + unsigned int xfer_shift; u8 xfer_mode; int rc; @@ -1618,11 +1762,6 @@ static void ata_set_mode(struct ata_port *ap) if (ap->ops->post_set_mode) ap->ops->post_set_mode(ap); - for (i = 0; i < 2; i++) { - struct ata_device *dev = &ap->device[i]; - ata_dev_set_protocol(dev); - } - return; err_out: @@ -1732,12 +1871,14 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask) * * LOCKING: * PCI/etc. bus probe sem. + * Obtains host_set lock. * */ static unsigned int ata_bus_edd(struct ata_port *ap) { struct ata_taskfile tf; + unsigned long flags; /* set up execute-device-diag (bus reset) taskfile */ /* also, take interrupts to a known state (disabled) */ @@ -1748,7 +1889,9 @@ static unsigned int ata_bus_edd(struct ata_port *ap) tf.protocol = ATA_PROT_NODATA; /* do bus reset */ + spin_lock_irqsave(&ap->host_set->lock, flags); ata_tf_to_host(ap, &tf); + spin_unlock_irqrestore(&ap->host_set->lock, flags); /* spec says at least 2ms. but who knows with those * crazy ATAPI devices... @@ -1896,7 +2039,8 @@ err_out: DPRINTK("EXIT\n"); } -static void ata_pr_blacklisted(struct ata_port *ap, struct ata_device *dev) +static void ata_pr_blacklisted(const struct ata_port *ap, + const struct ata_device *dev) { printk(KERN_WARNING "ata%u: dev %u is on DMA blacklist, disabling DMA\n", ap->id, dev->devno); @@ -1934,7 +2078,7 @@ static const char * ata_dma_blacklist [] = { "_NEC DV5800A", }; -static int ata_dma_blacklisted(struct ata_port *ap, struct ata_device *dev) +static int ata_dma_blacklisted(const struct ata_device *dev) { unsigned char model_num[40]; char *s; @@ -1959,9 +2103,9 @@ static int ata_dma_blacklisted(struct ata_port *ap, struct ata_device *dev) return 0; } -static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift) +static unsigned int ata_get_mode_mask(const struct ata_port *ap, int shift) { - struct ata_device *master, *slave; + const struct ata_device *master, *slave; unsigned int mask; master = &ap->device[0]; @@ -1973,14 +2117,14 @@ static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift) mask = ap->udma_mask; if (ata_dev_present(master)) { mask &= (master->id[ATA_ID_UDMA_MODES] & 0xff); - if (ata_dma_blacklisted(ap, master)) { + if (ata_dma_blacklisted(master)) { mask = 0; ata_pr_blacklisted(ap, master); } } if (ata_dev_present(slave)) { mask &= (slave->id[ATA_ID_UDMA_MODES] & 0xff); - if (ata_dma_blacklisted(ap, slave)) { + if (ata_dma_blacklisted(slave)) { mask = 0; ata_pr_blacklisted(ap, slave); } @@ -1990,14 +2134,14 @@ static unsigned int ata_get_mode_mask(struct ata_port *ap, int shift) mask = ap->mwdma_mask; if (ata_dev_present(master)) { mask &= (master->id[ATA_ID_MWDMA_MODES] & 0x07); - if (ata_dma_blacklisted(ap, master)) { + if (ata_dma_blacklisted(master)) { mask = 0; ata_pr_blacklisted(ap, master); } } if (ata_dev_present(slave)) { mask &= (slave->id[ATA_ID_MWDMA_MODES] & 0x07); - if (ata_dma_blacklisted(ap, slave)) { + if (ata_dma_blacklisted(slave)) { mask = 0; ata_pr_blacklisted(ap, slave); } @@ -2061,7 +2205,7 @@ static int fgb(u32 bitmap) * Zero on success, negative on error. */ -static int ata_choose_xfer_mode(struct ata_port *ap, +static int ata_choose_xfer_mode(const struct ata_port *ap, u8 *xfer_mode_out, unsigned int *xfer_shift_out) { @@ -2130,6 +2274,110 @@ static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev) } /** + * ata_dev_reread_id - Reread the device identify device info + * @ap: port where the device is + * @dev: device to reread the identify device info + * + * LOCKING: + */ + +static void ata_dev_reread_id(struct ata_port *ap, struct ata_device *dev) +{ + DECLARE_COMPLETION(wait); + struct ata_queued_cmd *qc; + unsigned long flags; + int rc; + + qc = ata_qc_new_init(ap, dev); + BUG_ON(qc == NULL); + + ata_sg_init_one(qc, dev->id, sizeof(dev->id)); + qc->dma_dir = DMA_FROM_DEVICE; + + if (dev->class == ATA_DEV_ATA) { + qc->tf.command = ATA_CMD_ID_ATA; + DPRINTK("do ATA identify\n"); + } else { + qc->tf.command = ATA_CMD_ID_ATAPI; + DPRINTK("do ATAPI identify\n"); + } + + qc->tf.flags |= ATA_TFLAG_DEVICE; + qc->tf.protocol = ATA_PROT_PIO; + qc->nsect = 1; + + qc->waiting = &wait; + qc->complete_fn = ata_qc_complete_noop; + + spin_lock_irqsave(&ap->host_set->lock, flags); + rc = ata_qc_issue(qc); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + if (rc) + goto err_out; + + wait_for_completion(&wait); + + swap_buf_le16(dev->id, ATA_ID_WORDS); + + ata_dump_id(dev); + + DPRINTK("EXIT\n"); + + return; +err_out: + ata_port_disable(ap); +} + +/** + * ata_dev_init_params - Issue INIT DEV PARAMS command + * @ap: Port associated with device @dev + * @dev: Device to which command will be sent + * + * LOCKING: + */ + +static void ata_dev_init_params(struct ata_port *ap, struct ata_device *dev) +{ + DECLARE_COMPLETION(wait); + struct ata_queued_cmd *qc; + int rc; + unsigned long flags; + u16 sectors = dev->id[6]; + u16 heads = dev->id[3]; + + /* Number of sectors per track 1-255. Number of heads 1-16 */ + if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16) + return; + + /* set up init dev params taskfile */ + DPRINTK("init dev params \n"); + + qc = ata_qc_new_init(ap, dev); + BUG_ON(qc == NULL); + + qc->tf.command = ATA_CMD_INIT_DEV_PARAMS; + qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + qc->tf.protocol = ATA_PROT_NODATA; + qc->tf.nsect = sectors; + qc->tf.device |= (heads - 1) & 0x0f; /* max head = num. of heads - 1 */ + + qc->waiting = &wait; + qc->complete_fn = ata_qc_complete_noop; + + spin_lock_irqsave(&ap->host_set->lock, flags); + rc = ata_qc_issue(qc); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + if (rc) + ata_port_disable(ap); + else + wait_for_completion(&wait); + + DPRINTK("EXIT\n"); +} + +/** * ata_sg_clean - Unmap DMA memory associated with command * @qc: Command containing DMA memory to be released * @@ -2268,34 +2516,14 @@ void ata_qc_prep(struct ata_queued_cmd *qc) * spin_lock_irqsave(host_set lock) */ - - -/** - * ata_sg_init_one - Prepare a one-entry scatter-gather list. - * @qc: Queued command - * @buf: transfer buffer - * @buflen: length of buf - * - * Builds a single-entry scatter-gather list to initiate a - * transfer utilizing the specified buffer. - * - * LOCKING: - */ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) { - struct scatterlist *sg; - qc->flags |= ATA_QCFLAG_SINGLE; - memset(&qc->sgent, 0, sizeof(qc->sgent)); qc->sg = &qc->sgent; qc->n_elem = 1; qc->buf_virt = buf; - - sg = qc->sg; - sg->page = virt_to_page(buf); - sg->offset = (unsigned long) buf & ~PAGE_MASK; - sg->length = buflen; + sg_init_one(qc->sg, buf, buflen); } /** @@ -2312,18 +2540,6 @@ void ata_sg_init_one(struct ata_queued_cmd *qc, void *buf, unsigned int buflen) * spin_lock_irqsave(host_set lock) */ - -/** - * ata_sg_init - Assign a scatter gather list to a queued command - * @qc: Queued command - * @sg: Scatter-gather list - * @n_elem: length of sg list - * - * Attaches a scatter-gather list to a queued command. - * - * LOCKING: - */ - void ata_sg_init(struct ata_queued_cmd *qc, struct scatterlist *sg, unsigned int n_elem) { @@ -2402,33 +2618,54 @@ static int ata_sg_setup(struct ata_queued_cmd *qc) } /** + * ata_poll_qc_complete - turn irq back on and finish qc + * @qc: Command to complete + * @drv_stat: ATA status register content + * + * LOCKING: + * None. (grabs host lock) + */ + +void ata_poll_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask) +{ + struct ata_port *ap = qc->ap; + unsigned long flags; + + spin_lock_irqsave(&ap->host_set->lock, flags); + ap->flags &= ~ATA_FLAG_NOINTR; + ata_irq_on(ap); + ata_qc_complete(qc, err_mask); + spin_unlock_irqrestore(&ap->host_set->lock, flags); +} + +/** * ata_pio_poll - - * @ap: + * @ap: the target ata_port * * LOCKING: * None. (executing in kernel thread context) * * RETURNS: - * + * timeout value to use */ static unsigned long ata_pio_poll(struct ata_port *ap) { u8 status; - unsigned int poll_state = PIO_ST_UNKNOWN; - unsigned int reg_state = PIO_ST_UNKNOWN; - const unsigned int tmout_state = PIO_ST_TMOUT; - - switch (ap->pio_task_state) { - case PIO_ST: - case PIO_ST_POLL: - poll_state = PIO_ST_POLL; - reg_state = PIO_ST; + unsigned int poll_state = HSM_ST_UNKNOWN; + unsigned int reg_state = HSM_ST_UNKNOWN; + const unsigned int tmout_state = HSM_ST_TMOUT; + + switch (ap->hsm_task_state) { + case HSM_ST: + case HSM_ST_POLL: + poll_state = HSM_ST_POLL; + reg_state = HSM_ST; break; - case PIO_ST_LAST: - case PIO_ST_LAST_POLL: - poll_state = PIO_ST_LAST_POLL; - reg_state = PIO_ST_LAST; + case HSM_ST_LAST: + case HSM_ST_LAST_POLL: + poll_state = HSM_ST_LAST_POLL; + reg_state = HSM_ST_LAST; break; default: BUG(); @@ -2438,68 +2675,72 @@ static unsigned long ata_pio_poll(struct ata_port *ap) status = ata_chk_status(ap); if (status & ATA_BUSY) { if (time_after(jiffies, ap->pio_task_timeout)) { - ap->pio_task_state = tmout_state; + ap->hsm_task_state = tmout_state; return 0; } - ap->pio_task_state = poll_state; + ap->hsm_task_state = poll_state; return ATA_SHORT_PAUSE; } - ap->pio_task_state = reg_state; + ap->hsm_task_state = reg_state; return 0; } /** - * ata_pio_complete - - * @ap: + * ata_pio_complete - check if drive is busy or idle + * @ap: the target ata_port * * LOCKING: * None. (executing in kernel thread context) + * + * RETURNS: + * Non-zero if qc completed, zero otherwise. */ -static void ata_pio_complete (struct ata_port *ap) +static int ata_pio_complete (struct ata_port *ap) { struct ata_queued_cmd *qc; u8 drv_stat; /* - * This is purely hueristic. This is a fast path. - * Sometimes when we enter, BSY will be cleared in - * a chk-status or two. If not, the drive is probably seeking - * or something. Snooze for a couple msecs, then - * chk-status again. If still busy, fall back to - * PIO_ST_POLL state. + * This is purely heuristic. This is a fast path. Sometimes when + * we enter, BSY will be cleared in a chk-status or two. If not, + * the drive is probably seeking or something. Snooze for a couple + * msecs, then chk-status again. If still busy, fall back to + * HSM_ST_POLL state. */ drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10); if (drv_stat & (ATA_BUSY | ATA_DRQ)) { msleep(2); drv_stat = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 10); if (drv_stat & (ATA_BUSY | ATA_DRQ)) { - ap->pio_task_state = PIO_ST_LAST_POLL; + ap->hsm_task_state = HSM_ST_LAST_POLL; ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO; - return; + return 0; } } drv_stat = ata_wait_idle(ap); if (!ata_ok(drv_stat)) { - ap->pio_task_state = PIO_ST_ERR; - return; + ap->hsm_task_state = HSM_ST_ERR; + return 0; } qc = ata_qc_from_tag(ap, ap->active_tag); assert(qc != NULL); - ap->pio_task_state = PIO_ST_IDLE; + ap->hsm_task_state = HSM_ST_IDLE; - ata_irq_on(ap); + ata_poll_qc_complete(qc, 0); - ata_qc_complete(qc, drv_stat); + /* another command may start at this point */ + + return 1; } /** - * swap_buf_le16 - + * swap_buf_le16 - swap halves of 16-words in place * @buf: Buffer to swap * @buf_words: Number of 16-bit words in buffer. * @@ -2508,6 +2749,7 @@ static void ata_pio_complete (struct ata_port *ap) * vice-versa. * * LOCKING: + * Inherited from caller. */ void swap_buf_le16(u16 *buf, unsigned int buf_words) { @@ -2519,6 +2761,19 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) #endif /* __BIG_ENDIAN */ } +/** + * ata_mmio_data_xfer - Transfer data by MMIO + * @ap: port to read/write + * @buf: data buffer + * @buflen: buffer length + * @write_data: read/write + * + * Transfer data from/to the device data register by MMIO. + * + * LOCKING: + * Inherited from caller. + */ + static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, unsigned int buflen, int write_data) { @@ -2527,6 +2782,7 @@ static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, u16 *buf16 = (u16 *) buf; void __iomem *mmio = (void __iomem *)ap->ioaddr.data_addr; + /* Transfer multiple of 2 bytes */ if (write_data) { for (i = 0; i < words; i++) writew(le16_to_cpu(buf16[i]), mmio); @@ -2534,19 +2790,74 @@ static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, for (i = 0; i < words; i++) buf16[i] = cpu_to_le16(readw(mmio)); } + + /* Transfer trailing 1 byte, if any. */ + if (unlikely(buflen & 0x01)) { + u16 align_buf[1] = { 0 }; + unsigned char *trailing_buf = buf + buflen - 1; + + if (write_data) { + memcpy(align_buf, trailing_buf, 1); + writew(le16_to_cpu(align_buf[0]), mmio); + } else { + align_buf[0] = cpu_to_le16(readw(mmio)); + memcpy(trailing_buf, align_buf, 1); + } + } } +/** + * ata_pio_data_xfer - Transfer data by PIO + * @ap: port to read/write + * @buf: data buffer + * @buflen: buffer length + * @write_data: read/write + * + * Transfer data from/to the device data register by PIO. + * + * LOCKING: + * Inherited from caller. + */ + static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf, unsigned int buflen, int write_data) { - unsigned int dwords = buflen >> 1; + unsigned int words = buflen >> 1; + /* Transfer multiple of 2 bytes */ if (write_data) - outsw(ap->ioaddr.data_addr, buf, dwords); + outsw(ap->ioaddr.data_addr, buf, words); else - insw(ap->ioaddr.data_addr, buf, dwords); + insw(ap->ioaddr.data_addr, buf, words); + + /* Transfer trailing 1 byte, if any. */ + if (unlikely(buflen & 0x01)) { + u16 align_buf[1] = { 0 }; + unsigned char *trailing_buf = buf + buflen - 1; + + if (write_data) { + memcpy(align_buf, trailing_buf, 1); + outw(le16_to_cpu(align_buf[0]), ap->ioaddr.data_addr); + } else { + align_buf[0] = cpu_to_le16(inw(ap->ioaddr.data_addr)); + memcpy(trailing_buf, align_buf, 1); + } + } } +/** + * ata_data_xfer - Transfer data from/to the data register. + * @ap: port to read/write + * @buf: data buffer + * @buflen: buffer length + * @do_write: read/write + * + * Transfer data from/to the device data register. + * + * LOCKING: + * Inherited from caller. + */ + static void ata_data_xfer(struct ata_port *ap, unsigned char *buf, unsigned int buflen, int do_write) { @@ -2556,6 +2867,16 @@ static void ata_data_xfer(struct ata_port *ap, unsigned char *buf, ata_pio_data_xfer(ap, buf, buflen, do_write); } +/** + * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data. + * @qc: Command on going + * + * Transfer ATA_SECT_SIZE of data from/to the ATA device. + * + * LOCKING: + * Inherited from caller. + */ + static void ata_pio_sector(struct ata_queued_cmd *qc) { int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); @@ -2566,7 +2887,7 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) unsigned char *buf; if (qc->cursect == (qc->nsect - 1)) - ap->pio_task_state = PIO_ST_LAST; + ap->hsm_task_state = HSM_ST_LAST; page = sg[qc->cursg].page; offset = sg[qc->cursg].offset + qc->cursg_ofs * ATA_SECT_SIZE; @@ -2594,6 +2915,18 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) kunmap(page); } +/** + * __atapi_pio_bytes - Transfer data from/to the ATAPI device. + * @qc: Command on going + * @bytes: number of bytes + * + * Transfer Transfer data from/to the ATAPI device. + * + * LOCKING: + * Inherited from caller. + * + */ + static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) { int do_write = (qc->tf.flags & ATA_TFLAG_WRITE); @@ -2603,10 +2936,33 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) unsigned char *buf; unsigned int offset, count; - if (qc->curbytes == qc->nbytes - bytes) - ap->pio_task_state = PIO_ST_LAST; + if (qc->curbytes + bytes >= qc->nbytes) + ap->hsm_task_state = HSM_ST_LAST; next_sg: + if (unlikely(qc->cursg >= qc->n_elem)) { + /* + * The end of qc->sg is reached and the device expects + * more data to transfer. In order not to overrun qc->sg + * and fulfill length specified in the byte count register, + * - for read case, discard trailing data from the device + * - for write case, padding zero data to the device + */ + u16 pad_buf[1] = { 0 }; + unsigned int words = bytes >> 1; + unsigned int i; + + if (words) /* warning if bytes > 1 */ + printk(KERN_WARNING "ata%u: %u bytes trailing data\n", + ap->id, bytes); + + for (i = 0; i < words; i++) + ata_data_xfer(ap, (unsigned char*)pad_buf, 2, do_write); + + ap->hsm_task_state = HSM_ST_LAST; + return; + } + sg = &qc->sg[qc->cursg]; page = sg->page; @@ -2640,11 +2996,20 @@ next_sg: kunmap(page); - if (bytes) { + if (bytes) goto next_sg; - } } +/** + * atapi_pio_bytes - Transfer data from/to the ATAPI device. + * @qc: Command on going + * + * Transfer Transfer data from/to the ATAPI device. + * + * LOCKING: + * Inherited from caller. + */ + static void atapi_pio_bytes(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; @@ -2674,12 +3039,12 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) err_out: printk(KERN_INFO "ata%u: dev %u: ATAPI check failed\n", ap->id, dev->devno); - ap->pio_task_state = PIO_ST_ERR; + ap->hsm_task_state = HSM_ST_ERR; } /** - * ata_pio_sector - - * @ap: + * ata_pio_block - start PIO on a block + * @ap: the target ata_port * * LOCKING: * None. (executing in kernel thread context) @@ -2691,19 +3056,19 @@ static void ata_pio_block(struct ata_port *ap) u8 status; /* - * This is purely hueristic. This is a fast path. + * This is purely heuristic. This is a fast path. * Sometimes when we enter, BSY will be cleared in * a chk-status or two. If not, the drive is probably seeking * or something. Snooze for a couple msecs, then * chk-status again. If still busy, fall back to - * PIO_ST_POLL state. + * HSM_ST_POLL state. */ status = ata_busy_wait(ap, ATA_BUSY, 5); if (status & ATA_BUSY) { msleep(2); status = ata_busy_wait(ap, ATA_BUSY, 10); if (status & ATA_BUSY) { - ap->pio_task_state = PIO_ST_POLL; + ap->hsm_task_state = HSM_ST_POLL; ap->pio_task_timeout = jiffies + ATA_TMOUT_PIO; return; } @@ -2715,11 +3080,7 @@ static void ata_pio_block(struct ata_port *ap) if (is_atapi_taskfile(&qc->tf)) { /* no more data to transfer or unsupported ATAPI command */ if ((status & ATA_DRQ) == 0) { - ap->pio_task_state = PIO_ST_IDLE; - - ata_irq_on(ap); - - ata_qc_complete(qc, status); + ap->hsm_task_state = HSM_ST_LAST; return; } @@ -2727,7 +3088,7 @@ static void ata_pio_block(struct ata_port *ap) } else { /* handle BSY=0, DRQ=0 as error */ if ((status & ATA_DRQ) == 0) { - ap->pio_task_state = PIO_ST_ERR; + ap->hsm_task_state = HSM_ST_ERR; return; } @@ -2738,101 +3099,54 @@ static void ata_pio_block(struct ata_port *ap) static void ata_pio_error(struct ata_port *ap) { struct ata_queued_cmd *qc; - u8 drv_stat; + + printk(KERN_WARNING "ata%u: PIO error\n", ap->id); qc = ata_qc_from_tag(ap, ap->active_tag); assert(qc != NULL); - drv_stat = ata_chk_status(ap); - printk(KERN_WARNING "ata%u: PIO error, drv_stat 0x%x\n", - ap->id, drv_stat); - - ap->pio_task_state = PIO_ST_IDLE; + ap->hsm_task_state = HSM_ST_IDLE; - ata_irq_on(ap); - - ata_qc_complete(qc, drv_stat | ATA_ERR); + ata_poll_qc_complete(qc, AC_ERR_ATA_BUS); } static void ata_pio_task(void *_data) { struct ata_port *ap = _data; - unsigned long timeout = 0; + unsigned long timeout; + int qc_completed; + +fsm_start: + timeout = 0; + qc_completed = 0; - switch (ap->pio_task_state) { - case PIO_ST_IDLE: + switch (ap->hsm_task_state) { + case HSM_ST_IDLE: return; - case PIO_ST: + case HSM_ST: ata_pio_block(ap); break; - case PIO_ST_LAST: - ata_pio_complete(ap); + case HSM_ST_LAST: + qc_completed = ata_pio_complete(ap); break; - case PIO_ST_POLL: - case PIO_ST_LAST_POLL: + case HSM_ST_POLL: + case HSM_ST_LAST_POLL: timeout = ata_pio_poll(ap); break; - case PIO_ST_TMOUT: - case PIO_ST_ERR: + case HSM_ST_TMOUT: + case HSM_ST_ERR: ata_pio_error(ap); return; } if (timeout) - queue_delayed_work(ata_wq, &ap->pio_task, - timeout); - else - queue_work(ata_wq, &ap->pio_task); -} - -static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev, - struct scsi_cmnd *cmd) -{ - DECLARE_COMPLETION(wait); - struct ata_queued_cmd *qc; - unsigned long flags; - int rc; - - DPRINTK("ATAPI request sense\n"); - - qc = ata_qc_new_init(ap, dev); - BUG_ON(qc == NULL); - - /* FIXME: is this needed? */ - memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); - - ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer)); - qc->dma_dir = DMA_FROM_DEVICE; - - memset(&qc->cdb, 0, ap->cdb_len); - qc->cdb[0] = REQUEST_SENSE; - qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; - - qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - qc->tf.command = ATA_CMD_PACKET; - - qc->tf.protocol = ATA_PROT_ATAPI; - qc->tf.lbam = (8 * 1024) & 0xff; - qc->tf.lbah = (8 * 1024) >> 8; - qc->nbytes = SCSI_SENSE_BUFFERSIZE; - - qc->waiting = &wait; - qc->complete_fn = ata_qc_complete_noop; - - spin_lock_irqsave(&ap->host_set->lock, flags); - rc = ata_qc_issue(qc); - spin_unlock_irqrestore(&ap->host_set->lock, flags); - - if (rc) - ata_port_disable(ap); - else - wait_for_completion(&wait); - - DPRINTK("EXIT\n"); + queue_delayed_work(ata_wq, &ap->pio_task, timeout); + else if (!qc_completed) + goto fsm_start; } /** @@ -2857,8 +3171,10 @@ static void atapi_request_sense(struct ata_port *ap, struct ata_device *dev, static void ata_qc_timeout(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; + struct ata_host_set *host_set = ap->host_set; struct ata_device *dev = qc->dev; u8 host_stat = 0, drv_stat; + unsigned long flags; DPRINTK("ENTER\n"); @@ -2869,7 +3185,9 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc) if (!(cmd->eh_eflags & SCSI_EH_CANCEL_CMD)) { /* finish completing original command */ + spin_lock_irqsave(&host_set->lock, flags); __ata_qc_complete(qc); + spin_unlock_irqrestore(&host_set->lock, flags); atapi_request_sense(ap, dev, cmd); @@ -2880,6 +3198,8 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc) } } + spin_lock_irqsave(&host_set->lock, flags); + /* hack alert! We cannot use the supplied completion * function from inside the ->eh_strategy_handler() thread. * libata is the only user of ->eh_strategy_handler() in @@ -2895,7 +3215,7 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc) host_stat = ap->ops->bmdma_status(ap); /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(ap); + ap->ops->bmdma_stop(qc); /* fall through */ @@ -2910,9 +3230,12 @@ static void ata_qc_timeout(struct ata_queued_cmd *qc) ap->id, qc->tf.command, drv_stat, host_stat); /* complete taskfile transaction */ - ata_qc_complete(qc, drv_stat); + ata_qc_complete(qc, ac_err_mask(drv_stat)); break; } + + spin_unlock_irqrestore(&host_set->lock, flags); + out: DPRINTK("EXIT\n"); } @@ -2943,14 +3266,14 @@ void ata_eng_timeout(struct ata_port *ap) DPRINTK("ENTER\n"); qc = ata_qc_from_tag(ap, ap->active_tag); - if (!qc) { + if (qc) + ata_qc_timeout(qc); + else { printk(KERN_ERR "ata%u: BUG: timeout without command\n", ap->id); goto out; } - ata_qc_timeout(qc); - out: DPRINTK("EXIT\n"); } @@ -3007,15 +3330,12 @@ struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, qc->nbytes = qc->curbytes = 0; ata_tf_init(ap, &qc->tf, dev->devno); - - if (dev->flags & ATA_DFLAG_LBA48) - qc->tf.flags |= ATA_TFLAG_LBA48; } return qc; } -static int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat) +int ata_qc_complete_noop(struct ata_queued_cmd *qc, unsigned int err_mask) { return 0; } @@ -3053,7 +3373,6 @@ static void __ata_qc_complete(struct ata_queued_cmd *qc) * * LOCKING: * spin_lock_irqsave(host_set lock) - * */ void ata_qc_free(struct ata_queued_cmd *qc) { @@ -3073,10 +3392,9 @@ void ata_qc_free(struct ata_queued_cmd *qc) * * LOCKING: * spin_lock_irqsave(host_set lock) - * */ -void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) +void ata_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask) { int rc; @@ -3086,10 +3404,15 @@ void ata_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) if (likely(qc->flags & ATA_QCFLAG_DMAMAP)) ata_sg_clean(qc); - /* call completion callback */ - rc = qc->complete_fn(qc, drv_stat); + /* atapi: mark qc as inactive to prevent the interrupt handler + * from completing the command twice later, before the error handler + * is called. (when rc != 0 and atapi request sense is needed) + */ qc->flags &= ~ATA_QCFLAG_ACTIVE; + /* call completion callback */ + rc = qc->complete_fn(qc, err_mask); + /* if callback indicates not to complete command (non-zero), * return immediately */ @@ -3195,7 +3518,7 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc) switch (qc->tf.protocol) { case ATA_PROT_NODATA: - ata_tf_to_host_nolock(ap, &qc->tf); + ata_tf_to_host(ap, &qc->tf); break; case ATA_PROT_DMA: @@ -3206,23 +3529,25 @@ int ata_qc_issue_prot(struct ata_queued_cmd *qc) case ATA_PROT_PIO: /* load tf registers, initiate polling pio */ ata_qc_set_polling(qc); - ata_tf_to_host_nolock(ap, &qc->tf); - ap->pio_task_state = PIO_ST; + ata_tf_to_host(ap, &qc->tf); + ap->hsm_task_state = HSM_ST; queue_work(ata_wq, &ap->pio_task); break; case ATA_PROT_ATAPI: ata_qc_set_polling(qc); - ata_tf_to_host_nolock(ap, &qc->tf); + ata_tf_to_host(ap, &qc->tf); queue_work(ata_wq, &ap->packet_task); break; case ATA_PROT_ATAPI_NODATA: - ata_tf_to_host_nolock(ap, &qc->tf); + ap->flags |= ATA_FLAG_NOINTR; + ata_tf_to_host(ap, &qc->tf); queue_work(ata_wq, &ap->packet_task); break; case ATA_PROT_ATAPI_DMA: + ap->flags |= ATA_FLAG_NOINTR; ap->ops->tf_load(ap, &qc->tf); /* load tf registers */ ap->ops->bmdma_setup(qc); /* set up bmdma */ queue_work(ata_wq, &ap->packet_task); @@ -3267,7 +3592,7 @@ static void ata_bmdma_setup_mmio (struct ata_queued_cmd *qc) } /** - * ata_bmdma_start - Start a PCI IDE BMDMA transaction + * ata_bmdma_start_mmio - Start a PCI IDE BMDMA transaction * @qc: Info associated with this ATA transaction. * * LOCKING: @@ -3431,14 +3756,14 @@ u8 ata_bmdma_status(struct ata_port *ap) void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; host_stat = readb(mmio + ATA_DMA_STATUS); } else - host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + host_stat = inb(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); return host_stat; } /** * ata_bmdma_stop - Stop PCI IDE BMDMA transfer - * @ap: Port associated with this ATA transaction. + * @qc: Command we are ending DMA for * * Clears the ATA_DMA_START flag in the dma control register * @@ -3448,8 +3773,9 @@ u8 ata_bmdma_status(struct ata_port *ap) * spin_lock_irqsave(host_set lock) */ -void ata_bmdma_stop(struct ata_port *ap) +void ata_bmdma_stop(struct ata_queued_cmd *qc) { + struct ata_port *ap = qc->ap; if (ap->flags & ATA_FLAG_MMIO) { void __iomem *mmio = (void __iomem *) ap->ioaddr.bmdma_addr; @@ -3501,7 +3827,7 @@ inline unsigned int ata_host_intr (struct ata_port *ap, goto idle_irq; /* before we do anything else, clear DMA-Start bit */ - ap->ops->bmdma_stop(ap); + ap->ops->bmdma_stop(qc); /* fall through */ @@ -3523,7 +3849,7 @@ inline unsigned int ata_host_intr (struct ata_port *ap, ap->ops->irq_clear(ap); /* complete taskfile transaction */ - ata_qc_complete(qc, status); + ata_qc_complete(qc, ac_err_mask(status)); break; default: @@ -3559,7 +3885,6 @@ idle_irq: * * RETURNS: * IRQ_NONE or IRQ_HANDLED. - * */ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) @@ -3576,7 +3901,8 @@ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) struct ata_port *ap; ap = host_set->ports[i]; - if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (ap && + !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -3618,7 +3944,7 @@ static void atapi_packet_task(void *_data) /* sleep-wait for BSY to clear */ DPRINTK("busy wait\n"); if (ata_busy_sleep(ap, ATA_TMOUT_CDB_QUICK, ATA_TMOUT_CDB)) - goto err_out; + goto err_out_status; /* make sure DRQ is set */ status = ata_chk_status(ap); @@ -3628,27 +3954,37 @@ static void atapi_packet_task(void *_data) /* send SCSI cdb */ DPRINTK("send cdb\n"); assert(ap->cdb_len >= 12); - ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); - /* if we are DMA'ing, irq handler takes over from here */ - if (qc->tf.protocol == ATA_PROT_ATAPI_DMA) - ap->ops->bmdma_start(qc); /* initiate bmdma */ + if (qc->tf.protocol == ATA_PROT_ATAPI_DMA || + qc->tf.protocol == ATA_PROT_ATAPI_NODATA) { + unsigned long flags; - /* non-data commands are also handled via irq */ - else if (qc->tf.protocol == ATA_PROT_ATAPI_NODATA) { - /* do nothing */ - } + /* Once we're done issuing command and kicking bmdma, + * irq handler takes over. To not lose irq, we need + * to clear NOINTR flag before sending cdb, but + * interrupt handler shouldn't be invoked before we're + * finished. Hence, the following locking. + */ + spin_lock_irqsave(&ap->host_set->lock, flags); + ap->flags &= ~ATA_FLAG_NOINTR; + ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); + if (qc->tf.protocol == ATA_PROT_ATAPI_DMA) + ap->ops->bmdma_start(qc); /* initiate bmdma */ + spin_unlock_irqrestore(&ap->host_set->lock, flags); + } else { + ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); - /* PIO commands are handled by polling */ - else { - ap->pio_task_state = PIO_ST; + /* PIO commands are handled by polling */ + ap->hsm_task_state = HSM_ST; queue_work(ata_wq, &ap->pio_task); } return; +err_out_status: + status = ata_chk_status(ap); err_out: - ata_qc_complete(qc, ATA_ERR); + ata_poll_qc_complete(qc, __ac_err_mask(status)); } @@ -3662,6 +3998,7 @@ err_out: * May be used as the port_start() entry in ata_port_operations. * * LOCKING: + * Inherited from caller. */ int ata_port_start (struct ata_port *ap) @@ -3687,6 +4024,7 @@ int ata_port_start (struct ata_port *ap) * May be used as the port_stop() entry in ata_port_operations. * * LOCKING: + * Inherited from caller. */ void ata_port_stop (struct ata_port *ap) @@ -3709,6 +4047,7 @@ void ata_host_stop (struct ata_host_set *host_set) * @do_unregister: 1 if we fully unregister, 0 to just stop the port * * LOCKING: + * Inherited from caller. */ static void ata_host_remove(struct ata_port *ap, unsigned int do_unregister) @@ -3736,12 +4075,11 @@ static void ata_host_remove(struct ata_port *ap, unsigned int do_unregister) * * LOCKING: * Inherited from caller. - * */ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, struct ata_host_set *host_set, - struct ata_probe_ent *ent, unsigned int port_no) + const struct ata_probe_ent *ent, unsigned int port_no) { unsigned int i; @@ -3751,8 +4089,6 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, host->unique_id = ata_unique_id++; host->max_cmd_len = 12; - scsi_assign_lock(host, &host_set->lock); - ap->flags = ATA_FLAG_PORT_DISABLED; ap->id = host->unique_id; ap->host = host; @@ -3797,10 +4133,9 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host, * * RETURNS: * New ata_port on success, for NULL on error. - * */ -static struct ata_port * ata_host_add(struct ata_probe_ent *ent, +static struct ata_port * ata_host_add(const struct ata_probe_ent *ent, struct ata_host_set *host_set, unsigned int port_no) { @@ -3845,10 +4180,9 @@ err_out: * * RETURNS: * Number of ports registered. Zero on error (no ports registered). - * */ -int ata_device_add(struct ata_probe_ent *ent) +int ata_device_add(const struct ata_probe_ent *ent) { unsigned int count = 0, i; struct device *dev = ent->dev; @@ -3856,11 +4190,10 @@ int ata_device_add(struct ata_probe_ent *ent) DPRINTK("ENTER\n"); /* alloc a container for our list of ATA ports (buses) */ - host_set = kmalloc(sizeof(struct ata_host_set) + + host_set = kzalloc(sizeof(struct ata_host_set) + (ent->n_ports * sizeof(void *)), GFP_KERNEL); if (!host_set) return 0; - memset(host_set, 0, sizeof(struct ata_host_set) + (ent->n_ports * sizeof(void *))); spin_lock_init(&host_set->lock); host_set->dev = dev; @@ -3900,10 +4233,8 @@ int ata_device_add(struct ata_probe_ent *ent) count++; } - if (!count) { - kfree(host_set); - return 0; - } + if (!count) + goto err_free_ret; /* obtain irq, that is shared between channels */ if (request_irq(ent->irq, ent->port_ops->irq_handler, ent->irq_flags, @@ -3948,7 +4279,7 @@ int ata_device_add(struct ata_probe_ent *ent) for (i = 0; i < count; i++) { struct ata_port *ap = host_set->ports[i]; - scsi_scan_host(ap->host); + ata_scsi_scan_host(ap); } dev_set_drvdata(dev, host_set); @@ -3961,12 +4292,59 @@ err_out: ata_host_remove(host_set->ports[i], 1); scsi_host_put(host_set->ports[i]->host); } +err_free_ret: kfree(host_set); VPRINTK("EXIT, returning 0\n"); return 0; } /** + * ata_host_set_remove - PCI layer callback for device removal + * @host_set: ATA host set that was removed + * + * Unregister all objects associated with this host set. Free those + * objects. + * + * LOCKING: + * Inherited from calling layer (may sleep). + */ + +void ata_host_set_remove(struct ata_host_set *host_set) +{ + struct ata_port *ap; + unsigned int i; + + for (i = 0; i < host_set->n_ports; i++) { + ap = host_set->ports[i]; + scsi_remove_host(ap->host); + } + + free_irq(host_set->irq, host_set); + + for (i = 0; i < host_set->n_ports; i++) { + ap = host_set->ports[i]; + + ata_scsi_release(ap->host); + + if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { + struct ata_ioports *ioaddr = &ap->ioaddr; + + if (ioaddr->cmd_addr == 0x1f0) + release_region(0x1f0, 8); + else if (ioaddr->cmd_addr == 0x170) + release_region(0x170, 8); + } + + scsi_host_put(ap->host); + } + + if (host_set->ops->host_stop) + host_set->ops->host_stop(host_set); + + kfree(host_set); +} + +/** * ata_scsi_release - SCSI layer callback hook for host unload * @host: libata host to be unloaded * @@ -4020,19 +4398,17 @@ void ata_std_ports(struct ata_ioports *ioaddr) } static struct ata_probe_ent * -ata_probe_ent_alloc(struct device *dev, struct ata_port_info *port) +ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) { struct ata_probe_ent *probe_ent; - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); if (!probe_ent) { printk(KERN_ERR DRV_NAME "(%s): out of memory\n", kobject_name(&(dev->kobj))); return NULL; } - memset(probe_ent, 0, sizeof(*probe_ent)); - INIT_LIST_HEAD(&probe_ent->node); probe_ent->dev = dev; @@ -4048,90 +4424,99 @@ ata_probe_ent_alloc(struct device *dev, struct ata_port_info *port) +#ifdef CONFIG_PCI + +void ata_pci_host_stop (struct ata_host_set *host_set) +{ + struct pci_dev *pdev = to_pci_dev(host_set->dev); + + pci_iounmap(pdev, host_set->mmio_base); +} + /** * ata_pci_init_native_mode - Initialize native-mode driver * @pdev: pci device to be initialized * @port: array[2] of pointers to port info structures. + * @ports: bitmap of ports present * * Utility function which allocates and initializes an * ata_probe_ent structure for a standard dual-port * PIO-based IDE controller. The returned ata_probe_ent * structure can be passed to ata_device_add(). The returned * ata_probe_ent structure should then be freed with kfree(). + * + * The caller need only pass the address of the primary port, the + * secondary will be deduced automatically. If the device has non + * standard secondary port mappings this function can be called twice, + * once for each interface. */ -#ifdef CONFIG_PCI struct ata_probe_ent * -ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port) +ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports) { struct ata_probe_ent *probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); + int p = 0; + if (!probe_ent) return NULL; - probe_ent->n_ports = 2; probe_ent->irq = pdev->irq; probe_ent->irq_flags = SA_SHIRQ; - probe_ent->port[0].cmd_addr = pci_resource_start(pdev, 0); - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = - pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; - probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4); - - probe_ent->port[1].cmd_addr = pci_resource_start(pdev, 2); - probe_ent->port[1].altstatus_addr = - probe_ent->port[1].ctl_addr = - pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; - probe_ent->port[1].bmdma_addr = pci_resource_start(pdev, 4) + 8; + if (ports & ATA_PORT_PRIMARY) { + probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 0); + probe_ent->port[p].altstatus_addr = + probe_ent->port[p].ctl_addr = + pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS; + probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4); + ata_std_ports(&probe_ent->port[p]); + p++; + } - ata_std_ports(&probe_ent->port[0]); - ata_std_ports(&probe_ent->port[1]); + if (ports & ATA_PORT_SECONDARY) { + probe_ent->port[p].cmd_addr = pci_resource_start(pdev, 2); + probe_ent->port[p].altstatus_addr = + probe_ent->port[p].ctl_addr = + pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS; + probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4) + 8; + ata_std_ports(&probe_ent->port[p]); + p++; + } + probe_ent->n_ports = p; return probe_ent; } -static struct ata_probe_ent * -ata_pci_init_legacy_mode(struct pci_dev *pdev, struct ata_port_info **port, - struct ata_probe_ent **ppe2) +static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, struct ata_port_info *port, int port_num) { - struct ata_probe_ent *probe_ent, *probe_ent2; + struct ata_probe_ent *probe_ent; - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); + probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port); if (!probe_ent) return NULL; - probe_ent2 = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[1]); - if (!probe_ent2) { - kfree(probe_ent); - return NULL; - } - - probe_ent->n_ports = 1; - probe_ent->irq = 14; - probe_ent->hard_port_no = 0; probe_ent->legacy_mode = 1; - - probe_ent2->n_ports = 1; - probe_ent2->irq = 15; - - probe_ent2->hard_port_no = 1; - probe_ent2->legacy_mode = 1; - - probe_ent->port[0].cmd_addr = 0x1f0; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = 0x3f6; - probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4); - - probe_ent2->port[0].cmd_addr = 0x170; - probe_ent2->port[0].altstatus_addr = - probe_ent2->port[0].ctl_addr = 0x376; - probe_ent2->port[0].bmdma_addr = pci_resource_start(pdev, 4)+8; - + probe_ent->n_ports = 1; + probe_ent->hard_port_no = port_num; + + switch(port_num) + { + case 0: + probe_ent->irq = 14; + probe_ent->port[0].cmd_addr = 0x1f0; + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = 0x3f6; + break; + case 1: + probe_ent->irq = 15; + probe_ent->port[0].cmd_addr = 0x170; + probe_ent->port[0].altstatus_addr = + probe_ent->port[0].ctl_addr = 0x376; + break; + } + probe_ent->port[0].bmdma_addr = pci_resource_start(pdev, 4) + 8 * port_num; ata_std_ports(&probe_ent->port[0]); - ata_std_ports(&probe_ent2->port[0]); - - *ppe2 = probe_ent2; return probe_ent; } @@ -4154,13 +4539,12 @@ ata_pci_init_legacy_mode(struct pci_dev *pdev, struct ata_port_info **port, * * RETURNS: * Zero on success, negative on errno-based value on error. - * */ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, unsigned int n_ports) { - struct ata_probe_ent *probe_ent, *probe_ent2 = NULL; + struct ata_probe_ent *probe_ent = NULL, *probe_ent2 = NULL; struct ata_port_info *port[2]; u8 tmp8, mask; unsigned int legacy_mode = 0; @@ -4177,7 +4561,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, if ((port[0]->host_flags & ATA_FLAG_NO_LEGACY) == 0 && (pdev->class >> 8) == PCI_CLASS_STORAGE_IDE) { - /* TODO: support transitioning to native mode? */ + /* TODO: What if one channel is in native mode ... */ pci_read_config_byte(pdev, PCI_CLASS_PROG, &tmp8); mask = (1 << 2) | (1 << 0); if ((tmp8 & mask) != mask) @@ -4185,11 +4569,20 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, } /* FIXME... */ - if ((!legacy_mode) && (n_ports > 1)) { - printk(KERN_ERR "ata: BUG: native mode, n_ports > 1\n"); - return -EINVAL; + if ((!legacy_mode) && (n_ports > 2)) { + printk(KERN_ERR "ata: BUG: native mode, n_ports > 2\n"); + n_ports = 2; + /* For now */ } + /* FIXME: Really for ATA it isn't safe because the device may be + multi-purpose and we want to leave it alone if it was already + enabled. Secondly for shared use as Arjan says we want refcounting + + Checking dev->is_enabled is insufficient as this is not set at + boot for the primary video which is BIOS enabled + */ + rc = pci_enable_device(pdev); if (rc) return rc; @@ -4200,6 +4593,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out; } + /* FIXME: Should use platform specific mappers for legacy port ranges */ if (legacy_mode) { if (!request_region(0x1f0, 8, "libata")) { struct resource *conflict, res; @@ -4244,10 +4638,17 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, goto err_out_regions; if (legacy_mode) { - probe_ent = ata_pci_init_legacy_mode(pdev, port, &probe_ent2); - } else - probe_ent = ata_pci_init_native_mode(pdev, port); - if (!probe_ent) { + if (legacy_mode & (1 << 0)) + probe_ent = ata_pci_init_legacy_port(pdev, port[0], 0); + if (legacy_mode & (1 << 1)) + probe_ent2 = ata_pci_init_legacy_port(pdev, port[1], 1); + } else { + if (n_ports == 2) + probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); + else + probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY); + } + if (!probe_ent && !probe_ent2) { rc = -ENOMEM; goto err_out_regions; } @@ -4285,7 +4686,7 @@ err_out: * @pdev: PCI device that was removed * * PCI layer indicates to libata via this hook that - * hot-unplug or module unload event has occured. + * hot-unplug or module unload event has occurred. * Handle this by unregistering all objects associated * with this PCI device. Free those objects. Then finally * release PCI resources and disable device. @@ -4298,46 +4699,15 @@ void ata_pci_remove_one (struct pci_dev *pdev) { struct device *dev = pci_dev_to_dev(pdev); struct ata_host_set *host_set = dev_get_drvdata(dev); - struct ata_port *ap; - unsigned int i; - - for (i = 0; i < host_set->n_ports; i++) { - ap = host_set->ports[i]; - - scsi_remove_host(ap->host); - } - - free_irq(host_set->irq, host_set); - - for (i = 0; i < host_set->n_ports; i++) { - ap = host_set->ports[i]; - - ata_scsi_release(ap->host); - - if ((ap->flags & ATA_FLAG_NO_LEGACY) == 0) { - struct ata_ioports *ioaddr = &ap->ioaddr; - - if (ioaddr->cmd_addr == 0x1f0) - release_region(0x1f0, 8); - else if (ioaddr->cmd_addr == 0x170) - release_region(0x170, 8); - } - - scsi_host_put(ap->host); - } - - if (host_set->ops->host_stop) - host_set->ops->host_stop(host_set); - - kfree(host_set); + ata_host_set_remove(host_set); pci_release_regions(pdev); pci_disable_device(pdev); dev_set_drvdata(dev, NULL); } /* move to PCI subsystem */ -int pci_test_config_bits(struct pci_dev *pdev, struct pci_bits *bits) +int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) { unsigned long tmp = 0; @@ -4390,6 +4760,27 @@ static void __exit ata_exit(void) module_init(ata_init); module_exit(ata_exit); +static unsigned long ratelimit_time; +static spinlock_t ata_ratelimit_lock = SPIN_LOCK_UNLOCKED; + +int ata_ratelimit(void) +{ + int rc; + unsigned long flags; + + spin_lock_irqsave(&ata_ratelimit_lock, flags); + + if (time_after(jiffies, ratelimit_time)) { + rc = 1; + ratelimit_time = jiffies + (HZ/5); + } else + rc = 0; + + spin_unlock_irqrestore(&ata_ratelimit_lock, flags); + + return rc; +} + /* * libata is essentially a library of internal helper functions for * low-level ATA host controller drivers. As such, the API/ABI is @@ -4400,6 +4791,7 @@ module_exit(ata_exit); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_device_add); +EXPORT_SYMBOL_GPL(ata_host_set_remove); EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init_one); EXPORT_SYMBOL_GPL(ata_qc_complete); @@ -4413,7 +4805,6 @@ EXPORT_SYMBOL_GPL(ata_tf_to_fis); EXPORT_SYMBOL_GPL(ata_tf_from_fis); EXPORT_SYMBOL_GPL(ata_check_status); EXPORT_SYMBOL_GPL(ata_altstatus); -EXPORT_SYMBOL_GPL(ata_chk_err); EXPORT_SYMBOL_GPL(ata_exec_command); EXPORT_SYMBOL_GPL(ata_port_start); EXPORT_SYMBOL_GPL(ata_port_stop); @@ -4430,6 +4821,7 @@ EXPORT_SYMBOL_GPL(sata_phy_reset); EXPORT_SYMBOL_GPL(__sata_phy_reset); EXPORT_SYMBOL_GPL(ata_bus_reset); EXPORT_SYMBOL_GPL(ata_port_disable); +EXPORT_SYMBOL_GPL(ata_ratelimit); EXPORT_SYMBOL_GPL(ata_scsi_ioctl); EXPORT_SYMBOL_GPL(ata_scsi_queuecmd); EXPORT_SYMBOL_GPL(ata_scsi_error); @@ -4441,8 +4833,12 @@ EXPORT_SYMBOL_GPL(ata_dev_id_string); EXPORT_SYMBOL_GPL(ata_dev_config); EXPORT_SYMBOL_GPL(ata_scsi_simulate); +EXPORT_SYMBOL_GPL(ata_timing_compute); +EXPORT_SYMBOL_GPL(ata_timing_merge); + #ifdef CONFIG_PCI EXPORT_SYMBOL_GPL(pci_test_config_bits); +EXPORT_SYMBOL_GPL(ata_pci_host_stop); EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); EXPORT_SYMBOL_GPL(ata_pci_init_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 794fb559efb0..248baae96486 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -1,25 +1,36 @@ /* - libata-scsi.c - helper library for ATA - - Copyright 2003-2004 Red Hat, Inc. All rights reserved. - Copyright 2003-2004 Jeff Garzik - - The contents of this file are subject to the Open - Software License version 1.1 that can be found at - http://www.opensource.org/licenses/osl-1.1.txt and is included herein - by reference. - - Alternatively, the contents of this file may be used under the terms - of the GNU General Public License version 2 (the "GPL") as distributed - in the kernel source COPYING file, in which case the provisions of - the GPL are applicable instead of the above. If you wish to allow - the use of your version of this file only under the terms of the - GPL and not to allow others to use your version of this file under - the OSL, indicate your decision by deleting the provisions above and - replace them with the notice and other provisions required by the GPL. - If you do not delete the provisions above, a recipient may use your - version of this file under either the OSL or the GPL. - + * libata-scsi.c - helper library for ATA + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org + * on emails. + * + * Copyright 2003-2004 Red Hat, Inc. All rights reserved. + * Copyright 2003-2004 Jeff Garzik + * + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available from + * - http://www.t10.org/ + * - http://www.t13.org/ + * */ #include <linux/kernel.h> @@ -28,15 +39,66 @@ #include <scsi/scsi.h> #include "scsi.h" #include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> #include <linux/libata.h> +#include <linux/hdreg.h> #include <asm/uaccess.h> #include "libata.h" -typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd); -static struct ata_device * -ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev); +#define SECTOR_SIZE 512 +typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, const u8 *scsicmd); +static struct ata_device * +ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); + +#define RW_RECOVERY_MPAGE 0x1 +#define RW_RECOVERY_MPAGE_LEN 12 +#define CACHE_MPAGE 0x8 +#define CACHE_MPAGE_LEN 20 +#define CONTROL_MPAGE 0xa +#define CONTROL_MPAGE_LEN 12 +#define ALL_MPAGES 0x3f +#define ALL_SUB_MPAGES 0xff + + +static const u8 def_rw_recovery_mpage[] = { + RW_RECOVERY_MPAGE, + RW_RECOVERY_MPAGE_LEN - 2, + (1 << 7) | /* AWRE, sat-r06 say it shall be 0 */ + (1 << 6), /* ARRE (auto read reallocation) */ + 0, /* read retry count */ + 0, 0, 0, 0, + 0, /* write retry count */ + 0, 0, 0 +}; + +static const u8 def_cache_mpage[CACHE_MPAGE_LEN] = { + CACHE_MPAGE, + CACHE_MPAGE_LEN - 2, + 0, /* contains WCE, needs to be 0 for logic */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, /* contains DRA, needs to be 0 for logic */ + 0, 0, 0, 0, 0, 0, 0 +}; + +static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { + CONTROL_MPAGE, + CONTROL_MPAGE_LEN - 2, + 2, /* DSENSE=0, GLTSD=1 */ + 0, /* [QAM+QERR may be 1, see 05-359r1] */ + 0, 0, 0, 0, 0xff, 0xff, + 0, 30 /* extended self test time, see 05-359r1 */ +}; + + +static void ata_scsi_invalid_field(struct scsi_cmnd *cmd, + void (*done)(struct scsi_cmnd *)) +{ + ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + done(cmd); +} /** * ata_std_bios_param - generic bios head/sector/cylinder calculator used by sd. @@ -67,6 +129,150 @@ int ata_std_bios_param(struct scsi_device *sdev, struct block_device *bdev, return 0; } +/** + * ata_cmd_ioctl - Handler for HDIO_DRIVE_CMD ioctl + * @dev: Device to whom we are issuing command + * @arg: User provided data for issuing command + * + * LOCKING: + * Defined by the SCSI layer. We don't really care. + * + * RETURNS: + * Zero on success, negative errno on error. + */ + +int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg) +{ + int rc = 0; + u8 scsi_cmd[MAX_COMMAND_SIZE]; + u8 args[4], *argbuf = NULL; + int argsize = 0; + struct scsi_request *sreq; + + if (NULL == (void *)arg) + return -EINVAL; + + if (copy_from_user(args, arg, sizeof(args))) + return -EFAULT; + + sreq = scsi_allocate_request(scsidev, GFP_KERNEL); + if (!sreq) + return -EINTR; + + memset(scsi_cmd, 0, sizeof(scsi_cmd)); + + if (args[3]) { + argsize = SECTOR_SIZE * args[3]; + argbuf = kmalloc(argsize, GFP_KERNEL); + if (argbuf == NULL) { + rc = -ENOMEM; + goto error; + } + + scsi_cmd[1] = (4 << 1); /* PIO Data-in */ + scsi_cmd[2] = 0x0e; /* no off.line or cc, read from dev, + block count in sector count field */ + sreq->sr_data_direction = DMA_FROM_DEVICE; + } else { + scsi_cmd[1] = (3 << 1); /* Non-data */ + /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ + sreq->sr_data_direction = DMA_NONE; + } + + scsi_cmd[0] = ATA_16; + + scsi_cmd[4] = args[2]; + if (args[0] == WIN_SMART) { /* hack -- ide driver does this too... */ + scsi_cmd[6] = args[3]; + scsi_cmd[8] = args[1]; + scsi_cmd[10] = 0x4f; + scsi_cmd[12] = 0xc2; + } else { + scsi_cmd[6] = args[1]; + } + scsi_cmd[14] = args[0]; + + /* Good values for timeout and retries? Values below + from scsi_ioctl_send_command() for default case... */ + scsi_wait_req(sreq, scsi_cmd, argbuf, argsize, (10*HZ), 5); + + if (sreq->sr_result) { + rc = -EIO; + goto error; + } + + /* Need code to retrieve data from check condition? */ + + if ((argbuf) + && copy_to_user((void *)(arg + sizeof(args)), argbuf, argsize)) + rc = -EFAULT; +error: + scsi_release_request(sreq); + + if (argbuf) + kfree(argbuf); + + return rc; +} + +/** + * ata_task_ioctl - Handler for HDIO_DRIVE_TASK ioctl + * @dev: Device to whom we are issuing command + * @arg: User provided data for issuing command + * + * LOCKING: + * Defined by the SCSI layer. We don't really care. + * + * RETURNS: + * Zero on success, negative errno on error. + */ +int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) +{ + int rc = 0; + u8 scsi_cmd[MAX_COMMAND_SIZE]; + u8 args[7]; + struct scsi_request *sreq; + + if (NULL == (void *)arg) + return -EINVAL; + + if (copy_from_user(args, arg, sizeof(args))) + return -EFAULT; + + memset(scsi_cmd, 0, sizeof(scsi_cmd)); + scsi_cmd[0] = ATA_16; + scsi_cmd[1] = (3 << 1); /* Non-data */ + /* scsi_cmd[2] is already 0 -- no off.line, cc, or data xfer */ + scsi_cmd[4] = args[1]; + scsi_cmd[6] = args[2]; + scsi_cmd[8] = args[3]; + scsi_cmd[10] = args[4]; + scsi_cmd[12] = args[5]; + scsi_cmd[14] = args[0]; + + sreq = scsi_allocate_request(scsidev, GFP_KERNEL); + if (!sreq) { + rc = -EINTR; + goto error; + } + + sreq->sr_data_direction = DMA_NONE; + /* Good values for timeout and retries? Values below + from scsi_ioctl_send_command() for default case... */ + scsi_wait_req(sreq, scsi_cmd, NULL, 0, (10*HZ), 5); + + if (sreq->sr_result) { + rc = -EIO; + goto error; + } + + /* Need code to retrieve data from check condition? */ + +error: + scsi_release_request(sreq); + return rc; +} + int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg) { struct ata_port *ap; @@ -96,6 +302,16 @@ int ata_scsi_ioctl(struct scsi_device *scsidev, int cmd, void __user *arg) return -EINVAL; return 0; + case HDIO_DRIVE_CMD: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + return ata_cmd_ioctl(scsidev, arg); + + case HDIO_DRIVE_TASK: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + return ata_task_ioctl(scsidev, arg); + default: rc = -ENOTTY; break; @@ -154,24 +370,70 @@ struct ata_queued_cmd *ata_scsi_qc_new(struct ata_port *ap, } /** + * ata_dump_status - user friendly display of error info + * @id: id of the port in question + * @tf: ptr to filled out taskfile + * + * Decode and dump the ATA error/status registers for the user so + * that they have some idea what really happened at the non + * make-believe layer. + * + * LOCKING: + * inherited from caller + */ +void ata_dump_status(unsigned id, struct ata_taskfile *tf) +{ + u8 stat = tf->command, err = tf->feature; + + printk(KERN_WARNING "ata%u: status=0x%02x { ", id, stat); + if (stat & ATA_BUSY) { + printk("Busy }\n"); /* Data is not valid in this case */ + } else { + if (stat & 0x40) printk("DriveReady "); + if (stat & 0x20) printk("DeviceFault "); + if (stat & 0x10) printk("SeekComplete "); + if (stat & 0x08) printk("DataRequest "); + if (stat & 0x04) printk("CorrectedError "); + if (stat & 0x02) printk("Index "); + if (stat & 0x01) printk("Error "); + printk("}\n"); + + if (err) { + printk(KERN_WARNING "ata%u: error=0x%02x { ", id, err); + if (err & 0x04) printk("DriveStatusError "); + if (err & 0x80) { + if (err & 0x04) printk("BadCRC "); + else printk("Sector "); + } + if (err & 0x40) printk("UncorrectableError "); + if (err & 0x10) printk("SectorIdNotFound "); + if (err & 0x02) printk("TrackZeroNotFound "); + if (err & 0x01) printk("AddrMarkNotFound "); + printk("}\n"); + } + } +} + +/** * ata_to_sense_error - convert ATA error to SCSI error - * @qc: Command that we are erroring out * @drv_stat: value contained in ATA status register + * @drv_err: value contained in ATA error register + * @sk: the sense key we'll fill out + * @asc: the additional sense code we'll fill out + * @ascq: the additional sense code qualifier we'll fill out * - * Converts an ATA error into a SCSI error. While we are at it - * we decode and dump the ATA error for the user so that they - * have some idea what really happened at the non make-believe - * layer. + * Converts an ATA error into a SCSI error. Fill out pointers to + * SK, ASC, and ASCQ bytes for later use in fixed or descriptor + * format sense blocks. * * LOCKING: * spin_lock_irqsave(host_set lock) */ - -void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) +void ata_to_sense_error(unsigned id, u8 drv_stat, u8 drv_err, u8 *sk, u8 *asc, + u8 *ascq) { - struct scsi_cmnd *cmd = qc->scsicmd; - u8 err = 0; - unsigned char *sb = cmd->sense_buffer; + int i; + /* Based on the 3ware driver translation table */ static unsigned char sense_table[][4] = { /* BBD|ECC|ID|MAR */ @@ -212,105 +474,192 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) {0x04, RECOVERED_ERROR, 0x11, 0x00}, // Recovered ECC error Medium error, recovered {0xFF, 0xFF, 0xFF, 0xFF}, // END mark }; - int i = 0; - - cmd->result = SAM_STAT_CHECK_CONDITION; /* * Is this an error we can process/parse */ + if (drv_stat & ATA_BUSY) { + drv_err = 0; /* Ignore the err bits, they're invalid */ + } - if(drv_stat & ATA_ERR) - /* Read the err bits */ - err = ata_chk_err(qc->ap); + if (drv_err) { + /* Look for drv_err */ + for (i = 0; sense_table[i][0] != 0xFF; i++) { + /* Look for best matches first */ + if ((sense_table[i][0] & drv_err) == + sense_table[i][0]) { + *sk = sense_table[i][1]; + *asc = sense_table[i][2]; + *ascq = sense_table[i][3]; + goto translate_done; + } + } + /* No immediate match */ + printk(KERN_WARNING "ata%u: no sense translation for " + "error 0x%02x\n", id, drv_err); + } - /* Display the ATA level error info */ + /* Fall back to interpreting status bits */ + for (i = 0; stat_table[i][0] != 0xFF; i++) { + if (stat_table[i][0] & drv_stat) { + *sk = stat_table[i][1]; + *asc = stat_table[i][2]; + *ascq = stat_table[i][3]; + goto translate_done; + } + } + /* No error? Undecoded? */ + printk(KERN_WARNING "ata%u: no sense translation for status: 0x%02x\n", + id, drv_stat); + + /* For our last chance pick, use medium read error because + * it's much more common than an ATA drive telling you a write + * has failed. + */ + *sk = MEDIUM_ERROR; + *asc = 0x11; /* "unrecovered read error" */ + *ascq = 0x04; /* "auto-reallocation failed" */ + + translate_done: + printk(KERN_ERR "ata%u: translated ATA stat/err 0x%02x/%02x to " + "SCSI SK/ASC/ASCQ 0x%x/%02x/%02x\n", id, drv_stat, drv_err, + *sk, *asc, *ascq); + return; +} + +/* + * ata_gen_ata_desc_sense - Generate check condition sense block. + * @qc: Command that completed. + * + * This function is specific to the ATA descriptor format sense + * block specified for the ATA pass through commands. Regardless + * of whether the command errored or not, return a sense + * block. Copy all controller registers into the sense + * block. Clear sense key, ASC & ASCQ if there is no error. + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + */ +void ata_gen_ata_desc_sense(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + struct ata_taskfile *tf = &qc->tf; + unsigned char *sb = cmd->sense_buffer; + unsigned char *desc = sb + 8; + + memset(sb, 0, SCSI_SENSE_BUFFERSIZE); + + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + + /* + * Read the controller registers. + */ + assert(NULL != qc->ap->ops->tf_read); + qc->ap->ops->tf_read(qc->ap, tf); - printk(KERN_WARNING "ata%u: status=0x%02x { ", qc->ap->id, drv_stat); - if(drv_stat & 0x80) - { - printk("Busy "); - err = 0; /* Data is not valid in this case */ + /* + * Use ata_to_sense_error() to map status register bits + * onto sense key, asc & ascq. + */ + if (tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { + ata_to_sense_error(qc->ap->id, tf->command, tf->feature, + &sb[1], &sb[2], &sb[3]); + sb[1] &= 0x0f; } - else { - if(drv_stat & 0x40) printk("DriveReady "); - if(drv_stat & 0x20) printk("DeviceFault "); - if(drv_stat & 0x10) printk("SeekComplete "); - if(drv_stat & 0x08) printk("DataRequest "); - if(drv_stat & 0x04) printk("CorrectedError "); - if(drv_stat & 0x02) printk("Index "); - if(drv_stat & 0x01) printk("Error "); + + /* + * Sense data is current and format is descriptor. + */ + sb[0] = 0x72; + + desc[0] = 0x09; + + /* + * Set length of additional sense data. + * Since we only populate descriptor 0, the total + * length is the same (fixed) length as descriptor 0. + */ + desc[1] = sb[7] = 14; + + /* + * Copy registers into sense buffer. + */ + desc[2] = 0x00; + desc[3] = tf->feature; /* == error reg */ + desc[5] = tf->nsect; + desc[7] = tf->lbal; + desc[9] = tf->lbam; + desc[11] = tf->lbah; + desc[12] = tf->device; + desc[13] = tf->command; /* == status reg */ + + /* + * Fill in Extend bit, and the high order bytes + * if applicable. + */ + if (tf->flags & ATA_TFLAG_LBA48) { + desc[2] |= 0x01; + desc[4] = tf->hob_nsect; + desc[6] = tf->hob_lbal; + desc[8] = tf->hob_lbam; + desc[10] = tf->hob_lbah; } - printk("}\n"); - - if(err) - { - printk(KERN_WARNING "ata%u: error=0x%02x { ", qc->ap->id, err); - if(err & 0x04) printk("DriveStatusError "); - if(err & 0x80) - { - if(err & 0x04) - printk("BadCRC "); - else - printk("Sector "); - } - if(err & 0x40) printk("UncorrectableError "); - if(err & 0x10) printk("SectorIdNotFound "); - if(err & 0x02) printk("TrackZeroNotFound "); - if(err & 0x01) printk("AddrMarkNotFound "); - printk("}\n"); +} + +/** + * ata_gen_fixed_sense - generate a SCSI fixed sense block + * @qc: Command that we are erroring out + * + * Leverage ata_to_sense_error() to give us the codes. Fit our + * LBA in here if there's room. + * + * LOCKING: + * inherited from caller + */ +void ata_gen_fixed_sense(struct ata_queued_cmd *qc) +{ + struct scsi_cmnd *cmd = qc->scsicmd; + struct ata_taskfile *tf = &qc->tf; + unsigned char *sb = cmd->sense_buffer; + + memset(sb, 0, SCSI_SENSE_BUFFERSIZE); + + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + + /* + * Read the controller registers. + */ + assert(NULL != qc->ap->ops->tf_read); + qc->ap->ops->tf_read(qc->ap, tf); - /* Should we dump sector info here too ?? */ + /* + * Use ata_to_sense_error() to map status register bits + * onto sense key, asc & ascq. + */ + if (tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { + ata_to_sense_error(qc->ap->id, tf->command, tf->feature, + &sb[2], &sb[12], &sb[13]); + sb[2] &= 0x0f; } + sb[0] = 0x70; + sb[7] = 0x0a; - /* Look for err */ - while(sense_table[i][0] != 0xFF) - { - /* Look for best matches first */ - if((sense_table[i][0] & err) == sense_table[i][0]) - { - sb[0] = 0x70; - sb[2] = sense_table[i][1]; - sb[7] = 0x0a; - sb[12] = sense_table[i][2]; - sb[13] = sense_table[i][3]; - return; - } - i++; + if (tf->flags & ATA_TFLAG_LBA48) { + /* TODO: find solution for LBA48 descriptors */ } - /* No immediate match */ - if(err) - printk(KERN_DEBUG "ata%u: no sense translation for 0x%02x\n", qc->ap->id, err); - i = 0; - /* Fall back to interpreting status bits */ - while(stat_table[i][0] != 0xFF) - { - if(stat_table[i][0] & drv_stat) - { - sb[0] = 0x70; - sb[2] = stat_table[i][1]; - sb[7] = 0x0a; - sb[12] = stat_table[i][2]; - sb[13] = stat_table[i][3]; - return; - } - i++; + else if (tf->flags & ATA_TFLAG_LBA) { + /* A small (28b) LBA will fit in the 32b info field */ + sb[0] |= 0x80; /* set valid bit */ + sb[3] = tf->device & 0x0f; + sb[4] = tf->lbah; + sb[5] = tf->lbam; + sb[6] = tf->lbal; } - /* No error ?? */ - printk(KERN_ERR "ata%u: called with no error (%02X)!\n", qc->ap->id, drv_stat); - /* additional-sense-code[-qualifier] */ - sb[0] = 0x70; - sb[2] = MEDIUM_ERROR; - sb[7] = 0x0A; - if (cmd->sc_data_direction == DMA_FROM_DEVICE) { - sb[12] = 0x11; /* "unrecovered read error" */ - sb[13] = 0x04; - } else { - sb[12] = 0x0C; /* "write error - */ - sb[13] = 0x02; /* auto-reallocation failed" */ + else { + /* TODO: C/H/S */ } } @@ -385,12 +734,83 @@ int ata_scsi_error(struct Scsi_Host *host) * appropriate place */ host->host_failed--; + INIT_LIST_HEAD(&host->eh_cmd_q); DPRINTK("EXIT\n"); return 0; } /** + * ata_scsi_start_stop_xlat - Translate SCSI START STOP UNIT command + * @qc: Storage for translated ATA taskfile + * @scsicmd: SCSI command to translate + * + * Sets up an ATA taskfile to issue STANDBY (to stop) or READ VERIFY + * (to start). Perhaps these commands should be preceded by + * CHECK POWER MODE to see what power mode the device is already in. + * [See SAT revision 5 at www.t10.org] + * + * LOCKING: + * spin_lock_irqsave(host_set lock) + * + * RETURNS: + * Zero on success, non-zero on error. + */ + +static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, + const u8 *scsicmd) +{ + struct ata_taskfile *tf = &qc->tf; + + tf->flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf->protocol = ATA_PROT_NODATA; + if (scsicmd[1] & 0x1) { + ; /* ignore IMMED bit, violates sat-r05 */ + } + if (scsicmd[4] & 0x2) + goto invalid_fld; /* LOEJ bit set not supported */ + if (((scsicmd[4] >> 4) & 0xf) != 0) + goto invalid_fld; /* power conditions not supported */ + if (scsicmd[4] & 0x1) { + tf->nsect = 1; /* 1 sector, lba=0 */ + + if (qc->dev->flags & ATA_DFLAG_LBA) { + qc->tf.flags |= ATA_TFLAG_LBA; + + tf->lbah = 0x0; + tf->lbam = 0x0; + tf->lbal = 0x0; + tf->device |= ATA_LBA; + } else { + /* CHS */ + tf->lbal = 0x1; /* sect */ + tf->lbam = 0x0; /* cyl low */ + tf->lbah = 0x0; /* cyl high */ + } + + tf->command = ATA_CMD_VERIFY; /* READ VERIFY */ + } else { + tf->nsect = 0; /* time period value (0 implies now) */ + tf->command = ATA_CMD_STANDBY; + /* Consider: ATA STANDBY IMMEDIATE command */ + } + /* + * Standby and Idle condition timers could be implemented but that + * would require libata to implement the Power condition mode page + * and allow the user to change it. Changing mode pages requires + * MODE SELECT to be implemented. + */ + + return 0; + +invalid_fld: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; +} + + +/** * ata_scsi_flush_xlat - Translate SCSI SYNCHRONIZE CACHE command * @qc: Storage for translated ATA taskfile * @scsicmd: SCSI command to translate (ignored) @@ -405,14 +825,14 @@ int ata_scsi_error(struct Scsi_Host *host) * Zero on success, non-zero on error. */ -static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; tf->flags |= ATA_TFLAG_DEVICE; tf->protocol = ATA_PROT_NODATA; - if ((tf->flags & ATA_TFLAG_LBA48) && + if ((qc->dev->flags & ATA_DFLAG_LBA48) && (ata_id_has_flush_ext(qc->dev->id))) tf->command = ATA_CMD_FLUSH_EXT; else @@ -422,6 +842,99 @@ static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) } /** + * scsi_6_lba_len - Get LBA and transfer length + * @scsicmd: SCSI command to translate + * + * Calculate LBA and transfer length for 6-byte commands. + * + * RETURNS: + * @plba: the LBA + * @plen: the transfer length + */ + +static void scsi_6_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) +{ + u64 lba = 0; + u32 len = 0; + + VPRINTK("six-byte command\n"); + + lba |= ((u64)scsicmd[2]) << 8; + lba |= ((u64)scsicmd[3]); + + len |= ((u32)scsicmd[4]); + + *plba = lba; + *plen = len; +} + +/** + * scsi_10_lba_len - Get LBA and transfer length + * @scsicmd: SCSI command to translate + * + * Calculate LBA and transfer length for 10-byte commands. + * + * RETURNS: + * @plba: the LBA + * @plen: the transfer length + */ + +static void scsi_10_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) +{ + u64 lba = 0; + u32 len = 0; + + VPRINTK("ten-byte command\n"); + + lba |= ((u64)scsicmd[2]) << 24; + lba |= ((u64)scsicmd[3]) << 16; + lba |= ((u64)scsicmd[4]) << 8; + lba |= ((u64)scsicmd[5]); + + len |= ((u32)scsicmd[7]) << 8; + len |= ((u32)scsicmd[8]); + + *plba = lba; + *plen = len; +} + +/** + * scsi_16_lba_len - Get LBA and transfer length + * @scsicmd: SCSI command to translate + * + * Calculate LBA and transfer length for 16-byte commands. + * + * RETURNS: + * @plba: the LBA + * @plen: the transfer length + */ + +static void scsi_16_lba_len(const u8 *scsicmd, u64 *plba, u32 *plen) +{ + u64 lba = 0; + u32 len = 0; + + VPRINTK("sixteen-byte command\n"); + + lba |= ((u64)scsicmd[2]) << 56; + lba |= ((u64)scsicmd[3]) << 48; + lba |= ((u64)scsicmd[4]) << 40; + lba |= ((u64)scsicmd[5]) << 32; + lba |= ((u64)scsicmd[6]) << 24; + lba |= ((u64)scsicmd[7]) << 16; + lba |= ((u64)scsicmd[8]) << 8; + lba |= ((u64)scsicmd[9]); + + len |= ((u32)scsicmd[10]) << 24; + len |= ((u32)scsicmd[11]) << 16; + len |= ((u32)scsicmd[12]) << 8; + len |= ((u32)scsicmd[13]); + + *plba = lba; + *plen = len; +} + +/** * ata_scsi_verify_xlat - Translate SCSI VERIFY command into an ATA one * @qc: Storage for translated ATA taskfile * @scsicmd: SCSI command to translate @@ -435,82 +948,110 @@ static unsigned int ata_scsi_flush_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) * Zero on success, non-zero on error. */ -static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; - unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + struct ata_device *dev = qc->dev; u64 dev_sectors = qc->dev->n_sectors; - u64 sect = 0; - u32 n_sect = 0; + u64 block; + u32 n_block; tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->protocol = ATA_PROT_NODATA; - tf->device |= ATA_LBA; - if (scsicmd[0] == VERIFY) { - sect |= ((u64)scsicmd[2]) << 24; - sect |= ((u64)scsicmd[3]) << 16; - sect |= ((u64)scsicmd[4]) << 8; - sect |= ((u64)scsicmd[5]); + if (scsicmd[0] == VERIFY) + scsi_10_lba_len(scsicmd, &block, &n_block); + else if (scsicmd[0] == VERIFY_16) + scsi_16_lba_len(scsicmd, &block, &n_block); + else + goto invalid_fld; - n_sect |= ((u32)scsicmd[7]) << 8; - n_sect |= ((u32)scsicmd[8]); - } + if (!n_block) + goto nothing_to_do; + if (block >= dev_sectors) + goto out_of_range; + if ((block + n_block) > dev_sectors) + goto out_of_range; - else if (scsicmd[0] == VERIFY_16) { - sect |= ((u64)scsicmd[2]) << 56; - sect |= ((u64)scsicmd[3]) << 48; - sect |= ((u64)scsicmd[4]) << 40; - sect |= ((u64)scsicmd[5]) << 32; - sect |= ((u64)scsicmd[6]) << 24; - sect |= ((u64)scsicmd[7]) << 16; - sect |= ((u64)scsicmd[8]) << 8; - sect |= ((u64)scsicmd[9]); - - n_sect |= ((u32)scsicmd[10]) << 24; - n_sect |= ((u32)scsicmd[11]) << 16; - n_sect |= ((u32)scsicmd[12]) << 8; - n_sect |= ((u32)scsicmd[13]); - } + if (dev->flags & ATA_DFLAG_LBA) { + tf->flags |= ATA_TFLAG_LBA; - else - return 1; + if (dev->flags & ATA_DFLAG_LBA48) { + if (n_block > (64 * 1024)) + goto invalid_fld; - if (!n_sect) - return 1; - if (sect >= dev_sectors) - return 1; - if ((sect + n_sect) > dev_sectors) - return 1; - if (lba48) { - if (n_sect > (64 * 1024)) - return 1; - } else { - if (n_sect > 256) - return 1; - } + /* use LBA48 */ + tf->flags |= ATA_TFLAG_LBA48; + tf->command = ATA_CMD_VERIFY_EXT; + + tf->hob_nsect = (n_block >> 8) & 0xff; - if (lba48) { - tf->command = ATA_CMD_VERIFY_EXT; + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + if (n_block > 256) + goto invalid_fld; + + /* use LBA28 */ + tf->command = ATA_CMD_VERIFY; + + tf->device |= (block >> 24) & 0xf; + } - tf->hob_nsect = (n_sect >> 8) & 0xff; + tf->nsect = n_block & 0xff; - tf->hob_lbah = (sect >> 40) & 0xff; - tf->hob_lbam = (sect >> 32) & 0xff; - tf->hob_lbal = (sect >> 24) & 0xff; + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; + + tf->device |= ATA_LBA; } else { + /* CHS */ + u32 sect, head, cyl, track; + + if (n_block > 256) + goto invalid_fld; + + /* Convert LBA to CHS */ + track = (u32)block / dev->sectors; + cyl = track / dev->heads; + head = track % dev->heads; + sect = (u32)block % dev->sectors + 1; + + DPRINTK("block %u track %u cyl %u head %u sect %u\n", + (u32)block, track, cyl, head, sect); + + /* Check whether the converted CHS can fit. + Cylinder: 0-65535 + Head: 0-15 + Sector: 1-255*/ + if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) + goto out_of_range; + tf->command = ATA_CMD_VERIFY; - - tf->device |= (sect >> 24) & 0xf; + tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ + tf->lbal = sect; + tf->lbam = cyl; + tf->lbah = cyl >> 8; + tf->device |= head; } - tf->nsect = n_sect & 0xff; + return 0; - tf->lbah = (sect >> 16) & 0xff; - tf->lbam = (sect >> 8) & 0xff; - tf->lbal = sect & 0xff; +invalid_fld: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; - return 0; +out_of_range: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x21, 0x0); + /* "Logical Block Address out of range" */ + return 1; + +nothing_to_do: + qc->scsicmd->result = SAM_STAT_GOOD; + return 1; } /** @@ -533,107 +1074,175 @@ static unsigned int ata_scsi_verify_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) * Zero on success, non-zero on error. */ -static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; - unsigned int lba48 = tf->flags & ATA_TFLAG_LBA48; + struct ata_device *dev = qc->dev; + u64 block; + u32 n_block; tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf->protocol = qc->dev->xfer_protocol; - tf->device |= ATA_LBA; - if (scsicmd[0] == READ_10 || scsicmd[0] == READ_6 || - scsicmd[0] == READ_16) { - tf->command = qc->dev->read_cmd; - } else { - tf->command = qc->dev->write_cmd; + if (scsicmd[0] == WRITE_10 || scsicmd[0] == WRITE_6 || + scsicmd[0] == WRITE_16) tf->flags |= ATA_TFLAG_WRITE; + + /* Calculate the SCSI LBA and transfer length. */ + switch (scsicmd[0]) { + case READ_10: + case WRITE_10: + scsi_10_lba_len(scsicmd, &block, &n_block); + break; + case READ_6: + case WRITE_6: + scsi_6_lba_len(scsicmd, &block, &n_block); + + /* for 6-byte r/w commands, transfer length 0 + * means 256 blocks of data, not 0 block. + */ + if (!n_block) + n_block = 256; + break; + case READ_16: + case WRITE_16: + scsi_16_lba_len(scsicmd, &block, &n_block); + break; + default: + DPRINTK("no-byte command\n"); + goto invalid_fld; } - if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) { - if (lba48) { - tf->hob_nsect = scsicmd[7]; - tf->hob_lbal = scsicmd[2]; + /* Check and compose ATA command */ + if (!n_block) + /* For 10-byte and 16-byte SCSI R/W commands, transfer + * length 0 means transfer 0 block of data. + * However, for ATA R/W commands, sector count 0 means + * 256 or 65536 sectors, not 0 sectors as in SCSI. + */ + goto nothing_to_do; - qc->nsect = ((unsigned int)scsicmd[7] << 8) | - scsicmd[8]; - } else { - /* if we don't support LBA48 addressing, the request - * -may- be too large. */ - if ((scsicmd[2] & 0xf0) || scsicmd[7]) - return 1; + if (dev->flags & ATA_DFLAG_LBA) { + tf->flags |= ATA_TFLAG_LBA; - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= scsicmd[2]; + if (dev->flags & ATA_DFLAG_LBA48) { + /* The request -may- be too large for LBA48. */ + if ((block >> 48) || (n_block > 65536)) + goto out_of_range; - qc->nsect = scsicmd[8]; - } + /* use LBA48 */ + tf->flags |= ATA_TFLAG_LBA48; - tf->nsect = scsicmd[8]; - tf->lbal = scsicmd[5]; - tf->lbam = scsicmd[4]; - tf->lbah = scsicmd[3]; + tf->hob_nsect = (n_block >> 8) & 0xff; - VPRINTK("ten-byte command\n"); - return 0; - } + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + /* use LBA28 */ - if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { - qc->nsect = tf->nsect = scsicmd[4]; - tf->lbal = scsicmd[3]; - tf->lbam = scsicmd[2]; - tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */ + /* The request -may- be too large for LBA28. */ + if ((block >> 28) || (n_block > 256)) + goto out_of_range; - VPRINTK("six-byte command\n"); - return 0; - } + tf->device |= (block >> 24) & 0xf; + } - if (scsicmd[0] == READ_16 || scsicmd[0] == WRITE_16) { - /* rule out impossible LBAs and sector counts */ - if (scsicmd[2] || scsicmd[3] || scsicmd[10] || scsicmd[11]) - return 1; + ata_rwcmd_protocol(qc); - if (lba48) { - tf->hob_nsect = scsicmd[12]; - tf->hob_lbal = scsicmd[6]; - tf->hob_lbam = scsicmd[5]; - tf->hob_lbah = scsicmd[4]; + qc->nsect = n_block; + tf->nsect = n_block & 0xff; - qc->nsect = ((unsigned int)scsicmd[12] << 8) | - scsicmd[13]; - } else { - /* once again, filter out impossible non-zero values */ - if (scsicmd[4] || scsicmd[5] || scsicmd[12] || - (scsicmd[6] & 0xf0)) - return 1; + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= scsicmd[6]; + tf->device |= ATA_LBA; + } else { + /* CHS */ + u32 sect, head, cyl, track; - qc->nsect = scsicmd[13]; - } + /* The request -may- be too large for CHS addressing. */ + if ((block >> 28) || (n_block > 256)) + goto out_of_range; - tf->nsect = scsicmd[13]; - tf->lbal = scsicmd[9]; - tf->lbam = scsicmd[8]; - tf->lbah = scsicmd[7]; + ata_rwcmd_protocol(qc); - VPRINTK("sixteen-byte command\n"); - return 0; + /* Convert LBA to CHS */ + track = (u32)block / dev->sectors; + cyl = track / dev->heads; + head = track % dev->heads; + sect = (u32)block % dev->sectors + 1; + + DPRINTK("block %u track %u cyl %u head %u sect %u\n", + (u32)block, track, cyl, head, sect); + + /* Check whether the converted CHS can fit. + Cylinder: 0-65535 + Head: 0-15 + Sector: 1-255*/ + if ((cyl >> 16) || (head >> 4) || (sect >> 8) || (!sect)) + goto out_of_range; + + qc->nsect = n_block; + tf->nsect = n_block & 0xff; /* Sector count 0 means 256 sectors */ + tf->lbal = sect; + tf->lbam = cyl; + tf->lbah = cyl >> 8; + tf->device |= head; } - DPRINTK("no-byte command\n"); + return 0; + +invalid_fld: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; + +out_of_range: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x21, 0x0); + /* "Logical Block Address out of range" */ + return 1; + +nothing_to_do: + qc->scsicmd->result = SAM_STAT_GOOD; return 1; } -static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) +static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, + unsigned int err_mask) { struct scsi_cmnd *cmd = qc->scsicmd; + u8 *cdb = cmd->cmnd; + int need_sense = (err_mask != 0); + + /* For ATA pass thru (SAT) commands, generate a sense block if + * user mandated it or if there's an error. Note that if we + * generate because the user forced us to, a check condition + * is generated and the ATA register values are returned + * whether the command completed successfully or not. If there + * was no error, SK, ASC and ASCQ will all be zero. + */ + if (((cdb[0] == ATA_16) || (cdb[0] == ATA_12)) && + ((cdb[2] & 0x20) || need_sense)) { + ata_gen_ata_desc_sense(qc); + } else { + if (!need_sense) { + cmd->result = SAM_STAT_GOOD; + } else { + /* TODO: decide which descriptor format to use + * for 48b LBA devices and call that here + * instead of the fixed desc, which is only + * good for smaller LBA (and maybe CHS?) + * devices. + */ + ata_gen_fixed_sense(qc); + } + } - if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) - ata_to_sense_error(qc, drv_stat); - else - cmd->result = SAM_STAT_GOOD; + if (need_sense) { + /* The ata_gen_..._sense routines fill in tf */ + ata_dump_status(qc->ap->id, &qc->tf); + } qc->scsidone(cmd); @@ -655,6 +1264,12 @@ static int ata_scsi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) * This function sets up an ata_queued_cmd structure for the * SCSI command, and sends that ata_queued_cmd to the hardware. * + * The xlat_func argument (actor) returns 0 if ready to execute + * ATA command, else 1 to finish translation. If 1 is returned + * then cmd->result (and possibly cmd->sense_buffer) are assumed + * to be set reflecting an error condition or clean (early) + * termination. + * * LOCKING: * spin_lock_irqsave(host_set lock) */ @@ -671,7 +1286,7 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev, qc = ata_scsi_qc_new(ap, dev, cmd, done); if (!qc) - return; + goto err_mem; /* data is present; dma-map it */ if (cmd->sc_data_direction == DMA_FROM_DEVICE || @@ -679,7 +1294,7 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev, if (unlikely(cmd->request_bufflen < 1)) { printk(KERN_WARNING "ata%u(%u): WARNING: zero len r/w req\n", ap->id, dev->devno); - goto err_out; + goto err_did; } if (cmd->use_sg) @@ -694,19 +1309,28 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev, qc->complete_fn = ata_scsi_qc_complete; if (xlat_func(qc, scsicmd)) - goto err_out; + goto early_finish; /* select device, send command to hardware */ if (ata_qc_issue(qc)) - goto err_out; + goto err_did; VPRINTK("EXIT\n"); return; -err_out: +early_finish: + ata_qc_free(qc); + done(cmd); + DPRINTK("EXIT - early finish (good or error)\n"); + return; + +err_did: ata_qc_free(qc); - ata_bad_cdb(cmd, done); - DPRINTK("EXIT - badcmd\n"); +err_mem: + cmd->result = (DID_ERROR << 16); + done(cmd); + DPRINTK("EXIT - internal\n"); + return; } /** @@ -773,7 +1397,8 @@ static inline void ata_scsi_rbuf_put(struct scsi_cmnd *cmd, u8 *buf) * Mapping the response buffer, calling the command's handler, * and handling the handler's return value. This return value * indicates whether the handler wishes the SCSI command to be - * completed successfully, or not. + * completed successfully (0), or not (in which case cmd->result + * and sense buffer are assumed to be set). * * LOCKING: * spin_lock_irqsave(host_set lock) @@ -792,12 +1417,9 @@ void ata_scsi_rbuf_fill(struct ata_scsi_args *args, rc = actor(args, rbuf, buflen); ata_scsi_rbuf_put(cmd, rbuf); - if (rc) - ata_bad_cdb(cmd, args->done); - else { + if (rc == 0) cmd->result = SAM_STAT_GOOD; - args->done(cmd); - } + args->done(cmd); } /** @@ -1011,13 +1633,9 @@ static void ata_msense_push(u8 **ptr_io, const u8 *last, static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io, const u8 *last) { - u8 page[] = { - 0x8, /* page code */ - 0x12, /* page length */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 zeroes */ - 0, 0, 0, 0, 0, 0, 0, 0 /* 8 zeroes */ - }; + u8 page[CACHE_MPAGE_LEN]; + memcpy(page, def_cache_mpage, sizeof(page)); if (ata_id_wcache_enabled(id)) page[2] |= (1 << 2); /* write cache enable */ if (!ata_id_rahead_enabled(id)) @@ -1041,15 +1659,9 @@ static unsigned int ata_msense_caching(u16 *id, u8 **ptr_io, static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last) { - const u8 page[] = {0xa, 0xa, 6, 0, 0, 0, 0, 0, 0xff, 0xff, 0, 30}; - - /* byte 2: set the descriptor format sense data bit (bit 2) - * since we need to support returning this format for SAT - * commands and any SCSI commands against a 48b LBA device. - */ - - ata_msense_push(ptr_io, last, page, sizeof(page)); - return sizeof(page); + ata_msense_push(ptr_io, last, def_control_mpage, + sizeof(def_control_mpage)); + return sizeof(def_control_mpage); } /** @@ -1066,15 +1678,10 @@ static unsigned int ata_msense_ctl_mode(u8 **ptr_io, const u8 *last) static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last) { - const u8 page[] = { - 0x1, /* page code */ - 0xa, /* page length */ - (1 << 7) | (1 << 6), /* note auto r/w reallocation */ - 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 9 zeroes */ - }; - ata_msense_push(ptr_io, last, page, sizeof(page)); - return sizeof(page); + ata_msense_push(ptr_io, last, def_rw_recovery_mpage, + sizeof(def_rw_recovery_mpage)); + return sizeof(def_rw_recovery_mpage); } /** @@ -1083,7 +1690,9 @@ static unsigned int ata_msense_rw_recovery(u8 **ptr_io, const u8 *last) * @rbuf: Response buffer, to which simulated SCSI cmd output is sent. * @buflen: Response buffer length. * - * Simulate MODE SENSE commands. + * Simulate MODE SENSE commands. Assume this is invoked for direct + * access devices (e.g. disks) only. There should be no block + * descriptor for other device types. * * LOCKING: * spin_lock_irqsave(host_set lock) @@ -1093,61 +1702,115 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen) { u8 *scsicmd = args->cmd->cmnd, *p, *last; - unsigned int page_control, six_byte, output_len; + const u8 sat_blk_desc[] = { + 0, 0, 0, 0, /* number of blocks: sat unspecified */ + 0, + 0, 0x2, 0x0 /* block length: 512 bytes */ + }; + u8 pg, spg; + unsigned int ebd, page_control, six_byte, output_len, alloc_len, minlen; VPRINTK("ENTER\n"); six_byte = (scsicmd[0] == MODE_SENSE); - - /* we only support saved and current values (which we treat - * in the same manner) + ebd = !(scsicmd[1] & 0x8); /* dbd bit inverted == edb */ + /* + * LLBA bit in msense(10) ignored (compliant) */ + page_control = scsicmd[2] >> 6; - if ((page_control != 0) && (page_control != 3)) - return 1; + switch (page_control) { + case 0: /* current */ + break; /* supported */ + case 3: /* saved */ + goto saving_not_supp; + case 1: /* changeable */ + case 2: /* defaults */ + default: + goto invalid_fld; + } - if (six_byte) - output_len = 4; - else - output_len = 8; + if (six_byte) { + output_len = 4 + (ebd ? 8 : 0); + alloc_len = scsicmd[4]; + } else { + output_len = 8 + (ebd ? 8 : 0); + alloc_len = (scsicmd[7] << 8) + scsicmd[8]; + } + minlen = (alloc_len < buflen) ? alloc_len : buflen; p = rbuf + output_len; - last = rbuf + buflen - 1; + last = rbuf + minlen - 1; + + pg = scsicmd[2] & 0x3f; + spg = scsicmd[3]; + /* + * No mode subpages supported (yet) but asking for _all_ + * subpages may be valid + */ + if (spg && (spg != ALL_SUB_MPAGES)) + goto invalid_fld; - switch(scsicmd[2] & 0x3f) { - case 0x01: /* r/w error recovery */ + switch(pg) { + case RW_RECOVERY_MPAGE: output_len += ata_msense_rw_recovery(&p, last); break; - case 0x08: /* caching */ + case CACHE_MPAGE: output_len += ata_msense_caching(args->id, &p, last); break; - case 0x0a: { /* control mode */ + case CONTROL_MPAGE: { output_len += ata_msense_ctl_mode(&p, last); break; } - case 0x3f: /* all pages */ + case ALL_MPAGES: output_len += ata_msense_rw_recovery(&p, last); output_len += ata_msense_caching(args->id, &p, last); output_len += ata_msense_ctl_mode(&p, last); break; default: /* invalid page code */ - return 1; + goto invalid_fld; } + if (minlen < 1) + return 0; if (six_byte) { output_len--; rbuf[0] = output_len; + if (ebd) { + if (minlen > 3) + rbuf[3] = sizeof(sat_blk_desc); + if (minlen > 11) + memcpy(rbuf + 4, sat_blk_desc, + sizeof(sat_blk_desc)); + } } else { output_len -= 2; rbuf[0] = output_len >> 8; - rbuf[1] = output_len; + if (minlen > 1) + rbuf[1] = output_len; + if (ebd) { + if (minlen > 7) + rbuf[7] = sizeof(sat_blk_desc); + if (minlen > 15) + memcpy(rbuf + 8, sat_blk_desc, + sizeof(sat_blk_desc)); + } } - return 0; + +invalid_fld: + ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; + +saving_not_supp: + ata_scsi_set_sense(args->cmd, ILLEGAL_REQUEST, 0x39, 0x0); + /* "Saving parameters not supported" */ + return 1; } /** @@ -1170,10 +1833,20 @@ unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf, VPRINTK("ENTER\n"); - if (ata_id_has_lba48(args->id)) - n_sectors = ata_id_u64(args->id, 100); - else - n_sectors = ata_id_u32(args->id, 60); + if (ata_id_has_lba(args->id)) { + if (ata_id_has_lba48(args->id)) + n_sectors = ata_id_u64(args->id, 100); + else + n_sectors = ata_id_u32(args->id, 60); + } else { + /* CHS default translation */ + n_sectors = args->id[1] * args->id[3] * args->id[6]; + + if (ata_id_current_chs_valid(args->id)) + /* CHS current translation */ + n_sectors = ata_id_u32(args->id, 57); + } + n_sectors--; /* ATA TotalUserSectors - 1 */ if (args->cmd->cmnd[0] == READ_CAPACITY) { @@ -1237,6 +1910,34 @@ unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, } /** + * ata_scsi_set_sense - Set SCSI sense data and status + * @cmd: SCSI request to be handled + * @sk: SCSI-defined sense key + * @asc: SCSI-defined additional sense code + * @ascq: SCSI-defined additional sense code qualifier + * + * Helper function that builds a valid fixed format, current + * response code and the given sense key (sk), additional sense + * code (asc) and additional sense code qualifier (ascq) with + * a SCSI command status of %SAM_STAT_CHECK_CONDITION and + * DRIVER_SENSE set in the upper bits of scsi_cmnd::result . + * + * LOCKING: + * Not required + */ + +void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +{ + cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; + + cmd->sense_buffer[0] = 0x70; /* fixed format, current */ + cmd->sense_buffer[2] = sk; + cmd->sense_buffer[7] = 18 - 8; /* additional sense length */ + cmd->sense_buffer[12] = asc; + cmd->sense_buffer[13] = ascq; +} + +/** * ata_scsi_badcmd - End a SCSI request with an error * @cmd: SCSI request to be handled * @done: SCSI command completion function @@ -1254,30 +1955,89 @@ unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq) { DPRINTK("ENTER\n"); - cmd->result = SAM_STAT_CHECK_CONDITION; - - cmd->sense_buffer[0] = 0x70; - cmd->sense_buffer[2] = ILLEGAL_REQUEST; - cmd->sense_buffer[7] = 14 - 8; /* addnl. sense len. FIXME: correct? */ - cmd->sense_buffer[12] = asc; - cmd->sense_buffer[13] = ascq; + ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, asc, ascq); done(cmd); } -static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) +void atapi_request_sense(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd) +{ + DECLARE_COMPLETION(wait); + struct ata_queued_cmd *qc; + unsigned long flags; + int rc; + + DPRINTK("ATAPI request sense\n"); + + qc = ata_qc_new_init(ap, dev); + BUG_ON(qc == NULL); + + /* FIXME: is this needed? */ + memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer)); + + ata_sg_init_one(qc, cmd->sense_buffer, sizeof(cmd->sense_buffer)); + qc->dma_dir = DMA_FROM_DEVICE; + + memset(&qc->cdb, 0, ap->cdb_len); + qc->cdb[0] = REQUEST_SENSE; + qc->cdb[4] = SCSI_SENSE_BUFFERSIZE; + + qc->tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + qc->tf.command = ATA_CMD_PACKET; + + qc->tf.protocol = ATA_PROT_ATAPI; + qc->tf.lbam = (8 * 1024) & 0xff; + qc->tf.lbah = (8 * 1024) >> 8; + qc->nbytes = SCSI_SENSE_BUFFERSIZE; + + qc->waiting = &wait; + qc->complete_fn = ata_qc_complete_noop; + + spin_lock_irqsave(&ap->host_set->lock, flags); + rc = ata_qc_issue(qc); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + if (rc) + ata_port_disable(ap); + else + wait_for_completion(&wait); + + DPRINTK("EXIT\n"); +} + +static int atapi_qc_complete(struct ata_queued_cmd *qc, unsigned int err_mask) { struct scsi_cmnd *cmd = qc->scsicmd; - if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) { + VPRINTK("ENTER, err_mask 0x%X\n", err_mask); + + if (unlikely(err_mask & AC_ERR_DEV)) { DPRINTK("request check condition\n"); + /* FIXME: command completion with check condition + * but no sense causes the error handler to run, + * which then issues REQUEST SENSE, fills in the sense + * buffer, and completes the command (for the second + * time). We need to issue REQUEST SENSE some other + * way, to avoid completing the command twice. + */ cmd->result = SAM_STAT_CHECK_CONDITION; qc->scsidone(cmd); return 1; - } else { + } + + else if (unlikely(err_mask)) + /* FIXME: not quite right; we don't want the + * translation of taskfile registers into + * a sense descriptors, since that's only + * correct for ATA, not ATAPI + */ + ata_gen_ata_desc_sense(qc); + + else { u8 *scsicmd = cmd->cmnd; if (scsicmd[0] == INQUIRY) { @@ -1285,15 +2045,30 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) unsigned int buflen; buflen = ata_scsi_rbuf_get(cmd, &buf); - buf[2] = 0x5; - buf[3] = (buf[3] & 0xf0) | 2; + + /* ATAPI devices typically report zero for their SCSI version, + * and sometimes deviate from the spec WRT response data + * format. If SCSI version is reported as zero like normal, + * then we make the following fixups: 1) Fake MMC-5 version, + * to indicate to the Linux scsi midlayer this is a modern + * device. 2) Ensure response data format / ATAPI information + * are always correct. + */ + /* FIXME: do we ever override EVPD pages and the like, with + * this code? + */ + if (buf[2] == 0) { + buf[2] = 0x5; + buf[3] = 0x32; + } + ata_scsi_rbuf_put(cmd, buf); } + cmd->result = SAM_STAT_GOOD; } qc->scsidone(cmd); - return 0; } /** @@ -1308,7 +2083,7 @@ static int atapi_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) * Zero on success, non-zero on failure. */ -static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) +static unsigned int atapi_xlat(struct ata_queued_cmd *qc, const u8 *scsicmd) { struct scsi_cmnd *cmd = qc->scsicmd; struct ata_device *dev = qc->dev; @@ -1377,7 +2152,7 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc, u8 *scsicmd) */ static struct ata_device * -ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev) +ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev) { struct ata_device *dev; @@ -1394,14 +2169,151 @@ ata_scsi_find_dev(struct ata_port *ap, struct scsi_device *scsidev) if (unlikely(!ata_dev_present(dev))) return NULL; -#ifndef ATA_ENABLE_ATAPI - if (unlikely(dev->class == ATA_DEV_ATAPI)) - return NULL; -#endif + if (!atapi_enabled) { + if (unlikely(dev->class == ATA_DEV_ATAPI)) + return NULL; + } return dev; } +/* + * ata_scsi_map_proto - Map pass-thru protocol value to taskfile value. + * @byte1: Byte 1 from pass-thru CDB. + * + * RETURNS: + * ATA_PROT_UNKNOWN if mapping failed/unimplemented, protocol otherwise. + */ +static u8 +ata_scsi_map_proto(u8 byte1) +{ + switch((byte1 & 0x1e) >> 1) { + case 3: /* Non-data */ + return ATA_PROT_NODATA; + + case 6: /* DMA */ + return ATA_PROT_DMA; + + case 4: /* PIO Data-in */ + case 5: /* PIO Data-out */ + if (byte1 & 0xe0) { + return ATA_PROT_PIO_MULT; + } + return ATA_PROT_PIO; + + case 10: /* Device Reset */ + case 0: /* Hard Reset */ + case 1: /* SRST */ + case 2: /* Bus Idle */ + case 7: /* Packet */ + case 8: /* DMA Queued */ + case 9: /* Device Diagnostic */ + case 11: /* UDMA Data-in */ + case 12: /* UDMA Data-Out */ + case 13: /* FPDMA */ + default: /* Reserved */ + break; + } + + return ATA_PROT_UNKNOWN; +} + +/** + * ata_scsi_pass_thru - convert ATA pass-thru CDB to taskfile + * @qc: command structure to be initialized + * @cmd: SCSI command to convert + * + * Handles either 12 or 16-byte versions of the CDB. + * + * RETURNS: + * Zero on success, non-zero on failure. + */ +static unsigned int +ata_scsi_pass_thru(struct ata_queued_cmd *qc, const u8 *scsicmd) +{ + struct ata_taskfile *tf = &(qc->tf); + struct scsi_cmnd *cmd = qc->scsicmd; + + if ((tf->protocol = ata_scsi_map_proto(scsicmd[1])) == ATA_PROT_UNKNOWN) + return 1; + + /* + * 12 and 16 byte CDBs use different offsets to + * provide the various register values. + */ + if (scsicmd[0] == ATA_16) { + /* + * 16-byte CDB - may contain extended commands. + * + * If that is the case, copy the upper byte register values. + */ + if (scsicmd[1] & 0x01) { + tf->hob_feature = scsicmd[3]; + tf->hob_nsect = scsicmd[5]; + tf->hob_lbal = scsicmd[7]; + tf->hob_lbam = scsicmd[9]; + tf->hob_lbah = scsicmd[11]; + tf->flags |= ATA_TFLAG_LBA48; + } else + tf->flags &= ~ATA_TFLAG_LBA48; + + /* + * Always copy low byte, device and command registers. + */ + tf->feature = scsicmd[4]; + tf->nsect = scsicmd[6]; + tf->lbal = scsicmd[8]; + tf->lbam = scsicmd[10]; + tf->lbah = scsicmd[12]; + tf->device = scsicmd[13]; + tf->command = scsicmd[14]; + } else { + /* + * 12-byte CDB - incapable of extended commands. + */ + tf->flags &= ~ATA_TFLAG_LBA48; + + tf->feature = scsicmd[3]; + tf->nsect = scsicmd[4]; + tf->lbal = scsicmd[5]; + tf->lbam = scsicmd[6]; + tf->lbah = scsicmd[7]; + tf->device = scsicmd[8]; + tf->command = scsicmd[9]; + } + + /* + * Filter SET_FEATURES - XFER MODE command -- otherwise, + * SET_FEATURES - XFER MODE must be preceded/succeeded + * by an update to hardware-specific registers for each + * controller (i.e. the reason for ->set_piomode(), + * ->set_dmamode(), and ->post_set_mode() hooks). + */ + if ((tf->command == ATA_CMD_SET_FEATURES) + && (tf->feature == SETFEATURES_XFER)) + return 1; + + /* + * Set flags so that all registers will be written, + * and pass on write indication (used for PIO/DMA + * setup.) + */ + tf->flags |= (ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE); + + if (cmd->sc_data_direction == DMA_TO_DEVICE) + tf->flags |= ATA_TFLAG_WRITE; + + /* + * Set transfer length. + * + * TODO: find out if we need to do more here to + * cover scatter/gather case. + */ + qc->nsect = cmd->bufflen / ATA_SECT_SIZE; + + return 0; +} + /** * ata_get_xlat_func - check if SCSI to ATA translation is possible * @dev: ATA device @@ -1434,6 +2346,13 @@ static inline ata_xlat_func_t ata_get_xlat_func(struct ata_device *dev, u8 cmd) case VERIFY: case VERIFY_16: return ata_scsi_verify_xlat; + + case ATA_12: + case ATA_16: + return ata_scsi_pass_thru; + + case START_STOP: + return ata_scsi_start_stop_xlat; } return NULL; @@ -1487,8 +2406,12 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) struct ata_port *ap; struct ata_device *dev; struct scsi_device *scsidev = cmd->device; + struct Scsi_Host *shost = scsidev->host; - ap = (struct ata_port *) &scsidev->host->hostdata[0]; + ap = (struct ata_port *) &shost->hostdata[0]; + + spin_unlock(shost->host_lock); + spin_lock(&ap->host_set->lock); ata_scsi_dump_cdb(ap, cmd); @@ -1511,6 +2434,8 @@ int ata_scsi_queuecmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) ata_scsi_translate(ap, dev, cmd, done, atapi_xlat); out_unlock: + spin_unlock(&ap->host_set->lock); + spin_lock(shost->host_lock); return 0; } @@ -1532,7 +2457,7 @@ void ata_scsi_simulate(u16 *id, void (*done)(struct scsi_cmnd *)) { struct ata_scsi_args args; - u8 *scsicmd = cmd->cmnd; + const u8 *scsicmd = cmd->cmnd; args.id = id; args.cmd = cmd; @@ -1552,7 +2477,7 @@ void ata_scsi_simulate(u16 *id, case INQUIRY: if (scsicmd[1] & 2) /* is CmdDt set? */ - ata_bad_cdb(cmd, done); + ata_scsi_invalid_field(cmd, done); else if ((scsicmd[1] & 1) == 0) /* is EVPD clear? */ ata_scsi_rbuf_fill(&args, ata_scsiop_inq_std); else if (scsicmd[2] == 0x00) @@ -1562,7 +2487,7 @@ void ata_scsi_simulate(u16 *id, else if (scsicmd[2] == 0x83) ata_scsi_rbuf_fill(&args, ata_scsiop_inq_83); else - ata_bad_cdb(cmd, done); + ata_scsi_invalid_field(cmd, done); break; case MODE_SENSE: @@ -1572,7 +2497,7 @@ void ata_scsi_simulate(u16 *id, case MODE_SELECT: /* unconditionally return */ case MODE_SELECT_10: /* bad-field-in-cdb */ - ata_bad_cdb(cmd, done); + ata_scsi_invalid_field(cmd, done); break; case READ_CAPACITY: @@ -1583,20 +2508,38 @@ void ata_scsi_simulate(u16 *id, if ((scsicmd[1] & 0x1f) == SAI_READ_CAPACITY_16) ata_scsi_rbuf_fill(&args, ata_scsiop_read_cap); else - ata_bad_cdb(cmd, done); + ata_scsi_invalid_field(cmd, done); break; case REPORT_LUNS: ata_scsi_rbuf_fill(&args, ata_scsiop_report_luns); break; - /* mandantory commands we haven't implemented yet */ + /* mandatory commands we haven't implemented yet */ case REQUEST_SENSE: /* all other commands */ default: - ata_bad_scsiop(cmd, done); + ata_scsi_set_sense(cmd, ILLEGAL_REQUEST, 0x20, 0x0); + /* "Invalid command operation code" */ + done(cmd); break; } } +void ata_scsi_scan_host(struct ata_port *ap) +{ + struct ata_device *dev; + unsigned int i; + + if (ap->flags & ATA_FLAG_PORT_DISABLED) + return; + + for (i = 0; i < ATA_MAX_DEVICES; i++) { + dev = &ap->device[i]; + + if (ata_dev_present(dev)) + scsi_scan_target(&ap->host->shost_gendev, 0, i, 0, 0); + } +} + diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h index d90430bbb0de..fad051ca4672 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -1,32 +1,35 @@ /* - libata.h - helper library for ATA - - Copyright 2003-2004 Red Hat, Inc. All rights reserved. - Copyright 2003-2004 Jeff Garzik - - The contents of this file are subject to the Open - Software License version 1.1 that can be found at - http://www.opensource.org/licenses/osl-1.1.txt and is included herein - by reference. - - Alternatively, the contents of this file may be used under the terms - of the GNU General Public License version 2 (the "GPL") as distributed - in the kernel source COPYING file, in which case the provisions of - the GPL are applicable instead of the above. If you wish to allow - the use of your version of this file only under the terms of the - GPL and not to allow others to use your version of this file under - the OSL, indicate your decision by deleting the provisions above and - replace them with the notice and other provisions required by the GPL. - If you do not delete the provisions above, a recipient may use your - version of this file under either the OSL or the GPL. - + * libata.h - helper library for ATA + * + * Copyright 2003-2004 Red Hat, Inc. All rights reserved. + * Copyright 2003-2004 Jeff Garzik + * + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * */ #ifndef __LIBATA_H__ #define __LIBATA_H__ #define DRV_NAME "libata" -#define DRV_VERSION "1.11" /* must be exactly four chars */ +#define DRV_VERSION "1.12" /* must be exactly four chars */ struct ata_scsi_args { u16 *id; @@ -35,19 +38,25 @@ struct ata_scsi_args { }; /* libata-core.c */ +extern int atapi_enabled; +extern int ata_qc_complete_noop(struct ata_queued_cmd *qc, unsigned int err_mask); extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap, struct ata_device *dev); +extern void ata_rwcmd_protocol(struct ata_queued_cmd *qc); extern void ata_qc_free(struct ata_queued_cmd *qc); extern int ata_qc_issue(struct ata_queued_cmd *qc); extern int ata_check_atapi_dma(struct ata_queued_cmd *qc); extern void ata_dev_select(struct ata_port *ap, unsigned int device, unsigned int wait, unsigned int can_sleep); -extern void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf); extern void swap_buf_le16(u16 *buf, unsigned int buf_words); +extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); +extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); /* libata-scsi.c */ -extern void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat); +extern void atapi_request_sense(struct ata_port *ap, struct ata_device *dev, + struct scsi_cmnd *cmd); +extern void ata_scsi_scan_host(struct ata_port *ap); extern int ata_scsi_error(struct Scsi_Host *host); extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen); @@ -72,18 +81,10 @@ extern unsigned int ata_scsiop_report_luns(struct ata_scsi_args *args, u8 *rbuf, extern void ata_scsi_badcmd(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *), u8 asc, u8 ascq); -extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, +extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, + u8 sk, u8 asc, u8 ascq); +extern void ata_scsi_rbuf_fill(struct ata_scsi_args *args, unsigned int (*actor) (struct ata_scsi_args *args, u8 *rbuf, unsigned int buflen)); -static inline void ata_bad_scsiop(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) -{ - ata_scsi_badcmd(cmd, done, 0x20, 0x00); -} - -static inline void ata_bad_cdb(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) -{ - ata_scsi_badcmd(cmd, done, 0x24, 0x00); -} - #endif /* __LIBATA_H__ */ diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index 3bb82aae432e..adb95674823f 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -342,9 +342,6 @@ struct lpfc_hba { #define VPD_MASK 0xf /* mask for any vpd data */ struct timer_list els_tmofunc; - - void *link_stats; - /* * stat counters */ @@ -370,6 +367,8 @@ struct lpfc_hba { struct list_head freebufList; struct list_head ctrspbuflist; struct list_head rnidrspbuflist; + + struct fc_host_statistics link_stats; }; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 3cea92883019..acae7c48ef7d 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -23,6 +23,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> @@ -965,21 +966,21 @@ static void lpfc_get_host_fabric_name (struct Scsi_Host *shost) { struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata[0]; - u64 nodename; + u64 node_name; spin_lock_irq(shost->host_lock); if ((phba->fc_flag & FC_FABRIC) || ((phba->fc_topology == TOPOLOGY_LOOP) && (phba->fc_flag & FC_PUBLIC_LOOP))) - memcpy(&nodename, &phba->fc_fabparam.nodeName, sizeof(u64)); + node_name = wwn_to_u64(phba->fc_fabparam.nodeName.u.wwn); else /* fabric is local port if there is no F/FL_Port */ - memcpy(&nodename, &phba->fc_nodename, sizeof(u64)); + node_name = wwn_to_u64(phba->fc_nodename.u.wwn); spin_unlock_irq(shost->host_lock); - fc_host_fabric_name(shost) = be64_to_cpu(nodename); + fc_host_fabric_name(shost) = node_name; } @@ -988,8 +989,7 @@ lpfc_get_stats(struct Scsi_Host *shost) { struct lpfc_hba *phba = (struct lpfc_hba *)shost->hostdata[0]; struct lpfc_sli *psli = &phba->sli; - struct fc_host_statistics *hs = - (struct fc_host_statistics *)phba->link_stats; + struct fc_host_statistics *hs = &phba->link_stats; LPFC_MBOXQ_t *pmboxq; MAILBOX_t *pmb; int rc=0; @@ -1020,6 +1020,8 @@ lpfc_get_stats(struct Scsi_Host *shost) return NULL; } + memset(hs, 0, sizeof (struct fc_host_statistics)); + hs->tx_frames = pmb->un.varRdStatus.xmitFrameCnt; hs->tx_words = (pmb->un.varRdStatus.xmitByteCnt * 256); hs->rx_frames = pmb->un.varRdStatus.rcvFrameCnt; @@ -1101,21 +1103,20 @@ lpfc_get_starget_node_name(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; - uint64_t node_name = 0; + u64 node_name = 0; struct lpfc_nodelist *ndlp = NULL; spin_lock_irq(shost->host_lock); /* Search the mapped list for this target ID */ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { if (starget->id == ndlp->nlp_sid) { - memcpy(&node_name, &ndlp->nlp_nodename, - sizeof(struct lpfc_name)); + node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); break; } } spin_unlock_irq(shost->host_lock); - fc_starget_node_name(starget) = be64_to_cpu(node_name); + fc_starget_node_name(starget) = node_name; } static void @@ -1123,21 +1124,20 @@ lpfc_get_starget_port_name(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); struct lpfc_hba *phba = (struct lpfc_hba *) shost->hostdata[0]; - uint64_t port_name = 0; + u64 port_name = 0; struct lpfc_nodelist *ndlp = NULL; spin_lock_irq(shost->host_lock); /* Search the mapped list for this target ID */ list_for_each_entry(ndlp, &phba->fc_nlpmap_list, nlp_listp) { if (starget->id == ndlp->nlp_sid) { - memcpy(&port_name, &ndlp->nlp_portname, - sizeof(struct lpfc_name)); + port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); break; } } spin_unlock_irq(shost->host_lock); - fc_starget_port_name(starget) = be64_to_cpu(port_name); + fc_starget_port_name(starget) = port_name; } static void diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 78adee4699af..1280f0e54636 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -27,8 +27,10 @@ #include <linux/interrupt.h> #include <linux/utsname.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_transport_fc.h> #include "lpfc_hw.h" #include "lpfc_sli.h" diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 2b1c9572dae7..63caf7fe9725 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -23,6 +23,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 233901e9dfde..56052f4510c3 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -24,6 +24,7 @@ #include <linux/kthread.h> #include <linux/interrupt.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> @@ -1016,13 +1017,10 @@ lpfc_register_remote_port(struct lpfc_hba * phba, struct fc_rport *rport; struct lpfc_rport_data *rdata; struct fc_rport_identifiers rport_ids; - uint64_t wwn; /* Remote port has reappeared. Re-register w/ FC transport */ - memcpy(&wwn, &ndlp->nlp_nodename, sizeof(uint64_t)); - rport_ids.node_name = be64_to_cpu(wwn); - memcpy(&wwn, &ndlp->nlp_portname, sizeof(uint64_t)); - rport_ids.port_name = be64_to_cpu(wwn); + rport_ids.node_name = wwn_to_u64(ndlp->nlp_nodename.u.wwn); + rport_ids.port_name = wwn_to_u64(ndlp->nlp_portname.u.wwn); rport_ids.port_id = ndlp->nlp_DID; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; if (ndlp->nlp_type & NLP_FCP_TARGET) @@ -1135,6 +1133,8 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list) switch(list) { case NLP_NO_LIST: /* No list, just remove it */ lpfc_nlp_remove(phba, nlp); + /* as node removed - stop further transport calls */ + rport_del = none; break; case NLP_UNUSED_LIST: spin_lock_irq(phba->host->host_lock); diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 21591cb9f551..86c41981188b 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -262,12 +262,14 @@ struct lpfc_sli_ct_request { #define FF_FRAME_SIZE 2048 struct lpfc_name { + union { + struct { #ifdef __BIG_ENDIAN_BITFIELD - uint8_t nameType:4; /* FC Word 0, bit 28:31 */ - uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ #else /* __LITTLE_ENDIAN_BITFIELD */ - uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ - uint8_t nameType:4; /* FC Word 0, bit 28:31 */ + uint8_t IEEEextMsn:4; /* FC Word 0, bit 24:27, bit 8:11 of IEEE ext */ + uint8_t nameType:4; /* FC Word 0, bit 28:31 */ #endif #define NAME_IEEE 0x1 /* IEEE name - nameType */ @@ -276,8 +278,11 @@ struct lpfc_name { #define NAME_IP_TYPE 0x4 /* IP address */ #define NAME_CCITT_TYPE 0xC #define NAME_CCITT_GR_TYPE 0xE - uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ - uint8_t IEEE[6]; /* FC IEEE address */ + uint8_t IEEEextLsb; /* FC Word 0, bit 16:23, IEEE extended Lsb */ + uint8_t IEEE[6]; /* FC IEEE address */ + } s; + uint8_t wwn[8]; + } u; }; struct csp { diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 34d416d2b007..0856ff7d3b33 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -28,6 +28,7 @@ #include <linux/pci.h> #include <linux/spinlock.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> @@ -284,7 +285,7 @@ lpfc_config_port_post(struct lpfc_hba * phba) if (phba->SerialNumber[0] == 0) { uint8_t *outptr; - outptr = (uint8_t *) & phba->fc_nodename.IEEE[0]; + outptr = &phba->fc_nodename.u.s.IEEE[0]; for (i = 0; i < 12; i++) { status = *outptr++; j = ((status & 0xf0) >> 4); @@ -1332,21 +1333,18 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) unsigned long bar0map_len, bar2map_len; int error = -ENODEV, retval; int i; - u64 wwname; if (pci_enable_device(pdev)) goto out; if (pci_request_regions(pdev, LPFC_DRIVER_NAME)) goto out_disable_device; - host = scsi_host_alloc(&lpfc_template, - sizeof (struct lpfc_hba) + sizeof (unsigned long)); + host = scsi_host_alloc(&lpfc_template, sizeof (struct lpfc_hba)); if (!host) goto out_release_regions; phba = (struct lpfc_hba*)host->hostdata; memset(phba, 0, sizeof (struct lpfc_hba)); - phba->link_stats = (void *)&phba[1]; phba->host = host; phba->fc_flag |= FC_LOADING; @@ -1525,10 +1523,8 @@ lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid) * Must done after lpfc_sli_hba_setup() */ - memcpy(&wwname, &phba->fc_nodename, sizeof(u64)); - fc_host_node_name(host) = be64_to_cpu(wwname); - memcpy(&wwname, &phba->fc_portname, sizeof(u64)); - fc_host_port_name(host) = be64_to_cpu(wwname); + fc_host_node_name(host) = wwn_to_u64(phba->fc_nodename.u.wwn); + fc_host_port_name(host) = wwn_to_u64(phba->fc_portname.u.wwn); fc_host_supported_classes(host) = FC_COS_CLASS3; memset(fc_host_supported_fc4s(host), 0, diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c index c27cf94795db..73eb89f91593 100644 --- a/drivers/scsi/lpfc/lpfc_mbox.c +++ b/drivers/scsi/lpfc/lpfc_mbox.c @@ -23,6 +23,11 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_transport_fc.h> + +#include <scsi/scsi.h> + #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_disc.h" diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index a5cfb6421fa9..352df47bcaca 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -23,6 +23,11 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_transport_fc.h> + +#include <scsi/scsi.h> + #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_disc.h" @@ -34,7 +39,7 @@ #define LPFC_MEM_POOL_SIZE 64 /* max elem in non-DMA safety pool */ static void * -lpfc_pool_kmalloc(unsigned int gfp_flags, void *data) +lpfc_pool_kmalloc(gfp_t gfp_flags, void *data) { return kmalloc((unsigned long)data, gfp_flags); } diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c index 45dc0210fc49..9b35eaac781d 100644 --- a/drivers/scsi/lpfc/lpfc_nportdisc.c +++ b/drivers/scsi/lpfc/lpfc_nportdisc.c @@ -23,6 +23,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> +#include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport_fc.h> diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 17e4974d4445..b5ad1871d34b 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -40,11 +40,6 @@ #define LPFC_RESET_WAIT 2 #define LPFC_ABORT_WAIT 2 -static inline void lpfc_put_lun(struct fcp_cmnd *fcmd, unsigned int lun) -{ - fcmd->fcpLunLsl = 0; - fcmd->fcpLunMsl = swab16((uint16_t)lun); -} /* * This routine allocates a scsi buffer, which contains all the necessary @@ -238,6 +233,8 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd) bpl->tus.f.bdeSize = scsi_cmnd->request_bufflen; if (datadir == DMA_TO_DEVICE) bpl->tus.f.bdeFlags = 0; + else + bpl->tus.f.bdeFlags = BUFF_USE_RCV; bpl->tus.w = le32_to_cpu(bpl->tus.w); num_bde = 1; bpl++; @@ -245,8 +242,11 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd) /* * Finish initializing those IOCB fields that are dependent on the - * scsi_cmnd request_buffer + * scsi_cmnd request_buffer. Note that the bdeSize is explicitly + * reinitialized since all iocb memory resources are used many times + * for transmit, receive, and continuation bpl's. */ + iocb_cmd->un.fcpi64.bdl.bdeSize = (2 * sizeof (struct ulp_bde64)); iocb_cmd->un.fcpi64.bdl.bdeSize += (num_bde * sizeof (struct ulp_bde64)); iocb_cmd->ulpBdeCount = 1; @@ -445,8 +445,11 @@ lpfc_scsi_prep_cmnd(struct lpfc_hba * phba, struct lpfc_scsi_buf * lpfc_cmd, int datadir = scsi_cmnd->sc_data_direction; lpfc_cmd->fcp_rsp->rspSnsLen = 0; + /* clear task management bits */ + lpfc_cmd->fcp_cmnd->fcpCntl2 = 0; - lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun); + int_to_scsilun(lpfc_cmd->pCmd->device->lun, + &lpfc_cmd->fcp_cmnd->fcp_lun); memcpy(&fcp_cmnd->fcpCdb[0], scsi_cmnd->cmnd, 16); @@ -545,7 +548,8 @@ lpfc_scsi_prep_task_mgmt_cmd(struct lpfc_hba *phba, piocb = &piocbq->iocb; fcp_cmnd = lpfc_cmd->fcp_cmnd; - lpfc_put_lun(lpfc_cmd->fcp_cmnd, lpfc_cmd->pCmd->device->lun); + int_to_scsilun(lpfc_cmd->pCmd->device->lun, + &lpfc_cmd->fcp_cmnd->fcp_lun); fcp_cmnd->fcpCntl2 = task_mgmt_cmd; piocb->ulpCommand = CMD_FCP_ICMND64_CR; @@ -746,6 +750,10 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *)) cmnd->result = ScsiResult(DID_NO_CONNECT, 0); goto out_fail_command; } + else if (ndlp->nlp_state == NLP_STE_NPR_NODE) { + cmnd->result = ScsiResult(DID_BUS_BUSY, 0); + goto out_fail_command; + } /* * The device is most likely recovered and the driver * needs a bit more time to finish. Ask the midlayer diff --git a/drivers/scsi/lpfc/lpfc_scsi.h b/drivers/scsi/lpfc/lpfc_scsi.h index 0fd9ba14e1b5..acd64c49e849 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.h +++ b/drivers/scsi/lpfc/lpfc_scsi.h @@ -78,18 +78,7 @@ struct fcp_rsp { }; struct fcp_cmnd { - uint32_t fcpLunMsl; /* most significant lun word (32 bits) */ - uint32_t fcpLunLsl; /* least significant lun word (32 bits) */ - /* # of bits to shift lun id to end up in right - * payload word, little endian = 8, big = 16. - */ -#ifdef __BIG_ENDIAN -#define FC_LUN_SHIFT 16 -#define FC_ADDR_MODE_SHIFT 24 -#else /* __LITTLE_ENDIAN */ -#define FC_LUN_SHIFT 8 -#define FC_ADDR_MODE_SHIFT 0 -#endif + struct scsi_lun fcp_lun; uint8_t fcpCntl0; /* FCP_CNTL byte 0 (reserved) */ uint8_t fcpCntl1; /* FCP_CNTL byte 1 task codes */ diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 1775508ed276..e74e224fd77c 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -24,9 +24,11 @@ #include <linux/interrupt.h> #include <linux/delay.h> +#include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> +#include <scsi/scsi_transport_fc.h> #include "lpfc_hw.h" #include "lpfc_sli.h" diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 47dea48ee0ec..7e6747b06f90 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.0.29" +#define LPFC_DRIVER_VERSION "8.0.30" #define LPFC_DRIVER_NAME "lpfc" diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 6f308ebe3e79..61a6fd810bb4 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -621,8 +621,6 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) if(islogical) { switch (cmd->cmnd[0]) { case TEST_UNIT_READY: - memset(cmd->request_buffer, 0, cmd->request_bufflen); - #if MEGA_HAVE_CLUSTERING /* * Do we support clustering and is the support enabled @@ -652,11 +650,28 @@ mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) return NULL; #endif - case MODE_SENSE: + case MODE_SENSE: { + char *buf; + + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *)cmd->request_buffer; + buf = kmap_atomic(sg->page, KM_IRQ0) + + sg->offset; + } else + buf = cmd->request_buffer; memset(cmd->request_buffer, 0, cmd->cmnd[4]); + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *)cmd->request_buffer; + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } cmd->result = (DID_OK << 16); cmd->scsi_done(cmd); return NULL; + } case READ_CAPACITY: case INQUIRY: @@ -1685,14 +1700,23 @@ mega_rundoneq (adapter_t *adapter) static void mega_free_scb(adapter_t *adapter, scb_t *scb) { + unsigned long length; + switch( scb->dma_type ) { case MEGA_DMA_TYPE_NONE: break; case MEGA_BULK_DATA: + if (scb->cmd->use_sg == 0) + length = scb->cmd->request_bufflen; + else { + struct scatterlist *sgl = + (struct scatterlist *)scb->cmd->request_buffer; + length = sgl->length; + } pci_unmap_page(adapter->dev, scb->dma_h_bulkdata, - scb->cmd->request_bufflen, scb->dma_direction); + length, scb->dma_direction); break; case MEGA_SGLIST: @@ -1741,6 +1765,7 @@ mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) struct scatterlist *sgl; struct page *page; unsigned long offset; + unsigned int length; Scsi_Cmnd *cmd; int sgcnt; int idx; @@ -1748,14 +1773,23 @@ mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) cmd = scb->cmd; /* Scatter-gather not used */ - if( !cmd->use_sg ) { - - page = virt_to_page(cmd->request_buffer); - offset = offset_in_page(cmd->request_buffer); + if( cmd->use_sg == 0 || (cmd->use_sg == 1 && + !adapter->has_64bit_addr)) { + + if (cmd->use_sg == 0) { + page = virt_to_page(cmd->request_buffer); + offset = offset_in_page(cmd->request_buffer); + length = cmd->request_bufflen; + } else { + sgl = (struct scatterlist *)cmd->request_buffer; + page = sgl->page; + offset = sgl->offset; + length = sgl->length; + } scb->dma_h_bulkdata = pci_map_page(adapter->dev, page, offset, - cmd->request_bufflen, + length, scb->dma_direction); scb->dma_type = MEGA_BULK_DATA; @@ -1765,14 +1799,14 @@ mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) */ if( adapter->has_64bit_addr ) { scb->sgl64[0].address = scb->dma_h_bulkdata; - scb->sgl64[0].length = cmd->request_bufflen; + scb->sgl64[0].length = length; *buf = (u32)scb->sgl_dma_addr; - *len = (u32)cmd->request_bufflen; + *len = (u32)length; return 1; } else { *buf = (u32)scb->dma_h_bulkdata; - *len = (u32)cmd->request_bufflen; + *len = (u32)length; } return 0; } @@ -1791,27 +1825,23 @@ mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) if( sgcnt > adapter->sglen ) BUG(); + *len = 0; + for( idx = 0; idx < sgcnt; idx++, sgl++ ) { if( adapter->has_64bit_addr ) { scb->sgl64[idx].address = sg_dma_address(sgl); - scb->sgl64[idx].length = sg_dma_len(sgl); + *len += scb->sgl64[idx].length = sg_dma_len(sgl); } else { scb->sgl[idx].address = sg_dma_address(sgl); - scb->sgl[idx].length = sg_dma_len(sgl); + *len += scb->sgl[idx].length = sg_dma_len(sgl); } } /* Reset pointer and length fields */ *buf = scb->sgl_dma_addr; - /* - * For passthru command, dataxferlen must be set, even for commands - * with a sg list - */ - *len = (u32)cmd->request_bufflen; - /* Return count of SG requests */ return sgcnt; } diff --git a/drivers/scsi/megaraid/Kconfig.megaraid b/drivers/scsi/megaraid/Kconfig.megaraid index 917d591d90b2..7363e12663ac 100644 --- a/drivers/scsi/megaraid/Kconfig.megaraid +++ b/drivers/scsi/megaraid/Kconfig.megaraid @@ -76,3 +76,12 @@ config MEGARAID_LEGACY To compile this driver as a module, choose M here: the module will be called megaraid endif + +config MEGARAID_SAS + tristate "LSI Logic MegaRAID SAS RAID Module" + depends on PCI && SCSI + help + Module for LSI Logic's SAS based RAID controllers. + To compile this driver as a module, choose 'm' here. + Module will be called megaraid_sas + diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile index 6dd99f275722..f469915b97c3 100644 --- a/drivers/scsi/megaraid/Makefile +++ b/drivers/scsi/megaraid/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o +obj-$(CONFIG_MEGARAID_SAS) += megaraid_sas.o diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index d47be8e0ea3a..c9e743ba09ec 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -76,7 +76,7 @@ static void megaraid_exit(void); static int megaraid_probe_one(struct pci_dev*, const struct pci_device_id *); static void megaraid_detach_one(struct pci_dev *); -static void megaraid_mbox_shutdown(struct device *); +static void megaraid_mbox_shutdown(struct pci_dev *); static int megaraid_io_attach(adapter_t *); static void megaraid_io_detach(adapter_t *); @@ -369,9 +369,7 @@ static struct pci_driver megaraid_pci_driver_g = { .id_table = pci_id_table_g, .probe = megaraid_probe_one, .remove = __devexit_p(megaraid_detach_one), - .driver = { - .shutdown = megaraid_mbox_shutdown, - } + .shutdown = megaraid_mbox_shutdown, }; @@ -673,9 +671,9 @@ megaraid_detach_one(struct pci_dev *pdev) * Shutdown notification, perform flush cache */ static void -megaraid_mbox_shutdown(struct device *device) +megaraid_mbox_shutdown(struct pci_dev *pdev) { - adapter_t *adapter = pci_get_drvdata(to_pci_dev(device)); + adapter_t *adapter = pci_get_drvdata(pdev); static int counter; if (!adapter) { diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c new file mode 100644 index 000000000000..c3f637395734 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -0,0 +1,2806 @@ +/* + * + * Linux MegaRAID driver for SAS based RAID controllers + * + * Copyright (c) 2003-2005 LSI Logic Corporation. + * + * 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 the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_sas.c + * Version : v00.00.02.00-rc4 + * + * Authors: + * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com> + * Sumant Patro <Sumant.Patro@lsil.com> + * + * List of supported controllers + * + * OEM Product Name VID DID SSVID SSID + * --- ------------ --- --- ---- ---- + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/version.h> +#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/uio.h> +#include <asm/uaccess.h> +#include <linux/fs.h> +#include <linux/compat.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include "megaraid_sas.h" + +MODULE_LICENSE("GPL"); +MODULE_VERSION(MEGASAS_VERSION); +MODULE_AUTHOR("sreenivas.bagalkote@lsil.com"); +MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver"); + +/* + * PCI ID table for all supported controllers + */ +static struct pci_device_id megasas_pci_table[] = { + + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_ID_LSI_SAS1064R, + PCI_ANY_ID, + PCI_ANY_ID, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_DELL_PERC5, + PCI_ANY_ID, + PCI_ANY_ID, + }, + {0} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, megasas_pci_table); + +static int megasas_mgmt_majorno; +static struct megasas_mgmt_info megasas_mgmt_info; +static struct fasync_struct *megasas_async_queue; +static DECLARE_MUTEX(megasas_async_queue_mutex); + +/** + * megasas_get_cmd - Get a command from the free pool + * @instance: Adapter soft state + * + * Returns a free command from the pool + */ +static inline struct megasas_cmd *megasas_get_cmd(struct megasas_instance + *instance) +{ + unsigned long flags; + struct megasas_cmd *cmd = NULL; + + spin_lock_irqsave(&instance->cmd_pool_lock, flags); + + if (!list_empty(&instance->cmd_pool)) { + cmd = list_entry((&instance->cmd_pool)->next, + struct megasas_cmd, list); + list_del_init(&cmd->list); + } else { + printk(KERN_ERR "megasas: Command pool empty!\n"); + } + + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); + return cmd; +} + +/** + * megasas_return_cmd - Return a cmd to free command pool + * @instance: Adapter soft state + * @cmd: Command packet to be returned to free command pool + */ +static inline void +megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&instance->cmd_pool_lock, flags); + + cmd->scmd = NULL; + list_add_tail(&cmd->list, &instance->cmd_pool); + + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); +} + +/** + * megasas_enable_intr - Enables interrupts + * @regs: MFI register set + */ +static inline void +megasas_enable_intr(struct megasas_register_set __iomem * regs) +{ + writel(1, &(regs)->outbound_intr_mask); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_disable_intr - Disables interrupts + * @regs: MFI register set + */ +static inline void +megasas_disable_intr(struct megasas_register_set __iomem * regs) +{ + u32 mask = readl(®s->outbound_intr_mask) & (~0x00000001); + 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 + * + * For polling, MFI requires the cmd_status to be set to 0xFF before posting. + */ +static int +megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + int i; + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000; + + struct megasas_header *frame_hdr = &cmd->frame->hdr; + + frame_hdr->cmd_status = 0xFF; + frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + /* + * Issue the frame using inbound queue port + */ + writel(cmd->frame_phys_addr >> 3, + &instance->reg_set->inbound_queue_port); + + /* + * Wait for cmd_status to change + */ + for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i++) { + rmb(); + msleep(1); + } + + if (frame_hdr->cmd_status == 0xff) + return -ETIME; + + return 0; +} + +/** + * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds + * @instance: Adapter soft state + * @cmd: Command to be issued + * + * This function waits on an event for the command to be returned from ISR. + * Used to issue ioctl commands. + */ +static int +megasas_issue_blocked_cmd(struct megasas_instance *instance, + struct megasas_cmd *cmd) +{ + cmd->cmd_status = ENODATA; + + writel(cmd->frame_phys_addr >> 3, + &instance->reg_set->inbound_queue_port); + + wait_event(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA)); + + return 0; +} + +/** + * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd + * @instance: Adapter soft state + * @cmd_to_abort: Previously issued cmd to be aborted + * + * 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. + */ +static int +megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, + struct megasas_cmd *cmd_to_abort) +{ + struct megasas_cmd *cmd; + struct megasas_abort_frame *abort_fr; + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return -1; + + abort_fr = &cmd->frame->abort; + + /* + * Prepare and issue the abort frame + */ + abort_fr->cmd = MFI_CMD_ABORT; + abort_fr->cmd_status = 0xFF; + abort_fr->flags = 0; + abort_fr->abort_context = cmd_to_abort->index; + abort_fr->abort_mfi_phys_addr_lo = cmd_to_abort->frame_phys_addr; + abort_fr->abort_mfi_phys_addr_hi = 0; + + cmd->sync_cmd = 1; + cmd->cmd_status = 0xFF; + + writel(cmd->frame_phys_addr >> 3, + &instance->reg_set->inbound_queue_port); + + /* + * Wait for this cmd to complete + */ + wait_event(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF)); + + megasas_return_cmd(instance, cmd); + return 0; +} + +/** + * megasas_make_sgl32 - Prepares 32-bit SGL + * @instance: Adapter soft state + * @scp: SCSI command from the mid-layer + * @mfi_sgl: SGL to be filled in + * + * If successful, this function returns the number of SG elements. Otherwise, + * it returnes -1. + */ +static inline int +megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp, + union megasas_sgl *mfi_sgl) +{ + int i; + int sge_count; + struct scatterlist *os_sgl; + + /* + * Return 0 if there is no data transfer + */ + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { + mfi_sgl->sge32[0].phys_addr = pci_map_single(instance->pdev, + scp-> + request_buffer, + scp-> + request_bufflen, + scp-> + sc_data_direction); + mfi_sgl->sge32[0].length = scp->request_bufflen; + + return 1; + } + + os_sgl = (struct scatterlist *)scp->request_buffer; + sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg, + scp->sc_data_direction); + + for (i = 0; i < sge_count; i++, os_sgl++) { + mfi_sgl->sge32[i].length = sg_dma_len(os_sgl); + mfi_sgl->sge32[i].phys_addr = sg_dma_address(os_sgl); + } + + return sge_count; +} + +/** + * megasas_make_sgl64 - Prepares 64-bit SGL + * @instance: Adapter soft state + * @scp: SCSI command from the mid-layer + * @mfi_sgl: SGL to be filled in + * + * If successful, this function returns the number of SG elements. Otherwise, + * it returnes -1. + */ +static inline int +megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, + union megasas_sgl *mfi_sgl) +{ + int i; + int sge_count; + struct scatterlist *os_sgl; + + /* + * Return 0 if there is no data transfer + */ + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { + mfi_sgl->sge64[0].phys_addr = pci_map_single(instance->pdev, + scp-> + request_buffer, + scp-> + request_bufflen, + scp-> + sc_data_direction); + + mfi_sgl->sge64[0].length = scp->request_bufflen; + + return 1; + } + + os_sgl = (struct scatterlist *)scp->request_buffer; + sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg, + scp->sc_data_direction); + + for (i = 0; i < sge_count; i++, os_sgl++) { + mfi_sgl->sge64[i].length = sg_dma_len(os_sgl); + mfi_sgl->sge64[i].phys_addr = sg_dma_address(os_sgl); + } + + return sge_count; +} + +/** + * megasas_build_dcdb - Prepares a direct cdb (DCDB) command + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to be prepared in + * + * This function prepares CDB commands. These are typcially pass-through + * commands to the devices. + */ +static inline 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; + struct megasas_pthru_frame *pthru; + + is_logical = MEGASAS_IS_LOGICAL(scp); + device_id = MEGASAS_DEV_INDEX(instance, scp); + pthru = (struct megasas_pthru_frame *)cmd->frame; + + if (scp->sc_data_direction == PCI_DMA_TODEVICE) + flags = MFI_FRAME_DIR_WRITE; + else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + flags = MFI_FRAME_DIR_READ; + else if (scp->sc_data_direction == PCI_DMA_NONE) + flags = MFI_FRAME_DIR_NONE; + + /* + * Prepare the DCDB frame + */ + pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO : MFI_CMD_PD_SCSI_IO; + pthru->cmd_status = 0x0; + pthru->scsi_status = 0x0; + pthru->target_id = device_id; + pthru->lun = scp->device->lun; + pthru->cdb_len = scp->cmd_len; + pthru->timeout = 0; + pthru->flags = flags; + pthru->data_xfer_len = scp->request_bufflen; + + memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); + + /* + * 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, + &pthru->sgl); + } else + pthru->sge_count = megasas_make_sgl32(instance, scp, + &pthru->sgl); + + /* + * Sense info specific + */ + pthru->sense_len = SCSI_SENSE_BUFFERSIZE; + 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; + + return cmd->frame_count; +} + +/** + * megasas_build_ldio - Prepares IOs to logical devices + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to to be prepared + * + * Frames (and accompanying SGLs) for regular SCSI IOs use this function. + */ +static inline 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; + struct megasas_io_frame *ldio; + + device_id = MEGASAS_DEV_INDEX(instance, scp); + ldio = (struct megasas_io_frame *)cmd->frame; + + if (scp->sc_data_direction == PCI_DMA_TODEVICE) + flags = MFI_FRAME_DIR_WRITE; + else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + flags = MFI_FRAME_DIR_READ; + + /* + * Preare 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; + ldio->scsi_status = 0x0; + ldio->target_id = device_id; + ldio->timeout = 0; + ldio->reserved_0 = 0; + ldio->pad_0 = 0; + ldio->flags = flags; + ldio->start_lba_hi = 0; + ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0; + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if (scp->cmd_len == 6) { + ldio->lba_count = (u32) scp->cmnd[4]; + ldio->start_lba_lo = ((u32) scp->cmnd[1] << 16) | + ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3]; + + ldio->start_lba_lo &= 0x1FFFFF; + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + else if (scp->cmd_len == 10) { + ldio->lba_count = (u32) scp->cmnd[8] | + ((u32) scp->cmnd[7] << 8); + ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + else if (scp->cmd_len == 12) { + ldio->lba_count = ((u32) scp->cmnd[6] << 24) | + ((u32) scp->cmnd[7] << 16) | + ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; + + ldio->start_lba_lo = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + } + + /* + * 16-byte READ(0x88) or WRITE(0x8A) cdb + */ + else if (scp->cmd_len == 16) { + ldio->lba_count = ((u32) scp->cmnd[10] << 24) | + ((u32) scp->cmnd[11] << 16) | + ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13]; + + ldio->start_lba_lo = ((u32) scp->cmnd[6] << 24) | + ((u32) scp->cmnd[7] << 16) | + ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; + + ldio->start_lba_hi = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + + } + + /* + * 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); + } else + ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); + + /* + * Sense info specific + */ + ldio->sense_len = SCSI_SENSE_BUFFERSIZE; + 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; + + return cmd->frame_count; +} + +/** + * megasas_build_cmd - Prepares a command packet + * @instance: Adapter soft state + * @scp: SCSI command + * @frame_count: [OUT] Number of frames used to prepare this command + */ +static inline struct megasas_cmd *megasas_build_cmd(struct megasas_instance + *instance, + struct scsi_cmnd *scp, + int *frame_count) +{ + u32 logical_cmd; + struct megasas_cmd *cmd; + + /* + * Find out if this is logical or physical drive command. + */ + logical_cmd = MEGASAS_IS_LOGICAL(scp); + + /* + * Logical drive command + */ + if (logical_cmd) { + + if (scp->device->id >= MEGASAS_MAX_LD) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + switch (scp->cmnd[0]) { + + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + case READ_6: + case WRITE_6: + case READ_16: + case WRITE_16: + /* + * Fail for LUN > 0 + */ + if (scp->device->lun) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + scp->result = DID_IMM_RETRY << 16; + return NULL; + } + + *frame_count = megasas_build_ldio(instance, scp, cmd); + + if (!(*frame_count)) { + megasas_return_cmd(instance, cmd); + return NULL; + } + + return cmd; + + default: + /* + * Fail for LUN > 0 + */ + if (scp->device->lun) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + scp->result = DID_IMM_RETRY << 16; + return NULL; + } + + *frame_count = megasas_build_dcdb(instance, scp, cmd); + + if (!(*frame_count)) { + megasas_return_cmd(instance, cmd); + return NULL; + } + + return cmd; + } + } else { + cmd = megasas_get_cmd(instance); + + if (!cmd) { + scp->result = DID_IMM_RETRY << 16; + return NULL; + } + + *frame_count = megasas_build_dcdb(instance, scp, cmd); + + if (!(*frame_count)) { + megasas_return_cmd(instance, cmd); + return NULL; + } + + return cmd; + } + + return NULL; +} + +/** + * megasas_queue_command - Queue entry point + * @scmd: SCSI command to be queued + * @done: Callback entry point + */ +static int +megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) +{ + u32 frame_count; + unsigned long flags; + struct megasas_cmd *cmd; + struct megasas_instance *instance; + + instance = (struct megasas_instance *) + scmd->device->host->hostdata; + scmd->scsi_done = done; + scmd->result = 0; + + cmd = megasas_build_cmd(instance, scmd, &frame_count); + + if (!cmd) { + done(scmd); + return 0; + } + + cmd->scmd = scmd; + scmd->SCp.ptr = (char *)cmd; + scmd->SCp.sent_command = jiffies; + + /* + * Issue the command to the FW + */ + spin_lock_irqsave(&instance->instance_lock, flags); + instance->fw_outstanding++; + spin_unlock_irqrestore(&instance->instance_lock, flags); + + writel(((cmd->frame_phys_addr >> 3) | (cmd->frame_count - 1)), + &instance->reg_set->inbound_queue_port); + + return 0; +} + +/** + * megasas_wait_for_outstanding - Wait for all outstanding cmds + * @instance: Adapter soft state + * + * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to + * complete all its outstanding commands. Returns error if one or more IOs + * are pending after this time period. It also marks the controller dead. + */ +static int megasas_wait_for_outstanding(struct megasas_instance *instance) +{ + int i; + u32 wait_time = MEGASAS_RESET_WAIT_TIME; + + for (i = 0; i < wait_time; i++) { + + if (!instance->fw_outstanding) + break; + + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { + printk(KERN_NOTICE "megasas: [%2d]waiting for %d " + "commands to complete\n", i, + instance->fw_outstanding); + } + + msleep(1000); + } + + if (instance->fw_outstanding) { + instance->hw_crit_error = 1; + return FAILED; + } + + return SUCCESS; +} + +/** + * megasas_generic_reset - Generic reset routine + * @scmd: Mid-layer SCSI command + * + * This routine implements a generic reset handler for device, bus and host + * reset requests. Device, bus and host specific reset handlers can use this + * function after they do their specific tasks. + */ +static int megasas_generic_reset(struct scsi_cmnd *scmd) +{ + int ret_val; + struct megasas_instance *instance; + + instance = (struct megasas_instance *)scmd->device->host->hostdata; + + printk(KERN_NOTICE "megasas: RESET -%ld cmd=%x <c=%d t=%d l=%d>\n", + scmd->serial_number, scmd->cmnd[0], scmd->device->channel, + scmd->device->id, scmd->device->lun); + + if (instance->hw_crit_error) { + printk(KERN_ERR "megasas: cannot recover from previous reset " + "failures\n"); + return FAILED; + } + + spin_unlock(scmd->device->host->host_lock); + + ret_val = megasas_wait_for_outstanding(instance); + + if (ret_val == SUCCESS) + printk(KERN_NOTICE "megasas: reset successful \n"); + else + printk(KERN_ERR "megasas: failed to do reset\n"); + + spin_lock(scmd->device->host->host_lock); + + return ret_val; +} + +static enum scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) +{ + unsigned long seconds; + + if (scmd->SCp.ptr) { + seconds = (jiffies - scmd->SCp.sent_command) / HZ; + + if (seconds < 90) { + return EH_RESET_TIMER; + } else { + return EH_NOT_HANDLED; + } + } + + return EH_HANDLED; +} + +/** + * megasas_reset_device - Device reset handler entry point + */ +static int megasas_reset_device(struct scsi_cmnd *scmd) +{ + int ret; + + /* + * First wait for all commands to complete + */ + ret = megasas_generic_reset(scmd); + + return ret; +} + +/** + * megasas_reset_bus_host - Bus & host reset handler entry point + */ +static int megasas_reset_bus_host(struct scsi_cmnd *scmd) +{ + int ret; + + /* + * Frist wait for all commands to complete + */ + ret = megasas_generic_reset(scmd); + + return ret; +} + +/** + * megasas_service_aen - Processes an event notification + * @instance: Adapter soft state + * @cmd: AEN command completed by the ISR + * + * For AEN, driver sends a command down to FW that is held by the FW till an + * event occurs. When an event of interest occurs, FW completes the command + * that it was previously holding. + * + * This routines sends SIGIO signal to processes that have registered with the + * driver for AEN. + */ +static void +megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + /* + * Don't signal app if it is just an aborted previously registered aen + */ + if (!cmd->abort_aen) + kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); + else + cmd->abort_aen = 0; + + instance->aen_cmd = NULL; + megasas_return_cmd(instance, cmd); +} + +/* + * Scsi host template for megaraid_sas driver + */ +static struct scsi_host_template megasas_template = { + + .module = THIS_MODULE, + .name = "LSI Logic SAS based MegaRAID driver", + .proc_name = "megaraid_sas", + .queuecommand = megasas_queue_command, + .eh_device_reset_handler = megasas_reset_device, + .eh_bus_reset_handler = megasas_reset_bus_host, + .eh_host_reset_handler = megasas_reset_bus_host, + .eh_timed_out = megasas_reset_timer, + .use_clustering = ENABLE_CLUSTERING, +}; + +/** + * megasas_complete_int_cmd - Completes an internal command + * @instance: Adapter soft state + * @cmd: Command to be completed + * + * The megasas_issue_blocked_cmd() function waits for a command to complete + * after it issues a command. This function wakes up that waiting routine by + * calling wake_up() on the wait queue. + */ +static void +megasas_complete_int_cmd(struct megasas_instance *instance, + struct megasas_cmd *cmd) +{ + cmd->cmd_status = cmd->frame->io.cmd_status; + + if (cmd->cmd_status == ENODATA) { + cmd->cmd_status = 0; + } + wake_up(&instance->int_cmd_wait_q); +} + +/** + * megasas_complete_abort - Completes aborting a command + * @instance: Adapter soft state + * @cmd: Cmd that was issued to abort another cmd + * + * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q + * after it issues an abort on a previously issued command. This function + * wakes up all functions waiting on the same wait queue. + */ +static void +megasas_complete_abort(struct megasas_instance *instance, + struct megasas_cmd *cmd) +{ + if (cmd->sync_cmd) { + cmd->sync_cmd = 0; + cmd->cmd_status = 0; + wake_up(&instance->abort_cmd_wait_q); + } + + return; +} + +/** + * megasas_unmap_sgbuf - Unmap SG buffers + * @instance: Adapter soft state + * @cmd: Completed command + */ +static inline void +megasas_unmap_sgbuf(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + dma_addr_t buf_h; + u8 opcode; + + if (cmd->scmd->use_sg) { + pci_unmap_sg(instance->pdev, cmd->scmd->request_buffer, + cmd->scmd->use_sg, cmd->scmd->sc_data_direction); + return; + } + + if (!cmd->scmd->request_bufflen) + return; + + opcode = cmd->frame->hdr.cmd; + + if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) { + if (IS_DMA64) + buf_h = cmd->frame->io.sgl.sge64[0].phys_addr; + else + buf_h = cmd->frame->io.sgl.sge32[0].phys_addr; + } else { + if (IS_DMA64) + buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr; + else + buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr; + } + + pci_unmap_single(instance->pdev, buf_h, cmd->scmd->request_bufflen, + cmd->scmd->sc_data_direction); + return; +} + +/** + * megasas_complete_cmd - Completes a command + * @instance: Adapter soft state + * @cmd: Command to be completed + * @alt_status: If non-zero, use this value as status to + * SCSI mid-layer instead of the value returned + * by the FW. This should be used if caller wants + * an alternate status (as in the case of aborted + * commands) + */ +static inline void +megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, + u8 alt_status) +{ + int exception = 0; + struct megasas_header *hdr = &cmd->frame->hdr; + unsigned long flags; + + if (cmd->scmd) { + cmd->scmd->SCp.ptr = (char *)0; + } + + switch (hdr->cmd) { + + case MFI_CMD_PD_SCSI_IO: + case MFI_CMD_LD_SCSI_IO: + + /* + * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been + * issued either through an IO path or an IOCTL path. If it + * was via IOCTL, we will send it to internal completion. + */ + if (cmd->sync_cmd) { + cmd->sync_cmd = 0; + megasas_complete_int_cmd(instance, cmd); + break; + } + + /* + * Don't export physical disk devices to mid-layer. + */ + if (!MEGASAS_IS_LOGICAL(cmd->scmd) && + (hdr->cmd_status == MFI_STAT_OK) && + (cmd->scmd->cmnd[0] == INQUIRY)) { + + if (((*(u8 *) cmd->scmd->request_buffer) & 0x1F) == + TYPE_DISK) { + cmd->scmd->result = DID_BAD_TARGET << 16; + exception = 1; + } + } + + case MFI_CMD_LD_READ: + case MFI_CMD_LD_WRITE: + + if (alt_status) { + cmd->scmd->result = alt_status << 16; + exception = 1; + } + + if (exception) { + + spin_lock_irqsave(&instance->instance_lock, flags); + instance->fw_outstanding--; + spin_unlock_irqrestore(&instance->instance_lock, flags); + + megasas_unmap_sgbuf(instance, cmd); + cmd->scmd->scsi_done(cmd->scmd); + megasas_return_cmd(instance, cmd); + + break; + } + + switch (hdr->cmd_status) { + + case MFI_STAT_OK: + cmd->scmd->result = DID_OK << 16; + break; + + case MFI_STAT_SCSI_IO_FAILED: + case MFI_STAT_LD_INIT_IN_PROGRESS: + cmd->scmd->result = + (DID_ERROR << 16) | hdr->scsi_status; + break; + + case MFI_STAT_SCSI_DONE_WITH_ERROR: + + cmd->scmd->result = (DID_OK << 16) | hdr->scsi_status; + + if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) { + memset(cmd->scmd->sense_buffer, 0, + SCSI_SENSE_BUFFERSIZE); + memcpy(cmd->scmd->sense_buffer, cmd->sense, + hdr->sense_len); + + cmd->scmd->result |= DRIVER_SENSE << 24; + } + + break; + + case MFI_STAT_LD_OFFLINE: + case MFI_STAT_DEVICE_NOT_FOUND: + cmd->scmd->result = DID_BAD_TARGET << 16; + break; + + default: + printk(KERN_DEBUG "megasas: MFI FW status %#x\n", + hdr->cmd_status); + cmd->scmd->result = DID_ERROR << 16; + break; + } + + spin_lock_irqsave(&instance->instance_lock, flags); + instance->fw_outstanding--; + spin_unlock_irqrestore(&instance->instance_lock, flags); + + megasas_unmap_sgbuf(instance, cmd); + cmd->scmd->scsi_done(cmd->scmd); + megasas_return_cmd(instance, cmd); + + break; + + case MFI_CMD_SMP: + case MFI_CMD_STP: + case MFI_CMD_DCMD: + + /* + * See if got an event notification + */ + if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT) + megasas_service_aen(instance, cmd); + else + megasas_complete_int_cmd(instance, cmd); + + break; + + case MFI_CMD_ABORT: + /* + * Cmd issued to abort another cmd returned + */ + megasas_complete_abort(instance, cmd); + break; + + default: + printk("megasas: Unknown command completed! [0x%X]\n", + hdr->cmd); + break; + } +} + +/** + * megasas_deplete_reply_queue - Processes all completed commands + * @instance: Adapter soft state + * @alt_status: Alternate status to be returned to + * SCSI mid-layer instead of the status + * returned by the FW + */ +static inline int +megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) +{ + u32 status; + u32 producer; + u32 consumer; + u32 context; + struct megasas_cmd *cmd; + + /* + * Check if it is our interrupt + */ + status = readl(&instance->reg_set->outbound_intr_status); + + if (!(status & MFI_OB_INTR_STATUS_MASK)) { + return IRQ_NONE; + } + + /* + * Clear the interrupt by writing back the same value + */ + writel(status, &instance->reg_set->outbound_intr_status); + + 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; + + return IRQ_HANDLED; +} + +/** + * megasas_isr - isr entry point + */ +static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs) +{ + return megasas_deplete_reply_queue((struct megasas_instance *)devp, + DID_OK); +} + +/** + * megasas_transition_to_ready - Move the FW to READY state + * @reg_set: MFI register set + * + * During the initialization, FW passes can potentially be in any one of + * several possible states. If the FW in operational, waiting-for-handshake + * states, driver must take steps to bring it to ready state. Otherwise, it + * has to wait for the ready state. + */ +static int +megasas_transition_to_ready(struct megasas_register_set __iomem * reg_set) +{ + int i; + u8 max_wait; + u32 fw_state; + u32 cur_state; + + fw_state = readl(®_set->outbound_msg_0) & MFI_STATE_MASK; + + 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: + + printk(KERN_DEBUG "megasas: FW in FAULT state!!\n"); + return -ENODEV; + + case MFI_STATE_WAIT_HANDSHAKE: + /* + * Set the CLR bit in inbound doorbell + */ + writel(MFI_INIT_CLEAR_HANDSHAKE, + ®_set->inbound_doorbell); + + max_wait = 2; + cur_state = MFI_STATE_WAIT_HANDSHAKE; + break; + + case MFI_STATE_OPERATIONAL: + /* + * Bring it to READY state; assuming max wait 2 secs + */ + megasas_disable_intr(reg_set); + writel(MFI_INIT_READY, ®_set->inbound_doorbell); + + max_wait = 10; + cur_state = MFI_STATE_OPERATIONAL; + break; + + case MFI_STATE_UNDEFINED: + /* + * This state should not last for more than 2 seconds + */ + max_wait = 2; + cur_state = MFI_STATE_UNDEFINED; + break; + + case MFI_STATE_BB_INIT: + max_wait = 2; + cur_state = MFI_STATE_BB_INIT; + break; + + case MFI_STATE_FW_INIT: + max_wait = 20; + cur_state = MFI_STATE_FW_INIT; + break; + + case MFI_STATE_FW_INIT_2: + max_wait = 20; + cur_state = MFI_STATE_FW_INIT_2; + break; + + case MFI_STATE_DEVICE_SCAN: + max_wait = 20; + cur_state = MFI_STATE_DEVICE_SCAN; + break; + + case MFI_STATE_FLUSH_CACHE: + max_wait = 20; + cur_state = MFI_STATE_FLUSH_CACHE; + break; + + default: + printk(KERN_DEBUG "megasas: Unknown state 0x%x\n", + fw_state); + return -ENODEV; + } + + /* + * The cur_state should not last for more than max_wait secs + */ + for (i = 0; i < (max_wait * 1000); i++) { + fw_state = MFI_STATE_MASK & + readl(®_set->outbound_msg_0); + + if (fw_state == cur_state) { + msleep(1); + } else + break; + } + + /* + * Return error if fw_state hasn't changed after max_wait + */ + if (fw_state == cur_state) { + printk(KERN_DEBUG "FW state [%d] hasn't changed " + "in %d secs\n", fw_state, max_wait); + return -ENODEV; + } + }; + + return 0; +} + +/** + * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool + * @instance: Adapter soft state + */ +static void megasas_teardown_frame_pool(struct megasas_instance *instance) +{ + int i; + u32 max_cmd = instance->max_fw_cmds; + struct megasas_cmd *cmd; + + if (!instance->frame_dma_pool) + return; + + /* + * Return all frames to pool + */ + for (i = 0; i < max_cmd; i++) { + + cmd = instance->cmd_list[i]; + + if (cmd->frame) + pci_pool_free(instance->frame_dma_pool, cmd->frame, + cmd->frame_phys_addr); + + if (cmd->sense) + pci_pool_free(instance->sense_dma_pool, cmd->frame, + cmd->sense_phys_addr); + } + + /* + * Now destroy the pool itself + */ + pci_pool_destroy(instance->frame_dma_pool); + pci_pool_destroy(instance->sense_dma_pool); + + instance->frame_dma_pool = NULL; + instance->sense_dma_pool = NULL; +} + +/** + * megasas_create_frame_pool - Creates DMA pool for cmd frames + * @instance: Adapter soft state + * + * Each command packet has an embedded DMA memory buffer that is used for + * filling MFI frame and the SG list that immediately follows the frame. This + * function creates those DMA memory buffers for each command packet by using + * PCI pool facility. + */ +static int megasas_create_frame_pool(struct megasas_instance *instance) +{ + int i; + u32 max_cmd; + u32 sge_sz; + u32 sgl_sz; + u32 total_sz; + u32 frame_count; + struct megasas_cmd *cmd; + + max_cmd = instance->max_fw_cmds; + + /* + * Size of our frame is 64 bytes for MFI frame, followed by max SG + * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer + */ + sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : + sizeof(struct megasas_sge32); + + /* + * Calculated the number of 64byte frames required for SGL + */ + sgl_sz = sge_sz * instance->max_num_sge; + frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE; + + /* + * We need one extra frame for the MFI command + */ + frame_count++; + + total_sz = MEGAMFI_FRAME_SIZE * frame_count; + /* + * Use DMA pool facility provided by PCI layer + */ + instance->frame_dma_pool = pci_pool_create("megasas frame pool", + instance->pdev, total_sz, 64, + 0); + + if (!instance->frame_dma_pool) { + printk(KERN_DEBUG "megasas: failed to setup frame pool\n"); + return -ENOMEM; + } + + instance->sense_dma_pool = pci_pool_create("megasas sense pool", + instance->pdev, 128, 4, 0); + + if (!instance->sense_dma_pool) { + printk(KERN_DEBUG "megasas: failed to setup sense pool\n"); + + pci_pool_destroy(instance->frame_dma_pool); + instance->frame_dma_pool = NULL; + + return -ENOMEM; + } + + /* + * Allocate and attach a frame to each of the commands in cmd_list. + * By making cmd->index as the context instead of the &cmd, we can + * always use 32bit context regardless of the architecture + */ + for (i = 0; i < max_cmd; i++) { + + cmd = instance->cmd_list[i]; + + cmd->frame = pci_pool_alloc(instance->frame_dma_pool, + GFP_KERNEL, &cmd->frame_phys_addr); + + cmd->sense = pci_pool_alloc(instance->sense_dma_pool, + GFP_KERNEL, &cmd->sense_phys_addr); + + /* + * megasas_teardown_frame_pool() takes care of freeing + * whatever has been allocated + */ + if (!cmd->frame || !cmd->sense) { + printk(KERN_DEBUG "megasas: pci_pool_alloc failed \n"); + megasas_teardown_frame_pool(instance); + return -ENOMEM; + } + + cmd->frame->io.context = cmd->index; + } + + return 0; +} + +/** + * megasas_free_cmds - Free all the cmds in the free cmd pool + * @instance: Adapter soft state + */ +static void megasas_free_cmds(struct megasas_instance *instance) +{ + int i; + /* First free the MFI frame pool */ + megasas_teardown_frame_pool(instance); + + /* Free all the commands in the cmd_list */ + for (i = 0; i < instance->max_fw_cmds; i++) + kfree(instance->cmd_list[i]); + + /* Free the cmd_list buffer itself */ + kfree(instance->cmd_list); + instance->cmd_list = NULL; + + INIT_LIST_HEAD(&instance->cmd_pool); +} + +/** + * megasas_alloc_cmds - Allocates the command packets + * @instance: Adapter soft state + * + * Each command that is issued to the FW, whether IO commands from the OS or + * internal commands like IOCTLs, are wrapped in local data structure called + * megasas_cmd. The frame embedded in this megasas_cmd is actually issued to + * the FW. + * + * Each frame has a 32-bit field called context (tag). This context is used + * to get back the megasas_cmd from the frame when a frame gets completed in + * the ISR. Typically the address of the megasas_cmd itself would be used as + * the context. But we wanted to keep the differences between 32 and 64 bit + * systems to the mininum. We always use 32 bit integers for the context. In + * this driver, the 32 bit values are the indices into an array cmd_list. + * This array is used only to look up the megasas_cmd given the context. The + * free commands themselves are maintained in a linked list called cmd_pool. + */ +static int megasas_alloc_cmds(struct megasas_instance *instance) +{ + int i; + int j; + u32 max_cmd; + struct megasas_cmd *cmd; + + max_cmd = instance->max_fw_cmds; + + /* + * instance->cmd_list is an array of struct megasas_cmd pointers. + * Allocate the dynamic array first and then allocate individual + * commands. + */ + instance->cmd_list = kmalloc(sizeof(struct megasas_cmd *) * max_cmd, + GFP_KERNEL); + + if (!instance->cmd_list) { + printk(KERN_DEBUG "megasas: out of memory\n"); + return -ENOMEM; + } + + memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) * max_cmd); + + for (i = 0; i < max_cmd; i++) { + instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd), + GFP_KERNEL); + + if (!instance->cmd_list[i]) { + + for (j = 0; j < i; j++) + kfree(instance->cmd_list[j]); + + kfree(instance->cmd_list); + instance->cmd_list = NULL; + + return -ENOMEM; + } + } + + /* + * Add all the commands to command pool (instance->cmd_pool) + */ + for (i = 0; i < max_cmd; i++) { + cmd = instance->cmd_list[i]; + memset(cmd, 0, sizeof(struct megasas_cmd)); + cmd->index = i; + cmd->instance = instance; + + list_add_tail(&cmd->list, &instance->cmd_pool); + } + + /* + * Create a frame pool and assign one frame to each cmd + */ + if (megasas_create_frame_pool(instance)) { + printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n"); + megasas_free_cmds(instance); + } + + return 0; +} + +/** + * megasas_get_controller_info - Returns FW's controller structure + * @instance: Adapter soft state + * @ctrl_info: Controller information structure + * + * Issues an internal command (DCMD) to get the FW's controller structure. + * This information is mainly used to find out the maximum IO transfer per + * command supported by the FW. + */ +static int +megasas_get_ctrl_info(struct megasas_instance *instance, + struct megasas_ctrl_info *ctrl_info) +{ + int ret = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct megasas_ctrl_info *ci; + dma_addr_t ci_h = 0; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas: Failed to get a free cmd\n"); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + ci = pci_alloc_consistent(instance->pdev, + sizeof(struct megasas_ctrl_info), &ci_h); + + if (!ci) { + printk(KERN_DEBUG "Failed to alloc mem for ctrl info\n"); + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info); + dcmd->opcode = MR_DCMD_CTRL_GET_INFO; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = sizeof(struct megasas_ctrl_info); + + if (!megasas_issue_polled(instance, cmd)) { + ret = 0; + memcpy(ctrl_info, ci, sizeof(struct megasas_ctrl_info)); + } else { + ret = -1; + } + + pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info), + ci, ci_h); + + megasas_return_cmd(instance, cmd); + return ret; +} + +/** + * megasas_init_mfi - Initializes the FW + * @instance: Adapter soft state + * + * This is the main function for initializing MFI firmware. + */ +static int megasas_init_mfi(struct megasas_instance *instance) +{ + u32 context_sz; + u32 reply_q_sz; + u32 max_sectors_1; + u32 max_sectors_2; + struct megasas_register_set __iomem *reg_set; + + struct megasas_cmd *cmd; + struct megasas_ctrl_info *ctrl_info; + + struct megasas_init_frame *init_frame; + struct megasas_init_queue_info *initq_info; + dma_addr_t init_frame_h; + dma_addr_t initq_info_h; + + /* + * Map the message registers + */ + instance->base_addr = pci_resource_start(instance->pdev, 0); + + if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) { + printk(KERN_DEBUG "megasas: IO memory region busy!\n"); + return -EBUSY; + } + + instance->reg_set = ioremap_nocache(instance->base_addr, 8192); + + if (!instance->reg_set) { + printk(KERN_DEBUG "megasas: Failed to map IO mem\n"); + goto fail_ioremap; + } + + reg_set = instance->reg_set; + + /* + * We expect the FW state to be READY + */ + if (megasas_transition_to_ready(instance->reg_set)) + goto fail_ready_state; + + /* + * Get various operational parameters from status register + */ + instance->max_fw_cmds = readl(®_set->outbound_msg_0) & 0x00FFFF; + instance->max_num_sge = (readl(®_set->outbound_msg_0) & 0xFF0000) >> + 0x10; + /* + * Create a pool of commands + */ + if (megasas_alloc_cmds(instance)) + goto fail_alloc_cmds; + + /* + * Allocate memory for reply queue. Length of reply queue should + * be _one_ more than the maximum commands handled by the firmware. + * + * Note: When FW completes commands, it places corresponding contex + * values in this circular reply queue. This circular queue is a fairly + * typical producer-consumer queue. FW is the producer (of completed + * commands) and the driver is the consumer. + */ + context_sz = sizeof(u32); + reply_q_sz = context_sz * (instance->max_fw_cmds + 1); + + instance->reply_queue = pci_alloc_consistent(instance->pdev, + reply_q_sz, + &instance->reply_queue_h); + + if (!instance->reply_queue) { + printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n"); + goto fail_reply_queue; + } + + /* + * Prepare a init frame. Note the init frame points to queue info + * structure. Each frame has SGL allocated after first 64 bytes. For + * this frame - since we don't need any SGL - we use SGL's space as + * queue info structure + * + * We will not get a NULL command below. We just created the pool. + */ + cmd = megasas_get_cmd(instance); + + init_frame = (struct megasas_init_frame *)cmd->frame; + initq_info = (struct megasas_init_queue_info *) + ((unsigned long)init_frame + 64); + + init_frame_h = cmd->frame_phys_addr; + initq_info_h = init_frame_h + 64; + + memset(init_frame, 0, MEGAMFI_FRAME_SIZE); + memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); + + initq_info->reply_queue_entries = instance->max_fw_cmds + 1; + initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h; + + initq_info->producer_index_phys_addr_lo = instance->producer_h; + initq_info->consumer_index_phys_addr_lo = instance->consumer_h; + + init_frame->cmd = MFI_CMD_INIT; + init_frame->cmd_status = 0xFF; + init_frame->queue_info_new_phys_addr_lo = initq_info_h; + + init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info); + + /* + * Issue the init frame in polled mode + */ + if (megasas_issue_polled(instance, cmd)) { + printk(KERN_DEBUG "megasas: Failed to init firmware\n"); + goto fail_fw_init; + } + + megasas_return_cmd(instance, cmd); + + ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); + + /* + * Compute the max allowed sectors per IO: The controller info has two + * limits on max sectors. Driver should use the minimum of these two. + * + * 1 << stripe_sz_ops.min = max sectors per strip + * + * Note that older firmwares ( < FW ver 30) didn't report information + * to calculate max_sectors_1. So the number ended up as zero always. + */ + if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) { + + max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) * + ctrl_info->max_strips_per_io; + max_sectors_2 = ctrl_info->max_request_size; + + instance->max_sectors_per_req = (max_sectors_1 < max_sectors_2) + ? max_sectors_1 : max_sectors_2; + } else + instance->max_sectors_per_req = instance->max_num_sge * + PAGE_SIZE / 512; + + kfree(ctrl_info); + + return 0; + + fail_fw_init: + megasas_return_cmd(instance, cmd); + + pci_free_consistent(instance->pdev, reply_q_sz, + instance->reply_queue, instance->reply_queue_h); + fail_reply_queue: + megasas_free_cmds(instance); + + fail_alloc_cmds: + fail_ready_state: + iounmap(instance->reg_set); + + fail_ioremap: + pci_release_regions(instance->pdev); + + return -EINVAL; +} + +/** + * megasas_release_mfi - Reverses the FW initialization + * @intance: Adapter soft state + */ +static void megasas_release_mfi(struct megasas_instance *instance) +{ + u32 reply_q_sz = sizeof(u32) * (instance->max_fw_cmds + 1); + + pci_free_consistent(instance->pdev, reply_q_sz, + instance->reply_queue, instance->reply_queue_h); + + megasas_free_cmds(instance); + + iounmap(instance->reg_set); + + pci_release_regions(instance->pdev); +} + +/** + * megasas_get_seq_num - Gets latest event sequence numbers + * @instance: Adapter soft state + * @eli: FW event log sequence numbers information + * + * FW maintains a log of all events in a non-volatile area. Upper layers would + * usually find out the latest sequence number of the events, the seq number at + * the boot etc. They would "read" all the events below the latest seq number + * by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq + * number), they would subsribe to AEN (asynchronous event notification) and + * wait for the events to happen. + */ +static int +megasas_get_seq_num(struct megasas_instance *instance, + struct megasas_evt_log_info *eli) +{ + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct megasas_evt_log_info *el_info; + dma_addr_t el_info_h = 0; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + el_info = pci_alloc_consistent(instance->pdev, + sizeof(struct megasas_evt_log_info), + &el_info_h); + + if (!el_info) { + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + memset(el_info, 0, sizeof(*el_info)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info); + dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO; + dcmd->sgl.sge32[0].phys_addr = el_info_h; + dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_log_info); + + megasas_issue_blocked_cmd(instance, cmd); + + /* + * Copy the data back into callers buffer + */ + memcpy(eli, el_info, sizeof(struct megasas_evt_log_info)); + + pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info), + el_info, el_info_h); + + megasas_return_cmd(instance, cmd); + + return 0; +} + +/** + * megasas_register_aen - Registers for asynchronous event notification + * @instance: Adapter soft state + * @seq_num: The starting sequence number + * @class_locale: Class of the event + * + * This function subscribes for AEN for events beyond the @seq_num. It requests + * to be notified if and only if the event is of type @class_locale + */ +static int +megasas_register_aen(struct megasas_instance *instance, u32 seq_num, + u32 class_locale_word) +{ + int ret_val; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + union megasas_evt_class_locale curr_aen; + union megasas_evt_class_locale prev_aen; + + /* + * If there an AEN pending already (aen_cmd), check if the + * class_locale of that pending AEN is inclusive of the new + * AEN request we currently have. If it is, then we don't have + * to do anything. In other words, whichever events the current + * AEN request is subscribing to, have already been subscribed + * to. + * + * If the old_cmd is _not_ inclusive, then we have to abort + * that command, form a class_locale that is superset of both + * old and current and re-issue to the FW + */ + + curr_aen.word = class_locale_word; + + if (instance->aen_cmd) { + + prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1]; + + /* + * A class whose enum value is smaller is inclusive of all + * higher values. If a PROGRESS (= -1) was previously + * registered, then a new registration requests for higher + * classes need not be sent to FW. They are automatically + * included. + * + * Locale numbers don't have such hierarchy. They are bitmap + * values + */ + if ((prev_aen.members.class <= curr_aen.members.class) && + !((prev_aen.members.locale & curr_aen.members.locale) ^ + curr_aen.members.locale)) { + /* + * Previously issued event registration includes + * current request. Nothing to do. + */ + return 0; + } else { + curr_aen.members.locale |= prev_aen.members.locale; + + if (prev_aen.members.class < curr_aen.members.class) + curr_aen.members.class = prev_aen.members.class; + + instance->aen_cmd->abort_aen = 1; + ret_val = megasas_issue_blocked_abort_cmd(instance, + instance-> + aen_cmd); + + if (ret_val) { + printk(KERN_DEBUG "megasas: Failed to abort " + "previous AEN command\n"); + return ret_val; + } + } + } + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return -ENOMEM; + + dcmd = &cmd->frame->dcmd; + + memset(instance->evt_detail, 0, sizeof(struct megasas_evt_detail)); + + /* + * Prepare DCMD for aen registration + */ + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->data_xfer_len = sizeof(struct megasas_evt_detail); + dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT; + dcmd->mbox.w[0] = seq_num; + dcmd->mbox.w[1] = curr_aen.word; + dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h; + dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail); + + /* + * Store reference to the cmd used to register for AEN. When an + * application wants us to register for AEN, we have to abort this + * cmd and re-register with a new EVENT LOCALE supplied by that app + */ + instance->aen_cmd = cmd; + + /* + * Issue the aen registration frame + */ + writel(cmd->frame_phys_addr >> 3, + &instance->reg_set->inbound_queue_port); + + return 0; +} + +/** + * megasas_start_aen - Subscribes to AEN during driver load time + * @instance: Adapter soft state + */ +static int megasas_start_aen(struct megasas_instance *instance) +{ + struct megasas_evt_log_info eli; + union megasas_evt_class_locale class_locale; + + /* + * Get the latest sequence number from FW + */ + memset(&eli, 0, sizeof(eli)); + + if (megasas_get_seq_num(instance, &eli)) + return -1; + + /* + * Register AEN with FW for latest sequence number plus 1 + */ + class_locale.members.reserved = 0; + class_locale.members.locale = MR_EVT_LOCALE_ALL; + class_locale.members.class = MR_EVT_CLASS_DEBUG; + + return megasas_register_aen(instance, eli.newest_seq_num + 1, + class_locale.word); +} + +/** + * megasas_io_attach - Attaches this driver to SCSI mid-layer + * @instance: Adapter soft state + */ +static int megasas_io_attach(struct megasas_instance *instance) +{ + struct Scsi_Host *host = instance->host; + + /* + * Export parameters required by SCSI mid-layer + */ + host->irq = instance->pdev->irq; + host->unique_id = instance->unique_id; + host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; + host->this_id = instance->init_id; + host->sg_tablesize = instance->max_num_sge; + host->max_sectors = instance->max_sectors_per_req; + host->cmd_per_lun = 128; + host->max_channel = MEGASAS_MAX_CHANNELS - 1; + host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL; + host->max_lun = MEGASAS_MAX_LUN; + + /* + * Notify the mid-layer about the new controller + */ + if (scsi_add_host(host, &instance->pdev->dev)) { + printk(KERN_DEBUG "megasas: scsi_add_host failed\n"); + return -ENODEV; + } + + /* + * Trigger SCSI to scan our drives + */ + scsi_scan_host(host); + return 0; +} + +/** + * megasas_probe_one - PCI hotplug entry point + * @pdev: PCI device structure + * @id: PCI ids of supported hotplugged adapter + */ +static int __devinit +megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int rval; + struct Scsi_Host *host; + struct megasas_instance *instance; + + /* + * Announce PCI information + */ + printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", + pdev->vendor, pdev->device, pdev->subsystem_vendor, + pdev->subsystem_device); + + printk("bus %d:slot %d:func %d\n", + pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + + /* + * PCI prepping: enable device set bus mastering and dma mask + */ + rval = pci_enable_device(pdev); + + if (rval) { + return rval; + } + + pci_set_master(pdev); + + /* + * All our contollers are capable of performing 64-bit DMA + */ + if (IS_DMA64) { + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) { + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) + goto fail_set_dma_mask; + } + } else { + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) + goto fail_set_dma_mask; + } + + host = scsi_host_alloc(&megasas_template, + sizeof(struct megasas_instance)); + + if (!host) { + printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n"); + goto fail_alloc_instance; + } + + instance = (struct megasas_instance *)host->hostdata; + memset(instance, 0, sizeof(*instance)); + + instance->producer = pci_alloc_consistent(pdev, sizeof(u32), + &instance->producer_h); + instance->consumer = pci_alloc_consistent(pdev, sizeof(u32), + &instance->consumer_h); + + if (!instance->producer || !instance->consumer) { + printk(KERN_DEBUG "megasas: Failed to allocate memory for " + "producer, consumer\n"); + goto fail_alloc_dma_buf; + } + + *instance->producer = 0; + *instance->consumer = 0; + + instance->evt_detail = pci_alloc_consistent(pdev, + sizeof(struct + megasas_evt_detail), + &instance->evt_detail_h); + + if (!instance->evt_detail) { + printk(KERN_DEBUG "megasas: Failed to allocate memory for " + "event detail structure\n"); + goto fail_alloc_dma_buf; + } + + /* + * Initialize locks and queues + */ + INIT_LIST_HEAD(&instance->cmd_pool); + + init_waitqueue_head(&instance->int_cmd_wait_q); + init_waitqueue_head(&instance->abort_cmd_wait_q); + + spin_lock_init(&instance->cmd_pool_lock); + spin_lock_init(&instance->instance_lock); + + sema_init(&instance->aen_mutex, 1); + sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); + + /* + * Initialize PCI related and misc parameters + */ + instance->pdev = pdev; + instance->host = host; + instance->unique_id = pdev->bus->number << 8 | pdev->devfn; + instance->init_id = MEGASAS_DEFAULT_INIT_ID; + + /* + * Initialize MFI Firmware + */ + if (megasas_init_mfi(instance)) + goto fail_init_mfi; + + /* + * Register IRQ + */ + if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas", instance)) { + printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); + goto fail_irq; + } + + megasas_enable_intr(instance->reg_set); + + /* + * Store instance in PCI softstate + */ + pci_set_drvdata(pdev, instance); + + /* + * Add this controller to megasas_mgmt_info structure so that it + * can be exported to management applications + */ + megasas_mgmt_info.count++; + megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance; + megasas_mgmt_info.max_index++; + + /* + * Initiate AEN (Asynchronous Event Notification) + */ + if (megasas_start_aen(instance)) { + printk(KERN_DEBUG "megasas: start aen failed\n"); + goto fail_start_aen; + } + + /* + * Register with SCSI mid-layer + */ + if (megasas_io_attach(instance)) + goto fail_io_attach; + + return 0; + + fail_start_aen: + fail_io_attach: + megasas_mgmt_info.count--; + megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; + megasas_mgmt_info.max_index--; + + pci_set_drvdata(pdev, NULL); + megasas_disable_intr(instance->reg_set); + free_irq(instance->pdev->irq, instance); + + megasas_release_mfi(instance); + + fail_irq: + fail_init_mfi: + fail_alloc_dma_buf: + if (instance->evt_detail) + pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), + instance->evt_detail, + instance->evt_detail_h); + + if (instance->producer) + pci_free_consistent(pdev, sizeof(u32), instance->producer, + instance->producer_h); + if (instance->consumer) + pci_free_consistent(pdev, sizeof(u32), instance->consumer, + instance->consumer_h); + scsi_host_put(host); + + fail_alloc_instance: + fail_set_dma_mask: + pci_disable_device(pdev); + + return -ENODEV; +} + +/** + * megasas_flush_cache - Requests FW to flush all its caches + * @instance: Adapter soft state + */ +static void megasas_flush_cache(struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return; + + dcmd = &cmd->frame->dcmd; + + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 0; + dcmd->flags = MFI_FRAME_DIR_NONE; + dcmd->timeout = 0; + dcmd->data_xfer_len = 0; + dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH; + dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; + + megasas_issue_blocked_cmd(instance, cmd); + + megasas_return_cmd(instance, cmd); + + return; +} + +/** + * megasas_shutdown_controller - Instructs FW to shutdown the controller + * @instance: Adapter soft state + */ +static void megasas_shutdown_controller(struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return; + + if (instance->aen_cmd) + megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); + + dcmd = &cmd->frame->dcmd; + + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 0; + dcmd->flags = MFI_FRAME_DIR_NONE; + dcmd->timeout = 0; + dcmd->data_xfer_len = 0; + dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN; + + megasas_issue_blocked_cmd(instance, cmd); + + megasas_return_cmd(instance, cmd); + + return; +} + +/** + * megasas_detach_one - PCI hot"un"plug entry point + * @pdev: PCI device structure + */ +static void megasas_detach_one(struct pci_dev *pdev) +{ + int i; + struct Scsi_Host *host; + struct megasas_instance *instance; + + instance = pci_get_drvdata(pdev); + host = instance->host; + + scsi_remove_host(instance->host); + megasas_flush_cache(instance); + megasas_shutdown_controller(instance); + + /* + * Take the instance off the instance array. Note that we will not + * decrement the max_index. We let this array be sparse array + */ + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + if (megasas_mgmt_info.instance[i] == instance) { + megasas_mgmt_info.count--; + megasas_mgmt_info.instance[i] = NULL; + + break; + } + } + + pci_set_drvdata(instance->pdev, NULL); + + megasas_disable_intr(instance->reg_set); + + free_irq(instance->pdev->irq, instance); + + megasas_release_mfi(instance); + + pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), + instance->evt_detail, instance->evt_detail_h); + + pci_free_consistent(pdev, sizeof(u32), instance->producer, + instance->producer_h); + + pci_free_consistent(pdev, sizeof(u32), instance->consumer, + instance->consumer_h); + + scsi_host_put(host); + + pci_set_drvdata(pdev, NULL); + + pci_disable_device(pdev); + + return; +} + +/** + * megasas_shutdown - Shutdown entry point + * @device: Generic device structure + */ +static void megasas_shutdown(struct pci_dev *pdev) +{ + struct megasas_instance *instance = pci_get_drvdata(pdev); + megasas_flush_cache(instance); +} + +/** + * megasas_mgmt_open - char node "open" entry point + */ +static int megasas_mgmt_open(struct inode *inode, struct file *filep) +{ + /* + * Allow only those users with admin rights + */ + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + return 0; +} + +/** + * megasas_mgmt_release - char node "release" entry point + */ +static int megasas_mgmt_release(struct inode *inode, struct file *filep) +{ + filep->private_data = NULL; + fasync_helper(-1, filep, 0, &megasas_async_queue); + + return 0; +} + +/** + * megasas_mgmt_fasync - Async notifier registration from applications + * + * This function adds the calling process to a driver global queue. When an + * event occurs, SIGIO will be sent to all processes in this queue. + */ +static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) +{ + int rc; + + down(&megasas_async_queue_mutex); + + rc = fasync_helper(fd, filep, mode, &megasas_async_queue); + + up(&megasas_async_queue_mutex); + + if (rc >= 0) { + /* For sanity check when we get ioctl */ + filep->private_data = filep; + return 0; + } + + printk(KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc); + + return rc; +} + +/** + * megasas_mgmt_fw_ioctl - Issues management ioctls to FW + * @instance: Adapter soft state + * @argp: User's ioctl packet + */ +static int +megasas_mgmt_fw_ioctl(struct megasas_instance *instance, + struct megasas_iocpacket __user * user_ioc, + struct megasas_iocpacket *ioc) +{ + struct megasas_sge32 *kern_sge32; + struct megasas_cmd *cmd; + void *kbuff_arr[MAX_IOCTL_SGE]; + dma_addr_t buf_handle = 0; + int error = 0, i; + void *sense = NULL; + dma_addr_t sense_handle; + u32 *sense_ptr; + + memset(kbuff_arr, 0, sizeof(kbuff_arr)); + + if (ioc->sge_count > MAX_IOCTL_SGE) { + printk(KERN_DEBUG "megasas: SGE count [%d] > max limit [%d]\n", + ioc->sge_count, MAX_IOCTL_SGE); + return -EINVAL; + } + + cmd = megasas_get_cmd(instance); + if (!cmd) { + printk(KERN_DEBUG "megasas: Failed to get a cmd packet\n"); + return -ENOMEM; + } + + /* + * User's IOCTL packet has 2 frames (maximum). Copy those two + * frames into our cmd's frames. cmd->frame's context will get + * overwritten when we copy from user's frames. So set that value + * alone separately + */ + memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); + cmd->frame->hdr.context = cmd->index; + + /* + * The management interface between applications and the fw uses + * MFI frames. E.g, RAID configuration changes, LD property changes + * etc are accomplishes through different kinds of MFI frames. The + * driver needs to care only about substituting user buffers with + * kernel buffers in SGLs. The location of SGL is embedded in the + * struct iocpacket itself. + */ + kern_sge32 = (struct megasas_sge32 *) + ((unsigned long)cmd->frame + ioc->sgl_off); + + /* + * For each user buffer, create a mirror buffer and copy in + */ + for (i = 0; i < ioc->sge_count; i++) { + kbuff_arr[i] = pci_alloc_consistent(instance->pdev, + ioc->sgl[i].iov_len, + &buf_handle); + if (!kbuff_arr[i]) { + printk(KERN_DEBUG "megasas: Failed to alloc " + "kernel SGL buffer for IOCTL \n"); + error = -ENOMEM; + goto out; + } + + /* + * We don't change the dma_coherent_mask, so + * pci_alloc_consistent only returns 32bit addresses + */ + kern_sge32[i].phys_addr = (u32) buf_handle; + kern_sge32[i].length = ioc->sgl[i].iov_len; + + /* + * We created a kernel buffer corresponding to the + * user buffer. Now copy in from the user buffer + */ + if (copy_from_user(kbuff_arr[i], ioc->sgl[i].iov_base, + (u32) (ioc->sgl[i].iov_len))) { + error = -EFAULT; + goto out; + } + } + + if (ioc->sense_len) { + sense = pci_alloc_consistent(instance->pdev, ioc->sense_len, + &sense_handle); + if (!sense) { + error = -ENOMEM; + goto out; + } + + sense_ptr = + (u32 *) ((unsigned long)cmd->frame + ioc->sense_off); + *sense_ptr = sense_handle; + } + + /* + * Set the sync_cmd flag so that the ISR knows not to complete this + * cmd to the SCSI mid-layer + */ + cmd->sync_cmd = 1; + megasas_issue_blocked_cmd(instance, cmd); + cmd->sync_cmd = 0; + + /* + * copy out the kernel buffers to user buffers + */ + for (i = 0; i < ioc->sge_count; i++) { + if (copy_to_user(ioc->sgl[i].iov_base, kbuff_arr[i], + ioc->sgl[i].iov_len)) { + error = -EFAULT; + goto out; + } + } + + /* + * copy out the sense + */ + if (ioc->sense_len) { + /* + * sense_ptr points to the location that has the user + * sense buffer address + */ + sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw + + ioc->sense_off); + + if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)), + sense, ioc->sense_len)) { + error = -EFAULT; + goto out; + } + } + + /* + * copy the status codes returned by the fw + */ + if (copy_to_user(&user_ioc->frame.hdr.cmd_status, + &cmd->frame->hdr.cmd_status, sizeof(u8))) { + printk(KERN_DEBUG "megasas: Error copying out cmd_status\n"); + error = -EFAULT; + } + + out: + if (sense) { + pci_free_consistent(instance->pdev, ioc->sense_len, + sense, sense_handle); + } + + for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) { + pci_free_consistent(instance->pdev, + kern_sge32[i].length, + kbuff_arr[i], kern_sge32[i].phys_addr); + } + + megasas_return_cmd(instance, cmd); + return error; +} + +static struct megasas_instance *megasas_lookup_instance(u16 host_no) +{ + int i; + + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + + if ((megasas_mgmt_info.instance[i]) && + (megasas_mgmt_info.instance[i]->host->host_no == host_no)) + return megasas_mgmt_info.instance[i]; + } + + return NULL; +} + +static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) +{ + struct megasas_iocpacket __user *user_ioc = + (struct megasas_iocpacket __user *)arg; + struct megasas_iocpacket *ioc; + struct megasas_instance *instance; + int error; + + ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); + if (!ioc) + return -ENOMEM; + + if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) { + error = -EFAULT; + goto out_kfree_ioc; + } + + instance = megasas_lookup_instance(ioc->host_no); + if (!instance) { + error = -ENODEV; + goto out_kfree_ioc; + } + + /* + * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds + */ + if (down_interruptible(&instance->ioctl_sem)) { + error = -ERESTARTSYS; + goto out_kfree_ioc; + } + error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); + up(&instance->ioctl_sem); + + out_kfree_ioc: + kfree(ioc); + return error; +} + +static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) +{ + struct megasas_instance *instance; + struct megasas_aen aen; + int error; + + if (file->private_data != file) { + printk(KERN_DEBUG "megasas: fasync_helper was not " + "called first\n"); + return -EINVAL; + } + + if (copy_from_user(&aen, (void __user *)arg, sizeof(aen))) + return -EFAULT; + + instance = megasas_lookup_instance(aen.host_no); + + if (!instance) + return -ENODEV; + + down(&instance->aen_mutex); + error = megasas_register_aen(instance, aen.seq_num, + aen.class_locale_word); + up(&instance->aen_mutex); + return error; +} + +/** + * megasas_mgmt_ioctl - char node ioctl entry point + */ +static long +megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case MEGASAS_IOC_FIRMWARE: + return megasas_mgmt_ioctl_fw(file, arg); + + case MEGASAS_IOC_GET_AEN: + return megasas_mgmt_ioctl_aen(file, arg); + } + + return -ENOTTY; +} + +#ifdef CONFIG_COMPAT +static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) +{ + struct compat_megasas_iocpacket __user *cioc = + (struct compat_megasas_iocpacket __user *)arg; + struct megasas_iocpacket __user *ioc = + compat_alloc_user_space(sizeof(struct megasas_iocpacket)); + int i; + int error = 0; + + clear_user(ioc, sizeof(*ioc)); + + if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) || + copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) || + copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) || + copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) || + copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) || + copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32))) + return -EFAULT; + + for (i = 0; i < MAX_IOCTL_SGE; i++) { + compat_uptr_t ptr; + + if (get_user(ptr, &cioc->sgl[i].iov_base) || + put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) || + copy_in_user(&ioc->sgl[i].iov_len, + &cioc->sgl[i].iov_len, sizeof(compat_size_t))) + return -EFAULT; + } + + error = megasas_mgmt_ioctl_fw(file, (unsigned long)ioc); + + if (copy_in_user(&cioc->frame.hdr.cmd_status, + &ioc->frame.hdr.cmd_status, sizeof(u8))) { + printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n"); + return -EFAULT; + } + return error; +} + +static long +megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case MEGASAS_IOC_FIRMWARE:{ + return megasas_mgmt_compat_ioctl_fw(file, arg); + } + case MEGASAS_IOC_GET_AEN: + return megasas_mgmt_ioctl_aen(file, arg); + } + + return -ENOTTY; +} +#endif + +/* + * File operations structure for management interface + */ +static struct file_operations megasas_mgmt_fops = { + .owner = THIS_MODULE, + .open = megasas_mgmt_open, + .release = megasas_mgmt_release, + .fasync = megasas_mgmt_fasync, + .unlocked_ioctl = megasas_mgmt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = megasas_mgmt_compat_ioctl, +#endif +}; + +/* + * PCI hotplug support registration structure + */ +static struct pci_driver megasas_pci_driver = { + + .name = "megaraid_sas", + .id_table = megasas_pci_table, + .probe = megasas_probe_one, + .remove = __devexit_p(megasas_detach_one), + .shutdown = megasas_shutdown, +}; + +/* + * Sysfs driver attributes + */ +static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf) +{ + return snprintf(buf, strlen(MEGASAS_VERSION) + 2, "%s\n", + MEGASAS_VERSION); +} + +static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL); + +static ssize_t +megasas_sysfs_show_release_date(struct device_driver *dd, char *buf) +{ + return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", + MEGASAS_RELDATE); +} + +static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, + NULL); + +/** + * megasas_init - Driver load entry point + */ +static int __init megasas_init(void) +{ + int rval; + + /* + * Announce driver version and other information + */ + printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION, + MEGASAS_EXT_VERSION); + + memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); + + /* + * Register character device node + */ + rval = register_chrdev(0, "megaraid_sas_ioctl", &megasas_mgmt_fops); + + if (rval < 0) { + printk(KERN_DEBUG "megasas: failed to open device node\n"); + return rval; + } + + megasas_mgmt_majorno = rval; + + /* + * Register ourselves as PCI hotplug module + */ + rval = pci_module_init(&megasas_pci_driver); + + 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); + + return rval; +} + +/** + * megasas_exit - Driver unload entry point + */ +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_release_date); + + pci_unregister_driver(&megasas_pci_driver); + unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); +} + +module_init(megasas_init); +module_exit(megasas_exit); diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h new file mode 100644 index 000000000000..eaec9d531424 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -0,0 +1,1142 @@ +/* + * + * Linux MegaRAID driver for SAS based RAID controllers + * + * Copyright (c) 2003-2005 LSI Logic Corporation. + * + * 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 the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * FILE : megaraid_sas.h + */ + +#ifndef LSI_MEGARAID_SAS_H +#define LSI_MEGARAID_SAS_H + +/** + * MegaRAID SAS Driver meta data + */ +#define MEGASAS_VERSION "00.00.02.00-rc4" +#define MEGASAS_RELDATE "Sep 16, 2005" +#define MEGASAS_EXT_VERSION "Fri Sep 16 12:37:08 EDT 2005" + +/* + * ===================================== + * MegaRAID SAS MFI firmware definitions + * ===================================== + */ + +/* + * MFI stands for MegaRAID SAS FW Interface. This is just a moniker for + * protocol between the software and firmware. Commands are issued using + * "message frames" + */ + +/** + * FW posts its state in upper 4 bits of outbound_msg_0 register + */ +#define MFI_STATE_MASK 0xF0000000 +#define MFI_STATE_UNDEFINED 0x00000000 +#define MFI_STATE_BB_INIT 0x10000000 +#define MFI_STATE_FW_INIT 0x40000000 +#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 +#define MFI_STATE_FW_INIT_2 0x70000000 +#define MFI_STATE_DEVICE_SCAN 0x80000000 +#define MFI_STATE_FLUSH_CACHE 0xA0000000 +#define MFI_STATE_READY 0xB0000000 +#define MFI_STATE_OPERATIONAL 0xC0000000 +#define MFI_STATE_FAULT 0xF0000000 + +#define MEGAMFI_FRAME_SIZE 64 + +/** + * During FW init, clear pending cmds & reset state using inbound_msg_0 + * + * ABORT : Abort all pending cmds + * 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 + */ +#define MFI_INIT_ABORT 0x00000000 +#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 + +/** + * MFI frame flags + */ +#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000 +#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001 +#define MFI_FRAME_SGL32 0x0000 +#define MFI_FRAME_SGL64 0x0002 +#define MFI_FRAME_SENSE32 0x0000 +#define MFI_FRAME_SENSE64 0x0004 +#define MFI_FRAME_DIR_NONE 0x0000 +#define MFI_FRAME_DIR_WRITE 0x0008 +#define MFI_FRAME_DIR_READ 0x0010 +#define MFI_FRAME_DIR_BOTH 0x0018 + +/** + * Definition for cmd_status + */ +#define MFI_CMD_STATUS_POLL_MODE 0xFF + +/** + * MFI command opcodes + */ +#define MFI_CMD_INIT 0x00 +#define MFI_CMD_LD_READ 0x01 +#define MFI_CMD_LD_WRITE 0x02 +#define MFI_CMD_LD_SCSI_IO 0x03 +#define MFI_CMD_PD_SCSI_IO 0x04 +#define MFI_CMD_DCMD 0x05 +#define MFI_CMD_ABORT 0x06 +#define MFI_CMD_SMP 0x07 +#define MFI_CMD_STP 0x08 + +#define MR_DCMD_CTRL_GET_INFO 0x01010000 + +#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000 +#define MR_FLUSH_CTRL_CACHE 0x01 +#define MR_FLUSH_DISK_CACHE 0x02 + +#define MR_DCMD_CTRL_SHUTDOWN 0x01050000 +#define MR_ENABLE_DRIVE_SPINDOWN 0x01 + +#define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100 +#define MR_DCMD_CTRL_EVENT_GET 0x01040300 +#define MR_DCMD_CTRL_EVENT_WAIT 0x01040500 +#define MR_DCMD_LD_GET_PROPERTIES 0x03030000 + +#define MR_DCMD_CLUSTER 0x08000000 +#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 +#define MR_DCMD_CLUSTER_RESET_LD 0x08010200 + +/** + * MFI command completion codes + */ +enum MFI_STAT { + MFI_STAT_OK = 0x00, + MFI_STAT_INVALID_CMD = 0x01, + MFI_STAT_INVALID_DCMD = 0x02, + MFI_STAT_INVALID_PARAMETER = 0x03, + MFI_STAT_INVALID_SEQUENCE_NUMBER = 0x04, + MFI_STAT_ABORT_NOT_POSSIBLE = 0x05, + MFI_STAT_APP_HOST_CODE_NOT_FOUND = 0x06, + MFI_STAT_APP_IN_USE = 0x07, + MFI_STAT_APP_NOT_INITIALIZED = 0x08, + MFI_STAT_ARRAY_INDEX_INVALID = 0x09, + MFI_STAT_ARRAY_ROW_NOT_EMPTY = 0x0a, + MFI_STAT_CONFIG_RESOURCE_CONFLICT = 0x0b, + MFI_STAT_DEVICE_NOT_FOUND = 0x0c, + MFI_STAT_DRIVE_TOO_SMALL = 0x0d, + MFI_STAT_FLASH_ALLOC_FAIL = 0x0e, + MFI_STAT_FLASH_BUSY = 0x0f, + MFI_STAT_FLASH_ERROR = 0x10, + MFI_STAT_FLASH_IMAGE_BAD = 0x11, + MFI_STAT_FLASH_IMAGE_INCOMPLETE = 0x12, + MFI_STAT_FLASH_NOT_OPEN = 0x13, + MFI_STAT_FLASH_NOT_STARTED = 0x14, + MFI_STAT_FLUSH_FAILED = 0x15, + MFI_STAT_HOST_CODE_NOT_FOUNT = 0x16, + MFI_STAT_LD_CC_IN_PROGRESS = 0x17, + MFI_STAT_LD_INIT_IN_PROGRESS = 0x18, + MFI_STAT_LD_LBA_OUT_OF_RANGE = 0x19, + MFI_STAT_LD_MAX_CONFIGURED = 0x1a, + MFI_STAT_LD_NOT_OPTIMAL = 0x1b, + MFI_STAT_LD_RBLD_IN_PROGRESS = 0x1c, + MFI_STAT_LD_RECON_IN_PROGRESS = 0x1d, + MFI_STAT_LD_WRONG_RAID_LEVEL = 0x1e, + MFI_STAT_MAX_SPARES_EXCEEDED = 0x1f, + MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, + MFI_STAT_MFC_HW_ERROR = 0x21, + MFI_STAT_NO_HW_PRESENT = 0x22, + MFI_STAT_NOT_FOUND = 0x23, + MFI_STAT_NOT_IN_ENCL = 0x24, + MFI_STAT_PD_CLEAR_IN_PROGRESS = 0x25, + MFI_STAT_PD_TYPE_WRONG = 0x26, + MFI_STAT_PR_DISABLED = 0x27, + MFI_STAT_ROW_INDEX_INVALID = 0x28, + MFI_STAT_SAS_CONFIG_INVALID_ACTION = 0x29, + MFI_STAT_SAS_CONFIG_INVALID_DATA = 0x2a, + MFI_STAT_SAS_CONFIG_INVALID_PAGE = 0x2b, + MFI_STAT_SAS_CONFIG_INVALID_TYPE = 0x2c, + MFI_STAT_SCSI_DONE_WITH_ERROR = 0x2d, + MFI_STAT_SCSI_IO_FAILED = 0x2e, + MFI_STAT_SCSI_RESERVATION_CONFLICT = 0x2f, + MFI_STAT_SHUTDOWN_FAILED = 0x30, + MFI_STAT_TIME_NOT_SET = 0x31, + MFI_STAT_WRONG_STATE = 0x32, + MFI_STAT_LD_OFFLINE = 0x33, + MFI_STAT_PEER_NOTIFICATION_REJECTED = 0x34, + MFI_STAT_PEER_NOTIFICATION_FAILED = 0x35, + MFI_STAT_RESERVATION_IN_PROGRESS = 0x36, + MFI_STAT_I2C_ERRORS_DETECTED = 0x37, + MFI_STAT_PCI_ERRORS_DETECTED = 0x38, + + MFI_STAT_INVALID_STATUS = 0xFF +}; + +/* + * Number of mailbox bytes in DCMD message frame + */ +#define MFI_MBOX_SIZE 12 + +enum MR_EVT_CLASS { + + MR_EVT_CLASS_DEBUG = -2, + MR_EVT_CLASS_PROGRESS = -1, + MR_EVT_CLASS_INFO = 0, + MR_EVT_CLASS_WARNING = 1, + MR_EVT_CLASS_CRITICAL = 2, + MR_EVT_CLASS_FATAL = 3, + MR_EVT_CLASS_DEAD = 4, + +}; + +enum MR_EVT_LOCALE { + + MR_EVT_LOCALE_LD = 0x0001, + MR_EVT_LOCALE_PD = 0x0002, + MR_EVT_LOCALE_ENCL = 0x0004, + MR_EVT_LOCALE_BBU = 0x0008, + MR_EVT_LOCALE_SAS = 0x0010, + MR_EVT_LOCALE_CTRL = 0x0020, + MR_EVT_LOCALE_CONFIG = 0x0040, + MR_EVT_LOCALE_CLUSTER = 0x0080, + MR_EVT_LOCALE_ALL = 0xffff, + +}; + +enum MR_EVT_ARGS { + + MR_EVT_ARGS_NONE, + MR_EVT_ARGS_CDB_SENSE, + MR_EVT_ARGS_LD, + MR_EVT_ARGS_LD_COUNT, + MR_EVT_ARGS_LD_LBA, + MR_EVT_ARGS_LD_OWNER, + MR_EVT_ARGS_LD_LBA_PD_LBA, + MR_EVT_ARGS_LD_PROG, + MR_EVT_ARGS_LD_STATE, + MR_EVT_ARGS_LD_STRIP, + MR_EVT_ARGS_PD, + MR_EVT_ARGS_PD_ERR, + MR_EVT_ARGS_PD_LBA, + MR_EVT_ARGS_PD_LBA_LD, + MR_EVT_ARGS_PD_PROG, + MR_EVT_ARGS_PD_STATE, + MR_EVT_ARGS_PCI, + MR_EVT_ARGS_RATE, + MR_EVT_ARGS_STR, + MR_EVT_ARGS_TIME, + MR_EVT_ARGS_ECC, + +}; + +/* + * SAS controller properties + */ +struct megasas_ctrl_prop { + + u16 seq_num; + u16 pred_fail_poll_interval; + u16 intr_throttle_count; + u16 intr_throttle_timeouts; + u8 rebuild_rate; + u8 patrol_read_rate; + u8 bgi_rate; + u8 cc_rate; + u8 recon_rate; + u8 cache_flush_interval; + u8 spinup_drv_count; + u8 spinup_delay; + u8 cluster_enable; + u8 coercion_mode; + u8 alarm_enable; + u8 disable_auto_rebuild; + u8 disable_battery_warn; + u8 ecc_bucket_size; + u16 ecc_bucket_leak_rate; + u8 restore_hotspare_on_insertion; + u8 expose_encl_devices; + u8 reserved[38]; + +} __attribute__ ((packed)); + +/* + * SAS controller information + */ +struct megasas_ctrl_info { + + /* + * PCI device information + */ + struct { + + u16 vendor_id; + u16 device_id; + u16 sub_vendor_id; + u16 sub_device_id; + u8 reserved[24]; + + } __attribute__ ((packed)) pci; + + /* + * Host interface information + */ + struct { + + u8 PCIX:1; + u8 PCIE:1; + u8 iSCSI:1; + u8 SAS_3G:1; + u8 reserved_0:4; + u8 reserved_1[6]; + u8 port_count; + u64 port_addr[8]; + + } __attribute__ ((packed)) host_interface; + + /* + * Device (backend) interface information + */ + struct { + + u8 SPI:1; + u8 SAS_3G:1; + u8 SATA_1_5G:1; + u8 SATA_3G:1; + u8 reserved_0:4; + u8 reserved_1[6]; + u8 port_count; + u64 port_addr[8]; + + } __attribute__ ((packed)) device_interface; + + /* + * List of components residing in flash. All str are null terminated + */ + u32 image_check_word; + u32 image_component_count; + + struct { + + char name[8]; + char version[32]; + char build_date[16]; + char built_time[16]; + + } __attribute__ ((packed)) image_component[8]; + + /* + * List of flash components that have been flashed on the card, but + * are not in use, pending reset of the adapter. This list will be + * empty if a flash operation has not occurred. All stings are null + * terminated + */ + u32 pending_image_component_count; + + struct { + + char name[8]; + char version[32]; + char build_date[16]; + char build_time[16]; + + } __attribute__ ((packed)) pending_image_component[8]; + + u8 max_arms; + u8 max_spans; + u8 max_arrays; + u8 max_lds; + + char product_name[80]; + char serial_no[32]; + + /* + * Other physical/controller/operation information. Indicates the + * presence of the hardware + */ + struct { + + u32 bbu:1; + u32 alarm:1; + u32 nvram:1; + u32 uart:1; + u32 reserved:28; + + } __attribute__ ((packed)) hw_present; + + u32 current_fw_time; + + /* + * Maximum data transfer sizes + */ + u16 max_concurrent_cmds; + u16 max_sge_count; + u32 max_request_size; + + /* + * Logical and physical device counts + */ + u16 ld_present_count; + u16 ld_degraded_count; + u16 ld_offline_count; + + u16 pd_present_count; + u16 pd_disk_present_count; + u16 pd_disk_pred_failure_count; + u16 pd_disk_failed_count; + + /* + * Memory size information + */ + u16 nvram_size; + u16 memory_size; + u16 flash_size; + + /* + * Error counters + */ + u16 mem_correctable_error_count; + u16 mem_uncorrectable_error_count; + + /* + * Cluster information + */ + u8 cluster_permitted; + u8 cluster_active; + + /* + * Additional max data transfer sizes + */ + u16 max_strips_per_io; + + /* + * Controller capabilities structures + */ + struct { + + u32 raid_level_0:1; + u32 raid_level_1:1; + u32 raid_level_5:1; + u32 raid_level_1E:1; + u32 raid_level_6:1; + u32 reserved:27; + + } __attribute__ ((packed)) raid_levels; + + struct { + + u32 rbld_rate:1; + u32 cc_rate:1; + u32 bgi_rate:1; + u32 recon_rate:1; + u32 patrol_rate:1; + u32 alarm_control:1; + u32 cluster_supported:1; + u32 bbu:1; + u32 spanning_allowed:1; + u32 dedicated_hotspares:1; + u32 revertible_hotspares:1; + u32 foreign_config_import:1; + u32 self_diagnostic:1; + u32 mixed_redundancy_arr:1; + u32 global_hot_spares:1; + u32 reserved:17; + + } __attribute__ ((packed)) adapter_operations; + + struct { + + u32 read_policy:1; + u32 write_policy:1; + u32 io_policy:1; + u32 access_policy:1; + u32 disk_cache_policy:1; + u32 reserved:27; + + } __attribute__ ((packed)) ld_operations; + + struct { + + u8 min; + u8 max; + u8 reserved[2]; + + } __attribute__ ((packed)) stripe_sz_ops; + + struct { + + u32 force_online:1; + u32 force_offline:1; + u32 force_rebuild:1; + u32 reserved:29; + + } __attribute__ ((packed)) pd_operations; + + struct { + + u32 ctrl_supports_sas:1; + u32 ctrl_supports_sata:1; + u32 allow_mix_in_encl:1; + u32 allow_mix_in_ld:1; + u32 allow_sata_in_cluster:1; + u32 reserved:27; + + } __attribute__ ((packed)) pd_mix_support; + + /* + * Define ECC single-bit-error bucket information + */ + u8 ecc_bucket_count; + u8 reserved_2[11]; + + /* + * Include the controller properties (changeable items) + */ + struct megasas_ctrl_prop properties; + + /* + * Define FW pkg version (set in envt v'bles on OEM basis) + */ + char package_version[0x60]; + + u8 pad[0x800 - 0x6a0]; + +} __attribute__ ((packed)); + +/* + * =============================== + * MegaRAID SAS driver definitions + * =============================== + */ +#define MEGASAS_MAX_PD_CHANNELS 2 +#define MEGASAS_MAX_LD_CHANNELS 2 +#define MEGASAS_MAX_CHANNELS (MEGASAS_MAX_PD_CHANNELS + \ + MEGASAS_MAX_LD_CHANNELS) +#define MEGASAS_MAX_DEV_PER_CHANNEL 128 +#define MEGASAS_DEFAULT_INIT_ID -1 +#define MEGASAS_MAX_LUN 8 +#define MEGASAS_MAX_LD 64 + +/* + * When SCSI mid-layer calls driver's reset routine, driver waits for + * MEGASAS_RESET_WAIT_TIME seconds for all outstanding IO to complete. Note + * that the driver cannot _actually_ abort or reset pending commands. While + * it is waiting for the commands to complete, it prints a diagnostic message + * every MEGASAS_RESET_NOTICE_INTERVAL seconds + */ +#define MEGASAS_RESET_WAIT_TIME 180 +#define MEGASAS_RESET_NOTICE_INTERVAL 5 + +#define MEGASAS_IOCTL_CMD 0 + +/* + * FW reports the maximum of number of commands that it can accept (maximum + * commands that can be outstanding) at any time. The driver must report a + * lower number to the mid layer because it can issue a few internal commands + * itself (E.g, AEN, abort cmd, IOCTLs etc). The number of commands it needs + * is shown below + */ +#define MEGASAS_INT_CMDS 32 + +/* + * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit + * SGLs based on the size of dma_addr_t + */ +#define IS_DMA64 (sizeof(dma_addr_t) == 8) + +#define MFI_OB_INTR_STATUS_MASK 0x00000002 +#define MFI_POLL_TIMEOUT_SECS 10 + +struct megasas_register_set { + + u32 reserved_0[4]; /*0000h */ + + u32 inbound_msg_0; /*0010h */ + u32 inbound_msg_1; /*0014h */ + u32 outbound_msg_0; /*0018h */ + u32 outbound_msg_1; /*001Ch */ + + u32 inbound_doorbell; /*0020h */ + u32 inbound_intr_status; /*0024h */ + u32 inbound_intr_mask; /*0028h */ + + u32 outbound_doorbell; /*002Ch */ + u32 outbound_intr_status; /*0030h */ + u32 outbound_intr_mask; /*0034h */ + + u32 reserved_1[2]; /*0038h */ + + u32 inbound_queue_port; /*0040h */ + u32 outbound_queue_port; /*0044h */ + + u32 reserved_2; /*004Ch */ + + u32 index_registers[1004]; /*0050h */ + +} __attribute__ ((packed)); + +struct megasas_sge32 { + + u32 phys_addr; + u32 length; + +} __attribute__ ((packed)); + +struct megasas_sge64 { + + u64 phys_addr; + u32 length; + +} __attribute__ ((packed)); + +union megasas_sgl { + + struct megasas_sge32 sge32[1]; + struct megasas_sge64 sge64[1]; + +} __attribute__ ((packed)); + +struct megasas_header { + + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xferlen; /*14h */ + +} __attribute__ ((packed)); + +union megasas_sgl_frame { + + struct megasas_sge32 sge32[8]; + struct megasas_sge64 sge64[5]; + +} __attribute__ ((packed)); + +struct megasas_init_frame { + + u8 cmd; /*00h */ + u8 reserved_0; /*01h */ + u8 cmd_status; /*02h */ + + u8 reserved_1; /*03h */ + u32 reserved_2; /*04h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 reserved_3; /*12h */ + u32 data_xfer_len; /*14h */ + + u32 queue_info_new_phys_addr_lo; /*18h */ + u32 queue_info_new_phys_addr_hi; /*1Ch */ + u32 queue_info_old_phys_addr_lo; /*20h */ + u32 queue_info_old_phys_addr_hi; /*24h */ + + u32 reserved_4[6]; /*28h */ + +} __attribute__ ((packed)); + +struct megasas_init_queue_info { + + u32 init_flags; /*00h */ + u32 reply_queue_entries; /*04h */ + + u32 reply_queue_start_phys_addr_lo; /*08h */ + u32 reply_queue_start_phys_addr_hi; /*0Ch */ + u32 producer_index_phys_addr_lo; /*10h */ + u32 producer_index_phys_addr_hi; /*14h */ + u32 consumer_index_phys_addr_lo; /*18h */ + u32 consumer_index_phys_addr_hi; /*1Ch */ + +} __attribute__ ((packed)); + +struct megasas_io_frame { + + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 access_byte; /*05h */ + u8 reserved_0; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 lba_count; /*14h */ + + u32 sense_buf_phys_addr_lo; /*18h */ + u32 sense_buf_phys_addr_hi; /*1Ch */ + + u32 start_lba_lo; /*20h */ + u32 start_lba_hi; /*24h */ + + union megasas_sgl sgl; /*28h */ + +} __attribute__ ((packed)); + +struct megasas_pthru_frame { + + u8 cmd; /*00h */ + u8 sense_len; /*01h */ + u8 cmd_status; /*02h */ + u8 scsi_status; /*03h */ + + u8 target_id; /*04h */ + u8 lun; /*05h */ + u8 cdb_len; /*06h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + u32 data_xfer_len; /*14h */ + + u32 sense_buf_phys_addr_lo; /*18h */ + u32 sense_buf_phys_addr_hi; /*1Ch */ + + u8 cdb[16]; /*20h */ + union megasas_sgl sgl; /*30h */ + +} __attribute__ ((packed)); + +struct megasas_dcmd_frame { + + u8 cmd; /*00h */ + u8 reserved_0; /*01h */ + u8 cmd_status; /*02h */ + u8 reserved_1[4]; /*03h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + + u32 data_xfer_len; /*14h */ + u32 opcode; /*18h */ + + union { /*1Ch */ + u8 b[12]; + u16 s[6]; + u32 w[3]; + } mbox; + + union megasas_sgl sgl; /*28h */ + +} __attribute__ ((packed)); + +struct megasas_abort_frame { + + u8 cmd; /*00h */ + u8 reserved_0; /*01h */ + u8 cmd_status; /*02h */ + + u8 reserved_1; /*03h */ + u32 reserved_2; /*04h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 reserved_3; /*12h */ + u32 reserved_4; /*14h */ + + u32 abort_context; /*18h */ + u32 pad_1; /*1Ch */ + + u32 abort_mfi_phys_addr_lo; /*20h */ + u32 abort_mfi_phys_addr_hi; /*24h */ + + u32 reserved_5[6]; /*28h */ + +} __attribute__ ((packed)); + +struct megasas_smp_frame { + + u8 cmd; /*00h */ + u8 reserved_1; /*01h */ + u8 cmd_status; /*02h */ + u8 connection_status; /*03h */ + + u8 reserved_2[3]; /*04h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + + u32 data_xfer_len; /*14h */ + u64 sas_addr; /*18h */ + + union { + struct megasas_sge32 sge32[2]; /* [0]: resp [1]: req */ + struct megasas_sge64 sge64[2]; /* [0]: resp [1]: req */ + } sgl; + +} __attribute__ ((packed)); + +struct megasas_stp_frame { + + u8 cmd; /*00h */ + u8 reserved_1; /*01h */ + u8 cmd_status; /*02h */ + u8 reserved_2; /*03h */ + + u8 target_id; /*04h */ + u8 reserved_3[2]; /*05h */ + u8 sge_count; /*07h */ + + u32 context; /*08h */ + u32 pad_0; /*0Ch */ + + u16 flags; /*10h */ + u16 timeout; /*12h */ + + u32 data_xfer_len; /*14h */ + + u16 fis[10]; /*18h */ + u32 stp_flags; + + union { + struct megasas_sge32 sge32[2]; /* [0]: resp [1]: data */ + struct megasas_sge64 sge64[2]; /* [0]: resp [1]: data */ + } sgl; + +} __attribute__ ((packed)); + +union megasas_frame { + + struct megasas_header hdr; + struct megasas_init_frame init; + struct megasas_io_frame io; + struct megasas_pthru_frame pthru; + struct megasas_dcmd_frame dcmd; + struct megasas_abort_frame abort; + struct megasas_smp_frame smp; + struct megasas_stp_frame stp; + + u8 raw_bytes[64]; +}; + +struct megasas_cmd; + +union megasas_evt_class_locale { + + struct { + u16 locale; + u8 reserved; + s8 class; + } __attribute__ ((packed)) members; + + u32 word; + +} __attribute__ ((packed)); + +struct megasas_evt_log_info { + u32 newest_seq_num; + u32 oldest_seq_num; + u32 clear_seq_num; + u32 shutdown_seq_num; + u32 boot_seq_num; + +} __attribute__ ((packed)); + +struct megasas_progress { + + u16 progress; + u16 elapsed_seconds; + +} __attribute__ ((packed)); + +struct megasas_evtarg_ld { + + u16 target_id; + u8 ld_index; + u8 reserved; + +} __attribute__ ((packed)); + +struct megasas_evtarg_pd { + u16 device_id; + u8 encl_index; + u8 slot_number; + +} __attribute__ ((packed)); + +struct megasas_evt_detail { + + u32 seq_num; + u32 time_stamp; + u32 code; + union megasas_evt_class_locale cl; + u8 arg_type; + u8 reserved1[15]; + + union { + struct { + struct megasas_evtarg_pd pd; + u8 cdb_length; + u8 sense_length; + u8 reserved[2]; + u8 cdb[16]; + u8 sense[64]; + } __attribute__ ((packed)) cdbSense; + + struct megasas_evtarg_ld ld; + + struct { + struct megasas_evtarg_ld ld; + u64 count; + } __attribute__ ((packed)) ld_count; + + struct { + u64 lba; + struct megasas_evtarg_ld ld; + } __attribute__ ((packed)) ld_lba; + + struct { + struct megasas_evtarg_ld ld; + u32 prevOwner; + u32 newOwner; + } __attribute__ ((packed)) ld_owner; + + struct { + u64 ld_lba; + u64 pd_lba; + struct megasas_evtarg_ld ld; + struct megasas_evtarg_pd pd; + } __attribute__ ((packed)) ld_lba_pd_lba; + + struct { + struct megasas_evtarg_ld ld; + struct megasas_progress prog; + } __attribute__ ((packed)) ld_prog; + + struct { + struct megasas_evtarg_ld ld; + u32 prev_state; + u32 new_state; + } __attribute__ ((packed)) ld_state; + + struct { + u64 strip; + struct megasas_evtarg_ld ld; + } __attribute__ ((packed)) ld_strip; + + struct megasas_evtarg_pd pd; + + struct { + struct megasas_evtarg_pd pd; + u32 err; + } __attribute__ ((packed)) pd_err; + + struct { + u64 lba; + struct megasas_evtarg_pd pd; + } __attribute__ ((packed)) pd_lba; + + struct { + u64 lba; + struct megasas_evtarg_pd pd; + struct megasas_evtarg_ld ld; + } __attribute__ ((packed)) pd_lba_ld; + + struct { + struct megasas_evtarg_pd pd; + struct megasas_progress prog; + } __attribute__ ((packed)) pd_prog; + + struct { + struct megasas_evtarg_pd pd; + u32 prevState; + u32 newState; + } __attribute__ ((packed)) pd_state; + + struct { + u16 vendorId; + u16 deviceId; + u16 subVendorId; + u16 subDeviceId; + } __attribute__ ((packed)) pci; + + u32 rate; + char str[96]; + + struct { + u32 rtc; + u32 elapsedSeconds; + } __attribute__ ((packed)) time; + + struct { + u32 ecar; + u32 elog; + char str[64]; + } __attribute__ ((packed)) ecc; + + u8 b[96]; + u16 s[48]; + u32 w[24]; + u64 d[12]; + } args; + + char description[128]; + +} __attribute__ ((packed)); + +struct megasas_instance { + + u32 *producer; + dma_addr_t producer_h; + u32 *consumer; + dma_addr_t consumer_h; + + u32 *reply_queue; + dma_addr_t reply_queue_h; + + unsigned long base_addr; + struct megasas_register_set __iomem *reg_set; + + s8 init_id; + u8 reserved[3]; + + u16 max_num_sge; + u16 max_fw_cmds; + u32 max_sectors_per_req; + + struct megasas_cmd **cmd_list; + struct list_head cmd_pool; + spinlock_t cmd_pool_lock; + struct dma_pool *frame_dma_pool; + struct dma_pool *sense_dma_pool; + + struct megasas_evt_detail *evt_detail; + dma_addr_t evt_detail_h; + struct megasas_cmd *aen_cmd; + struct semaphore aen_mutex; + struct semaphore ioctl_sem; + + struct Scsi_Host *host; + + wait_queue_head_t int_cmd_wait_q; + wait_queue_head_t abort_cmd_wait_q; + + struct pci_dev *pdev; + u32 unique_id; + + u32 fw_outstanding; + u32 hw_crit_error; + spinlock_t instance_lock; +}; + +#define MEGASAS_IS_LOGICAL(scp) \ + (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 + +#define MEGASAS_DEV_INDEX(inst, scp) \ + ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ + scp->device->id + +struct megasas_cmd { + + union megasas_frame *frame; + dma_addr_t frame_phys_addr; + u8 *sense; + dma_addr_t sense_phys_addr; + + u32 index; + u8 sync_cmd; + u8 cmd_status; + u16 abort_aen; + + struct list_head list; + struct scsi_cmnd *scmd; + struct megasas_instance *instance; + u32 frame_count; +}; + +#define MAX_MGMT_ADAPTERS 1024 +#define MAX_IOCTL_SGE 16 + +struct megasas_iocpacket { + + u16 host_no; + u16 __pad1; + u32 sgl_off; + u32 sge_count; + u32 sense_off; + u32 sense_len; + union { + u8 raw[128]; + struct megasas_header hdr; + } frame; + + struct iovec sgl[MAX_IOCTL_SGE]; + +} __attribute__ ((packed)); + +struct megasas_aen { + u16 host_no; + u16 __pad1; + u32 seq_num; + u32 class_locale_word; +} __attribute__ ((packed)); + +#ifdef CONFIG_COMPAT +struct compat_megasas_iocpacket { + u16 host_no; + u16 __pad1; + u32 sgl_off; + u32 sge_count; + u32 sense_off; + u32 sense_len; + union { + u8 raw[128]; + struct megasas_header hdr; + } frame; + struct compat_iovec sgl[MAX_IOCTL_SGE]; +} __attribute__ ((packed)); + +#define MEGASAS_IOC_FIRMWARE _IOWR('M', 1, struct compat_megasas_iocpacket) +#else +#define MEGASAS_IOC_FIRMWARE _IOWR('M', 1, struct megasas_iocpacket) +#endif + +#define MEGASAS_IOC_GET_AEN _IOW('M', 3, struct megasas_aen) + +struct megasas_mgmt_info { + + u16 count; + struct megasas_instance *instance[MAX_MGMT_ADAPTERS]; + int max_index; +}; + +#endif /*LSI_MEGARAID_SAS_H */ diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c index ff1933298da6..bdccf73cf9fe 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -730,7 +730,7 @@ static void start_phase(struct mesh_state *ms) * issue a SEQ_MSGOUT to get the mesh to drop ACK. */ if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) { - dlog(ms, "bus0 was %.2x explictly asserting ATN", mr->bus_status0); + dlog(ms, "bus0 was %.2x explicitly asserting ATN", mr->bus_status0); out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */ mesh_flush_io(mr); udelay(1); @@ -1766,7 +1766,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state) struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); unsigned long flags; - if (state == mdev->ofdev.dev.power.power_state || state < 2) + if (state.event == mdev->ofdev.dev.power.power_state.event || state.event < 2) return 0; scsi_block_requests(ms->host); @@ -1791,7 +1791,7 @@ static int mesh_resume(struct macio_dev *mdev) struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev); unsigned long flags; - if (mdev->ofdev.dev.power.power_state == 0) + if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON) return 0; set_mesh_power(ms, 1); @@ -1802,7 +1802,7 @@ static int mesh_resume(struct macio_dev *mdev) enable_irq(ms->meshintr); scsi_unblock_requests(ms->host); - mdev->ofdev.dev.power.power_state = 0; + mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON; return 0; } @@ -1959,22 +1959,35 @@ static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match) /* Set it up */ mesh_init(ms); - /* XXX FIXME: error should be fatal */ - if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) + /* Request interrupt */ + if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) { printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr); + goto out_shutdown; + } - /* XXX FIXME: handle failure */ - scsi_add_host(mesh_host, &mdev->ofdev.dev); + /* Add scsi host & scan */ + if (scsi_add_host(mesh_host, &mdev->ofdev.dev)) + goto out_release_irq; scsi_scan_host(mesh_host); return 0; -out_unmap: + out_release_irq: + free_irq(ms->meshintr, ms); + out_shutdown: + /* shutdown & reset bus in case of error or macos can be confused + * at reboot if the bus was set to synchronous mode already + */ + mesh_shutdown(mdev); + set_mesh_power(ms, 0); + pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, + ms->dma_cmd_space, ms->dma_cmd_bus); + out_unmap: iounmap(ms->dma); iounmap(ms->mesh); -out_free: + out_free: scsi_host_put(mesh_host); -out_release: + out_release: macio_release_resources(mdev); return -ENODEV; @@ -2001,7 +2014,7 @@ static int mesh_remove(struct macio_dev *mdev) /* Free DMA commands memory */ pci_free_consistent(macio_get_pci_dev(mdev), ms->dma_cmd_size, - ms->dma_cmd_space, ms->dma_cmd_bus); + ms->dma_cmd_space, ms->dma_cmd_bus); /* Release memory resources */ macio_release_resources(mdev); diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 89a4a0615c22..172839fce0eb 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -1377,7 +1377,7 @@ static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct scsi if ((STp->buffer)->syscall_result || !SRpnt) { printk(KERN_ERR "%s:E: Failed to read frame back from OnStream buffer\n", name); - vfree((void *)buffer); + vfree(buffer); *aSRpnt = SRpnt; return (-EIO); } @@ -1419,7 +1419,7 @@ static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct scsi if (new_frame > frame + 1000) { printk(KERN_ERR "%s:E: Failed to find writable tape media\n", name); - vfree((void *)buffer); + vfree(buffer); return (-EIO); } if ( i >= nframes + pending ) break; @@ -1500,7 +1500,7 @@ static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct scsi SRpnt->sr_sense_buffer[12] == 0 && SRpnt->sr_sense_buffer[13] == 2) { printk(KERN_ERR "%s:E: Volume overflow in write error recovery\n", name); - vfree((void *)buffer); + vfree(buffer); return (-EIO); /* hit end of tape = fail */ } i = ((SRpnt->sr_sense_buffer[3] << 24) | @@ -1525,7 +1525,7 @@ static int osst_read_back_buffer_and_rewrite(struct osst_tape * STp, struct scsi } if (!pending) osst_copy_to_buffer(STp->buffer, p); /* so buffer content == at entry in all cases */ - vfree((void *)buffer); + vfree(buffer); return 0; } @@ -5146,7 +5146,8 @@ static long osst_compat_ioctl(struct file * file, unsigned int cmd_in, unsigned /* Try to allocate a new tape buffer skeleton. Caller must not hold os_scsi_tapes_lock */ static struct osst_buffer * new_tape_buffer( int from_initialization, int need_dma, int max_sg ) { - int i, priority; + int i; + gfp_t priority; struct osst_buffer *tb; if (from_initialization) @@ -5178,7 +5179,8 @@ static struct osst_buffer * new_tape_buffer( int from_initialization, int need_d /* Try to allocate a temporary (while a user has the device open) enlarged tape buffer */ static int enlarge_buffer(struct osst_buffer *STbuffer, int need_dma) { - int segs, nbr, max_segs, b_size, priority, order, got; + int segs, nbr, max_segs, b_size, order, got; + gfp_t priority; if (STbuffer->buffer_size >= OS_FRAME_SIZE) return 1; @@ -5627,7 +5629,7 @@ static void osst_sysfs_add(dev_t dev, struct device *device, struct osst_tape * if (!osst_sysfs_valid) return; - osst_class_member = class_device_create(osst_sysfs_class, 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; @@ -5852,7 +5854,7 @@ static int osst_remove(struct device *dev) os_scsi_tapes[i] = NULL; osst_nr_dev--; write_unlock(&os_scsi_tapes_lock); - if (tpnt->header_cache != NULL) vfree(tpnt->header_cache); + vfree(tpnt->header_cache); if (tpnt->buffer) { normalize_buffer(tpnt->buffer); kfree(tpnt->buffer); @@ -5896,8 +5898,7 @@ static void __exit exit_osst (void) for (i=0; i < osst_max_dev; ++i) { if (!(STp = os_scsi_tapes[i])) continue; /* This is defensive, supposed to happen during detach */ - if (STp->header_cache) - vfree(STp->header_cache); + vfree(STp->header_cache); if (STp->buffer) { normalize_buffer(STp->buffer); kfree(STp->buffer); diff --git a/drivers/scsi/pdc_adma.c b/drivers/scsi/pdc_adma.c new file mode 100644 index 000000000000..665017eda8a6 --- /dev/null +++ b/drivers/scsi/pdc_adma.c @@ -0,0 +1,741 @@ +/* + * pdc_adma.c - Pacific Digital Corporation ADMA + * + * Maintained by: Mark Lord <mlord@pobox.com> + * + * Copyright 2005 Mark Lord + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * + * Supports ATA disks in single-packet ADMA mode. + * Uses PIO for everything else. + * + * TODO: Use ADMA transfers for ATAPI devices, when possible. + * This requires careful attention to a number of quirks of the chip. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/device.h> +#include "scsi.h" +#include <scsi/scsi_host.h> +#include <asm/io.h> +#include <linux/libata.h> + +#define DRV_NAME "pdc_adma" +#define DRV_VERSION "0.03" + +/* macro to calculate base address for ATA regs */ +#define ADMA_ATA_REGS(base,port_no) ((base) + ((port_no) * 0x40)) + +/* macro to calculate base address for ADMA regs */ +#define ADMA_REGS(base,port_no) ((base) + 0x80 + ((port_no) * 0x20)) + +enum { + ADMA_PORTS = 2, + ADMA_CPB_BYTES = 40, + ADMA_PRD_BYTES = LIBATA_MAX_PRD * 16, + ADMA_PKT_BYTES = ADMA_CPB_BYTES + ADMA_PRD_BYTES, + + ADMA_DMA_BOUNDARY = 0xffffffff, + + /* global register offsets */ + ADMA_MODE_LOCK = 0x00c7, + + /* per-channel register offsets */ + ADMA_CONTROL = 0x0000, /* ADMA control */ + ADMA_STATUS = 0x0002, /* ADMA status */ + ADMA_CPB_COUNT = 0x0004, /* CPB count */ + ADMA_CPB_CURRENT = 0x000c, /* current CPB address */ + ADMA_CPB_NEXT = 0x000c, /* next CPB address */ + ADMA_CPB_LOOKUP = 0x0010, /* CPB lookup table */ + ADMA_FIFO_IN = 0x0014, /* input FIFO threshold */ + ADMA_FIFO_OUT = 0x0016, /* output FIFO threshold */ + + /* ADMA_CONTROL register bits */ + aNIEN = (1 << 8), /* irq mask: 1==masked */ + aGO = (1 << 7), /* packet trigger ("Go!") */ + aRSTADM = (1 << 5), /* ADMA logic reset */ + aPIOMD4 = 0x0003, /* PIO mode 4 */ + + /* ADMA_STATUS register bits */ + aPSD = (1 << 6), + aUIRQ = (1 << 4), + aPERR = (1 << 0), + + /* CPB bits */ + cDONE = (1 << 0), + cVLD = (1 << 0), + cDAT = (1 << 2), + cIEN = (1 << 3), + + /* PRD bits */ + pORD = (1 << 4), + pDIRO = (1 << 5), + pEND = (1 << 7), + + /* ATA register flags */ + rIGN = (1 << 5), + rEND = (1 << 7), + + /* ATA register addresses */ + ADMA_REGS_CONTROL = 0x0e, + ADMA_REGS_SECTOR_COUNT = 0x12, + ADMA_REGS_LBA_LOW = 0x13, + ADMA_REGS_LBA_MID = 0x14, + ADMA_REGS_LBA_HIGH = 0x15, + ADMA_REGS_DEVICE = 0x16, + ADMA_REGS_COMMAND = 0x17, + + /* PCI device IDs */ + board_1841_idx = 0, /* ADMA 2-port controller */ +}; + +typedef enum { adma_state_idle, adma_state_pkt, adma_state_mmio } adma_state_t; + +struct adma_port_priv { + u8 *pkt; + dma_addr_t pkt_dma; + adma_state_t state; +}; + +static int adma_ata_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent); +static irqreturn_t adma_intr (int irq, void *dev_instance, + struct pt_regs *regs); +static int adma_port_start(struct ata_port *ap); +static void adma_host_stop(struct ata_host_set *host_set); +static void adma_port_stop(struct ata_port *ap); +static void adma_phy_reset(struct ata_port *ap); +static void adma_qc_prep(struct ata_queued_cmd *qc); +static int adma_qc_issue(struct ata_queued_cmd *qc); +static int adma_check_atapi_dma(struct ata_queued_cmd *qc); +static void adma_bmdma_stop(struct ata_queued_cmd *qc); +static u8 adma_bmdma_status(struct ata_port *ap); +static void adma_irq_clear(struct ata_port *ap); +static void adma_eng_timeout(struct ata_port *ap); + +static Scsi_Host_Template adma_ata_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ENABLE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ADMA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, +}; + +static const struct ata_port_operations adma_ata_ops = { + .port_disable = ata_port_disable, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .check_atapi_dma = adma_check_atapi_dma, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + .phy_reset = adma_phy_reset, + .qc_prep = adma_qc_prep, + .qc_issue = adma_qc_issue, + .eng_timeout = adma_eng_timeout, + .irq_handler = adma_intr, + .irq_clear = adma_irq_clear, + .port_start = adma_port_start, + .port_stop = adma_port_stop, + .host_stop = adma_host_stop, + .bmdma_stop = adma_bmdma_stop, + .bmdma_status = adma_bmdma_status, +}; + +static struct ata_port_info adma_port_info[] = { + /* board_1841_idx */ + { + .sht = &adma_ata_sht, + .host_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | + ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO, + .pio_mask = 0x10, /* pio4 */ + .udma_mask = 0x1f, /* udma0-4 */ + .port_ops = &adma_ata_ops, + }, +}; + +static struct pci_device_id adma_ata_pci_tbl[] = { + { PCI_VENDOR_ID_PDC, 0x1841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_1841_idx }, + + { } /* terminate list */ +}; + +static struct pci_driver adma_ata_pci_driver = { + .name = DRV_NAME, + .id_table = adma_ata_pci_tbl, + .probe = adma_ata_init_one, + .remove = ata_pci_remove_one, +}; + +static int adma_check_atapi_dma(struct ata_queued_cmd *qc) +{ + return 1; /* ATAPI DMA not yet supported */ +} + +static void adma_bmdma_stop(struct ata_queued_cmd *qc) +{ + /* nothing */ +} + +static u8 adma_bmdma_status(struct ata_port *ap) +{ + return 0; +} + +static void adma_irq_clear(struct ata_port *ap) +{ + /* nothing */ +} + +static void adma_reset_engine(void __iomem *chan) +{ + /* reset ADMA to idle state */ + writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); + udelay(2); + writew(aPIOMD4, chan + ADMA_CONTROL); + udelay(2); +} + +static void adma_reinit_engine(struct ata_port *ap) +{ + struct adma_port_priv *pp = ap->private_data; + void __iomem *mmio_base = ap->host_set->mmio_base; + void __iomem *chan = ADMA_REGS(mmio_base, ap->port_no); + + /* mask/clear ATA interrupts */ + writeb(ATA_NIEN, (void __iomem *)ap->ioaddr.ctl_addr); + ata_check_status(ap); + + /* reset the ADMA engine */ + adma_reset_engine(chan); + + /* set in-FIFO threshold to 0x100 */ + writew(0x100, chan + ADMA_FIFO_IN); + + /* set CPB pointer */ + writel((u32)pp->pkt_dma, chan + ADMA_CPB_NEXT); + + /* set out-FIFO threshold to 0x100 */ + writew(0x100, chan + ADMA_FIFO_OUT); + + /* set CPB count */ + writew(1, chan + ADMA_CPB_COUNT); + + /* read/discard ADMA status */ + readb(chan + ADMA_STATUS); +} + +static inline void adma_enter_reg_mode(struct ata_port *ap) +{ + void __iomem *chan = ADMA_REGS(ap->host_set->mmio_base, ap->port_no); + + writew(aPIOMD4, chan + ADMA_CONTROL); + readb(chan + ADMA_STATUS); /* flush */ +} + +static void adma_phy_reset(struct ata_port *ap) +{ + struct adma_port_priv *pp = ap->private_data; + + pp->state = adma_state_idle; + adma_reinit_engine(ap); + ata_port_probe(ap); + ata_bus_reset(ap); +} + +static void adma_eng_timeout(struct ata_port *ap) +{ + struct adma_port_priv *pp = ap->private_data; + + if (pp->state != adma_state_idle) /* healthy paranoia */ + pp->state = adma_state_mmio; + adma_reinit_engine(ap); + ata_eng_timeout(ap); +} + +static int adma_fill_sg(struct ata_queued_cmd *qc) +{ + struct scatterlist *sg = qc->sg; + struct ata_port *ap = qc->ap; + struct adma_port_priv *pp = ap->private_data; + u8 *buf = pp->pkt; + int nelem, i = (2 + buf[3]) * 8; + u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0); + + for (nelem = 0; nelem < qc->n_elem; nelem++,sg++) { + u32 addr; + u32 len; + + addr = (u32)sg_dma_address(sg); + *(__le32 *)(buf + i) = cpu_to_le32(addr); + i += 4; + + len = sg_dma_len(sg) >> 3; + *(__le32 *)(buf + i) = cpu_to_le32(len); + i += 4; + + if ((nelem + 1) == qc->n_elem) + pFLAGS |= pEND; + buf[i++] = pFLAGS; + buf[i++] = qc->dev->dma_mode & 0xf; + buf[i++] = 0; /* pPKLW */ + buf[i++] = 0; /* reserved */ + + *(__le32 *)(buf + i) + = (pFLAGS & pEND) ? 0 : cpu_to_le32(pp->pkt_dma + i + 4); + i += 4; + + VPRINTK("PRD[%u] = (0x%lX, 0x%X)\n", nelem, + (unsigned long)addr, len); + } + return i; +} + +static void adma_qc_prep(struct ata_queued_cmd *qc) +{ + struct adma_port_priv *pp = qc->ap->private_data; + u8 *buf = pp->pkt; + u32 pkt_dma = (u32)pp->pkt_dma; + int i = 0; + + VPRINTK("ENTER\n"); + + adma_enter_reg_mode(qc->ap); + if (qc->tf.protocol != ATA_PROT_DMA) { + ata_qc_prep(qc); + return; + } + + buf[i++] = 0; /* Response flags */ + buf[i++] = 0; /* reserved */ + buf[i++] = cVLD | cDAT | cIEN; + i++; /* cLEN, gets filled in below */ + + *(__le32 *)(buf+i) = cpu_to_le32(pkt_dma); /* cNCPB */ + i += 4; /* cNCPB */ + i += 4; /* cPRD, gets filled in below */ + + buf[i++] = 0; /* reserved */ + buf[i++] = 0; /* reserved */ + buf[i++] = 0; /* reserved */ + buf[i++] = 0; /* reserved */ + + /* ATA registers; must be a multiple of 4 */ + buf[i++] = qc->tf.device; + buf[i++] = ADMA_REGS_DEVICE; + if ((qc->tf.flags & ATA_TFLAG_LBA48)) { + buf[i++] = qc->tf.hob_nsect; + buf[i++] = ADMA_REGS_SECTOR_COUNT; + buf[i++] = qc->tf.hob_lbal; + buf[i++] = ADMA_REGS_LBA_LOW; + buf[i++] = qc->tf.hob_lbam; + buf[i++] = ADMA_REGS_LBA_MID; + buf[i++] = qc->tf.hob_lbah; + buf[i++] = ADMA_REGS_LBA_HIGH; + } + buf[i++] = qc->tf.nsect; + buf[i++] = ADMA_REGS_SECTOR_COUNT; + buf[i++] = qc->tf.lbal; + buf[i++] = ADMA_REGS_LBA_LOW; + buf[i++] = qc->tf.lbam; + buf[i++] = ADMA_REGS_LBA_MID; + buf[i++] = qc->tf.lbah; + buf[i++] = ADMA_REGS_LBA_HIGH; + buf[i++] = 0; + buf[i++] = ADMA_REGS_CONTROL; + buf[i++] = rIGN; + buf[i++] = 0; + buf[i++] = qc->tf.command; + buf[i++] = ADMA_REGS_COMMAND | rEND; + + buf[3] = (i >> 3) - 2; /* cLEN */ + *(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i); /* cPRD */ + + i = adma_fill_sg(qc); + wmb(); /* flush PRDs and pkt to memory */ +#if 0 + /* dump out CPB + PRDs for debug */ + { + int j, len = 0; + static char obuf[2048]; + for (j = 0; j < i; ++j) { + len += sprintf(obuf+len, "%02x ", buf[j]); + if ((j & 7) == 7) { + printk("%s\n", obuf); + len = 0; + } + } + if (len) + printk("%s\n", obuf); + } +#endif +} + +static inline void adma_packet_start(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *chan = ADMA_REGS(ap->host_set->mmio_base, ap->port_no); + + VPRINTK("ENTER, ap %p\n", ap); + + /* fire up the ADMA engine */ + writew(aPIOMD4 | aGO, chan + ADMA_CONTROL); +} + +static int adma_qc_issue(struct ata_queued_cmd *qc) +{ + struct adma_port_priv *pp = qc->ap->private_data; + + switch (qc->tf.protocol) { + case ATA_PROT_DMA: + pp->state = adma_state_pkt; + adma_packet_start(qc); + return 0; + + case ATA_PROT_ATAPI_DMA: + BUG(); + break; + + default: + break; + } + + pp->state = adma_state_mmio; + return ata_qc_issue_prot(qc); +} + +static inline unsigned int adma_intr_pkt(struct ata_host_set *host_set) +{ + unsigned int handled = 0, port_no; + u8 __iomem *mmio_base = host_set->mmio_base; + + for (port_no = 0; port_no < host_set->n_ports; ++port_no) { + struct ata_port *ap = host_set->ports[port_no]; + struct adma_port_priv *pp; + struct ata_queued_cmd *qc; + void __iomem *chan = ADMA_REGS(mmio_base, port_no); + u8 status = readb(chan + ADMA_STATUS); + + if (status == 0) + continue; + handled = 1; + adma_enter_reg_mode(ap); + if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)) + continue; + pp = ap->private_data; + if (!pp || pp->state != adma_state_pkt) + continue; + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) { + unsigned int err_mask = 0; + + if ((status & (aPERR | aPSD | aUIRQ))) + err_mask = AC_ERR_OTHER; + else if (pp->pkt[0] != cDONE) + err_mask = AC_ERR_OTHER; + + ata_qc_complete(qc, err_mask); + } + } + return handled; +} + +static inline unsigned int adma_intr_mmio(struct ata_host_set *host_set) +{ + unsigned int handled = 0, port_no; + + for (port_no = 0; port_no < host_set->n_ports; ++port_no) { + struct ata_port *ap; + ap = host_set->ports[port_no]; + if (ap && (!(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)))) { + struct ata_queued_cmd *qc; + struct adma_port_priv *pp = ap->private_data; + if (!pp || pp->state != adma_state_mmio) + continue; + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && (!(qc->tf.ctl & ATA_NIEN))) { + + /* check main status, clearing INTRQ */ + u8 status = ata_check_status(ap); + if ((status & ATA_BUSY)) + continue; + DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", + ap->id, qc->tf.protocol, status); + + /* complete taskfile transaction */ + pp->state = adma_state_idle; + ata_qc_complete(qc, ac_err_mask(status)); + handled = 1; + } + } + } + return handled; +} + +static irqreturn_t adma_intr(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + unsigned int handled = 0; + + VPRINTK("ENTER\n"); + + spin_lock(&host_set->lock); + handled = adma_intr_pkt(host_set) | adma_intr_mmio(host_set); + spin_unlock(&host_set->lock); + + VPRINTK("EXIT\n"); + + return IRQ_RETVAL(handled); +} + +static void adma_ata_setup_port(struct ata_ioports *port, unsigned long base) +{ + port->cmd_addr = + port->data_addr = base + 0x000; + port->error_addr = + port->feature_addr = base + 0x004; + port->nsect_addr = base + 0x008; + port->lbal_addr = base + 0x00c; + port->lbam_addr = base + 0x010; + port->lbah_addr = base + 0x014; + port->device_addr = base + 0x018; + port->status_addr = + port->command_addr = base + 0x01c; + port->altstatus_addr = + port->ctl_addr = base + 0x038; +} + +static int adma_port_start(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct adma_port_priv *pp; + int rc; + + rc = ata_port_start(ap); + if (rc) + return rc; + adma_enter_reg_mode(ap); + rc = -ENOMEM; + pp = kcalloc(1, sizeof(*pp), GFP_KERNEL); + if (!pp) + goto err_out; + pp->pkt = dma_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma, + GFP_KERNEL); + if (!pp->pkt) + goto err_out_kfree; + /* paranoia? */ + if ((pp->pkt_dma & 7) != 0) { + printk("bad alignment for pp->pkt_dma: %08x\n", + (u32)pp->pkt_dma); + dma_free_coherent(dev, ADMA_PKT_BYTES, + pp->pkt, pp->pkt_dma); + goto err_out_kfree; + } + memset(pp->pkt, 0, ADMA_PKT_BYTES); + ap->private_data = pp; + adma_reinit_engine(ap); + return 0; + +err_out_kfree: + kfree(pp); +err_out: + ata_port_stop(ap); + return rc; +} + +static void adma_port_stop(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct adma_port_priv *pp = ap->private_data; + + adma_reset_engine(ADMA_REGS(ap->host_set->mmio_base, ap->port_no)); + if (pp != NULL) { + ap->private_data = NULL; + if (pp->pkt != NULL) + dma_free_coherent(dev, ADMA_PKT_BYTES, + pp->pkt, pp->pkt_dma); + kfree(pp); + } + ata_port_stop(ap); +} + +static void adma_host_stop(struct ata_host_set *host_set) +{ + unsigned int port_no; + + for (port_no = 0; port_no < ADMA_PORTS; ++port_no) + adma_reset_engine(ADMA_REGS(host_set->mmio_base, port_no)); + + ata_pci_host_stop(host_set); +} + +static void adma_host_init(unsigned int chip_id, + struct ata_probe_ent *probe_ent) +{ + unsigned int port_no; + void __iomem *mmio_base = probe_ent->mmio_base; + + /* enable/lock aGO operation */ + writeb(7, mmio_base + ADMA_MODE_LOCK); + + /* reset the ADMA logic */ + for (port_no = 0; port_no < ADMA_PORTS; ++port_no) + adma_reset_engine(ADMA_REGS(mmio_base, port_no)); +} + +static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) +{ + int rc; + + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + return 0; +} + +static int adma_ata_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + static int printed_version; + struct ata_probe_ent *probe_ent = NULL; + void __iomem *mmio_base; + unsigned int board_idx = (unsigned int) ent->driver_data; + int rc, port_no; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto err_out; + + if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) { + rc = -ENODEV; + goto err_out_regions; + } + + mmio_base = pci_iomap(pdev, 4, 0); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + rc = adma_set_dma_masks(pdev, mmio_base); + if (rc) + goto err_out_iounmap; + + probe_ent = kcalloc(1, sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_iounmap; + } + + probe_ent->dev = pci_dev_to_dev(pdev); + INIT_LIST_HEAD(&probe_ent->node); + + probe_ent->sht = adma_port_info[board_idx].sht; + probe_ent->host_flags = adma_port_info[board_idx].host_flags; + probe_ent->pio_mask = adma_port_info[board_idx].pio_mask; + probe_ent->mwdma_mask = adma_port_info[board_idx].mwdma_mask; + probe_ent->udma_mask = adma_port_info[board_idx].udma_mask; + probe_ent->port_ops = adma_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + probe_ent->n_ports = ADMA_PORTS; + + for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { + adma_ata_setup_port(&probe_ent->port[port_no], + ADMA_ATA_REGS((unsigned long)mmio_base, port_no)); + } + + pci_set_master(pdev); + + /* initialize adapter */ + adma_host_init(board_idx, probe_ent); + + rc = ata_device_add(probe_ent); + kfree(probe_ent); + if (rc != ADMA_PORTS) + goto err_out_iounmap; + return 0; + +err_out_iounmap: + pci_iounmap(pdev, mmio_base); +err_out_regions: + pci_release_regions(pdev); +err_out: + pci_disable_device(pdev); + return rc; +} + +static int __init adma_ata_init(void) +{ + return pci_module_init(&adma_ata_pci_driver); +} + +static void __exit adma_ata_exit(void) +{ + pci_unregister_driver(&adma_ata_pci_driver); +} + +MODULE_AUTHOR("Mark Lord"); +MODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +module_init(adma_ata_init); +module_exit(adma_ata_exit); diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c index 623082d3a83f..c89da7d5b6df 100644 --- a/drivers/scsi/pluto.c +++ b/drivers/scsi/pluto.c @@ -95,8 +95,7 @@ int __init pluto_detect(Scsi_Host_Template *tpnt) int i, retry, nplutos; fc_channel *fc; Scsi_Device dev; - struct timer_list fc_timer = - TIMER_INITIALIZER(pluto_detect_timeout, 0, 0); + DEFINE_TIMER(fc_timer, pluto_detect_timeout, 0, 0); tpnt->proc_name = "pluto"; fcscount = 0; diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c index b993652bfa25..637fb6565d28 100644 --- a/drivers/scsi/qla1280.c +++ b/drivers/scsi/qla1280.c @@ -996,7 +996,6 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action) break; case ABORT_DEVICE: - ha->flags.in_reset = 1; if (qla1280_verbose) printk(KERN_INFO "scsi(%ld:%d:%d:%d): Queueing abort device " @@ -1010,7 +1009,6 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action) printk(KERN_INFO "scsi(%ld:%d:%d:%d): Queueing device reset " "command.\n", ha->host_no, bus, target, lun); - ha->flags.in_reset = 1; if (qla1280_device_reset(ha, bus, target) == 0) result = SUCCESS; break; @@ -1019,7 +1017,6 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action) if (qla1280_verbose) printk(KERN_INFO "qla1280(%ld:%d): Issuing BUS " "DEVICE RESET\n", ha->host_no, bus); - ha->flags.in_reset = 1; if (qla1280_bus_reset(ha, bus == 0)) result = SUCCESS; @@ -1047,7 +1044,6 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action) if (!list_empty(&ha->done_q)) qla1280_done(ha); - ha->flags.in_reset = 0; /* If we didn't manage to issue the action, or we have no * command to wait for, exit here */ @@ -1269,6 +1265,22 @@ qla1280_biosparam_old(Disk * disk, kdev_t dev, int geom[]) return qla1280_biosparam(disk->device, NULL, disk->capacity, geom); } #endif + +/* disable risc and host interrupts */ +static inline void +qla1280_disable_intrs(struct scsi_qla_host *ha) +{ + WRT_REG_WORD(&ha->iobase->ictrl, 0); + RD_REG_WORD(&ha->iobase->ictrl); /* PCI Posted Write flush */ +} + +/* enable risc and host interrupts */ +static inline void +qla1280_enable_intrs(struct scsi_qla_host *ha) +{ + WRT_REG_WORD(&ha->iobase->ictrl, (ISP_EN_INT | ISP_EN_RISC)); + RD_REG_WORD(&ha->iobase->ictrl); /* PCI Posted Write flush */ +} /************************************************************************** * qla1280_intr_handler @@ -1290,7 +1302,7 @@ qla1280_intr_handler(int irq, void *dev_id, struct pt_regs *regs) ha->isr_count++; reg = ha->iobase; - WRT_REG_WORD(®->ictrl, 0); /* disable our interrupt. */ + qla1280_disable_intrs(ha); data = qla1280_debounce_register(®->istatus); /* Check for pending interrupts. */ @@ -1303,8 +1315,7 @@ qla1280_intr_handler(int irq, void *dev_id, struct pt_regs *regs) spin_unlock(HOST_LOCK); - /* enable our interrupt. */ - WRT_REG_WORD(®->ictrl, (ISP_EN_INT | ISP_EN_RISC)); + qla1280_enable_intrs(ha); LEAVE_INTR("qla1280_intr_handler"); return IRQ_RETVAL(handled); @@ -1317,7 +1328,7 @@ qla1280_set_target_parameters(struct scsi_qla_host *ha, int bus, int target) uint8_t mr; uint16_t mb[MAILBOX_REGISTER_COUNT]; struct nvram *nv; - int status; + int status, lun; nv = &ha->nvram; @@ -1325,24 +1336,38 @@ qla1280_set_target_parameters(struct scsi_qla_host *ha, int bus, int target) /* Set Target Parameters. */ mb[0] = MBC_SET_TARGET_PARAMETERS; - mb[1] = (uint16_t) (bus ? target | BIT_7 : target); - mb[1] <<= 8; - - mb[2] = (nv->bus[bus].target[target].parameter.c << 8); + mb[1] = (uint16_t)((bus ? target | BIT_7 : target) << 8); + mb[2] = nv->bus[bus].target[target].parameter.renegotiate_on_error << 8; + mb[2] |= nv->bus[bus].target[target].parameter.stop_queue_on_check << 9; + mb[2] |= nv->bus[bus].target[target].parameter.auto_request_sense << 10; + mb[2] |= nv->bus[bus].target[target].parameter.tag_queuing << 11; + mb[2] |= nv->bus[bus].target[target].parameter.enable_sync << 12; + mb[2] |= nv->bus[bus].target[target].parameter.enable_wide << 13; + mb[2] |= nv->bus[bus].target[target].parameter.parity_checking << 14; + mb[2] |= nv->bus[bus].target[target].parameter.disconnect_allowed << 15; if (IS_ISP1x160(ha)) { mb[2] |= nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr << 5; - mb[3] = (nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8) | - nv->bus[bus].target[target].sync_period; + mb[3] = (nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8); mb[6] = (nv->bus[bus].target[target].ppr_1x160.flags.ppr_options << 8) | nv->bus[bus].target[target].ppr_1x160.flags.ppr_bus_width; mr |= BIT_6; } else { - mb[3] = (nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8) | - nv->bus[bus].target[target].sync_period; + mb[3] = (nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8); } + mb[3] |= nv->bus[bus].target[target].sync_period; - status = qla1280_mailbox_command(ha, mr, &mb[0]); + status = qla1280_mailbox_command(ha, mr, mb); + + /* Set Device Queue Parameters. */ + for (lun = 0; lun < MAX_LUNS; lun++) { + mb[0] = MBC_SET_DEVICE_QUEUE; + mb[1] = (uint16_t)((bus ? target | BIT_7 : target) << 8); + mb[1] |= lun; + mb[2] = nv->bus[bus].max_queue_depth; + mb[3] = nv->bus[bus].target[target].execution_throttle; + status |= qla1280_mailbox_command(ha, 0x0f, mb); + } if (status) printk(KERN_WARNING "scsi(%ld:%i:%i): " @@ -1389,19 +1414,19 @@ qla1280_slave_configure(struct scsi_device *device) } #if LINUX_VERSION_CODE > 0x020500 - nv->bus[bus].target[target].parameter.f.enable_sync = device->sdtr; - nv->bus[bus].target[target].parameter.f.enable_wide = device->wdtr; + nv->bus[bus].target[target].parameter.enable_sync = device->sdtr; + nv->bus[bus].target[target].parameter.enable_wide = device->wdtr; nv->bus[bus].target[target].ppr_1x160.flags.enable_ppr = device->ppr; #endif if (driver_setup.no_sync || (driver_setup.sync_mask && (~driver_setup.sync_mask & (1 << target)))) - nv->bus[bus].target[target].parameter.f.enable_sync = 0; + nv->bus[bus].target[target].parameter.enable_sync = 0; if (driver_setup.no_wide || (driver_setup.wide_mask && (~driver_setup.wide_mask & (1 << target)))) - nv->bus[bus].target[target].parameter.f.enable_wide = 0; + nv->bus[bus].target[target].parameter.enable_wide = 0; if (IS_ISP1x160(ha)) { if (driver_setup.no_ppr || (driver_setup.ppr_mask && @@ -1410,7 +1435,7 @@ qla1280_slave_configure(struct scsi_device *device) } spin_lock_irqsave(HOST_LOCK, flags); - if (nv->bus[bus].target[target].parameter.f.enable_sync) + if (nv->bus[bus].target[target].parameter.enable_sync) status = qla1280_set_target_parameters(ha, bus, target); qla1280_get_target_parameters(ha, device); spin_unlock_irqrestore(HOST_LOCK, flags); @@ -1448,7 +1473,6 @@ qla1280_select_queue_depth(struct Scsi_Host *host, struct scsi_device *sdev_q) * * Input: * ha = adapter block pointer. - * done_q = done queue. */ static void qla1280_done(struct scsi_qla_host *ha) @@ -1522,7 +1546,7 @@ qla1280_return_status(struct response * sts, struct scsi_cmnd *cp) int host_status = DID_ERROR; uint16_t comp_status = le16_to_cpu(sts->comp_status); uint16_t state_flags = le16_to_cpu(sts->state_flags); - uint16_t residual_length = le16_to_cpu(sts->residual_length); + uint16_t residual_length = le32_to_cpu(sts->residual_length); uint16_t scsi_status = le16_to_cpu(sts->scsi_status); #if DEBUG_QLA1280_INTR static char *reason[] = { @@ -1582,7 +1606,7 @@ qla1280_return_status(struct response * sts, struct scsi_cmnd *cp) case CS_DATA_OVERRUN: dprintk(2, "Data overrun 0x%x\n", residual_length); - dprintk(2, "qla1280_isr: response packet data\n"); + dprintk(2, "qla1280_return_status: response packet data\n"); qla1280_dump_buffer(2, (char *)sts, RESPONSE_ENTRY_SIZE); host_status = DID_ERROR; break; @@ -1617,40 +1641,6 @@ qla1280_return_status(struct response * sts, struct scsi_cmnd *cp) /* QLogic ISP1280 Hardware Support Functions. */ /****************************************************************************/ - /* - * qla2100_enable_intrs - * qla2100_disable_intrs - * - * Input: - * ha = adapter block pointer. - * - * Returns: - * None - */ -static inline void -qla1280_enable_intrs(struct scsi_qla_host *ha) -{ - struct device_reg __iomem *reg; - - reg = ha->iobase; - /* enable risc and host interrupts */ - WRT_REG_WORD(®->ictrl, (ISP_EN_INT | ISP_EN_RISC)); - RD_REG_WORD(®->ictrl); /* PCI Posted Write flush */ - ha->flags.ints_enabled = 1; -} - -static inline void -qla1280_disable_intrs(struct scsi_qla_host *ha) -{ - struct device_reg __iomem *reg; - - reg = ha->iobase; - /* disable risc and host interrupts */ - WRT_REG_WORD(®->ictrl, 0); - RD_REG_WORD(®->ictrl); /* PCI Posted Write flush */ - ha->flags.ints_enabled = 0; -} - /* * qla1280_initialize_adapter * Initialize board. @@ -1679,7 +1669,6 @@ qla1280_initialize_adapter(struct scsi_qla_host *ha) ha->flags.reset_active = 0; ha->flags.abort_isp_active = 0; - ha->flags.ints_enabled = 0; #if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) if (ia64_platform_is("sn2")) { printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA " @@ -1758,69 +1747,6 @@ qla1280_initialize_adapter(struct scsi_qla_host *ha) return status; } - -/* - * ISP Firmware Test - * Checks if present version of RISC firmware is older than - * driver firmware. - * - * Input: - * ha = adapter block pointer. - * - * Returns: - * 0 = firmware does not need to be loaded. - */ -static int -qla1280_isp_firmware(struct scsi_qla_host *ha) -{ - struct nvram *nv = (struct nvram *) ha->response_ring; - int status = 0; /* dg 2/27 always loads RISC */ - uint16_t mb[MAILBOX_REGISTER_COUNT]; - - ENTER("qla1280_isp_firmware"); - - dprintk(1, "scsi(%li): Determining if RISC is loaded\n", ha->host_no); - - /* Bad NVRAM data, load RISC code. */ - if (!ha->nvram_valid) { - ha->flags.disable_risc_code_load = 0; - } else - ha->flags.disable_risc_code_load = - nv->cntr_flags_1.disable_loading_risc_code; - - if (ha->flags.disable_risc_code_load) { - dprintk(3, "qla1280_isp_firmware: Telling RISC to verify " - "checksum of loaded BIOS code.\n"); - - /* Verify checksum of loaded RISC code. */ - mb[0] = MBC_VERIFY_CHECKSUM; - /* mb[1] = ql12_risc_code_addr01; */ - mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; - - if (!(status = - qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]))) { - /* Start firmware execution. */ - dprintk(3, "qla1280_isp_firmware: Startng F/W " - "execution.\n"); - - mb[0] = MBC_EXECUTE_FIRMWARE; - /* mb[1] = ql12_risc_code_addr01; */ - mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; - qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); - } else - printk(KERN_INFO "qla1280: RISC checksum failed.\n"); - } else { - dprintk(1, "qla1280: NVRAM configured to load RISC load.\n"); - status = 1; - } - - if (status) - dprintk(2, "qla1280_isp_firmware: **** Load RISC code ****\n"); - - LEAVE("qla1280_isp_firmware"); - return status; -} - /* * Chip diagnostics * Test chip for proper operation. @@ -2006,7 +1932,7 @@ qla1280_load_firmware_dma(struct scsi_qla_host *ha) "%d,%d(0x%x)\n", risc_code_address, cnt, num, risc_address); for(i = 0; i < cnt; i++) - ((uint16_t *)ha->request_ring)[i] = + ((__le16 *)ha->request_ring)[i] = cpu_to_le16(risc_code_address[i]); mb[0] = MBC_LOAD_RAM; @@ -2085,7 +2011,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha) mb[1] = *ql1280_board_tbl[ha->devnum].fwstart; err = qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); if (err) { - printk(KERN_ERR "scsi(%li): Failed checksum\n", ha->host_no); + printk(KERN_ERR "scsi(%li): RISC checksum failed.\n", ha->host_no); return err; } @@ -2105,14 +2031,7 @@ qla1280_start_firmware(struct scsi_qla_host *ha) static int qla1280_load_firmware(struct scsi_qla_host *ha) { - int err = -ENODEV; - - /* If firmware needs to be loaded */ - if (!qla1280_isp_firmware(ha)) { - printk(KERN_ERR "scsi(%li): isp_firmware() failed!\n", - ha->host_no); - goto out; - } + int err; err = qla1280_chip_diag(ha); if (err) @@ -2246,17 +2165,17 @@ qla1280_set_target_defaults(struct scsi_qla_host *ha, int bus, int target) { struct nvram *nv = &ha->nvram; - nv->bus[bus].target[target].parameter.f.renegotiate_on_error = 1; - nv->bus[bus].target[target].parameter.f.auto_request_sense = 1; - nv->bus[bus].target[target].parameter.f.tag_queuing = 1; - nv->bus[bus].target[target].parameter.f.enable_sync = 1; + nv->bus[bus].target[target].parameter.renegotiate_on_error = 1; + nv->bus[bus].target[target].parameter.auto_request_sense = 1; + nv->bus[bus].target[target].parameter.tag_queuing = 1; + nv->bus[bus].target[target].parameter.enable_sync = 1; #if 1 /* Some SCSI Processors do not seem to like this */ - nv->bus[bus].target[target].parameter.f.enable_wide = 1; + nv->bus[bus].target[target].parameter.enable_wide = 1; #endif - nv->bus[bus].target[target].parameter.f.parity_checking = 1; - nv->bus[bus].target[target].parameter.f.disconnect_allowed = 1; nv->bus[bus].target[target].execution_throttle = nv->bus[bus].max_queue_depth - 1; + nv->bus[bus].target[target].parameter.parity_checking = 1; + nv->bus[bus].target[target].parameter.disconnect_allowed = 1; if (IS_ISP1x160(ha)) { nv->bus[bus].target[target].flags.flags1x160.device_enable = 1; @@ -2284,9 +2203,9 @@ qla1280_set_defaults(struct scsi_qla_host *ha) /* nv->cntr_flags_1.disable_loading_risc_code = 1; */ nv->firmware_feature.f.enable_fast_posting = 1; nv->firmware_feature.f.disable_synchronous_backoff = 1; - nv->termination.f.scsi_bus_0_control = 3; - nv->termination.f.scsi_bus_1_control = 3; - nv->termination.f.auto_term_support = 1; + nv->termination.scsi_bus_0_control = 3; + nv->termination.scsi_bus_1_control = 3; + nv->termination.auto_term_support = 1; /* * Set default FIFO magic - What appropriate values would be here @@ -2296,7 +2215,12 @@ qla1280_set_defaults(struct scsi_qla_host *ha) * header file provided by QLogic seems to be bogus or incomplete * at best. */ - nv->isp_config.c = ISP_CFG1_BENAB|ISP_CFG1_F128; + nv->isp_config.burst_enable = 1; + if (IS_ISP1040(ha)) + nv->isp_config.fifo_threshold |= 3; + else + nv->isp_config.fifo_threshold |= 4; + if (IS_ISP1x160(ha)) nv->isp_parameter = 0x01; /* fast memory enable */ @@ -2327,66 +2251,53 @@ qla1280_config_target(struct scsi_qla_host *ha, int bus, int target) struct nvram *nv = &ha->nvram; uint16_t mb[MAILBOX_REGISTER_COUNT]; int status, lun; + uint16_t flag; /* Set Target Parameters. */ mb[0] = MBC_SET_TARGET_PARAMETERS; - mb[1] = (uint16_t) (bus ? target | BIT_7 : target); - mb[1] <<= 8; - - /* - * Do not enable wide, sync, and ppr for the initial - * INQUIRY run. We enable this later if we determine - * the target actually supports it. - */ - nv->bus[bus].target[target].parameter.f. - auto_request_sense = 1; - nv->bus[bus].target[target].parameter.f. - stop_queue_on_check = 0; - - if (IS_ISP1x160(ha)) - nv->bus[bus].target[target].ppr_1x160. - flags.enable_ppr = 0; + mb[1] = (uint16_t)((bus ? target | BIT_7 : target) << 8); /* - * No sync, wide, etc. while probing + * Do not enable sync and ppr for the initial INQUIRY run. We + * enable this later if we determine the target actually + * supports it. */ - mb[2] = (nv->bus[bus].target[target].parameter.c << 8) & - ~(TP_SYNC /*| TP_WIDE | TP_PPR*/); + mb[2] = (TP_RENEGOTIATE | TP_AUTO_REQUEST_SENSE | TP_TAGGED_QUEUE + | TP_WIDE | TP_PARITY | TP_DISCONNECT); if (IS_ISP1x160(ha)) mb[3] = nv->bus[bus].target[target].flags.flags1x160.sync_offset << 8; else mb[3] = nv->bus[bus].target[target].flags.flags1x80.sync_offset << 8; mb[3] |= nv->bus[bus].target[target].sync_period; - - status = qla1280_mailbox_command(ha, BIT_3 | BIT_2 | BIT_1 | BIT_0, &mb[0]); + status = qla1280_mailbox_command(ha, 0x0f, mb); /* Save Tag queuing enable flag. */ - mb[0] = BIT_0 << target; - if (nv->bus[bus].target[target].parameter.f.tag_queuing) - ha->bus_settings[bus].qtag_enables |= mb[0]; + flag = (BIT_0 << target) & mb[0]; + if (nv->bus[bus].target[target].parameter.tag_queuing) + ha->bus_settings[bus].qtag_enables |= flag; /* Save Device enable flag. */ if (IS_ISP1x160(ha)) { if (nv->bus[bus].target[target].flags.flags1x160.device_enable) - ha->bus_settings[bus].device_enables |= mb[0]; + ha->bus_settings[bus].device_enables |= flag; ha->bus_settings[bus].lun_disables |= 0; } else { if (nv->bus[bus].target[target].flags.flags1x80.device_enable) - ha->bus_settings[bus].device_enables |= mb[0]; + ha->bus_settings[bus].device_enables |= flag; /* Save LUN disable flag. */ if (nv->bus[bus].target[target].flags.flags1x80.lun_disable) - ha->bus_settings[bus].lun_disables |= mb[0]; + ha->bus_settings[bus].lun_disables |= flag; } /* Set Device Queue Parameters. */ for (lun = 0; lun < MAX_LUNS; lun++) { mb[0] = MBC_SET_DEVICE_QUEUE; - mb[1] = (uint16_t)(bus ? target | BIT_7 : target); - mb[1] = mb[1] << 8 | lun; + mb[1] = (uint16_t)((bus ? target | BIT_7 : target) << 8); + mb[1] |= lun; mb[2] = nv->bus[bus].max_queue_depth; mb[3] = nv->bus[bus].target[target].execution_throttle; - status |= qla1280_mailbox_command(ha, 0x0f, &mb[0]); + status |= qla1280_mailbox_command(ha, 0x0f, mb); } return status; @@ -2431,7 +2342,6 @@ qla1280_nvram_config(struct scsi_qla_host *ha) struct nvram *nv = &ha->nvram; int bus, target, status = 0; uint16_t mb[MAILBOX_REGISTER_COUNT]; - uint16_t mask; ENTER("qla1280_nvram_config"); @@ -2439,7 +2349,7 @@ qla1280_nvram_config(struct scsi_qla_host *ha) /* Always force AUTO sense for LINUX SCSI */ for (bus = 0; bus < MAX_BUSES; bus++) for (target = 0; target < MAX_TARGETS; target++) { - nv->bus[bus].target[target].parameter.f. + nv->bus[bus].target[target].parameter. auto_request_sense = 1; } } else { @@ -2457,31 +2367,40 @@ qla1280_nvram_config(struct scsi_qla_host *ha) hwrev = RD_REG_WORD(®->cfg_0) & ISP_CFG0_HWMSK; - cfg1 = RD_REG_WORD(®->cfg_1); + cfg1 = RD_REG_WORD(®->cfg_1) & ~(BIT_4 | BIT_5 | BIT_6); cdma_conf = RD_REG_WORD(®->cdma_cfg); ddma_conf = RD_REG_WORD(®->ddma_cfg); /* Busted fifo, says mjacob. */ - if (hwrev == ISP_CFG0_1040A) - WRT_REG_WORD(®->cfg_1, cfg1 | ISP_CFG1_F64); - else - WRT_REG_WORD(®->cfg_1, cfg1 | ISP_CFG1_F64 | ISP_CFG1_BENAB); + if (hwrev != ISP_CFG0_1040A) + cfg1 |= nv->isp_config.fifo_threshold << 4; + + cfg1 |= nv->isp_config.burst_enable << 2; + WRT_REG_WORD(®->cfg_1, cfg1); WRT_REG_WORD(®->cdma_cfg, cdma_conf | CDMA_CONF_BENAB); WRT_REG_WORD(®->ddma_cfg, cdma_conf | DDMA_CONF_BENAB); } else { + uint16_t cfg1, term; + /* Set ISP hardware DMA burst */ - mb[0] = nv->isp_config.c; + cfg1 = nv->isp_config.fifo_threshold << 4; + cfg1 |= nv->isp_config.burst_enable << 2; /* Enable DMA arbitration on dual channel controllers */ if (ha->ports > 1) - mb[0] |= BIT_13; - WRT_REG_WORD(®->cfg_1, mb[0]); + cfg1 |= BIT_13; + WRT_REG_WORD(®->cfg_1, cfg1); /* Set SCSI termination. */ - WRT_REG_WORD(®->gpio_enable, (BIT_3 + BIT_2 + BIT_1 + BIT_0)); - mb[0] = nv->termination.c & (BIT_3 + BIT_2 + BIT_1 + BIT_0); - WRT_REG_WORD(®->gpio_data, mb[0]); + WRT_REG_WORD(®->gpio_enable, + BIT_7 | BIT_3 | BIT_2 | BIT_1 | BIT_0); + term = nv->termination.scsi_bus_1_control; + term |= nv->termination.scsi_bus_0_control << 2; + term |= nv->termination.auto_term_support << 7; + RD_REG_WORD(®->id_l); /* Flush PCI write */ + WRT_REG_WORD(®->gpio_data, term); } + RD_REG_WORD(®->id_l); /* Flush PCI write */ /* ISP parameter word. */ mb[0] = MBC_SET_SYSTEM_PARAMETER; @@ -2497,16 +2416,17 @@ qla1280_nvram_config(struct scsi_qla_host *ha) /* Firmware feature word. */ mb[0] = MBC_SET_FIRMWARE_FEATURES; - mask = BIT_5 | BIT_1 | BIT_0; - mb[1] = le16_to_cpu(nv->firmware_feature.w) & (mask); + mb[1] = nv->firmware_feature.f.enable_fast_posting; + mb[1] |= nv->firmware_feature.f.report_lvd_bus_transition << 1; + mb[1] |= nv->firmware_feature.f.disable_synchronous_backoff << 5; #if defined(CONFIG_IA64_GENERIC) || defined (CONFIG_IA64_SGI_SN2) if (ia64_platform_is("sn2")) { printk(KERN_INFO "scsi(%li): Enabling SN2 PCI DMA " "workaround\n", ha->host_no); - mb[1] |= BIT_9; + mb[1] |= nv->firmware_feature.f.unused_9 << 9; /* XXX */ } #endif - status |= qla1280_mailbox_command(ha, mask, &mb[0]); + status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); /* Retry count and delay. */ mb[0] = MBC_SET_RETRY_COUNT; @@ -2535,27 +2455,27 @@ qla1280_nvram_config(struct scsi_qla_host *ha) mb[2] |= BIT_5; if (nv->bus[1].config_2.data_line_active_negation) mb[2] |= BIT_4; - status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]); + status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, mb); mb[0] = MBC_SET_DATA_OVERRUN_RECOVERY; mb[1] = 2; /* Reset SCSI bus and return all outstanding IO */ - status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); + status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); /* thingy */ mb[0] = MBC_SET_PCI_CONTROL; - mb[1] = 2; /* Data DMA Channel Burst Enable */ - mb[2] = 2; /* Command DMA Channel Burst Enable */ - status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]); + mb[1] = BIT_1; /* Data DMA Channel Burst Enable */ + mb[2] = BIT_1; /* Command DMA Channel Burst Enable */ + status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, mb); mb[0] = MBC_SET_TAG_AGE_LIMIT; mb[1] = 8; - status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, &mb[0]); + status |= qla1280_mailbox_command(ha, BIT_1 | BIT_0, mb); /* Selection timeout. */ mb[0] = MBC_SET_SELECTION_TIMEOUT; mb[1] = nv->bus[0].selection_timeout; mb[2] = nv->bus[1].selection_timeout; - status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, &mb[0]); + status |= qla1280_mailbox_command(ha, BIT_2 | BIT_1 | BIT_0, mb); for (bus = 0; bus < ha->ports; bus++) status |= qla1280_config_bus(ha, bus); @@ -3066,7 +2986,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) struct scsi_cmnd *cmd = sp->cmd; cmd_a64_entry_t *pkt; struct scatterlist *sg = NULL; - u32 *dword_ptr; + __le32 *dword_ptr; dma_addr_t dma_handle; int status = 0; int cnt; @@ -3104,10 +3024,13 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) REQUEST_ENTRY_CNT - (ha->req_ring_index - cnt); } + dprintk(3, "Number of free entries=(%d) seg_cnt=0x%x\n", + ha->req_q_cnt, seg_cnt); + /* If room for request in request ring. */ if ((req_cnt + 2) >= ha->req_q_cnt) { status = 1; - dprintk(2, "qla1280_64bit_start_scsi: in-ptr=0x%x req_q_cnt=" + dprintk(2, "qla1280_start_scsi: in-ptr=0x%x req_q_cnt=" "0x%xreq_cnt=0x%x", ha->req_ring_index, ha->req_q_cnt, req_cnt); goto out; @@ -3119,7 +3042,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) if (cnt >= MAX_OUTSTANDING_COMMANDS) { status = 1; - dprintk(2, "qla1280_64bit_start_scsi: NO ROOM IN " + dprintk(2, "qla1280_start_scsi: NO ROOM IN " "OUTSTANDING ARRAY, req_q_cnt=0x%x", ha->req_q_cnt); goto out; } @@ -3128,7 +3051,7 @@ qla1280_64bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) ha->req_q_cnt -= req_cnt; CMD_HANDLE(sp->cmd) = (unsigned char *)(unsigned long)(cnt + 1); - dprintk(2, "64bit_start: cmd=%p sp=%p CDB=%xm, handle %lx\n", cmd, sp, + dprintk(2, "start: cmd=%p sp=%p CDB=%xm, handle %lx\n", cmd, sp, cmd->cmnd[0], (long)CMD_HANDLE(sp->cmd)); dprintk(2, " bus %i, target %i, lun %i\n", SCSI_BUS_32(cmd), SCSI_TCN_32(cmd), SCSI_LUN_32(cmd)); @@ -3350,7 +3273,7 @@ qla1280_32bit_start_scsi(struct scsi_qla_host *ha, struct srb * sp) struct scsi_cmnd *cmd = sp->cmd; struct cmd_entry *pkt; struct scatterlist *sg = NULL; - uint32_t *dword_ptr; + __le32 *dword_ptr; int status = 0; int cnt; int req_cnt; @@ -3993,21 +3916,21 @@ qla1280_get_target_options(struct scsi_cmnd *cmd, struct scsi_qla_host *ha) result = cmd->request_buffer; n = &ha->nvram; - n->bus[bus].target[target].parameter.f.enable_wide = 0; - n->bus[bus].target[target].parameter.f.enable_sync = 0; + n->bus[bus].target[target].parameter.enable_wide = 0; + n->bus[bus].target[target].parameter.enable_sync = 0; n->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 0; if (result[7] & 0x60) - n->bus[bus].target[target].parameter.f.enable_wide = 1; + n->bus[bus].target[target].parameter.enable_wide = 1; if (result[7] & 0x10) - n->bus[bus].target[target].parameter.f.enable_sync = 1; + n->bus[bus].target[target].parameter.enable_sync = 1; if ((result[2] >= 3) && (result[4] + 5 > 56) && (result[56] & 0x4)) n->bus[bus].target[target].ppr_1x160.flags.enable_ppr = 1; dprintk(2, "get_target_options(): wide %i, sync %i, ppr %i\n", - n->bus[bus].target[target].parameter.f.enable_wide, - n->bus[bus].target[target].parameter.f.enable_sync, + n->bus[bus].target[target].parameter.enable_wide, + n->bus[bus].target[target].parameter.enable_sync, n->bus[bus].target[target].ppr_1x160.flags.enable_ppr); } #endif @@ -4071,7 +3994,7 @@ qla1280_status_entry(struct scsi_qla_host *ha, struct response *pkt, /* Save ISP completion status */ CMD_RESULT(cmd) = qla1280_return_status(pkt, cmd); - if (scsi_status & SS_CHECK_CONDITION) { + if (scsi_status & SAM_STAT_CHECK_CONDITION) { if (comp_status != CS_ARS_FAILED) { uint16_t req_sense_length = le16_to_cpu(pkt->req_sense_length); @@ -4650,7 +4573,7 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (pci_set_dma_mask(ha->pdev, (dma_addr_t) ~ 0ULL)) { if (pci_set_dma_mask(ha->pdev, 0xffffffff)) { printk(KERN_WARNING "scsi(%li): Unable to set a " - " suitable DMA mask - aboring\n", ha->host_no); + "suitable DMA mask - aborting\n", ha->host_no); error = -ENODEV; goto error_free_irq; } @@ -4660,14 +4583,14 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) #else if (pci_set_dma_mask(ha->pdev, 0xffffffff)) { printk(KERN_WARNING "scsi(%li): Unable to set a " - " suitable DMA mask - aboring\n", ha->host_no); + "suitable DMA mask - aborting\n", ha->host_no); error = -ENODEV; goto error_free_irq; } #endif ha->request_ring = pci_alloc_consistent(ha->pdev, - ((REQUEST_ENTRY_CNT + 1) * (sizeof(request_t))), + ((REQUEST_ENTRY_CNT + 1) * sizeof(request_t)), &ha->request_dma); if (!ha->request_ring) { printk(KERN_INFO "qla1280: Failed to get request memory\n"); @@ -4675,7 +4598,7 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) } ha->response_ring = pci_alloc_consistent(ha->pdev, - ((RESPONSE_ENTRY_CNT + 1) * (sizeof(struct response))), + ((RESPONSE_ENTRY_CNT + 1) * sizeof(struct response)), &ha->response_dma); if (!ha->response_ring) { printk(KERN_INFO "qla1280: Failed to get response memory\n"); @@ -4758,7 +4681,7 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) #if LINUX_VERSION_CODE >= 0x020600 error_disable_adapter: - WRT_REG_WORD(&ha->iobase->ictrl, 0); + qla1280_disable_intrs(ha); #endif error_free_irq: free_irq(pdev->irq, ha); @@ -4770,11 +4693,11 @@ qla1280_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) #endif error_free_response_ring: pci_free_consistent(ha->pdev, - ((RESPONSE_ENTRY_CNT + 1) * (sizeof(struct response))), + ((RESPONSE_ENTRY_CNT + 1) * sizeof(struct response)), ha->response_ring, ha->response_dma); error_free_request_ring: pci_free_consistent(ha->pdev, - ((REQUEST_ENTRY_CNT + 1) * (sizeof(request_t))), + ((REQUEST_ENTRY_CNT + 1) * sizeof(request_t)), ha->request_ring, ha->request_dma); error_put_host: scsi_host_put(host); @@ -4795,7 +4718,7 @@ qla1280_remove_one(struct pci_dev *pdev) scsi_remove_host(host); #endif - WRT_REG_WORD(&ha->iobase->ictrl, 0); + qla1280_disable_intrs(ha); free_irq(pdev->irq, ha); diff --git a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h index d245ae07518e..59915fb70301 100644 --- a/drivers/scsi/qla1280.h +++ b/drivers/scsi/qla1280.h @@ -94,9 +94,6 @@ #define REQUEST_ENTRY_CNT 256 /* Number of request entries. */ #define RESPONSE_ENTRY_CNT 16 /* Number of response entries. */ -/* Number of segments 1 - 65535 */ -#define SG_SEGMENTS 32 /* Cmd entry + 6 continuations */ - /* * SCSI Request Block structure (sp) that is placed * on cmd->SCp location of every I/O @@ -378,29 +375,23 @@ struct nvram { uint16_t unused_12; /* 12, 13 */ uint16_t unused_14; /* 14, 15 */ - union { - uint8_t c; - struct { - uint8_t reserved:2; - uint8_t burst_enable:1; - uint8_t reserved_1:1; - uint8_t fifo_threshold:4; - } f; + struct { + uint8_t reserved:2; + uint8_t burst_enable:1; + uint8_t reserved_1:1; + uint8_t fifo_threshold:4; } isp_config; /* 16 */ /* Termination * 0 = Disable, 1 = high only, 3 = Auto term */ - union { - uint8_t c; - struct { - uint8_t scsi_bus_1_control:2; - uint8_t scsi_bus_0_control:2; - uint8_t unused_0:1; - uint8_t unused_1:1; - uint8_t unused_2:1; - uint8_t auto_term_support:1; - } f; + struct { + uint8_t scsi_bus_1_control:2; + uint8_t scsi_bus_0_control:2; + uint8_t unused_0:1; + uint8_t unused_1:1; + uint8_t unused_2:1; + uint8_t auto_term_support:1; } termination; /* 17 */ uint16_t isp_parameter; /* 18, 19 */ @@ -460,18 +451,15 @@ struct nvram { uint16_t unused_38; /* 38, 39 */ struct { - union { - uint8_t c; - struct { - uint8_t renegotiate_on_error:1; - uint8_t stop_queue_on_check:1; - uint8_t auto_request_sense:1; - uint8_t tag_queuing:1; - uint8_t enable_sync:1; - uint8_t enable_wide:1; - uint8_t parity_checking:1; - uint8_t disconnect_allowed:1; - } f; + struct { + uint8_t renegotiate_on_error:1; + uint8_t stop_queue_on_check:1; + uint8_t auto_request_sense:1; + uint8_t tag_queuing:1; + uint8_t enable_sync:1; + uint8_t enable_wide:1; + uint8_t parity_checking:1; + uint8_t disconnect_allowed:1; } parameter; /* 40 */ uint8_t execution_throttle; /* 41 */ @@ -528,23 +516,23 @@ struct cmd_entry { uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - uint32_t handle; /* System handle. */ + __le32 handle; /* System handle. */ uint8_t lun; /* SCSI LUN */ uint8_t target; /* SCSI ID */ - uint16_t cdb_len; /* SCSI command length. */ - uint16_t control_flags; /* Control flags. */ - uint16_t reserved; - uint16_t timeout; /* Command timeout. */ - uint16_t dseg_count; /* Data segment count. */ + __le16 cdb_len; /* SCSI command length. */ + __le16 control_flags; /* Control flags. */ + __le16 reserved; + __le16 timeout; /* Command timeout. */ + __le16 dseg_count; /* Data segment count. */ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */ - uint32_t dseg_0_address; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ - uint32_t dseg_2_address; /* Data segment 2 address. */ - uint32_t dseg_2_length; /* Data segment 2 length. */ - uint32_t dseg_3_address; /* Data segment 3 address. */ - uint32_t dseg_3_length; /* Data segment 3 length. */ + __le32 dseg_0_address; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address; /* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ + __le32 dseg_2_address; /* Data segment 2 address. */ + __le32 dseg_2_length; /* Data segment 2 length. */ + __le32 dseg_3_address; /* Data segment 3 address. */ + __le32 dseg_3_length; /* Data segment 3 length. */ }; /* @@ -556,21 +544,21 @@ struct cont_entry { uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - uint32_t reserved; /* Reserved */ - uint32_t dseg_0_address; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ - uint32_t dseg_2_address; /* Data segment 2 address. */ - uint32_t dseg_2_length; /* Data segment 2 length. */ - uint32_t dseg_3_address; /* Data segment 3 address. */ - uint32_t dseg_3_length; /* Data segment 3 length. */ - uint32_t dseg_4_address; /* Data segment 4 address. */ - uint32_t dseg_4_length; /* Data segment 4 length. */ - uint32_t dseg_5_address; /* Data segment 5 address. */ - uint32_t dseg_5_length; /* Data segment 5 length. */ - uint32_t dseg_6_address; /* Data segment 6 address. */ - uint32_t dseg_6_length; /* Data segment 6 length. */ + __le32 reserved; /* Reserved */ + __le32 dseg_0_address; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address; /* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ + __le32 dseg_2_address; /* Data segment 2 address. */ + __le32 dseg_2_length; /* Data segment 2 length. */ + __le32 dseg_3_address; /* Data segment 3 address. */ + __le32 dseg_3_length; /* Data segment 3 length. */ + __le32 dseg_4_address; /* Data segment 4 address. */ + __le32 dseg_4_length; /* Data segment 4 length. */ + __le32 dseg_5_address; /* Data segment 5 address. */ + __le32 dseg_5_length; /* Data segment 5 length. */ + __le32 dseg_6_address; /* Data segment 6 address. */ + __le32 dseg_6_length; /* Data segment 6 length. */ }; /* @@ -586,22 +574,22 @@ struct response { #define RF_FULL BIT_1 /* Full */ #define RF_BAD_HEADER BIT_2 /* Bad header. */ #define RF_BAD_PAYLOAD BIT_3 /* Bad payload. */ - uint32_t handle; /* System handle. */ - uint16_t scsi_status; /* SCSI status. */ - uint16_t comp_status; /* Completion status. */ - uint16_t state_flags; /* State flags. */ -#define SF_TRANSFER_CMPL BIT_14 /* Transfer Complete. */ -#define SF_GOT_SENSE BIT_13 /* Got Sense */ -#define SF_GOT_STATUS BIT_12 /* Got Status */ -#define SF_TRANSFERRED_DATA BIT_11 /* Transferred data */ -#define SF_SENT_CDB BIT_10 /* Send CDB */ -#define SF_GOT_TARGET BIT_9 /* */ -#define SF_GOT_BUS BIT_8 /* */ - uint16_t status_flags; /* Status flags. */ - uint16_t time; /* Time. */ - uint16_t req_sense_length; /* Request sense data length. */ - uint32_t residual_length; /* Residual transfer length. */ - uint16_t reserved[4]; + __le32 handle; /* System handle. */ + __le16 scsi_status; /* SCSI status. */ + __le16 comp_status; /* Completion status. */ + __le16 state_flags; /* State flags. */ +#define SF_TRANSFER_CMPL BIT_14 /* Transfer Complete. */ +#define SF_GOT_SENSE BIT_13 /* Got Sense */ +#define SF_GOT_STATUS BIT_12 /* Got Status */ +#define SF_TRANSFERRED_DATA BIT_11 /* Transferred data */ +#define SF_SENT_CDB BIT_10 /* Send CDB */ +#define SF_GOT_TARGET BIT_9 /* */ +#define SF_GOT_BUS BIT_8 /* */ + __le16 status_flags; /* Status flags. */ + __le16 time; /* Time. */ + __le16 req_sense_length;/* Request sense data length. */ + __le32 residual_length; /* Residual transfer length. */ + __le16 reserved[4]; uint8_t req_sense_data[32]; /* Request sense data. */ }; @@ -614,7 +602,7 @@ struct mrk_entry { uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - uint32_t reserved; + __le32 reserved; uint8_t lun; /* SCSI LUN */ uint8_t target; /* SCSI ID */ uint8_t modifier; /* Modifier (7-0). */ @@ -638,11 +626,11 @@ struct ecmd_entry { uint32_t handle; /* System handle. */ uint8_t lun; /* SCSI LUN */ uint8_t target; /* SCSI ID */ - uint16_t cdb_len; /* SCSI command length. */ - uint16_t control_flags; /* Control flags. */ - uint16_t reserved; - uint16_t timeout; /* Command timeout. */ - uint16_t dseg_count; /* Data segment count. */ + __le16 cdb_len; /* SCSI command length. */ + __le16 control_flags; /* Control flags. */ + __le16 reserved; + __le16 timeout; /* Command timeout. */ + __le16 dseg_count; /* Data segment count. */ uint8_t scsi_cdb[88]; /* SCSI command words. */ }; @@ -655,20 +643,20 @@ typedef struct { uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - uint32_t handle; /* System handle. */ + __le32 handle; /* System handle. */ uint8_t lun; /* SCSI LUN */ uint8_t target; /* SCSI ID */ - uint16_t cdb_len; /* SCSI command length. */ - uint16_t control_flags; /* Control flags. */ - uint16_t reserved; - uint16_t timeout; /* Command timeout. */ - uint16_t dseg_count; /* Data segment count. */ + __le16 cdb_len; /* SCSI command length. */ + __le16 control_flags; /* Control flags. */ + __le16 reserved; + __le16 timeout; /* Command timeout. */ + __le16 dseg_count; /* Data segment count. */ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */ - uint32_t reserved_1[2]; /* unused */ - uint32_t dseg_0_address[2]; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address[2]; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ + __le32 reserved_1[2]; /* unused */ + __le32 dseg_0_address[2]; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address[2]; /* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ } cmd_a64_entry_t, request_t; /* @@ -680,16 +668,16 @@ struct cont_a64_entry { uint8_t entry_count; /* Entry count. */ uint8_t sys_define; /* System defined. */ uint8_t entry_status; /* Entry Status. */ - uint32_t dseg_0_address[2]; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address[2]; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ - uint32_t dseg_2_address[2]; /* Data segment 2 address. */ - uint32_t dseg_2_length; /* Data segment 2 length. */ - uint32_t dseg_3_address[2]; /* Data segment 3 address. */ - uint32_t dseg_3_length; /* Data segment 3 length. */ - uint32_t dseg_4_address[2]; /* Data segment 4 address. */ - uint32_t dseg_4_length; /* Data segment 4 length. */ + __le32 dseg_0_address[2]; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address[2]; /* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ + __le32 dseg_2_address[2]; /* Data segment 2 address. */ + __le32 dseg_2_length; /* Data segment 2 length. */ + __le32 dseg_3_address[2]; /* Data segment 3 address. */ + __le32 dseg_3_length; /* Data segment 3 length. */ + __le32 dseg_4_address[2]; /* Data segment 4 address. */ + __le32 dseg_4_length; /* Data segment 4 length. */ }; /* @@ -701,10 +689,10 @@ struct elun_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status not used. */ - uint32_t reserved_2; - uint16_t lun; /* Bit 15 is bus number. */ - uint16_t reserved_4; - uint32_t option_flags; + __le32 reserved_2; + __le16 lun; /* Bit 15 is bus number. */ + __le16 reserved_4; + __le32 option_flags; uint8_t status; uint8_t reserved_5; uint8_t command_count; /* Number of ATIOs allocated. */ @@ -714,8 +702,8 @@ struct elun_entry { /* commands (2-26). */ uint8_t group_7_length; /* SCSI CDB length for group 7 */ /* commands (2-26). */ - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t reserved_6[20]; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 reserved_6[20]; }; /* @@ -729,20 +717,20 @@ struct modify_lun_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; /* SCSI LUN */ uint8_t reserved_3; uint8_t operators; uint8_t reserved_4; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t reserved_5; uint8_t command_count; /* Number of ATIOs allocated. */ uint8_t immed_notify_count; /* Number of Immediate Notify */ /* entries allocated. */ - uint16_t reserved_6; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t reserved_7[20]; + __le16 reserved_6; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 reserved_7[20]; }; /* @@ -754,20 +742,20 @@ struct notify_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t reserved_4; uint8_t tag_value; /* Received queue tag message value */ uint8_t tag_type; /* Received queue tag message type */ /* entries allocated. */ - uint16_t seq_id; + __le16 seq_id; uint8_t scsi_msg[8]; /* SCSI message not handled by ISP */ - uint16_t reserved_5[8]; + __le16 reserved_5[8]; uint8_t sense_data[18]; }; @@ -780,16 +768,16 @@ struct nack_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t event; - uint16_t seq_id; - uint16_t reserved_4[22]; + __le16 seq_id; + __le16 reserved_4[22]; }; /* @@ -801,12 +789,12 @@ struct atio_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; uint8_t initiator_id; uint8_t cdb_len; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t scsi_status; uint8_t tag_value; /* Received queue tag message value */ @@ -824,28 +812,28 @@ struct ctio_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; /* SCSI LUN */ uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t scsi_status; uint8_t tag_value; /* Received queue tag message value */ uint8_t tag_type; /* Received queue tag message type */ - uint32_t transfer_length; - uint32_t residual; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ - uint32_t dseg_0_address; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ - uint32_t dseg_2_address; /* Data segment 2 address. */ - uint32_t dseg_2_length; /* Data segment 2 length. */ - uint32_t dseg_3_address; /* Data segment 3 address. */ - uint32_t dseg_3_length; /* Data segment 3 length. */ + __le32 transfer_length; + __le32 residual; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 dseg_count; /* Data segment count. */ + __le32 dseg_0_address; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address; /* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ + __le32 dseg_2_address; /* Data segment 2 address. */ + __le32 dseg_2_length; /* Data segment 2 length. */ + __le32 dseg_3_address; /* Data segment 3 address. */ + __le32 dseg_3_length; /* Data segment 3 length. */ }; /* @@ -857,24 +845,24 @@ struct ctio_ret_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; /* SCSI LUN */ uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t scsi_status; uint8_t tag_value; /* Received queue tag message value */ uint8_t tag_type; /* Received queue tag message type */ - uint32_t transfer_length; - uint32_t residual; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ - uint32_t dseg_0_address; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address; /* Data segment 1 address. */ - uint16_t dseg_1_length; /* Data segment 1 length. */ + __le32 transfer_length; + __le32 residual; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 dseg_count; /* Data segment count. */ + __le32 dseg_0_address; /* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address; /* Data segment 1 address. */ + __le16 dseg_1_length; /* Data segment 1 length. */ uint8_t sense_data[18]; }; @@ -887,25 +875,25 @@ struct ctio_a64_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; /* SCSI LUN */ uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t scsi_status; uint8_t tag_value; /* Received queue tag message value */ uint8_t tag_type; /* Received queue tag message type */ - uint32_t transfer_length; - uint32_t residual; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ - uint32_t reserved_4[2]; - uint32_t dseg_0_address[2]; /* Data segment 0 address. */ - uint32_t dseg_0_length; /* Data segment 0 length. */ - uint32_t dseg_1_address[2]; /* Data segment 1 address. */ - uint32_t dseg_1_length; /* Data segment 1 length. */ + __le32 transfer_length; + __le32 residual; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 dseg_count; /* Data segment count. */ + __le32 reserved_4[2]; + __le32 dseg_0_address[2];/* Data segment 0 address. */ + __le32 dseg_0_length; /* Data segment 0 length. */ + __le32 dseg_1_address[2];/* Data segment 1 address. */ + __le32 dseg_1_length; /* Data segment 1 length. */ }; /* @@ -917,21 +905,21 @@ struct ctio_a64_ret_entry { uint8_t entry_count; /* Entry count. */ uint8_t reserved_1; uint8_t entry_status; /* Entry Status. */ - uint32_t reserved_2; + __le32 reserved_2; uint8_t lun; /* SCSI LUN */ uint8_t initiator_id; uint8_t reserved_3; uint8_t target_id; - uint32_t option_flags; + __le32 option_flags; uint8_t status; uint8_t scsi_status; uint8_t tag_value; /* Received queue tag message value */ uint8_t tag_type; /* Received queue tag message type */ - uint32_t transfer_length; - uint32_t residual; - uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ - uint16_t dseg_count; /* Data segment count. */ - uint16_t reserved_4[7]; + __le32 transfer_length; + __le32 residual; + __le16 timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + __le16 dseg_count; /* Data segment count. */ + __le16 reserved_4[7]; uint8_t sense_data[18]; }; @@ -979,14 +967,6 @@ struct ctio_a64_ret_entry { #define CS_RETRY 0x82 /* Driver defined */ /* - * ISP status entry - SCSI status byte bit definitions. - */ -#define SS_CHECK_CONDITION BIT_1 -#define SS_CONDITION_MET BIT_2 -#define SS_BUSY_CONDITION BIT_3 -#define SS_RESERVE_CONFLICT (BIT_4 | BIT_3) - -/* * ISP target entries - Option flags bit definitions. */ #define OF_ENABLE_TAG BIT_1 /* Tagged queue action enable */ @@ -1082,10 +1062,6 @@ struct scsi_qla_host { uint32_t reset_active:1; /* 3 */ uint32_t abort_isp_active:1; /* 4 */ uint32_t disable_risc_code_load:1; /* 5 */ - uint32_t enable_64bit_addressing:1; /* 6 */ - uint32_t in_reset:1; /* 7 */ - uint32_t ints_enabled:1; - uint32_t ignore_nvram:1; #ifdef __ia64__ uint32_t use_pci_vchannel:1; #endif diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 659a5d63467d..fc25cd834668 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -211,6 +211,138 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) sysfs_remove_bin_file(&host->shost_gendev.kobj, &sysfs_nvram_attr); } +/* Scsi_Host attributes. */ + +static ssize_t +qla2x00_drvr_version_show(struct class_device *cdev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str); +} + +static ssize_t +qla2x00_fw_version_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + char fw_str[30]; + + return snprintf(buf, PAGE_SIZE, "%s\n", + ha->isp_ops.fw_version_str(ha, fw_str)); +} + +static ssize_t +qla2x00_serial_num_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + uint32_t sn; + + sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; + return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, + sn % 100000); +} + +static ssize_t +qla2x00_isp_name_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + return snprintf(buf, PAGE_SIZE, "%s\n", ha->brd_info->isp_name); +} + +static ssize_t +qla2x00_isp_id_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", + ha->product_id[0], ha->product_id[1], ha->product_id[2], + ha->product_id[3]); +} + +static ssize_t +qla2x00_model_name_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + return snprintf(buf, PAGE_SIZE, "%s\n", ha->model_number); +} + +static ssize_t +qla2x00_model_desc_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + return snprintf(buf, PAGE_SIZE, "%s\n", + ha->model_desc ? ha->model_desc: ""); +} + +static ssize_t +qla2x00_pci_info_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + char pci_info[30]; + + return snprintf(buf, PAGE_SIZE, "%s\n", + ha->isp_ops.pci_info_str(ha, pci_info)); +} + +static ssize_t +qla2x00_state_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + int len = 0; + + if (atomic_read(&ha->loop_state) == LOOP_DOWN || + atomic_read(&ha->loop_state) == LOOP_DEAD) + len = snprintf(buf, PAGE_SIZE, "Link Down\n"); + else if (atomic_read(&ha->loop_state) != LOOP_READY || + test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) || + test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) + len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n"); + else { + len = snprintf(buf, PAGE_SIZE, "Link Up - "); + + switch (ha->current_topology) { + case ISP_CFG_NL: + len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + break; + case ISP_CFG_FL: + len += snprintf(buf + len, PAGE_SIZE-len, "FL_Port\n"); + break; + case ISP_CFG_N: + len += snprintf(buf + len, PAGE_SIZE-len, + "N_Port to N_Port\n"); + break; + case ISP_CFG_F: + len += snprintf(buf + len, PAGE_SIZE-len, "F_Port\n"); + break; + default: + len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + break; + } + } + return len; +} + +static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, + NULL); +static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); +static CLASS_DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); +static CLASS_DEVICE_ATTR(isp_name, S_IRUGO, qla2x00_isp_name_show, NULL); +static CLASS_DEVICE_ATTR(isp_id, S_IRUGO, qla2x00_isp_id_show, NULL); +static CLASS_DEVICE_ATTR(model_name, S_IRUGO, qla2x00_model_name_show, NULL); +static CLASS_DEVICE_ATTR(model_desc, S_IRUGO, qla2x00_model_desc_show, NULL); +static CLASS_DEVICE_ATTR(pci_info, S_IRUGO, qla2x00_pci_info_show, NULL); +static CLASS_DEVICE_ATTR(state, S_IRUGO, qla2x00_state_show, NULL); + +struct class_device_attribute *qla2x00_host_attrs[] = { + &class_device_attr_driver_version, + &class_device_attr_fw_version, + &class_device_attr_serial_num, + &class_device_attr_isp_name, + &class_device_attr_isp_id, + &class_device_attr_model_name, + &class_device_attr_model_desc, + &class_device_attr_pci_info, + &class_device_attr_state, + NULL, +}; + /* Host attributes. */ static void @@ -228,16 +360,16 @@ qla2x00_get_starget_node_name(struct scsi_target *starget) struct Scsi_Host *host = dev_to_shost(starget->dev.parent); scsi_qla_host_t *ha = to_qla_host(host); fc_port_t *fcport; - uint64_t node_name = 0; + u64 node_name = 0; list_for_each_entry(fcport, &ha->fcports, list) { if (starget->id == fcport->os_target_id) { - node_name = *(uint64_t *)fcport->node_name; + node_name = wwn_to_u64(fcport->node_name); break; } } - fc_starget_node_name(starget) = be64_to_cpu(node_name); + fc_starget_node_name(starget) = node_name; } static void @@ -246,16 +378,16 @@ qla2x00_get_starget_port_name(struct scsi_target *starget) struct Scsi_Host *host = dev_to_shost(starget->dev.parent); scsi_qla_host_t *ha = to_qla_host(host); fc_port_t *fcport; - uint64_t port_name = 0; + u64 port_name = 0; list_for_each_entry(fcport, &ha->fcports, list) { if (starget->id == fcport->os_target_id) { - port_name = *(uint64_t *)fcport->port_name; + port_name = wwn_to_u64(fcport->port_name); break; } } - fc_starget_port_name(starget) = be64_to_cpu(port_name); + fc_starget_port_name(starget) = port_name; } static void @@ -304,10 +436,13 @@ struct fc_function_template qla2xxx_transport_functions = { .show_host_node_name = 1, .show_host_port_name = 1, + .show_host_supported_classes = 1, + .get_host_port_id = qla2x00_get_host_port_id, .show_host_port_id = 1, .dd_fcrport_size = sizeof(struct fc_port *), + .show_rport_supported_classes = 1, .get_starget_node_name = qla2x00_get_starget_node_name, .show_starget_node_name = 1, @@ -325,8 +460,7 @@ struct fc_function_template qla2xxx_transport_functions = { void qla2x00_init_host_attr(scsi_qla_host_t *ha) { - fc_host_node_name(ha->host) = - be64_to_cpu(*(uint64_t *)ha->init_cb->node_name); - fc_host_port_name(ha->host) = - be64_to_cpu(*(uint64_t *)ha->init_cb->port_name); + fc_host_node_name(ha->host) = wwn_to_u64(ha->init_cb->node_name); + fc_host_port_name(ha->host) = wwn_to_u64(ha->init_cb->port_name); + fc_host_supported_classes(ha->host) = FC_COS_CLASS3; } diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 72bbaa91dc77..9791496fa788 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -1334,7 +1334,7 @@ qla24xx_fw_dump(scsi_qla_host_t *ha, int hardware_locked) dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); WRT_REG_DWORD(dmp_reg, 0xB0200000); - dmp_reg = (uint32_t *)((uint8_t *)reg + 0xFC); + dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xFC); fw->shadow_reg[2] = RD_REG_DWORD(dmp_reg); dmp_reg = (uint32_t __iomem *)((uint8_t __iomem *)reg + 0xF0); diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index b8d90e97e017..9684e7a91fa9 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -81,6 +81,7 @@ #define DEBUG2_3_11(x) do {x;} while (0); #define DEBUG2_9_10(x) do {x;} while (0); #define DEBUG2_11(x) do {x;} while (0); +#define DEBUG2_13(x) do {x;} while (0); #else #define DEBUG2(x) do {} while (0); #endif @@ -169,8 +170,14 @@ #if defined(QL_DEBUG_LEVEL_13) #define DEBUG13(x) do {x;} while (0) +#if !defined(DEBUG2_13) +#define DEBUG2_13(x) do {x;} while(0) +#endif #else #define DEBUG13(x) do {} while (0) +#if !defined(QL_DEBUG_LEVEL_2) +#define DEBUG2_13(x) do {} while(0) +#endif #endif #if defined(QL_DEBUG_LEVEL_14) diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 1c6d366f4fad..b455c31405e4 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -214,6 +214,7 @@ * valid range of an N-PORT id is 0 through 0x7ef. */ #define NPH_LAST_HANDLE 0x7ef +#define NPH_MGMT_SERVER 0x7fa /* FFFFFA */ #define NPH_SNS 0x7fc /* FFFFFC */ #define NPH_FABRIC_CONTROLLER 0x7fd /* FFFFFD */ #define NPH_F_PORT 0x7fe /* FFFFFE */ @@ -630,6 +631,7 @@ typedef struct { #define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */ #define MBC_READ_RAM_EXTENDED 0xf /* Read RAM extended. */ #define MBC_IOCB_COMMAND 0x12 /* Execute IOCB command. */ +#define MBC_STOP_FIRMWARE 0x14 /* Stop firmware. */ #define MBC_ABORT_COMMAND 0x15 /* Abort IOCB command. */ #define MBC_ABORT_DEVICE 0x16 /* Abort device (ID/LUN). */ #define MBC_ABORT_TARGET 0x17 /* Abort target (ID). */ @@ -913,7 +915,7 @@ typedef struct { * MSB BIT 1 = * MSB BIT 2 = * MSB BIT 3 = - * MSB BIT 4 = + * MSB BIT 4 = LED mode * MSB BIT 5 = enable 50 ohm termination * MSB BIT 6 = Data Rate (2300 only) * MSB BIT 7 = Data Rate (2300 only) @@ -1035,7 +1037,7 @@ typedef struct { * MSB BIT 1 = * MSB BIT 2 = * MSB BIT 3 = - * MSB BIT 4 = + * MSB BIT 4 = LED mode * MSB BIT 5 = enable 50 ohm termination * MSB BIT 6 = Data Rate (2300 only) * MSB BIT 7 = Data Rate (2300 only) @@ -1131,10 +1133,7 @@ typedef struct { uint8_t link_down_timeout; - uint8_t adapter_id_0[4]; - uint8_t adapter_id_1[4]; - uint8_t adapter_id_2[4]; - uint8_t adapter_id_3[4]; + uint8_t adapter_id[16]; uint8_t alt1_boot_node_name[WWN_SIZE]; uint16_t alt1_boot_lun_number; @@ -1673,6 +1672,7 @@ typedef struct fc_port { uint8_t cur_path; /* current path id */ struct fc_rport *rport; + u32 supported_classes; } fc_port_t; /* @@ -1727,6 +1727,8 @@ typedef struct fc_port { #define CT_REJECT_RESPONSE 0x8001 #define CT_ACCEPT_RESPONSE 0x8002 +#define CT_REASON_CANNOT_PERFORM 0x09 +#define CT_EXPL_ALREADY_REGISTERED 0x10 #define NS_N_PORT_TYPE 0x01 #define NS_NL_PORT_TYPE 0x02 @@ -1768,6 +1770,100 @@ typedef struct fc_port { #define RSNN_NN_REQ_SIZE (16 + 8 + 1 + 255) #define RSNN_NN_RSP_SIZE 16 +/* + * HBA attribute types. + */ +#define FDMI_HBA_ATTR_COUNT 9 +#define FDMI_HBA_NODE_NAME 1 +#define FDMI_HBA_MANUFACTURER 2 +#define FDMI_HBA_SERIAL_NUMBER 3 +#define FDMI_HBA_MODEL 4 +#define FDMI_HBA_MODEL_DESCRIPTION 5 +#define FDMI_HBA_HARDWARE_VERSION 6 +#define FDMI_HBA_DRIVER_VERSION 7 +#define FDMI_HBA_OPTION_ROM_VERSION 8 +#define FDMI_HBA_FIRMWARE_VERSION 9 +#define FDMI_HBA_OS_NAME_AND_VERSION 0xa +#define FDMI_HBA_MAXIMUM_CT_PAYLOAD_LENGTH 0xb + +struct ct_fdmi_hba_attr { + uint16_t type; + uint16_t len; + union { + uint8_t node_name[WWN_SIZE]; + uint8_t manufacturer[32]; + uint8_t serial_num[8]; + uint8_t model[16]; + uint8_t model_desc[80]; + uint8_t hw_version[16]; + uint8_t driver_version[32]; + uint8_t orom_version[16]; + uint8_t fw_version[16]; + uint8_t os_version[128]; + uint8_t max_ct_len[4]; + } a; +}; + +struct ct_fdmi_hba_attributes { + uint32_t count; + struct ct_fdmi_hba_attr entry[FDMI_HBA_ATTR_COUNT]; +}; + +/* + * Port attribute types. + */ +#define FDMI_PORT_ATTR_COUNT 5 +#define FDMI_PORT_FC4_TYPES 1 +#define FDMI_PORT_SUPPORT_SPEED 2 +#define FDMI_PORT_CURRENT_SPEED 3 +#define FDMI_PORT_MAX_FRAME_SIZE 4 +#define FDMI_PORT_OS_DEVICE_NAME 5 +#define FDMI_PORT_HOST_NAME 6 + +struct ct_fdmi_port_attr { + uint16_t type; + uint16_t len; + union { + uint8_t fc4_types[32]; + uint32_t sup_speed; + uint32_t cur_speed; + uint32_t max_frame_size; + uint8_t os_dev_name[32]; + uint8_t host_name[32]; + } a; +}; + +/* + * Port Attribute Block. + */ +struct ct_fdmi_port_attributes { + uint32_t count; + struct ct_fdmi_port_attr entry[FDMI_PORT_ATTR_COUNT]; +}; + +/* FDMI definitions. */ +#define GRHL_CMD 0x100 +#define GHAT_CMD 0x101 +#define GRPL_CMD 0x102 +#define GPAT_CMD 0x110 + +#define RHBA_CMD 0x200 +#define RHBA_RSP_SIZE 16 + +#define RHAT_CMD 0x201 +#define RPRT_CMD 0x210 + +#define RPA_CMD 0x211 +#define RPA_RSP_SIZE 16 + +#define DHBA_CMD 0x300 +#define DHBA_REQ_SIZE (16 + 8) +#define DHBA_RSP_SIZE 16 + +#define DHAT_CMD 0x301 +#define DPRT_CMD 0x310 +#define DPA_CMD 0x311 + /* CT command header -- request/response common fields */ struct ct_cmd_hdr { uint8_t revision; @@ -1825,6 +1921,43 @@ struct ct_sns_req { uint8_t name_len; uint8_t sym_node_name[255]; } rsnn_nn; + + struct { + uint8_t hba_indentifier[8]; + } ghat; + + struct { + uint8_t hba_identifier[8]; + uint32_t entry_count; + uint8_t port_name[8]; + struct ct_fdmi_hba_attributes attrs; + } rhba; + + struct { + uint8_t hba_identifier[8]; + struct ct_fdmi_hba_attributes attrs; + } rhat; + + struct { + uint8_t port_name[8]; + struct ct_fdmi_port_attributes attrs; + } rpa; + + struct { + uint8_t port_name[8]; + } dhba; + + struct { + uint8_t port_name[8]; + } dhat; + + struct { + uint8_t port_name[8]; + } dprt; + + struct { + uint8_t port_name[8]; + } dpa; } req; }; @@ -1882,6 +2015,12 @@ struct ct_sns_rsp { struct { uint8_t fc4_types[32]; } gft_id; + + struct { + uint32_t entry_count; + uint8_t port_name[8]; + struct ct_fdmi_hba_attributes attrs; + } ghat; } rsp; }; @@ -2032,6 +2171,8 @@ struct isp_operations { uint16_t (*calc_req_entries) (uint16_t); void (*build_iocbs) (srb_t *, cmd_entry_t *, uint16_t); void * (*prep_ms_iocb) (struct scsi_qla_host *, uint32_t, uint32_t); + void * (*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t, + uint32_t); uint8_t * (*read_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); @@ -2111,6 +2252,7 @@ typedef struct scsi_qla_host { #define IOCTL_ERROR_RECOVERY 23 #define LOOP_RESET_NEEDED 24 #define BEACON_BLINK_NEEDED 25 +#define REGISTER_FDMI_NEEDED 26 uint32_t device_flags; #define DFLG_LOCAL_DEVICES BIT_0 @@ -2204,6 +2346,7 @@ typedef struct scsi_qla_host { int port_down_retry_count; uint8_t mbx_count; uint16_t last_loop_id; + uint16_t mgmt_svr_loop_id; uint32_t login_retry_count; @@ -2318,6 +2461,7 @@ typedef struct scsi_qla_host { uint8_t model_number[16+1]; #define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" char *model_desc; + uint8_t adapter_id[16+1]; uint8_t *node_name; uint8_t *port_name; @@ -2377,6 +2521,7 @@ typedef struct scsi_qla_host { #define QLA_SUSPENDED 0x106 #define QLA_BUSY 0x107 #define QLA_RSCNS_HANDLED 0x108 +#define QLA_ALREADY_REGISTERED 0x109 /* * Stat info for all adpaters diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 665c203e0675..e451941ad81d 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -52,7 +52,7 @@ extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc_flash(scsi_qla_host_t *, uint32_t *); extern int qla24xx_load_risc_hotplug(scsi_qla_host_t *, uint32_t *); -extern fc_port_t *qla2x00_alloc_fcport(scsi_qla_host_t *, int); +extern fc_port_t *qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t); extern int qla2x00_loop_resync(scsi_qla_host_t *); @@ -79,6 +79,7 @@ extern int ql2xplogiabsentdevice; extern int ql2xenablezio; extern int ql2xintrdelaytimer; extern int ql2xloginretrycount; +extern int ql2xfdmienable; extern void qla2x00_sp_compl(scsi_qla_host_t *, srb_t *); @@ -147,9 +148,6 @@ qla2x00_abort_target(fc_port_t *); #endif extern int -qla2x00_target_reset(scsi_qla_host_t *, struct fc_port *); - -extern int qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *, uint8_t *, uint16_t *); @@ -215,6 +213,9 @@ qla2x00_get_serdes_params(scsi_qla_host_t *, uint16_t *, uint16_t *, extern int qla2x00_set_serdes_params(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t); +extern int +qla2x00_stop_firmware(scsi_qla_host_t *); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -269,11 +270,14 @@ extern int qla2x00_rft_id(scsi_qla_host_t *); extern int qla2x00_rff_id(scsi_qla_host_t *); extern int qla2x00_rnn_id(scsi_qla_host_t *); 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 *); /* * Global Function Prototypes in qla_rscn.c source file. */ -extern fc_port_t *qla2x00_alloc_rscn_fcport(scsi_qla_host_t *, int); +extern fc_port_t *qla2x00_alloc_rscn_fcport(scsi_qla_host_t *, gfp_t); extern int qla2x00_handle_port_rscn(scsi_qla_host_t *, uint32_t, fc_port_t *, int); extern void qla2x00_process_iodesc(scsi_qla_host_t *, struct mbx_entry *); @@ -289,6 +293,8 @@ extern void qla2x00_cancel_io_descriptors(scsi_qla_host_t *); /* * Global Function Prototypes in qla_attr.c source file. */ +struct class_device_attribute; +extern struct class_device_attribute *qla2x00_host_attrs[]; struct fc_function_template; extern struct fc_function_template qla2xxx_transport_functions; extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 31ce4f62da13..e7b138c2e339 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1099,3 +1099,567 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *ha) return (rval); } + +/** + * qla2x00_mgmt_svr_login() - Login to fabric Managment Service. + * @ha: HA context + * + * Returns 0 on success. + */ +static int +qla2x00_mgmt_svr_login(scsi_qla_host_t *ha) +{ + int ret; + uint16_t mb[MAILBOX_REGISTER_COUNT]; + + ret = QLA_SUCCESS; + if (ha->flags.management_server_logged_in) + return ret; + + ha->isp_ops.fabric_login(ha, ha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa, + mb, BIT_1); + if (mb[0] != MBS_COMMAND_COMPLETE) { + DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: " + "loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n", + __func__, ha->host_no, ha->mgmt_svr_loop_id, mb[0], mb[1], + mb[2], mb[6], mb[7])); + ret = QLA_FUNCTION_FAILED; + } else + ha->flags.management_server_logged_in = 1; + + return ret; +} + +/** + * qla2x00_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query. + * @ha: HA context + * @req_size: request size in bytes + * @rsp_size: response size in bytes + * + * Returns a pointer to the @ha's ms_iocb. + */ +void * +qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size, + uint32_t rsp_size) +{ + ms_iocb_entry_t *ms_pkt; + + ms_pkt = ha->ms_iocb; + memset(ms_pkt, 0, sizeof(ms_iocb_entry_t)); + + ms_pkt->entry_type = MS_IOCB_TYPE; + ms_pkt->entry_count = 1; + SET_TARGET_ID(ha, ms_pkt->loop_id, ha->mgmt_svr_loop_id); + ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG); + ms_pkt->timeout = __constant_cpu_to_le16(59); + ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1); + ms_pkt->total_dsd_count = __constant_cpu_to_le16(2); + ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size); + ms_pkt->req_bytecount = cpu_to_le32(req_size); + + ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); + ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ms_pkt->dseg_req_length = ms_pkt->req_bytecount; + + ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma)); + ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); + ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount; + + return ms_pkt; +} + +/** + * qla24xx_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query. + * @ha: HA context + * @req_size: request size in bytes + * @rsp_size: response size in bytes + * + * Returns a pointer to the @ha's ms_iocb. + */ +void * +qla24xx_prep_ms_fdmi_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 ms_iocb_entry_t * +qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *ha, uint32_t req_size) +{ + ms_iocb_entry_t *ms_pkt = ha->ms_iocb; + struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb; + + if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) { + ct_pkt->cmd_byte_count = cpu_to_le32(req_size); + ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count; + } else { + ms_pkt->req_bytecount = cpu_to_le32(req_size); + ms_pkt->dseg_req_length = ms_pkt->req_bytecount; + } + + return ms_pkt; +} + +/** + * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query. + * @ct_req: CT request buffer + * @cmd: GS command + * @rsp_size: response size in bytes + * + * Returns a pointer to the intitialized @ct_req. + */ +static inline struct ct_sns_req * +qla2x00_prep_ct_fdmi_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 = 0x10; + ct_req->command = cpu_to_be16(cmd); + ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4); + + return ct_req; +} + +/** + * qla2x00_fdmi_rhba() - + * @ha: HA context + * + * Returns 0 on success. + */ +static int +qla2x00_fdmi_rhba(scsi_qla_host_t *ha) +{ + int rval, alen; + uint32_t size, sn; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + uint8_t *entries; + struct ct_fdmi_hba_attr *eiter; + + /* Issue RHBA */ + /* Prepare common MS IOCB */ + /* Request size adjusted after CT preparation */ + ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, 0, RHBA_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD, + RHBA_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare FDMI command arguments -- attribute block, attributes. */ + memcpy(ct_req->req.rhba.hba_identifier, ha->port_name, WWN_SIZE); + ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1); + memcpy(ct_req->req.rhba.port_name, ha->port_name, WWN_SIZE); + size = 2 * WWN_SIZE + 4 + 4; + + /* Attributes */ + ct_req->req.rhba.attrs.count = + __constant_cpu_to_be32(FDMI_HBA_ATTR_COUNT); + entries = ct_req->req.rhba.hba_identifier; + + /* Nodename. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME); + eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE); + memcpy(eiter->a.node_name, ha->node_name, WWN_SIZE); + size += 4 + WWN_SIZE; + + DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n", + __func__, ha->host_no, + eiter->a.node_name[0], eiter->a.node_name[1], eiter->a.node_name[2], + eiter->a.node_name[3], eiter->a.node_name[4], eiter->a.node_name[5], + eiter->a.node_name[6], eiter->a.node_name[7])); + + /* Manufacturer. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_MANUFACTURER); + strcpy(eiter->a.manufacturer, "QLogic Corporation"); + alen = strlen(eiter->a.manufacturer); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, ha->host_no, + eiter->a.manufacturer)); + + /* Serial number. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_SERIAL_NUMBER); + sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; + sprintf(eiter->a.serial_num, "%c%05d", 'A' + sn / 100000, sn % 100000); + alen = strlen(eiter->a.serial_num); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, ha->host_no, + eiter->a.serial_num)); + + /* Model name. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL); + strcpy(eiter->a.model, ha->model_number); + alen = strlen(eiter->a.model); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, ha->host_no, + eiter->a.model)); + + /* Model description. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION); + if (ha->model_desc) + strncpy(eiter->a.model_desc, ha->model_desc, 80); + alen = strlen(eiter->a.model_desc); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, ha->host_no, + eiter->a.model_desc)); + + /* Hardware version. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_HARDWARE_VERSION); + strcpy(eiter->a.hw_version, ha->adapter_id); + alen = strlen(eiter->a.hw_version); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, ha->host_no, + eiter->a.hw_version)); + + /* Driver version. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_DRIVER_VERSION); + strcpy(eiter->a.driver_version, qla2x00_version_str); + alen = strlen(eiter->a.driver_version); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, ha->host_no, + eiter->a.driver_version)); + + /* Option ROM version. */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_OPTION_ROM_VERSION); + strcpy(eiter->a.orom_version, "0.00"); + alen = strlen(eiter->a.orom_version); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, ha->host_no, + eiter->a.orom_version)); + + /* Firmware version */ + eiter = (struct ct_fdmi_hba_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION); + ha->isp_ops.fw_version_str(ha, eiter->a.fw_version); + alen = strlen(eiter->a.fw_version); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, ha->host_no, + eiter->a.fw_version)); + + /* Update MS request size. */ + qla2x00_update_ms_fdmi_iocb(ha, size + 16); + + DEBUG13(printk("%s(%ld): RHBA identifier=" + "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, + ha->host_no, ct_req->req.rhba.hba_identifier[0], + ct_req->req.rhba.hba_identifier[1], + ct_req->req.rhba.hba_identifier[2], + ct_req->req.rhba.hba_identifier[3], + ct_req->req.rhba.hba_identifier[4], + ct_req->req.rhba.hba_identifier[5], + ct_req->req.rhba.hba_identifier[6], + ct_req->req.rhba.hba_identifier[7], size)); + DEBUG13(qla2x00_dump_buffer(entries, 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): RHBA issue IOCB failed (%d).\n", + ha->host_no, rval)); + } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RHBA") != + QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM && + ct_rsp->header.explanation_code == + CT_EXPL_ALREADY_REGISTERED) { + DEBUG2_13(printk("%s(%ld): HBA already registered.\n", + __func__, ha->host_no)); + rval = QLA_ALREADY_REGISTERED; + } + } else { + DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n", + ha->host_no)); + } + + return rval; +} + +/** + * qla2x00_fdmi_dhba() - + * @ha: HA context + * + * Returns 0 on success. + */ +static int +qla2x00_fdmi_dhba(scsi_qla_host_t *ha) +{ + int rval; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + + /* Issue RPA */ + /* Prepare common MS IOCB */ + ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, DHBA_REQ_SIZE, + DHBA_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, DHBA_CMD, + DHBA_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare FDMI command arguments -- portname. */ + memcpy(ct_req->req.dhba.port_name, ha->port_name, WWN_SIZE); + + DEBUG13(printk("%s(%ld): DHBA portname=" + "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, ha->host_no, + ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1], + ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3], + ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5], + ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7])); + + /* 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): DHBA issue IOCB failed (%d).\n", + ha->host_no, rval)); + } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "DHBA") != + QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + } else { + DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n", + ha->host_no)); + } + + return rval; +} + +/** + * qla2x00_fdmi_rpa() - + * @ha: HA context + * + * Returns 0 on success. + */ +static int +qla2x00_fdmi_rpa(scsi_qla_host_t *ha) +{ + int rval, alen; + uint32_t size, max_frame_size; + + ms_iocb_entry_t *ms_pkt; + struct ct_sns_req *ct_req; + struct ct_sns_rsp *ct_rsp; + uint8_t *entries; + struct ct_fdmi_port_attr *eiter; + struct init_cb_24xx *icb24 = (struct init_cb_24xx *)ha->init_cb; + + /* Issue RPA */ + /* Prepare common MS IOCB */ + /* Request size adjusted after CT preparation */ + ms_pkt = ha->isp_ops.prep_ms_fdmi_iocb(ha, 0, RPA_RSP_SIZE); + + /* Prepare CT request */ + ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD, + RPA_RSP_SIZE); + ct_rsp = &ha->ct_sns->p.rsp; + + /* Prepare FDMI command arguments -- attribute block, attributes. */ + memcpy(ct_req->req.rpa.port_name, ha->port_name, WWN_SIZE); + size = WWN_SIZE + 4; + + /* Attributes */ + ct_req->req.rpa.attrs.count = + __constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT); + entries = ct_req->req.rpa.port_name; + + /* FC4 types. */ + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_FC4_TYPES); + eiter->len = __constant_cpu_to_be16(4 + 32); + eiter->a.fc4_types[2] = 0x01; + size += 4 + 32; + + DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__, ha->host_no, + eiter->a.fc4_types[2], eiter->a.fc4_types[1])); + + /* Supported speed. */ + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED); + eiter->len = __constant_cpu_to_be16(4 + 4); + if (IS_QLA25XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32(4); + else if (IS_QLA24XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32(8); + else if (IS_QLA23XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32(2); + else + eiter->a.sup_speed = __constant_cpu_to_be32(1); + size += 4 + 4; + + DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, ha->host_no, + eiter->a.sup_speed)); + + /* Current speed. */ + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_CURRENT_SPEED); + eiter->len = __constant_cpu_to_be16(4 + 4); + switch (ha->link_data_rate) { + case 0: + eiter->a.cur_speed = __constant_cpu_to_be32(1); + break; + case 1: + eiter->a.cur_speed = __constant_cpu_to_be32(2); + break; + case 3: + eiter->a.cur_speed = __constant_cpu_to_be32(8); + break; + case 4: + eiter->a.cur_speed = __constant_cpu_to_be32(4); + break; + } + size += 4 + 4; + + DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, ha->host_no, + eiter->a.cur_speed)); + + /* Max frame size. */ + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_MAX_FRAME_SIZE); + eiter->len = __constant_cpu_to_be16(4 + 4); + max_frame_size = IS_QLA24XX(ha) || IS_QLA25XX(ha) ? + (uint32_t) icb24->frame_payload_size: + (uint32_t) ha->init_cb->frame_payload_size; + eiter->a.max_frame_size = cpu_to_be32(max_frame_size); + size += 4 + 4; + + DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, ha->host_no, + eiter->a.max_frame_size)); + + /* OS device name. */ + eiter = (struct ct_fdmi_port_attr *) (entries + size); + eiter->type = __constant_cpu_to_be16(FDMI_PORT_OS_DEVICE_NAME); + sprintf(eiter->a.os_dev_name, "/proc/scsi/qla2xxx/%ld", ha->host_no); + alen = strlen(eiter->a.os_dev_name); + alen += (alen & 3) ? (4 - (alen & 3)) : 4; + eiter->len = cpu_to_be16(4 + alen); + size += 4 + alen; + + DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, ha->host_no, + eiter->a.os_dev_name)); + + /* Update MS request size. */ + qla2x00_update_ms_fdmi_iocb(ha, size + 16); + + DEBUG13(printk("%s(%ld): RPA portname=" + "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, + ha->host_no, ct_req->req.rpa.port_name[0], + ct_req->req.rpa.port_name[1], ct_req->req.rpa.port_name[2], + ct_req->req.rpa.port_name[3], ct_req->req.rpa.port_name[4], + ct_req->req.rpa.port_name[5], ct_req->req.rpa.port_name[6], + ct_req->req.rpa.port_name[7], size)); + DEBUG13(qla2x00_dump_buffer(entries, 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): RPA issue IOCB failed (%d).\n", + ha->host_no, rval)); + } else if (qla2x00_chk_ms_status(ha, ms_pkt, ct_rsp, "RPA") != + QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + } else { + DEBUG2(printk("scsi(%ld): RPA exiting normally.\n", + ha->host_no)); + } + + return rval; +} + +/** + * qla2x00_fdmi_register() - + * @ha: HA context + * + * Returns 0 on success. + */ +int +qla2x00_fdmi_register(scsi_qla_host_t *ha) +{ + int rval; + + rval = qla2x00_mgmt_svr_login(ha); + if (rval) + return rval; + + rval = qla2x00_fdmi_rhba(ha); + if (rval) { + if (rval != QLA_ALREADY_REGISTERED) + return rval; + + rval = qla2x00_fdmi_dhba(ha); + if (rval) + return rval; + + rval = qla2x00_fdmi_rhba(ha); + if (rval) + return rval; + } + rval = qla2x00_fdmi_rpa(ha); + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index a6d2559217cd..fbb6feee40cf 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -88,6 +88,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) ha->mbx_flags = 0; ha->isp_abort_cnt = 0; ha->beacon_blink_led = 0; + set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); qla_printk(KERN_INFO, ha, "Configuring PCI space...\n"); rval = ha->isp_ops.pci_config(ha); @@ -200,6 +201,7 @@ int qla2100_pci_config(scsi_qla_host_t *ha) { uint16_t w, mwi; + uint32_t d; unsigned long flags; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -214,9 +216,9 @@ qla2100_pci_config(scsi_qla_host_t *ha) pci_write_config_word(ha->pdev, PCI_COMMAND, w); /* Reset expansion ROM address decode enable */ - pci_read_config_word(ha->pdev, PCI_ROM_ADDRESS, &w); - w &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_word(ha->pdev, PCI_ROM_ADDRESS, w); + pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); + d &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -236,6 +238,7 @@ int qla2300_pci_config(scsi_qla_host_t *ha) { uint16_t w, mwi; + uint32_t d; unsigned long flags = 0; uint32_t cnt; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -301,9 +304,9 @@ qla2300_pci_config(scsi_qla_host_t *ha) pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); /* Reset expansion ROM address decode enable */ - pci_read_config_word(ha->pdev, PCI_ROM_ADDRESS, &w); - w &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_word(ha->pdev, PCI_ROM_ADDRESS, w); + pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); + d &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -323,6 +326,7 @@ int qla24xx_pci_config(scsi_qla_host_t *ha) { uint16_t w, mwi; + uint32_t d; unsigned long flags = 0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; int pcix_cmd_reg, pcie_dctl_reg; @@ -365,9 +369,9 @@ qla24xx_pci_config(scsi_qla_host_t *ha) } /* Reset expansion ROM address decode enable */ - pci_read_config_word(ha->pdev, PCI_ROM_ADDRESS, &w); - w &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_word(ha->pdev, PCI_ROM_ADDRESS, w); + pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); + d &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1563,7 +1567,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) ha->flags.enable_lip_reset = ((nv->host_p[1] & BIT_1) ? 1 : 0); ha->flags.enable_lip_full_login = ((nv->host_p[1] & BIT_2) ? 1 : 0); ha->flags.enable_target_reset = ((nv->host_p[1] & BIT_3) ? 1 : 0); - ha->flags.enable_led_scheme = ((nv->efi_parameters & BIT_3) ? 1 : 0); + ha->flags.enable_led_scheme = (nv->special_options[1] & BIT_4) ? 1 : 0; ha->operating_mode = (icb->add_firmware_options[0] & (BIT_6 | BIT_5 | BIT_4)) >> 4; @@ -1681,7 +1685,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) * Returns a pointer to the allocated fcport, or NULL, if none available. */ fc_port_t * -qla2x00_alloc_fcport(scsi_qla_host_t *ha, int flags) +qla2x00_alloc_fcport(scsi_qla_host_t *ha, gfp_t flags) { fc_port_t *fcport; @@ -1697,6 +1701,7 @@ qla2x00_alloc_fcport(scsi_qla_host_t *ha, int flags) fcport->iodesc_idx_sent = IODESC_INVALID_INDEX; atomic_set(&fcport->state, FCS_UNCONFIGURED); fcport->flags = FCF_RLC_SUPPORT; + fcport->supported_classes = FC_COS_UNSPECIFIED; return (fcport); } @@ -1898,7 +1903,8 @@ qla2x00_configure_local_loop(scsi_qla_host_t *ha) continue; /* Bypass if not same domain and area of adapter. */ - if (area != ha->d_id.b.area || domain != ha->d_id.b.domain) + if (area && domain && + (area != ha->d_id.b.area || domain != ha->d_id.b.domain)) continue; /* Bypass invalid local loop ID. */ @@ -2063,8 +2069,8 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) return; } - rport_ids.node_name = be64_to_cpu(*(uint64_t *)fcport->node_name); - rport_ids.port_name = be64_to_cpu(*(uint64_t *)fcport->port_name); + rport_ids.node_name = wwn_to_u64(fcport->node_name); + rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; @@ -2075,6 +2081,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *ha, fc_port_t *fcport) return; } rport->dd_data = fcport; + rport->supported_classes = fcport->supported_classes; rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; if (fcport->port_type == FCT_INITIATOR) @@ -2130,6 +2137,11 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha) return (QLA_SUCCESS); } do { + /* FDMI support. */ + if (ql2xfdmienable && + test_and_clear_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags)) + qla2x00_fdmi_register(ha); + /* Ensure we are logged into the SNS. */ if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) loop_id = NPH_SNS; @@ -2392,6 +2404,12 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *ha, struct list_head *new_fcports) if (new_fcport->d_id.b24 == ha->d_id.b24) continue; + /* Bypass if same domain and area of adapter. */ + if (((new_fcport->d_id.b24 & 0xffff00) == + (ha->d_id.b24 & 0xffff00)) && ha->current_topology == + ISP_CFG_FL) + continue; + /* Bypass reserved domain fields. */ if ((new_fcport->d_id.b.domain & 0xf0) == 0xf0) continue; @@ -2794,6 +2812,11 @@ qla2x00_fabric_login(scsi_qla_host_t *ha, fc_port_t *fcport, } } + if (mb[10] & BIT_0) + fcport->supported_classes |= FC_COS_CLASS2; + if (mb[10] & BIT_1) + fcport->supported_classes |= FC_COS_CLASS3; + rval = QLA_SUCCESS; break; } else if (mb[0] == MBS_LOOP_ID_USED) { diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index ebdc3c54d155..37f82e2cd7fb 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -810,12 +810,8 @@ qla24xx_start_scsi(srb_t *sp) ha->req_q_cnt = ha->request_q_length - (ha->req_ring_index - cnt); } - if (ha->req_q_cnt < (req_cnt + 2)) { - if (cmd->use_sg) - pci_unmap_sg(ha->pdev, sg, cmd->use_sg, - cmd->sc_data_direction); + if (ha->req_q_cnt < (req_cnt + 2)) goto queuing_error; - } /* Build command packet. */ ha->current_outstanding_cmd = handle; diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index f910de6dd437..c255bb0268a9 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -451,6 +451,8 @@ qla2x00_async_event(scsi_qla_host_t *ha, uint16_t *mb) ha->flags.management_server_logged_in = 0; ha->link_data_rate = 0; + if (ql2xfdmienable) + set_bit(REGISTER_FDMI_NEEDED, &ha->dpc_flags); /* Update AEN queue. */ qla2x00_enqueue_aen(ha, MBA_LOOP_DOWN, NULL); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 409ea0ac4032..13e1c9047079 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -19,6 +19,7 @@ #include "qla_def.h" #include <linux/delay.h> +#include <scsi/scsi_transport_fc.h> static void qla2x00_mbx_sem_timeout(unsigned long data) @@ -251,7 +252,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) mb0 = RD_REG_WORD(®->isp24.mailbox0); ictrl = RD_REG_DWORD(®->isp24.ictrl); } else { - mb0 = RD_MAILBOX_REG(ha, reg->isp, 0); + mb0 = RD_MAILBOX_REG(ha, ®->isp, 0); ictrl = RD_REG_WORD(®->isp.ictrl); } printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n", @@ -983,58 +984,6 @@ qla2x00_abort_target(fc_port_t *fcport) #endif /* - * qla2x00_target_reset - * Issue target reset mailbox command. - * - * Input: - * ha = adapter block pointer. - * TARGET_QUEUE_LOCK must be released. - * ADAPTER_STATE_LOCK must be released. - * - * Returns: - * qla2x00 local function return status code. - * - * Context: - * Kernel context. - */ -int -qla2x00_target_reset(scsi_qla_host_t *ha, struct fc_port *fcport) -{ - int rval; - mbx_cmd_t mc; - mbx_cmd_t *mcp = &mc; - - DEBUG11(printk("qla2x00_target_reset(%ld): entered.\n", ha->host_no);) - - if (atomic_read(&fcport->state) != FCS_ONLINE) - return 0; - - mcp->mb[0] = MBC_TARGET_RESET; - if (HAS_EXTENDED_IDS(ha)) - mcp->mb[1] = fcport->loop_id; - else - mcp->mb[1] = fcport->loop_id << 8; - mcp->mb[2] = ha->loop_reset_delay; - mcp->out_mb = MBX_2|MBX_1|MBX_0; - mcp->in_mb = MBX_0; - mcp->tov = 30; - mcp->flags = 0; - rval = qla2x00_mailbox_command(ha, mcp); - - if (rval != QLA_SUCCESS) { - /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_target_reset(%ld): failed=%x.\n", - ha->host_no, rval);) - } else { - /*EMPTY*/ - DEBUG11(printk("qla2x00_target_reset(%ld): done.\n", - ha->host_no);) - } - - return rval; -} - -/* * qla2x00_get_adapter_id * Get adapter ID and topology. * @@ -1326,6 +1275,10 @@ qla2x00_get_port_database(scsi_qla_host_t *ha, fc_port_t *fcport, uint8_t opt) fcport->port_type = FCT_INITIATOR; else fcport->port_type = FCT_TARGET; + + /* Passback COS information. */ + fcport->supported_classes = (pd->options & BIT_4) ? + FC_COS_CLASS2: FC_COS_CLASS3; } gpd_error_out: @@ -1661,6 +1614,13 @@ qla24xx_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, mb[1] |= BIT_1; } else mb[1] = BIT_0; + + /* Passback COS information. */ + mb[10] = 0; + if (lg->io_parameter[7] || lg->io_parameter[8]) + mb[10] |= BIT_0; /* Class 2. */ + if (lg->io_parameter[9] || lg->io_parameter[10]) + mb[10] |= BIT_1; /* Class 3. */ } dma_pool_free(ha->s_dma_pool, lg, lg_dma); @@ -1723,6 +1683,8 @@ qla2x00_login_fabric(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, mb[2] = mcp->mb[2]; mb[6] = mcp->mb[6]; mb[7] = mcp->mb[7]; + /* COS retrieved from Get-Port-Database mailbox command. */ + mb[10] = 0; } if (rval != QLA_SUCCESS) { @@ -2465,3 +2427,32 @@ qla2x00_set_serdes_params(scsi_qla_host_t *ha, uint16_t sw_em_1g, return rval; } + +int +qla2x00_stop_firmware(scsi_qla_host_t *ha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA24XX(ha) && !IS_QLA25XX(ha)) + return QLA_FUNCTION_FAILED; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_STOP_FIRMWARE; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = 5; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + 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 9000659bfbcf..7aec93f9d423 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -79,7 +79,7 @@ module_param(ql2xloginretrycount, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xloginretrycount, "Specify an alternate value for the NVRAM login retry count."); -int ql2xfwloadbin; +int ql2xfwloadbin=1; module_param(ql2xfwloadbin, int, S_IRUGO|S_IRUSR); MODULE_PARM_DESC(ql2xfwloadbin, "Load ISP2xxx firmware image via hotplug."); @@ -88,6 +88,12 @@ static void qla2x00_free_device(scsi_qla_host_t *); static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha); +int ql2xfdmienable; +module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xfdmienable, + "Enables FDMI registratons " + "Default is 0 - no FDMI. 1 - perfom FDMI."); + /* * SCSI host template entry points */ @@ -105,6 +111,9 @@ static int qla2xxx_eh_host_reset(struct scsi_cmnd *); static int qla2x00_loop_reset(scsi_qla_host_t *ha); static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *); +static int qla2x00_change_queue_depth(struct scsi_device *, int); +static int qla2x00_change_queue_type(struct scsi_device *, int); + static struct scsi_host_template qla2x00_driver_template = { .module = THIS_MODULE, .name = "qla2xxx", @@ -119,6 +128,8 @@ static struct scsi_host_template qla2x00_driver_template = { .slave_alloc = qla2xxx_slave_alloc, .slave_destroy = qla2xxx_slave_destroy, + .change_queue_depth = qla2x00_change_queue_depth, + .change_queue_type = qla2x00_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, @@ -129,6 +140,7 @@ static struct scsi_host_template qla2x00_driver_template = { * which equates to 0x800000 sectors. */ .max_sectors = 0xFFFF, + .shost_attrs = qla2x00_host_attrs, }; static struct scsi_host_template qla24xx_driver_template = { @@ -145,12 +157,15 @@ static struct scsi_host_template qla24xx_driver_template = { .slave_alloc = qla2xxx_slave_alloc, .slave_destroy = qla2xxx_slave_destroy, + .change_queue_depth = qla2x00_change_queue_depth, + .change_queue_type = qla2x00_change_queue_type, .this_id = -1, .cmd_per_lun = 3, .use_clustering = ENABLE_CLUSTERING, .sg_tablesize = SG_ALL, .max_sectors = 0xFFFF, + .shost_attrs = qla2x00_host_attrs, }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -487,14 +502,13 @@ qc24_fail_command: static int qla2x00_eh_wait_on_command(scsi_qla_host_t *ha, struct scsi_cmnd *cmd) { -#define ABORT_POLLING_PERIOD HZ -#define ABORT_WAIT_ITER ((10 * HZ) / (ABORT_POLLING_PERIOD)) +#define ABORT_POLLING_PERIOD 1000 +#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD)) unsigned long wait_iter = ABORT_WAIT_ITER; int ret = QLA_SUCCESS; while (CMD_SP(cmd)) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(ABORT_POLLING_PERIOD); + msleep(ABORT_POLLING_PERIOD); if (--wait_iter) break; @@ -1016,7 +1030,7 @@ qla2x00_loop_reset(scsi_qla_host_t *ha) if (fcport->port_type != FCT_TARGET) continue; - status = qla2x00_target_reset(ha, fcport); + status = qla2x00_device_reset(ha, fcport); if (status != QLA_SUCCESS) break; } @@ -1103,6 +1117,28 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) sdev->hostdata = NULL; } +static int +qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth) +{ + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); + return sdev->queue_depth; +} + +static int +qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) +{ + if (sdev->tagged_supported) { + scsi_set_tag_type(sdev, tag_type); + if (tag_type) + scsi_activate_tcq(sdev, sdev->queue_depth); + else + scsi_deactivate_tcq(sdev, sdev->queue_depth); + } else + tag_type = 0; + + return tag_type; +} + /** * qla2x00_config_dma_addressing() - Configure OS DMA addressing method. * @ha: HA context @@ -1113,36 +1149,23 @@ qla2xxx_slave_destroy(struct scsi_device *sdev) static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha) { - /* Assume 32bit DMA address */ + /* Assume a 32bit DMA mask. */ ha->flags.enable_64bit_addressing = 0; - /* - * Given the two variants pci_set_dma_mask(), allow the compiler to - * assist in setting the proper dma mask. - */ - if (sizeof(dma_addr_t) > 4) { - if (pci_set_dma_mask(ha->pdev, DMA_64BIT_MASK) == 0) { + if (!dma_set_mask(&ha->pdev->dev, DMA_64BIT_MASK)) { + /* Any upper-dword bits set? */ + if (MSD(dma_get_required_mask(&ha->pdev->dev)) && + !pci_set_consistent_dma_mask(ha->pdev, DMA_64BIT_MASK)) { + /* Ok, a 64bit DMA mask is applicable. */ ha->flags.enable_64bit_addressing = 1; ha->isp_ops.calc_req_entries = qla2x00_calc_iocbs_64; ha->isp_ops.build_iocbs = qla2x00_build_scsi_iocbs_64; - - if (pci_set_consistent_dma_mask(ha->pdev, - DMA_64BIT_MASK)) { - qla_printk(KERN_DEBUG, ha, - "Failed to set 64 bit PCI consistent mask; " - "using 32 bit.\n"); - pci_set_consistent_dma_mask(ha->pdev, - DMA_32BIT_MASK); - } - } else { - qla_printk(KERN_DEBUG, ha, - "Failed to set 64 bit PCI DMA mask, falling back " - "to 32 bit MASK.\n"); - pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK); + return; } - } else { - pci_set_dma_mask(ha->pdev, DMA_32BIT_MASK); } + + dma_set_mask(&ha->pdev->dev, DMA_32BIT_MASK); + pci_set_consistent_dma_mask(ha->pdev, DMA_32BIT_MASK); } static int @@ -1302,6 +1325,8 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) ha->brd_info = brd_info; sprintf(ha->host_str, "%s_%ld", ha->brd_info->drv_name, ha->host_no); + ha->dpc_pid = -1; + /* Configure PCI I/O space */ ret = qla2x00_iospace_config(ha); if (ret) @@ -1316,6 +1341,7 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) ha->prev_topology = 0; ha->ports = MAX_BUSES; ha->init_cb_size = sizeof(init_cb_t); + ha->mgmt_svr_loop_id = MANAGEMENT_SERVER; /* Assign ISP specific operations. */ ha->isp_ops.pci_config = qla2100_pci_config; @@ -1338,6 +1364,7 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) ha->isp_ops.calc_req_entries = qla2x00_calc_iocbs_32; ha->isp_ops.build_iocbs = qla2x00_build_scsi_iocbs_32; ha->isp_ops.prep_ms_iocb = qla2x00_prep_ms_iocb; + ha->isp_ops.prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb; ha->isp_ops.read_nvram = qla2x00_read_nvram_data; ha->isp_ops.write_nvram = qla2x00_write_nvram_data; ha->isp_ops.fw_dump = qla2100_fw_dump; @@ -1375,6 +1402,7 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) ha->response_q_length = RESPONSE_ENTRY_CNT_2300; ha->last_loop_id = SNS_LAST_LOOP_ID_2300; ha->init_cb_size = sizeof(struct init_cb_24xx); + ha->mgmt_svr_loop_id = 10; ha->isp_ops.pci_config = qla24xx_pci_config; ha->isp_ops.reset_chip = qla24xx_reset_chip; ha->isp_ops.chip_diag = qla24xx_chip_diag; @@ -1395,6 +1423,7 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) ha->isp_ops.fabric_login = qla24xx_login_fabric; ha->isp_ops.fabric_logout = qla24xx_fabric_logout; ha->isp_ops.prep_ms_iocb = qla24xx_prep_ms_iocb; + ha->isp_ops.prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb; ha->isp_ops.read_nvram = qla24xx_read_nvram_data; ha->isp_ops.write_nvram = qla24xx_write_nvram_data; ha->isp_ops.fw_dump = qla24xx_fw_dump; @@ -1421,7 +1450,6 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) */ spin_lock_init(&ha->mbx_reg_lock); - ha->dpc_pid = -1; init_completion(&ha->dpc_inited); init_completion(&ha->dpc_exited); @@ -1558,8 +1586,6 @@ int qla2x00_probe_one(struct pci_dev *pdev, struct qla_board_info *brd_info) return 0; probe_failed: - fc_remove_host(ha->host); - qla2x00_free_device(ha); scsi_host_put(host); @@ -1601,10 +1627,6 @@ qla2x00_free_device(scsi_qla_host_t *ha) if (!IS_QLA2100(ha) && !IS_QLA2200(ha)) qla2x00_cancel_io_descriptors(ha); - /* turn-off interrupts on the card */ - if (ha->interrupts_on) - ha->isp_ops.disable_intrs(ha); - /* Disable timer */ if (ha->timer_active) qla2x00_stop_timer(ha); @@ -1624,8 +1646,14 @@ qla2x00_free_device(scsi_qla_host_t *ha) } } - qla2x00_mem_free(ha); + /* Stop currently executing firmware. */ + qla2x00_stop_firmware(ha); + + /* turn-off interrupts on the card */ + if (ha->interrupts_on) + ha->isp_ops.disable_intrs(ha); + qla2x00_mem_free(ha); ha->flags.online = 0; @@ -1934,7 +1962,7 @@ qla2x00_mem_free(scsi_qla_host_t *ha) { struct list_head *fcpl, *fcptemp; fc_port_t *fcport; - unsigned long wtime;/* max wait time if mbx cmd is busy. */ + unsigned int wtime;/* max wait time if mbx cmd is busy. */ if (ha == NULL) { /* error */ @@ -1943,11 +1971,9 @@ qla2x00_mem_free(scsi_qla_host_t *ha) } /* Make sure all other threads are stopped. */ - wtime = 60 * HZ; - while (ha->dpc_wait && wtime) { - set_current_state(TASK_INTERRUPTIBLE); - wtime = schedule_timeout(wtime); - } + wtime = 60 * 1000; + while (ha->dpc_wait && wtime) + wtime = msleep_interruptible(wtime); /* free ioctl memory */ qla2x00_free_ioctl_mem(ha); @@ -2478,15 +2504,15 @@ qla2x00_timer(scsi_qla_host_t *ha) int qla2x00_down_timeout(struct semaphore *sema, unsigned long timeout) { - const unsigned int step = HZ/10; + const unsigned int step = 100; /* msecs */ + unsigned int iterations = jiffies_to_msecs(timeout)/100; do { if (!down_trylock(sema)) return 0; - set_current_state(TASK_INTERRUPTIBLE); - if (schedule_timeout(step)) + if (msleep_interruptible(step)) break; - } while ((timeout -= step) > 0); + } while (--iterations >= 0); return -ETIMEDOUT; } diff --git a/drivers/scsi/qla2xxx/qla_rscn.c b/drivers/scsi/qla2xxx/qla_rscn.c index bdc3bc74bbe1..7534efcc8918 100644 --- a/drivers/scsi/qla2xxx/qla_rscn.c +++ b/drivers/scsi/qla2xxx/qla_rscn.c @@ -330,6 +330,8 @@ qla2x00_update_login_fcport(scsi_qla_host_t *ha, struct mbx_entry *mbxstat, fcport->flags &= ~FCF_FAILOVER_NEEDED; fcport->iodesc_idx_sent = IODESC_INVALID_INDEX; atomic_set(&fcport->state, FCS_ONLINE); + if (fcport->rport) + fc_remote_port_unblock(fcport->rport); } @@ -1064,7 +1066,7 @@ qla2x00_send_login_iocb_cb(scsi_qla_host_t *ha, struct io_descriptor *iodesc, * Returns a pointer to the allocated RSCN fcport, or NULL, if none available. */ fc_port_t * -qla2x00_alloc_rscn_fcport(scsi_qla_host_t *ha, int flags) +qla2x00_alloc_rscn_fcport(scsi_qla_host_t *ha, gfp_t flags) { fc_port_t *fcport; diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index d7f5c608009c..c14abf743b7c 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -468,21 +468,12 @@ qla24xx_read_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, uint32_t dwords) { uint32_t i; - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - - /* Pause RISC. */ - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ /* Dword reads to flash. */ for (i = 0; i < dwords; i++, faddr++) dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, flash_data_to_access_addr(faddr))); - /* Release RISC pause. */ - WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - return dwptr; } @@ -532,10 +523,6 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, ret = QLA_SUCCESS; - /* Pause RISC. */ - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); DEBUG9(printk("%s(%ld): Flash man_id=%d flash_id=%d\n", __func__, ha->host_no, man_id, flash_id)); @@ -599,10 +586,6 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ - /* Release RISC pause. */ - WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - return ret; } @@ -630,11 +613,6 @@ qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, { uint32_t i; uint32_t *dwptr; - struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - - /* Pause RISC. */ - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ /* Dword reads to flash. */ dwptr = (uint32_t *)buf; @@ -642,10 +620,6 @@ qla24xx_read_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha, nvram_data_to_access_addr(naddr))); - /* Release RISC pause. */ - WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - return buf; } @@ -690,10 +664,6 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, ret = QLA_SUCCESS; - /* Pause RISC. */ - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - /* Enable flash write. */ WRT_REG_DWORD(®->ctrl_status, RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); @@ -728,9 +698,5 @@ qla24xx_write_nvram_data(scsi_qla_host_t *ha, uint8_t *buf, uint32_t naddr, RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ - /* Release RISC pause. */ - WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); - RD_REG_DWORD(®->hccr); /* PCI Posting. */ - return ret; } diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index e3cd3618bc54..eae7d6edd531 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -19,9 +19,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.01.00b5-k" +#define QLA2XXX_VERSION "8.01.00-k" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 1 #define QLA_DRIVER_PATCH_VER 0 -#define QLA_DRIVER_BETA_VER 5 +#define QLA_DRIVER_BETA_VER 0 diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index a917ab7475ac..1fd5fc6d0fe3 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1119,6 +1119,36 @@ static inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int host->sg_tablesize = QLOGICPTI_MAX_SG(num_free); } +static unsigned int scsi_rbuf_get(struct scsi_cmnd *cmd, unsigned char **buf_out) +{ + unsigned char *buf; + unsigned int buflen; + + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset; + buflen = sg->length; + } else { + buf = cmd->request_buffer; + buflen = cmd->request_bufflen; + } + + *buf_out = buf; + return buflen; +} + +static void scsi_rbuf_put(struct scsi_cmnd *cmd, unsigned char *buf) +{ + if (cmd->use_sg) { + struct scatterlist *sg; + + sg = (struct scatterlist *) cmd->request_buffer; + kunmap_atomic(buf - sg->offset, KM_IRQ0); + } +} + /* * Until we scan the entire bus with inquiries, go throught this fella... */ @@ -1145,11 +1175,9 @@ static void ourdone(struct scsi_cmnd *Cmnd) int ok = host_byte(Cmnd->result) == DID_OK; if (Cmnd->cmnd[0] == 0x12 && ok) { unsigned char *iqd; + unsigned int iqd_len; - if (Cmnd->use_sg != 0) - BUG(); - - iqd = ((unsigned char *)Cmnd->buffer); + iqd_len = scsi_rbuf_get(Cmnd, &iqd); /* tags handled in midlayer */ /* enable sync mode? */ @@ -1163,6 +1191,9 @@ static void ourdone(struct scsi_cmnd *Cmnd) if (iqd[7] & 0x20) { qpti->dev_param[tgt].device_flags |= 0x20; } + + scsi_rbuf_put(Cmnd, iqd); + qpti->sbits |= (1 << tgt); } else if (!ok) { qpti->sbits |= (1 << tgt); diff --git a/drivers/scsi/raid_class.c b/drivers/scsi/raid_class.c new file mode 100644 index 000000000000..f1ea5027865f --- /dev/null +++ b/drivers/scsi/raid_class.c @@ -0,0 +1,250 @@ +/* + * RAID Attributes + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/raid_class.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> + +#define RAID_NUM_ATTRS 3 + +struct raid_internal { + struct raid_template r; + struct raid_function_template *f; + /* The actual attributes */ + struct class_device_attribute private_attrs[RAID_NUM_ATTRS]; + /* The array of null terminated pointers to attributes + * needed by scsi_sysfs.c */ + struct class_device_attribute *attrs[RAID_NUM_ATTRS + 1]; +}; + +struct raid_component { + struct list_head node; + struct device *dev; + int num; +}; + +#define to_raid_internal(tmpl) container_of(tmpl, struct raid_internal, r) + +#define tc_to_raid_internal(tcont) ({ \ + struct raid_template *r = \ + container_of(tcont, struct raid_template, raid_attrs); \ + to_raid_internal(r); \ +}) + +#define ac_to_raid_internal(acont) ({ \ + struct transport_container *tc = \ + container_of(acont, struct transport_container, ac); \ + tc_to_raid_internal(tc); \ +}) + +#define class_device_to_raid_internal(cdev) ({ \ + struct attribute_container *ac = \ + attribute_container_classdev_to_container(cdev); \ + ac_to_raid_internal(ac); \ +}) + + +static int raid_match(struct attribute_container *cont, struct device *dev) +{ + /* We have to look for every subsystem that could house + * emulated RAID devices, so start with SCSI */ + struct raid_internal *i = ac_to_raid_internal(cont); + + if (scsi_is_sdev_device(dev)) { + struct scsi_device *sdev = to_scsi_device(dev); + + if (i->f->cookie != sdev->host->hostt) + return 0; + + return i->f->is_raid(dev); + } + /* FIXME: look at other subsystems too */ + return 0; +} + +static int raid_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) +{ + struct raid_data *rd; + + BUG_ON(class_get_devdata(cdev)); + + rd = kmalloc(sizeof(*rd), GFP_KERNEL); + if (!rd) + return -ENOMEM; + + memset(rd, 0, sizeof(*rd)); + INIT_LIST_HEAD(&rd->component_list); + class_set_devdata(cdev, rd); + + return 0; +} + +static int raid_remove(struct transport_container *tc, struct device *dev, + struct class_device *cdev) +{ + struct raid_data *rd = class_get_devdata(cdev); + struct raid_component *rc, *next; + class_set_devdata(cdev, NULL); + list_for_each_entry_safe(rc, next, &rd->component_list, node) { + char buf[40]; + snprintf(buf, sizeof(buf), "component-%d", rc->num); + list_del(&rc->node); + sysfs_remove_link(&cdev->kobj, buf); + kfree(rc); + } + kfree(class_get_devdata(cdev)); + return 0; +} + +static DECLARE_TRANSPORT_CLASS(raid_class, + "raid_devices", + raid_setup, + raid_remove, + NULL); + +static struct { + enum raid_state value; + char *name; +} raid_states[] = { + { RAID_ACTIVE, "active" }, + { RAID_DEGRADED, "degraded" }, + { RAID_RESYNCING, "resyncing" }, + { RAID_OFFLINE, "offline" }, +}; + +static const char *raid_state_name(enum raid_state state) +{ + int i; + char *name = NULL; + + for (i = 0; i < sizeof(raid_states)/sizeof(raid_states[0]); i++) { + if (raid_states[i].value == state) { + name = raid_states[i].name; + break; + } + } + return name; +} + + +#define raid_attr_show_internal(attr, fmt, var, code) \ +static ssize_t raid_show_##attr(struct class_device *cdev, char *buf) \ +{ \ + struct raid_data *rd = class_get_devdata(cdev); \ + code \ + return snprintf(buf, 20, #fmt "\n", var); \ +} + +#define raid_attr_ro_states(attr, states, code) \ +raid_attr_show_internal(attr, %s, name, \ + const char *name; \ + code \ + name = raid_##states##_name(rd->attr); \ +) \ +static CLASS_DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL) + + +#define raid_attr_ro_internal(attr, code) \ +raid_attr_show_internal(attr, %d, rd->attr, code) \ +static CLASS_DEVICE_ATTR(attr, S_IRUGO, raid_show_##attr, NULL) + +#define ATTR_CODE(attr) \ + struct raid_internal *i = class_device_to_raid_internal(cdev); \ + if (i->f->get_##attr) \ + i->f->get_##attr(cdev->dev); + +#define raid_attr_ro(attr) raid_attr_ro_internal(attr, ) +#define raid_attr_ro_fn(attr) raid_attr_ro_internal(attr, ATTR_CODE(attr)) +#define raid_attr_ro_state(attr) raid_attr_ro_states(attr, attr, ATTR_CODE(attr)) + +raid_attr_ro(level); +raid_attr_ro_fn(resync); +raid_attr_ro_state(state); + +void 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); + char buf[40]; + + rc = kmalloc(sizeof(*rc), GFP_KERNEL); + if (!rc) + return; + + INIT_LIST_HEAD(&rc->node); + rc->dev = component_dev; + rc->num = rd->component_count++; + + snprintf(buf, sizeof(buf), "component-%d", rc->num); + list_add_tail(&rc->node, &rd->component_list); + sysfs_create_link(&cdev->kobj, &component_dev->kobj, buf); +} +EXPORT_SYMBOL(raid_component_add); + +struct raid_template * +raid_class_attach(struct raid_function_template *ft) +{ + struct raid_internal *i = kmalloc(sizeof(struct raid_internal), + GFP_KERNEL); + int count = 0; + + if (unlikely(!i)) + return NULL; + + memset(i, 0, sizeof(*i)); + + i->f = ft; + + i->r.raid_attrs.ac.class = &raid_class.class; + i->r.raid_attrs.ac.match = raid_match; + i->r.raid_attrs.ac.attrs = &i->attrs[0]; + + attribute_container_register(&i->r.raid_attrs.ac); + + i->attrs[count++] = &class_device_attr_level; + i->attrs[count++] = &class_device_attr_resync; + i->attrs[count++] = &class_device_attr_state; + + i->attrs[count] = NULL; + BUG_ON(count > RAID_NUM_ATTRS); + + return &i->r; +} +EXPORT_SYMBOL(raid_class_attach); + +void +raid_class_release(struct raid_template *r) +{ + struct raid_internal *i = to_raid_internal(r); + + attribute_container_unregister(&i->r.raid_attrs.ac); + + kfree(i); +} +EXPORT_SYMBOL(raid_class_release); + +static __init int raid_init(void) +{ + return transport_class_register(&raid_class); +} + +static __exit void raid_exit(void) +{ + transport_class_unregister(&raid_class); +} + +MODULE_AUTHOR("James Bottomley"); +MODULE_DESCRIPTION("RAID device class"); +MODULE_LICENSE("GPL"); + +module_init(raid_init); +module_exit(raid_exit); + diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c new file mode 100644 index 000000000000..46dbdee79f77 --- /dev/null +++ b/drivers/scsi/sata_mv.c @@ -0,0 +1,1575 @@ +/* + * sata_mv.c - Marvell SATA support + * + * Copyright 2005: EMC Corporation, all rights reserved. + * + * Please ALWAYS copy linux-ide@vger.kernel.org on emails. + * + * 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 + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include "scsi.h" +#include <scsi/scsi_host.h> +#include <linux/libata.h> +#include <asm/io.h> + +#define DRV_NAME "sata_mv" +#define DRV_VERSION "0.25" + +enum { + /* BAR's are enumerated in terms of pci_resource_start() terms */ + MV_PRIMARY_BAR = 0, /* offset 0x10: memory space */ + MV_IO_BAR = 2, /* offset 0x18: IO space */ + MV_MISC_BAR = 3, /* offset 0x1c: FLASH, NVRAM, SRAM */ + + MV_MAJOR_REG_AREA_SZ = 0x10000, /* 64KB */ + MV_MINOR_REG_AREA_SZ = 0x2000, /* 8KB */ + + MV_PCI_REG_BASE = 0, + MV_IRQ_COAL_REG_BASE = 0x18000, /* 6xxx part only */ + MV_SATAHC0_REG_BASE = 0x20000, + + MV_PCI_REG_SZ = MV_MAJOR_REG_AREA_SZ, + MV_SATAHC_REG_SZ = MV_MAJOR_REG_AREA_SZ, + MV_SATAHC_ARBTR_REG_SZ = MV_MINOR_REG_AREA_SZ, /* arbiter */ + MV_PORT_REG_SZ = MV_MINOR_REG_AREA_SZ, + + MV_USE_Q_DEPTH = ATA_DEF_QUEUE, + + MV_MAX_Q_DEPTH = 32, + MV_MAX_Q_DEPTH_MASK = MV_MAX_Q_DEPTH - 1, + + /* CRQB needs alignment on a 1KB boundary. Size == 1KB + * CRPB needs alignment on a 256B boundary. Size == 256B + * SG count of 176 leads to MV_PORT_PRIV_DMA_SZ == 4KB + * ePRD (SG) entries need alignment on a 16B boundary. Size == 16B + */ + MV_CRQB_Q_SZ = (32 * MV_MAX_Q_DEPTH), + MV_CRPB_Q_SZ = (8 * MV_MAX_Q_DEPTH), + MV_MAX_SG_CT = 176, + MV_SG_TBL_SZ = (16 * MV_MAX_SG_CT), + MV_PORT_PRIV_DMA_SZ = (MV_CRQB_Q_SZ + MV_CRPB_Q_SZ + MV_SG_TBL_SZ), + + /* Our DMA boundary is determined by an ePRD being unable to handle + * anything larger than 64KB + */ + MV_DMA_BOUNDARY = 0xffffU, + + MV_PORTS_PER_HC = 4, + /* == (port / MV_PORTS_PER_HC) to determine HC from 0-7 port */ + MV_PORT_HC_SHIFT = 2, + /* == (port % MV_PORTS_PER_HC) to determine hard port from 0-7 port */ + MV_PORT_MASK = 3, + + /* Host Flags */ + MV_FLAG_DUAL_HC = (1 << 30), /* two SATA Host Controllers */ + MV_FLAG_IRQ_COALESCE = (1 << 29), /* IRQ coalescing capability */ + MV_FLAG_GLBL_SFT_RST = (1 << 28), /* Global Soft Reset support */ + MV_COMMON_FLAGS = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO), + MV_6XXX_FLAGS = (MV_FLAG_IRQ_COALESCE | + MV_FLAG_GLBL_SFT_RST), + + chip_504x = 0, + chip_508x = 1, + chip_604x = 2, + chip_608x = 3, + + CRQB_FLAG_READ = (1 << 0), + CRQB_TAG_SHIFT = 1, + CRQB_CMD_ADDR_SHIFT = 8, + CRQB_CMD_CS = (0x2 << 11), + CRQB_CMD_LAST = (1 << 15), + + CRPB_FLAG_STATUS_SHIFT = 8, + + EPRD_FLAG_END_OF_TBL = (1 << 31), + + /* PCI interface registers */ + + PCI_COMMAND_OFS = 0xc00, + + PCI_MAIN_CMD_STS_OFS = 0xd30, + STOP_PCI_MASTER = (1 << 2), + PCI_MASTER_EMPTY = (1 << 3), + GLOB_SFT_RST = (1 << 4), + + PCI_IRQ_CAUSE_OFS = 0x1d58, + PCI_IRQ_MASK_OFS = 0x1d5c, + PCI_UNMASK_ALL_IRQS = 0x7fffff, /* bits 22-0 */ + + HC_MAIN_IRQ_CAUSE_OFS = 0x1d60, + HC_MAIN_IRQ_MASK_OFS = 0x1d64, + PORT0_ERR = (1 << 0), /* shift by port # */ + PORT0_DONE = (1 << 1), /* shift by port # */ + HC0_IRQ_PEND = 0x1ff, /* bits 0-8 = HC0's ports */ + HC_SHIFT = 9, /* bits 9-17 = HC1's ports */ + PCI_ERR = (1 << 18), + TRAN_LO_DONE = (1 << 19), /* 6xxx: IRQ coalescing */ + TRAN_HI_DONE = (1 << 20), /* 6xxx: IRQ coalescing */ + PORTS_0_7_COAL_DONE = (1 << 21), /* 6xxx: IRQ coalescing */ + GPIO_INT = (1 << 22), + SELF_INT = (1 << 23), + TWSI_INT = (1 << 24), + HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */ + HC_MAIN_MASKED_IRQS = (TRAN_LO_DONE | TRAN_HI_DONE | + PORTS_0_7_COAL_DONE | GPIO_INT | TWSI_INT | + HC_MAIN_RSVD), + + /* SATAHC registers */ + HC_CFG_OFS = 0, + + HC_IRQ_CAUSE_OFS = 0x14, + CRPB_DMA_DONE = (1 << 0), /* shift by port # */ + HC_IRQ_COAL = (1 << 4), /* IRQ coalescing */ + DEV_IRQ = (1 << 8), /* shift by port # */ + + /* Shadow block registers */ + SHD_BLK_OFS = 0x100, + SHD_CTL_AST_OFS = 0x20, /* ofs from SHD_BLK_OFS */ + + /* SATA registers */ + SATA_STATUS_OFS = 0x300, /* ctrl, err regs follow status */ + SATA_ACTIVE_OFS = 0x350, + + /* Port registers */ + EDMA_CFG_OFS = 0, + EDMA_CFG_Q_DEPTH = 0, /* queueing disabled */ + EDMA_CFG_NCQ = (1 << 5), + EDMA_CFG_NCQ_GO_ON_ERR = (1 << 14), /* continue on error */ + EDMA_CFG_RD_BRST_EXT = (1 << 11), /* read burst 512B */ + EDMA_CFG_WR_BUFF_LEN = (1 << 13), /* write buffer 512B */ + + EDMA_ERR_IRQ_CAUSE_OFS = 0x8, + EDMA_ERR_IRQ_MASK_OFS = 0xc, + EDMA_ERR_D_PAR = (1 << 0), + EDMA_ERR_PRD_PAR = (1 << 1), + EDMA_ERR_DEV = (1 << 2), + EDMA_ERR_DEV_DCON = (1 << 3), + EDMA_ERR_DEV_CON = (1 << 4), + EDMA_ERR_SERR = (1 << 5), + EDMA_ERR_SELF_DIS = (1 << 7), + EDMA_ERR_BIST_ASYNC = (1 << 8), + EDMA_ERR_CRBQ_PAR = (1 << 9), + EDMA_ERR_CRPB_PAR = (1 << 10), + EDMA_ERR_INTRL_PAR = (1 << 11), + EDMA_ERR_IORDY = (1 << 12), + EDMA_ERR_LNK_CTRL_RX = (0xf << 13), + EDMA_ERR_LNK_CTRL_RX_2 = (1 << 15), + EDMA_ERR_LNK_DATA_RX = (0xf << 17), + EDMA_ERR_LNK_CTRL_TX = (0x1f << 21), + EDMA_ERR_LNK_DATA_TX = (0x1f << 26), + EDMA_ERR_TRANS_PROTO = (1 << 31), + EDMA_ERR_FATAL = (EDMA_ERR_D_PAR | EDMA_ERR_PRD_PAR | + EDMA_ERR_DEV_DCON | EDMA_ERR_CRBQ_PAR | + EDMA_ERR_CRPB_PAR | EDMA_ERR_INTRL_PAR | + EDMA_ERR_IORDY | EDMA_ERR_LNK_CTRL_RX_2 | + EDMA_ERR_LNK_DATA_RX | + EDMA_ERR_LNK_DATA_TX | + EDMA_ERR_TRANS_PROTO), + + EDMA_REQ_Q_BASE_HI_OFS = 0x10, + EDMA_REQ_Q_IN_PTR_OFS = 0x14, /* also contains BASE_LO */ + EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U, + + EDMA_REQ_Q_OUT_PTR_OFS = 0x18, + EDMA_REQ_Q_PTR_SHIFT = 5, + + EDMA_RSP_Q_BASE_HI_OFS = 0x1c, + EDMA_RSP_Q_IN_PTR_OFS = 0x20, + EDMA_RSP_Q_OUT_PTR_OFS = 0x24, /* also contains BASE_LO */ + EDMA_RSP_Q_BASE_LO_MASK = 0xffffff00U, + EDMA_RSP_Q_PTR_SHIFT = 3, + + EDMA_CMD_OFS = 0x28, + EDMA_EN = (1 << 0), + EDMA_DS = (1 << 1), + ATA_RST = (1 << 2), + + /* Host private flags (hp_flags) */ + MV_HP_FLAG_MSI = (1 << 0), + + /* Port private flags (pp_flags) */ + MV_PP_FLAG_EDMA_EN = (1 << 0), + MV_PP_FLAG_EDMA_DS_ACT = (1 << 1), +}; + +/* Command ReQuest Block: 32B */ +struct mv_crqb { + u32 sg_addr; + u32 sg_addr_hi; + u16 ctrl_flags; + u16 ata_cmd[11]; +}; + +/* Command ResPonse Block: 8B */ +struct mv_crpb { + u16 id; + u16 flags; + u32 tmstmp; +}; + +/* EDMA Physical Region Descriptor (ePRD); A.K.A. SG */ +struct mv_sg { + u32 addr; + u32 flags_size; + u32 addr_hi; + u32 reserved; +}; + +struct mv_port_priv { + struct mv_crqb *crqb; + dma_addr_t crqb_dma; + struct mv_crpb *crpb; + dma_addr_t crpb_dma; + struct mv_sg *sg_tbl; + dma_addr_t sg_tbl_dma; + + unsigned req_producer; /* cp of req_in_ptr */ + unsigned rsp_consumer; /* cp of rsp_out_ptr */ + u32 pp_flags; +}; + +struct mv_host_priv { + u32 hp_flags; +}; + +static void mv_irq_clear(struct ata_port *ap); +static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in); +static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val); +static void mv_phy_reset(struct ata_port *ap); +static void mv_host_stop(struct ata_host_set *host_set); +static int mv_port_start(struct ata_port *ap); +static void mv_port_stop(struct ata_port *ap); +static void mv_qc_prep(struct ata_queued_cmd *qc); +static int mv_qc_issue(struct ata_queued_cmd *qc); +static irqreturn_t mv_interrupt(int irq, void *dev_instance, + struct pt_regs *regs); +static void mv_eng_timeout(struct ata_port *ap); +static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); + +static Scsi_Host_Template mv_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = MV_USE_Q_DEPTH, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = MV_MAX_SG_CT, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = MV_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, + .ordered_flush = 1, +}; + +static const struct ata_port_operations mv_ops = { + .port_disable = ata_port_disable, + + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .phy_reset = mv_phy_reset, + + .qc_prep = mv_qc_prep, + .qc_issue = mv_qc_issue, + + .eng_timeout = mv_eng_timeout, + + .irq_handler = mv_interrupt, + .irq_clear = mv_irq_clear, + + .scr_read = mv_scr_read, + .scr_write = mv_scr_write, + + .port_start = mv_port_start, + .port_stop = mv_port_stop, + .host_stop = mv_host_stop, +}; + +static struct ata_port_info mv_port_info[] = { + { /* chip_504x */ + .sht = &mv_sht, + .host_flags = MV_COMMON_FLAGS, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0, /* 0x7f (udma0-6 disabled for now) */ + .port_ops = &mv_ops, + }, + { /* chip_508x */ + .sht = &mv_sht, + .host_flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0, /* 0x7f (udma0-6 disabled for now) */ + .port_ops = &mv_ops, + }, + { /* chip_604x */ + .sht = &mv_sht, + .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 */ + .port_ops = &mv_ops, + }, + { /* chip_608x */ + .sht = &mv_sht, + .host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS | + MV_FLAG_DUAL_HC), + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 */ + .port_ops = &mv_ops, + }, +}; + +static struct pci_device_id mv_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x}, + + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6080), 0, 0, chip_608x}, + {PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6081), 0, 0, chip_608x}, + {} /* terminate list */ +}; + +static struct pci_driver mv_pci_driver = { + .name = DRV_NAME, + .id_table = mv_pci_tbl, + .probe = mv_init_one, + .remove = ata_pci_remove_one, +}; + +/* + * Functions + */ + +static inline void writelfl(unsigned long data, void __iomem *addr) +{ + writel(data, addr); + (void) readl(addr); /* flush to avoid PCI posted write */ +} + +static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc) +{ + return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ)); +} + +static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port) +{ + return (mv_hc_base(base, port >> MV_PORT_HC_SHIFT) + + MV_SATAHC_ARBTR_REG_SZ + + ((port & MV_PORT_MASK) * MV_PORT_REG_SZ)); +} + +static inline void __iomem *mv_ap_base(struct ata_port *ap) +{ + return mv_port_base(ap->host_set->mmio_base, ap->port_no); +} + +static inline int mv_get_hc_count(unsigned long hp_flags) +{ + return ((hp_flags & MV_FLAG_DUAL_HC) ? 2 : 1); +} + +static void mv_irq_clear(struct ata_port *ap) +{ +} + +/** + * mv_start_dma - Enable eDMA engine + * @base: port base address + * @pp: port private data + * + * Verify the local cache of the eDMA state is accurate with an + * assert. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp) +{ + if (!(MV_PP_FLAG_EDMA_EN & pp->pp_flags)) { + writelfl(EDMA_EN, base + EDMA_CMD_OFS); + pp->pp_flags |= MV_PP_FLAG_EDMA_EN; + } + assert(EDMA_EN & readl(base + EDMA_CMD_OFS)); +} + +/** + * mv_stop_dma - Disable eDMA engine + * @ap: ATA channel to manipulate + * + * Verify the local cache of the eDMA state is accurate with an + * assert. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_stop_dma(struct ata_port *ap) +{ + void __iomem *port_mmio = mv_ap_base(ap); + struct mv_port_priv *pp = ap->private_data; + u32 reg; + int i; + + if (MV_PP_FLAG_EDMA_EN & pp->pp_flags) { + /* Disable EDMA if active. The disable bit auto clears. + */ + writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS); + pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; + } else { + assert(!(EDMA_EN & readl(port_mmio + EDMA_CMD_OFS))); + } + + /* now properly wait for the eDMA to stop */ + for (i = 1000; i > 0; i--) { + reg = readl(port_mmio + EDMA_CMD_OFS); + if (!(EDMA_EN & reg)) { + break; + } + udelay(100); + } + + if (EDMA_EN & reg) { + printk(KERN_ERR "ata%u: Unable to stop eDMA\n", ap->id); + /* FIXME: Consider doing a reset here to recover */ + } +} + +#ifdef ATA_DEBUG +static void mv_dump_mem(void __iomem *start, unsigned bytes) +{ + int b, w; + for (b = 0; b < bytes; ) { + DPRINTK("%p: ", start + b); + for (w = 0; b < bytes && w < 4; w++) { + printk("%08x ",readl(start + b)); + b += sizeof(u32); + } + printk("\n"); + } +} +#endif + +static void mv_dump_pci_cfg(struct pci_dev *pdev, unsigned bytes) +{ +#ifdef ATA_DEBUG + int b, w; + u32 dw; + for (b = 0; b < bytes; ) { + DPRINTK("%02x: ", b); + for (w = 0; b < bytes && w < 4; w++) { + (void) pci_read_config_dword(pdev,b,&dw); + printk("%08x ",dw); + b += sizeof(u32); + } + printk("\n"); + } +#endif +} +static void mv_dump_all_regs(void __iomem *mmio_base, int port, + struct pci_dev *pdev) +{ +#ifdef ATA_DEBUG + void __iomem *hc_base = mv_hc_base(mmio_base, + port >> MV_PORT_HC_SHIFT); + void __iomem *port_base; + int start_port, num_ports, p, start_hc, num_hcs, hc; + + if (0 > port) { + start_hc = start_port = 0; + num_ports = 8; /* shld be benign for 4 port devs */ + num_hcs = 2; + } else { + start_hc = port >> MV_PORT_HC_SHIFT; + start_port = port; + num_ports = num_hcs = 1; + } + DPRINTK("All registers for port(s) %u-%u:\n", start_port, + num_ports > 1 ? num_ports - 1 : start_port); + + if (NULL != pdev) { + DPRINTK("PCI config space regs:\n"); + mv_dump_pci_cfg(pdev, 0x68); + } + DPRINTK("PCI regs:\n"); + mv_dump_mem(mmio_base+0xc00, 0x3c); + mv_dump_mem(mmio_base+0xd00, 0x34); + mv_dump_mem(mmio_base+0xf00, 0x4); + mv_dump_mem(mmio_base+0x1d00, 0x6c); + for (hc = start_hc; hc < start_hc + num_hcs; hc++) { + hc_base = mv_hc_base(mmio_base, port >> MV_PORT_HC_SHIFT); + DPRINTK("HC regs (HC %i):\n", hc); + mv_dump_mem(hc_base, 0x1c); + } + for (p = start_port; p < start_port + num_ports; p++) { + port_base = mv_port_base(mmio_base, p); + DPRINTK("EDMA regs (port %i):\n",p); + mv_dump_mem(port_base, 0x54); + DPRINTK("SATA regs (port %i):\n",p); + mv_dump_mem(port_base+0x300, 0x60); + } +#endif +} + +static unsigned int mv_scr_offset(unsigned int sc_reg_in) +{ + unsigned int ofs; + + switch (sc_reg_in) { + case SCR_STATUS: + case SCR_CONTROL: + case SCR_ERROR: + ofs = SATA_STATUS_OFS + (sc_reg_in * sizeof(u32)); + break; + case SCR_ACTIVE: + ofs = SATA_ACTIVE_OFS; /* active is not with the others */ + break; + default: + ofs = 0xffffffffU; + break; + } + return ofs; +} + +static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +{ + unsigned int ofs = mv_scr_offset(sc_reg_in); + + if (0xffffffffU != ofs) { + return readl(mv_ap_base(ap) + ofs); + } else { + return (u32) ofs; + } +} + +static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) +{ + unsigned int ofs = mv_scr_offset(sc_reg_in); + + if (0xffffffffU != ofs) { + writelfl(val, mv_ap_base(ap) + ofs); + } +} + +/** + * mv_global_soft_reset - Perform the 6xxx global soft reset + * @mmio_base: base address of the HBA + * + * This routine only applies to 6xxx parts. + * + * LOCKING: + * Inherited from caller. + */ +static int mv_global_soft_reset(void __iomem *mmio_base) +{ + void __iomem *reg = mmio_base + PCI_MAIN_CMD_STS_OFS; + int i, rc = 0; + u32 t; + + /* Following procedure defined in PCI "main command and status + * register" table. + */ + t = readl(reg); + writel(t | STOP_PCI_MASTER, reg); + + for (i = 0; i < 1000; i++) { + udelay(1); + t = readl(reg); + if (PCI_MASTER_EMPTY & t) { + break; + } + } + if (!(PCI_MASTER_EMPTY & t)) { + printk(KERN_ERR DRV_NAME ": PCI master won't flush\n"); + rc = 1; + goto done; + } + + /* set reset */ + i = 5; + do { + writel(t | GLOB_SFT_RST, reg); + t = readl(reg); + udelay(1); + } while (!(GLOB_SFT_RST & t) && (i-- > 0)); + + if (!(GLOB_SFT_RST & t)) { + printk(KERN_ERR DRV_NAME ": can't set global reset\n"); + rc = 1; + goto done; + } + + /* clear reset and *reenable the PCI master* (not mentioned in spec) */ + i = 5; + do { + writel(t & ~(GLOB_SFT_RST | STOP_PCI_MASTER), reg); + t = readl(reg); + udelay(1); + } while ((GLOB_SFT_RST & t) && (i-- > 0)); + + if (GLOB_SFT_RST & t) { + printk(KERN_ERR DRV_NAME ": can't clear global reset\n"); + rc = 1; + } +done: + return rc; +} + +/** + * mv_host_stop - Host specific cleanup/stop routine. + * @host_set: host data structure + * + * Disable ints, cleanup host memory, call general purpose + * host_stop. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_host_stop(struct ata_host_set *host_set) +{ + struct mv_host_priv *hpriv = host_set->private_data; + struct pci_dev *pdev = to_pci_dev(host_set->dev); + + if (hpriv->hp_flags & MV_HP_FLAG_MSI) { + pci_disable_msi(pdev); + } else { + pci_intx(pdev, 0); + } + kfree(hpriv); + ata_host_stop(host_set); +} + +/** + * mv_port_start - Port specific init/start routine. + * @ap: ATA channel to manipulate + * + * Allocate and point to DMA memory, init port private memory, + * zero indices. + * + * LOCKING: + * Inherited from caller. + */ +static int mv_port_start(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct mv_port_priv *pp; + void __iomem *port_mmio = mv_ap_base(ap); + void *mem; + dma_addr_t mem_dma; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) { + return -ENOMEM; + } + memset(pp, 0, sizeof(*pp)); + + mem = dma_alloc_coherent(dev, MV_PORT_PRIV_DMA_SZ, &mem_dma, + GFP_KERNEL); + if (!mem) { + kfree(pp); + return -ENOMEM; + } + memset(mem, 0, MV_PORT_PRIV_DMA_SZ); + + /* First item in chunk of DMA memory: + * 32-slot command request table (CRQB), 32 bytes each in size + */ + pp->crqb = mem; + pp->crqb_dma = mem_dma; + mem += MV_CRQB_Q_SZ; + mem_dma += MV_CRQB_Q_SZ; + + /* Second item: + * 32-slot command response table (CRPB), 8 bytes each in size + */ + pp->crpb = mem; + pp->crpb_dma = mem_dma; + mem += MV_CRPB_Q_SZ; + mem_dma += MV_CRPB_Q_SZ; + + /* Third item: + * Table of scatter-gather descriptors (ePRD), 16 bytes each + */ + pp->sg_tbl = mem; + pp->sg_tbl_dma = mem_dma; + + writelfl(EDMA_CFG_Q_DEPTH | EDMA_CFG_RD_BRST_EXT | + EDMA_CFG_WR_BUFF_LEN, port_mmio + EDMA_CFG_OFS); + + writel((pp->crqb_dma >> 16) >> 16, port_mmio + EDMA_REQ_Q_BASE_HI_OFS); + writelfl(pp->crqb_dma & EDMA_REQ_Q_BASE_LO_MASK, + port_mmio + EDMA_REQ_Q_IN_PTR_OFS); + + writelfl(0, port_mmio + EDMA_REQ_Q_OUT_PTR_OFS); + writelfl(0, port_mmio + EDMA_RSP_Q_IN_PTR_OFS); + + writel((pp->crpb_dma >> 16) >> 16, port_mmio + EDMA_RSP_Q_BASE_HI_OFS); + writelfl(pp->crpb_dma & EDMA_RSP_Q_BASE_LO_MASK, + port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); + + pp->req_producer = pp->rsp_consumer = 0; + + /* Don't turn on EDMA here...do it before DMA commands only. Else + * we'll be unable to send non-data, PIO, etc due to restricted access + * to shadow regs. + */ + ap->private_data = pp; + return 0; +} + +/** + * mv_port_stop - Port specific cleanup/stop routine. + * @ap: ATA channel to manipulate + * + * Stop DMA, cleanup port memory. + * + * LOCKING: + * This routine uses the host_set lock to protect the DMA stop. + */ +static void mv_port_stop(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct mv_port_priv *pp = ap->private_data; + unsigned long flags; + + spin_lock_irqsave(&ap->host_set->lock, flags); + mv_stop_dma(ap); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + + ap->private_data = NULL; + dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma); + kfree(pp); +} + +/** + * mv_fill_sg - Fill out the Marvell ePRD (scatter gather) entries + * @qc: queued command whose SG list to source from + * + * Populate the SG list and mark the last entry. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_fill_sg(struct ata_queued_cmd *qc) +{ + struct mv_port_priv *pp = qc->ap->private_data; + unsigned int i; + + for (i = 0; i < qc->n_elem; i++) { + u32 sg_len; + dma_addr_t addr; + + addr = sg_dma_address(&qc->sg[i]); + sg_len = sg_dma_len(&qc->sg[i]); + + pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff); + pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); + assert(0 == (sg_len & ~MV_DMA_BOUNDARY)); + pp->sg_tbl[i].flags_size = cpu_to_le32(sg_len); + } + if (0 < qc->n_elem) { + pp->sg_tbl[qc->n_elem - 1].flags_size |= + cpu_to_le32(EPRD_FLAG_END_OF_TBL); + } +} + +static inline unsigned mv_inc_q_index(unsigned *index) +{ + *index = (*index + 1) & MV_MAX_Q_DEPTH_MASK; + return *index; +} + +static inline void mv_crqb_pack_cmd(u16 *cmdw, u8 data, u8 addr, unsigned last) +{ + *cmdw = data | (addr << CRQB_CMD_ADDR_SHIFT) | CRQB_CMD_CS | + (last ? CRQB_CMD_LAST : 0); +} + +/** + * mv_qc_prep - Host specific command preparation. + * @qc: queued command to prepare + * + * This routine simply redirects to the general purpose routine + * if command is not DMA. Else, it handles prep of the CRQB + * (command request block), does some sanity checking, and calls + * the SG load routine. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_qc_prep(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct mv_port_priv *pp = ap->private_data; + u16 *cw; + struct ata_taskfile *tf; + u16 flags = 0; + + if (ATA_PROT_DMA != qc->tf.protocol) { + return; + } + + /* the req producer index should be the same as we remember it */ + assert(((readl(mv_ap_base(qc->ap) + EDMA_REQ_Q_IN_PTR_OFS) >> + EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK) == + pp->req_producer); + + /* Fill in command request block + */ + if (!(qc->tf.flags & ATA_TFLAG_WRITE)) { + flags |= CRQB_FLAG_READ; + } + assert(MV_MAX_Q_DEPTH > qc->tag); + flags |= qc->tag << CRQB_TAG_SHIFT; + + pp->crqb[pp->req_producer].sg_addr = + cpu_to_le32(pp->sg_tbl_dma & 0xffffffff); + pp->crqb[pp->req_producer].sg_addr_hi = + cpu_to_le32((pp->sg_tbl_dma >> 16) >> 16); + pp->crqb[pp->req_producer].ctrl_flags = cpu_to_le16(flags); + + cw = &pp->crqb[pp->req_producer].ata_cmd[0]; + tf = &qc->tf; + + /* Sadly, the CRQB cannot accomodate all registers--there are + * only 11 bytes...so we must pick and choose required + * registers based on the command. So, we drop feature and + * hob_feature for [RW] DMA commands, but they are needed for + * NCQ. NCQ will drop hob_nsect. + */ + switch (tf->command) { + case ATA_CMD_READ: + case ATA_CMD_READ_EXT: + case ATA_CMD_WRITE: + case ATA_CMD_WRITE_EXT: + mv_crqb_pack_cmd(cw++, tf->hob_nsect, ATA_REG_NSECT, 0); + break; +#ifdef LIBATA_NCQ /* FIXME: remove this line when NCQ added */ + case ATA_CMD_FPDMA_READ: + case ATA_CMD_FPDMA_WRITE: + mv_crqb_pack_cmd(cw++, tf->hob_feature, ATA_REG_FEATURE, 0); + mv_crqb_pack_cmd(cw++, tf->feature, ATA_REG_FEATURE, 0); + break; +#endif /* FIXME: remove this line when NCQ added */ + default: + /* The only other commands EDMA supports in non-queued and + * non-NCQ mode are: [RW] STREAM DMA and W DMA FUA EXT, none + * of which are defined/used by Linux. If we get here, this + * driver needs work. + * + * FIXME: modify libata to give qc_prep a return value and + * return error here. + */ + BUG_ON(tf->command); + break; + } + mv_crqb_pack_cmd(cw++, tf->nsect, ATA_REG_NSECT, 0); + mv_crqb_pack_cmd(cw++, tf->hob_lbal, ATA_REG_LBAL, 0); + mv_crqb_pack_cmd(cw++, tf->lbal, ATA_REG_LBAL, 0); + mv_crqb_pack_cmd(cw++, tf->hob_lbam, ATA_REG_LBAM, 0); + mv_crqb_pack_cmd(cw++, tf->lbam, ATA_REG_LBAM, 0); + mv_crqb_pack_cmd(cw++, tf->hob_lbah, ATA_REG_LBAH, 0); + mv_crqb_pack_cmd(cw++, tf->lbah, ATA_REG_LBAH, 0); + mv_crqb_pack_cmd(cw++, tf->device, ATA_REG_DEVICE, 0); + mv_crqb_pack_cmd(cw++, tf->command, ATA_REG_CMD, 1); /* last */ + + if (!(qc->flags & ATA_QCFLAG_DMAMAP)) { + return; + } + mv_fill_sg(qc); +} + +/** + * mv_qc_issue - Initiate a command to the host + * @qc: queued command to start + * + * This routine simply redirects to the general purpose routine + * if command is not DMA. Else, it sanity checks our local + * caches of the request producer/consumer indices then enables + * DMA and bumps the request producer index. + * + * LOCKING: + * Inherited from caller. + */ +static int mv_qc_issue(struct ata_queued_cmd *qc) +{ + void __iomem *port_mmio = mv_ap_base(qc->ap); + struct mv_port_priv *pp = qc->ap->private_data; + u32 in_ptr; + + if (ATA_PROT_DMA != qc->tf.protocol) { + /* We're about to send a non-EDMA capable command to the + * port. Turn off EDMA so there won't be problems accessing + * shadow block, etc registers. + */ + mv_stop_dma(qc->ap); + return ata_qc_issue_prot(qc); + } + + in_ptr = readl(port_mmio + EDMA_REQ_Q_IN_PTR_OFS); + + /* the req producer index should be the same as we remember it */ + assert(((in_ptr >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK) == + pp->req_producer); + /* until we do queuing, the queue should be empty at this point */ + assert(((in_ptr >> EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK) == + ((readl(port_mmio + EDMA_REQ_Q_OUT_PTR_OFS) >> + EDMA_REQ_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK)); + + mv_inc_q_index(&pp->req_producer); /* now incr producer index */ + + mv_start_dma(port_mmio, pp); + + /* and write the request in pointer to kick the EDMA to life */ + in_ptr &= EDMA_REQ_Q_BASE_LO_MASK; + in_ptr |= pp->req_producer << EDMA_REQ_Q_PTR_SHIFT; + writelfl(in_ptr, port_mmio + EDMA_REQ_Q_IN_PTR_OFS); + + return 0; +} + +/** + * mv_get_crpb_status - get status from most recently completed cmd + * @ap: ATA channel to manipulate + * + * This routine is for use when the port is in DMA mode, when it + * will be using the CRPB (command response block) method of + * returning command completion information. We assert indices + * are good, grab status, and bump the response consumer index to + * prove that we're up to date. + * + * LOCKING: + * Inherited from caller. + */ +static u8 mv_get_crpb_status(struct ata_port *ap) +{ + void __iomem *port_mmio = mv_ap_base(ap); + struct mv_port_priv *pp = ap->private_data; + u32 out_ptr; + + out_ptr = readl(port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); + + /* the response consumer index should be the same as we remember it */ + assert(((out_ptr >> EDMA_RSP_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK) == + pp->rsp_consumer); + + /* increment our consumer index... */ + pp->rsp_consumer = mv_inc_q_index(&pp->rsp_consumer); + + /* and, until we do NCQ, there should only be 1 CRPB waiting */ + assert(((readl(port_mmio + EDMA_RSP_Q_IN_PTR_OFS) >> + EDMA_RSP_Q_PTR_SHIFT) & MV_MAX_Q_DEPTH_MASK) == + pp->rsp_consumer); + + /* write out our inc'd consumer index so EDMA knows we're caught up */ + out_ptr &= EDMA_RSP_Q_BASE_LO_MASK; + out_ptr |= pp->rsp_consumer << EDMA_RSP_Q_PTR_SHIFT; + writelfl(out_ptr, port_mmio + EDMA_RSP_Q_OUT_PTR_OFS); + + /* Return ATA status register for completed CRPB */ + return (pp->crpb[pp->rsp_consumer].flags >> CRPB_FLAG_STATUS_SHIFT); +} + +/** + * mv_err_intr - Handle error interrupts on the port + * @ap: ATA channel to manipulate + * + * In most cases, just clear the interrupt and move on. However, + * some cases require an eDMA reset, which is done right before + * the COMRESET in mv_phy_reset(). The SERR case requires a + * clear of pending errors in the SATA SERROR register. Finally, + * if the port disabled DMA, update our cached copy to match. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_err_intr(struct ata_port *ap) +{ + void __iomem *port_mmio = mv_ap_base(ap); + u32 edma_err_cause, serr = 0; + + edma_err_cause = readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); + + if (EDMA_ERR_SERR & edma_err_cause) { + serr = scr_read(ap, SCR_ERROR); + scr_write_flush(ap, SCR_ERROR, serr); + } + if (EDMA_ERR_SELF_DIS & edma_err_cause) { + struct mv_port_priv *pp = ap->private_data; + pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; + } + DPRINTK(KERN_ERR "ata%u: port error; EDMA err cause: 0x%08x " + "SERR: 0x%08x\n", ap->id, edma_err_cause, serr); + + /* Clear EDMA now that SERR cleanup done */ + writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); + + /* check for fatal here and recover if needed */ + if (EDMA_ERR_FATAL & edma_err_cause) { + mv_phy_reset(ap); + } +} + +/** + * mv_host_intr - Handle all interrupts on the given host controller + * @host_set: host specific structure + * @relevant: port error bits relevant to this host controller + * @hc: which host controller we're to look at + * + * Read then write clear the HC interrupt status then walk each + * port connected to the HC and see if it needs servicing. Port + * success ints are reported in the HC interrupt status reg, the + * port error ints are reported in the higher level main + * interrupt status register and thus are passed in via the + * 'relevant' argument. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, + unsigned int hc) +{ + void __iomem *mmio = host_set->mmio_base; + void __iomem *hc_mmio = mv_hc_base(mmio, hc); + struct ata_port *ap; + struct ata_queued_cmd *qc; + u32 hc_irq_cause; + int shift, port, port0, hard_port, handled; + unsigned int err_mask; + u8 ata_status = 0; + + if (hc == 0) { + port0 = 0; + } else { + port0 = MV_PORTS_PER_HC; + } + + /* we'll need the HC success int register in most cases */ + hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); + if (hc_irq_cause) { + writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); + } + + VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", + hc,relevant,hc_irq_cause); + + for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) { + ap = host_set->ports[port]; + hard_port = port & MV_PORT_MASK; /* range 0-3 */ + handled = 0; /* ensure ata_status is set if handled++ */ + + if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) { + /* new CRPB on the queue; just one at a time until NCQ + */ + ata_status = mv_get_crpb_status(ap); + handled++; + } else if ((DEV_IRQ << hard_port) & hc_irq_cause) { + /* received ATA IRQ; read the status reg to clear INTRQ + */ + ata_status = readb((void __iomem *) + ap->ioaddr.status_addr); + handled++; + } + + err_mask = ac_err_mask(ata_status); + + shift = port << 1; /* (port * 2) */ + if (port >= MV_PORTS_PER_HC) { + shift++; /* skip bit 8 in the HC Main IRQ reg */ + } + if ((PORT0_ERR << shift) & relevant) { + mv_err_intr(ap); + err_mask |= AC_ERR_OTHER; + handled++; + } + + if (handled && ap) { + qc = ata_qc_from_tag(ap, ap->active_tag); + if (NULL != qc) { + VPRINTK("port %u IRQ found for qc, " + "ata_status 0x%x\n", port,ata_status); + /* mark qc status appropriately */ + ata_qc_complete(qc, err_mask); + } + } + } + VPRINTK("EXIT\n"); +} + +/** + * mv_interrupt - + * @irq: unused + * @dev_instance: private data; in this case the host structure + * @regs: unused + * + * Read the read only register to determine if any host + * controllers have pending interrupts. If so, call lower level + * routine to handle. Also check for PCI errors which are only + * reported here. + * + * LOCKING: + * This routine holds the host_set lock while processing pending + * interrupts. + */ +static irqreturn_t mv_interrupt(int irq, void *dev_instance, + struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + unsigned int hc, handled = 0, n_hcs; + void __iomem *mmio = host_set->mmio_base; + u32 irq_stat; + + irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE_OFS); + + /* check the cases where we either have nothing pending or have read + * a bogus register value which can indicate HW removal or PCI fault + */ + if (!irq_stat || (0xffffffffU == irq_stat)) { + return IRQ_NONE; + } + + n_hcs = mv_get_hc_count(host_set->ports[0]->flags); + spin_lock(&host_set->lock); + + for (hc = 0; hc < n_hcs; hc++) { + u32 relevant = irq_stat & (HC0_IRQ_PEND << (hc * HC_SHIFT)); + if (relevant) { + mv_host_intr(host_set, relevant, hc); + handled++; + } + } + if (PCI_ERR & irq_stat) { + printk(KERN_ERR DRV_NAME ": PCI ERROR; PCI IRQ cause=0x%08x\n", + readl(mmio + PCI_IRQ_CAUSE_OFS)); + + DPRINTK("All regs @ PCI error\n"); + mv_dump_all_regs(mmio, -1, to_pci_dev(host_set->dev)); + + writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); + handled++; + } + spin_unlock(&host_set->lock); + + return IRQ_RETVAL(handled); +} + +/** + * mv_phy_reset - Perform eDMA reset followed by COMRESET + * @ap: ATA channel to manipulate + * + * Part of this is taken from __sata_phy_reset and modified to + * not sleep since this routine gets called from interrupt level. + * + * LOCKING: + * Inherited from caller. This is coded to safe to call at + * interrupt level, i.e. it does not sleep. + */ +static void mv_phy_reset(struct ata_port *ap) +{ + void __iomem *port_mmio = mv_ap_base(ap); + struct ata_taskfile tf; + struct ata_device *dev = &ap->device[0]; + unsigned long timeout; + + VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio); + + mv_stop_dma(ap); + + writelfl(ATA_RST, port_mmio + EDMA_CMD_OFS); + udelay(25); /* allow reset propagation */ + + /* Spec never mentions clearing the bit. Marvell's driver does + * clear the bit, however. + */ + writelfl(0, port_mmio + EDMA_CMD_OFS); + + VPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x " + "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), + mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); + + /* proceed to init communications via the scr_control reg */ + scr_write_flush(ap, SCR_CONTROL, 0x301); + mdelay(1); + scr_write_flush(ap, SCR_CONTROL, 0x300); + timeout = jiffies + (HZ * 1); + do { + mdelay(10); + if ((scr_read(ap, SCR_STATUS) & 0xf) != 1) + break; + } while (time_before(jiffies, timeout)); + + VPRINTK("S-regs after PHY wake: SStat 0x%08x SErr 0x%08x " + "SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS), + mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL)); + + if (sata_dev_present(ap)) { + ata_port_probe(ap); + } else { + printk(KERN_INFO "ata%u: no device found (phy stat %08x)\n", + ap->id, scr_read(ap, SCR_STATUS)); + ata_port_disable(ap); + return; + } + ap->cbl = ATA_CBL_SATA; + + tf.lbah = readb((void __iomem *) ap->ioaddr.lbah_addr); + tf.lbam = readb((void __iomem *) ap->ioaddr.lbam_addr); + tf.lbal = readb((void __iomem *) ap->ioaddr.lbal_addr); + tf.nsect = readb((void __iomem *) ap->ioaddr.nsect_addr); + + dev->class = ata_dev_classify(&tf); + if (!ata_dev_present(dev)) { + VPRINTK("Port disabled post-sig: No device present.\n"); + ata_port_disable(ap); + } + VPRINTK("EXIT\n"); +} + +/** + * mv_eng_timeout - Routine called by libata when SCSI times out I/O + * @ap: ATA channel to manipulate + * + * Intent is to clear all pending error conditions, reset the + * chip/bus, fail the command, and move on. + * + * LOCKING: + * This routine holds the host_set lock while failing the command. + */ +static void mv_eng_timeout(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + unsigned long flags; + + printk(KERN_ERR "ata%u: Entering mv_eng_timeout\n",ap->id); + DPRINTK("All regs @ start of eng_timeout\n"); + mv_dump_all_regs(ap->host_set->mmio_base, ap->port_no, + to_pci_dev(ap->host_set->dev)); + + qc = ata_qc_from_tag(ap, ap->active_tag); + printk(KERN_ERR "mmio_base %p ap %p qc %p scsi_cmnd %p &cmnd %p\n", + ap->host_set->mmio_base, ap, qc, qc->scsicmd, + &qc->scsicmd->cmnd); + + mv_err_intr(ap); + mv_phy_reset(ap); + + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + } else { + /* hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + spin_lock_irqsave(&ap->host_set->lock, flags); + qc->scsidone = scsi_finish_command; + ata_qc_complete(qc, AC_ERR_OTHER); + spin_unlock_irqrestore(&ap->host_set->lock, flags); + } +} + +/** + * mv_port_init - Perform some early initialization on a single port. + * @port: libata data structure storing shadow register addresses + * @port_mmio: base address of the port + * + * Initialize shadow register mmio addresses, clear outstanding + * interrupts on the port, and unmask interrupts for the future + * start of the port. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_port_init(struct ata_ioports *port, void __iomem *port_mmio) +{ + unsigned long shd_base = (unsigned long) port_mmio + SHD_BLK_OFS; + unsigned serr_ofs; + + /* PIO related setup + */ + port->data_addr = shd_base + (sizeof(u32) * ATA_REG_DATA); + port->error_addr = + port->feature_addr = shd_base + (sizeof(u32) * ATA_REG_ERR); + port->nsect_addr = shd_base + (sizeof(u32) * ATA_REG_NSECT); + port->lbal_addr = shd_base + (sizeof(u32) * ATA_REG_LBAL); + port->lbam_addr = shd_base + (sizeof(u32) * ATA_REG_LBAM); + port->lbah_addr = shd_base + (sizeof(u32) * ATA_REG_LBAH); + port->device_addr = shd_base + (sizeof(u32) * ATA_REG_DEVICE); + port->status_addr = + port->command_addr = shd_base + (sizeof(u32) * ATA_REG_STATUS); + /* special case: control/altstatus doesn't have ATA_REG_ address */ + port->altstatus_addr = port->ctl_addr = shd_base + SHD_CTL_AST_OFS; + + /* unused: */ + port->cmd_addr = port->bmdma_addr = port->scr_addr = 0; + + /* Clear any currently outstanding port interrupt conditions */ + serr_ofs = mv_scr_offset(SCR_ERROR); + writelfl(readl(port_mmio + serr_ofs), port_mmio + serr_ofs); + writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); + + /* unmask all EDMA error interrupts */ + writelfl(~0, port_mmio + EDMA_ERR_IRQ_MASK_OFS); + + VPRINTK("EDMA cfg=0x%08x EDMA IRQ err cause/mask=0x%08x/0x%08x\n", + readl(port_mmio + EDMA_CFG_OFS), + readl(port_mmio + EDMA_ERR_IRQ_CAUSE_OFS), + readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS)); +} + +/** + * mv_host_init - Perform some early initialization of the host. + * @probe_ent: early data struct representing the host + * + * If possible, do an early global reset of the host. Then do + * our port init and clear/unmask all/relevant host interrupts. + * + * LOCKING: + * Inherited from caller. + */ +static int mv_host_init(struct ata_probe_ent *probe_ent) +{ + int rc = 0, n_hc, port, hc; + void __iomem *mmio = probe_ent->mmio_base; + void __iomem *port_mmio; + + if ((MV_FLAG_GLBL_SFT_RST & probe_ent->host_flags) && + mv_global_soft_reset(probe_ent->mmio_base)) { + rc = 1; + goto done; + } + + n_hc = mv_get_hc_count(probe_ent->host_flags); + probe_ent->n_ports = MV_PORTS_PER_HC * n_hc; + + for (port = 0; port < probe_ent->n_ports; port++) { + port_mmio = mv_port_base(mmio, port); + mv_port_init(&probe_ent->port[port], port_mmio); + } + + for (hc = 0; hc < n_hc; hc++) { + void __iomem *hc_mmio = mv_hc_base(mmio, hc); + + VPRINTK("HC%i: HC config=0x%08x HC IRQ cause " + "(before clear)=0x%08x\n", hc, + readl(hc_mmio + HC_CFG_OFS), + readl(hc_mmio + HC_IRQ_CAUSE_OFS)); + + /* Clear any currently outstanding hc interrupt conditions */ + writelfl(0, hc_mmio + HC_IRQ_CAUSE_OFS); + } + + /* Clear any currently outstanding host interrupt conditions */ + writelfl(0, mmio + PCI_IRQ_CAUSE_OFS); + + /* and unmask interrupt generation for host regs */ + writelfl(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK_OFS); + writelfl(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); + + VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x " + "PCI int cause/mask=0x%08x/0x%08x\n", + readl(mmio + HC_MAIN_IRQ_CAUSE_OFS), + readl(mmio + HC_MAIN_IRQ_MASK_OFS), + readl(mmio + PCI_IRQ_CAUSE_OFS), + readl(mmio + PCI_IRQ_MASK_OFS)); +done: + return rc; +} + +/** + * mv_print_info - Dump key info to kernel log for perusal. + * @probe_ent: early data struct representing the host + * + * FIXME: complete this. + * + * LOCKING: + * Inherited from caller. + */ +static void mv_print_info(struct ata_probe_ent *probe_ent) +{ + struct pci_dev *pdev = to_pci_dev(probe_ent->dev); + struct mv_host_priv *hpriv = probe_ent->private_data; + u8 rev_id, scc; + const char *scc_s; + + /* Use this to determine the HW stepping of the chip so we know + * what errata to workaround + */ + pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + + pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &scc); + if (scc == 0) + scc_s = "SCSI"; + else if (scc == 0x01) + scc_s = "RAID"; + else + scc_s = "unknown"; + + dev_printk(KERN_INFO, &pdev->dev, + "%u slots %u ports %s mode IRQ via %s\n", + (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, + scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx"); +} + +/** + * mv_init_one - handle a positive probe of a Marvell host + * @pdev: PCI device found + * @ent: PCI device ID entry for the matched host + * + * LOCKING: + * Inherited from caller. + */ +static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version = 0; + struct ata_probe_ent *probe_ent = NULL; + struct mv_host_priv *hpriv; + unsigned int board_idx = (unsigned int)ent->driver_data; + void __iomem *mmio_base; + int pci_dev_busy = 0, rc; + + if (!printed_version++) + dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) { + return rc; + } + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) { + pci_dev_busy = 1; + goto err_out; + } + + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (probe_ent == NULL) { + rc = -ENOMEM; + goto err_out_regions; + } + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->dev = pci_dev_to_dev(pdev); + INIT_LIST_HEAD(&probe_ent->node); + + mmio_base = pci_iomap(pdev, MV_PRIMARY_BAR, 0); + if (mmio_base == NULL) { + rc = -ENOMEM; + goto err_out_free_ent; + } + + hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) { + rc = -ENOMEM; + goto err_out_iounmap; + } + memset(hpriv, 0, sizeof(*hpriv)); + + probe_ent->sht = mv_port_info[board_idx].sht; + probe_ent->host_flags = mv_port_info[board_idx].host_flags; + probe_ent->pio_mask = mv_port_info[board_idx].pio_mask; + probe_ent->udma_mask = mv_port_info[board_idx].udma_mask; + probe_ent->port_ops = mv_port_info[board_idx].port_ops; + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = mmio_base; + probe_ent->private_data = hpriv; + + /* initialize adapter */ + rc = mv_host_init(probe_ent); + if (rc) { + goto err_out_hpriv; + } + + /* Enable interrupts */ + if (pci_enable_msi(pdev) == 0) { + hpriv->hp_flags |= MV_HP_FLAG_MSI; + } else { + pci_intx(pdev, 1); + } + + mv_dump_pci_cfg(pdev, 0x68); + mv_print_info(probe_ent); + + if (ata_device_add(probe_ent) == 0) { + rc = -ENODEV; /* No devices discovered */ + goto err_out_dev_add; + } + + kfree(probe_ent); + return 0; + +err_out_dev_add: + if (MV_HP_FLAG_MSI & hpriv->hp_flags) { + pci_disable_msi(pdev); + } else { + pci_intx(pdev, 0); + } +err_out_hpriv: + kfree(hpriv); +err_out_iounmap: + pci_iounmap(pdev, mmio_base); +err_out_free_ent: + kfree(probe_ent); +err_out_regions: + pci_release_regions(pdev); +err_out: + if (!pci_dev_busy) { + pci_disable_device(pdev); + } + + return rc; +} + +static int __init mv_init(void) +{ + return pci_module_init(&mv_pci_driver); +} + +static void __exit mv_exit(void) +{ + pci_unregister_driver(&mv_pci_driver); +} + +MODULE_AUTHOR("Brett Russ"); +MODULE_DESCRIPTION("SCSI low-level driver for Marvell SATA controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, mv_pci_tbl); +MODULE_VERSION(DRV_VERSION); + +module_init(mv_init); +module_exit(mv_exit); diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index b0403ccd8a25..d573888eda76 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -4,21 +4,39 @@ * Copyright 2004 NVIDIA Corp. All rights reserved. * Copyright 2004 Andrew Chew * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * No hardware documentation available outside of NVIDIA. + * This driver programs the NVIDIA SATA controller in a similar + * fashion as with other PCI IDE BMDMA controllers, with a few + * NV-specific details such as register offsets, SATA phy location, + * hotplug info, etc. + * + * 0.09 + * - Fixed bug introduced by 0.08's MCP51 and MCP55 support. + * + * 0.08 + * - Added support for MCP51 and MCP55. + * + * 0.07 + * - Added support for RAID class code. * * 0.06 * - Added generic SATA support by using a pci_device_id that filters on @@ -43,12 +61,13 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> #define DRV_NAME "sata_nv" -#define DRV_VERSION "0.6" +#define DRV_VERSION "0.8" #define NV_PORTS 2 #define NV_PIO_MASK 0x1f @@ -134,9 +153,20 @@ static struct pci_device_id nv_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP04_SATA2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CK804 }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SATA2, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, GENERIC }, { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE<<8, 0xffff00, GENERIC }, + { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_STORAGE_RAID<<8, 0xffff00, GENERIC }, { 0, } /* terminate list */ }; @@ -209,7 +239,7 @@ static Scsi_Host_Template nv_sht = { .ordered_flush = 1, }; -static struct ata_port_operations nv_ops = { +static const struct ata_port_operations nv_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -274,7 +304,8 @@ static irqreturn_t nv_interrupt (int irq, void *dev_instance, struct ata_port *ap; ap = host_set->ports[i]; - if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (ap && + !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -301,7 +332,7 @@ static u32 nv_scr_read (struct ata_port *ap, unsigned int sc_reg) return 0xffffffffU; if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) - return readl((void*)ap->ioaddr.scr_addr + (sc_reg * 4)); + return readl((void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4)); else return inl(ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -315,7 +346,7 @@ static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) return; if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) - writel(val, (void*)ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, (void __iomem *)ap->ioaddr.scr_addr + (sc_reg * 4)); else outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -323,6 +354,7 @@ static void nv_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) static void nv_host_stop (struct ata_host_set *host_set) { struct nv_host *host = host_set->private_data; + struct pci_dev *pdev = to_pci_dev(host_set->dev); // Disable hotplug event interrupts. if (host->host_desc->disable_hotplug) @@ -330,7 +362,8 @@ static void nv_host_stop (struct ata_host_set *host_set) kfree(host); - ata_host_stop(host_set); + if (host_set->mmio_base) + pci_iounmap(pdev, host_set->mmio_base); } static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) @@ -351,7 +384,7 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -373,7 +406,7 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) rc = -ENOMEM; ppi = &nv_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi); + probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); if (!probe_ent) goto err_out_regions; @@ -392,8 +425,7 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) { unsigned long base; - probe_ent->mmio_base = ioremap(pci_resource_start(pdev, 5), - pci_resource_len(pdev, 5)); + probe_ent->mmio_base = pci_iomap(pdev, 5, 0); if (probe_ent->mmio_base == NULL) { rc = -EIO; goto err_out_free_host; @@ -429,7 +461,7 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) err_out_iounmap: if (host->host_flags & NV_HOST_FLAGS_SCR_MMIO) - iounmap(probe_ent->mmio_base); + pci_iounmap(pdev, probe_ent->mmio_base); err_out_free_host: kfree(host); err_out_free_ent: diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c index 5c1d4411457a..b41c977d6fab 100644 --- a/drivers/scsi/sata_promise.c +++ b/drivers/scsi/sata_promise.c @@ -7,21 +7,26 @@ * * Copyright 2003-2004 Red Hat, Inc. * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware information only available under NDA. * */ @@ -33,6 +38,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -40,7 +46,7 @@ #include "sata_promise.h" #define DRV_NAME "sata_promise" -#define DRV_VERSION "1.01" +#define DRV_VERSION "1.02" enum { @@ -79,13 +85,15 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *r static void pdc_eng_timeout(struct ata_port *ap); static int pdc_port_start(struct ata_port *ap); static void pdc_port_stop(struct ata_port *ap); -static void pdc_phy_reset(struct ata_port *ap); +static void pdc_pata_phy_reset(struct ata_port *ap); +static void pdc_sata_phy_reset(struct ata_port *ap); static void pdc_qc_prep(struct ata_queued_cmd *qc); -static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf); -static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); +static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc_irq_clear(struct ata_port *ap); static int pdc_qc_issue_prot(struct ata_queued_cmd *qc); + static Scsi_Host_Template pdc_ata_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -106,24 +114,48 @@ static Scsi_Host_Template pdc_ata_sht = { .ordered_flush = 1, }; -static struct ata_port_operations pdc_ata_ops = { +static const struct ata_port_operations pdc_sata_ops = { .port_disable = ata_port_disable, .tf_load = pdc_tf_load_mmio, .tf_read = ata_tf_read, .check_status = ata_check_status, .exec_command = pdc_exec_command_mmio, .dev_select = ata_std_dev_select, - .phy_reset = pdc_phy_reset, + + .phy_reset = pdc_sata_phy_reset, + .qc_prep = pdc_qc_prep, .qc_issue = pdc_qc_issue_prot, .eng_timeout = pdc_eng_timeout, .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, + .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, .port_start = pdc_port_start, .port_stop = pdc_port_stop, - .host_stop = ata_host_stop, + .host_stop = ata_pci_host_stop, +}; + +static const struct ata_port_operations pdc_pata_ops = { + .port_disable = ata_port_disable, + .tf_load = pdc_tf_load_mmio, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = pdc_exec_command_mmio, + .dev_select = ata_std_dev_select, + + .phy_reset = pdc_pata_phy_reset, + + .qc_prep = pdc_qc_prep, + .qc_issue = pdc_qc_issue_prot, + .eng_timeout = pdc_eng_timeout, + .irq_handler = pdc_interrupt, + .irq_clear = pdc_irq_clear, + + .port_start = pdc_port_start, + .port_stop = pdc_port_stop, + .host_stop = ata_pci_host_stop, }; static struct ata_port_info pdc_port_info[] = { @@ -135,7 +167,7 @@ static struct ata_port_info pdc_port_info[] = { .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_ata_ops, + .port_ops = &pdc_sata_ops, }, /* board_20319 */ @@ -146,7 +178,7 @@ static struct ata_port_info pdc_port_info[] = { .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_ata_ops, + .port_ops = &pdc_sata_ops, }, /* board_20619 */ @@ -157,13 +189,15 @@ static struct ata_port_info pdc_port_info[] = { .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ - .port_ops = &pdc_ata_ops, + .port_ops = &pdc_pata_ops, }, }; static struct pci_device_id pdc_ata_pci_tbl[] = { { PCI_VENDOR_ID_PROMISE, 0x3371, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3570, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, { PCI_VENDOR_ID_PROMISE, 0x3571, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_2037x }, { PCI_VENDOR_ID_PROMISE, 0x3373, PCI_ANY_ID, PCI_ANY_ID, 0, 0, @@ -176,11 +210,17 @@ static struct pci_device_id pdc_ata_pci_tbl[] = { board_2037x }, { PCI_VENDOR_ID_PROMISE, 0x3d75, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_2037x }, + { PCI_VENDOR_ID_PROMISE, 0x3d73, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_2037x }, { PCI_VENDOR_ID_PROMISE, 0x3318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_20319 }, { PCI_VENDOR_ID_PROMISE, 0x3319, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3519, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, + { PCI_VENDOR_ID_PROMISE, 0x3d17, PCI_ANY_ID, PCI_ANY_ID, 0, 0, + board_20319 }, { PCI_VENDOR_ID_PROMISE, 0x3d18, PCI_ANY_ID, PCI_ANY_ID, 0, 0, board_20319 }, @@ -248,7 +288,7 @@ static void pdc_port_stop(struct ata_port *ap) static void pdc_reset_port(struct ata_port *ap) { - void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_CTLSTAT; + void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT; unsigned int i; u32 tmp; @@ -268,17 +308,28 @@ static void pdc_reset_port(struct ata_port *ap) readl(mmio); /* flush */ } -static void pdc_phy_reset(struct ata_port *ap) +static void pdc_sata_phy_reset(struct ata_port *ap) { pdc_reset_port(ap); sata_phy_reset(ap); } +static void pdc_pata_phy_reset(struct ata_port *ap) +{ + /* FIXME: add cable detect. Don't assume 40-pin cable */ + ap->cbl = ATA_CBL_PATA40; + ap->udma_mask &= ATA_UDMA_MASK_40C; + + pdc_reset_port(ap); + ata_port_probe(ap); + ata_bus_reset(ap); +} + static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { if (sc_reg > SCR_CONTROL) return 0xffffffffU; - return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -287,7 +338,7 @@ static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, { if (sc_reg > SCR_CONTROL) return; - writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } static void pdc_qc_prep(struct ata_queued_cmd *qc) @@ -321,11 +372,15 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc) static void pdc_eng_timeout(struct ata_port *ap) { + struct ata_host_set *host_set = ap->host_set; u8 drv_stat; struct ata_queued_cmd *qc; + unsigned long flags; DPRINTK("ENTER\n"); + spin_lock_irqsave(&host_set->lock, flags); + qc = ata_qc_from_tag(ap, ap->active_tag); if (!qc) { printk(KERN_ERR "ata%u: BUG: timeout without command\n", @@ -345,7 +400,8 @@ static void pdc_eng_timeout(struct ata_port *ap) case ATA_PROT_DMA: case ATA_PROT_NODATA: printk(KERN_ERR "ata%u: command timeout\n", ap->id); - ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); + drv_stat = ata_wait_idle(ap); + ata_qc_complete(qc, __ac_err_mask(drv_stat)); break; default: @@ -354,50 +410,48 @@ static void pdc_eng_timeout(struct ata_port *ap) printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n", ap->id, qc->tf.command, drv_stat); - ata_qc_complete(qc, drv_stat); + ata_qc_complete(qc, ac_err_mask(drv_stat)); break; } out: + spin_unlock_irqrestore(&host_set->lock, flags); DPRINTK("EXIT\n"); } static inline unsigned int pdc_host_intr( struct ata_port *ap, struct ata_queued_cmd *qc) { - u8 status; - unsigned int handled = 0, have_err = 0; + unsigned int handled = 0, err_mask = 0; u32 tmp; - void *mmio = (void *) ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; + void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; tmp = readl(mmio); if (tmp & PDC_ERR_MASK) { - have_err = 1; + err_mask = AC_ERR_DEV; pdc_reset_port(ap); } switch (qc->tf.protocol) { case ATA_PROT_DMA: case ATA_PROT_NODATA: - status = ata_wait_idle(ap); - if (have_err) - status |= ATA_ERR; - ata_qc_complete(qc, status); + err_mask |= ac_err_mask(ata_wait_idle(ap)); + ata_qc_complete(qc, err_mask); handled = 1; break; default: - ap->stats.idle_irq++; - break; + ap->stats.idle_irq++; + break; } - return handled; + return handled; } static void pdc_irq_clear(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; - void *mmio = host_set->mmio_base; + void __iomem *mmio = host_set->mmio_base; readl(mmio + PDC_INT_SEQMASK); } @@ -409,7 +463,7 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *r u32 mask = 0; unsigned int i, tmp; unsigned int handled = 0; - void *mmio_base; + void __iomem *mmio_base; VPRINTK("ENTER\n"); @@ -441,7 +495,8 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance, struct pt_regs *r VPRINTK("port %u\n", i); ap = host_set->ports[i]; tmp = mask & (1 << (i + 1)); - if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (tmp && ap && + !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -471,8 +526,8 @@ static inline void pdc_packet_start(struct ata_queued_cmd *qc) pp->pkt[2] = seq; wmb(); /* flush PRD, pkt writes */ - writel(pp->pkt_dma, (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */ + writel(pp->pkt_dma, (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); /* flush */ } static int pdc_qc_issue_prot(struct ata_queued_cmd *qc) @@ -494,7 +549,7 @@ static int pdc_qc_issue_prot(struct ata_queued_cmd *qc) return ata_qc_issue_prot(qc); } -static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { WARN_ON (tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_NODATA); @@ -502,7 +557,7 @@ static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) } -static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { WARN_ON (tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_NODATA); @@ -530,7 +585,7 @@ static void pdc_ata_setup_port(struct ata_ioports *port, unsigned long base) static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) { - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; u32 tmp; /* @@ -573,13 +628,13 @@ static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *e static int printed_version; struct ata_probe_ent *probe_ent = NULL; unsigned long base; - void *mmio_base; + void __iomem *mmio_base; unsigned int board_idx = (unsigned int) ent->driver_data; int pci_dev_busy = 0; int rc; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* * If this driver happens to only be useful on Apple's K2, then @@ -612,8 +667,7 @@ static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *e probe_ent->dev = pci_dev_to_dev(pdev); INIT_LIST_HEAD(&probe_ent->node); - mmio_base = ioremap(pci_resource_start(pdev, 3), - pci_resource_len(pdev, 3)); + mmio_base = pci_iomap(pdev, 3, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; diff --git a/drivers/scsi/sata_promise.h b/drivers/scsi/sata_promise.h index 6e7e96b9ee13..6ee5e190262d 100644 --- a/drivers/scsi/sata_promise.h +++ b/drivers/scsi/sata_promise.h @@ -3,21 +3,24 @@ * * Copyright 2003-2004 Red Hat, Inc. * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* * */ diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c index 1383e8a28d72..9938dae782b6 100644 --- a/drivers/scsi/sata_qstor.c +++ b/drivers/scsi/sata_qstor.c @@ -6,21 +6,24 @@ * Copyright 2005 Pacific Digital Corporation. * (OSL/GPL code release authorized by Jalil Fadavi). * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* * */ @@ -32,6 +35,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <asm/io.h> @@ -48,8 +52,6 @@ enum { QS_PRD_BYTES = QS_MAX_PRD * 16, QS_PKT_BYTES = QS_CPB_BYTES + QS_PRD_BYTES, - QS_DMA_BOUNDARY = ~0UL, - /* global register offsets */ QS_HCF_CNFG3 = 0x0003, /* host configuration offset */ QS_HID_HPHY = 0x0004, /* host physical interface info */ @@ -98,6 +100,10 @@ enum { board_2068_idx = 0, /* QStor 4-port SATA/RAID */ }; +enum { + QS_DMA_BOUNDARY = ~0UL +}; + typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t; struct qs_port_priv { @@ -117,7 +123,7 @@ static void qs_phy_reset(struct ata_port *ap); static void qs_qc_prep(struct ata_queued_cmd *qc); static int qs_qc_issue(struct ata_queued_cmd *qc); static int qs_check_atapi_dma(struct ata_queued_cmd *qc); -static void qs_bmdma_stop(struct ata_port *ap); +static void qs_bmdma_stop(struct ata_queued_cmd *qc); static u8 qs_bmdma_status(struct ata_port *ap); static void qs_irq_clear(struct ata_port *ap); static void qs_eng_timeout(struct ata_port *ap); @@ -142,7 +148,7 @@ static Scsi_Host_Template qs_ata_sht = { .bios_param = ata_std_bios_param, }; -static struct ata_port_operations qs_ata_ops = { +static const struct ata_port_operations qs_ata_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -198,7 +204,7 @@ static int qs_check_atapi_dma(struct ata_queued_cmd *qc) return 1; /* ATAPI DMA not supported */ } -static void qs_bmdma_stop(struct ata_port *ap) +static void qs_bmdma_stop(struct ata_queued_cmd *qc) { /* nothing */ } @@ -386,7 +392,8 @@ static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set) DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n", sff1, sff0, port_no, sHST, sDST); handled = 1; - if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (ap && !(ap->flags & + (ATA_FLAG_PORT_DISABLED|ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; struct qs_port_priv *pp = ap->private_data; if (!pp || pp->state != qs_state_pkt) @@ -394,11 +401,12 @@ static inline unsigned int qs_intr_pkt(struct ata_host_set *host_set) qc = ata_qc_from_tag(ap, ap->active_tag); if (qc && (!(qc->tf.ctl & ATA_NIEN))) { switch (sHST) { - case 0: /* sucessful CPB */ + case 0: /* successful CPB */ case 3: /* device error */ pp->state = qs_state_idle; qs_enter_reg_mode(qc->ap); - ata_qc_complete(qc, sDST); + ata_qc_complete(qc, + ac_err_mask(sDST)); break; default: break; @@ -417,7 +425,8 @@ static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set) for (port_no = 0; port_no < host_set->n_ports; ++port_no) { struct ata_port *ap; ap = host_set->ports[port_no]; - if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (ap && + !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; struct qs_port_priv *pp = ap->private_data; if (!pp || pp->state != qs_state_mmio) @@ -426,15 +435,15 @@ static inline unsigned int qs_intr_mmio(struct ata_host_set *host_set) if (qc && (!(qc->tf.ctl & ATA_NIEN))) { /* check main status, clearing INTRQ */ - u8 status = ata_chk_status(ap); + u8 status = ata_check_status(ap); if ((status & ATA_BUSY)) continue; DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", ap->id, qc->tf.protocol, status); - + /* complete taskfile transaction */ pp->state = qs_state_idle; - ata_qc_complete(qc, status); + ata_qc_complete(qc, ac_err_mask(status)); handled = 1; } } @@ -489,7 +498,7 @@ static int qs_port_start(struct ata_port *ap) if (rc) return rc; qs_enter_reg_mode(ap); - pp = kcalloc(1, sizeof(*pp), GFP_KERNEL); + pp = kzalloc(sizeof(*pp), GFP_KERNEL); if (!pp) { rc = -ENOMEM; goto err_out; @@ -533,11 +542,12 @@ static void qs_port_stop(struct ata_port *ap) static void qs_host_stop(struct ata_host_set *host_set) { void __iomem *mmio_base = host_set->mmio_base; + struct pci_dev *pdev = to_pci_dev(host_set->dev); writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ - ata_host_stop(host_set); + pci_iounmap(pdev, mmio_base); } static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) @@ -591,25 +601,22 @@ static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) if (rc) { rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME - "(%s): 64-bit DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); return rc; } } } else { rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME - "(%s): 32-bit DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); return rc; } rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (rc) { - printk(KERN_ERR DRV_NAME - "(%s): 32-bit consistent DMA enable failed\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); return rc; } } @@ -626,7 +633,7 @@ static int qs_ata_init_one(struct pci_dev *pdev, int rc, port_no; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -641,8 +648,7 @@ static int qs_ata_init_one(struct pci_dev *pdev, goto err_out_regions; } - mmio_base = ioremap(pci_resource_start(pdev, 4), - pci_resource_len(pdev, 4)); + mmio_base = pci_iomap(pdev, 4, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_regions; @@ -692,7 +698,7 @@ static int qs_ata_init_one(struct pci_dev *pdev, return 0; err_out_iounmap: - iounmap(mmio_base); + pci_iounmap(pdev, mmio_base); err_out_regions: pci_release_regions(pdev); err_out: diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index 49ed557a4b66..435f7e0085ec 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -5,24 +5,32 @@ * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * - * Copyright 2003 Red Hat, Inc. + * Copyright 2003-2005 Red Hat, Inc. * Copyright 2003 Benjamin Herrenschmidt * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Documentation for SiI 3112: + * http://gkernel.sourceforge.net/specs/sii/3112A_SiI-DS-0095-B2.pdf.bz2 + * + * Other errata and documentation available under NDA. * */ @@ -33,6 +41,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -41,8 +50,11 @@ #define DRV_VERSION "0.9" enum { + SIL_FLAG_MOD15WRITE = (1 << 30), + sil_3112 = 0, - sil_3114 = 1, + sil_3112_m15w = 1, + sil_3114 = 2, SIL_FIFO_R0 = 0x40, SIL_FIFO_W0 = 0x41, @@ -75,14 +87,15 @@ static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void sil_post_set_mode (struct ata_port *ap); + static struct pci_device_id sil_pci_tbl[] = { - { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1095, 0x3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_m15w }, + { 0x1095, 0x0240, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_m15w }, { 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, { 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 }, - { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, - { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 }, + { 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_m15w }, + { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_m15w }, + { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_m15w }, { } /* terminate list */ }; @@ -138,7 +151,7 @@ static Scsi_Host_Template sil_sht = { .ordered_flush = 1, }; -static struct ata_port_operations sil_ops = { +static const struct ata_port_operations sil_ops = { .port_disable = ata_port_disable, .dev_config = sil_dev_config, .tf_load = ata_tf_load, @@ -161,7 +174,7 @@ static struct ata_port_operations sil_ops = { .scr_write = sil_scr_write, .port_start = ata_port_start, .port_stop = ata_port_stop, - .host_stop = ata_host_stop, + .host_stop = ata_pci_host_stop, }; static struct ata_port_info sil_port_info[] = { @@ -174,6 +187,16 @@ static struct ata_port_info sil_port_info[] = { .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x3f, /* udma0-5 */ .port_ops = &sil_ops, + }, /* sil_3112_15w - keep it sync'd w/ sil_3112 */ + { + .sht = &sil_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SRST | ATA_FLAG_MMIO | + SIL_FLAG_MOD15WRITE, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil_ops, }, /* sil_3114 */ { .sht = &sil_sht, @@ -210,6 +233,7 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sil_pci_tbl); MODULE_VERSION(DRV_VERSION); + static unsigned char sil_get_device_cache_line(struct pci_dev *pdev) { u8 cache_line = 0; @@ -221,7 +245,8 @@ static void sil_post_set_mode (struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; struct ata_device *dev; - void *addr = host_set->mmio_base + sil_port[ap->port_no].xfer_mode; + void __iomem *addr = + host_set->mmio_base + sil_port[ap->port_no].xfer_mode; u32 tmp, dev_mode[2]; unsigned int i; @@ -265,7 +290,7 @@ static inline unsigned long sil_scr_addr(struct ata_port *ap, unsigned int sc_re static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) { - void *mmio = (void *) sil_scr_addr(ap, sc_reg); + void __iomem *mmio = (void __iomem *) sil_scr_addr(ap, sc_reg); if (mmio) return readl(mmio); return 0xffffffffU; @@ -273,7 +298,7 @@ static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { - void *mmio = (void *) sil_scr_addr(ap, sc_reg); + void *mmio = (void __iomem *) sil_scr_addr(ap, sc_reg); if (mmio) writel(val, mmio); } @@ -323,15 +348,15 @@ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) while ((len > 0) && (s[len - 1] == ' ')) len--; - for (n = 0; sil_blacklist[n].product; n++) + for (n = 0; sil_blacklist[n].product; n++) if (!memcmp(sil_blacklist[n].product, s, strlen(sil_blacklist[n].product))) { quirks = sil_blacklist[n].quirk; break; } - + /* limit requests to 15 sectors */ - if (quirks & SIL_QUIRK_MOD15WRITE) { + if ((ap->flags & SIL_FLAG_MOD15WRITE) && (quirks & SIL_QUIRK_MOD15WRITE)) { printk(KERN_INFO "ata%u(%u): applying Seagate errata fix\n", ap->id, dev->devno); ap->host->max_sectors = 15; @@ -354,7 +379,7 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) static int printed_version; struct ata_probe_ent *probe_ent = NULL; unsigned long base; - void *mmio_base; + void __iomem *mmio_base; int rc; unsigned int i; int pci_dev_busy = 0; @@ -362,7 +387,7 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) u8 cls; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* * If this driver happens to only be useful on Apple's K2, then @@ -404,8 +429,7 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->irq_flags = SA_SHIRQ; probe_ent->host_flags = sil_port_info[ent->driver_data].host_flags; - mmio_base = ioremap(pci_resource_start(pdev, 5), - pci_resource_len(pdev, 5)); + mmio_base = pci_iomap(pdev, 5, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; @@ -440,8 +464,8 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) writeb(cls, mmio_base + SIL_FIFO_W3); } } else - printk(KERN_WARNING DRV_NAME "(%s): cache line size not set. Driver may not function\n", - pci_name(pdev)); + dev_printk(KERN_WARNING, &pdev->dev, + "cache line size not set. Driver may not function\n"); if (ent->driver_data == sil_3114) { irq_mask = SIL_MASK_4PORT; diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c new file mode 100644 index 000000000000..c66548025657 --- /dev/null +++ b/drivers/scsi/sata_sil24.c @@ -0,0 +1,871 @@ +/* + * sata_sil24.c - Driver for Silicon Image 3124/3132 SATA-2 controllers + * + * Copyright 2005 Tejun Heo + * + * Based on preview driver from Silicon Image. + * + * NOTE: No NCQ/ATAPI support yet. The preview driver didn't support + * NCQ nor ATAPI, and, unfortunately, I couldn't find out how to make + * those work. Enabling those shouldn't be difficult. Basic + * structure is all there (in libata-dev tree). If you have any + * information about this hardware, please contact me or linux-ide. + * Info is needed on... + * + * - How to issue tagged commands and turn on sactive on issue accordingly. + * - Where to put an ATAPI command and how to tell the device to send it. + * - How to enable/use 64bit. + * + * 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 the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/device.h> +#include <scsi/scsi_host.h> +#include "scsi.h" +#include <linux/libata.h> +#include <asm/io.h> + +#define DRV_NAME "sata_sil24" +#define DRV_VERSION "0.22" /* Silicon Image's preview driver was 0.10 */ + +/* + * Port request block (PRB) 32 bytes + */ +struct sil24_prb { + u16 ctrl; + u16 prot; + u32 rx_cnt; + u8 fis[6 * 4]; +}; + +/* + * Scatter gather entry (SGE) 16 bytes + */ +struct sil24_sge { + u64 addr; + u32 cnt; + u32 flags; +}; + +/* + * Port multiplier + */ +struct sil24_port_multiplier { + u32 diag; + u32 sactive; +}; + +enum { + /* + * Global controller registers (128 bytes @ BAR0) + */ + /* 32 bit regs */ + HOST_SLOT_STAT = 0x00, /* 32 bit slot stat * 4 */ + HOST_CTRL = 0x40, + HOST_IRQ_STAT = 0x44, + HOST_PHY_CFG = 0x48, + HOST_BIST_CTRL = 0x50, + HOST_BIST_PTRN = 0x54, + HOST_BIST_STAT = 0x58, + HOST_MEM_BIST_STAT = 0x5c, + HOST_FLASH_CMD = 0x70, + /* 8 bit regs */ + HOST_FLASH_DATA = 0x74, + HOST_TRANSITION_DETECT = 0x75, + HOST_GPIO_CTRL = 0x76, + HOST_I2C_ADDR = 0x78, /* 32 bit */ + HOST_I2C_DATA = 0x7c, + HOST_I2C_XFER_CNT = 0x7e, + HOST_I2C_CTRL = 0x7f, + + /* HOST_SLOT_STAT bits */ + HOST_SSTAT_ATTN = (1 << 31), + + /* + * Port registers + * (8192 bytes @ +0x0000, +0x2000, +0x4000 and +0x6000 @ BAR2) + */ + PORT_REGS_SIZE = 0x2000, + PORT_PRB = 0x0000, /* (32 bytes PRB + 16 bytes SGEs * 6) * 31 (3968 bytes) */ + + PORT_PM = 0x0f80, /* 8 bytes PM * 16 (128 bytes) */ + /* 32 bit regs */ + PORT_CTRL_STAT = 0x1000, /* write: ctrl-set, read: stat */ + PORT_CTRL_CLR = 0x1004, /* write: ctrl-clear */ + PORT_IRQ_STAT = 0x1008, /* high: status, low: interrupt */ + PORT_IRQ_ENABLE_SET = 0x1010, /* write: enable-set */ + PORT_IRQ_ENABLE_CLR = 0x1014, /* write: enable-clear */ + PORT_ACTIVATE_UPPER_ADDR= 0x101c, + PORT_EXEC_FIFO = 0x1020, /* command execution fifo */ + PORT_CMD_ERR = 0x1024, /* command error number */ + PORT_FIS_CFG = 0x1028, + PORT_FIFO_THRES = 0x102c, + /* 16 bit regs */ + PORT_DECODE_ERR_CNT = 0x1040, + PORT_DECODE_ERR_THRESH = 0x1042, + PORT_CRC_ERR_CNT = 0x1044, + PORT_CRC_ERR_THRESH = 0x1046, + PORT_HSHK_ERR_CNT = 0x1048, + PORT_HSHK_ERR_THRESH = 0x104a, + /* 32 bit regs */ + PORT_PHY_CFG = 0x1050, + PORT_SLOT_STAT = 0x1800, + PORT_CMD_ACTIVATE = 0x1c00, /* 64 bit cmd activate * 31 (248 bytes) */ + PORT_EXEC_DIAG = 0x1e00, /* 32bit exec diag * 16 (64 bytes, 0-10 used on 3124) */ + PORT_PSD_DIAG = 0x1e40, /* 32bit psd diag * 16 (64 bytes, 0-8 used on 3124) */ + PORT_SCONTROL = 0x1f00, + PORT_SSTATUS = 0x1f04, + PORT_SERROR = 0x1f08, + PORT_SACTIVE = 0x1f0c, + + /* PORT_CTRL_STAT bits */ + PORT_CS_PORT_RST = (1 << 0), /* port reset */ + PORT_CS_DEV_RST = (1 << 1), /* device reset */ + PORT_CS_INIT = (1 << 2), /* port initialize */ + PORT_CS_IRQ_WOC = (1 << 3), /* interrupt write one to clear */ + PORT_CS_RESUME = (1 << 6), /* port resume */ + PORT_CS_32BIT_ACTV = (1 << 10), /* 32-bit activation */ + PORT_CS_PM_EN = (1 << 13), /* port multiplier enable */ + PORT_CS_RDY = (1 << 31), /* port ready to accept commands */ + + /* PORT_IRQ_STAT/ENABLE_SET/CLR */ + /* bits[11:0] are masked */ + PORT_IRQ_COMPLETE = (1 << 0), /* command(s) completed */ + PORT_IRQ_ERROR = (1 << 1), /* command execution error */ + PORT_IRQ_PORTRDY_CHG = (1 << 2), /* port ready change */ + PORT_IRQ_PWR_CHG = (1 << 3), /* power management change */ + PORT_IRQ_PHYRDY_CHG = (1 << 4), /* PHY ready change */ + PORT_IRQ_COMWAKE = (1 << 5), /* COMWAKE received */ + PORT_IRQ_UNK_FIS = (1 << 6), /* Unknown FIS received */ + PORT_IRQ_SDB_FIS = (1 << 11), /* SDB FIS received */ + + /* bits[27:16] are unmasked (raw) */ + PORT_IRQ_RAW_SHIFT = 16, + PORT_IRQ_MASKED_MASK = 0x7ff, + PORT_IRQ_RAW_MASK = (0x7ff << PORT_IRQ_RAW_SHIFT), + + /* ENABLE_SET/CLR specific, intr steering - 2 bit field */ + PORT_IRQ_STEER_SHIFT = 30, + PORT_IRQ_STEER_MASK = (3 << PORT_IRQ_STEER_SHIFT), + + /* PORT_CMD_ERR constants */ + PORT_CERR_DEV = 1, /* Error bit in D2H Register FIS */ + PORT_CERR_SDB = 2, /* Error bit in SDB FIS */ + PORT_CERR_DATA = 3, /* Error in data FIS not detected by dev */ + PORT_CERR_SEND = 4, /* Initial cmd FIS transmission failure */ + PORT_CERR_INCONSISTENT = 5, /* Protocol mismatch */ + PORT_CERR_DIRECTION = 6, /* Data direction mismatch */ + PORT_CERR_UNDERRUN = 7, /* Ran out of SGEs while writing */ + PORT_CERR_OVERRUN = 8, /* Ran out of SGEs while reading */ + PORT_CERR_PKT_PROT = 11, /* DIR invalid in 1st PIO setup of ATAPI */ + PORT_CERR_SGT_BOUNDARY = 16, /* PLD ecode 00 - SGT not on qword boundary */ + PORT_CERR_SGT_TGTABRT = 17, /* PLD ecode 01 - target abort */ + PORT_CERR_SGT_MSTABRT = 18, /* PLD ecode 10 - master abort */ + PORT_CERR_SGT_PCIPERR = 19, /* PLD ecode 11 - PCI parity err while fetching SGT */ + PORT_CERR_CMD_BOUNDARY = 24, /* ctrl[15:13] 001 - PRB not on qword boundary */ + PORT_CERR_CMD_TGTABRT = 25, /* ctrl[15:13] 010 - target abort */ + PORT_CERR_CMD_MSTABRT = 26, /* ctrl[15:13] 100 - master abort */ + PORT_CERR_CMD_PCIPERR = 27, /* ctrl[15:13] 110 - PCI parity err while fetching PRB */ + PORT_CERR_XFR_UNDEF = 32, /* PSD ecode 00 - undefined */ + PORT_CERR_XFR_TGTABRT = 33, /* PSD ecode 01 - target abort */ + PORT_CERR_XFR_MSGABRT = 34, /* PSD ecode 10 - master abort */ + PORT_CERR_XFR_PCIPERR = 35, /* PSD ecode 11 - PCI prity err during transfer */ + PORT_CERR_SENDSERVICE = 36, /* FIS received while sending service */ + + /* + * Other constants + */ + SGE_TRM = (1 << 31), /* Last SGE in chain */ + PRB_SOFT_RST = (1 << 7), /* Soft reset request (ign BSY?) */ + + /* board id */ + BID_SIL3124 = 0, + BID_SIL3132 = 1, + BID_SIL3131 = 2, + + IRQ_STAT_4PORTS = 0xf, +}; + +struct sil24_cmd_block { + struct sil24_prb prb; + struct sil24_sge sge[LIBATA_MAX_PRD]; +}; + +/* + * ap->private_data + * + * The preview driver always returned 0 for status. We emulate it + * here from the previous interrupt. + */ +struct sil24_port_priv { + struct sil24_cmd_block *cmd_block; /* 32 cmd blocks */ + dma_addr_t cmd_block_dma; /* DMA base addr for them */ + struct ata_taskfile tf; /* Cached taskfile registers */ +}; + +/* ap->host_set->private_data */ +struct sil24_host_priv { + void __iomem *host_base; /* global controller control (128 bytes @BAR0) */ + void __iomem *port_base; /* port registers (4 * 8192 bytes @BAR2) */ +}; + +static u8 sil24_check_status(struct ata_port *ap); +static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg); +static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); +static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf); +static void sil24_phy_reset(struct ata_port *ap); +static void sil24_qc_prep(struct ata_queued_cmd *qc); +static int sil24_qc_issue(struct ata_queued_cmd *qc); +static void sil24_irq_clear(struct ata_port *ap); +static void sil24_eng_timeout(struct ata_port *ap); +static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs); +static int sil24_port_start(struct ata_port *ap); +static void sil24_port_stop(struct ata_port *ap); +static void sil24_host_stop(struct ata_host_set *host_set); +static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); + +static struct pci_device_id sil24_pci_tbl[] = { + { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 }, + { 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 }, + { 0x1095, 0x3131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, + { 0x1095, 0x3531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 }, + { } /* terminate list */ +}; + +static struct pci_driver sil24_pci_driver = { + .name = DRV_NAME, + .id_table = sil24_pci_tbl, + .probe = sil24_init_one, + .remove = ata_pci_remove_one, /* safe? */ +}; + +static Scsi_Host_Template sil24_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .eh_strategy_handler = ata_scsi_error, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .max_sectors = ATA_MAX_SECTORS, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .bios_param = ata_std_bios_param, + .ordered_flush = 1, /* NCQ not supported yet */ +}; + +static const struct ata_port_operations sil24_ops = { + .port_disable = ata_port_disable, + + .check_status = sil24_check_status, + .check_altstatus = sil24_check_status, + .dev_select = ata_noop_dev_select, + + .tf_read = sil24_tf_read, + + .phy_reset = sil24_phy_reset, + + .qc_prep = sil24_qc_prep, + .qc_issue = sil24_qc_issue, + + .eng_timeout = sil24_eng_timeout, + + .irq_handler = sil24_interrupt, + .irq_clear = sil24_irq_clear, + + .scr_read = sil24_scr_read, + .scr_write = sil24_scr_write, + + .port_start = sil24_port_start, + .port_stop = sil24_port_stop, + .host_stop = sil24_host_stop, +}; + +/* + * Use bits 30-31 of host_flags to encode available port numbers. + * Current maxium is 4. + */ +#define SIL24_NPORTS2FLAG(nports) ((((unsigned)(nports) - 1) & 0x3) << 30) +#define SIL24_FLAG2NPORTS(flag) ((((flag) >> 30) & 0x3) + 1) + +static struct ata_port_info sil24_port_info[] = { + /* sil_3124 */ + { + .sht = &sil24_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(4), + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil24_ops, + }, + /* sil_3132 */ + { + .sht = &sil24_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(2), + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil24_ops, + }, + /* sil_3131/sil_3531 */ + { + .sht = &sil24_sht, + .host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | + ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(1), + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x3f, /* udma0-5 */ + .port_ops = &sil24_ops, + }, +}; + +static inline void sil24_update_tf(struct ata_port *ap) +{ + struct sil24_port_priv *pp = ap->private_data; + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + struct sil24_prb __iomem *prb = port; + u8 fis[6 * 4]; + + memcpy_fromio(fis, prb->fis, 6 * 4); + ata_tf_from_fis(fis, &pp->tf); +} + +static u8 sil24_check_status(struct ata_port *ap) +{ + struct sil24_port_priv *pp = ap->private_data; + return pp->tf.command; +} + +static int sil24_scr_map[] = { + [SCR_CONTROL] = 0, + [SCR_STATUS] = 1, + [SCR_ERROR] = 2, + [SCR_ACTIVE] = 3, +}; + +static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg) +{ + void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { + void __iomem *addr; + addr = scr_addr + sil24_scr_map[sc_reg] * 4; + return readl(scr_addr + sil24_scr_map[sc_reg] * 4); + } + return 0xffffffffU; +} + +static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val) +{ + void __iomem *scr_addr = (void __iomem *)ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { + void __iomem *addr; + addr = scr_addr + sil24_scr_map[sc_reg] * 4; + writel(val, scr_addr + sil24_scr_map[sc_reg] * 4); + } +} + +static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct sil24_port_priv *pp = ap->private_data; + *tf = pp->tf; +} + +static void sil24_phy_reset(struct ata_port *ap) +{ + __sata_phy_reset(ap); + /* + * No ATAPI yet. Just unconditionally indicate ATA device. + * If ATAPI device is attached, it will fail ATA_CMD_ID_ATA + * and libata core will ignore the device. + */ + if (!(ap->flags & ATA_FLAG_PORT_DISABLED)) + ap->device[0].class = ATA_DEV_ATA; +} + +static inline void sil24_fill_sg(struct ata_queued_cmd *qc, + struct sil24_cmd_block *cb) +{ + struct scatterlist *sg = qc->sg; + struct sil24_sge *sge = cb->sge; + unsigned i; + + for (i = 0; i < qc->n_elem; i++, sg++, sge++) { + sge->addr = cpu_to_le64(sg_dma_address(sg)); + sge->cnt = cpu_to_le32(sg_dma_len(sg)); + sge->flags = 0; + sge->flags = i < qc->n_elem - 1 ? 0 : cpu_to_le32(SGE_TRM); + } +} + +static void sil24_qc_prep(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct sil24_port_priv *pp = ap->private_data; + struct sil24_cmd_block *cb = pp->cmd_block + qc->tag; + struct sil24_prb *prb = &cb->prb; + + switch (qc->tf.protocol) { + case ATA_PROT_PIO: + case ATA_PROT_DMA: + case ATA_PROT_NODATA: + break; + default: + /* ATAPI isn't supported yet */ + BUG(); + } + + ata_tf_to_fis(&qc->tf, prb->fis, 0); + + if (qc->flags & ATA_QCFLAG_DMAMAP) + sil24_fill_sg(qc, cb); +} + +static int sil24_qc_issue(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + struct sil24_port_priv *pp = ap->private_data; + dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block); + + writel((u32)paddr, port + PORT_CMD_ACTIVATE); + return 0; +} + +static void sil24_irq_clear(struct ata_port *ap) +{ + /* unused */ +} + +static int __sil24_reset_controller(void __iomem *port) +{ + int cnt; + u32 tmp; + + /* Reset controller state. Is this correct? */ + writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT); + readl(port + PORT_CTRL_STAT); /* sync */ + + /* Max ~100ms */ + for (cnt = 0; cnt < 1000; cnt++) { + udelay(100); + tmp = readl(port + PORT_CTRL_STAT); + if (!(tmp & PORT_CS_DEV_RST)) + break; + } + + if (tmp & PORT_CS_DEV_RST) + return -1; + return 0; +} + +static void sil24_reset_controller(struct ata_port *ap) +{ + printk(KERN_NOTICE DRV_NAME + " ata%u: resetting controller...\n", ap->id); + if (__sil24_reset_controller((void __iomem *)ap->ioaddr.cmd_addr)) + printk(KERN_ERR DRV_NAME + " ata%u: failed to reset controller\n", ap->id); +} + +static void sil24_eng_timeout(struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (!qc) { + printk(KERN_ERR "ata%u: BUG: timeout without command\n", + ap->id); + return; + } + + /* + * hack alert! We cannot use the supplied completion + * function from inside the ->eh_strategy_handler() thread. + * libata is the only user of ->eh_strategy_handler() in + * any kernel, so the default scsi_done() assumes it is + * not being called from the SCSI EH. + */ + printk(KERN_ERR "ata%u: command timeout\n", ap->id); + qc->scsidone = scsi_finish_command; + ata_qc_complete(qc, AC_ERR_OTHER); + + sil24_reset_controller(ap); +} + +static void sil24_error_intr(struct ata_port *ap, u32 slot_stat) +{ + struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); + struct sil24_port_priv *pp = ap->private_data; + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + u32 irq_stat, cmd_err, sstatus, serror; + unsigned int err_mask; + + irq_stat = readl(port + PORT_IRQ_STAT); + writel(irq_stat, port + PORT_IRQ_STAT); /* clear irq */ + + if (!(irq_stat & PORT_IRQ_ERROR)) { + /* ignore non-completion, non-error irqs for now */ + printk(KERN_WARNING DRV_NAME + "ata%u: non-error exception irq (irq_stat %x)\n", + ap->id, irq_stat); + return; + } + + cmd_err = readl(port + PORT_CMD_ERR); + sstatus = readl(port + PORT_SSTATUS); + serror = readl(port + PORT_SERROR); + if (serror) + writel(serror, port + PORT_SERROR); + + printk(KERN_ERR DRV_NAME " ata%u: error interrupt on port%d\n" + " stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n", + ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror); + + if (cmd_err == PORT_CERR_DEV || cmd_err == PORT_CERR_SDB) { + /* + * Device is reporting error, tf registers are valid. + */ + sil24_update_tf(ap); + err_mask = ac_err_mask(pp->tf.command); + } else { + /* + * Other errors. libata currently doesn't have any + * mechanism to report these errors. Just turn on + * ATA_ERR. + */ + err_mask = AC_ERR_OTHER; + } + + if (qc) + ata_qc_complete(qc, err_mask); + + sil24_reset_controller(ap); +} + +static inline void sil24_host_intr(struct ata_port *ap) +{ + struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag); + void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr; + u32 slot_stat; + + slot_stat = readl(port + PORT_SLOT_STAT); + if (!(slot_stat & HOST_SSTAT_ATTN)) { + struct sil24_port_priv *pp = ap->private_data; + /* + * !HOST_SSAT_ATTN guarantees successful completion, + * so reading back tf registers is unnecessary for + * most commands. TODO: read tf registers for + * commands which require these values on successful + * completion (EXECUTE DEVICE DIAGNOSTIC, CHECK POWER, + * DEVICE RESET and READ PORT MULTIPLIER (any more?). + */ + sil24_update_tf(ap); + + if (qc) + ata_qc_complete(qc, ac_err_mask(pp->tf.command)); + } else + sil24_error_intr(ap, slot_stat); +} + +static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs) +{ + struct ata_host_set *host_set = dev_instance; + struct sil24_host_priv *hpriv = host_set->private_data; + unsigned handled = 0; + u32 status; + int i; + + status = readl(hpriv->host_base + HOST_IRQ_STAT); + + if (status == 0xffffffff) { + printk(KERN_ERR DRV_NAME ": IRQ status == 0xffffffff, " + "PCI fault or device removal?\n"); + goto out; + } + + if (!(status & IRQ_STAT_4PORTS)) + goto out; + + spin_lock(&host_set->lock); + + for (i = 0; i < host_set->n_ports; i++) + if (status & (1 << i)) { + struct ata_port *ap = host_set->ports[i]; + if (ap && !(ap->flags & ATA_FLAG_PORT_DISABLED)) { + sil24_host_intr(host_set->ports[i]); + handled++; + } else + printk(KERN_ERR DRV_NAME + ": interrupt from disabled port %d\n", i); + } + + spin_unlock(&host_set->lock); + out: + return IRQ_RETVAL(handled); +} + +static int sil24_port_start(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct sil24_port_priv *pp; + struct sil24_cmd_block *cb; + size_t cb_size = sizeof(*cb); + dma_addr_t cb_dma; + + pp = kmalloc(sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + memset(pp, 0, sizeof(*pp)); + + pp->tf.command = ATA_DRDY; + + cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL); + if (!cb) { + kfree(pp); + return -ENOMEM; + } + memset(cb, 0, cb_size); + + pp->cmd_block = cb; + pp->cmd_block_dma = cb_dma; + + ap->private_data = pp; + + return 0; +} + +static void sil24_port_stop(struct ata_port *ap) +{ + struct device *dev = ap->host_set->dev; + struct sil24_port_priv *pp = ap->private_data; + size_t cb_size = sizeof(*pp->cmd_block); + + dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma); + kfree(pp); +} + +static void sil24_host_stop(struct ata_host_set *host_set) +{ + struct sil24_host_priv *hpriv = host_set->private_data; + + iounmap(hpriv->host_base); + iounmap(hpriv->port_base); + kfree(hpriv); +} + +static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version = 0; + unsigned int board_id = (unsigned int)ent->driver_data; + struct ata_port_info *pinfo = &sil24_port_info[board_id]; + struct ata_probe_ent *probe_ent = NULL; + struct sil24_host_priv *hpriv = NULL; + void __iomem *host_base = NULL; + void __iomem *port_base = NULL; + int i, rc; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + + rc = pci_enable_device(pdev); + if (rc) + return rc; + + rc = pci_request_regions(pdev, DRV_NAME); + if (rc) + goto out_disable; + + rc = -ENOMEM; + /* ioremap mmio registers */ + host_base = ioremap(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + if (!host_base) + goto out_free; + port_base = ioremap(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + if (!port_base) + goto out_free; + + /* allocate & init probe_ent and hpriv */ + probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); + if (!probe_ent) + goto out_free; + + hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL); + if (!hpriv) + goto out_free; + + memset(probe_ent, 0, sizeof(*probe_ent)); + probe_ent->dev = pci_dev_to_dev(pdev); + INIT_LIST_HEAD(&probe_ent->node); + + probe_ent->sht = pinfo->sht; + probe_ent->host_flags = pinfo->host_flags; + probe_ent->pio_mask = pinfo->pio_mask; + probe_ent->udma_mask = pinfo->udma_mask; + probe_ent->port_ops = pinfo->port_ops; + probe_ent->n_ports = SIL24_FLAG2NPORTS(pinfo->host_flags); + + probe_ent->irq = pdev->irq; + probe_ent->irq_flags = SA_SHIRQ; + probe_ent->mmio_base = port_base; + probe_ent->private_data = hpriv; + + memset(hpriv, 0, sizeof(*hpriv)); + hpriv->host_base = host_base; + hpriv->port_base = port_base; + + /* + * Configure the device + */ + /* + * FIXME: This device is certainly 64-bit capable. We just + * don't know how to use it. After fixing 32bit activation in + * this function, enable 64bit masks here. + */ + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + goto out_free; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + goto out_free; + } + + /* GPIO off */ + writel(0, host_base + HOST_FLASH_CMD); + + /* Mask interrupts during initialization */ + writel(0, host_base + HOST_CTRL); + + for (i = 0; i < probe_ent->n_ports; i++) { + void __iomem *port = port_base + i * PORT_REGS_SIZE; + unsigned long portu = (unsigned long)port; + u32 tmp; + int cnt; + + probe_ent->port[i].cmd_addr = portu + PORT_PRB; + probe_ent->port[i].scr_addr = portu + PORT_SCONTROL; + + ata_std_ports(&probe_ent->port[i]); + + /* Initial PHY setting */ + writel(0x20c, port + PORT_PHY_CFG); + + /* Clear port RST */ + tmp = readl(port + PORT_CTRL_STAT); + if (tmp & PORT_CS_PORT_RST) { + writel(PORT_CS_PORT_RST, port + PORT_CTRL_CLR); + readl(port + PORT_CTRL_STAT); /* sync */ + for (cnt = 0; cnt < 10; cnt++) { + msleep(10); + tmp = readl(port + PORT_CTRL_STAT); + if (!(tmp & PORT_CS_PORT_RST)) + break; + } + if (tmp & PORT_CS_PORT_RST) + dev_printk(KERN_ERR, &pdev->dev, + "failed to clear port RST\n"); + } + + /* Zero error counters. */ + writel(0x8000, port + PORT_DECODE_ERR_THRESH); + writel(0x8000, port + PORT_CRC_ERR_THRESH); + writel(0x8000, port + PORT_HSHK_ERR_THRESH); + writel(0x0000, port + PORT_DECODE_ERR_CNT); + writel(0x0000, port + PORT_CRC_ERR_CNT); + writel(0x0000, port + PORT_HSHK_ERR_CNT); + + /* FIXME: 32bit activation? */ + writel(0, port + PORT_ACTIVATE_UPPER_ADDR); + writel(PORT_CS_32BIT_ACTV, port + PORT_CTRL_STAT); + + /* Configure interrupts */ + writel(0xffff, port + PORT_IRQ_ENABLE_CLR); + writel(PORT_IRQ_COMPLETE | PORT_IRQ_ERROR | PORT_IRQ_SDB_FIS, + port + PORT_IRQ_ENABLE_SET); + + /* Clear interrupts */ + writel(0x0fff0fff, port + PORT_IRQ_STAT); + writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); + + /* Clear port multiplier enable and resume bits */ + writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR); + + /* Reset itself */ + if (__sil24_reset_controller(port)) + dev_printk(KERN_ERR, &pdev->dev, + "failed to reset controller\n"); + } + + /* Turn on interrupts */ + writel(IRQ_STAT_4PORTS, host_base + HOST_CTRL); + + pci_set_master(pdev); + + /* FIXME: check ata_device_add return value */ + ata_device_add(probe_ent); + + kfree(probe_ent); + return 0; + + out_free: + if (host_base) + iounmap(host_base); + if (port_base) + iounmap(port_base); + kfree(probe_ent); + kfree(hpriv); + pci_release_regions(pdev); + out_disable: + pci_disable_device(pdev); + return rc; +} + +static int __init sil24_init(void) +{ + return pci_module_init(&sil24_pci_driver); +} + +static void __exit sil24_exit(void) +{ + pci_unregister_driver(&sil24_pci_driver); +} + +MODULE_AUTHOR("Tejun Heo"); +MODULE_DESCRIPTION("Silicon Image 3124/3132 SATA low-level driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, sil24_pci_tbl); + +module_init(sil24_init); +module_exit(sil24_exit); diff --git a/drivers/scsi/sata_sis.c b/drivers/scsi/sata_sis.c index e418b89c6b9d..42288be0e561 100644 --- a/drivers/scsi/sata_sis.c +++ b/drivers/scsi/sata_sis.c @@ -7,21 +7,26 @@ * * Copyright 2004 Uwe Koziolek * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available under NDA. * */ @@ -33,6 +38,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -47,7 +53,10 @@ enum { /* PCI configuration registers */ SIS_GENCTL = 0x54, /* IDE General Control register */ SIS_SCR_BASE = 0xc0, /* sata0 phy SCR registers */ - SIS_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + SIS180_SATA1_OFS = 0x10, /* offset from sata0->sata1 phy regs */ + SIS182_SATA1_OFS = 0x20, /* offset from sata0->sata1 phy regs */ + SIS_PMR = 0x90, /* port mapping register */ + SIS_PMR_COMBINED = 0x30, /* random bits */ SIS_FLAG_CFGSCR = (1 << 30), /* host flag: SCRs via PCI cfg */ @@ -62,6 +71,7 @@ static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static struct pci_device_id sis_pci_tbl[] = { { PCI_VENDOR_ID_SI, 0x180, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, { PCI_VENDOR_ID_SI, 0x181, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, + { PCI_VENDOR_ID_SI, 0x182, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sis_180 }, { } /* terminate list */ }; @@ -93,7 +103,7 @@ static Scsi_Host_Template sis_sht = { .ordered_flush = 1, }; -static struct ata_port_operations sis_ops = { +static const struct ata_port_operations sis_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -134,77 +144,111 @@ MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, sis_pci_tbl); MODULE_VERSION(DRV_VERSION); -static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg) +static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg, int device) { unsigned int addr = SIS_SCR_BASE + (4 * sc_reg); - if (port_no) - addr += SIS_SATA1_OFS; + if (port_no) { + if (device == 0x182) + addr += SIS182_SATA1_OFS; + else + addr += SIS180_SATA1_OFS; + } + return addr; } static u32 sis_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg) { struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg); - u32 val; + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg, pdev->device); + u32 val, val2 = 0; + u8 pmr; if (sc_reg == SCR_ERROR) /* doesn't exist in PCI cfg space */ return 0xffffffff; + + pci_read_config_byte(pdev, SIS_PMR, &pmr); + pci_read_config_dword(pdev, cfg_addr, &val); - return val; + + if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) + pci_read_config_dword(pdev, cfg_addr+0x10, &val2); + + return val|val2; } static void sis_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val) { struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); - unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr); + unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr, pdev->device); + u8 pmr; if (scr == SCR_ERROR) /* doesn't exist in PCI cfg space */ return; + + pci_read_config_byte(pdev, SIS_PMR, &pmr); + pci_write_config_dword(pdev, cfg_addr, val); + + if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) + pci_write_config_dword(pdev, cfg_addr+0x10, val); } static u32 sis_scr_read (struct ata_port *ap, unsigned int sc_reg) { + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u32 val, val2 = 0; + u8 pmr; + if (sc_reg > SCR_CONTROL) return 0xffffffffU; if (ap->flags & SIS_FLAG_CFGSCR) return sis_scr_cfg_read(ap, sc_reg); - return inl(ap->ioaddr.scr_addr + (sc_reg * 4)); + + pci_read_config_byte(pdev, SIS_PMR, &pmr); + + val = inl(ap->ioaddr.scr_addr + (sc_reg * 4)); + + if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) + val2 = inl(ap->ioaddr.scr_addr + (sc_reg * 4) + 0x10); + + return val | val2; } static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { + struct pci_dev *pdev = to_pci_dev(ap->host_set->dev); + u8 pmr; + if (sc_reg > SCR_CONTROL) return; + pci_read_config_byte(pdev, SIS_PMR, &pmr); + if (ap->flags & SIS_FLAG_CFGSCR) sis_scr_cfg_write(ap, sc_reg, val); - else + else { outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)); -} - -/* move to PCI layer, integrate w/ MSI stuff */ -static void pci_enable_intx(struct pci_dev *pdev) -{ - u16 pci_command; - - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_INTX_DISABLE) { - pci_command &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); + if ((pdev->device == 0x182) || (pmr & SIS_PMR_COMBINED)) + outl(val, ap->ioaddr.scr_addr + (sc_reg * 4)+0x10); } } static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static int printed_version; struct ata_probe_ent *probe_ent = NULL; int rc; u32 genctl; struct ata_port_info *ppi; int pci_dev_busy = 0; + u8 pmr; + u8 port2_start; + + if (!printed_version++) + dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -224,7 +268,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_regions; ppi = &sis_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi); + probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); if (!probe_ent) { rc = -ENOMEM; goto err_out_regions; @@ -234,7 +278,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_read_config_dword(pdev, SIS_GENCTL, &genctl); if ((genctl & GENCTL_IOMAPPED_SCR) == 0) probe_ent->host_flags |= SIS_FLAG_CFGSCR; - + /* if hardware thinks SCRs are in IO space, but there are * no IO resources assigned, change to PCI cfg space. */ @@ -246,15 +290,33 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent->host_flags |= SIS_FLAG_CFGSCR; } + pci_read_config_byte(pdev, SIS_PMR, &pmr); + if (ent->device != 0x182) { + if ((pmr & SIS_PMR_COMBINED) == 0) { + dev_printk(KERN_INFO, &pdev->dev, + "Detected SiS 180/181 chipset in SATA mode\n"); + port2_start = 64; + } + else { + dev_printk(KERN_INFO, &pdev->dev, + "Detected SiS 180/181 chipset in combined mode\n"); + port2_start=0; + } + } + else { + dev_printk(KERN_INFO, &pdev->dev, "Detected SiS 182 chipset\n"); + port2_start = 0x20; + } + if (!(probe_ent->host_flags & SIS_FLAG_CFGSCR)) { probe_ent->port[0].scr_addr = pci_resource_start(pdev, SIS_SCR_PCI_BAR); probe_ent->port[1].scr_addr = - pci_resource_start(pdev, SIS_SCR_PCI_BAR) + 64; + pci_resource_start(pdev, SIS_SCR_PCI_BAR) + port2_start; } pci_set_master(pdev); - pci_enable_intx(pdev); + pci_intx(pdev, 1); /* FIXME: check ata_device_add return value */ ata_device_add(probe_ent); diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c index 858e07185dbd..db615ff794d8 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -13,21 +13,26 @@ * This driver probably works with non-Apple versions of the * Broadcom chipset... * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available under NDA. * */ @@ -39,6 +44,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -79,6 +85,8 @@ /* Port stride */ #define K2_SATA_PORT_OFFSET 0x100 +static u8 k2_stat_check_status(struct ata_port *ap); + static u32 k2_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { @@ -97,7 +105,7 @@ static void k2_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, } -static void k2_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) +static void k2_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; @@ -131,16 +139,24 @@ static void k2_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) static void k2_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; - u16 nsect, lbal, lbam, lbah; + u16 nsect, lbal, lbam, lbah, feature; - nsect = tf->nsect = readw(ioaddr->nsect_addr); - lbal = tf->lbal = readw(ioaddr->lbal_addr); - lbam = tf->lbam = readw(ioaddr->lbam_addr); - lbah = tf->lbah = readw(ioaddr->lbah_addr); + tf->command = k2_stat_check_status(ap); tf->device = readw(ioaddr->device_addr); + feature = readw(ioaddr->error_addr); + nsect = readw(ioaddr->nsect_addr); + lbal = readw(ioaddr->lbal_addr); + lbam = readw(ioaddr->lbam_addr); + lbah = readw(ioaddr->lbah_addr); + + tf->feature = feature; + tf->nsect = nsect; + tf->lbal = lbal; + tf->lbam = lbam; + tf->lbah = lbah; if (tf->flags & ATA_TFLAG_LBA48) { - tf->hob_feature = readw(ioaddr->error_addr) >> 8; + tf->hob_feature = feature >> 8; tf->hob_nsect = nsect >> 8; tf->hob_lbal = lbal >> 8; tf->hob_lbam = lbam >> 8; @@ -195,18 +211,18 @@ static void k2_bmdma_start_mmio (struct ata_queued_cmd *qc) /* start host DMA transaction */ dmactl = readb(mmio + ATA_DMA_CMD); writeb(dmactl | ATA_DMA_START, mmio + ATA_DMA_CMD); - /* There is a race condition in certain SATA controllers that can - be seen when the r/w command is given to the controller before the + /* There is a race condition in certain SATA controllers that can + be seen when the r/w command is given to the controller before the host DMA is started. On a Read command, the controller would initiate the command to the drive even before it sees the DMA start. When there - are very fast drives connected to the controller, or when the data request + are very fast drives connected to the controller, or when the data request hits in the drive cache, there is the possibility that the drive returns a part or all of the requested data to the controller before the DMA start is issued. In this case, the controller would become confused as to what to do with the data. In the worst case when all the data is returned back to the controller, the controller could hang. In other cases it could return partial data returning in data corruption. This problem has been seen in PPC systems and can also appear - on an system with very fast disks, where the SATA controller is sitting behind a + on an system with very fast disks, where the SATA controller is sitting behind a number of bridges, and hence there is significant latency between the r/w command and the start command. */ /* issue r/w command if the access is to ATA*/ @@ -214,7 +230,7 @@ static void k2_bmdma_start_mmio (struct ata_queued_cmd *qc) ap->ops->exec_command(ap, &qc->tf); } - + static u8 k2_stat_check_status(struct ata_port *ap) { return readl((void *) ap->ioaddr.status_addr); @@ -292,7 +308,7 @@ static Scsi_Host_Template k2_sata_sht = { }; -static struct ata_port_operations k2_sata_ops = { +static const struct ata_port_operations k2_sata_ops = { .port_disable = ata_port_disable, .tf_load = k2_sata_tf_load, .tf_read = k2_sata_tf_read, @@ -313,7 +329,7 @@ static struct ata_port_operations k2_sata_ops = { .scr_write = k2_sata_scr_write, .port_start = ata_port_start, .port_stop = ata_port_stop, - .host_stop = ata_host_stop, + .host_stop = ata_pci_host_stop, }; static void k2_sata_setup_port(struct ata_ioports *port, unsigned long base) @@ -341,13 +357,13 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e static int printed_version; struct ata_probe_ent *probe_ent = NULL; unsigned long base; - void *mmio_base; + void __iomem *mmio_base; int pci_dev_busy = 0; int rc; int i; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* * If this driver happens to only be useful on Apple's K2, then @@ -387,8 +403,7 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e probe_ent->dev = pci_dev_to_dev(pdev); INIT_LIST_HEAD(&probe_ent->node); - mmio_base = ioremap(pci_resource_start(pdev, 5), - pci_resource_len(pdev, 5)); + mmio_base = pci_iomap(pdev, 5, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c index 140cea05de3f..0ec21e09f5d8 100644 --- a/drivers/scsi/sata_sx4.c +++ b/drivers/scsi/sata_sx4.c @@ -7,21 +7,26 @@ * * Copyright 2003-2004 Red Hat, Inc. * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available under NDA. * */ @@ -33,6 +38,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/sched.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -94,7 +100,7 @@ enum { PDC_DIMM1_CONTROL_OFFSET = 0x84, PDC_SDRAM_CONTROL_OFFSET = 0x88, PDC_I2C_WRITE = 0x00000000, - PDC_I2C_READ = 0x00000040, + PDC_I2C_READ = 0x00000040, PDC_I2C_START = 0x00000080, PDC_I2C_MASK_INT = 0x00000020, PDC_I2C_COMPLETE = 0x00010000, @@ -105,16 +111,16 @@ enum { PDC_DIMM_SPD_COLUMN_NUM = 4, PDC_DIMM_SPD_MODULE_ROW = 5, PDC_DIMM_SPD_TYPE = 11, - PDC_DIMM_SPD_FRESH_RATE = 12, - PDC_DIMM_SPD_BANK_NUM = 17, + PDC_DIMM_SPD_FRESH_RATE = 12, + PDC_DIMM_SPD_BANK_NUM = 17, PDC_DIMM_SPD_CAS_LATENCY = 18, - PDC_DIMM_SPD_ATTRIBUTE = 21, + PDC_DIMM_SPD_ATTRIBUTE = 21, PDC_DIMM_SPD_ROW_PRE_CHARGE = 27, - PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28, + PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28, PDC_DIMM_SPD_RAS_CAS_DELAY = 29, PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30, PDC_DIMM_SPD_SYSTEM_FREQ = 126, - PDC_CTL_STATUS = 0x08, + PDC_CTL_STATUS = 0x08, PDC_DIMM_WINDOW_CTLR = 0x0C, PDC_TIME_CONTROL = 0x3C, PDC_TIME_PERIOD = 0x40, @@ -132,7 +138,7 @@ struct pdc_port_priv { }; struct pdc_host_priv { - void *dimm_mmio; + void __iomem *dimm_mmio; unsigned int doing_hdma; unsigned int hdma_prod; @@ -152,20 +158,20 @@ static void pdc_20621_phy_reset (struct ata_port *ap); static int pdc_port_start(struct ata_port *ap); static void pdc_port_stop(struct ata_port *ap); static void pdc20621_qc_prep(struct ata_queued_cmd *qc); -static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf); -static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf); +static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); +static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc20621_host_stop(struct ata_host_set *host_set); static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); static int pdc20621_detect_dimm(struct ata_probe_ent *pe); -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, +static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, u32 subaddr, u32 *pdata); static int pdc20621_prog_dimm0(struct ata_probe_ent *pe); static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe); #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, +static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, u32 offset, u32 size); #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, +static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, u32 offset, u32 size); static void pdc20621_irq_clear(struct ata_port *ap); static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc); @@ -191,7 +197,7 @@ static Scsi_Host_Template pdc_sata_sht = { .ordered_flush = 1, }; -static struct ata_port_operations pdc_20621_ops = { +static const struct ata_port_operations pdc_20621_ops = { .port_disable = ata_port_disable, .tf_load = pdc_tf_load_mmio, .tf_read = ata_tf_read, @@ -240,13 +246,14 @@ static struct pci_driver pdc_sata_pci_driver = { static void pdc20621_host_stop(struct ata_host_set *host_set) { + struct pci_dev *pdev = to_pci_dev(host_set->dev); struct pdc_host_priv *hpriv = host_set->private_data; - void *dimm_mmio = hpriv->dimm_mmio; + void __iomem *dimm_mmio = hpriv->dimm_mmio; - iounmap(dimm_mmio); + pci_iounmap(pdev, dimm_mmio); kfree(hpriv); - ata_host_stop(host_set); + pci_iounmap(pdev, host_set->mmio_base); } static int pdc_port_start(struct ata_port *ap) @@ -446,9 +453,9 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc) struct scatterlist *sg = qc->sg; struct ata_port *ap = qc->ap; struct pdc_port_priv *pp = ap->private_data; - void *mmio = ap->host_set->mmio_base; + void __iomem *mmio = ap->host_set->mmio_base; struct pdc_host_priv *hpriv = ap->host_set->private_data; - void *dimm_mmio = hpriv->dimm_mmio; + void __iomem *dimm_mmio = hpriv->dimm_mmio; unsigned int portno = ap->port_no; unsigned int i, last, idx, total_len = 0, sgt_len; u32 *buf = (u32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ]; @@ -468,7 +475,7 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc) for (i = 0; i < last; i++) { buf[idx++] = cpu_to_le32(sg_dma_address(&sg[i])); buf[idx++] = cpu_to_le32(sg_dma_len(&sg[i])); - total_len += sg[i].length; + total_len += sg_dma_len(&sg[i]); } buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT); sgt_len = idx * 4; @@ -508,9 +515,9 @@ static void pdc20621_nodata_prep(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; struct pdc_port_priv *pp = ap->private_data; - void *mmio = ap->host_set->mmio_base; + void __iomem *mmio = ap->host_set->mmio_base; struct pdc_host_priv *hpriv = ap->host_set->private_data; - void *dimm_mmio = hpriv->dimm_mmio; + void __iomem *dimm_mmio = hpriv->dimm_mmio; unsigned int portno = ap->port_no; unsigned int i; @@ -560,7 +567,7 @@ static void __pdc20621_push_hdma(struct ata_queued_cmd *qc, { struct ata_port *ap = qc->ap; struct ata_host_set *host_set = ap->host_set; - void *mmio = host_set->mmio_base; + void __iomem *mmio = host_set->mmio_base; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -634,7 +641,7 @@ static void pdc20621_packet_start(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; struct ata_host_set *host_set = ap->host_set; unsigned int port_no = ap->port_no; - void *mmio = host_set->mmio_base; + void __iomem *mmio = host_set->mmio_base; unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); u8 seq = (u8) (port_no + 1); unsigned int port_ofs; @@ -663,8 +670,8 @@ static void pdc20621_packet_start(struct ata_queued_cmd *qc) readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */ writel(port_ofs + PDC_DIMM_ATA_PKT, - (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); VPRINTK("submitted ofs 0x%x (%u), seq %u\n", port_ofs + PDC_DIMM_ATA_PKT, port_ofs + PDC_DIMM_ATA_PKT, @@ -694,7 +701,7 @@ static int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc) static inline unsigned int pdc20621_host_intr( struct ata_port *ap, struct ata_queued_cmd *qc, unsigned int doing_hdma, - void *mmio) + void __iomem *mmio) { unsigned int port_no = ap->port_no; unsigned int port_ofs = @@ -712,7 +719,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* get drive status; clear intr; complete txn */ - ata_qc_complete(qc, ata_wait_idle(ap)); + ata_qc_complete(qc, ac_err_mask(ata_wait_idle(ap))); pdc20621_pop_hdma(qc); } @@ -741,8 +748,8 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4)); readl(mmio + PDC_20621_SEQCTL + (seq * 4)); writel(port_ofs + PDC_DIMM_ATA_PKT, - (void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); - readl((void *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + (void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); + readl((void __iomem *) ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT); } /* step two - execute ATA command */ @@ -750,7 +757,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* get drive status; clear intr; complete txn */ - ata_qc_complete(qc, ata_wait_idle(ap)); + ata_qc_complete(qc, ac_err_mask(ata_wait_idle(ap))); pdc20621_pop_hdma(qc); } handled = 1; @@ -760,7 +767,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, status = ata_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000); DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status); - ata_qc_complete(qc, status); + ata_qc_complete(qc, ac_err_mask(status)); handled = 1; } else { @@ -773,7 +780,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, static void pdc20621_irq_clear(struct ata_port *ap) { struct ata_host_set *host_set = ap->host_set; - void *mmio = host_set->mmio_base; + void __iomem *mmio = host_set->mmio_base; mmio += PDC_CHIP0_OFS; @@ -787,7 +794,7 @@ static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_re u32 mask = 0; unsigned int i, tmp, port_no; unsigned int handled = 0; - void *mmio_base; + void __iomem *mmio_base; VPRINTK("ENTER\n"); @@ -825,7 +832,8 @@ static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_re ap = host_set->ports[port_no]; tmp = mask & (1 << i); VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); - if (tmp && ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (tmp && ap && + !(ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -847,10 +855,14 @@ static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance, struct pt_re static void pdc_eng_timeout(struct ata_port *ap) { u8 drv_stat; + struct ata_host_set *host_set = ap->host_set; struct ata_queued_cmd *qc; + unsigned long flags; DPRINTK("ENTER\n"); + spin_lock_irqsave(&host_set->lock, flags); + qc = ata_qc_from_tag(ap, ap->active_tag); if (!qc) { printk(KERN_ERR "ata%u: BUG: timeout without command\n", @@ -870,7 +882,7 @@ static void pdc_eng_timeout(struct ata_port *ap) case ATA_PROT_DMA: case ATA_PROT_NODATA: printk(KERN_ERR "ata%u: command timeout\n", ap->id); - ata_qc_complete(qc, ata_wait_idle(ap) | ATA_ERR); + ata_qc_complete(qc, __ac_err_mask(ata_wait_idle(ap))); break; default: @@ -879,15 +891,16 @@ static void pdc_eng_timeout(struct ata_port *ap) printk(KERN_ERR "ata%u: unknown timeout, cmd 0x%x stat 0x%x\n", ap->id, qc->tf.command, drv_stat); - ata_qc_complete(qc, drv_stat); + ata_qc_complete(qc, ac_err_mask(drv_stat)); break; } out: + spin_unlock_irqrestore(&host_set->lock, flags); DPRINTK("EXIT\n"); } -static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { WARN_ON (tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_NODATA); @@ -895,7 +908,7 @@ static void pdc_tf_load_mmio(struct ata_port *ap, struct ata_taskfile *tf) } -static void pdc_exec_command_mmio(struct ata_port *ap, struct ata_taskfile *tf) +static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf) { WARN_ON (tf->protocol == ATA_PROT_DMA || tf->protocol == ATA_PROT_NODATA); @@ -922,23 +935,23 @@ static void pdc_sata_setup_port(struct ata_ioports *port, unsigned long base) #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; struct pdc_host_priv *hpriv = pe->private_data; - void *dimm_mmio = hpriv->dimm_mmio; + void __iomem *dimm_mmio = hpriv->dimm_mmio; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; - page_mask = 0x00; - window_size = 0x2000 * 4; /* 32K byte uchar size */ - idx = (u16) (offset / window_size); + page_mask = 0x00; + window_size = 0x2000 * 4; /* 32K byte uchar size */ + idx = (u16) (offset / window_size); writel(0x01, mmio + PDC_GENERAL_CTLR); readl(mmio + PDC_GENERAL_CTLR); @@ -947,19 +960,19 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, offset -= (idx * window_size); idx++; - dist = ((long) (window_size - (offset + size))) >= 0 ? size : + dist = ((long) (window_size - (offset + size))) >= 0 ? size : (long) (window_size - offset); - memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), + memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), dist); - psource += dist; + psource += dist; size -= dist; for (; (long) size >= (long) window_size ;) { writel(0x01, mmio + PDC_GENERAL_CTLR); readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), + memcpy_fromio((char *) psource, (char *) (dimm_mmio), window_size / 4); psource += window_size; size -= window_size; @@ -971,71 +984,70 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), + memcpy_fromio((char *) psource, (char *) (dimm_mmio), size / 4); } } #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; struct pdc_host_priv *hpriv = pe->private_data; - void *dimm_mmio = hpriv->dimm_mmio; + void __iomem *dimm_mmio = hpriv->dimm_mmio; - /* hard-code chip #0 */ + /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; - page_mask = 0x00; - window_size = 0x2000 * 4; /* 32K byte uchar size */ + page_mask = 0x00; + window_size = 0x2000 * 4; /* 32K byte uchar size */ idx = (u16) (offset / window_size); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - offset -= (idx * window_size); + offset -= (idx * window_size); idx++; dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size : (long) (window_size - offset); - memcpy_toio((char *) (dimm_mmio + offset / 4), (char *) psource, dist); + memcpy_toio(dimm_mmio + offset / 4, psource, dist); writel(0x01, mmio + PDC_GENERAL_CTLR); readl(mmio + PDC_GENERAL_CTLR); - psource += dist; + psource += dist; size -= dist; for (; (long) size >= (long) window_size ;) { writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_toio((char *) (dimm_mmio), (char *) psource, - window_size / 4); + memcpy_toio(dimm_mmio, psource, window_size / 4); writel(0x01, mmio + PDC_GENERAL_CTLR); readl(mmio + PDC_GENERAL_CTLR); psource += window_size; size -= window_size; idx ++; } - + if (size) { writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_toio((char *) (dimm_mmio), (char *) psource, size / 4); + memcpy_toio(dimm_mmio, psource, size / 4); writel(0x01, mmio + PDC_GENERAL_CTLR); readl(mmio + PDC_GENERAL_CTLR); } } -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, +static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, u32 subaddr, u32 *pdata) { - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; u32 i2creg = 0; - u32 status; + u32 status; u32 count =0; /* hard-code chip #0 */ @@ -1049,7 +1061,7 @@ static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, readl(mmio + PDC_I2C_ADDR_DATA_OFFSET); /* Write Control to perform read operation, mask int */ - writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT, + writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT, mmio + PDC_I2C_CONTROL_OFFSET); for (count = 0; count <= 1000; count ++) { @@ -1062,26 +1074,26 @@ static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, } *pdata = (status >> 8) & 0x000000ff; - return 1; + return 1; } static int pdc20621_detect_dimm(struct ata_probe_ent *pe) { u32 data=0 ; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { if (data == 100) return 100; } else return 0; - + if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { - if(data <= 0x75) + if(data <= 0x75) return 133; } else return 0; - + return 0; } @@ -1091,15 +1103,15 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) u32 spd0[50]; u32 data = 0; int size, i; - u8 bdimmsize; - void *mmio = pe->mmio_base; + u8 bdimmsize; + void __iomem *mmio = pe->mmio_base; static const struct { unsigned int reg; unsigned int ofs; } pdc_i2c_read_data [] = { - { PDC_DIMM_SPD_TYPE, 11 }, + { PDC_DIMM_SPD_TYPE, 11 }, { PDC_DIMM_SPD_FRESH_RATE, 12 }, - { PDC_DIMM_SPD_COLUMN_NUM, 4 }, + { PDC_DIMM_SPD_COLUMN_NUM, 4 }, { PDC_DIMM_SPD_ATTRIBUTE, 21 }, { PDC_DIMM_SPD_ROW_NUM, 3 }, { PDC_DIMM_SPD_BANK_NUM, 17 }, @@ -1108,7 +1120,7 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) { PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 }, { PDC_DIMM_SPD_RAS_CAS_DELAY, 29 }, { PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 }, - { PDC_DIMM_SPD_CAS_LATENCY, 18 }, + { PDC_DIMM_SPD_CAS_LATENCY, 18 }, }; /* hard-code chip #0 */ @@ -1116,17 +1128,17 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) for(i=0; i<ARRAY_SIZE(pdc_i2c_read_data); i++) pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, - pdc_i2c_read_data[i].reg, + pdc_i2c_read_data[i].reg, &spd0[pdc_i2c_read_data[i].ofs]); - + data |= (spd0[4] - 8) | ((spd0[21] != 0) << 3) | ((spd0[3]-11) << 4); - data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) | + data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) | ((((spd0[27] + 9) / 10) - 1) << 8) ; - data |= (((((spd0[29] > spd0[28]) - ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10; + data |= (((((spd0[29] > spd0[28]) + ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10; data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12; - - if (spd0[18] & 0x08) + + if (spd0[18] & 0x08) data |= ((0x03) << 14); else if (spd0[18] & 0x04) data |= ((0x02) << 14); @@ -1135,7 +1147,7 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) else data |= (0 << 14); - /* + /* Calculate the size of bDIMMSize (power of 2) and merge the DIMM size by program start/end address. */ @@ -1145,9 +1157,9 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) data |= (((size / 16) - 1) << 16); data |= (0 << 23); data |= 8; - writel(data, mmio + PDC_DIMM0_CONTROL_OFFSET); + writel(data, mmio + PDC_DIMM0_CONTROL_OFFSET); readl(mmio + PDC_DIMM0_CONTROL_OFFSET); - return size; + return size; } @@ -1155,7 +1167,7 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) { u32 data, spd0; int error, i; - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1167,12 +1179,12 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) Refresh Enable (bit 17) */ - data = 0x022259F1; + data = 0x022259F1; writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); readl(mmio + PDC_SDRAM_CONTROL_OFFSET); /* Turn on for ECC */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { data |= (0x01 << 16); @@ -1186,22 +1198,22 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) data |= (1<<19); writel(data, mmio + PDC_SDRAM_CONTROL_OFFSET); - error = 1; + error = 1; for (i = 1; i <= 10; i++) { /* polling ~5 secs */ data = readl(mmio + PDC_SDRAM_CONTROL_OFFSET); if (!(data & (1<<19))) { error = 0; - break; + break; } msleep(i*100); } return error; } - + static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) { - int speed, size, length; + int speed, size, length; u32 addr,spd0,pci_status; u32 tmp=0; u32 time_period=0; @@ -1209,7 +1221,7 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) u32 ticks=0; u32 clock=0; u32 fparam=0; - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1228,7 +1240,7 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) /* Wait 3 seconds */ msleep(3000); - /* + /* When timer is enabled, counter is decreased every internal clock cycle. */ @@ -1236,24 +1248,24 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) tcount = readl(mmio + PDC_TIME_COUNTER); VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount); - /* + /* If SX4 is on PCI-X bus, after 3 seconds, the timer counter register should be >= (0xffffffff - 3x10^8). */ if(tcount >= PCI_X_TCOUNT) { ticks = (time_period - tcount); VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks); - + clock = (ticks / 300000); VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock); - + clock = (clock * 33); VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock); /* PLL F Param (bit 22:16) */ fparam = (1400000 / clock) - 2; VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam); - + /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */ pci_status = (0x8a001824 | (fparam << 16)); } else @@ -1264,21 +1276,21 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) writel(pci_status, mmio + PDC_CTL_STATUS); readl(mmio + PDC_CTL_STATUS); - /* + /* Read SPD of DIMM by I2C interface, and program the DIMM Module Controller. */ if (!(speed = pdc20621_detect_dimm(pe))) { - printk(KERN_ERR "Detect Local DIMM Fail\n"); + printk(KERN_ERR "Detect Local DIMM Fail\n"); return 1; /* DIMM error */ } VPRINTK("Local DIMM Speed = %d\n", speed); - /* Programming DIMM0 Module Control Register (index_CID0:80h) */ + /* Programming DIMM0 Module Control Register (index_CID0:80h) */ size = pdc20621_prog_dimm0(pe); VPRINTK("Local DIMM Size = %dMB\n",size); - /* Programming DIMM Module Global Control Register (index_CID0:88h) */ + /* Programming DIMM Module Global Control Register (index_CID0:88h) */ if (pdc20621_prog_dimm_global(pe)) { printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); return 1; @@ -1297,30 +1309,30 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40); pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, + pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40); pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); - printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], + printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); } #endif /* ECC initiliazation. */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { VPRINTK("Start ECC initialization\n"); addr = 0; length = size * 1024 * 1024; while (addr < length) { - pdc20621_put_to_dimm(pe, (void *) &tmp, addr, + pdc20621_put_to_dimm(pe, (void *) &tmp, addr, sizeof(u32)); addr += sizeof(u32); } @@ -1333,7 +1345,7 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) static void pdc_20621_init(struct ata_probe_ent *pe) { u32 tmp; - void *mmio = pe->mmio_base; + void __iomem *mmio = pe->mmio_base; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1366,14 +1378,15 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * static int printed_version; struct ata_probe_ent *probe_ent = NULL; unsigned long base; - void *mmio_base, *dimm_mmio = NULL; + void __iomem *mmio_base; + void __iomem *dimm_mmio = NULL; struct pdc_host_priv *hpriv = NULL; unsigned int board_idx = (unsigned int) ent->driver_data; int pci_dev_busy = 0; int rc; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); /* * If this driver happens to only be useful on Apple's K2, then @@ -1406,8 +1419,7 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * probe_ent->dev = pci_dev_to_dev(pdev); INIT_LIST_HEAD(&probe_ent->node); - mmio_base = ioremap(pci_resource_start(pdev, 3), - pci_resource_len(pdev, 3)); + mmio_base = pci_iomap(pdev, 3, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; @@ -1421,8 +1433,7 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * } memset(hpriv, 0, sizeof(*hpriv)); - dimm_mmio = ioremap(pci_resource_start(pdev, 4), - pci_resource_len(pdev, 4)); + dimm_mmio = pci_iomap(pdev, 4, 0); if (!dimm_mmio) { kfree(hpriv); rc = -ENOMEM; @@ -1469,9 +1480,9 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * err_out_iounmap_dimm: /* only get to this label if 20621 */ kfree(hpriv); - iounmap(dimm_mmio); + pci_iounmap(pdev, dimm_mmio); err_out_iounmap: - iounmap(mmio_base); + pci_iounmap(pdev, mmio_base); err_out_free_ent: kfree(probe_ent); err_out_regions: diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c index a71fb54eebd3..a5e245c098e1 100644 --- a/drivers/scsi/sata_uli.c +++ b/drivers/scsi/sata_uli.c @@ -1,21 +1,26 @@ /* * sata_uli.c - ULi Electronics SATA * - * The contents of this file are subject to the Open - * Software License version 1.1 that can be found at - * http://www.opensource.org/licenses/osl-1.1.txt and is included herein - * by reference. * - * Alternatively, the contents of this file may be used under the terms - * of the GNU General Public License version 2 (the "GPL") as distributed - * in the kernel source COPYING file, in which case the provisions of - * the GPL are applicable instead of the above. If you wish to allow - * the use of your version of this file only under the terms of the - * GPL and not to allow others to use your version of this file under - * the OSL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the GPL. - * If you do not delete the provisions above, a recipient may use your - * version of this file under either the OSL or the GPL. + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available under NDA. * */ @@ -27,6 +32,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -85,7 +91,7 @@ static Scsi_Host_Template uli_sht = { .ordered_flush = 1, }; -static struct ata_port_operations uli_ops = { +static const struct ata_port_operations uli_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, @@ -120,8 +126,8 @@ static struct ata_port_info uli_port_info = { .sht = &uli_sht, .host_flags = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET | ATA_FLAG_NO_LEGACY, - .pio_mask = 0x03, //support pio mode 4 (FIXME) - .udma_mask = 0x7f, //support udma mode 6 + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &uli_ops, }; @@ -171,26 +177,18 @@ static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) uli_scr_cfg_write(ap, sc_reg, val); } -/* move to PCI layer, integrate w/ MSI stuff */ -static void pci_enable_intx(struct pci_dev *pdev) -{ - u16 pci_command; - - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_INTX_DISABLE) { - pci_command &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_command); - } -} - static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static int printed_version; struct ata_probe_ent *probe_ent; struct ata_port_info *ppi; int rc; unsigned int board_idx = (unsigned int) ent->driver_data; int pci_dev_busy = 0; + if (!printed_version++) + dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); + rc = pci_enable_device(pdev); if (rc) return rc; @@ -209,12 +207,12 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_regions; ppi = &uli_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi); + probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); if (!probe_ent) { rc = -ENOMEM; goto err_out_regions; } - + switch (board_idx) { case uli_5287: probe_ent->port[0].scr_addr = ULI5287_BASE; @@ -255,7 +253,7 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } pci_set_master(pdev); - pci_enable_intx(pdev); + pci_intx(pdev, 1); /* FIXME: check ata_device_add return value */ ata_device_add(probe_ent); diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c index f43183c19a12..b3ecdbe400e9 100644 --- a/drivers/scsi/sata_via.c +++ b/drivers/scsi/sata_via.c @@ -1,34 +1,38 @@ /* - sata_via.c - VIA Serial ATA controllers - - Maintained by: Jeff Garzik <jgarzik@pobox.com> - Please ALWAYS copy linux-ide@vger.kernel.org + * sata_via.c - VIA Serial ATA controllers + * + * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Please ALWAYS copy linux-ide@vger.kernel.org on emails. - - Copyright 2003-2004 Red Hat, Inc. All rights reserved. - Copyright 2003-2004 Jeff Garzik - - The contents of this file are subject to the Open - Software License version 1.1 that can be found at - http://www.opensource.org/licenses/osl-1.1.txt and is included herein - by reference. - - Alternatively, the contents of this file may be used under the terms - of the GNU General Public License version 2 (the "GPL") as distributed - in the kernel source COPYING file, in which case the provisions of - the GPL are applicable instead of the above. If you wish to allow - the use of your version of this file only under the terms of the - GPL and not to allow others to use your version of this file under - the OSL, indicate your decision by deleting the provisions above and - replace them with the notice and other provisions required by the GPL. - If you do not delete the provisions above, a recipient may use your - version of this file under either the OSL or the GPL. - - ---------------------------------------------------------------------- - - To-do list: - * VT6421 PATA support - + * + * Copyright 2003-2004 Red Hat, Inc. All rights reserved. + * Copyright 2003-2004 Jeff Garzik + * + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Hardware documentation available under NDA. + * + * + * To-do list: + * - VT6421 PATA support + * */ #include <linux/kernel.h> @@ -37,6 +41,7 @@ #include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -105,7 +110,7 @@ static Scsi_Host_Template svia_sht = { .ordered_flush = 1, }; -static struct ata_port_operations svia_sata_ops = { +static const struct ata_port_operations svia_sata_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, @@ -208,7 +213,7 @@ static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) struct ata_probe_ent *probe_ent; struct ata_port_info *ppi = &svia_port_info; - probe_ent = ata_pci_init_native_mode(pdev, &ppi); + probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); if (!probe_ent) return NULL; @@ -255,15 +260,15 @@ static void svia_configure(struct pci_dev *pdev) u8 tmp8; pci_read_config_byte(pdev, PCI_INTERRUPT_LINE, &tmp8); - printk(KERN_INFO DRV_NAME "(%s): routed to hard irq line %d\n", - pci_name(pdev), + dev_printk(KERN_INFO, &pdev->dev, "routed to hard irq line %d\n", (int) (tmp8 & 0xf0) == 0xf0 ? 0 : tmp8 & 0x0f); /* make sure SATA channels are enabled */ pci_read_config_byte(pdev, SATA_CHAN_ENAB, &tmp8); if ((tmp8 & ALL_PORTS) != ALL_PORTS) { - printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channels (0x%x)\n", - pci_name(pdev), (int) tmp8); + dev_printk(KERN_DEBUG, &pdev->dev, + "enabling SATA channels (0x%x)\n", + (int) tmp8); tmp8 |= ALL_PORTS; pci_write_config_byte(pdev, SATA_CHAN_ENAB, tmp8); } @@ -271,8 +276,9 @@ static void svia_configure(struct pci_dev *pdev) /* make sure interrupts for each channel sent to us */ pci_read_config_byte(pdev, SATA_INT_GATE, &tmp8); if ((tmp8 & ALL_PORTS) != ALL_PORTS) { - printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel interrupts (0x%x)\n", - pci_name(pdev), (int) tmp8); + dev_printk(KERN_DEBUG, &pdev->dev, + "enabling SATA channel interrupts (0x%x)\n", + (int) tmp8); tmp8 |= ALL_PORTS; pci_write_config_byte(pdev, SATA_INT_GATE, tmp8); } @@ -280,8 +286,9 @@ static void svia_configure(struct pci_dev *pdev) /* make sure native mode is enabled */ pci_read_config_byte(pdev, SATA_NATIVE_MODE, &tmp8); if ((tmp8 & NATIVE_MODE_ALL) != NATIVE_MODE_ALL) { - printk(KERN_DEBUG DRV_NAME "(%s): enabling SATA channel native mode (0x%x)\n", - pci_name(pdev), (int) tmp8); + dev_printk(KERN_DEBUG, &pdev->dev, + "enabling SATA channel native mode (0x%x)\n", + (int) tmp8); tmp8 |= NATIVE_MODE_ALL; pci_write_config_byte(pdev, SATA_NATIVE_MODE, tmp8); } @@ -299,7 +306,7 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) u8 tmp8; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -314,8 +321,9 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (board_id == vt6420) { pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8); if (tmp8 & SATA_2DEV) { - printk(KERN_ERR DRV_NAME "(%s): SATA master/slave not supported (0x%x)\n", - pci_name(pdev), (int) tmp8); + dev_printk(KERN_ERR, &pdev->dev, + "SATA master/slave not supported (0x%x)\n", + (int) tmp8); rc = -EIO; goto err_out_regions; } @@ -328,10 +336,11 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) for (i = 0; i < ARRAY_SIZE(svia_bar_sizes); i++) if ((pci_resource_start(pdev, i) == 0) || (pci_resource_len(pdev, i) < bar_sizes[i])) { - printk(KERN_ERR DRV_NAME "(%s): invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n", - pci_name(pdev), i, - pci_resource_start(pdev, i), - pci_resource_len(pdev, i)); + dev_printk(KERN_ERR, &pdev->dev, + "invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n", + i, + pci_resource_start(pdev, i), + pci_resource_len(pdev, i)); rc = -ENODEV; goto err_out_regions; } @@ -347,10 +356,9 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) probe_ent = vt6420_init_probe_ent(pdev); else probe_ent = vt6421_init_probe_ent(pdev); - + if (!probe_ent) { - printk(KERN_ERR DRV_NAME "(%s): out of memory\n", - pci_name(pdev)); + dev_printk(KERN_ERR, &pdev->dev, "out of memory\n"); rc = -ENOMEM; goto err_out_regions; } diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index c5e09dc6f3de..bb84ba0c7e83 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -9,9 +9,29 @@ * * Bits from Jeff Garzik, Copyright RedHat, Inc. * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. + * + * 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 + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * libata documentation is available via 'make {ps|pdf}docs', + * as Documentation/DocBook/libata.* + * + * Vitesse hardware documentation presumably available under NDA. + * Intel 31244 (same hardware interface) documentation presumably + * available from http://developer.intel.com/ + * */ #include <linux/kernel.h> @@ -22,6 +42,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/dma-mapping.h> +#include <linux/device.h> #include "scsi.h" #include <scsi/scsi_host.h> #include <linux/libata.h> @@ -66,7 +87,7 @@ static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { if (sc_reg > SCR_CONTROL) return 0xffffffffU; - return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + return readl((void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -75,16 +96,16 @@ static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, { if (sc_reg > SCR_CONTROL) return; - writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); + writel(val, (void __iomem *) ap->ioaddr.scr_addr + (sc_reg * 4)); } static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) { - unsigned long mask_addr; + void __iomem *mask_addr; u8 mask; - mask_addr = (unsigned long) ap->host_set->mmio_base + + mask_addr = ap->host_set->mmio_base + VSC_SATA_INT_MASK_OFFSET + ap->port_no; mask = readb(mask_addr); if (ctl & ATA_NIEN) @@ -95,7 +116,7 @@ static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) } -static void vsc_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) +static void vsc_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; @@ -133,16 +154,24 @@ static void vsc_sata_tf_load(struct ata_port *ap, struct ata_taskfile *tf) static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) { struct ata_ioports *ioaddr = &ap->ioaddr; - u16 nsect, lbal, lbam, lbah; + u16 nsect, lbal, lbam, lbah, feature; - nsect = tf->nsect = readw(ioaddr->nsect_addr); - lbal = tf->lbal = readw(ioaddr->lbal_addr); - lbam = tf->lbam = readw(ioaddr->lbam_addr); - lbah = tf->lbah = readw(ioaddr->lbah_addr); + tf->command = ata_check_status(ap); tf->device = readw(ioaddr->device_addr); + feature = readw(ioaddr->error_addr); + nsect = readw(ioaddr->nsect_addr); + lbal = readw(ioaddr->lbal_addr); + lbam = readw(ioaddr->lbam_addr); + lbah = readw(ioaddr->lbah_addr); + + tf->feature = feature; + tf->nsect = nsect; + tf->lbal = lbal; + tf->lbam = lbam; + tf->lbah = lbah; if (tf->flags & ATA_TFLAG_LBA48) { - tf->hob_feature = readb(ioaddr->error_addr); + tf->hob_feature = feature >> 8; tf->hob_nsect = nsect >> 8; tf->hob_lbal = lbal >> 8; tf->hob_lbam = lbam >> 8; @@ -173,7 +202,8 @@ static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance, struct ata_port *ap; ap = host_set->ports[i]; - if (ap && (!(ap->flags & ATA_FLAG_PORT_DISABLED))) { + if (ap && !(ap->flags & + (ATA_FLAG_PORT_DISABLED|ATA_FLAG_NOINTR))) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->active_tag); @@ -210,7 +240,7 @@ static Scsi_Host_Template vsc_sata_sht = { }; -static struct ata_port_operations vsc_sata_ops = { +static const struct ata_port_operations vsc_sata_ops = { .port_disable = ata_port_disable, .tf_load = vsc_sata_tf_load, .tf_read = vsc_sata_tf_read, @@ -231,7 +261,7 @@ static struct ata_port_operations vsc_sata_ops = { .scr_write = vsc_sata_scr_write, .port_start = ata_port_start, .port_stop = ata_port_stop, - .host_stop = ata_host_stop, + .host_stop = ata_pci_host_stop, }; static void __devinit vsc_sata_setup_port(struct ata_ioports *port, unsigned long base) @@ -262,11 +292,11 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d struct ata_probe_ent *probe_ent = NULL; unsigned long base; int pci_dev_busy = 0; - void *mmio_base; + void __iomem *mmio_base; int rc; if (!printed_version++) - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); rc = pci_enable_device(pdev); if (rc) @@ -305,8 +335,7 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d probe_ent->dev = pci_dev_to_dev(pdev); INIT_LIST_HEAD(&probe_ent->node); - mmio_base = ioremap(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); + mmio_base = pci_iomap(pdev, 0, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; @@ -342,7 +371,7 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d pci_set_master(pdev); - /* + /* * Config offset 0x98 is "Extended Control and Status Register 0" * Default value is (1 << 28). All bits except bit 28 are reserved in * DPA mode. If bit 28 is set, LED 0 reflects all ports' activity. diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index d14523d7e449..a5711d545d71 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -130,7 +130,7 @@ EXPORT_SYMBOL(scsi_device_types); * Returns: Pointer to request block. */ struct scsi_request *scsi_allocate_request(struct scsi_device *sdev, - int gfp_mask) + gfp_t gfp_mask) { const int offset = ALIGN(sizeof(struct scsi_request), 4); const int size = offset + sizeof(struct request); @@ -196,7 +196,7 @@ struct scsi_host_cmd_pool { unsigned int users; char *name; unsigned int slab_flags; - unsigned int gfp_mask; + gfp_t gfp_mask; }; static struct scsi_host_cmd_pool scsi_cmd_pool = { @@ -213,7 +213,7 @@ static struct scsi_host_cmd_pool scsi_cmd_dma_pool = { static DECLARE_MUTEX(host_cmd_pool_mutex); static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, - int gfp_mask) + gfp_t gfp_mask) { struct scsi_cmnd *cmd; @@ -245,7 +245,7 @@ static struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, * * Returns: The allocated scsi command structure. */ -struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, int gfp_mask) +struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask) { struct scsi_cmnd *cmd; @@ -268,6 +268,7 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, int gfp_mask) } else put_device(&dev->sdev_gendev); + cmd->jiffies_at_alloc = jiffies; return cmd; } EXPORT_SYMBOL(scsi_get_command); @@ -627,7 +628,7 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd) spin_lock_irqsave(host->host_lock, flags); scsi_cmd_get_serial(host, cmd); - if (unlikely(test_bit(SHOST_CANCEL, &host->shost_state))) { + if (unlikely(host->shost_state == SHOST_DEL)) { cmd->result = (DID_NO_CONNECT << 16); scsi_done(cmd); } else { @@ -798,9 +799,23 @@ static void scsi_softirq(struct softirq_action *h) while (!list_empty(&local_q)) { struct scsi_cmnd *cmd = list_entry(local_q.next, struct scsi_cmnd, eh_entry); + /* The longest time any command should be outstanding is the + * per command timeout multiplied by the number of retries. + * + * For a typical command, this is 2.5 minutes */ + unsigned long wait_for + = cmd->allowed * cmd->timeout_per_command; list_del_init(&cmd->eh_entry); disposition = scsi_decide_disposition(cmd); + if (disposition != SUCCESS && + time_before(cmd->jiffies_at_alloc + wait_for, jiffies)) { + dev_printk(KERN_ERR, &cmd->device->sdev_gendev, + "timing out command, waited %lus\n", + wait_for/HZ); + disposition = SUCCESS; + } + scsi_log_completion(cmd, disposition); switch (disposition) { case SUCCESS: @@ -1250,9 +1265,8 @@ int scsi_device_cancel(struct scsi_device *sdev, int recovery) list_for_each_safe(lh, lh_sf, &active_list) { scmd = list_entry(lh, struct scsi_cmnd, eh_entry); list_del_init(lh); - if (recovery) { - scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD); - } else { + if (recovery && + !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)) { scmd->result = (DID_ABORT << 16); scsi_finish_command(scmd); } diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 6121dc1bfada..e69477d1889b 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -110,10 +110,12 @@ static struct { {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, + {"transtec", "T5008", "0001", BLIST_NOREPORTLUN }, {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */ {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* locks up */ + {"", "Scanner", "1.80", BLIST_NOLUN}, /* responds to all lun */ /* * Other types of devices that have special flags. @@ -135,7 +137,7 @@ static struct { {"COMPAQ", "MSA1000 VOLUME", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, {"COMPAQ", "HSV110", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD}, {"DDN", "SAN DataDirector", "*", BLIST_SPARSELUN}, - {"DEC", "HSG80", NULL, BLIST_SPARSELUN | BLIST_NOSTARTONADD}, + {"DEC", "HSG80", NULL, BLIST_REPORTLUN2 | BLIST_NOSTARTONADD}, {"DELL", "PV660F", NULL, BLIST_SPARSELUN}, {"DELL", "PV660F PSEUDO", NULL, BLIST_SPARSELUN}, {"DELL", "PSEUDO DEVICE .", NULL, BLIST_SPARSELUN}, /* Dell PV 530F */ @@ -183,6 +185,7 @@ static struct { {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-624X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, {"SanDisk", "ImageMate CF-SD1", NULL, BLIST_FORCELUN}, {"SEAGATE", "ST34555N", "0930", BLIST_NOTQ}, /* Chokes on tagged INQUIRY */ @@ -191,6 +194,7 @@ static struct { {"SGI", "RAID5", "*", BLIST_SPARSELUN}, {"SGI", "TP9100", "*", BLIST_REPORTLUN2}, {"SGI", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, + {"IBM", "Universal Xport", "*", BLIST_NO_ULD_ATTACH}, {"SMSC", "USB 2 HS-CF", NULL, BLIST_SPARSELUN | BLIST_INQUIRY_36}, {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 0fc8b48f052b..52b348c36d56 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -20,6 +20,7 @@ #include <linux/string.h> #include <linux/slab.h> #include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/interrupt.h> #include <linux/blkdev.h> #include <linux/delay.h> @@ -49,7 +50,7 @@ void scsi_eh_wakeup(struct Scsi_Host *shost) { if (shost->host_busy == shost->host_failed) { - up(shost->eh_wait); + wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread\n")); } @@ -67,19 +68,24 @@ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) { struct Scsi_Host *shost = scmd->device->host; unsigned long flags; + int ret = 0; - if (shost->eh_wait == NULL) + if (!shost->ehandler) return 0; spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RECOVERY)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) + goto out_unlock; + ret = 1; scmd->eh_eflags |= eh_flag; list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); - set_bit(SHOST_RECOVERY, &shost->shost_state); shost->host_failed++; scsi_eh_wakeup(shost); + out_unlock: spin_unlock_irqrestore(shost->host_lock, flags); - return 1; + return ret; } /** @@ -115,7 +121,6 @@ void scsi_add_timer(struct scsi_cmnd *scmd, int timeout, add_timer(&scmd->eh_timeout); } -EXPORT_SYMBOL(scsi_add_timer); /** * scsi_delete_timer - Delete/cancel timer for a given function. @@ -143,7 +148,6 @@ int scsi_delete_timer(struct scsi_cmnd *scmd) return rtn; } -EXPORT_SYMBOL(scsi_delete_timer); /** * scsi_times_out - Timeout function for normal scsi commands. @@ -177,8 +181,8 @@ void scsi_times_out(struct scsi_cmnd *scmd) } if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { - panic("Error handler thread not present at %p %p %s %d", - scmd, scmd->device->host, __FILE__, __LINE__); + scmd->result |= DID_TIME_OUT << 16; + __scsi_done(scmd); } } @@ -197,7 +201,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev) { int online; - wait_event(sdev->host->host_wait, (!test_bit(SHOST_RECOVERY, &sdev->host->shost_state))); + wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host)); online = scsi_device_online(sdev); @@ -775,9 +779,11 @@ retry_tur: __FUNCTION__, scmd, rtn)); if (rtn == SUCCESS) return 0; - else if (rtn == NEEDS_RETRY) + else if (rtn == NEEDS_RETRY) { if (retry_cnt--) goto retry_tur; + return 0; + } return 1; } @@ -1439,6 +1445,7 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) static void scsi_restart_operations(struct Scsi_Host *shost) { struct scsi_device *sdev; + unsigned long flags; /* * If the door was locked, we need to insert a door lock request @@ -1458,7 +1465,11 @@ static void scsi_restart_operations(struct Scsi_Host *shost) SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", __FUNCTION__)); - clear_bit(SHOST_RECOVERY, &shost->shost_state); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RUNNING)) + if (scsi_host_set_state(shost, SHOST_CANCEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL)); + spin_unlock_irqrestore(shost->host_lock, flags); wake_up(&shost->host_wait); @@ -1580,50 +1591,31 @@ int scsi_error_handler(void *data) { struct Scsi_Host *shost = (struct Scsi_Host *) data; int rtn; - DECLARE_MUTEX_LOCKED(sem); - - /* - * Flush resources - */ - - daemonize("scsi_eh_%d", shost->host_no); current->flags |= PF_NOFREEZE; - shost->eh_wait = &sem; - shost->ehandler = current; - + /* - * Wake up the thread that created us. + * Note - we always use TASK_INTERRUPTIBLE even if the module + * was loaded as part of the kernel. The reason is that + * UNINTERRUPTIBLE would cause this thread to be counted in + * the load average as a running process, and an interruptible + * wait doesn't. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of" - " scsi_eh_%d\n",shost->host_no)); - - complete(shost->eh_notify); - - while (1) { - /* - * If we get a signal, it means we are supposed to go - * away and die. This typically happens if the user is - * trying to unload a module. - */ - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" - " scsi_eh_%d" - " sleeping\n",shost->host_no)); - - /* - * Note - we always use down_interruptible with the semaphore - * even if the module was loaded as part of the kernel. The - * reason is that down() will cause this thread to be counted - * in the load average as a running process, and down - * interruptible doesn't. Given that we need to allow this - * thread to die if the driver was loaded as a module, using - * semaphores isn't unreasonable. - */ - down_interruptible(&sem); - if (shost->eh_kill) - break; + set_current_state(TASK_INTERRUPTIBLE); + while (!kthread_should_stop()) { + if (shost->host_failed == 0 || + shost->host_failed != shost->host_busy) { + SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" + " scsi_eh_%d" + " sleeping\n", + shost->host_no)); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + continue; + } + __set_current_state(TASK_RUNNING); SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" " scsi_eh_%d waking" " up\n",shost->host_no)); @@ -1650,32 +1642,18 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - + set_current_state(TASK_INTERRUPTIBLE); } + __set_current_state(TASK_RUNNING); + SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d" " exiting\n",shost->host_no)); /* * Make sure that nobody tries to wake us up again. */ - shost->eh_wait = NULL; - - /* - * Knock this down too. From this point on, the host is flying - * without a pilot. If this is because the module is being unloaded, - * that's fine. If the user sent a signal to this thing, we are - * potentially in real danger. - */ - shost->eh_active = 0; shost->ehandler = NULL; - - /* - * If anyone is waiting for us to exit (i.e. someone trying to unload - * a driver), then wake up that process to let them know we are on - * the way out the door. - */ - complete_and_exit(shost->eh_notify, 0); return 0; } @@ -1846,12 +1824,16 @@ EXPORT_SYMBOL(scsi_reset_provider); int scsi_normalize_sense(const u8 *sense_buffer, int sb_len, struct scsi_sense_hdr *sshdr) { - if (!sense_buffer || !sb_len || (sense_buffer[0] & 0x70) != 0x70) + if (!sense_buffer || !sb_len) return 0; memset(sshdr, 0, sizeof(struct scsi_sense_hdr)); sshdr->response_code = (sense_buffer[0] & 0x7f); + + if (!scsi_sense_valid(sshdr)) + return 0; + if (sshdr->response_code >= 0x72) { /* * descriptor format diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index 7a6b530115ac..6a3f6aae8a97 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -30,20 +30,20 @@ #define MAX_BUF PAGE_SIZE -/* - * If we are told to probe a host, we will return 0 if the host is not - * present, 1 if the host is present, and will return an identifying - * string at *arg, if arg is non null, filling to the length stored at - * (int *) arg +/** + * ioctl_probe -- return host identification + * @host: host to identify + * @buffer: userspace buffer for identification + * + * Return an identifying string at @buffer, if @buffer is non-NULL, filling + * to the length stored at * (int *) @buffer. */ - static int ioctl_probe(struct Scsi_Host *host, void __user *buffer) { unsigned int len, slen; const char *string; - int temp = host->hostt->present; - if (temp && buffer) { + if (buffer) { if (get_user(len, (unsigned int __user *) buffer)) return -EFAULT; @@ -59,7 +59,7 @@ static int ioctl_probe(struct Scsi_Host *host, void __user *buffer) return -EFAULT; } } - return temp; + return 1; } /* @@ -88,25 +88,18 @@ static int ioctl_probe(struct Scsi_Host *host, void __user *buffer) static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, int timeout, int retries) { - struct scsi_request *sreq; int result; struct scsi_sense_hdr sshdr; SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", *cmd)); - sreq = scsi_allocate_request(sdev, GFP_KERNEL); - if (!sreq) { - printk(KERN_WARNING "SCSI internal ioctl failed, no memory\n"); - return -ENOMEM; - } - - sreq->sr_data_direction = DMA_NONE; - scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries); + result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, + &sshdr, timeout, retries); - SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", sreq->sr_result)); + SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", result)); - if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && - (scsi_request_normalize_sense(sreq, &sshdr))) { + if ((driver_byte(result) & DRIVER_SENSE) && + (scsi_sense_valid(&sshdr))) { switch (sshdr.sense_key) { case ILLEGAL_REQUEST: if (cmd[0] == ALLOW_MEDIUM_REMOVAL) @@ -125,7 +118,7 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, case UNIT_ATTENTION: if (sdev->removable) { sdev->changed = 1; - sreq->sr_result = 0; /* This is no longer considered an error */ + result = 0; /* This is no longer considered an error */ break; } default: /* Fall through for non-removable media */ @@ -135,15 +128,13 @@ static int ioctl_internal_command(struct scsi_device *sdev, char *cmd, sdev->channel, sdev->id, sdev->lun, - sreq->sr_result); - scsi_print_req_sense(" ", sreq); + result); + scsi_print_sense_hdr(" ", &sshdr); break; } } - result = sreq->sr_result; SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n")); - scsi_release_request(sreq); return result; } @@ -208,13 +199,14 @@ int scsi_ioctl_send_command(struct scsi_device *sdev, { char *buf; unsigned char cmd[MAX_COMMAND_SIZE]; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; char __user *cmd_in; - struct scsi_request *sreq; unsigned char opcode; unsigned int inlen, outlen, cmdlen; unsigned int needed, buf_needed; int timeout, retries, result; - int data_direction, gfp_mask = GFP_KERNEL; + int data_direction; + gfp_t gfp_mask = GFP_KERNEL; if (!sic) return -EINVAL; @@ -321,31 +313,23 @@ int scsi_ioctl_send_command(struct scsi_device *sdev, break; } - sreq = scsi_allocate_request(sdev, GFP_KERNEL); - if (!sreq) { - result = -EINTR; - goto error; - } - - sreq->sr_data_direction = data_direction; - scsi_wait_req(sreq, cmd, buf, needed, timeout, retries); + result = scsi_execute(sdev, cmd, data_direction, buf, needed, + sense, timeout, retries, 0); /* * If there was an error condition, pass the info back to the user. */ - result = sreq->sr_result; if (result) { - int sb_len = sizeof(sreq->sr_sense_buffer); + int sb_len = sizeof(*sense); sb_len = (sb_len > OMAX_SB_LEN) ? OMAX_SB_LEN : sb_len; - if (copy_to_user(cmd_in, sreq->sr_sense_buffer, sb_len)) + if (copy_to_user(cmd_in, sense, sb_len)) result = -EFAULT; } else { if (copy_to_user(cmd_in, buf, outlen)) result = -EFAULT; } - scsi_release_request(sreq); error: kfree(buf); return result; @@ -475,8 +459,7 @@ int scsi_nonblockable_ioctl(struct scsi_device *sdev, int cmd, * error processing, as long as the device was opened * non-blocking */ if (filp && filp->f_flags & O_NONBLOCK) { - if (test_bit(SHOST_RECOVERY, - &sdev->host->shost_state)) + if (scsi_host_in_recovery(sdev->host)) return -ENODEV; } else if (!scsi_block_when_processing_errors(sdev)) return -ENODEV; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 7a91ca3d32a6..3ff538809786 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -99,6 +99,28 @@ int scsi_insert_special_req(struct scsi_request *sreq, int at_head) static void scsi_run_queue(struct request_queue *q); /* + * Function: scsi_unprep_request() + * + * Purpose: Remove all preparation done for a request, including its + * associated scsi_cmnd, so that it can be requeued. + * + * Arguments: req - request to unprepare + * + * Lock status: Assumed that no locks are held upon entry. + * + * Returns: Nothing. + */ +static void scsi_unprep_request(struct request *req) +{ + struct scsi_cmnd *cmd = req->special; + + req->flags &= ~REQ_DONTPREP; + req->special = (req->flags & REQ_SPECIAL) ? cmd->sc_request : NULL; + + scsi_put_command(cmd); +} + +/* * Function: scsi_queue_insert() * * Purpose: Insert a command in the midlevel queue. @@ -232,23 +254,6 @@ void scsi_do_req(struct scsi_request *sreq, const void *cmnd, } EXPORT_SYMBOL(scsi_do_req); -static void scsi_wait_done(struct scsi_cmnd *cmd) -{ - struct request *req = cmd->request; - struct request_queue *q = cmd->device->request_queue; - unsigned long flags; - - req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */ - - spin_lock_irqsave(q->queue_lock, flags); - if (blk_rq_tagged(req)) - blk_queue_end_tag(q, req); - spin_unlock_irqrestore(q->queue_lock, flags); - - if (req->waiting) - complete(req->waiting); -} - /* This is the end routine we get to if a command was never attached * to the request. Simply complete the request without changing * rq_status; this will cause a DRIVER_ERROR. */ @@ -263,21 +268,114 @@ void scsi_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer, unsigned bufflen, int timeout, int retries) { DECLARE_COMPLETION(wait); - - sreq->sr_request->waiting = &wait; - sreq->sr_request->rq_status = RQ_SCSI_BUSY; - sreq->sr_request->end_io = scsi_wait_req_end_io; - scsi_do_req(sreq, cmnd, buffer, bufflen, scsi_wait_done, - timeout, retries); + int write = (sreq->sr_data_direction == DMA_TO_DEVICE); + struct request *req; + + req = blk_get_request(sreq->sr_device->request_queue, write, + __GFP_WAIT); + if (bufflen && blk_rq_map_kern(sreq->sr_device->request_queue, req, + buffer, bufflen, __GFP_WAIT)) { + sreq->sr_result = DRIVER_ERROR << 24; + blk_put_request(req); + return; + } + + req->flags |= REQ_NOMERGE; + req->waiting = &wait; + req->end_io = scsi_wait_req_end_io; + req->cmd_len = COMMAND_SIZE(((u8 *)cmnd)[0]); + req->sense = sreq->sr_sense_buffer; + req->sense_len = 0; + memcpy(req->cmd, cmnd, req->cmd_len); + req->timeout = timeout; + req->flags |= REQ_BLOCK_PC; + req->rq_disk = NULL; + blk_insert_request(sreq->sr_device->request_queue, req, + sreq->sr_data_direction == DMA_TO_DEVICE, NULL); wait_for_completion(&wait); sreq->sr_request->waiting = NULL; - if (sreq->sr_request->rq_status != RQ_SCSI_DONE) + sreq->sr_result = req->errors; + if (req->errors) sreq->sr_result |= (DRIVER_ERROR << 24); - __scsi_release_request(sreq); + blk_put_request(req); } + EXPORT_SYMBOL(scsi_wait_req); +/** + * scsi_execute - insert request and wait for the result + * @sdev: scsi device + * @cmd: scsi command + * @data_direction: data direction + * @buffer: data buffer + * @bufflen: len of buffer + * @sense: optional sense buffer + * @timeout: request timeout in seconds + * @retries: number of times to retry request + * @flags: or into request flags; + * + * returns the req->errors value which is the the scsi_cmnd result + * field. + **/ +int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, + int data_direction, void *buffer, unsigned bufflen, + unsigned char *sense, int timeout, int retries, int flags) +{ + struct request *req; + int write = (data_direction == DMA_TO_DEVICE); + int ret = DRIVER_ERROR << 24; + + req = blk_get_request(sdev->request_queue, write, __GFP_WAIT); + + if (bufflen && blk_rq_map_kern(sdev->request_queue, req, + buffer, bufflen, __GFP_WAIT)) + goto out; + + req->cmd_len = COMMAND_SIZE(cmd[0]); + memcpy(req->cmd, cmd, req->cmd_len); + req->sense = sense; + req->sense_len = 0; + req->timeout = timeout; + req->flags |= flags | REQ_BLOCK_PC | REQ_SPECIAL | REQ_QUIET; + + /* + * head injection *required* here otherwise quiesce won't work + */ + blk_execute_rq(req->q, NULL, req, 1); + + ret = req->errors; + out: + blk_put_request(req); + + return ret; +} +EXPORT_SYMBOL(scsi_execute); + + +int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, + int data_direction, void *buffer, unsigned bufflen, + struct scsi_sense_hdr *sshdr, int timeout, int retries) +{ + char *sense = NULL; + int result; + + if (sshdr) { + sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); + if (!sense) + return DRIVER_ERROR << 24; + memset(sense, 0, SCSI_SENSE_BUFFERSIZE); + } + result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen, + sense, timeout, retries, 0); + if (sshdr) + scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, sshdr); + + kfree(sense); + return result; +} +EXPORT_SYMBOL(scsi_execute_req); + /* * Function: scsi_init_cmd_errh() * @@ -348,7 +446,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, flags); shost->host_busy--; - if (unlikely(test_bit(SHOST_RECOVERY, &shost->shost_state) && + if (unlikely(scsi_host_in_recovery(shost) && shost->host_failed)) scsi_eh_wakeup(shost); spin_unlock(shost->host_lock); @@ -476,15 +574,16 @@ static void scsi_run_queue(struct request_queue *q) * I/O errors in the middle of the request, in which case * we need to request the blocks that come after the bad * sector. + * Notes: Upon return, cmd is a stale pointer. */ static void scsi_requeue_command(struct request_queue *q, struct scsi_cmnd *cmd) { + struct request *req = cmd->request; unsigned long flags; - cmd->request->flags &= ~REQ_DONTPREP; - + scsi_unprep_request(req); spin_lock_irqsave(q->queue_lock, flags); - blk_requeue_request(q, cmd->request); + blk_requeue_request(q, req); spin_unlock_irqrestore(q->queue_lock, flags); scsi_run_queue(q); @@ -519,13 +618,14 @@ void scsi_run_host_queues(struct Scsi_Host *shost) * * Lock status: Assumed that lock is not held upon entry. * - * Returns: cmd if requeue done or required, NULL otherwise + * Returns: cmd if requeue required, NULL otherwise. * * Notes: This is called for block device requests in order to * mark some number of sectors as complete. * * We are guaranteeing that the request queue will be goosed * at some point during this call. + * Notes: If cmd was requeued, upon return it will be a stale pointer. */ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, int bytes, int requeue) @@ -548,14 +648,15 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, if (!uptodate && blk_noretry_request(req)) end_that_request_chunk(req, 0, leftover); else { - if (requeue) + if (requeue) { /* * Bleah. Leftovers again. Stick the * leftovers in the front of the * queue, and goose the queue again. */ scsi_requeue_command(q, cmd); - + cmd = NULL; + } return cmd; } } @@ -576,7 +677,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, return NULL; } -static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, int gfp_mask) +static struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) { struct scsi_host_sg_pool *sgp; struct scatterlist *sgl; @@ -781,15 +882,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, * requeueing right here - we will requeue down below * when we handle the bad sectors. */ - cmd = scsi_end_request(cmd, 1, good_bytes, result == 0); /* - * If the command completed without error, then either finish off the - * rest of the command, or start a new one. + * If the command completed without error, then either + * finish off the rest of the command, or start a new one. */ - if (result == 0 || cmd == NULL ) { + if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL) return; - } } /* * Now, if we were good little boys and girls, Santa left us a request @@ -804,7 +903,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, * and quietly refuse further access. */ cmd->device->changed = 1; - cmd = scsi_end_request(cmd, 0, + scsi_end_request(cmd, 0, this_count, 1); return; } else { @@ -838,7 +937,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, scsi_requeue_command(q, cmd); result = 0; } else { - cmd = scsi_end_request(cmd, 0, this_count, 1); + scsi_end_request(cmd, 0, this_count, 1); return; } break; @@ -851,18 +950,21 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, scsi_requeue_command(q, cmd); return; } - printk(KERN_INFO "Device %s not ready.\n", - req->rq_disk ? req->rq_disk->disk_name : ""); - cmd = scsi_end_request(cmd, 0, this_count, 1); + if (!(req->flags & REQ_QUIET)) + dev_printk(KERN_INFO, + &cmd->device->sdev_gendev, + "Device not ready.\n"); + scsi_end_request(cmd, 0, this_count, 1); return; case VOLUME_OVERFLOW: - printk(KERN_INFO "Volume overflow <%d %d %d %d> CDB: ", - cmd->device->host->host_no, - (int)cmd->device->channel, - (int)cmd->device->id, (int)cmd->device->lun); - __scsi_print_command(cmd->data_cmnd); - scsi_print_sense("", cmd); - cmd = scsi_end_request(cmd, 0, block_bytes, 1); + if (!(req->flags & REQ_QUIET)) { + dev_printk(KERN_INFO, + &cmd->device->sdev_gendev, + "Volume overflow, CDB: "); + __scsi_print_command(cmd->data_cmnd); + scsi_print_sense("", cmd); + } + scsi_end_request(cmd, 0, block_bytes, 1); return; default: break; @@ -878,14 +980,13 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, return; } if (result) { - printk(KERN_INFO "SCSI error : <%d %d %d %d> return code " - "= 0x%x\n", cmd->device->host->host_no, - cmd->device->channel, - cmd->device->id, - cmd->device->lun, result); - - if (driver_byte(result) & DRIVER_SENSE) - scsi_print_sense("", cmd); + if (!(req->flags & REQ_QUIET)) { + dev_printk(KERN_INFO, &cmd->device->sdev_gendev, + "SCSI error: return code = 0x%x\n", result); + + if (driver_byte(result) & DRIVER_SENSE) + scsi_print_sense("", cmd); + } /* * Mark a single buffer as not uptodate. Queue the remainder. * We sometimes get this cruft in the event that a medium error @@ -894,7 +995,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, block_bytes = req->hard_cur_sectors << 9; if (!block_bytes) block_bytes = req->data_len; - cmd = scsi_end_request(cmd, 0, block_bytes, 1); + scsi_end_request(cmd, 0, block_bytes, 1); } } EXPORT_SYMBOL(scsi_io_completion); @@ -938,8 +1039,10 @@ static int scsi_init_io(struct scsi_cmnd *cmd) * if sg table allocation fails, requeue request later. */ sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC); - if (unlikely(!sgpnt)) + if (unlikely(!sgpnt)) { + scsi_unprep_request(req); return BLKPREP_DEFER; + } cmd->request_buffer = (char *) sgpnt; cmd->request_bufflen = req->nr_sectors << 9; @@ -1020,6 +1123,12 @@ static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk, return -EOPNOTSUPP; } +static void scsi_generic_done(struct scsi_cmnd *cmd) +{ + BUG_ON(!blk_pc_request(cmd->request)); + scsi_io_completion(cmd, cmd->result == 0 ? cmd->bufflen : 0, 0); +} + static int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device *sdev = q->queuedata; @@ -1034,7 +1143,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (unlikely(!scsi_device_online(sdev))) { printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } if (unlikely(sdev->sdev_state != SDEV_RUNNING)) { /* OK, we're not in a running state don't prep @@ -1044,7 +1153,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * at all allowed down */ printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to dead device\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } /* OK, we only allow special commands (i.e. not * user initiated ones */ @@ -1061,7 +1170,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * these two cases differently. We differentiate by looking * at request->cmd, as this tells us the real story. */ - if (req->flags & REQ_SPECIAL) { + if (req->flags & REQ_SPECIAL && req->special) { struct scsi_request *sreq = req->special; if (sreq->sr_magic == SCSI_REQ_MAGIC) { @@ -1073,14 +1182,14 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) cmd = req->special; } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { - if(unlikely(specials_only)) { + if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) { if(specials_only == SDEV_QUIESCE || specials_only == SDEV_BLOCK) - return BLKPREP_DEFER; + goto defer; printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to device being removed\n", sdev->host->host_no, sdev->id, sdev->lun); - return BLKPREP_KILL; + goto kill; } @@ -1098,7 +1207,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) cmd->tag = req->tag; } else { blk_dump_rq_flags(req, "SCSI bad req"); - return BLKPREP_KILL; + goto kill; } /* note the overloading of req->special. When the tag @@ -1136,17 +1245,38 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) * required). */ ret = scsi_init_io(cmd); - if (ret) /* BLKPREP_KILL return also releases the command */ - return ret; + switch(ret) { + /* For BLKPREP_KILL/DEFER the cmd was released */ + case BLKPREP_KILL: + goto kill; + case BLKPREP_DEFER: + goto defer; + } /* * Initialize the actual SCSI command for this request. */ - drv = *(struct scsi_driver **)req->rq_disk->private_data; - if (unlikely(!drv->init_command(cmd))) { - scsi_release_buffers(cmd); - scsi_put_command(cmd); - return BLKPREP_KILL; + if (req->rq_disk) { + drv = *(struct scsi_driver **)req->rq_disk->private_data; + if (unlikely(!drv->init_command(cmd))) { + scsi_release_buffers(cmd); + scsi_put_command(cmd); + goto kill; + } + } else { + memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd)); + cmd->cmd_len = req->cmd_len; + if (rq_data_dir(req) == WRITE) + cmd->sc_data_direction = DMA_TO_DEVICE; + else if (req->data_len) + cmd->sc_data_direction = DMA_FROM_DEVICE; + else + cmd->sc_data_direction = DMA_NONE; + + cmd->transfersize = req->data_len; + cmd->allowed = 3; + cmd->timeout_per_command = req->timeout; + cmd->done = scsi_generic_done; } } @@ -1163,6 +1293,9 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (sdev->device_busy == 0) blk_plug_device(q); return BLKPREP_DEFER; + kill: + req->errors = DID_NO_CONNECT << 16; + return BLKPREP_KILL; } /* @@ -1207,7 +1340,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, struct Scsi_Host *shost, struct scsi_device *sdev) { - if (test_bit(SHOST_RECOVERY, &shost->shost_state)) + if (scsi_host_in_recovery(shost)) return 0; if (shost->host_busy == 0 && shost->host_blocked) { /* @@ -1237,19 +1370,24 @@ static inline int scsi_host_queue_ready(struct request_queue *q, } /* - * Kill requests for a dead device + * Kill a request for a dead device */ -static void scsi_kill_requests(request_queue_t *q) +static void scsi_kill_request(struct request *req, request_queue_t *q) { - struct request *req; + struct scsi_cmnd *cmd = req->special; + + blkdev_dequeue_request(req); - while ((req = elv_next_request(q)) != NULL) { - blkdev_dequeue_request(req); - req->flags |= REQ_QUIET; - while (end_that_request_first(req, 0, req->nr_sectors)) - ; - end_that_request_last(req); + if (unlikely(cmd == NULL)) { + printk(KERN_CRIT "impossible request in %s.\n", + __FUNCTION__); + BUG(); } + + scsi_init_cmd_errh(cmd); + cmd->result = DID_NO_CONNECT << 16; + atomic_inc(&cmd->device->iorequest_cnt); + __scsi_done(cmd); } /* @@ -1272,7 +1410,8 @@ static void scsi_request_fn(struct request_queue *q) if (!sdev) { printk("scsi: killing requests for dead queue\n"); - scsi_kill_requests(q); + while ((req = elv_next_request(q)) != NULL) + scsi_kill_request(req, q); return; } @@ -1299,11 +1438,7 @@ static void scsi_request_fn(struct request_queue *q) if (unlikely(!scsi_device_online(sdev))) { printk(KERN_ERR "scsi%d (%d:%d): rejecting I/O to offline device\n", sdev->host->host_no, sdev->id, sdev->lun); - blkdev_dequeue_request(req); - req->flags |= REQ_QUIET; - while (end_that_request_first(req, 0, req->nr_sectors)) - ; - end_that_request_last(req); + scsi_kill_request(req, q); continue; } @@ -1316,6 +1451,14 @@ static void scsi_request_fn(struct request_queue *q) sdev->device_busy++; spin_unlock(q->queue_lock); + cmd = req->special; + if (unlikely(cmd == NULL)) { + printk(KERN_CRIT "impossible request in %s.\n" + "please mail a stack trace to " + "linux-scsi@vger.kernel.org", + __FUNCTION__); + BUG(); + } spin_lock(shost->host_lock); if (!scsi_host_queue_ready(q, shost, sdev)) @@ -1334,15 +1477,6 @@ static void scsi_request_fn(struct request_queue *q) */ spin_unlock_irq(shost->host_lock); - cmd = req->special; - if (unlikely(cmd == NULL)) { - printk(KERN_CRIT "impossible request in %s.\n" - "please mail a stack trace to " - "linux-scsi@vger.kernel.org", - __FUNCTION__); - BUG(); - } - /* * Finally, initialize any error handling parameters, and set up * the timers for timeouts. @@ -1539,9 +1673,9 @@ void scsi_exit_queue(void) } } /** - * __scsi_mode_sense - issue a mode sense, falling back from 10 to + * scsi_mode_sense - issue a mode sense, falling back from 10 to * six bytes if necessary. - * @sreq: SCSI request to fill in with the MODE_SENSE + * @sdev: SCSI device to be queried * @dbd: set if mode sense will allow block descriptors to be returned * @modepage: mode page being requested * @buffer: request buffer (may not be smaller than eight bytes) @@ -1549,26 +1683,34 @@ void scsi_exit_queue(void) * @timeout: command timeout * @retries: number of retries before failing * @data: returns a structure abstracting the mode header data + * @sense: place to put sense data (or NULL if no sense to be collected). + * must be SCSI_SENSE_BUFFERSIZE big. * * Returns zero if unsuccessful, or the header offset (either 4 * or 8 depending on whether a six or ten byte command was * issued) if successful. **/ int -__scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage, +scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, unsigned char *buffer, int len, int timeout, int retries, - struct scsi_mode_data *data) { + struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr) { unsigned char cmd[12]; int use_10_for_ms; int header_length; + int result; + struct scsi_sense_hdr my_sshdr; memset(data, 0, sizeof(*data)); memset(&cmd[0], 0, 12); cmd[1] = dbd & 0x18; /* allows DBD and LLBA bits */ cmd[2] = modepage; + /* caller might not be interested in sense, but we need it */ + if (!sshdr) + sshdr = &my_sshdr; + retry: - use_10_for_ms = sreq->sr_device->use_10_for_ms; + use_10_for_ms = sdev->use_10_for_ms; if (use_10_for_ms) { if (len < 8) @@ -1586,36 +1728,31 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage, header_length = 4; } - sreq->sr_cmd_len = 0; - memset(sreq->sr_sense_buffer, 0, sizeof(sreq->sr_sense_buffer)); - sreq->sr_data_direction = DMA_FROM_DEVICE; - memset(buffer, 0, len); - scsi_wait_req(sreq, cmd, buffer, len, timeout, retries); + result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer, len, + sshdr, timeout, retries); /* This code looks awful: what it's doing is making sure an * ILLEGAL REQUEST sense return identifies the actual command * byte as the problem. MODE_SENSE commands can return * ILLEGAL REQUEST if the code page isn't supported */ - if (use_10_for_ms && !scsi_status_is_good(sreq->sr_result) && - (driver_byte(sreq->sr_result) & DRIVER_SENSE)) { - struct scsi_sense_hdr sshdr; - - if (scsi_request_normalize_sense(sreq, &sshdr)) { - if ((sshdr.sense_key == ILLEGAL_REQUEST) && - (sshdr.asc == 0x20) && (sshdr.ascq == 0)) { + if (use_10_for_ms && !scsi_status_is_good(result) && + (driver_byte(result) & DRIVER_SENSE)) { + if (scsi_sense_valid(sshdr)) { + if ((sshdr->sense_key == ILLEGAL_REQUEST) && + (sshdr->asc == 0x20) && (sshdr->ascq == 0)) { /* * Invalid command operation code */ - sreq->sr_device->use_10_for_ms = 0; + sdev->use_10_for_ms = 0; goto retry; } } } - if(scsi_status_is_good(sreq->sr_result)) { + if(scsi_status_is_good(result)) { data->header_length = header_length; if(use_10_for_ms) { data->length = buffer[0]*256 + buffer[1] + 2; @@ -1632,73 +1769,31 @@ __scsi_mode_sense(struct scsi_request *sreq, int dbd, int modepage, } } - return sreq->sr_result; -} -EXPORT_SYMBOL(__scsi_mode_sense); - -/** - * scsi_mode_sense - issue a mode sense, falling back from 10 to - * six bytes if necessary. - * @sdev: scsi device to send command to. - * @dbd: set if mode sense will disable block descriptors in the return - * @modepage: mode page being requested - * @buffer: request buffer (may not be smaller than eight bytes) - * @len: length of request buffer. - * @timeout: command timeout - * @retries: number of retries before failing - * - * Returns zero if unsuccessful, or the header offset (either 4 - * or 8 depending on whether a six or ten byte command was - * issued) if successful. - **/ -int -scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, - unsigned char *buffer, int len, int timeout, int retries, - struct scsi_mode_data *data) -{ - struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL); - int ret; - - if (!sreq) - return -1; - - ret = __scsi_mode_sense(sreq, dbd, modepage, buffer, len, - timeout, retries, data); - - scsi_release_request(sreq); - - return ret; + return result; } EXPORT_SYMBOL(scsi_mode_sense); int scsi_test_unit_ready(struct scsi_device *sdev, int timeout, int retries) { - struct scsi_request *sreq; char cmd[] = { TEST_UNIT_READY, 0, 0, 0, 0, 0, }; + struct scsi_sense_hdr sshdr; int result; - sreq = scsi_allocate_request(sdev, GFP_KERNEL); - if (!sreq) - return -ENOMEM; - - sreq->sr_data_direction = DMA_NONE; - scsi_wait_req(sreq, cmd, NULL, 0, timeout, retries); + result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL, 0, &sshdr, + timeout, retries); - if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && sdev->removable) { - struct scsi_sense_hdr sshdr; + if ((driver_byte(result) & DRIVER_SENSE) && sdev->removable) { - if ((scsi_request_normalize_sense(sreq, &sshdr)) && + if ((scsi_sense_valid(&sshdr)) && ((sshdr.sense_key == UNIT_ATTENTION) || (sshdr.sense_key == NOT_READY))) { sdev->changed = 1; - sreq->sr_result = 0; + result = 0; } } - result = sreq->sr_result; - scsi_release_request(sreq); return result; } EXPORT_SYMBOL(scsi_test_unit_ready); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index d30d7f4e63ec..d05f778d31a8 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -63,6 +63,9 @@ extern int __init scsi_init_devinfo(void); extern void scsi_exit_devinfo(void); /* scsi_error.c */ +extern void scsi_add_timer(struct scsi_cmnd *, int, + void (*)(struct scsi_cmnd *)); +extern int scsi_delete_timer(struct scsi_cmnd *); extern void scsi_times_out(struct scsi_cmnd *cmd); extern int scsi_error_handler(void *host); extern int scsi_decide_disposition(struct scsi_cmnd *cmd); @@ -121,6 +124,7 @@ extern void scsi_sysfs_unregister(void); extern void scsi_sysfs_device_initialize(struct scsi_device *); extern int scsi_sysfs_target_initialize(struct scsi_device *); extern struct scsi_transport_template blank_transport_template; +extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 2d3c4ac475f2..327c5d7e5bd2 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -111,15 +111,14 @@ MODULE_PARM_DESC(inq_timeout, /** * scsi_unlock_floptical - unlock device via a special MODE SENSE command - * @sreq: used to send the command + * @sdev: scsi device to send command to * @result: area to store the result of the MODE SENSE * * Description: - * Send a vendor specific MODE SENSE (not a MODE SELECT) command using - * @sreq to unlock a device, storing the (unused) results into result. + * Send a vendor specific MODE SENSE (not a MODE SELECT) command. * Called for BLIST_KEY devices. **/ -static void scsi_unlock_floptical(struct scsi_request *sreq, +static void scsi_unlock_floptical(struct scsi_device *sdev, unsigned char *result) { unsigned char scsi_cmd[MAX_COMMAND_SIZE]; @@ -129,11 +128,10 @@ static void scsi_unlock_floptical(struct scsi_request *sreq, scsi_cmd[1] = 0; scsi_cmd[2] = 0x2e; scsi_cmd[3] = 0; - scsi_cmd[4] = 0x2a; /* size */ + scsi_cmd[4] = 0x2a; /* size */ scsi_cmd[5] = 0; - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; - scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3); + scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL, + SCSI_TIMEOUT, 3); } /** @@ -336,9 +334,23 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, unsigned long flags; const int size = sizeof(struct scsi_target) + shost->transportt->target_size; - struct scsi_target *starget = kmalloc(size, GFP_ATOMIC); + struct scsi_target *starget; struct scsi_target *found_target; + /* + * Obtain the real parent from the transport. The transport + * is allowed to fail (no error) if there is nothing at that + * target id. + */ + if (shost->transportt->target_parent) { + spin_lock_irqsave(shost->host_lock, flags); + parent = shost->transportt->target_parent(shost, channel, id); + spin_unlock_irqrestore(shost->host_lock, flags); + if (!parent) + return NULL; + } + + starget = kmalloc(size, GFP_KERNEL); if (!starget) { printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); return NULL; @@ -419,26 +431,25 @@ void scsi_target_reap(struct scsi_target *starget) /** * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY - * @sreq: used to send the INQUIRY + * @sdev: scsi_device to probe * @inq_result: area to store the INQUIRY result + * @result_len: len of inq_result * @bflags: store any bflags found here * * Description: - * Probe the lun associated with @sreq using a standard SCSI INQUIRY; + * Probe the lun associated with @req using a standard SCSI INQUIRY; * - * If the INQUIRY is successful, sreq->sr_result is zero and: the + * If the INQUIRY is successful, zero is returned and the * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length - * are copied to the Scsi_Device at @sreq->sr_device (sdev); - * any flags value is stored in *@bflags. + * are copied to the Scsi_Device any flags value is stored in *@bflags. **/ -static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, - int *bflags) +static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result, + int result_len, int *bflags) { - struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */ unsigned char scsi_cmd[MAX_COMMAND_SIZE]; int first_inquiry_len, try_inquiry_len, next_inquiry_len; int response_len = 0; - int pass, count; + int pass, count, result; struct scsi_sense_hdr sshdr; *bflags = 0; @@ -461,28 +472,26 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; scsi_cmd[4] = (unsigned char) try_inquiry_len; - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; memset(inq_result, 0, try_inquiry_len); - scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, - try_inquiry_len, - HZ/2 + HZ*scsi_inq_timeout, 3); + + result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, + inq_result, try_inquiry_len, &sshdr, + HZ / 2 + HZ * scsi_inq_timeout, 3); SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s " "with code 0x%x\n", - sreq->sr_result ? "failed" : "successful", - sreq->sr_result)); + result ? "failed" : "successful", result)); - if (sreq->sr_result) { + if (result) { /* * not-ready to ready transition [asc/ascq=0x28/0x0] * or power-on, reset [asc/ascq=0x29/0x0], continue. * INQUIRY should not yield UNIT_ATTENTION * but many buggy devices do so anyway. */ - if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) && - scsi_request_normalize_sense(sreq, &sshdr)) { + if ((driver_byte(result) & DRIVER_SENSE) && + scsi_sense_valid(&sshdr)) { if ((sshdr.sense_key == UNIT_ATTENTION) && ((sshdr.asc == 0x28) || (sshdr.asc == 0x29)) && @@ -493,7 +502,7 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, break; } - if (sreq->sr_result == 0) { + if (result == 0) { response_len = (unsigned char) inq_result[4] + 5; if (response_len > 255) response_len = first_inquiry_len; /* sanity */ @@ -542,8 +551,8 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, /* If the last transfer attempt got an error, assume the * peripheral doesn't exist or is dead. */ - if (sreq->sr_result) - return; + if (result) + return -EIO; /* Don't report any more data than the device says is valid */ sdev->inquiry_len = min(try_inquiry_len, response_len); @@ -578,8 +587,9 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result, if (sdev->scsi_level >= 2 || (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1)) sdev->scsi_level++; + sdev->sdev_target->scsi_level = sdev->scsi_level; - return; + return 0; } /** @@ -762,6 +772,15 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags) return SCSI_SCAN_LUN_PRESENT; } +static inline void scsi_destroy_sdev(struct scsi_device *sdev) +{ + if (sdev->host->hostt->slave_destroy) + sdev->host->hostt->slave_destroy(sdev); + transport_destroy_device(&sdev->sdev_gendev); + put_device(&sdev->sdev_gendev); +} + + /** * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it * @starget: pointer to target device structure @@ -786,18 +805,17 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, void *hostdata) { struct scsi_device *sdev; - struct scsi_request *sreq; unsigned char *result; - int bflags, res = SCSI_SCAN_NO_RESPONSE; + int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256; struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); /* * The rescan flag is used as an optimization, the first scan of a * host adapter calls into here with rescan == 0. */ - if (rescan) { - sdev = scsi_device_lookup_by_target(starget, lun); - if (sdev) { + sdev = scsi_device_lookup_by_target(starget, lun); + if (sdev) { + if (rescan || sdev->sdev_state != SDEV_CREATED) { SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: device exists on %s\n", sdev->sdev_gendev.bus_id)); @@ -812,21 +830,18 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, sdev->model); return SCSI_SCAN_LUN_PRESENT; } - } - - sdev = scsi_alloc_sdev(starget, lun, hostdata); + scsi_device_put(sdev); + } else + sdev = scsi_alloc_sdev(starget, lun, hostdata); if (!sdev) goto out; - sreq = scsi_allocate_request(sdev, GFP_ATOMIC); - if (!sreq) - goto out_free_sdev; - result = kmalloc(256, GFP_ATOMIC | + + result = kmalloc(result_len, GFP_ATOMIC | ((shost->unchecked_isa_dma) ? __GFP_DMA : 0)); if (!result) - goto out_free_sreq; + goto out_free_sdev; - scsi_probe_lun(sreq, result, &bflags); - if (sreq->sr_result) + if (scsi_probe_lun(sdev, result, result_len, &bflags)) goto out_free_result; /* @@ -854,7 +869,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, if (res == SCSI_SCAN_LUN_PRESENT) { if (bflags & BLIST_KEY) { sdev->lockable = 0; - scsi_unlock_floptical(sreq, result); + scsi_unlock_floptical(sdev, result); } if (bflagsp) *bflagsp = bflags; @@ -862,20 +877,18 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, out_free_result: kfree(result); - out_free_sreq: - scsi_release_request(sreq); out_free_sdev: if (res == SCSI_SCAN_LUN_PRESENT) { if (sdevp) { - scsi_device_get(sdev); - *sdevp = sdev; + if (scsi_device_get(sdev) == 0) { + *sdevp = sdev; + } else { + __scsi_remove_device(sdev); + res = SCSI_SCAN_NO_RESPONSE; + } } - } else { - if (sdev->host->hostt->slave_destroy) - sdev->host->hostt->slave_destroy(sdev); - transport_destroy_device(&sdev->sdev_gendev); - put_device(&sdev->sdev_gendev); - } + } else + scsi_destroy_sdev(sdev); out: return res; } @@ -1047,7 +1060,7 @@ EXPORT_SYMBOL(int_to_scsilun); * 0: scan completed (or no memory, so further scanning is futile) * 1: no report lun scan, or not configured **/ -static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, +static int scsi_report_lun_scan(struct scsi_target *starget, int bflags, int rescan) { char devname[64]; @@ -1056,11 +1069,12 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, unsigned int lun; unsigned int num_luns; unsigned int retries; + int result; struct scsi_lun *lunp, *lun_data; - struct scsi_request *sreq; u8 *data; struct scsi_sense_hdr sshdr; - struct scsi_target *starget = scsi_target(sdev); + struct scsi_device *sdev; + struct Scsi_Host *shost = dev_to_shost(&starget->dev); /* * Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set. @@ -1068,19 +1082,23 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, * support more than 8 LUNs. */ if ((bflags & BLIST_NOREPORTLUN) || - sdev->scsi_level < SCSI_2 || - (sdev->scsi_level < SCSI_3 && - (!(bflags & BLIST_REPORTLUN2) || sdev->host->max_lun <= 8)) ) + starget->scsi_level < SCSI_2 || + (starget->scsi_level < SCSI_3 && + (!(bflags & BLIST_REPORTLUN2) || shost->max_lun <= 8)) ) return 1; if (bflags & BLIST_NOLUN) return 0; - sreq = scsi_allocate_request(sdev, GFP_ATOMIC); - if (!sreq) - goto out; + if (!(sdev = scsi_device_lookup_by_target(starget, 0))) { + sdev = scsi_alloc_sdev(starget, 0, NULL); + if (!sdev) + return 0; + if (scsi_device_get(sdev)) + return 0; + } sprintf(devname, "host %d channel %d id %d", - sdev->host->host_no, sdev->channel, sdev->id); + shost->host_no, sdev->channel, sdev->id); /* * Allocate enough to hold the header (the same size as one scsi_lun) @@ -1095,8 +1113,10 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, length = (max_scsi_report_luns + 1) * sizeof(struct scsi_lun); lun_data = kmalloc(length, GFP_ATOMIC | (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0)); - if (!lun_data) - goto out_release_request; + if (!lun_data) { + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + goto out; + } scsi_cmd[0] = REPORT_LUNS; @@ -1115,8 +1135,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, scsi_cmd[10] = 0; /* reserved */ scsi_cmd[11] = 0; /* control */ - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; /* * We can get a UNIT ATTENTION, for example a power on/reset, so @@ -1132,29 +1150,29 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending" " REPORT LUNS to %s (try %d)\n", devname, retries)); - scsi_wait_req(sreq, scsi_cmd, lun_data, length, - SCSI_TIMEOUT + 4*HZ, 3); + + result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, + lun_data, length, &sshdr, + SCSI_TIMEOUT + 4 * HZ, 3); + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" - " %s (try %d) result 0x%x\n", sreq->sr_result - ? "failed" : "successful", retries, - sreq->sr_result)); - if (sreq->sr_result == 0) + " %s (try %d) result 0x%x\n", result + ? "failed" : "successful", retries, result)); + if (result == 0) break; - else if (scsi_request_normalize_sense(sreq, &sshdr)) { + else if (scsi_sense_valid(&sshdr)) { if (sshdr.sense_key != UNIT_ATTENTION) break; } } - if (sreq->sr_result) { + if (result) { /* * The device probably does not support a REPORT LUN command */ kfree(lun_data); - scsi_release_request(sreq); return 1; } - scsi_release_request(sreq); /* * Get the length from the first four bytes of lun_data. @@ -1200,10 +1218,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, for (i = 0; i < sizeof(struct scsi_lun); i++) printk("%02x", data[i]); printk(" has a LUN larger than currently supported.\n"); - } else if (lun == 0) { - /* - * LUN 0 has already been scanned. - */ } else if (lun > sdev->host->max_lun) { printk(KERN_WARNING "scsi: %s lun%d has a LUN larger" " than allowed by the host adapter\n", @@ -1226,15 +1240,13 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, } kfree(lun_data); - return 0; - - out_release_request: - scsi_release_request(sreq); out: - /* - * We are out of memory, don't try scanning any further. - */ - printk(ALLOC_FAILURE_MSG, __FUNCTION__); + scsi_device_put(sdev); + if (sdev->sdev_state == SDEV_CREATED) + /* + * the sdev we used didn't appear in the report luns scan + */ + scsi_destroy_sdev(sdev); return 0; } @@ -1251,9 +1263,12 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, get_device(&starget->dev); down(&shost->scan_mutex); - res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); - if (res != SCSI_SCAN_LUN_PRESENT) - sdev = ERR_PTR(-ENODEV); + if (scsi_host_scan_allowed(shost)) { + res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, + hostdata); + if (res != SCSI_SCAN_LUN_PRESENT) + sdev = ERR_PTR(-ENODEV); + } up(&shost->scan_mutex); scsi_target_reap(starget); put_device(&starget->dev); @@ -1262,6 +1277,19 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, } EXPORT_SYMBOL(__scsi_add_device); +int scsi_add_device(struct Scsi_Host *host, uint channel, + uint target, uint lun) +{ + struct scsi_device *sdev = + __scsi_add_device(host, channel, target, lun, NULL); + if (IS_ERR(sdev)) + return PTR_ERR(sdev); + + scsi_device_put(sdev); + return 0; +} +EXPORT_SYMBOL(scsi_add_device); + void scsi_rescan_device(struct device *dev) { struct scsi_driver *drv; @@ -1278,32 +1306,12 @@ void scsi_rescan_device(struct device *dev) } EXPORT_SYMBOL(scsi_rescan_device); -/** - * scsi_scan_target - scan a target id, possibly including all LUNs on the - * target. - * @sdevsca: Scsi_Device handle for scanning - * @shost: host to scan - * @channel: channel to scan - * @id: target id to scan - * - * Description: - * Scan the target id on @shost, @channel, and @id. Scan at least LUN - * 0, and possibly all LUNs on the target id. - * - * Use the pre-allocated @sdevscan as a handle for the scanning. This - * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the - * scanning functions modify sdevscan->lun. - * - * First try a REPORT LUN scan, if that does not scan the target, do a - * sequential scan of LUNs on the target id. - **/ -void scsi_scan_target(struct device *parent, unsigned int channel, - unsigned int id, unsigned int lun, int rescan) +static void __scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) { struct Scsi_Host *shost = dev_to_shost(parent); int bflags = 0; int res; - struct scsi_device *sdev = NULL; struct scsi_target *starget; if (shost->this_id == id) @@ -1312,9 +1320,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel, */ return; - starget = scsi_alloc_target(parent, channel, id); - if (!starget) return; @@ -1331,27 +1337,16 @@ void scsi_scan_target(struct device *parent, unsigned int channel, * Scan LUN 0, if there is some response, scan further. Ideally, we * would not configure LUN 0 until all LUNs are scanned. */ - res = scsi_probe_and_add_lun(starget, 0, &bflags, &sdev, rescan, NULL); - if (res == SCSI_SCAN_LUN_PRESENT) { - if (scsi_report_lun_scan(sdev, bflags, rescan) != 0) + res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL); + if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) { + if (scsi_report_lun_scan(starget, bflags, rescan) != 0) /* * The REPORT LUN did not scan the target, * do a sequential scan. */ scsi_sequential_lun_scan(starget, bflags, - res, sdev->scsi_level, rescan); - } else if (res == SCSI_SCAN_TARGET_PRESENT) { - /* - * There's a target here, but lun 0 is offline so we - * can't use the report_lun scan. Fall back to a - * sequential lun scan with a bflags of SPARSELUN and - * a default scsi level of SCSI_2 - */ - scsi_sequential_lun_scan(starget, BLIST_SPARSELUN, - SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan); + res, starget->scsi_level, rescan); } - if (sdev) - scsi_device_put(sdev); out_reap: /* now determine if the target has any children at all @@ -1360,6 +1355,33 @@ void scsi_scan_target(struct device *parent, unsigned int channel, put_device(&starget->dev); } + +/** + * scsi_scan_target - scan a target id, possibly including all LUNs on the + * target. + * @parent: host to scan + * @channel: channel to scan + * @id: target id to scan + * @lun: Specific LUN to scan or SCAN_WILD_CARD + * @rescan: passed to LUN scanning routines + * + * Description: + * Scan the target id on @parent, @channel, and @id. Scan at least LUN 0, + * and possibly all LUNs on the target id. + * + * First try a REPORT LUN scan, if that does not scan the target, do a + * sequential scan of LUNs on the target id. + **/ +void scsi_scan_target(struct device *parent, unsigned int channel, + unsigned int id, unsigned int lun, int rescan) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + + down(&shost->scan_mutex); + if (scsi_host_scan_allowed(shost)) + __scsi_scan_target(parent, channel, id, lun, rescan); + up(&shost->scan_mutex); +} EXPORT_SYMBOL(scsi_scan_target); static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, @@ -1385,10 +1407,12 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, order_id = shost->max_id - id - 1; else order_id = id; - scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + order_id, lun, rescan); } else - scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan); + __scsi_scan_target(&shost->shost_gendev, channel, + id, lun, rescan); } int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, @@ -1403,11 +1427,15 @@ int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel, return -EINVAL; down(&shost->scan_mutex); - if (channel == SCAN_WILD_CARD) - for (channel = 0; channel <= shost->max_channel; channel++) + if (scsi_host_scan_allowed(shost)) { + if (channel == SCAN_WILD_CARD) + for (channel = 0; channel <= shost->max_channel; + channel++) + scsi_scan_channel(shost, channel, id, lun, + rescan); + else scsi_scan_channel(shost, channel, id, lun, rescan); - else - scsi_scan_channel(shost, channel, id, lun, rescan); + } up(&shost->scan_mutex); return 0; @@ -1439,23 +1467,17 @@ EXPORT_SYMBOL(scsi_scan_single_target); void scsi_forget_host(struct Scsi_Host *shost) { - struct scsi_target *starget, *tmp; + struct scsi_device *sdev; unsigned long flags; - /* - * Ok, this look a bit strange. We always look for the first device - * on the list as scsi_remove_device removes them from it - thus we - * also have to release the lock. - * We don't need to get another reference to the device before - * releasing the lock as we already own the reference from - * scsi_register_device that's release in scsi_remove_device. And - * after that we don't look at sdev anymore. - */ + restart: spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry_safe(starget, tmp, &shost->__targets, siblings) { + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->sdev_state == SDEV_DEL) + continue; spin_unlock_irqrestore(shost->host_lock, flags); - scsi_remove_target(&starget->dev); - spin_lock_irqsave(shost->host_lock, flags); + __scsi_remove_device(sdev); + goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); } @@ -1482,12 +1504,15 @@ void scsi_forget_host(struct Scsi_Host *shost) */ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) { - struct scsi_device *sdev; + struct scsi_device *sdev = NULL; struct scsi_target *starget; + down(&shost->scan_mutex); + if (!scsi_host_scan_allowed(shost)) + goto out; starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id); if (!starget) - return NULL; + goto out; sdev = scsi_alloc_sdev(starget, 0, NULL); if (sdev) { @@ -1495,6 +1520,8 @@ struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost) sdev->borken = 0; } put_device(&starget->dev); + out: + up(&shost->scan_mutex); return sdev; } EXPORT_SYMBOL(scsi_get_host_dev); @@ -1516,10 +1543,7 @@ void scsi_free_host_dev(struct scsi_device *sdev) { BUG_ON(sdev->id != sdev->host->this_id); - if (sdev->host->hostt->slave_destroy) - sdev->host->hostt->slave_destroy(sdev); - transport_destroy_device(&sdev->sdev_gendev); - put_device(&sdev->sdev_gendev); + scsi_destroy_sdev(sdev); } EXPORT_SYMBOL(scsi_free_host_dev); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index beed7fbe1cbe..72a6550a056c 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -48,6 +48,32 @@ const char *scsi_device_state_name(enum scsi_device_state state) return name; } +static struct { + enum scsi_host_state value; + char *name; +} shost_states[] = { + { SHOST_CREATED, "created" }, + { SHOST_RUNNING, "running" }, + { SHOST_CANCEL, "cancel" }, + { SHOST_DEL, "deleted" }, + { SHOST_RECOVERY, "recovery" }, + { SHOST_CANCEL_RECOVERY, "cancel/recovery" }, + { SHOST_DEL_RECOVERY, "deleted/recovery", }, +}; +const char *scsi_host_state_name(enum scsi_host_state state) +{ + int i; + char *name = NULL; + + for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) { + if (shost_states[i].value == state) { + name = shost_states[i].name; + break; + } + } + return name; +} + static int check_set(unsigned int *val, char *src) { char *last; @@ -124,6 +150,43 @@ static ssize_t store_scan(struct class_device *class_dev, const char *buf, }; static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan); +static ssize_t +store_shost_state(struct class_device *class_dev, const char *buf, size_t count) +{ + int i; + struct Scsi_Host *shost = class_to_shost(class_dev); + enum scsi_host_state state = 0; + + for (i = 0; i < sizeof(shost_states)/sizeof(shost_states[0]); i++) { + const int len = strlen(shost_states[i].name); + if (strncmp(shost_states[i].name, buf, len) == 0 && + buf[len] == '\n') { + state = shost_states[i].value; + break; + } + } + if (!state) + return -EINVAL; + + if (scsi_host_set_state(shost, state)) + return -EINVAL; + return count; +} + +static ssize_t +show_shost_state(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + const char *name = scsi_host_state_name(shost->shost_state); + + if (!name) + return -EINVAL; + + return snprintf(buf, 20, "%s\n", name); +} + +static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state); + shost_rd_attr(unique_id, "%u\n"); shost_rd_attr(host_busy, "%hu\n"); shost_rd_attr(cmd_per_lun, "%hd\n"); @@ -139,6 +202,7 @@ static struct class_device_attribute *scsi_sysfs_shost_attrs[] = { &class_device_attr_unchecked_isa_dma, &class_device_attr_proc_name, &class_device_attr_scan, + &class_device_attr_state, NULL }; @@ -591,7 +655,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) error = attr_add(&sdev->sdev_gendev, sdev->host->hostt->sdev_attrs[i]); if (error) { - scsi_remove_device(sdev); + __scsi_remove_device(sdev); goto out; } } @@ -605,7 +669,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) scsi_sysfs_sdev_attrs[i]); error = device_create_file(&sdev->sdev_gendev, attr); if (error) { - scsi_remove_device(sdev); + __scsi_remove_device(sdev); goto out; } } @@ -625,17 +689,10 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) return error; } -/** - * scsi_remove_device - unregister a device from the scsi bus - * @sdev: scsi_device to unregister - **/ -void scsi_remove_device(struct scsi_device *sdev) +void __scsi_remove_device(struct scsi_device *sdev) { - struct Scsi_Host *shost = sdev->host; - - down(&shost->scan_mutex); if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0) - goto out; + return; class_device_unregister(&sdev->sdev_classdev); device_del(&sdev->sdev_gendev); @@ -644,7 +701,18 @@ void scsi_remove_device(struct scsi_device *sdev) sdev->host->hostt->slave_destroy(sdev); transport_unregister_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev); -out: +} + +/** + * scsi_remove_device - unregister a device from the scsi bus + * @sdev: scsi_device to unregister + **/ +void scsi_remove_device(struct scsi_device *sdev) +{ + struct Scsi_Host *shost = sdev->host; + + down(&shost->scan_mutex); + __scsi_remove_device(sdev); up(&shost->scan_mutex); } EXPORT_SYMBOL(scsi_remove_device); @@ -653,17 +721,20 @@ void __scsi_remove_target(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; - struct scsi_device *sdev, *tmp; + struct scsi_device *sdev; spin_lock_irqsave(shost->host_lock, flags); starget->reap_ref++; - list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) { + restart: + list_for_each_entry(sdev, &shost->__devices, siblings) { if (sdev->channel != starget->channel || - sdev->id != starget->id) + sdev->id != starget->id || + sdev->sdev_state == SDEV_DEL) continue; spin_unlock_irqrestore(shost->host_lock, flags); scsi_remove_device(sdev); spin_lock_irqsave(shost->host_lock, flags); + goto restart; } spin_unlock_irqrestore(shost->host_lock, flags); scsi_target_reap(starget); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 35d1c1e8e345..b856e140e65f 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -26,6 +26,7 @@ */ #include <linux/module.h> #include <linux/init.h> +#include <linux/sched.h> /* workqueue stuff, HZ */ #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> @@ -252,7 +253,8 @@ struct fc_internal { #define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t) -static int fc_target_setup(struct device *dev) +static int fc_target_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) { struct scsi_target *starget = to_scsi_target(dev); struct fc_rport *rport = starget_to_rport(starget); @@ -281,7 +283,8 @@ static DECLARE_TRANSPORT_CLASS(fc_transport_class, NULL, NULL); -static int fc_host_setup(struct device *dev) +static int fc_host_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); @@ -817,12 +820,15 @@ show_fc_private_host_tgtid_bind_type(struct class_device *cdev, char *buf) return snprintf(buf, FC_BINDTYPE_MAX_NAMELEN, "%s\n", name); } +#define get_list_head_entry(pos, head, member) \ + pos = list_entry((head)->next, typeof(*pos), member) + static ssize_t store_fc_private_host_tgtid_bind_type(struct class_device *cdev, const char *buf, size_t count) { struct Scsi_Host *shost = transport_class_to_shost(cdev); - struct fc_rport *rport, *next_rport; + struct fc_rport *rport; enum fc_tgtid_binding_type val; unsigned long flags; @@ -832,9 +838,13 @@ store_fc_private_host_tgtid_bind_type(struct class_device *cdev, /* if changing bind type, purge all unused consistent bindings */ if (val != fc_host_tgtid_bind_type(shost)) { spin_lock_irqsave(shost->host_lock, flags); - list_for_each_entry_safe(rport, next_rport, - &fc_host_rport_bindings(shost), peers) + while (!list_empty(&fc_host_rport_bindings(shost))) { + get_list_head_entry(rport, + &fc_host_rport_bindings(shost), peers); + spin_unlock_irqrestore(shost->host_lock, flags); fc_rport_terminate(rport); + spin_lock_irqsave(shost->host_lock, flags); + } spin_unlock_irqrestore(shost->host_lock, flags); } @@ -1022,6 +1032,23 @@ static int fc_rport_match(struct attribute_container *cont, return &i->rport_attr_cont.ac == cont; } + +/* + * Must be called with shost->host_lock held + */ +static struct device *fc_target_parent(struct Scsi_Host *shost, + int channel, uint id) +{ + struct fc_rport *rport; + + list_for_each_entry(rport, &fc_host_rports(shost), peers) + if ((rport->channel == channel) && + (rport->scsi_target_id == id)) + return &rport->dev; + + return NULL; +} + struct scsi_transport_template * fc_attach_transport(struct fc_function_template *ft) { @@ -1057,6 +1084,8 @@ fc_attach_transport(struct fc_function_template *ft) /* Transport uses the shost workq for scsi scanning */ i->t.create_work_queue = 1; + + i->t.target_parent = fc_target_parent; /* * Setup SCSI Target Attributes. diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 8bb8222ea589..d2caa35059d9 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -19,6 +19,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_device.h> diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c new file mode 100644 index 000000000000..1d145d2f9a38 --- /dev/null +++ b/drivers/scsi/scsi_transport_sas.c @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2005 Dell Inc. + * Released under GPL v2. + * + * Serial Attached SCSI (SAS) transport class. + * + * The SAS transport class contains common code to deal with SAS HBAs, + * an aproximated representation of SAS topologies in the driver model, + * and various sysfs attributes to expose these topologies and managment + * interfaces to userspace. + * + * In addition to the basic SCSI core objects this transport class + * introduces two additional intermediate objects: The SAS PHY + * as represented by struct sas_phy defines an "outgoing" PHY on + * a SAS HBA or Expander, and the SAS remote PHY represented by + * struct sas_rphy defines an "incoming" PHY on a SAS Expander or + * end device. Note that this is purely a software concept, the + * underlying hardware for a PHY and a remote PHY is the exactly + * the same. + * + * There is no concept of a SAS port in this code, users can see + * what PHYs form a wide port based on the port_identifier attribute, + * which is the same for all PHYs in a port. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/err.h> + +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_transport.h> +#include <scsi/scsi_transport_sas.h> + + +#define SAS_HOST_ATTRS 0 +#define SAS_PORT_ATTRS 11 +#define SAS_RPORT_ATTRS 5 + +struct sas_internal { + struct scsi_transport_template t; + struct sas_function_template *f; + + struct class_device_attribute private_host_attrs[SAS_HOST_ATTRS]; + struct class_device_attribute private_phy_attrs[SAS_PORT_ATTRS]; + struct class_device_attribute private_rphy_attrs[SAS_RPORT_ATTRS]; + + struct transport_container phy_attr_cont; + struct transport_container rphy_attr_cont; + + /* + * The array of null terminated pointers to attributes + * needed by scsi_sysfs.c + */ + struct class_device_attribute *host_attrs[SAS_HOST_ATTRS + 1]; + struct class_device_attribute *phy_attrs[SAS_PORT_ATTRS + 1]; + struct class_device_attribute *rphy_attrs[SAS_RPORT_ATTRS + 1]; +}; +#define to_sas_internal(tmpl) container_of(tmpl, struct sas_internal, t) + +struct sas_host_attrs { + struct list_head rphy_list; + spinlock_t lock; + u32 next_target_id; +}; +#define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) + + +/* + * Hack to allow attributes of the same name in different objects. + */ +#define SAS_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store) \ + struct class_device_attribute class_device_attr_##_prefix##_##_name = \ + __ATTR(_name,_mode,_show,_store) + + +/* + * Pretty printing helpers + */ + +#define sas_bitfield_name_match(title, table) \ +static ssize_t \ +get_sas_##title##_names(u32 table_key, char *buf) \ +{ \ + char *prefix = ""; \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ + if (table[i].value & table_key) { \ + len += sprintf(buf + len, "%s%s", \ + prefix, table[i].name); \ + prefix = ", "; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +#define sas_bitfield_name_search(title, table) \ +static ssize_t \ +get_sas_##title##_names(u32 table_key, char *buf) \ +{ \ + ssize_t len = 0; \ + int i; \ + \ + for (i = 0; i < sizeof(table)/sizeof(table[0]); i++) { \ + if (table[i].value == table_key) { \ + len += sprintf(buf + len, "%s", \ + table[i].name); \ + break; \ + } \ + } \ + len += sprintf(buf + len, "\n"); \ + return len; \ +} + +static struct { + u32 value; + char *name; +} sas_device_type_names[] = { + { SAS_PHY_UNUSED, "unused" }, + { SAS_END_DEVICE, "end device" }, + { SAS_EDGE_EXPANDER_DEVICE, "edge expander" }, + { SAS_FANOUT_EXPANDER_DEVICE, "fanout expander" }, +}; +sas_bitfield_name_search(device_type, sas_device_type_names) + + +static struct { + u32 value; + char *name; +} sas_protocol_names[] = { + { SAS_PROTOCOL_SATA, "sata" }, + { SAS_PROTOCOL_SMP, "smp" }, + { SAS_PROTOCOL_STP, "stp" }, + { SAS_PROTOCOL_SSP, "ssp" }, +}; +sas_bitfield_name_match(protocol, sas_protocol_names) + +static struct { + u32 value; + char *name; +} sas_linkspeed_names[] = { + { SAS_LINK_RATE_UNKNOWN, "Unknown" }, + { SAS_PHY_DISABLED, "Phy disabled" }, + { SAS_LINK_RATE_FAILED, "Link Rate failed" }, + { SAS_SATA_SPINUP_HOLD, "Spin-up hold" }, + { SAS_LINK_RATE_1_5_GBPS, "1.5 Gbit" }, + { SAS_LINK_RATE_3_0_GBPS, "3.0 Gbit" }, +}; +sas_bitfield_name_search(linkspeed, sas_linkspeed_names) + + +/* + * SAS host attributes + */ + +static int sas_host_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + INIT_LIST_HEAD(&sas_host->rphy_list); + spin_lock_init(&sas_host->lock); + sas_host->next_target_id = 0; + return 0; +} + +static DECLARE_TRANSPORT_CLASS(sas_host_class, + "sas_host", sas_host_setup, NULL, NULL); + +static int sas_host_match(struct attribute_container *cont, + struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_host_device(dev)) + return 0; + shost = dev_to_shost(dev); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->t.host_attrs.ac == cont; +} + +static int do_sas_phy_delete(struct device *dev, void *data) +{ + if (scsi_is_sas_phy(dev)) + sas_phy_delete(dev_to_phy(dev)); + return 0; +} + +/** + * sas_remove_host -- tear down a Scsi_Host's SAS data structures + * @shost: Scsi Host that is torn down + * + * Removes all SAS PHYs and remote PHYs for a given Scsi_Host. + * Must be called just before scsi_remove_host for SAS HBAs. + */ +void sas_remove_host(struct Scsi_Host *shost) +{ + device_for_each_child(&shost->shost_gendev, NULL, do_sas_phy_delete); +} +EXPORT_SYMBOL(sas_remove_host); + + +/* + * SAS Port attributes + */ + +#define sas_phy_show_simple(field, name, format_string, cast) \ +static ssize_t \ +show_sas_phy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + return snprintf(buf, 20, format_string, cast phy->field); \ +} + +#define sas_phy_simple_attr(field, name, format_string, type) \ + sas_phy_show_simple(field, name, format_string, (type)) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL) + +#define sas_phy_show_protocol(field, name) \ +static ssize_t \ +show_sas_phy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + if (!phy->field) \ + return snprintf(buf, 20, "none\n"); \ + return get_sas_protocol_names(phy->field, buf); \ +} + +#define sas_phy_protocol_attr(field, name) \ + sas_phy_show_protocol(field, name) \ +static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sas_phy_##name, NULL) + +#define sas_phy_show_linkspeed(field) \ +static ssize_t \ +show_sas_phy_##field(struct class_device *cdev, char *buf) \ +{ \ + struct sas_phy *phy = transport_class_to_phy(cdev); \ + \ + return get_sas_linkspeed_names(phy->field, buf); \ +} + +#define sas_phy_linkspeed_attr(field) \ + sas_phy_show_linkspeed(field) \ +static CLASS_DEVICE_ATTR(field, S_IRUGO, show_sas_phy_##field, NULL) + +static ssize_t +show_sas_device_type(struct class_device *cdev, char *buf) +{ + struct sas_phy *phy = transport_class_to_phy(cdev); + + if (!phy->identify.device_type) + return snprintf(buf, 20, "none\n"); + return get_sas_device_type_names(phy->identify.device_type, buf); +} + +static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL); + +sas_phy_protocol_attr(identify.initiator_port_protocols, + initiator_port_protocols); +sas_phy_protocol_attr(identify.target_port_protocols, + target_port_protocols); +sas_phy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", + unsigned long long); +sas_phy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); +sas_phy_simple_attr(port_identifier, port_identifier, "%d\n", u8); +sas_phy_linkspeed_attr(negotiated_linkrate); +sas_phy_linkspeed_attr(minimum_linkrate_hw); +sas_phy_linkspeed_attr(minimum_linkrate); +sas_phy_linkspeed_attr(maximum_linkrate_hw); +sas_phy_linkspeed_attr(maximum_linkrate); + + +static DECLARE_TRANSPORT_CLASS(sas_phy_class, + "sas_phy", NULL, NULL, NULL); + +static int sas_phy_match(struct attribute_container *cont, struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_sas_phy(dev)) + return 0; + shost = dev_to_shost(dev->parent); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->phy_attr_cont.ac == cont; +} + +static void sas_phy_release(struct device *dev) +{ + struct sas_phy *phy = dev_to_phy(dev); + + put_device(dev->parent); + kfree(phy); +} + +/** + * sas_phy_alloc -- allocates and initialize a SAS PHY structure + * @parent: Parent device + * @number: Port number + * + * Allocates an SAS PHY structure. It will be added in the device tree + * below the device specified by @parent, which has to be either a Scsi_Host + * or sas_rphy. + * + * Returns: + * SAS PHY allocated or %NULL if the allocation failed. + */ +struct sas_phy *sas_phy_alloc(struct device *parent, int number) +{ + struct Scsi_Host *shost = dev_to_shost(parent); + struct sas_phy *phy; + + phy = kmalloc(sizeof(*phy), GFP_KERNEL); + if (!phy) + return NULL; + memset(phy, 0, sizeof(*phy)); + + get_device(parent); + + phy->number = number; + + device_initialize(&phy->dev); + phy->dev.parent = get_device(parent); + phy->dev.release = sas_phy_release; + sprintf(phy->dev.bus_id, "phy-%d:%d", shost->host_no, number); + + transport_setup_device(&phy->dev); + + return phy; +} +EXPORT_SYMBOL(sas_phy_alloc); + +/** + * sas_phy_add -- add a SAS PHY to the device hierachy + * @phy: The PHY to be added + * + * Publishes a SAS PHY to the rest of the system. + */ +int sas_phy_add(struct sas_phy *phy) +{ + int error; + + error = device_add(&phy->dev); + if (!error) { + transport_add_device(&phy->dev); + transport_configure_device(&phy->dev); + } + + return error; +} +EXPORT_SYMBOL(sas_phy_add); + +/** + * sas_phy_free -- free a SAS PHY + * @phy: SAS PHY to free + * + * Frees the specified SAS PHY. + * + * Note: + * This function must only be called on a PHY that has not + * sucessfully been added using sas_phy_add(). + */ +void sas_phy_free(struct sas_phy *phy) +{ + transport_destroy_device(&phy->dev); + put_device(phy->dev.parent); + put_device(phy->dev.parent); + put_device(phy->dev.parent); + kfree(phy); +} +EXPORT_SYMBOL(sas_phy_free); + +/** + * sas_phy_delete -- remove SAS PHY + * @phy: SAS PHY to remove + * + * Removes the specified SAS PHY. If the SAS PHY has an + * associated remote PHY it is removed before. + */ +void +sas_phy_delete(struct sas_phy *phy) +{ + struct device *dev = &phy->dev; + + if (phy->rphy) + sas_rphy_delete(phy->rphy); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + put_device(dev->parent); +} +EXPORT_SYMBOL(sas_phy_delete); + +/** + * scsi_is_sas_phy -- check if a struct device represents a SAS PHY + * @dev: device to check + * + * Returns: + * %1 if the device represents a SAS PHY, %0 else + */ +int scsi_is_sas_phy(const struct device *dev) +{ + return dev->release == sas_phy_release; +} +EXPORT_SYMBOL(scsi_is_sas_phy); + +/* + * SAS remote PHY attributes. + */ + +#define sas_rphy_show_simple(field, name, format_string, cast) \ +static ssize_t \ +show_sas_rphy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ + \ + return snprintf(buf, 20, format_string, cast rphy->field); \ +} + +#define sas_rphy_simple_attr(field, name, format_string, type) \ + sas_rphy_show_simple(field, name, format_string, (type)) \ +static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \ + show_sas_rphy_##name, NULL) + +#define sas_rphy_show_protocol(field, name) \ +static ssize_t \ +show_sas_rphy_##name(struct class_device *cdev, char *buf) \ +{ \ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); \ + \ + if (!rphy->field) \ + return snprintf(buf, 20, "none\n"); \ + return get_sas_protocol_names(rphy->field, buf); \ +} + +#define sas_rphy_protocol_attr(field, name) \ + sas_rphy_show_protocol(field, name) \ +static SAS_CLASS_DEVICE_ATTR(rphy, name, S_IRUGO, \ + show_sas_rphy_##name, NULL) + +static ssize_t +show_sas_rphy_device_type(struct class_device *cdev, char *buf) +{ + struct sas_rphy *rphy = transport_class_to_rphy(cdev); + + if (!rphy->identify.device_type) + return snprintf(buf, 20, "none\n"); + return get_sas_device_type_names( + rphy->identify.device_type, buf); +} + +static SAS_CLASS_DEVICE_ATTR(rphy, device_type, S_IRUGO, + show_sas_rphy_device_type, NULL); + +sas_rphy_protocol_attr(identify.initiator_port_protocols, + initiator_port_protocols); +sas_rphy_protocol_attr(identify.target_port_protocols, target_port_protocols); +sas_rphy_simple_attr(identify.sas_address, sas_address, "0x%016llx\n", + unsigned long long); +sas_rphy_simple_attr(identify.phy_identifier, phy_identifier, "%d\n", u8); + +static DECLARE_TRANSPORT_CLASS(sas_rphy_class, + "sas_rphy", NULL, NULL, NULL); + +static int sas_rphy_match(struct attribute_container *cont, struct device *dev) +{ + struct Scsi_Host *shost; + struct sas_internal *i; + + if (!scsi_is_sas_rphy(dev)) + return 0; + shost = dev_to_shost(dev->parent->parent); + + if (!shost->transportt) + return 0; + if (shost->transportt->host_attrs.ac.class != + &sas_host_class.class) + return 0; + + i = to_sas_internal(shost->transportt); + return &i->rphy_attr_cont.ac == cont; +} + +static void sas_rphy_release(struct device *dev) +{ + struct sas_rphy *rphy = dev_to_rphy(dev); + + put_device(dev->parent); + kfree(rphy); +} + +/** + * sas_rphy_alloc -- allocates and initialize a SAS remote PHY structure + * @parent: SAS PHY this remote PHY is conneted to + * + * Allocates an SAS remote PHY structure, connected to @parent. + * + * Returns: + * SAS PHY allocated or %NULL if the allocation failed. + */ +struct sas_rphy *sas_rphy_alloc(struct sas_phy *parent) +{ + struct Scsi_Host *shost = dev_to_shost(&parent->dev); + struct sas_rphy *rphy; + + rphy = kmalloc(sizeof(*rphy), GFP_KERNEL); + if (!rphy) { + put_device(&parent->dev); + return NULL; + } + memset(rphy, 0, sizeof(*rphy)); + + device_initialize(&rphy->dev); + rphy->dev.parent = get_device(&parent->dev); + rphy->dev.release = sas_rphy_release; + sprintf(rphy->dev.bus_id, "rphy-%d:%d", + shost->host_no, parent->number); + transport_setup_device(&rphy->dev); + + return rphy; +} +EXPORT_SYMBOL(sas_rphy_alloc); + +/** + * sas_rphy_add -- add a SAS remote PHY to the device hierachy + * @rphy: The remote PHY to be added + * + * Publishes a SAS remote PHY to the rest of the system. + */ +int sas_rphy_add(struct sas_rphy *rphy) +{ + struct sas_phy *parent = dev_to_phy(rphy->dev.parent); + struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + struct sas_identify *identify = &rphy->identify; + int error; + + if (parent->rphy) + return -ENXIO; + parent->rphy = rphy; + + error = device_add(&rphy->dev); + if (error) + return error; + transport_add_device(&rphy->dev); + transport_configure_device(&rphy->dev); + + spin_lock(&sas_host->lock); + list_add_tail(&rphy->list, &sas_host->rphy_list); + if (identify->device_type == SAS_END_DEVICE && + (identify->target_port_protocols & + (SAS_PROTOCOL_SSP|SAS_PROTOCOL_STP|SAS_PROTOCOL_SATA))) + rphy->scsi_target_id = sas_host->next_target_id++; + else + rphy->scsi_target_id = -1; + spin_unlock(&sas_host->lock); + + if (rphy->scsi_target_id != -1) { + scsi_scan_target(&rphy->dev, parent->number, + rphy->scsi_target_id, ~0, 0); + } + + return 0; +} +EXPORT_SYMBOL(sas_rphy_add); + +/** + * sas_rphy_free -- free a SAS remote PHY + * @rphy SAS remote PHY to free + * + * Frees the specified SAS remote PHY. + * + * Note: + * This function must only be called on a remote + * PHY that has not sucessfully been added using + * sas_rphy_add(). + */ +void sas_rphy_free(struct sas_rphy *rphy) +{ + struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + spin_lock(&sas_host->lock); + list_del(&rphy->list); + spin_unlock(&sas_host->lock); + + transport_destroy_device(&rphy->dev); + put_device(rphy->dev.parent); + put_device(rphy->dev.parent); + put_device(rphy->dev.parent); + kfree(rphy); +} +EXPORT_SYMBOL(sas_rphy_free); + +/** + * sas_rphy_delete -- remove SAS remote PHY + * @rphy: SAS remote PHY to remove + * + * Removes the specified SAS remote PHY. + */ +void +sas_rphy_delete(struct sas_rphy *rphy) +{ + struct device *dev = &rphy->dev; + struct sas_phy *parent = dev_to_phy(dev->parent); + struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + + scsi_remove_target(dev); + + transport_remove_device(dev); + device_del(dev); + transport_destroy_device(dev); + + spin_lock(&sas_host->lock); + list_del(&rphy->list); + spin_unlock(&sas_host->lock); + + put_device(&parent->dev); +} +EXPORT_SYMBOL(sas_rphy_delete); + +/** + * scsi_is_sas_rphy -- check if a struct device represents a SAS remote PHY + * @dev: device to check + * + * Returns: + * %1 if the device represents a SAS remote PHY, %0 else + */ +int scsi_is_sas_rphy(const struct device *dev) +{ + return dev->release == sas_rphy_release; +} +EXPORT_SYMBOL(scsi_is_sas_rphy); + + +/* + * SCSI scan helper + */ + +static struct device *sas_target_parent(struct Scsi_Host *shost, + int channel, uint id) +{ + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + struct sas_rphy *rphy; + struct device *dev = NULL; + + spin_lock(&sas_host->lock); + list_for_each_entry(rphy, &sas_host->rphy_list, list) { + struct sas_phy *parent = dev_to_phy(rphy->dev.parent); + if (parent->number == channel && + rphy->scsi_target_id == id) + dev = &rphy->dev; + } + spin_unlock(&sas_host->lock); + + return dev; +} + + +/* + * Setup / Teardown code + */ + +#define SETUP_RPORT_ATTRIBUTE(field) \ + i->private_rphy_attrs[count] = class_device_attr_##field; \ + i->private_rphy_attrs[count].attr.mode = S_IRUGO; \ + i->private_rphy_attrs[count].store = NULL; \ + i->rphy_attrs[count] = &i->private_rphy_attrs[count]; \ + count++ + +#define SETUP_PORT_ATTRIBUTE(field) \ + i->private_phy_attrs[count] = class_device_attr_##field; \ + i->private_phy_attrs[count].attr.mode = S_IRUGO; \ + i->private_phy_attrs[count].store = NULL; \ + i->phy_attrs[count] = &i->private_phy_attrs[count]; \ + count++ + + +/** + * sas_attach_transport -- instantiate SAS transport template + * @ft: SAS transport class function template + */ +struct scsi_transport_template * +sas_attach_transport(struct sas_function_template *ft) +{ + struct sas_internal *i; + int count; + + i = kmalloc(sizeof(struct sas_internal), GFP_KERNEL); + if (!i) + return NULL; + memset(i, 0, sizeof(struct sas_internal)); + + i->t.target_parent = sas_target_parent; + + i->t.host_attrs.ac.attrs = &i->host_attrs[0]; + i->t.host_attrs.ac.class = &sas_host_class.class; + i->t.host_attrs.ac.match = sas_host_match; + transport_container_register(&i->t.host_attrs); + i->t.host_size = sizeof(struct sas_host_attrs); + + i->phy_attr_cont.ac.class = &sas_phy_class.class; + i->phy_attr_cont.ac.attrs = &i->phy_attrs[0]; + i->phy_attr_cont.ac.match = sas_phy_match; + transport_container_register(&i->phy_attr_cont); + + i->rphy_attr_cont.ac.class = &sas_rphy_class.class; + i->rphy_attr_cont.ac.attrs = &i->rphy_attrs[0]; + i->rphy_attr_cont.ac.match = sas_rphy_match; + transport_container_register(&i->rphy_attr_cont); + + i->f = ft; + + count = 0; + i->host_attrs[count] = NULL; + + count = 0; + SETUP_PORT_ATTRIBUTE(initiator_port_protocols); + SETUP_PORT_ATTRIBUTE(target_port_protocols); + SETUP_PORT_ATTRIBUTE(device_type); + SETUP_PORT_ATTRIBUTE(sas_address); + SETUP_PORT_ATTRIBUTE(phy_identifier); + SETUP_PORT_ATTRIBUTE(port_identifier); + SETUP_PORT_ATTRIBUTE(negotiated_linkrate); + SETUP_PORT_ATTRIBUTE(minimum_linkrate_hw); + SETUP_PORT_ATTRIBUTE(minimum_linkrate); + SETUP_PORT_ATTRIBUTE(maximum_linkrate_hw); + SETUP_PORT_ATTRIBUTE(maximum_linkrate); + i->phy_attrs[count] = NULL; + + count = 0; + SETUP_RPORT_ATTRIBUTE(rphy_initiator_port_protocols); + SETUP_RPORT_ATTRIBUTE(rphy_target_port_protocols); + SETUP_RPORT_ATTRIBUTE(rphy_device_type); + SETUP_RPORT_ATTRIBUTE(rphy_sas_address); + SETUP_RPORT_ATTRIBUTE(rphy_phy_identifier); + i->rphy_attrs[count] = NULL; + + return &i->t; +} +EXPORT_SYMBOL(sas_attach_transport); + +/** + * sas_release_transport -- release SAS transport template instance + * @t: transport template instance + */ +void sas_release_transport(struct scsi_transport_template *t) +{ + struct sas_internal *i = to_sas_internal(t); + + transport_container_unregister(&i->t.host_attrs); + transport_container_unregister(&i->phy_attr_cont); + transport_container_unregister(&i->rphy_attr_cont); + + kfree(i); +} +EXPORT_SYMBOL(sas_release_transport); + +static __init int sas_transport_init(void) +{ + int error; + + error = transport_class_register(&sas_host_class); + if (error) + goto out; + error = transport_class_register(&sas_phy_class); + if (error) + goto out_unregister_transport; + error = transport_class_register(&sas_rphy_class); + if (error) + goto out_unregister_phy; + + return 0; + + out_unregister_phy: + transport_class_unregister(&sas_phy_class); + out_unregister_transport: + transport_class_unregister(&sas_host_class); + out: + return error; + +} + +static void __exit sas_transport_exit(void) +{ + transport_class_unregister(&sas_host_class); + transport_class_unregister(&sas_phy_class); + transport_class_unregister(&sas_rphy_class); +} + +MODULE_AUTHOR("Christoph Hellwig"); +MODULE_DESCRIPTION("SAS Transphy Attributes"); +MODULE_LICENSE("GPL"); + +module_init(sas_transport_init); +module_exit(sas_transport_exit); diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 7670919a087a..ef577c8c2182 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -28,14 +28,14 @@ #include "scsi_priv.h" #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> -#include <scsi/scsi_request.h> +#include <scsi/scsi_cmnd.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_spi.h> #define SPI_PRINTK(x, l, f, a...) dev_printk(l, &(x)->dev, f , ##a) -#define SPI_NUM_ATTRS 13 /* increase this if you add attributes */ +#define SPI_NUM_ATTRS 14 /* increase this if you add attributes */ #define SPI_OTHER_ATTRS 1 /* Increase this if you add "always * on" attributes */ #define SPI_HOST_ATTRS 1 @@ -106,27 +106,31 @@ static int sprint_frac(char *dest, int value, int denom) return result; } -/* Modification of scsi_wait_req that will clear UNIT ATTENTION conditions - * resulting from (likely) bus and device resets */ -static void spi_wait_req(struct scsi_request *sreq, const void *cmd, - void *buffer, unsigned bufflen) +static int spi_execute(struct scsi_device *sdev, const void *cmd, + enum dma_data_direction dir, + void *buffer, unsigned bufflen, + struct scsi_sense_hdr *sshdr) { - int i; + int i, result; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; for(i = 0; i < DV_RETRIES; i++) { - sreq->sr_request->flags |= REQ_FAILFAST; - - scsi_wait_req(sreq, cmd, buffer, bufflen, - DV_TIMEOUT, /* retries */ 1); - if (sreq->sr_result & DRIVER_SENSE) { - struct scsi_sense_hdr sshdr; - - if (scsi_request_normalize_sense(sreq, &sshdr) - && sshdr.sense_key == UNIT_ATTENTION) + result = scsi_execute(sdev, cmd, dir, buffer, bufflen, + sense, DV_TIMEOUT, /* retries */ 1, + REQ_FAILFAST); + if (result & DRIVER_SENSE) { + struct scsi_sense_hdr sshdr_tmp; + if (!sshdr) + sshdr = &sshdr_tmp; + + if (scsi_normalize_sense(sense, sizeof(*sense), + sshdr) + && sshdr->sense_key == UNIT_ATTENTION) continue; } break; } + return result; } static struct { @@ -162,7 +166,8 @@ static inline enum spi_signal_type spi_signal_to_value(const char *name) return SPI_SIGNAL_UNKNOWN; } -static int spi_host_setup(struct device *dev) +static int spi_host_setup(struct transport_container *tc, struct device *dev, + struct class_device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); @@ -196,7 +201,9 @@ static int spi_host_match(struct attribute_container *cont, return &i->t.host_attrs.ac == cont; } -static int spi_device_configure(struct device *dev) +static int spi_device_configure(struct transport_container *tc, + struct device *dev, + struct class_device *cdev) { struct scsi_device *sdev = to_scsi_device(dev); struct scsi_target *starget = sdev->sdev_target; @@ -214,7 +221,9 @@ static int spi_device_configure(struct device *dev) return 0; } -static int spi_setup_transport_attrs(struct device *dev) +static int spi_setup_transport_attrs(struct transport_container *tc, + struct device *dev, + struct class_device *cdev) { struct scsi_target *starget = to_scsi_target(dev); @@ -231,6 +240,7 @@ static int spi_setup_transport_attrs(struct device *dev) spi_rd_strm(starget) = 0; spi_rti(starget) = 0; spi_pcomp_en(starget) = 0; + spi_hold_mcs(starget) = 0; spi_dv_pending(starget) = 0; spi_initial_dv(starget) = 0; init_MUTEX(&spi_dv_sem(starget)); @@ -347,6 +357,7 @@ spi_transport_rd_attr(wr_flow, "%d\n"); spi_transport_rd_attr(rd_strm, "%d\n"); spi_transport_rd_attr(rti, "%d\n"); spi_transport_rd_attr(pcomp_en, "%d\n"); +spi_transport_rd_attr(hold_mcs, "%d\n"); /* we only care about the first child device so we return 1 */ static int child_iter(struct device *dev, void *data) @@ -539,13 +550,13 @@ enum spi_compare_returns { /* This is for read/write Domain Validation: If the device supports * an echo buffer, we do read/write tests to it */ static enum spi_compare_returns -spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer, +spi_dv_device_echo_buffer(struct scsi_device *sdev, u8 *buffer, u8 *ptr, const int retries) { - struct scsi_device *sdev = sreq->sr_device; int len = ptr - buffer; - int j, k, r; + int j, k, r, result; unsigned int pattern = 0x0000ffff; + struct scsi_sense_hdr sshdr; const char spi_write_buffer[] = { WRITE_BUFFER, 0x0a, 0, 0, 0, 0, 0, len >> 8, len & 0xff, 0 @@ -590,14 +601,12 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer, } for (r = 0; r < retries; r++) { - sreq->sr_cmd_len = 0; /* wait_req to fill in */ - sreq->sr_data_direction = DMA_TO_DEVICE; - spi_wait_req(sreq, spi_write_buffer, buffer, len); - if(sreq->sr_result || !scsi_device_online(sdev)) { - struct scsi_sense_hdr sshdr; + result = spi_execute(sdev, spi_write_buffer, DMA_TO_DEVICE, + buffer, len, &sshdr); + if(result || !scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_QUIESCE); - if (scsi_request_normalize_sense(sreq, &sshdr) + if (scsi_sense_valid(&sshdr) && sshdr.sense_key == ILLEGAL_REQUEST /* INVALID FIELD IN CDB */ && sshdr.asc == 0x24 && sshdr.ascq == 0x00) @@ -609,14 +618,13 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer, return SPI_COMPARE_SKIP_TEST; - SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", sreq->sr_result); + SPI_PRINTK(sdev->sdev_target, KERN_ERR, "Write Buffer failure %x\n", result); return SPI_COMPARE_FAILURE; } memset(ptr, 0, len); - sreq->sr_cmd_len = 0; /* wait_req to fill in */ - sreq->sr_data_direction = DMA_FROM_DEVICE; - spi_wait_req(sreq, spi_read_buffer, ptr, len); + spi_execute(sdev, spi_read_buffer, DMA_FROM_DEVICE, + ptr, len, NULL); scsi_device_set_state(sdev, SDEV_QUIESCE); if (memcmp(buffer, ptr, len) != 0) @@ -628,25 +636,22 @@ spi_dv_device_echo_buffer(struct scsi_request *sreq, u8 *buffer, /* This is for the simplest form of Domain Validation: a read test * on the inquiry data from the device */ static enum spi_compare_returns -spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer, +spi_dv_device_compare_inquiry(struct scsi_device *sdev, u8 *buffer, u8 *ptr, const int retries) { - int r; - const int len = sreq->sr_device->inquiry_len; - struct scsi_device *sdev = sreq->sr_device; + int r, result; + const int len = sdev->inquiry_len; const char spi_inquiry[] = { INQUIRY, 0, 0, 0, len, 0 }; for (r = 0; r < retries; r++) { - sreq->sr_cmd_len = 0; /* wait_req to fill in */ - sreq->sr_data_direction = DMA_FROM_DEVICE; - memset(ptr, 0, len); - spi_wait_req(sreq, spi_inquiry, ptr, len); + result = spi_execute(sdev, spi_inquiry, DMA_FROM_DEVICE, + ptr, len, NULL); - if(sreq->sr_result || !scsi_device_online(sdev)) { + if(result || !scsi_device_online(sdev)) { scsi_device_set_state(sdev, SDEV_QUIESCE); return SPI_COMPARE_FAILURE; } @@ -667,12 +672,11 @@ spi_dv_device_compare_inquiry(struct scsi_request *sreq, u8 *buffer, } static enum spi_compare_returns -spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr, +spi_dv_retrain(struct scsi_device *sdev, u8 *buffer, u8 *ptr, enum spi_compare_returns - (*compare_fn)(struct scsi_request *, u8 *, u8 *, int)) + (*compare_fn)(struct scsi_device *, u8 *, u8 *, int)) { - struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt); - struct scsi_device *sdev = sreq->sr_device; + struct spi_internal *i = to_spi_internal(sdev->host->transportt); struct scsi_target *starget = sdev->sdev_target; int period = 0, prevperiod = 0; enum spi_compare_returns retval; @@ -680,7 +684,7 @@ spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr, for (;;) { int newperiod; - retval = compare_fn(sreq, buffer, ptr, DV_LOOPS); + retval = compare_fn(sdev, buffer, ptr, DV_LOOPS); if (retval == SPI_COMPARE_SUCCESS || retval == SPI_COMPARE_SKIP_TEST) @@ -726,9 +730,9 @@ spi_dv_retrain(struct scsi_request *sreq, u8 *buffer, u8 *ptr, } static int -spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer) +spi_dv_device_get_echo_buffer(struct scsi_device *sdev, u8 *buffer) { - int l; + int l, result; /* first off do a test unit ready. This can error out * because of reservations or some other reason. If it @@ -744,18 +748,16 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer) }; - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_NONE; - /* We send a set of three TURs to clear any outstanding * unit attention conditions if they exist (Otherwise the * buffer tests won't be happy). If the TUR still fails * (reservation conflict, device not ready, etc) just * skip the write tests */ for (l = 0; ; l++) { - spi_wait_req(sreq, spi_test_unit_ready, NULL, 0); + result = spi_execute(sdev, spi_test_unit_ready, DMA_NONE, + NULL, 0, NULL); - if(sreq->sr_result) { + if(result) { if(l >= 3) return 0; } else { @@ -764,12 +766,10 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer) } } - sreq->sr_cmd_len = 0; - sreq->sr_data_direction = DMA_FROM_DEVICE; - - spi_wait_req(sreq, spi_read_buffer_descriptor, buffer, 4); + result = spi_execute(sdev, spi_read_buffer_descriptor, + DMA_FROM_DEVICE, buffer, 4, NULL); - if (sreq->sr_result) + if (result) /* Device has no echo buffer */ return 0; @@ -777,17 +777,16 @@ spi_dv_device_get_echo_buffer(struct scsi_request *sreq, u8 *buffer) } static void -spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) +spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) { - struct spi_internal *i = to_spi_internal(sreq->sr_host->transportt); - struct scsi_device *sdev = sreq->sr_device; + struct spi_internal *i = to_spi_internal(sdev->host->transportt); struct scsi_target *starget = sdev->sdev_target; int len = sdev->inquiry_len; /* first set us up for narrow async */ DV_SET(offset, 0); DV_SET(width, 0); - if (spi_dv_device_compare_inquiry(sreq, buffer, buffer, DV_LOOPS) + if (spi_dv_device_compare_inquiry(sdev, buffer, buffer, DV_LOOPS) != SPI_COMPARE_SUCCESS) { SPI_PRINTK(starget, KERN_ERR, "Domain Validation Initial Inquiry Failed\n"); /* FIXME: should probably offline the device here? */ @@ -799,7 +798,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) scsi_device_wide(sdev)) { i->f->set_width(starget, 1); - if (spi_dv_device_compare_inquiry(sreq, buffer, + if (spi_dv_device_compare_inquiry(sdev, buffer, buffer + len, DV_LOOPS) != SPI_COMPARE_SUCCESS) { @@ -820,7 +819,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) len = 0; if (scsi_device_dt(sdev)) - len = spi_dv_device_get_echo_buffer(sreq, buffer); + len = spi_dv_device_get_echo_buffer(sdev, buffer); retry: @@ -846,7 +845,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) if (len == 0) { SPI_PRINTK(starget, KERN_INFO, "Domain Validation skipping write tests\n"); - spi_dv_retrain(sreq, buffer, buffer + len, + spi_dv_retrain(sdev, buffer, buffer + len, spi_dv_device_compare_inquiry); return; } @@ -856,7 +855,7 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) len = SPI_MAX_ECHO_BUFFER_SIZE; } - if (spi_dv_retrain(sreq, buffer, buffer + len, + if (spi_dv_retrain(sdev, buffer, buffer + len, spi_dv_device_echo_buffer) == SPI_COMPARE_SKIP_TEST) { /* OK, the stupid drive can't do a write echo buffer @@ -879,16 +878,12 @@ spi_dv_device_internal(struct scsi_request *sreq, u8 *buffer) void spi_dv_device(struct scsi_device *sdev) { - struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL); struct scsi_target *starget = sdev->sdev_target; u8 *buffer; const int len = SPI_MAX_ECHO_BUFFER_SIZE*2; - if (unlikely(!sreq)) - return; - if (unlikely(scsi_device_get(sdev))) - goto out_free_req; + return; buffer = kmalloc(len, GFP_KERNEL); @@ -909,7 +904,7 @@ spi_dv_device(struct scsi_device *sdev) SPI_PRINTK(starget, KERN_INFO, "Beginning Domain Validation\n"); - spi_dv_device_internal(sreq, buffer); + spi_dv_device_internal(sdev, buffer); SPI_PRINTK(starget, KERN_INFO, "Ending Domain Validation\n"); @@ -924,8 +919,6 @@ spi_dv_device(struct scsi_device *sdev) kfree(buffer); out_put: scsi_device_put(sdev); - out_free_req: - scsi_release_request(sreq); } EXPORT_SYMBOL(spi_dv_device); @@ -1028,10 +1021,17 @@ void spi_display_xfer_agreement(struct scsi_target *starget) sprint_frac(tmp, picosec, 1000); dev_info(&starget->dev, - "%s %sSCSI %d.%d MB/s %s%s%s (%s ns, offset %d)\n", - scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10, - tp->dt ? "DT" : "ST", tp->iu ? " IU" : "", - tp->qas ? " QAS" : "", tmp, tp->offset); + "%s %sSCSI %d.%d MB/s %s%s%s%s%s%s%s%s (%s ns, offset %d)\n", + scsi, tp->width ? "WIDE " : "", kb100/10, kb100 % 10, + tp->dt ? "DT" : "ST", + tp->iu ? " IU" : "", + tp->qas ? " QAS" : "", + tp->rd_strm ? " RDSTRM" : "", + tp->rti ? " RTI" : "", + tp->wr_flow ? " WRFLOW" : "", + tp->pcomp_en ? " PCOMP" : "", + tp->hold_mcs ? " HMCS" : "", + tmp, tp->offset); } else { dev_info(&starget->dev, "%sasynchronous.\n", tp->width ? "wide " : ""); @@ -1073,6 +1073,7 @@ static int spi_device_match(struct attribute_container *cont, { struct scsi_device *sdev; struct Scsi_Host *shost; + struct spi_internal *i; if (!scsi_is_sdev_device(dev)) return 0; @@ -1085,6 +1086,9 @@ static int spi_device_match(struct attribute_container *cont, /* Note: this class has no device attributes, so it has * no per-HBA allocation and thus we don't need to distinguish * the attribute containers for the device */ + i = to_spi_internal(shost->transportt); + if (i->f->deny_binding && i->f->deny_binding(sdev->sdev_target)) + return 0; return 1; } @@ -1092,6 +1096,7 @@ static int spi_target_match(struct attribute_container *cont, struct device *dev) { struct Scsi_Host *shost; + struct scsi_target *starget; struct spi_internal *i; if (!scsi_is_target_device(dev)) @@ -1103,7 +1108,11 @@ static int spi_target_match(struct attribute_container *cont, return 0; i = to_spi_internal(shost->transportt); - + starget = to_scsi_target(dev); + + if (i->f->deny_binding && i->f->deny_binding(starget)) + return 0; + return &i->t.target_attrs.ac == cont; } @@ -1154,6 +1163,7 @@ spi_attach_transport(struct spi_function_template *ft) SETUP_ATTRIBUTE(rd_strm); SETUP_ATTRIBUTE(rti); SETUP_ATTRIBUTE(pcomp_en); + SETUP_ATTRIBUTE(hold_mcs); /* if you add an attribute but forget to increase SPI_NUM_ATTRS * this bug will trigger */ diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 0410e1bf109a..9a1dc0cea03c 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -59,7 +59,6 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_host.h> #include <scsi/scsi_ioctl.h> -#include <scsi/scsi_request.h> #include <scsi/scsicam.h> #include "scsi_logging.h" @@ -125,7 +124,7 @@ static int sd_issue_flush(struct device *, sector_t *); static void sd_end_flush(request_queue_t *, struct request *); static int sd_prepare_flush(request_queue_t *, struct request *); static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, - struct scsi_request *SRpnt, unsigned char *buffer); + unsigned char *buffer); static struct scsi_driver sd_template = { .owner = THIS_MODULE, @@ -236,6 +235,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; else if (rq->data_len) @@ -682,19 +682,13 @@ not_present: static int sd_sync_cache(struct scsi_device *sdp) { - struct scsi_request *sreq; int retries, res; + struct scsi_sense_hdr sshdr; if (!scsi_device_online(sdp)) return -ENODEV; - sreq = scsi_allocate_request(sdp, GFP_KERNEL); - if (!sreq) { - printk("FAILED\n No memory for request\n"); - return -ENOMEM; - } - sreq->sr_data_direction = DMA_NONE; for (retries = 3; retries > 0; --retries) { unsigned char cmd[10] = { 0 }; @@ -703,22 +697,20 @@ static int sd_sync_cache(struct scsi_device *sdp) * Leave the rest of the command zero to indicate * flush everything. */ - scsi_wait_req(sreq, cmd, NULL, 0, SD_TIMEOUT, SD_MAX_RETRIES); - if (sreq->sr_result == 0) + res = scsi_execute_req(sdp, cmd, DMA_NONE, NULL, 0, &sshdr, + SD_TIMEOUT, SD_MAX_RETRIES); + if (res == 0) break; } - res = sreq->sr_result; - if (res) { - printk(KERN_WARNING "FAILED\n status = %x, message = %02x, " + if (res) { printk(KERN_WARNING "FAILED\n status = %x, message = %02x, " "host = %d, driver = %02x\n ", status_byte(res), msg_byte(res), host_byte(res), driver_byte(res)); if (driver_byte(res) & DRIVER_SENSE) - scsi_print_req_sense("sd", sreq); + scsi_print_sense_hdr("sd", &sshdr); } - scsi_release_request(sreq); return res; } @@ -957,22 +949,19 @@ static void sd_rw_intr(struct scsi_cmnd * SCpnt) scsi_io_completion(SCpnt, good_bytes, block_sectors << 9); } -static int media_not_present(struct scsi_disk *sdkp, struct scsi_request *srp) +static int media_not_present(struct scsi_disk *sdkp, + struct scsi_sense_hdr *sshdr) { - struct scsi_sense_hdr sshdr; - if (!srp->sr_result) - return 0; - if (!(driver_byte(srp->sr_result) & DRIVER_SENSE)) + if (!scsi_sense_valid(sshdr)) return 0; /* not invoked for commands that could return deferred errors */ - if (scsi_request_normalize_sense(srp, &sshdr)) { - if (sshdr.sense_key != NOT_READY && - sshdr.sense_key != UNIT_ATTENTION) - return 0; - if (sshdr.asc != 0x3A) /* medium not present */ - return 0; - } + if (sshdr->sense_key != NOT_READY && + sshdr->sense_key != UNIT_ATTENTION) + return 0; + if (sshdr->asc != 0x3A) /* medium not present */ + return 0; + set_media_not_present(sdkp); return 1; } @@ -981,10 +970,10 @@ static int media_not_present(struct scsi_disk *sdkp, struct scsi_request *srp) * spinup disk - called only in sd_revalidate_disk() */ static void -sd_spinup_disk(struct scsi_disk *sdkp, char *diskname, - struct scsi_request *SRpnt, unsigned char *buffer) { +sd_spinup_disk(struct scsi_disk *sdkp, char *diskname) +{ unsigned char cmd[10]; - unsigned long spintime_value = 0; + unsigned long spintime_expire = 0; int retries, spintime; unsigned int the_result; struct scsi_sense_hdr sshdr; @@ -1001,18 +990,13 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname, cmd[0] = TEST_UNIT_READY; memset((void *) &cmd[1], 0, 9); - SRpnt->sr_cmd_len = 0; - memset(SRpnt->sr_sense_buffer, 0, - SCSI_SENSE_BUFFERSIZE); - SRpnt->sr_data_direction = DMA_NONE; + the_result = scsi_execute_req(sdkp->device, cmd, + DMA_NONE, NULL, 0, + &sshdr, SD_TIMEOUT, + SD_MAX_RETRIES); - scsi_wait_req (SRpnt, (void *) cmd, (void *) buffer, - 0/*512*/, SD_TIMEOUT, SD_MAX_RETRIES); - - the_result = SRpnt->sr_result; if (the_result) - sense_valid = scsi_request_normalize_sense( - SRpnt, &sshdr); + sense_valid = scsi_sense_valid(&sshdr); retries++; } while (retries < 3 && (!scsi_status_is_good(the_result) || @@ -1024,7 +1008,7 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname, * any media in it, don't bother with any of the rest of * this crap. */ - if (media_not_present(sdkp, SRpnt)) + if (media_not_present(sdkp, &sshdr)) return; if ((driver_byte(the_result) & DRIVER_SENSE) == 0) { @@ -1063,33 +1047,42 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname, cmd[1] = 1; /* Return immediately */ memset((void *) &cmd[2], 0, 8); cmd[4] = 1; /* Start spin cycle */ - SRpnt->sr_cmd_len = 0; - memset(SRpnt->sr_sense_buffer, 0, - SCSI_SENSE_BUFFERSIZE); - - SRpnt->sr_data_direction = DMA_NONE; - scsi_wait_req(SRpnt, (void *)cmd, - (void *) buffer, 0/*512*/, - SD_TIMEOUT, SD_MAX_RETRIES); - spintime_value = jiffies; + scsi_execute_req(sdkp->device, cmd, DMA_NONE, + NULL, 0, &sshdr, + SD_TIMEOUT, SD_MAX_RETRIES); + spintime_expire = jiffies + 100 * HZ; + spintime = 1; } - spintime = 1; /* Wait 1 second for next try */ msleep(1000); printk("."); + + /* + * Wait for USB flash devices with slow firmware. + * Yes, this sense key/ASC combination shouldn't + * occur here. It's characteristic of these devices. + */ + } else if (sense_valid && + sshdr.sense_key == UNIT_ATTENTION && + sshdr.asc == 0x28) { + if (!spintime) { + spintime_expire = jiffies + 5 * HZ; + spintime = 1; + } + /* Wait 1 second for next try */ + msleep(1000); } else { /* we don't understand the sense code, so it's * probably pointless to loop */ if(!spintime) { printk(KERN_NOTICE "%s: Unit Not Ready, " "sense:\n", diskname); - scsi_print_req_sense("", SRpnt); + scsi_print_sense_hdr("", &sshdr); } break; } - } while (spintime && - time_after(spintime_value + 100 * HZ, jiffies)); + } while (spintime && time_before_eq(jiffies, spintime_expire)); if (spintime) { if (scsi_status_is_good(the_result)) @@ -1104,14 +1097,15 @@ sd_spinup_disk(struct scsi_disk *sdkp, char *diskname, */ static void sd_read_capacity(struct scsi_disk *sdkp, char *diskname, - struct scsi_request *SRpnt, unsigned char *buffer) { + unsigned char *buffer) +{ unsigned char cmd[16]; - struct scsi_device *sdp = sdkp->device; int the_result, retries; int sector_size = 0; int longrc = 0; struct scsi_sense_hdr sshdr; int sense_valid = 0; + struct scsi_device *sdp = sdkp->device; repeat: retries = 3; @@ -1128,20 +1122,15 @@ repeat: memset((void *) buffer, 0, 8); } - SRpnt->sr_cmd_len = 0; - memset(SRpnt->sr_sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); - SRpnt->sr_data_direction = DMA_FROM_DEVICE; - - scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, - longrc ? 12 : 8, SD_TIMEOUT, SD_MAX_RETRIES); + the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE, + buffer, longrc ? 12 : 8, &sshdr, + SD_TIMEOUT, SD_MAX_RETRIES); - if (media_not_present(sdkp, SRpnt)) + if (media_not_present(sdkp, &sshdr)) return; - the_result = SRpnt->sr_result; if (the_result) - sense_valid = scsi_request_normalize_sense(SRpnt, - &sshdr); + sense_valid = scsi_sense_valid(&sshdr); retries--; } while (the_result && retries); @@ -1156,7 +1145,7 @@ repeat: driver_byte(the_result)); if (driver_byte(the_result) & DRIVER_SENSE) - scsi_print_req_sense("sd", SRpnt); + scsi_print_sense_hdr("sd", &sshdr); else printk("%s : sense not available. \n", diskname); @@ -1296,11 +1285,13 @@ got_data: /* called with buffer of length 512 */ static inline int -sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage, - unsigned char *buffer, int len, struct scsi_mode_data *data) +sd_do_mode_sense(struct scsi_device *sdp, int dbd, int modepage, + unsigned char *buffer, int len, struct scsi_mode_data *data, + struct scsi_sense_hdr *sshdr) { - return __scsi_mode_sense(SRpnt, dbd, modepage, buffer, len, - SD_TIMEOUT, SD_MAX_RETRIES, data); + return scsi_mode_sense(sdp, dbd, modepage, buffer, len, + SD_TIMEOUT, SD_MAX_RETRIES, data, + sshdr); } /* @@ -1309,25 +1300,27 @@ sd_do_mode_sense(struct scsi_request *SRpnt, int dbd, int modepage, */ static void sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, - struct scsi_request *SRpnt, unsigned char *buffer) { + unsigned char *buffer) +{ int res; + struct scsi_device *sdp = sdkp->device; struct scsi_mode_data data; set_disk_ro(sdkp->disk, 0); - if (sdkp->device->skip_ms_page_3f) { + if (sdp->skip_ms_page_3f) { printk(KERN_NOTICE "%s: assuming Write Enabled\n", diskname); return; } - if (sdkp->device->use_192_bytes_for_3f) { - res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 192, &data); + if (sdp->use_192_bytes_for_3f) { + res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 192, &data, NULL); } else { /* * First attempt: ask for all pages (0x3F), but only 4 bytes. * We have to start carefully: some devices hang if we ask * for more than is available. */ - res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 4, &data); + res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 4, &data, NULL); /* * Second attempt: ask for page 0 When only page 0 is @@ -1336,14 +1329,14 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, * CDB. */ if (!scsi_status_is_good(res)) - res = sd_do_mode_sense(SRpnt, 0, 0, buffer, 4, &data); + res = sd_do_mode_sense(sdp, 0, 0, buffer, 4, &data, NULL); /* * Third attempt: ask 255 bytes, as we did earlier. */ if (!scsi_status_is_good(res)) - res = sd_do_mode_sense(SRpnt, 0, 0x3F, buffer, 255, - &data); + res = sd_do_mode_sense(sdp, 0, 0x3F, buffer, 255, + &data, NULL); } if (!scsi_status_is_good(res)) { @@ -1365,19 +1358,20 @@ sd_read_write_protect_flag(struct scsi_disk *sdkp, char *diskname, */ static void sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, - struct scsi_request *SRpnt, unsigned char *buffer) + unsigned char *buffer) { int len = 0, res; + struct scsi_device *sdp = sdkp->device; int dbd; int modepage; struct scsi_mode_data data; struct scsi_sense_hdr sshdr; - if (sdkp->device->skip_ms_page_8) + if (sdp->skip_ms_page_8) goto defaults; - if (sdkp->device->type == TYPE_RBC) { + if (sdp->type == TYPE_RBC) { modepage = 6; dbd = 8; } else { @@ -1386,7 +1380,7 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, } /* cautiously ask */ - res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, 4, &data); + res = sd_do_mode_sense(sdp, dbd, modepage, buffer, 4, &data, &sshdr); if (!scsi_status_is_good(res)) goto bad_sense; @@ -1407,7 +1401,7 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, len += data.header_length + data.block_descriptor_length; /* Get the data */ - res = sd_do_mode_sense(SRpnt, dbd, modepage, buffer, len, &data); + res = sd_do_mode_sense(sdp, dbd, modepage, buffer, len, &data, &sshdr); if (scsi_status_is_good(res)) { const char *types[] = { @@ -1439,7 +1433,7 @@ sd_read_cache_type(struct scsi_disk *sdkp, char *diskname, } bad_sense: - if (scsi_request_normalize_sense(SRpnt, &sshdr) && + if (scsi_sense_valid(&sshdr) && sshdr.sense_key == ILLEGAL_REQUEST && sshdr.asc == 0x24 && sshdr.ascq == 0x0) printk(KERN_NOTICE "%s: cache data unavailable\n", @@ -1464,7 +1458,6 @@ static int sd_revalidate_disk(struct gendisk *disk) { struct scsi_disk *sdkp = scsi_disk(disk); struct scsi_device *sdp = sdkp->device; - struct scsi_request *sreq; unsigned char *buffer; SCSI_LOG_HLQUEUE(3, printk("sd_revalidate_disk: disk=%s\n", disk->disk_name)); @@ -1476,18 +1469,11 @@ static int sd_revalidate_disk(struct gendisk *disk) if (!scsi_device_online(sdp)) goto out; - sreq = scsi_allocate_request(sdp, GFP_KERNEL); - if (!sreq) { - printk(KERN_WARNING "(sd_revalidate_disk:) Request allocation " - "failure.\n"); - goto out; - } - buffer = kmalloc(512, GFP_KERNEL | __GFP_DMA); if (!buffer) { printk(KERN_WARNING "(sd_revalidate_disk:) Memory allocation " "failure.\n"); - goto out_release_request; + goto out; } /* defaults, until the device tells us otherwise */ @@ -1498,25 +1484,23 @@ static int sd_revalidate_disk(struct gendisk *disk) sdkp->WCE = 0; sdkp->RCD = 0; - sd_spinup_disk(sdkp, disk->disk_name, sreq, buffer); + sd_spinup_disk(sdkp, disk->disk_name); /* * Without media there is no reason to ask; moreover, some devices * react badly if we do. */ if (sdkp->media_present) { - sd_read_capacity(sdkp, disk->disk_name, sreq, buffer); + sd_read_capacity(sdkp, disk->disk_name, buffer); if (sdp->removable) sd_read_write_protect_flag(sdkp, disk->disk_name, - sreq, buffer); - sd_read_cache_type(sdkp, disk->disk_name, sreq, buffer); + buffer); + sd_read_cache_type(sdkp, disk->disk_name, buffer); } set_capacity(disk, sdkp->capacity); kfree(buffer); - out_release_request: - scsi_release_request(sreq); out: return 0; } diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 51292f269ce5..d86d5c26061d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -49,6 +49,7 @@ static int sg_version_num = 30533; /* 2 digits for each component */ #include <linux/seq_file.h> #include <linux/blkdev.h> #include <linux/delay.h> +#include <linux/scatterlist.h> #include "scsi.h" #include <scsi/scsi_dbg.h> @@ -61,7 +62,7 @@ static int sg_version_num = 30533; /* 2 digits for each component */ #ifdef CONFIG_SCSI_PROC_FS #include <linux/proc_fs.h> -static char *sg_version_date = "20050328"; +static char *sg_version_date = "20050908"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -104,8 +105,8 @@ static int sg_allow_dio = SG_ALLOW_DIO_DEF; #define SG_DEV_ARR_LUMP 32 /* amount to over allocate sg_dev_arr by */ -static int sg_add(struct class_device *); -static void sg_remove(struct class_device *); +static int sg_add(struct class_device *, struct class_interface *); +static void sg_remove(struct class_device *, struct class_interface *); static Scsi_Request *dummy_cmdp; /* only used for sizeof */ @@ -1027,8 +1028,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (sdp->detached) return -ENODEV; if (filp->f_flags & O_NONBLOCK) { - if (test_bit(SHOST_RECOVERY, - &sdp->device->host->shost_state)) + if (scsi_host_in_recovery(sdp->device->host)) return -EBUSY; } else if (!scsi_block_when_processing_errors(sdp->device)) return -EBUSY; @@ -1300,7 +1300,7 @@ sg_mmap(struct file *filp, struct vm_area_struct *vma) sg_rb_correct4mmap(rsv_schp, 1); /* do only once per fd lifetime */ sfp->mmap_called = 1; } - vma->vm_flags |= (VM_RESERVED | VM_IO); + vma->vm_flags |= VM_RESERVED; vma->vm_private_data = sfp; vma->vm_ops = &sg_mmap_vm_ops; return 0; @@ -1507,7 +1507,7 @@ static int sg_alloc(struct gendisk *disk, struct scsi_device *scsidp) } static int -sg_add(struct class_device *cl_dev) +sg_add(struct class_device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->dev); struct gendisk *disk; @@ -1551,7 +1551,7 @@ sg_add(struct class_device *cl_dev) if (sg_sysfs_valid) { struct class_device * sg_class_member; - sg_class_member = class_device_create(sg_sysfs_class, + sg_class_member = class_device_create(sg_sysfs_class, NULL, MKDEV(SCSI_GENERIC_MAJOR, k), cl_dev->dev, "%s", disk->disk_name); @@ -1583,7 +1583,7 @@ out: } static void -sg_remove(struct class_device *cl_dev) +sg_remove(struct class_device *cl_dev, struct class_interface *cl_intf) { struct scsi_device *scsidp = to_scsi_device(cl_dev->dev); Sg_device *sdp = NULL; @@ -1795,12 +1795,12 @@ st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, unsigned long uaddr, size_t count, int rw, unsigned long max_pfn) { + unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long start = uaddr >> PAGE_SHIFT; + const int nr_pages = end - start; int res, i, j; - unsigned int nr_pages; struct page **pages; - nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT; - /* User attempted Overflow! */ if ((uaddr + count) < uaddr) return -EINVAL; @@ -1887,13 +1887,17 @@ st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, int i; for (i=0; i < nr_pages; i++) { - if (dirtied && !PageReserved(sgl[i].page)) - SetPageDirty(sgl[i].page); - /* unlock_page(sgl[i].page); */ + struct page *page = sgl[i].page; + + /* XXX: just for debug. Remove when PageReserved is removed */ + BUG_ON(PageReserved(page)); + if (dirtied) + SetPageDirty(page); + /* unlock_page(page); */ /* FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ - page_cache_release(sgl[i].page); + page_cache_release(page); } return 0; @@ -1993,9 +1997,7 @@ sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) if (!p) break; } - sclp->page = virt_to_page(p); - sclp->offset = offset_in_page(p); - sclp->length = ret_sz; + sg_set_buf(sclp, p, ret_sz); SCSI_LOG_TIMEOUT(5, printk("sg_build_build: k=%d, a=0x%p, len=%d\n", k, sg_scatg2virt(sclp), ret_sz)); @@ -2645,7 +2647,7 @@ static char * sg_page_malloc(int rqSz, int lowDma, int *retSzp) { char *resp = NULL; - int page_mask; + gfp_t page_mask; int order, a_size; int resSz = rqSz; @@ -2850,8 +2852,7 @@ sg_proc_init(void) struct proc_dir_entry *pdep; struct sg_proc_leaf * leaf; - sg_proc_sgp = create_proc_entry(sg_proc_sg_dirname, - S_IFDIR | S_IRUGO | S_IXUGO, NULL); + sg_proc_sgp = proc_mkdir(sg_proc_sg_dirname, NULL); if (!sg_proc_sgp) return 1; for (k = 0; k < num_leaves; ++k) { @@ -2971,23 +2972,22 @@ static void * dev_seq_start(struct seq_file *s, loff_t *pos) { struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL); + s->private = it; if (! it) return NULL; + if (NULL == sg_dev_arr) - goto err1; + return NULL; it->index = *pos; it->max = sg_last_dev(); if (it->index >= it->max) - goto err1; + return NULL; return it; -err1: - kfree(it); - return NULL; } static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) { - struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; + struct sg_proc_deviter * it = s->private; *pos = ++it->index; return (it->index < it->max) ? it : NULL; @@ -2995,7 +2995,7 @@ static void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) static void dev_seq_stop(struct seq_file *s, void *v) { - kfree (v); + kfree(s->private); } static int sg_proc_open_dev(struct inode *inode, struct file *file) diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 2f259f249522..561901b1cf11 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -50,10 +50,10 @@ #include <scsi/scsi_dbg.h> #include <scsi/scsi_device.h> #include <scsi/scsi_driver.h> +#include <scsi/scsi_cmnd.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_host.h> #include <scsi/scsi_ioctl.h> /* For the door lock/unlock commands */ -#include <scsi/scsi_request.h> #include "scsi_logging.h" #include "sr.h" @@ -199,15 +199,7 @@ int sr_media_change(struct cdrom_device_info *cdi, int slot) /* check multisession offset etc */ sr_cd_check(cdi); - /* - * If the disk changed, the capacity will now be different, - * so we force a re-read of this information - * Force 2048 for the sector size so that filesystems won't - * be trying to use something that is too small if the disc - * has changed. - */ - cd->needs_sector_size = 1; - cd->device->sector_size = 2048; + get_sectorsize(cd); } return retval; } @@ -334,6 +326,7 @@ static int sr_init_command(struct scsi_cmnd * SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (!rq->data_len) SCpnt->sc_data_direction = DMA_NONE; else if (rq_data_dir(rq) == WRITE) @@ -538,13 +531,6 @@ static int sr_open(struct cdrom_device_info *cdi, int purpose) if (!scsi_block_when_processing_errors(sdev)) goto error_out; - /* - * If this device did not have media in the drive at boot time, then - * we would have been unable to get the sector size. Check to see if - * this is the case, and try again. - */ - if (cd->needs_sector_size) - get_sectorsize(cd); return 0; error_out: @@ -604,7 +590,6 @@ static int sr_probe(struct device *dev) cd->driver = &sr_template; cd->disk = disk; cd->capacity = 0x1fffff; - cd->needs_sector_size = 1; cd->device->changed = 1; /* force recheck CD type */ cd->use = 1; cd->readcd_known = 0; @@ -658,43 +643,30 @@ static void get_sectorsize(struct scsi_cd *cd) unsigned char *buffer; int the_result, retries = 3; int sector_size; - struct scsi_request *SRpnt = NULL; request_queue_t *queue; buffer = kmalloc(512, GFP_KERNEL | GFP_DMA); if (!buffer) goto Enomem; - SRpnt = scsi_allocate_request(cd->device, GFP_KERNEL); - if (!SRpnt) - goto Enomem; do { cmd[0] = READ_CAPACITY; memset((void *) &cmd[1], 0, 9); - /* Mark as really busy */ - SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; - SRpnt->sr_cmd_len = 0; - memset(buffer, 0, 8); /* Do the command and wait.. */ - SRpnt->sr_data_direction = DMA_FROM_DEVICE; - scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer, - 8, SR_TIMEOUT, MAX_RETRIES); + the_result = scsi_execute_req(cd->device, cmd, DMA_FROM_DEVICE, + buffer, 8, NULL, SR_TIMEOUT, + MAX_RETRIES); - the_result = SRpnt->sr_result; retries--; } while (the_result && retries); - scsi_release_request(SRpnt); - SRpnt = NULL; - if (the_result) { cd->capacity = 0x1fffff; sector_size = 2048; /* A guess, just in case */ - cd->needs_sector_size = 1; } else { #if 0 if (cdrom_get_last_written(&cd->cdi, @@ -727,7 +699,6 @@ static void get_sectorsize(struct scsi_cd *cd) printk("%s: unsupported sector size %d.\n", cd->cdi.name, sector_size); cd->capacity = 0; - cd->needs_sector_size = 1; } cd->device->sector_size = sector_size; @@ -736,7 +707,6 @@ static void get_sectorsize(struct scsi_cd *cd) * Add this so that we have the ability to correctly gauge * what the device is capable of. */ - cd->needs_sector_size = 0; set_capacity(cd->disk, cd->capacity); } @@ -748,10 +718,7 @@ out: Enomem: cd->capacity = 0x1fffff; - sector_size = 2048; /* A guess, just in case */ - cd->needs_sector_size = 1; - if (SRpnt) - scsi_release_request(SRpnt); + cd->device->sector_size = 2048; /* A guess, just in case */ goto out; } @@ -759,8 +726,8 @@ static void get_capabilities(struct scsi_cd *cd) { unsigned char *buffer; struct scsi_mode_data data; - struct scsi_request *SRpnt; unsigned char cmd[MAX_COMMAND_SIZE]; + struct scsi_sense_hdr sshdr; unsigned int the_result; int retries, rc, n; @@ -776,19 +743,11 @@ static void get_capabilities(struct scsi_cd *cd) "" }; - /* allocate a request for the TEST_UNIT_READY */ - SRpnt = scsi_allocate_request(cd->device, GFP_KERNEL); - if (!SRpnt) { - printk(KERN_WARNING "(get_capabilities:) Request allocation " - "failure.\n"); - return; - } /* allocate transfer buffer */ buffer = kmalloc(512, GFP_KERNEL | GFP_DMA); if (!buffer) { printk(KERN_ERR "sr: out of memory.\n"); - scsi_release_request(SRpnt); return; } @@ -800,24 +759,19 @@ static void get_capabilities(struct scsi_cd *cd) memset((void *)cmd, 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = DMA_NONE; - - scsi_wait_req (SRpnt, (void *) cmd, buffer, - 0, SR_TIMEOUT, MAX_RETRIES); + the_result = scsi_execute_req (cd->device, cmd, DMA_NONE, NULL, + 0, &sshdr, SR_TIMEOUT, + MAX_RETRIES); - the_result = SRpnt->sr_result; retries++; } while (retries < 5 && (!scsi_status_is_good(the_result) || - ((driver_byte(the_result) & DRIVER_SENSE) && - SRpnt->sr_sense_buffer[2] == UNIT_ATTENTION))); + (scsi_sense_valid(&sshdr) && + sshdr.sense_key == UNIT_ATTENTION))); /* ask for mode page 0x2a */ rc = scsi_mode_sense(cd->device, 0, 0x2a, buffer, 128, - SR_TIMEOUT, 3, &data); + SR_TIMEOUT, 3, &data, NULL); if (!scsi_status_is_good(rc)) { /* failed, drive doesn't have capabilities mode page */ @@ -825,7 +779,6 @@ static void get_capabilities(struct scsi_cd *cd) cd->cdi.mask |= (CDC_CD_R | CDC_CD_RW | CDC_DVD_R | CDC_DVD | CDC_DVD_RAM | CDC_SELECT_DISC | CDC_SELECT_SPEED); - scsi_release_request(SRpnt); kfree(buffer); printk("%s: scsi-1 drive\n", cd->cdi.name); return; @@ -885,7 +838,6 @@ static void get_capabilities(struct scsi_cd *cd) cd->device->writeable = 1; } - scsi_release_request(SRpnt); kfree(buffer); } diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index 0b3178007203..d2bcd99c272f 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -33,7 +33,6 @@ typedef struct scsi_cd { struct scsi_device *device; unsigned int vendor; /* vendor code, see sr_vendor.c */ unsigned long ms_offset; /* for reading multisession-CD's */ - unsigned needs_sector_size:1; /* needs to get sector size */ unsigned use:1; /* is this device still supportable */ unsigned xa_flag:1; /* CD has XA sectors ? */ unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ diff --git a/drivers/scsi/sr_ioctl.c b/drivers/scsi/sr_ioctl.c index 82d68fdb1548..6e45ac3c43c5 100644 --- a/drivers/scsi/sr_ioctl.c +++ b/drivers/scsi/sr_ioctl.c @@ -17,7 +17,7 @@ #include <scsi/scsi_eh.h> #include <scsi/scsi_host.h> #include <scsi/scsi_ioctl.h> -#include <scsi/scsi_request.h> +#include <scsi/scsi_cmnd.h> #include "sr.h" @@ -84,41 +84,37 @@ static int sr_fake_playtrkind(struct cdrom_device_info *cdi, struct cdrom_ti *ti int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) { - struct scsi_request *SRpnt; struct scsi_device *SDev; - struct request *req; + struct scsi_sense_hdr sshdr; int result, err = 0, retries = 0; + struct request_sense *sense = cgc->sense; SDev = cd->device; - SRpnt = scsi_allocate_request(SDev, GFP_KERNEL); - if (!SRpnt) { - printk(KERN_ERR "Unable to allocate SCSI request in sr_do_ioctl"); - err = -ENOMEM; - goto out; - } - SRpnt->sr_data_direction = cgc->data_direction; + + if (!sense) { + sense = kmalloc(sizeof(*sense), GFP_KERNEL); + if (!sense) { + err = -ENOMEM; + goto out; + } + } retry: if (!scsi_block_when_processing_errors(SDev)) { err = -ENODEV; - goto out_free; + goto out; } - scsi_wait_req(SRpnt, cgc->cmd, cgc->buffer, cgc->buflen, - cgc->timeout, IOCTL_RETRIES); - - req = SRpnt->sr_request; - if (SRpnt->sr_buffer && req->buffer && SRpnt->sr_buffer != req->buffer) { - memcpy(req->buffer, SRpnt->sr_buffer, SRpnt->sr_bufflen); - kfree(SRpnt->sr_buffer); - SRpnt->sr_buffer = req->buffer; - } + memset(sense, 0, sizeof(*sense)); + result = scsi_execute(SDev, cgc->cmd, cgc->data_direction, + cgc->buffer, cgc->buflen, (char *)sense, + cgc->timeout, IOCTL_RETRIES, 0); - result = SRpnt->sr_result; + scsi_normalize_sense((char *)sense, sizeof(*sense), &sshdr); /* Minimal error checking. Ignore cases we know about, and report the rest. */ if (driver_byte(result) != 0) { - switch (SRpnt->sr_sense_buffer[2] & 0xf) { + switch (sshdr.sense_key) { case UNIT_ATTENTION: SDev->changed = 1; if (!cgc->quiet) @@ -128,8 +124,8 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) err = -ENOMEDIUM; break; case NOT_READY: /* This happens if there is no disc in drive */ - if (SRpnt->sr_sense_buffer[12] == 0x04 && - SRpnt->sr_sense_buffer[13] == 0x01) { + if (sshdr.asc == 0x04 && + sshdr.ascq == 0x01) { /* sense: Logical unit is in process of becoming ready */ if (!cgc->quiet) printk(KERN_INFO "%s: CDROM not ready yet.\n", cd->cdi.name); @@ -146,37 +142,33 @@ int sr_do_ioctl(Scsi_CD *cd, struct packet_command *cgc) if (!cgc->quiet) printk(KERN_INFO "%s: CDROM not ready. Make sure there is a disc in the drive.\n", cd->cdi.name); #ifdef DEBUG - scsi_print_req_sense("sr", SRpnt); + scsi_print_sense_hdr("sr", &sshdr); #endif err = -ENOMEDIUM; break; case ILLEGAL_REQUEST: err = -EIO; - if (SRpnt->sr_sense_buffer[12] == 0x20 && - SRpnt->sr_sense_buffer[13] == 0x00) + if (sshdr.asc == 0x20 && + sshdr.ascq == 0x00) /* sense: Invalid command operation code */ err = -EDRIVE_CANT_DO_THIS; #ifdef DEBUG __scsi_print_command(cgc->cmd); - scsi_print_req_sense("sr", SRpnt); + scsi_print_sense_hdr("sr", &sshdr); #endif break; default: printk(KERN_ERR "%s: CDROM (ioctl) error, command: ", cd->cdi.name); __scsi_print_command(cgc->cmd); - scsi_print_req_sense("sr", SRpnt); + scsi_print_sense_hdr("sr", &sshdr); err = -EIO; } } - if (cgc->sense) - memcpy(cgc->sense, SRpnt->sr_sense_buffer, sizeof(*cgc->sense)); - /* Wake up a process waiting for device */ - out_free: - scsi_release_request(SRpnt); - SRpnt = NULL; out: + if (!cgc->sense) + kfree(sense); cgc->stat = err; return err; } diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 0291a8fb654d..da9766283bd7 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -17,7 +17,7 @@ Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support */ -static char *verstr = "20050501"; +static char *verstr = "20050830"; #include <linux/module.h> @@ -219,6 +219,12 @@ static int switch_partition(struct scsi_tape *); static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); +static void scsi_tape_release(struct kref *); + +#define to_scsi_tape(obj) container_of(obj, struct scsi_tape, kref) + +static DECLARE_MUTEX(st_ref_sem); + #include "osst_detect.h" #ifndef SIGS_FROM_OSST @@ -230,6 +236,46 @@ static int st_int_ioctl(struct scsi_tape *, unsigned int, unsigned long); {"OnStream", "FW-", "", "osst"} #endif +static struct scsi_tape *scsi_tape_get(int dev) +{ + struct scsi_tape *STp = NULL; + + down(&st_ref_sem); + write_lock(&st_dev_arr_lock); + + if (dev < st_dev_max && scsi_tapes != NULL) + STp = scsi_tapes[dev]; + if (!STp) goto out; + + kref_get(&STp->kref); + + if (!STp->device) + goto out_put; + + if (scsi_device_get(STp->device)) + goto out_put; + + goto out; + +out_put: + kref_put(&STp->kref, scsi_tape_release); + STp = NULL; +out: + write_unlock(&st_dev_arr_lock); + up(&st_ref_sem); + return STp; +} + +static void scsi_tape_put(struct scsi_tape *STp) +{ + struct scsi_device *sdev = STp->device; + + down(&st_ref_sem); + kref_put(&STp->kref, scsi_tape_release); + scsi_device_put(sdev); + up(&st_ref_sem); +} + struct st_reject_data { char *vendor; char *model; @@ -311,7 +357,7 @@ static int st_chk_result(struct scsi_tape *STp, struct scsi_request * SRpnt) return 0; cmdstatp = &STp->buffer->cmdstat; - st_analyze_sense(STp->buffer->last_SRpnt, cmdstatp); + st_analyze_sense(SRpnt, cmdstatp); if (cmdstatp->have_sense) scode = STp->buffer->cmdstat.sense_hdr.sense_key; @@ -399,10 +445,10 @@ static void st_sleep_done(struct scsi_cmnd * SCpnt) (STp->buffer)->cmdstat.midlevel_result = SCpnt->result; SCpnt->request->rq_status = RQ_SCSI_DONE; - (STp->buffer)->last_SRpnt = SCpnt->sc_request; DEB( STp->write_pending = 0; ) - complete(SCpnt->request->waiting); + if (SCpnt->request->waiting) + complete(SCpnt->request->waiting); } /* Do the scsi command. Waits until command performed if do_wait is true. @@ -412,8 +458,20 @@ static struct scsi_request * st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd, int bytes, int direction, int timeout, int retries, int do_wait) { + struct completion *waiting; unsigned char *bp; + /* if async, make sure there's no command outstanding */ + if (!do_wait && ((STp->buffer)->last_SRpnt)) { + printk(KERN_ERR "%s: Async command already active.\n", + tape_name(STp)); + if (signal_pending(current)) + (STp->buffer)->syscall_result = (-EINTR); + else + (STp->buffer)->syscall_result = (-EBUSY); + return NULL; + } + if (SRpnt == NULL) { SRpnt = scsi_allocate_request(STp->device, GFP_ATOMIC); if (SRpnt == NULL) { @@ -427,7 +485,13 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c } } - init_completion(&STp->wait); + /* If async IO, set last_SRpnt. This ptr tells write_behind_check + which IO is outstanding. It's nulled out when the IO completes. */ + if (!do_wait) + (STp->buffer)->last_SRpnt = SRpnt; + + waiting = &STp->wait; + init_completion(waiting); SRpnt->sr_use_sg = STp->buffer->do_dio || (bytes > (STp->buffer)->frp[0].length); if (SRpnt->sr_use_sg) { if (!STp->buffer->do_dio) @@ -438,17 +502,20 @@ st_do_scsi(struct scsi_request * SRpnt, struct scsi_tape * STp, unsigned char *c bp = (STp->buffer)->b_data; SRpnt->sr_data_direction = direction; SRpnt->sr_cmd_len = 0; - SRpnt->sr_request->waiting = &(STp->wait); + SRpnt->sr_request->waiting = waiting; SRpnt->sr_request->rq_status = RQ_SCSI_BUSY; SRpnt->sr_request->rq_disk = STp->disk; + SRpnt->sr_request->end_io = blk_end_sync_rq; STp->buffer->cmdstat.have_sense = 0; scsi_do_req(SRpnt, (void *) cmd, bp, bytes, st_sleep_done, timeout, retries); if (do_wait) { - wait_for_completion(SRpnt->sr_request->waiting); + wait_for_completion(waiting); SRpnt->sr_request->waiting = NULL; + if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) + SRpnt->sr_result |= (DRIVER_ERROR << 24); (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); } return SRpnt; @@ -465,6 +532,7 @@ static int write_behind_check(struct scsi_tape * STp) struct st_buffer *STbuffer; struct st_partstat *STps; struct st_cmdstatus *cmdstatp; + struct scsi_request *SRpnt; STbuffer = STp->buffer; if (!STbuffer->writing) @@ -478,10 +546,14 @@ static int write_behind_check(struct scsi_tape * STp) ) /* end DEB */ wait_for_completion(&(STp->wait)); - (STp->buffer)->last_SRpnt->sr_request->waiting = NULL; + SRpnt = STbuffer->last_SRpnt; + STbuffer->last_SRpnt = NULL; + SRpnt->sr_request->waiting = NULL; + if (SRpnt->sr_request->rq_status != RQ_SCSI_DONE) + SRpnt->sr_result |= (DRIVER_ERROR << 24); - (STp->buffer)->syscall_result = st_chk_result(STp, (STp->buffer)->last_SRpnt); - scsi_release_request((STp->buffer)->last_SRpnt); + (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); + scsi_release_request(SRpnt); STbuffer->buffer_bytes -= STbuffer->writing; STps = &(STp->ps[STp->partition]); @@ -1055,25 +1127,20 @@ static int st_open(struct inode *inode, struct file *filp) */ filp->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE); + if (!(STp = scsi_tape_get(dev))) + return -ENXIO; + write_lock(&st_dev_arr_lock); - if (dev >= st_dev_max || scsi_tapes == NULL || - ((STp = scsi_tapes[dev]) == NULL)) { - write_unlock(&st_dev_arr_lock); - return (-ENXIO); - } filp->private_data = STp; name = tape_name(STp); if (STp->in_use) { write_unlock(&st_dev_arr_lock); + scsi_tape_put(STp); DEB( printk(ST_DEB_MSG "%s: Device already in use.\n", name); ) return (-EBUSY); } - if(scsi_device_get(STp->device)) { - write_unlock(&st_dev_arr_lock); - return (-ENXIO); - } STp->in_use = 1; write_unlock(&st_dev_arr_lock); STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0; @@ -1118,7 +1185,7 @@ static int st_open(struct inode *inode, struct file *filp) err_out: normalize_buffer(STp->buffer); STp->in_use = 0; - scsi_device_put(STp->device); + scsi_tape_put(STp); return retval; } @@ -1250,7 +1317,7 @@ static int st_release(struct inode *inode, struct file *filp) write_lock(&st_dev_arr_lock); STp->in_use = 0; write_unlock(&st_dev_arr_lock); - scsi_device_put(STp->device); + scsi_tape_put(STp); return result; } @@ -3510,7 +3577,8 @@ static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a static struct st_buffer * new_tape_buffer(int from_initialization, int need_dma, int max_sg) { - int i, priority, got = 0, segs = 0; + int i, got = 0, segs = 0; + gfp_t priority; struct st_buffer *tb; if (from_initialization) @@ -3543,7 +3611,8 @@ static struct st_buffer * /* Try to allocate enough space in the tape buffer */ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma) { - int segs, nbr, max_segs, b_size, priority, order, got; + int segs, nbr, max_segs, b_size, order, got; + gfp_t priority; if (new_size <= STbuffer->buffer_size) return 1; @@ -3887,6 +3956,7 @@ static int st_probe(struct device *dev) goto out_put_disk; } memset(tpnt, 0, sizeof(struct scsi_tape)); + kref_init(&tpnt->kref); tpnt->disk = disk; sprintf(disk->disk_name, "st%d", i); disk->private_data = &tpnt->driver; @@ -3902,6 +3972,7 @@ static int st_probe(struct device *dev) tpnt->tape_type = MT_ISSCSI2; tpnt->buffer = buffer; + tpnt->buffer->last_SRpnt = NULL; tpnt->inited = 0; tpnt->dirty = 0; @@ -4076,15 +4147,10 @@ static int st_remove(struct device *dev) tpnt->modes[mode].cdevs[j] = NULL; } } - tpnt->device = NULL; - if (tpnt->buffer) { - tpnt->buffer->orig_frp_segs = 0; - normalize_buffer(tpnt->buffer); - kfree(tpnt->buffer); - } - put_disk(tpnt->disk); - kfree(tpnt); + down(&st_ref_sem); + kref_put(&tpnt->kref, scsi_tape_release); + up(&st_ref_sem); return 0; } } @@ -4093,6 +4159,34 @@ static int st_remove(struct device *dev) return 0; } +/** + * scsi_tape_release - Called to free the Scsi_Tape structure + * @kref: pointer to embedded kref + * + * st_ref_sem must be held entering this routine. Because it is + * called on last put, you should always use the scsi_tape_get() + * scsi_tape_put() helpers which manipulate the semaphore directly + * and never do a direct kref_put(). + **/ +static void scsi_tape_release(struct kref *kref) +{ + struct scsi_tape *tpnt = to_scsi_tape(kref); + struct gendisk *disk = tpnt->disk; + + tpnt->device = NULL; + + if (tpnt->buffer) { + tpnt->buffer->orig_frp_segs = 0; + normalize_buffer(tpnt->buffer); + kfree(tpnt->buffer); + } + + disk->private_data = NULL; + put_disk(disk); + kfree(tpnt); + return; +} + static void st_intr(struct scsi_cmnd *SCpnt) { scsi_io_completion(SCpnt, (SCpnt->result ? 0: SCpnt->bufflen), 1); @@ -4114,6 +4208,7 @@ static int st_init_command(struct scsi_cmnd *SCpnt) return 0; memcpy(SCpnt->cmnd, rq->cmd, sizeof(SCpnt->cmnd)); + SCpnt->cmd_len = rq->cmd_len; if (rq_data_dir(rq) == WRITE) SCpnt->sc_data_direction = DMA_TO_DEVICE; @@ -4149,12 +4244,10 @@ static int __init init_st(void) do_create_driverfs_files(); return 0; } - if (st_sysfs_class) - class_destroy(st_sysfs_class); unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), - ST_MAX_TAPE_ENTRIES); } + class_destroy(st_sysfs_class); printk(KERN_ERR "Unable to get major %d for SCSI tapes\n", SCSI_TAPE_MAJOR); return 1; @@ -4162,13 +4255,11 @@ static int __init init_st(void) static void __exit exit_st(void) { - if (st_sysfs_class) - class_destroy(st_sysfs_class); - st_sysfs_class = NULL; do_remove_driverfs_files(); scsi_unregister_driver(&st_template.gendrv); unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), ST_MAX_TAPE_ENTRIES); + class_destroy(st_sysfs_class); kfree(scsi_tapes); printk(KERN_INFO "st: Unloaded.\n"); } @@ -4286,7 +4377,7 @@ static void do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) snprintf(name, 10, "%s%s%s", rew ? "n" : "", STp->disk->disk_name, st_formats[i]); st_class_member = - class_device_create(st_sysfs_class, + class_device_create(st_sysfs_class, NULL, MKDEV(SCSI_TAPE_MAJOR, TAPE_MINOR(dev_num, mode, rew)), &STp->device->sdev_gendev, "%s", name); @@ -4352,12 +4443,12 @@ static int st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pag static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, unsigned long uaddr, size_t count, int rw) { + unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; + unsigned long start = uaddr >> PAGE_SHIFT; + const int nr_pages = end - start; int res, i, j; - unsigned int nr_pages; struct page **pages; - nr_pages = ((uaddr & ~PAGE_MASK) + count + ~PAGE_MASK) >> PAGE_SHIFT; - /* User attempted Overflow! */ if ((uaddr + count) < uaddr) return -EINVAL; @@ -4435,12 +4526,16 @@ static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_p int i; for (i=0; i < nr_pages; i++) { - if (dirtied && !PageReserved(sgl[i].page)) - SetPageDirty(sgl[i].page); + struct page *page = sgl[i].page; + + /* XXX: just for debug. Remove when PageReserved is removed */ + BUG_ON(PageReserved(page)); + if (dirtied) + SetPageDirty(page); /* FIXME: cache flush missing for rw==READ * FIXME: call the correct reference counting function */ - page_cache_release(sgl[i].page); + page_cache_release(page); } return 0; diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 061da111398e..790acac160bc 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -3,7 +3,7 @@ #define _ST_H #include <linux/completion.h> - +#include <linux/kref.h> /* Descriptor for analyzed sense data */ struct st_cmdstatus { @@ -156,6 +156,7 @@ struct scsi_tape { unsigned char last_sense[16]; #endif struct gendisk *disk; + struct kref kref; }; /* Bit masks for use_pf */ diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index e753ba27dc59..a1a58e1d5ad3 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -37,6 +37,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <linux/slab.h> + #include "sym_glue.h" #include "sym_nvram.h" diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.h b/drivers/scsi/sym53c8xx_2/sym_hipd.h index 3131a6bf7ab7..3a264a408216 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.h +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.h @@ -37,6 +37,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <linux/gfp.h> + #ifndef SYM_HIPD_H #define SYM_HIPD_H diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c index 5a51051e31f0..b131432c677d 100644 --- a/drivers/scsi/zalon.c +++ b/drivers/scsi/zalon.c @@ -88,7 +88,7 @@ zalon_probe(struct parisc_device *dev) struct gsc_irq gsc_irq; u32 zalon_vers; int error = -ENODEV; - void __iomem *zalon = ioremap(dev->hpa, 4096); + void __iomem *zalon = ioremap(dev->hpa.start, 4096); void __iomem *io_port = zalon + GSC_SCSI_ZALON_OFFSET; static int unit = 0; struct Scsi_Host *host; @@ -127,7 +127,7 @@ zalon_probe(struct parisc_device *dev) device.chip = zalon720_chip; device.host_id = 7; device.dev = &dev->dev; - device.slot.base = dev->hpa + GSC_SCSI_ZALON_OFFSET; + device.slot.base = dev->hpa.start + GSC_SCSI_ZALON_OFFSET; device.slot.base_v = io_port; device.slot.irq = dev->irq; device.differential = 2; |