diff options
Diffstat (limited to 'drivers/scsi')
78 files changed, 10092 insertions, 1495 deletions
diff --git a/drivers/scsi/3w-9xxx.c b/drivers/scsi/3w-9xxx.c index bc6e4627c7a..a748fbfb669 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 8c8ecbed3b5..46f22cdc829 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/53c7xx.c b/drivers/scsi/53c7xx.c index 2341d27ceed..7a33c708f5b 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 c76a3b8b556..78c33180ebe 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -235,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" @@ -506,11 +513,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. @@ -522,6 +529,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 @@ -538,6 +553,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 @@ -570,6 +593,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 diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2349f902b48..8dfb9884afe 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_RAID_ATTRS) += raid_class.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_ISCSI_TCP) += iscsi_tcp.o obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o @@ -99,6 +100,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 @@ -129,6 +131,7 @@ 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 @@ -136,6 +139,7 @@ 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 d40ba0bd68a..23392ae7df8 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -91,7 +91,7 @@ #ifndef NDEBUG #define NDEBUG 0 #endif -#ifndef NDEBUG +#ifndef NDEBUG_ABORT #define NDEBUG_ABORT 0 #endif diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 44dbbe3dbe9..a913196459d 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -313,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; } @@ -460,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; @@ -497,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; } @@ -683,7 +720,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); @@ -834,8 +871,8 @@ int aac_get_adapter_info(struct aac_dev* dev) if (!(dev->raw_io_interface)) { dev->scsi_host_ptr->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); if (dev->dac_support) { /* * 38 scatter gather elements @@ -844,8 +881,8 @@ int aac_get_adapter_info(struct aac_dev* dev) (dev->max_fib_size - sizeof(struct aac_fibhdr) - sizeof(struct aac_write64) + - sizeof(struct sgmap64)) / - sizeof(struct sgmap64); + 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)) { @@ -882,7 +919,40 @@ static void io_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); - dprintk((KERN_DEBUG "io_callback[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3], 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(); @@ -923,7 +993,7 @@ static void io_callback(void *context, struct fib * fibptr) static int aac_read(struct scsi_cmnd * scsicmd, int cid) { - u32 lba; + u64 lba; u32 count; int status; @@ -935,23 +1005,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 */ @@ -964,8 +1080,8 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) 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(lba); - readcmd->block[1] = 0; + 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); @@ -992,7 +1108,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) 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; @@ -1017,7 +1133,7 @@ static int aac_read(struct scsi_cmnd * scsicmd, int cid) 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); @@ -1059,7 +1175,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; @@ -1076,13 +1192,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 */ @@ -1096,8 +1247,8 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) 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(lba); - writecmd->block[1] = 0; + 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; @@ -1124,7 +1275,7 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) 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; @@ -1149,7 +1300,7 @@ static int aac_write(struct scsi_cmnd * scsicmd, int cid) 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 */ @@ -1337,11 +1488,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; @@ -1402,7 +1560,6 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) memset(&inq_data, 0, sizeof (struct inquiry_data)); inq_data.inqd_ver = 2; /* claim compliance to SCSI-2 */ - inq_data.inqd_dtq = 0x80; /* set RMB bit to one indicating that the medium is removable */ 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 */ @@ -1424,13 +1581,55 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) 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[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; @@ -1444,6 +1643,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd) 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); @@ -1524,6 +1725,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 @@ -1531,17 +1734,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); @@ -1772,6 +1977,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 { @@ -1877,8 +2084,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); diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index bdfd4a7f950..2ebe402bc31 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 @@ -302,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 @@ -546,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. */ @@ -776,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; @@ -1012,6 +1015,7 @@ struct aac_dev /* 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; }; @@ -1362,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) @@ -1472,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 */ @@ -1707,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 */ @@ -1777,6 +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/comminit.c b/drivers/scsi/aacraid/comminit.c index 75abd045328..59a341b2aed 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -195,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) @@ -313,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)) @@ -342,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 a1d303f0348..e4d543a474a 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" @@ -269,40 +271,22 @@ static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entr /* Interrupt Moderation, only interrupt for first two entries */ if (idx != le32_to_cpu(*(q->headers.consumer))) { if (--idx == 0) { - if (qid == AdapHighCmdQueue) - idx = ADAP_HIGH_CMD_ENTRIES; - else if (qid == AdapNormCmdQueue) + if (qid == AdapNormCmdQueue) idx = ADAP_NORM_CMD_ENTRIES; - else if (qid == AdapHighRespQueue) - idx = ADAP_HIGH_RESP_ENTRIES; - else if (qid == AdapNormRespQueue) + 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", @@ -334,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)) { @@ -350,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 */ @@ -375,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 @@ -439,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; /* @@ -497,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. @@ -525,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(); @@ -622,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; @@ -652,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. */ @@ -669,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 { @@ -791,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 @@ -805,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); @@ -825,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 @@ -860,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 { @@ -869,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; /* @@ -880,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 @@ -912,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 */ @@ -929,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; } @@ -947,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 7a25dab48a9..c235d0c0e7a 100644 --- a/drivers/scsi/aacraid/linit.c +++ b/drivers/scsi/aacraid/linit.c @@ -453,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); } @@ -748,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, DMA_32BIT_MASK) || @@ -772,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; @@ -799,7 +801,9 @@ static int __devinit aac_probe_one(struct pci_dev *pdev, goto out_deinit; 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 @@ -927,8 +931,8 @@ static int __init aac_init(void) 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); diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c index 320df6cd3de..fe8187d6f58 100644 --- a/drivers/scsi/ahci.c +++ b/drivers/scsi/ahci.c @@ -216,7 +216,7 @@ 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, @@ -407,7 +407,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)); } @@ -425,7 +425,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) @@ -453,14 +453,14 @@ 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; + void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr; return (readl(mmio + PORT_TFDATA) >> 8) & 0xFF; } @@ -672,17 +672,36 @@ 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); + printk(KERN_WARNING + "ahci(%s): unhandled interrupt on port %u\n", + pci_name(pdev), 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); + printk(KERN_WARNING + "ahci(%s): interrupt on disabled port %u\n", + pci_name(pdev), i); + } } + + irq_ack |= (1 << i); } if (irq_ack) { @@ -865,22 +884,6 @@ 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; diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c index 70c5fb59c9e..d754b326786 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_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index 6b6d4e28779..95c285cc83e 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -1192,11 +1192,6 @@ ahd_platform_free(struct ahd_softc *ahd) int i, j; if (ahd->platform_data != NULL) { - if (ahd->platform_data->host != NULL) { - scsi_remove_host(ahd->platform_data->host); - scsi_host_put(ahd->platform_data->host); - } - /* destroy all of the device and target objects */ for (i = 0; i < AHD_NUM_TARGETS; i++) { starget = ahd->platform_data->starget[i]; @@ -1226,6 +1221,9 @@ ahd_platform_free(struct ahd_softc *ahd) release_mem_region(ahd->platform_data->mem_busaddr, 0x1000); } + if (ahd->platform_data->host) + scsi_host_put(ahd->platform_data->host); + free(ahd->platform_data, M_DEVBUF); } } diff --git a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c index 390b53852d4..bf360ae021a 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm_pci.c @@ -95,6 +95,9 @@ ahd_linux_pci_dev_remove(struct pci_dev *pdev) struct ahd_softc *ahd = pci_get_drvdata(pdev); u_long s; + 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); diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c index c932b3b9449..6ee1435d37f 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c @@ -1109,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 @@ -1218,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]; @@ -1251,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); } } diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.h b/drivers/scsi/aic7xxx/aic7xxx_osm.h index c5299626924..be9edbe26db 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm.h +++ b/drivers/scsi/aic7xxx/aic7xxx_osm.h @@ -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 0d44a6907dd..cb30d9c1153 100644 --- a/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c +++ b/drivers/scsi/aic7xxx/aic7xxx_osm_pci.c @@ -143,6 +143,9 @@ 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); @@ -180,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); @@ -209,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); } diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c index deec0cef88d..be021478f41 100644 --- a/drivers/scsi/ata_piix.c +++ b/drivers/scsi/ata_piix.c @@ -68,8 +68,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), @@ -147,7 +147,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, @@ -177,7 +177,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, @@ -377,7 +377,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) { @@ -401,7 +403,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)) @@ -440,7 +442,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. * @@ -566,18 +567,6 @@ 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) @@ -675,7 +664,7 @@ 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]; diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c index e6153fe5842..a8cfbef304b 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 89f43af39cf..62bae64a01c 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 bd0e1b6be1e..da6e51c7fe6 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -116,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 = @@ -560,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]; @@ -634,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: @@ -646,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; } @@ -671,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; } @@ -680,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)) { @@ -699,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) || @@ -721,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 ) || @@ -746,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; @@ -762,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)) @@ -825,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; } @@ -843,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)) { @@ -861,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); } } @@ -894,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; diff --git a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c index d72be0ce89c..3fda8d455c5 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/eata.c b/drivers/scsi/eata.c index c10e45b94b6..3d13fdee4fc 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 fa652f8aa64..d59d449a9e4 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 85503fad789..f24d84538fd 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -98,6 +98,7 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) switch (oldstate) { case SHOST_CREATED: case SHOST_RUNNING: + case SHOST_CANCEL_RECOVERY: break; default: goto illegal; @@ -107,12 +108,31 @@ int scsi_host_set_state(struct Scsi_Host *shost, enum scsi_host_state state) case SHOST_DEL: switch (oldstate) { case SHOST_CANCEL: + case SHOST_DEL_RECOVERY: break; default: goto illegal; } break; + 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; } shost->shost_state = state; return 0; @@ -134,17 +154,29 @@ EXPORT_SYMBOL(scsi_host_set_state); **/ void scsi_remove_host(struct Scsi_Host *shost) { + unsigned long flags; down(&shost->scan_mutex); - scsi_host_set_state(shost, SHOST_CANCEL); + 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_proc_host_rm(shost); - scsi_host_set_state(shost, SHOST_DEL); + 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); @@ -231,7 +263,6 @@ static void scsi_host_dev_release(struct device *dev) if (shost->work_q) destroy_workqueue(shost->work_q); - scsi_proc_hostdir_rm(shost->hostt); scsi_destroy_command_freelist(shost); kfree(shost->shost_data); @@ -256,7 +287,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; + gfp_t gfp_mask = GFP_KERNEL; + int rval; if (sht->unchecked_isa_dma && privsize) gfp_mask |= __GFP_DMA; diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c index b5dc3535557..19392f65127 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 5b14934ba86..ff25210b00b 100644 --- a/drivers/scsi/ibmvscsi/ibmvscsi.c +++ b/drivers/scsi/ibmvscsi/ibmvscsi.c @@ -727,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; + } } } diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 5cc53cd9323..f53d7b8ac33 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c @@ -48,6 +48,7 @@ #include <linux/completion.h> #include <linux/suspend.h> #include <linux/workqueue.h> +#include <linux/jiffies.h> #include <scsi/scsi.h> #include "scsi.h" #include "scsi_priv.h" @@ -62,14 +63,15 @@ 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; @@ -85,7 +87,7 @@ 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 * @@ -95,7 +97,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; @@ -153,7 +155,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; @@ -222,7 +224,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); @@ -242,7 +244,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); @@ -263,7 +265,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); @@ -283,7 +285,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); @@ -303,7 +305,7 @@ void ata_exec_command(struct ata_port *ap, struct ata_taskfile *tf) * Obtains host_set lock. */ -static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf) +static inline void ata_exec(struct ata_port *ap, const struct ata_taskfile *tf) { unsigned long flags; @@ -326,7 +328,7 @@ static inline void ata_exec(struct ata_port *ap, struct ata_taskfile *tf) * Obtains host_set lock. */ -static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf) +static void ata_tf_to_host(struct ata_port *ap, const struct ata_taskfile *tf) { ap->ops->tf_load(ap, tf); @@ -346,7 +348,7 @@ static void ata_tf_to_host(struct ata_port *ap, struct ata_taskfile *tf) * spin_lock_irqsave(host_set lock) */ -void ata_tf_to_host_nolock(struct ata_port *ap, struct ata_taskfile *tf) +void ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf) { ap->ops->tf_load(ap, tf); ap->ops->exec_command(ap, tf); @@ -556,7 +558,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, @@ -597,7 +599,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 */ @@ -615,79 +617,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[] = { @@ -869,7 +845,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, @@ -961,7 +937,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; @@ -1078,7 +1054,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 " @@ -1106,6 +1082,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 @@ -1131,7 +1132,7 @@ 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; @@ -1229,9 +1230,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; } @@ -1239,10 +1240,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); @@ -1251,32 +1250,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 */ @@ -1310,7 +1352,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))); } @@ -1496,7 +1538,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[] = { @@ -1603,7 +1791,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; @@ -1632,11 +1820,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: @@ -1910,7 +2093,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); @@ -1948,7 +2132,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; @@ -1973,9 +2157,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]; @@ -1987,14 +2171,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); } @@ -2004,14 +2188,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); } @@ -2075,7 +2259,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) { @@ -2144,6 +2328,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 * @@ -2413,32 +2701,32 @@ void ata_poll_qc_complete(struct ata_queued_cmd *qc, u8 drv_stat) /** * 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(); @@ -2448,26 +2736,29 @@ 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; @@ -2477,36 +2768,40 @@ static void ata_pio_complete (struct ata_port *ap) * 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. */ 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_poll_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. * @@ -2515,6 +2810,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) { @@ -2537,7 +2833,6 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words) * * LOCKING: * Inherited from caller. - * */ static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, @@ -2583,7 +2878,6 @@ static void ata_mmio_data_xfer(struct ata_port *ap, unsigned char *buf, * * LOCKING: * Inherited from caller. - * */ static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf, @@ -2623,7 +2917,6 @@ static void ata_pio_data_xfer(struct ata_port *ap, unsigned char *buf, * * LOCKING: * Inherited from caller. - * */ static void ata_data_xfer(struct ata_port *ap, unsigned char *buf, @@ -2655,7 +2948,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; @@ -2705,11 +2998,11 @@ static void __atapi_pio_bytes(struct ata_queued_cmd *qc, unsigned int bytes) unsigned int offset, count; if (qc->curbytes + bytes >= qc->nbytes) - ap->pio_task_state = PIO_ST_LAST; + 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, @@ -2721,13 +3014,13 @@ next_sg: unsigned int i; if (words) /* warning if bytes > 1 */ - printk(KERN_WARNING "ata%u: %u bytes trailing data\n", + 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->pio_task_state = PIO_ST_LAST; + ap->hsm_task_state = HSM_ST_LAST; return; } @@ -2776,7 +3069,6 @@ next_sg: * * LOCKING: * Inherited from caller. - * */ static void atapi_pio_bytes(struct ata_queued_cmd *qc) @@ -2808,12 +3100,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) @@ -2825,19 +3117,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; } @@ -2849,9 +3141,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_poll_qc_complete(qc, status); + ap->hsm_task_state = HSM_ST_LAST; return; } @@ -2859,7 +3149,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; } @@ -2879,7 +3169,7 @@ static void ata_pio_error(struct ata_port *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_poll_qc_complete(qc, drv_stat | ATA_ERR); } @@ -2887,82 +3177,40 @@ static void ata_pio_error(struct ata_port *ap) 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; } /** @@ -3082,14 +3330,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"); } @@ -3146,15 +3394,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, u8 drv_stat) { return 0; } @@ -3192,7 +3437,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) { @@ -3212,7 +3456,6 @@ 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) @@ -3351,7 +3594,7 @@ 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; + ap->hsm_task_state = HSM_ST; queue_work(ata_wq, &ap->pio_task); break; @@ -3577,7 +3820,7 @@ 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; } @@ -3706,7 +3949,6 @@ idle_irq: * * RETURNS: * IRQ_NONE or IRQ_HANDLED. - * */ irqreturn_t ata_interrupt (int irq, void *dev_instance, struct pt_regs *regs) @@ -3797,7 +4039,7 @@ static void atapi_packet_task(void *_data) ata_data_xfer(ap, qc->cdb, ap->cdb_len, 1); /* PIO commands are handled by polling */ - ap->pio_task_state = PIO_ST; + ap->hsm_task_state = HSM_ST; queue_work(ata_wq, &ap->pio_task); } @@ -3818,6 +4060,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) @@ -3843,6 +4086,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) @@ -3865,6 +4109,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) @@ -3892,12 +4137,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; @@ -3953,10 +4197,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) { @@ -4001,10 +4244,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; @@ -4104,7 +4346,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); @@ -4123,6 +4365,52 @@ err_out: } /** + * 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 * @@ -4176,7 +4464,7 @@ 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; @@ -4217,85 +4505,86 @@ void ata_pci_host_stop (struct ata_host_set *host_set) * 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. */ 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]); 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; } @@ -4318,13 +4607,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; @@ -4341,7 +4629,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) @@ -4349,11 +4637,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; @@ -4364,6 +4661,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; @@ -4408,10 +4706,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); + if (legacy_mode & (1 << 1)) + probe_ent2 = ata_pci_init_legacy_port(pdev, port, 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; } @@ -4449,7 +4754,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. @@ -4462,46 +4767,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; @@ -4554,6 +4828,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 @@ -4564,6 +4859,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); @@ -4594,6 +4890,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); @@ -4605,6 +4902,9 @@ 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); diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 104fd9a63e7..58858886d75 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c @@ -44,11 +44,19 @@ #include "libata.h" -typedef unsigned int (*ata_xlat_func_t)(struct ata_queued_cmd *qc, u8 *scsicmd); +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, struct scsi_device *scsidev); +ata_scsi_find_dev(struct ata_port *ap, const struct scsi_device *scsidev); +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. * @sdev: SCSI device for which BIOS geometry is to be determined @@ -182,7 +190,6 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) { struct scsi_cmnd *cmd = qc->scsicmd; u8 err = 0; - unsigned char *sb = cmd->sense_buffer; /* Based on the 3ware driver translation table */ static unsigned char sense_table[][4] = { /* BBD|ECC|ID|MAR */ @@ -225,8 +232,6 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) }; int i = 0; - cmd->result = SAM_STAT_CHECK_CONDITION; - /* * Is this an error we can process/parse */ @@ -281,11 +286,9 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) /* 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]; + ata_scsi_set_sense(cmd, sense_table[i][1] /* sk */, + sense_table[i][2] /* asc */, + sense_table[i][3] /* ascq */ ); return; } i++; @@ -300,11 +303,9 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) { 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]; + ata_scsi_set_sense(cmd, sense_table[i][1] /* sk */, + sense_table[i][2] /* asc */, + sense_table[i][3] /* ascq */ ); return; } i++; @@ -313,15 +314,12 @@ void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat) 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; + ata_scsi_set_sense(cmd, MEDIUM_ERROR, 0x11, 0x4); + /* "unrecovered read error" */ } else { - sb[12] = 0x0C; /* "write error - */ - sb[13] = 0x02; /* auto-reallocation failed" */ + ata_scsi_set_sense(cmd, MEDIUM_ERROR, 0xc, 0x2); + /* "write error - auto-reallocation failed" */ } } @@ -420,7 +418,7 @@ int ata_scsi_error(struct Scsi_Host *host) */ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, - u8 *scsicmd) + const u8 *scsicmd) { struct ata_taskfile *tf = &qc->tf; @@ -430,15 +428,26 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, ; /* ignore IMMED bit, violates sat-r05 */ } if (scsicmd[4] & 0x2) - return 1; /* LOEJ bit set not supported */ + goto invalid_fld; /* LOEJ bit set not supported */ if (((scsicmd[4] >> 4) & 0xf) != 0) - return 1; /* power conditions not supported */ + goto invalid_fld; /* power conditions not supported */ if (scsicmd[4] & 0x1) { tf->nsect = 1; /* 1 sector, lba=0 */ - tf->lbah = 0x0; - tf->lbam = 0x0; - tf->lbal = 0x0; - tf->device |= ATA_LBA; + + 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) */ @@ -453,6 +462,11 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, */ return 0; + +invalid_fld: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; } @@ -471,14 +485,14 @@ static unsigned int ata_scsi_start_stop_xlat(struct ata_queued_cmd *qc, * 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 @@ -488,6 +502,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 @@ -501,82 +608,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; - if (lba48) { - tf->command = ATA_CMD_VERIFY_EXT; + tf->hob_nsect = (n_block >> 8) & 0xff; - tf->hob_nsect = (n_sect >> 8) & 0xff; + 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; - tf->hob_lbah = (sect >> 40) & 0xff; - tf->hob_lbam = (sect >> 32) & 0xff; - tf->hob_lbal = (sect >> 24) & 0xff; + /* use LBA28 */ + tf->command = ATA_CMD_VERIFY; + + tf->device |= (block >> 24) & 0xf; + } + + tf->nsect = n_block & 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; + +invalid_fld: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x24, 0x0); + /* "Invalid field in cbd" */ + return 1; - tf->lbah = (sect >> 16) & 0xff; - tf->lbam = (sect >> 8) & 0xff; - tf->lbal = sect & 0xff; +out_of_range: + ata_scsi_set_sense(qc->scsicmd, ILLEGAL_REQUEST, 0x21, 0x0); + /* "Logical Block Address out of range" */ + return 1; - return 0; +nothing_to_do: + qc->scsicmd->result = SAM_STAT_GOOD; + return 1; } /** @@ -599,106 +734,137 @@ 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; - } - if (scsicmd[0] == READ_10 || scsicmd[0] == WRITE_10) { - if (lba48) { - tf->hob_nsect = scsicmd[7]; - tf->hob_lbal = scsicmd[2]; + /* 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); - 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; + /* 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; + } - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= 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 = scsicmd[8]; - } + if (dev->flags & ATA_DFLAG_LBA) { + tf->flags |= ATA_TFLAG_LBA; - tf->nsect = scsicmd[8]; - tf->lbal = scsicmd[5]; - tf->lbam = scsicmd[4]; - tf->lbah = scsicmd[3]; + if (dev->flags & ATA_DFLAG_LBA48) { + /* The request -may- be too large for LBA48. */ + if ((block >> 48) || (n_block > 65536)) + goto out_of_range; - VPRINTK("ten-byte command\n"); - if (qc->nsect == 0) /* we don't support length==0 cmds */ - return 1; - return 0; - } + /* use LBA48 */ + tf->flags |= ATA_TFLAG_LBA48; - if (scsicmd[0] == READ_6 || scsicmd[0] == WRITE_6) { - qc->nsect = tf->nsect = scsicmd[4]; - if (!qc->nsect) { - qc->nsect = 256; - if (lba48) - tf->hob_nsect = 1; - } + tf->hob_nsect = (n_block >> 8) & 0xff; - tf->lbal = scsicmd[3]; - tf->lbam = scsicmd[2]; - tf->lbah = scsicmd[1] & 0x1f; /* mask out reserved bits */ + tf->hob_lbah = (block >> 40) & 0xff; + tf->hob_lbam = (block >> 32) & 0xff; + tf->hob_lbal = (block >> 24) & 0xff; + } else { + /* use LBA28 */ - VPRINTK("six-byte command\n"); - return 0; - } + /* The request -may- be too large for LBA28. */ + if ((block >> 28) || (n_block > 256)) + goto out_of_range; - 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; + tf->device |= (block >> 24) & 0xf; + } - if (lba48) { - tf->hob_nsect = scsicmd[12]; - tf->hob_lbal = scsicmd[6]; - tf->hob_lbam = scsicmd[5]; - tf->hob_lbah = scsicmd[4]; + ata_rwcmd_protocol(qc); - 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; + qc->nsect = n_block; + tf->nsect = n_block & 0xff; - /* stores LBA27:24 in lower 4 bits of device reg */ - tf->device |= scsicmd[6]; + tf->lbah = (block >> 16) & 0xff; + tf->lbam = (block >> 8) & 0xff; + tf->lbal = block & 0xff; - qc->nsect = scsicmd[13]; - } + tf->device |= ATA_LBA; + } else { + /* CHS */ + u32 sect, head, cyl, track; + + /* The request -may- be too large for CHS addressing. */ + if ((block >> 28) || (n_block > 256)) + goto out_of_range; + + ata_rwcmd_protocol(qc); + + /* 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; + } - tf->nsect = scsicmd[13]; - tf->lbal = scsicmd[9]; - tf->lbam = scsicmd[8]; - tf->lbah = scsicmd[7]; + return 0; - VPRINTK("sixteen-byte command\n"); - if (qc->nsect == 0) /* we don't support length==0 cmds */ - return 1; - 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; - DPRINTK("no-byte command\n"); +nothing_to_do: + qc->scsicmd->result = SAM_STAT_GOOD; return 1; } @@ -731,6 +897,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) */ @@ -747,7 +919,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 || @@ -755,7 +927,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) @@ -770,19 +942,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; } /** @@ -849,7 +1030,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) @@ -868,12 +1050,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); } /** @@ -1179,8 +1358,16 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, * in the same manner) */ 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; @@ -1211,7 +1398,7 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, break; default: /* invalid page code */ - return 1; + goto invalid_fld; } if (six_byte) { @@ -1224,6 +1411,16 @@ unsigned int ata_scsiop_mode_sense(struct ata_scsi_args *args, u8 *rbuf, } 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; } /** @@ -1246,10 +1443,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) { @@ -1313,6 +1520,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 @@ -1330,30 +1565,84 @@ 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); } +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, u8 drv_stat) { struct scsi_cmnd *cmd = qc->scsicmd; - if (unlikely(drv_stat & (ATA_ERR | ATA_BUSY | ATA_DRQ))) { + VPRINTK("ENTER, drv_stat == 0x%x\n", drv_stat); + + if (unlikely(drv_stat & (ATA_BUSY | ATA_DRQ))) + ata_to_sense_error(qc, drv_stat); + + else if (unlikely(drv_stat & ATA_ERR)) { 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 { u8 *scsicmd = cmd->cmnd; if (scsicmd[0] == INQUIRY) { @@ -1361,15 +1650,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; } /** @@ -1384,7 +1688,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; @@ -1453,7 +1757,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; @@ -1610,7 +1914,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; @@ -1630,7 +1934,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) @@ -1640,7 +1944,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: @@ -1650,7 +1954,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: @@ -1661,7 +1965,7 @@ 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: @@ -1673,8 +1977,26 @@ void ata_scsi_simulate(u16 *id, /* 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 d608b3a0f6f..3d60190584b 100644 --- a/drivers/scsi/libata.h +++ b/drivers/scsi/libata.h @@ -39,18 +39,23 @@ struct ata_scsi_args { /* libata-core.c */ extern int atapi_enabled; +extern int ata_qc_complete_noop(struct ata_queued_cmd *qc, u8 drv_stat); 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 ata_tf_to_host_nolock(struct ata_port *ap, const struct ata_taskfile *tf); extern void swap_buf_le16(u16 *buf, unsigned int buf_words); /* libata-scsi.c */ +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 void ata_to_sense_error(struct ata_queued_cmd *qc, u8 drv_stat); extern int ata_scsi_error(struct Scsi_Host *host); extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf, @@ -76,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_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_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 0e089a42c03..acae7c48ef7 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -966,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; } @@ -1103,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 @@ -1125,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_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 0a8269d6b13..56052f4510c 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1017,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) diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 21591cb9f55..86c41981188 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 6f3cb59bf9e..0856ff7d3b3 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -285,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); @@ -1333,7 +1333,6 @@ 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; @@ -1524,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_mem.c b/drivers/scsi/lpfc/lpfc_mem.c index 0aba13ceaac..352df47bcac 100644 --- a/drivers/scsi/lpfc/lpfc_mem.c +++ b/drivers/scsi/lpfc/lpfc_mem.c @@ -39,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/megaraid.c b/drivers/scsi/megaraid.c index 6f308ebe3e7..61a6fd810bb 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 917d591d90b..7363e12663a 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 6dd99f27572..f469915b97c 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_sas.c b/drivers/scsi/megaraid/megaraid_sas.c new file mode 100644 index 00000000000..c3f63739573 --- /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 00000000000..eaec9d53142 --- /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 a4857db4f9b..b235556b7b6 100644 --- a/drivers/scsi/mesh.c +++ b/drivers/scsi/mesh.c @@ -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 89a4a0615c2..af1133104b3 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; @@ -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 00000000000..9820f272f88 --- /dev/null +++ b/drivers/scsi/pdc_adma.c @@ -0,0 +1,739 @@ +/* + * 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 "scsi.h" +#include <scsi/scsi_host.h> +#include <asm/io.h> +#include <linux/libata.h> + +#define DRV_NAME "pdc_adma" +#define DRV_VERSION "0.01" + +/* 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 */ + aRSTA = (1 << 2), /* ATA hard 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 drv_stat, status = readb(chan + ADMA_STATUS); + + if (status == 0) + continue; + handled = 1; + adma_enter_reg_mode(ap); + if ((ap->flags & ATA_FLAG_PORT_DISABLED)) + continue; + pp = ap->private_data; + if (!pp || pp->state != adma_state_pkt) + continue; + qc = ata_qc_from_tag(ap, ap->active_tag); + drv_stat = 0; + if ((status & (aPERR | aPSD | aUIRQ))) + drv_stat = ATA_ERR; + else if (pp->pkt[0] != cDONE) + drv_stat = ATA_ERR; + ata_qc_complete(qc, drv_stat); + } + 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_chk_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, 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); + goto err_out_kfree2; + } + memset(pp->pkt, 0, ADMA_PKT_BYTES); + ap->private_data = pp; + adma_reinit_engine(ap); + return 0; + +err_out_kfree2: + kfree(pp); +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) { + printk(KERN_ERR DRV_NAME + "(%s): 32-bit DMA enable failed\n", + pci_name(pdev)); + 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)); + 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++) + printk(KERN_DEBUG DRV_NAME " 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 623082d3a83..c89da7d5b6d 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/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index fe0fce71adc..fc25cd83466 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -360,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 @@ -378,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 @@ -460,9 +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 72bbaa91dc7..9791496fa78 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_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 1ed32e7b547..e451941ad81 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 *); @@ -277,7 +277,7 @@ 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 *); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index c619583e646..fbb6feee40c 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -201,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; @@ -215,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); @@ -237,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; @@ -302,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); @@ -324,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; @@ -366,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); @@ -1682,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; @@ -2066,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; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 8982978c42f..7aec93f9d42 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1325,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) @@ -1448,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); diff --git a/drivers/scsi/qla2xxx/qla_rscn.c b/drivers/scsi/qla2xxx/qla_rscn.c index bdc3bc74bbe..7534efcc891 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/qlogicpti.c b/drivers/scsi/qlogicpti.c index a917ab7475a..1fd5fc6d0fe 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/sata_mv.c b/drivers/scsi/sata_mv.c index f97e3afa97d..422e0b6f603 100644 --- a/drivers/scsi/sata_mv.c +++ b/drivers/scsi/sata_mv.c @@ -35,7 +35,7 @@ #include <asm/io.h> #define DRV_NAME "sata_mv" -#define DRV_VERSION "0.12" +#define DRV_VERSION "0.25" enum { /* BAR's are enumerated in terms of pci_resource_start() terms */ @@ -55,31 +55,61 @@ enum { MV_SATAHC_ARBTR_REG_SZ = MV_MINOR_REG_AREA_SZ, /* arbiter */ MV_PORT_REG_SZ = MV_MINOR_REG_AREA_SZ, - MV_Q_CT = 32, - MV_CRQB_SZ = 32, - MV_CRPB_SZ = 8, + MV_USE_Q_DEPTH = ATA_DEF_QUEUE, - MV_DMA_BOUNDARY = 0xffffffffU, - SATAHC_MASK = (~(MV_SATAHC_REG_SZ - 1)), + 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 port from 0-7 port */ + /* == (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_BDMA = (1 << 28), /* Basic DMA */ + 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), @@ -111,20 +141,13 @@ enum { HC_CFG_OFS = 0, HC_IRQ_CAUSE_OFS = 0x14, - CRBP_DMA_DONE = (1 << 0), /* shift by port # */ + 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_PIO_DATA_OFS = 0x100, - SHD_FEA_ERR_OFS = 0x104, - SHD_SECT_CNT_OFS = 0x108, - SHD_LBA_L_OFS = 0x10C, - SHD_LBA_M_OFS = 0x110, - SHD_LBA_H_OFS = 0x114, - SHD_DEV_HD_OFS = 0x118, - SHD_CMD_STA_OFS = 0x11C, - SHD_CTL_AST_OFS = 0x120, + 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 */ @@ -132,6 +155,11 @@ enum { /* 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, @@ -161,33 +189,85 @@ enum { 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), - /* BDMA is 6xxx part only */ - BDMA_CMD_OFS = 0x224, - BDMA_START = (1 << 0), + /* Host private flags (hp_flags) */ + MV_HP_FLAG_MSI = (1 << 0), - MV_UNDEF = 0, + /* Port private flags (pp_flags) */ + MV_PP_FLAG_EDMA_EN = (1 << 0), + MV_PP_FLAG_EDMA_DS_ACT = (1 << 1), }; -struct mv_port_priv { +/* 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; }; -struct mv_host_priv { +/* 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 u8 mv_check_err(struct ata_port *ap); static void mv_phy_reset(struct ata_port *ap); -static int mv_master_reset(void __iomem *mmio_base); +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 = { @@ -196,13 +276,13 @@ static Scsi_Host_Template mv_sht = { .ioctl = ata_scsi_ioctl, .queuecommand = ata_scsi_queuecmd, .eh_strategy_handler = ata_scsi_error, - .can_queue = ATA_DEF_QUEUE, + .can_queue = MV_USE_Q_DEPTH, .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = MV_UNDEF, + .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 = MV_UNDEF, + .use_clustering = ATA_SHT_USE_CLUSTERING, .proc_name = DRV_NAME, .dma_boundary = MV_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, @@ -210,21 +290,22 @@ static Scsi_Host_Template mv_sht = { .ordered_flush = 1, }; -static struct ata_port_operations mv_ops = { +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, + .check_err = mv_check_err, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, - .qc_prep = ata_qc_prep, - .qc_issue = ata_qc_issue_prot, + .qc_prep = mv_qc_prep, + .qc_issue = mv_qc_issue, - .eng_timeout = ata_eng_timeout, + .eng_timeout = mv_eng_timeout, .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, @@ -232,46 +313,39 @@ static struct ata_port_operations mv_ops = { .scr_read = mv_scr_read, .scr_write = mv_scr_write, - .port_start = ata_port_start, - .port_stop = ata_port_stop, - .host_stop = ata_host_stop, + .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 = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO), - .pio_mask = 0x1f, /* pio4-0 */ - .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */ + .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 = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | - MV_FLAG_DUAL_HC), - .pio_mask = 0x1f, /* pio4-0 */ - .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */ + .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 = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | - MV_FLAG_IRQ_COALESCE | MV_FLAG_BDMA), - .pio_mask = 0x1f, /* pio4-0 */ - .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */ + .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 = (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO | - MV_FLAG_IRQ_COALESCE | MV_FLAG_DUAL_HC | - MV_FLAG_BDMA), - .pio_mask = 0x1f, /* pio4-0 */ - .udma_mask = 0, /* 0x7f (udma6-0 disabled for now) */ + .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, }, }; @@ -306,12 +380,6 @@ static inline void writelfl(unsigned long data, void __iomem *addr) (void) readl(addr); /* flush to avoid PCI posted write */ } -static inline void __iomem *mv_port_addr_to_hc_base(void __iomem *port_mmio) -{ - return ((void __iomem *)((unsigned long)port_mmio & - (unsigned long)SATAHC_MASK)); -} - static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc) { return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ)); @@ -329,24 +397,150 @@ 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 flags) +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) { - return ((flags & MV_FLAG_DUAL_HC) ? 2 : 1); + 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)); } -static inline int mv_is_edma_active(struct ata_port *ap) +/** + * 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); - return (EDMA_EN & readl(port_mmio + EDMA_CMD_OFS)); + 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 */ + } } -static inline int mv_port_bdma_capable(struct ata_port *ap) +#ifdef ATA_DEBUG +static void mv_dump_mem(void __iomem *start, unsigned bytes) { - return (ap->flags & MV_FLAG_BDMA); + 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_irq_clear(struct ata_port *ap) +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) @@ -389,30 +583,37 @@ static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) } } -static int mv_master_reset(void __iomem *mmio_base) +/** + * 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; - VPRINTK("ENTER\n"); - /* Following procedure defined in PCI "main command and status * register" table. */ t = readl(reg); writel(t | STOP_PCI_MASTER, reg); - for (i = 0; i < 100; i++) { - msleep(10); + 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; /* broken HW? */ + printk(KERN_ERR DRV_NAME ": PCI master won't flush\n"); + rc = 1; goto done; } @@ -425,39 +626,399 @@ static int mv_master_reset(void __iomem *mmio_base) } 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; /* broken HW? */ + printk(KERN_ERR DRV_NAME ": can't set global reset\n"); + rc = 1; goto done; } - /* clear reset */ + /* clear reset and *reenable the PCI master* (not mentioned in spec) */ i = 5; do { - writel(t & ~GLOB_SFT_RST, reg); + 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; /* broken HW? */ + printk(KERN_ERR DRV_NAME ": can't clear global reset\n"); + rc = 1; } - - done: - VPRINTK("EXIT, rc = %i\n", rc); +done: return rc; } -static void mv_err_intr(struct ata_port *ap) +/** + * 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) { - void __iomem *port_mmio; - u32 edma_err_cause, serr = 0; + 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; + } - /* bug here b/c we got an err int on a port we don't know about, - * so there's no way to clear it + /* 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. */ - BUG_ON(NULL == ap); - port_mmio = mv_ap_base(ap); + 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); @@ -465,8 +1026,12 @@ static void mv_err_intr(struct ata_port *ap) serr = scr_read(ap, SCR_ERROR); scr_write_flush(ap, SCR_ERROR, serr); } - DPRINTK("port %u error; EDMA err cause: 0x%08x SERR: 0x%08x\n", - ap->port_no, edma_err_cause, 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); @@ -477,7 +1042,21 @@ static void mv_err_intr(struct ata_port *ap) } } -/* Handle any outstanding interrupts in a single SATAHC +/** + * 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) @@ -487,8 +1066,8 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, struct ata_port *ap; struct ata_queued_cmd *qc; u32 hc_irq_cause; - int shift, port, port0, hard_port; - u8 ata_status; + int shift, port, port0, hard_port, handled; + u8 ata_status = 0; if (hc == 0) { port0 = 0; @@ -499,7 +1078,7 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, /* 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(0, hc_mmio + HC_IRQ_CAUSE_OFS); + writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); } VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", @@ -508,35 +1087,38 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) { ap = host_set->ports[port]; hard_port = port & MV_PORT_MASK; /* range 0-3 */ - ata_status = 0xffU; + handled = 0; /* ensure ata_status is set if handled++ */ - if (((CRBP_DMA_DONE | DEV_IRQ) << hard_port) & hc_irq_cause) { - BUG_ON(NULL == ap); - /* rcv'd new resp, basic DMA complete, or ATA IRQ */ - /* This is needed to clear the ATA INTRQ. - * FIXME: don't read the status reg in EDMA mode! + 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++; } - shift = port * 2; + 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); - /* FIXME: smart to OR in ATA_ERR? */ + /* OR in ATA_ERR to ensure libata knows we took one */ ata_status = readb((void __iomem *) ap->ioaddr.status_addr) | ATA_ERR; + handled++; } - if (ap) { + 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); - BUG_ON(0xffU == ata_status); /* mark qc status appropriately */ ata_qc_complete(qc, ata_status); } @@ -545,17 +1127,30 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant, 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; + void __iomem *mmio = host_set->mmio_base; u32 irq_stat; - mmio = host_set->mmio_base; irq_stat = readl(mmio + HC_MAIN_IRQ_CAUSE_OFS); - n_hcs = mv_get_hc_count(host_set->ports[0]->flags); /* check the cases where we either have nothing pending or have read * a bogus register value which can indicate HW removal or PCI fault @@ -564,64 +1159,105 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance, 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 = 1; + handled++; } } if (PCI_ERR & irq_stat) { - /* FIXME: these are all masked by default, but still need - * to recover from them properly. - */ - } + 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_check_err - Return the error shadow register to caller. + * @ap: ATA channel to manipulate + * + * Marvell requires DMA to be stopped before accessing shadow + * registers. So we do that, then return the needed register. + * + * LOCKING: + * Inherited from caller. FIXME: protect mv_stop_dma with lock? + */ +static u8 mv_check_err(struct ata_port *ap) +{ + mv_stop_dma(ap); /* can't read shadow regs if DMA on */ + return readb((void __iomem *) ap->ioaddr.error_addr); +} + +/** + * 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]; - u32 edma = 0, bdma; + unsigned long timeout; VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio); - edma = readl(port_mmio + EDMA_CMD_OFS); - if (EDMA_EN & edma) { - /* disable EDMA if active */ - edma &= ~EDMA_EN; - writelfl(edma | EDMA_DS, port_mmio + EDMA_CMD_OFS); - udelay(1); - } else if (mv_port_bdma_capable(ap) && - (bdma = readl(port_mmio + BDMA_CMD_OFS)) & BDMA_START) { - /* disable BDMA if active */ - writelfl(bdma & ~BDMA_START, port_mmio + BDMA_CMD_OFS); - } + mv_stop_dma(ap); - writelfl(edma | ATA_RST, port_mmio + EDMA_CMD_OFS); + 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(edma & ~ATA_RST, port_mmio + EDMA_CMD_OFS); + writelfl(0, port_mmio + EDMA_CMD_OFS); - VPRINTK("Done. Now calling __sata_phy_reset()\n"); + 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 */ - __sata_phy_reset(ap); + 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)); - if (ap->flags & ATA_FLAG_PORT_DISABLED) { - VPRINTK("Port disabled pre-sig. Exiting.\n"); + 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); @@ -636,37 +1272,118 @@ static void mv_phy_reset(struct ata_port *ap) VPRINTK("EXIT\n"); } -static void mv_port_init(struct ata_ioports *port, unsigned long base) +/** + * 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, ATA_ERR); + 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) { - /* PIO related setup */ - port->data_addr = base + SHD_PIO_DATA_OFS; - port->error_addr = port->feature_addr = base + SHD_FEA_ERR_OFS; - port->nsect_addr = base + SHD_SECT_CNT_OFS; - port->lbal_addr = base + SHD_LBA_L_OFS; - port->lbam_addr = base + SHD_LBA_M_OFS; - port->lbah_addr = base + SHD_LBA_H_OFS; - port->device_addr = base + SHD_DEV_HD_OFS; - port->status_addr = port->command_addr = base + SHD_CMD_STA_OFS; - port->altstatus_addr = port->ctl_addr = base + SHD_CTL_AST_OFS; - /* unused */ + 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 */ - writel(~0, (void __iomem *)base + EDMA_ERR_IRQ_MASK_OFS); + 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((void __iomem *)base + EDMA_CFG_OFS), - readl((void __iomem *)base + EDMA_ERR_IRQ_CAUSE_OFS), - readl((void __iomem *)base + EDMA_ERR_IRQ_MASK_OFS)); + 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_master_reset(probe_ent->mmio_base)) { + if ((MV_FLAG_GLBL_SFT_RST & probe_ent->host_flags) && + mv_global_soft_reset(probe_ent->mmio_base)) { rc = 1; goto done; } @@ -676,17 +1393,27 @@ static int mv_host_init(struct ata_probe_ent *probe_ent) for (port = 0; port < probe_ent->n_ports; port++) { port_mmio = mv_port_base(mmio, port); - mv_port_init(&probe_ent->port[port], (unsigned long)port_mmio); + mv_port_init(&probe_ent->port[port], port_mmio); } for (hc = 0; hc < n_hc; hc++) { - VPRINTK("HC%i: HC config=0x%08x HC IRQ cause=0x%08x\n", hc, - readl(mv_hc_base(mmio, hc) + HC_CFG_OFS), - readl(mv_hc_base(mmio, hc) + HC_IRQ_CAUSE_OFS)); + 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); } - writel(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); - writel(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK_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", @@ -694,27 +1421,53 @@ static int mv_host_init(struct ata_probe_ent *probe_ent) readl(mmio + HC_MAIN_IRQ_MASK_OFS), readl(mmio + PCI_IRQ_CAUSE_OFS), readl(mmio + PCI_IRQ_MASK_OFS)); - - done: +done: return rc; } -/* move to PCI layer, integrate w/ MSI stuff */ -static void pci_intx(struct pci_dev *pdev, int enable) +/** + * 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) { - u16 pci_command, new; + 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; - pci_read_config_word(pdev, PCI_COMMAND, &pci_command); + /* 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); - if (enable) - new = pci_command & ~PCI_COMMAND_INTX_DISABLE; + pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &scc); + if (scc == 0) + scc_s = "SCSI"; + else if (scc == 0x01) + scc_s = "RAID"; else - new = pci_command | PCI_COMMAND_INTX_DISABLE; + scc_s = "unknown"; - if (new != pci_command) - pci_write_config_word(pdev, PCI_COMMAND, pci_command); + printk(KERN_INFO DRV_NAME + "(%s) %u slots %u ports %s mode IRQ via %s\n", + pci_name(pdev), (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; @@ -722,16 +1475,12 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) struct mv_host_priv *hpriv; unsigned int board_idx = (unsigned int)ent->driver_data; void __iomem *mmio_base; - int pci_dev_busy = 0; - int rc; + int pci_dev_busy = 0, rc; if (!printed_version++) { - printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); + printk(KERN_INFO DRV_NAME " version " DRV_VERSION "\n"); } - VPRINTK("ENTER for PCI Bus:Slot.Func=%u:%u.%u\n", pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); - rc = pci_enable_device(pdev); if (rc) { return rc; @@ -743,8 +1492,6 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out; } - pci_intx(pdev, 1); - probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL); if (probe_ent == NULL) { rc = -ENOMEM; @@ -755,8 +1502,7 @@ static int mv_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_nocache(pci_resource_start(pdev, MV_PRIMARY_BAR), - pci_resource_len(pdev, MV_PRIMARY_BAR)); + mmio_base = pci_iomap(pdev, MV_PRIMARY_BAR, 0); if (mmio_base == NULL) { rc = -ENOMEM; goto err_out_free_ent; @@ -785,37 +1531,40 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) { goto err_out_hpriv; } -/* mv_print_info(probe_ent); */ - { - int b, w; - u32 dw[4]; /* hold a line of 16b */ - VPRINTK("PCI config space:\n"); - for (b = 0; b < 0x40; ) { - for (w = 0; w < 4; w++) { - (void) pci_read_config_dword(pdev,b,&dw[w]); - b += sizeof(*dw); - } - VPRINTK("%08x %08x %08x %08x\n", - dw[0],dw[1],dw[2],dw[3]); - } + /* Enable interrupts */ + if (pci_enable_msi(pdev) == 0) { + hpriv->hp_flags |= MV_HP_FLAG_MSI; + } else { + pci_intx(pdev, 1); } - /* FIXME: check ata_device_add return value */ - ata_device_add(probe_ent); - kfree(probe_ent); + 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_hpriv: +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: - iounmap(mmio_base); - err_out_free_ent: +err_out_iounmap: + pci_iounmap(pdev, mmio_base); +err_out_free_ent: kfree(probe_ent); - err_out_regions: +err_out_regions: pci_release_regions(pdev); - err_out: +err_out: if (!pci_dev_busy) { pci_disable_device(pdev); } diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c index a1d62dee3be..1a56d6c79dd 100644 --- a/drivers/scsi/sata_nv.c +++ b/drivers/scsi/sata_nv.c @@ -29,6 +29,8 @@ * 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. @@ -132,9 +134,7 @@ enum nv_host_type GENERIC, NFORCE2, NFORCE3, - CK804, - MCP51, - MCP55 + CK804 }; static struct pci_device_id nv_pci_tbl[] = { @@ -153,11 +153,13 @@ static struct pci_device_id nv_pci_tbl[] = { { 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, MCP51 }, + 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, MCP51 }, + 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, MCP55 }, + 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 }, @@ -236,7 +238,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, @@ -329,7 +331,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)); } @@ -343,7 +345,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)); } @@ -403,7 +405,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; diff --git a/drivers/scsi/sata_promise.c b/drivers/scsi/sata_promise.c index 538ad727bd2..eee93b0016d 100644 --- a/drivers/scsi/sata_promise.c +++ b/drivers/scsi/sata_promise.c @@ -87,8 +87,8 @@ static void pdc_port_stop(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); @@ -113,7 +113,7 @@ static Scsi_Host_Template pdc_ata_sht = { .ordered_flush = 1, }; -static struct ata_port_operations pdc_sata_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, @@ -136,7 +136,7 @@ static struct ata_port_operations pdc_sata_ops = { .host_stop = ata_pci_host_stop, }; -static struct ata_port_operations pdc_pata_ops = { +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, @@ -324,7 +324,7 @@ 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)); } @@ -333,7 +333,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) @@ -438,11 +438,11 @@ static inline unsigned int pdc_host_intr( struct ata_port *ap, 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) @@ -523,8 +523,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) @@ -546,7 +546,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); @@ -554,7 +554,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); diff --git a/drivers/scsi/sata_qstor.c b/drivers/scsi/sata_qstor.c index ffcdeb68641..250dafa6bc3 100644 --- a/drivers/scsi/sata_qstor.c +++ b/drivers/scsi/sata_qstor.c @@ -51,8 +51,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 */ @@ -101,6 +99,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 { @@ -145,7 +147,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, diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c index ba98a175ee3..3a056173fb9 100644 --- a/drivers/scsi/sata_sil.c +++ b/drivers/scsi/sata_sil.c @@ -150,7 +150,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, @@ -289,7 +289,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; @@ -297,7 +297,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); } diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c new file mode 100644 index 00000000000..32d730bd5bb --- /dev/null +++ b/drivers/scsi/sata_sil24.c @@ -0,0 +1,875 @@ +/* + * 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 <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 *host_base; /* global controller control (128 bytes @BAR0) */ + void *port_base; /* port registers (4 * 8192 bytes @BAR2) */ +}; + +static u8 sil24_check_status(struct ata_port *ap); +static u8 sil24_check_err(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, + .check_err = sil24_check_err, + .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 *port = (void *)ap->ioaddr.cmd_addr; + struct sil24_prb *prb = port; + + ata_tf_from_fis(prb->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 u8 sil24_check_err(struct ata_port *ap) +{ + struct sil24_port_priv *pp = ap->private_data; + return pp->tf.feature; +} + +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 *scr_addr = (void *)ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { + void *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 *scr_addr = (void *)ap->ioaddr.scr_addr; + if (sc_reg < ARRAY_SIZE(sil24_scr_map)) { + void *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 *port = (void *)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 *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 *)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: tiemout 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, ATA_ERR); + + 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 *port = (void *)ap->ioaddr.cmd_addr; + u32 irq_stat, cmd_err, sstatus, serror; + + 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); + } else { + /* + * Other errors. libata currently doesn't have any + * mechanism to report these errors. Just turn on + * ATA_ERR. + */ + pp->tf.command = ATA_ERR; + } + + if (qc) + ata_qc_complete(qc, pp->tf.command); + + 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 *port = (void *)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, 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 *host_base = NULL, *port_base = NULL; + int i, rc; + + if (!printed_version++) + printk(KERN_DEBUG DRV_NAME " 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) { + printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n", + pci_name(pdev)); + goto out_free; + } + 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)); + 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 *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) + printk(KERN_ERR DRV_NAME + "(%s): failed to clear port RST\n", + pci_name(pdev)); + } + + /* 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)) + printk(KERN_ERR DRV_NAME + "(%s): failed to reset controller\n", + pci_name(pdev)); + } + + /* 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 43af445b3ad..057f7b98b6c 100644 --- a/drivers/scsi/sata_sis.c +++ b/drivers/scsi/sata_sis.c @@ -52,7 +52,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 */ @@ -67,6 +70,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 */ }; @@ -98,7 +102,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, @@ -139,67 +143,95 @@ 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); } } @@ -210,6 +242,8 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) u32 genctl; struct ata_port_info *ppi; int pci_dev_busy = 0; + u8 pmr; + u8 port2_start; rc = pci_enable_device(pdev); if (rc) @@ -229,7 +263,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; @@ -251,15 +285,31 @@ 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) { + printk(KERN_INFO "sata_sis: Detected SiS 180/181 chipset in SATA mode\n"); + port2_start = 64; + } + else { + printk(KERN_INFO "sata_sis: Detected SiS 180/181 chipset in combined mode\n"); + port2_start=0; + } + } + else { + printk(KERN_INFO "sata_sis: 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 d89d968beda..e0f9570bc6d 100644 --- a/drivers/scsi/sata_svw.c +++ b/drivers/scsi/sata_svw.c @@ -102,7 +102,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; @@ -297,7 +297,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, diff --git a/drivers/scsi/sata_sx4.c b/drivers/scsi/sata_sx4.c index 540a8519117..af08f4f650c 100644 --- a/drivers/scsi/sata_sx4.c +++ b/drivers/scsi/sata_sx4.c @@ -137,7 +137,7 @@ struct pdc_port_priv { }; struct pdc_host_priv { - void *dimm_mmio; + void __iomem *dimm_mmio; unsigned int doing_hdma; unsigned int hdma_prod; @@ -157,8 +157,8 @@ 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); @@ -196,7 +196,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, @@ -247,7 +247,7 @@ 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; pci_iounmap(pdev, dimm_mmio); kfree(hpriv); @@ -669,8 +669,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, @@ -747,8 +747,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 */ @@ -899,7 +899,7 @@ out: 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); @@ -907,7 +907,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); @@ -1014,7 +1014,7 @@ static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, 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); @@ -1023,8 +1023,7 @@ static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, 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; @@ -1035,7 +1034,7 @@ static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, 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); } diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c index 42e13ed8eb5..d68dc7d3422 100644 --- a/drivers/scsi/sata_uli.c +++ b/drivers/scsi/sata_uli.c @@ -90,7 +90,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, @@ -176,18 +176,6 @@ 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) { struct ata_probe_ent *probe_ent; @@ -214,7 +202,7 @@ 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; @@ -260,7 +248,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 128b996b07b..80e291a909a 100644 --- a/drivers/scsi/sata_via.c +++ b/drivers/scsi/sata_via.c @@ -109,7 +109,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, @@ -212,7 +212,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; diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c index cf94e0158a8..5af05fdf854 100644 --- a/drivers/scsi/sata_vsc.c +++ b/drivers/scsi/sata_vsc.c @@ -86,7 +86,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)); } @@ -95,16 +95,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) @@ -115,7 +115,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; @@ -231,7 +231,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, @@ -283,7 +283,7 @@ 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++) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index a780546eda9..a5711d545d7 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; @@ -1265,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 07b554affcf..e69477d1889 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -110,6 +110,7 @@ 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 */ @@ -184,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 */ diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 895c9452be4..52b348c36d5 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -50,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")); } @@ -68,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); - scsi_host_set_state(shost, SHOST_RECOVERY); shost->host_failed++; scsi_eh_wakeup(shost); + out_unlock: spin_unlock_irqrestore(shost->host_lock, flags); - return 1; + return ret; } /** @@ -176,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); } } @@ -196,8 +201,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev) { int online; - wait_event(sdev->host->host_wait, (sdev->host->shost_state != - SHOST_RECOVERY)); + wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host)); online = scsi_device_online(sdev); @@ -1441,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 @@ -1460,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__)); - scsi_host_set_state(shost, SHOST_RUNNING); + 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); @@ -1582,40 +1591,31 @@ int scsi_error_handler(void *data) { struct Scsi_Host *shost = (struct Scsi_Host *) data; int rtn; - DECLARE_MUTEX_LOCKED(sem); current->flags |= PF_NOFREEZE; - shost->eh_wait = &sem; + /* - * 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)); - - 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 (kthread_should_stop()) - 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)); @@ -1642,16 +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; + shost->ehandler = NULL; return 0; } diff --git a/drivers/scsi/scsi_ioctl.c b/drivers/scsi/scsi_ioctl.c index b7fddac8134..6a3f6aae8a9 100644 --- a/drivers/scsi/scsi_ioctl.c +++ b/drivers/scsi/scsi_ioctl.c @@ -205,7 +205,8 @@ int scsi_ioctl_send_command(struct scsi_device *sdev, 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; @@ -458,7 +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 (sdev->host->shost_state == SHOST_RECOVERY) + 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 77f2d444f7e..3ff53880978 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. @@ -339,7 +361,7 @@ int scsi_execute_req(struct scsi_device *sdev, const unsigned char *cmd, int result; if (sshdr) { - sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + sense = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_NOIO); if (!sense) return DRIVER_ERROR << 24; memset(sense, 0, SCSI_SENSE_BUFFERSIZE); @@ -424,7 +446,7 @@ void scsi_device_unbusy(struct scsi_device *sdev) spin_lock_irqsave(shost->host_lock, flags); shost->host_busy--; - if (unlikely((shost->shost_state == SHOST_RECOVERY) && + if (unlikely(scsi_host_in_recovery(shost) && shost->host_failed)) scsi_eh_wakeup(shost); spin_unlock(shost->host_lock); @@ -552,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); @@ -595,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) @@ -624,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; } } @@ -652,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; @@ -857,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 @@ -880,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 { @@ -914,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; @@ -931,7 +954,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, dev_printk(KERN_INFO, &cmd->device->sdev_gendev, "Device not ready.\n"); - cmd = scsi_end_request(cmd, 0, this_count, 1); + scsi_end_request(cmd, 0, this_count, 1); return; case VOLUME_OVERFLOW: if (!(req->flags & REQ_QUIET)) { @@ -941,7 +964,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes, __scsi_print_command(cmd->data_cmnd); scsi_print_sense("", cmd); } - cmd = scsi_end_request(cmd, 0, block_bytes, 1); + scsi_end_request(cmd, 0, block_bytes, 1); return; default: break; @@ -972,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); @@ -1016,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; @@ -1118,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 @@ -1128,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 */ @@ -1160,11 +1185,11 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) 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; } @@ -1182,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 @@ -1220,8 +1245,13 @@ 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. @@ -1231,10 +1261,11 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req) if (unlikely(!drv->init_command(cmd))) { scsi_release_buffers(cmd); scsi_put_command(cmd); - return BLKPREP_KILL; + 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) @@ -1262,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; } /* @@ -1306,7 +1340,7 @@ static inline int scsi_host_queue_ready(struct request_queue *q, struct Scsi_Host *shost, struct scsi_device *sdev) { - if (shost->shost_state == SHOST_RECOVERY) + if (scsi_host_in_recovery(shost)) return 0; if (shost->host_busy == 0 && shost->host_blocked) { /* @@ -1336,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); } /* @@ -1371,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; } @@ -1398,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; } @@ -1415,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)) @@ -1433,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. diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index ee6de1768e5..d05f778d31a 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -124,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 19c9a232a75..327c5d7e5bd 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -587,6 +587,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, 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 0; } @@ -771,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 @@ -803,9 +813,9 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, * 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)); @@ -820,9 +830,9 @@ 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; @@ -870,15 +880,15 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, 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; } @@ -1050,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]; @@ -1063,7 +1073,8 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, struct scsi_lun *lunp, *lun_data; 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. @@ -1071,15 +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; + 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) @@ -1094,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) + if (!lun_data) { + printk(ALLOC_FAILURE_MSG, __FUNCTION__); goto out; + } scsi_cmd[0] = REPORT_LUNS; @@ -1197,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", @@ -1223,13 +1240,13 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, } kfree(lun_data); - return 0; - 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; } @@ -1260,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; @@ -1276,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) @@ -1310,9 +1320,7 @@ void scsi_scan_target(struct device *parent, unsigned int channel, */ return; - starget = scsi_alloc_target(parent, channel, id); - if (!starget) return; @@ -1329,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 @@ -1358,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, @@ -1383,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, @@ -1441,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); } @@ -1484,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) { @@ -1497,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); @@ -1518,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 dae59d1da07..72a6550a056 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -57,6 +57,8 @@ static struct { { 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) { @@ -653,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; } } @@ -667,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; } } @@ -687,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); @@ -706,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); @@ -715,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 2cab556b6e8..771e97ef136 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -819,12 +819,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; @@ -834,9 +837,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); } diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c new file mode 100644 index 00000000000..1d145d2f9a3 --- /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/sd.c b/drivers/scsi/sd.c index de564b38605..9a1dc0cea03 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -235,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) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index b1b69d738d0..fd56b7ec88b 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -61,7 +61,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 = "20050901"; +static char *sg_version_date = "20050908"; static int sg_proc_init(void); static void sg_proc_cleanup(void); @@ -1027,7 +1027,7 @@ sg_ioctl(struct inode *inode, struct file *filp, if (sdp->detached) return -ENODEV; if (filp->f_flags & O_NONBLOCK) { - if (sdp->device->host->shost_state == SHOST_RECOVERY) + if (scsi_host_in_recovery(sdp->device->host)) return -EBUSY; } else if (!scsi_block_when_processing_errors(sdp->device)) return -EBUSY; @@ -1299,7 +1299,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; @@ -2644,7 +2644,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; @@ -2849,8 +2849,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) { diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index ce63fc8312d..561901b1cf1 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -326,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) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index a93308ae973..927d700f007 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -3577,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) @@ -3610,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; @@ -4206,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; |