summaryrefslogtreecommitdiff
path: root/drivers/scsi/sata_sil24.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/sata_sil24.c')
-rw-r--r--drivers/scsi/sata_sil24.c68
1 files changed, 47 insertions, 21 deletions
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 6e7728cfaf6b..4c76f05d9b65 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -216,6 +216,8 @@ enum {
SGE_DRD = (1 << 29), /* discard data read (/dev/null)
data address ignored */
+ SIL24_MAX_CMDS = 31,
+
/* board id */
BID_SIL3124 = 0,
BID_SIL3132 = 1,
@@ -223,7 +225,8 @@ enum {
/* host flags */
SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA,
+ ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA |
+ ATA_FLAG_NCQ,
SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */
IRQ_STAT_4PORTS = 0xf,
@@ -355,7 +358,8 @@ static struct scsi_host_template sil24_sht = {
.name = DRV_NAME,
.ioctl = ata_scsi_ioctl,
.queuecommand = ata_scsi_queuecmd,
- .can_queue = ATA_DEF_QUEUE,
+ .change_queue_depth = ata_scsi_change_queue_depth,
+ .can_queue = SIL24_MAX_CMDS,
.this_id = ATA_SHT_THIS_ID,
.sg_tablesize = LIBATA_MAX_PRD,
.cmd_per_lun = ATA_SHT_CMD_PER_LUN,
@@ -437,6 +441,13 @@ static struct ata_port_info sil24_port_info[] = {
},
};
+static int sil24_tag(int tag)
+{
+ if (unlikely(ata_tag_internal(tag)))
+ return 0;
+ return tag;
+}
+
static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev)
{
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
@@ -649,14 +660,17 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
struct sil24_port_priv *pp = ap->private_data;
- union sil24_cmd_block *cb = pp->cmd_block + qc->tag;
+ union sil24_cmd_block *cb;
struct sil24_prb *prb;
struct sil24_sge *sge;
u16 ctrl = 0;
+ cb = &pp->cmd_block[sil24_tag(qc->tag)];
+
switch (qc->tf.protocol) {
case ATA_PROT_PIO:
case ATA_PROT_DMA:
+ case ATA_PROT_NCQ:
case ATA_PROT_NODATA:
prb = &cb->ata.prb;
sge = cb->ata.sge;
@@ -694,12 +708,17 @@ static void sil24_qc_prep(struct ata_queued_cmd *qc)
static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
- void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
struct sil24_port_priv *pp = ap->private_data;
- dma_addr_t paddr = pp->cmd_block_dma + qc->tag * sizeof(*pp->cmd_block);
+ void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
+ unsigned int tag = sil24_tag(qc->tag);
+ dma_addr_t paddr;
+ void __iomem *activate;
- writel((u32)paddr, port + PORT_CMD_ACTIVATE);
- writel((u64)paddr >> 32, port + PORT_CMD_ACTIVATE + 4);
+ paddr = pp->cmd_block_dma + tag * sizeof(*pp->cmd_block);
+ activate = port + PORT_CMD_ACTIVATE + tag * 8;
+
+ writel((u32)paddr, activate);
+ writel((u64)paddr >> 32, activate + 4);
return 0;
}
@@ -791,9 +810,6 @@ static void sil24_error_intr(struct ata_port *ap)
/* record error info */
qc = ata_qc_from_tag(ap, ap->active_tag);
if (qc) {
- int tag = qc->tag;
- if (unlikely(ata_tag_internal(tag)))
- tag = 0;
sil24_update_tf(ap);
qc->err_mask |= err_mask;
} else
@@ -809,11 +825,17 @@ static void sil24_error_intr(struct ata_port *ap)
ata_port_abort(ap);
}
+static void sil24_finish_qc(struct ata_queued_cmd *qc)
+{
+ if (qc->flags & ATA_QCFLAG_RESULT_TF)
+ sil24_update_tf(qc->ap);
+}
+
static inline void sil24_host_intr(struct ata_port *ap)
{
void __iomem *port = (void __iomem *)ap->ioaddr.cmd_addr;
- struct ata_queued_cmd *qc;
- u32 slot_stat;
+ u32 slot_stat, qc_active;
+ int rc;
slot_stat = readl(port + PORT_SLOT_STAT);
@@ -825,18 +847,22 @@ static inline void sil24_host_intr(struct ata_port *ap)
if (ap->flags & SIL24_FLAG_PCIX_IRQ_WOC)
writel(PORT_IRQ_COMPLETE, port + PORT_IRQ_STAT);
- qc = ata_qc_from_tag(ap, ap->active_tag);
- if (qc) {
- if (qc->flags & ATA_QCFLAG_RESULT_TF)
- sil24_update_tf(ap);
- ata_qc_complete(qc);
+ qc_active = slot_stat & ~HOST_SSTAT_ATTN;
+ rc = ata_qc_complete_multiple(ap, qc_active, sil24_finish_qc);
+ if (rc > 0)
+ return;
+ if (rc < 0) {
+ struct ata_eh_info *ehi = &ap->eh_info;
+ ehi->err_mask |= AC_ERR_HSM;
+ ehi->action |= ATA_EH_SOFTRESET;
+ ata_port_freeze(ap);
return;
}
if (ata_ratelimit())
ata_port_printk(ap, KERN_INFO, "spurious interrupt "
- "(slot_stat 0x%x active_tag %d)\n",
- slot_stat, ap->active_tag);
+ "(slot_stat 0x%x active_tag %d sactive 0x%x)\n",
+ slot_stat, ap->active_tag, ap->sactive);
}
static irqreturn_t sil24_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
@@ -903,7 +929,7 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc)
static inline void sil24_cblk_free(struct sil24_port_priv *pp, struct device *dev)
{
- const size_t cb_size = sizeof(*pp->cmd_block);
+ const size_t cb_size = sizeof(*pp->cmd_block) * SIL24_MAX_CMDS;
dma_free_coherent(dev, cb_size, pp->cmd_block, pp->cmd_block_dma);
}
@@ -913,7 +939,7 @@ static int sil24_port_start(struct ata_port *ap)
struct device *dev = ap->host_set->dev;
struct sil24_port_priv *pp;
union sil24_cmd_block *cb;
- size_t cb_size = sizeof(*cb);
+ size_t cb_size = sizeof(*cb) * SIL24_MAX_CMDS;
dma_addr_t cb_dma;
int rc = -ENOMEM;