summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/ahci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ahci.c')
-rw-r--r--drivers/scsi/ahci.c103
1 files changed, 78 insertions, 25 deletions
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index da5bd33d982d..9a547ca9c864 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -32,13 +32,14 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/dma-mapping.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <asm/io.h>
#define DRV_NAME "ahci"
-#define DRV_VERSION "1.00"
+#define DRV_VERSION "1.01"
enum {
@@ -49,6 +50,7 @@ enum {
AHCI_CMD_SLOT_SZ = 32 * 32,
AHCI_RX_FIS_SZ = 256,
AHCI_CMD_TBL_HDR = 0x80,
+ AHCI_CMD_TBL_CDB = 0x40,
AHCI_CMD_TBL_SZ = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16),
AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ +
AHCI_RX_FIS_SZ,
@@ -133,6 +135,9 @@ enum {
PORT_CMD_ICC_ACTIVE = (0x1 << 28), /* Put i/f in active state */
PORT_CMD_ICC_PARTIAL = (0x2 << 28), /* Put i/f in partial state */
PORT_CMD_ICC_SLUMBER = (0x6 << 28), /* Put i/f in slumber state */
+
+ /* hpriv->flags bits */
+ AHCI_FLAG_MSI = (1 << 0),
};
struct ahci_cmd_hdr {
@@ -182,6 +187,7 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc);
static u8 ahci_check_status(struct ata_port *ap);
static u8 ahci_check_err(struct ata_port *ap);
static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+static void ahci_remove_one (struct pci_dev *pdev);
static Scsi_Host_Template ahci_sht = {
.module = THIS_MODULE,
@@ -271,7 +277,7 @@ static struct pci_driver ahci_pci_driver = {
.name = DRV_NAME,
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
- .remove = ata_pci_remove_one,
+ .remove = ahci_remove_one,
};
@@ -289,6 +295,8 @@ static void ahci_host_stop(struct ata_host_set *host_set)
{
struct ahci_host_priv *hpriv = host_set->private_data;
kfree(hpriv);
+
+ ata_host_stop(host_set);
}
static int ahci_port_start(struct ata_port *ap)
@@ -503,7 +511,8 @@ static void ahci_fill_sg(struct ata_queued_cmd *qc)
static void ahci_qc_prep(struct ata_queued_cmd *qc)
{
- struct ahci_port_priv *pp = qc->ap->private_data;
+ struct ata_port *ap = qc->ap;
+ struct ahci_port_priv *pp = ap->private_data;
u32 opts;
const u32 cmd_fis_len = 5; /* five dwords */
@@ -515,18 +524,8 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
opts = (qc->n_elem << 16) | cmd_fis_len;
if (qc->tf.flags & ATA_TFLAG_WRITE)
opts |= AHCI_CMD_WRITE;
-
- switch (qc->tf.protocol) {
- case ATA_PROT_ATAPI:
- case ATA_PROT_ATAPI_NODATA:
- case ATA_PROT_ATAPI_DMA:
+ if (is_atapi_taskfile(&qc->tf))
opts |= AHCI_CMD_ATAPI;
- break;
-
- default:
- /* do nothing */
- break;
- }
pp->cmd_slot[0].opts = cpu_to_le32(opts);
pp->cmd_slot[0].status = 0;
@@ -538,6 +537,10 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
* a SATA Register - Host to Device command FIS.
*/
ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0);
+ if (opts & AHCI_CMD_ATAPI) {
+ memset(pp->cmd_tbl + AHCI_CMD_TBL_CDB, 0, 32);
+ memcpy(pp->cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, ap->cdb_len);
+ }
if (!(qc->flags & ATA_QCFLAG_DMAMAP))
return;
@@ -792,8 +795,6 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
return rc;
}
}
-
- hpriv->flags |= HOST_CAP_64;
} else {
rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
if (rc) {
@@ -876,15 +877,19 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
}
/* move to PCI layer, integrate w/ MSI stuff */
-static void pci_enable_intx(struct pci_dev *pdev)
+static void pci_intx(struct pci_dev *pdev, int enable)
{
- u16 pci_command;
+ u16 pci_command, new;
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
- if (pci_command & PCI_COMMAND_INTX_DISABLE) {
- pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+
+ 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)
@@ -966,7 +971,7 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
unsigned long base;
void *mmio_base;
unsigned int board_idx = (unsigned int) ent->driver_data;
- int pci_dev_busy = 0;
+ int have_msi, pci_dev_busy = 0;
int rc;
VPRINTK("ENTER\n");
@@ -984,12 +989,17 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
goto err_out;
}
- pci_enable_intx(pdev);
+ if (pci_enable_msi(pdev) == 0)
+ have_msi = 1;
+ else {
+ pci_intx(pdev, 1);
+ have_msi = 0;
+ }
probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
if (probe_ent == NULL) {
rc = -ENOMEM;
- goto err_out_regions;
+ goto err_out_msi;
}
memset(probe_ent, 0, sizeof(*probe_ent));
@@ -1022,6 +1032,9 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
probe_ent->mmio_base = mmio_base;
probe_ent->private_data = hpriv;
+ if (have_msi)
+ hpriv->flags |= AHCI_FLAG_MSI;
+
/* initialize adapter */
rc = ahci_host_init(probe_ent);
if (rc)
@@ -1041,7 +1054,11 @@ err_out_iounmap:
iounmap(mmio_base);
err_out_free_ent:
kfree(probe_ent);
-err_out_regions:
+err_out_msi:
+ if (have_msi)
+ pci_disable_msi(pdev);
+ else
+ pci_intx(pdev, 0);
pci_release_regions(pdev);
err_out:
if (!pci_dev_busy)
@@ -1049,6 +1066,42 @@ err_out:
return rc;
}
+static void ahci_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 ahci_host_priv *hpriv = host_set->private_data;
+ struct ata_port *ap;
+ unsigned int i;
+ int have_msi;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ ap = host_set->ports[i];
+
+ scsi_remove_host(ap->host);
+ }
+
+ have_msi = hpriv->flags & AHCI_FLAG_MSI;
+ 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);
+ scsi_host_put(ap->host);
+ }
+
+ host_set->ops->host_stop(host_set);
+ kfree(host_set);
+
+ if (have_msi)
+ pci_disable_msi(pdev);
+ else
+ pci_intx(pdev, 0);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ dev_set_drvdata(dev, NULL);
+}
static int __init ahci_init(void)
{
OpenPOWER on IntegriCloud