aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/3w-9xxx.c83
-rw-r--r--drivers/scsi/3w-9xxx.h17
-rw-r--r--drivers/scsi/53c7xx.c4
-rw-r--r--drivers/scsi/Kconfig34
-rw-r--r--drivers/scsi/Makefile4
-rw-r--r--drivers/scsi/NCR5380.c2
-rw-r--r--drivers/scsi/aacraid/aachba.c283
-rw-r--r--drivers/scsi/aacraid/aacraid.h19
-rw-r--r--drivers/scsi/aacraid/comminit.c17
-rw-r--r--drivers/scsi/aacraid/commsup.c581
-rw-r--r--drivers/scsi/aacraid/linit.c14
-rw-r--r--drivers/scsi/ahci.c57
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c3
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c8
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm_pci.c3
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c17
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.h2
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm_pci.c11
-rw-r--r--drivers/scsi/ata_piix.c29
-rw-r--r--drivers/scsi/atp870u.c6
-rw-r--r--drivers/scsi/atp870u.h5
-rw-r--r--drivers/scsi/ch.c29
-rw-r--r--drivers/scsi/cpqfcTSinit.c3
-rw-r--r--drivers/scsi/eata.c2
-rw-r--r--drivers/scsi/fd_mcs.c2
-rw-r--r--drivers/scsi/hosts.c40
-rw-r--r--drivers/scsi/ibmmca.c3
-rw-r--r--drivers/scsi/ibmvscsi/ibmvscsi.c10
-rw-r--r--drivers/scsi/libata-core.c988
-rw-r--r--drivers/scsi/libata-scsi.c730
-rw-r--r--drivers/scsi/libata.h19
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c22
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c7
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h17
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c9
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c2
-rw-r--r--drivers/scsi/megaraid.c70
-rw-r--r--drivers/scsi/megaraid/Kconfig.megaraid9
-rw-r--r--drivers/scsi/megaraid/Makefile1
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.c2806
-rw-r--r--drivers/scsi/megaraid/megaraid_sas.h1142
-rw-r--r--drivers/scsi/mesh.c29
-rw-r--r--drivers/scsi/osst.c19
-rw-r--r--drivers/scsi/pdc_adma.c739
-rw-r--r--drivers/scsi/pluto.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c18
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h4
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c27
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c3
-rw-r--r--drivers/scsi/qla2xxx/qla_rscn.c4
-rw-r--r--drivers/scsi/qlogicpti.c39
-rw-r--r--drivers/scsi/sata_mv.c1147
-rw-r--r--drivers/scsi/sata_nv.c22
-rw-r--r--drivers/scsi/sata_promise.c26
-rw-r--r--drivers/scsi/sata_qstor.c8
-rw-r--r--drivers/scsi/sata_sil.c6
-rw-r--r--drivers/scsi/sata_sil24.c875
-rw-r--r--drivers/scsi/sata_sis.c100
-rw-r--r--drivers/scsi/sata_svw.c4
-rw-r--r--drivers/scsi/sata_sx4.c29
-rw-r--r--drivers/scsi/sata_uli.c18
-rw-r--r--drivers/scsi/sata_via.c4
-rw-r--r--drivers/scsi/sata_vsc.c14
-rw-r--r--drivers/scsi/scsi.c13
-rw-r--r--drivers/scsi/scsi_devinfo.c2
-rw-r--r--drivers/scsi/scsi_error.c80
-rw-r--r--drivers/scsi/scsi_ioctl.c5
-rw-r--r--drivers/scsi/scsi_lib.c141
-rw-r--r--drivers/scsi/scsi_priv.h1
-rw-r--r--drivers/scsi/scsi_scan.c204
-rw-r--r--drivers/scsi/scsi_sysfs.c39
-rw-r--r--drivers/scsi/scsi_transport_fc.c13
-rw-r--r--drivers/scsi/scsi_transport_sas.c819
-rw-r--r--drivers/scsi/sd.c1
-rw-r--r--drivers/scsi/sg.c11
-rw-r--r--drivers/scsi/sr.c1
-rw-r--r--drivers/scsi/st.c7
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, &params, sizeof(params)))
+ if (copy_to_user(argp, &params, 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(&regs->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(&regs->outbound_intr_mask) & (~0x00000001);
+ writel(mask, &regs->outbound_intr_mask);
+
+ /* Dummy readl to force pci flush */
+ readl(&regs->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(&reg_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,
+ &reg_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, &reg_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(&reg_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(&reg_set->outbound_msg_0) & 0x00FFFF;
+ instance->max_num_sge = (readl(&reg_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;