summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/sata_sx4.c166
1 files changed, 121 insertions, 45 deletions
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c
index dce3dccced3f..1ce98afa69b6 100644
--- a/drivers/ata/sata_sx4.c
+++ b/drivers/ata/sata_sx4.c
@@ -213,8 +213,9 @@ struct pdc_host_priv {
static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
-static void pdc_eng_timeout(struct ata_port *ap);
-static void pdc_20621_phy_reset(struct ata_port *ap);
+static void pdc_error_handler(struct ata_port *ap);
+static void pdc_freeze(struct ata_port *ap);
+static void pdc_thaw(struct ata_port *ap);
static int pdc_port_start(struct ata_port *ap);
static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
@@ -233,6 +234,10 @@ static void pdc20621_put_to_dimm(struct ata_host *host,
void *psource, u32 offset, u32 size);
static void pdc20621_irq_clear(struct ata_port *ap);
static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc);
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline);
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
static struct scsi_host_template pdc_sata_sht = {
@@ -243,20 +248,24 @@ static struct scsi_host_template pdc_sata_sht = {
/* TODO: inherit from base port_ops after converting to new EH */
static struct ata_port_operations pdc_20621_ops = {
- .sff_tf_load = pdc_tf_load_mmio,
- .sff_tf_read = ata_sff_tf_read,
- .sff_check_status = ata_sff_check_status,
- .sff_exec_command = pdc_exec_command_mmio,
- .sff_dev_select = ata_sff_dev_select,
- .phy_reset = pdc_20621_phy_reset,
+ .inherits = &ata_sff_port_ops,
+
+ .check_atapi_dma = pdc_check_atapi_dma,
.qc_prep = pdc20621_qc_prep,
.qc_issue = pdc20621_qc_issue,
- .qc_fill_rtf = ata_sff_qc_fill_rtf,
- .sff_data_xfer = ata_sff_data_xfer,
- .eng_timeout = pdc_eng_timeout,
- .sff_irq_clear = pdc20621_irq_clear,
- .sff_irq_on = ata_sff_irq_on,
+
+ .freeze = pdc_freeze,
+ .thaw = pdc_thaw,
+ .softreset = pdc_softreset,
+ .error_handler = pdc_error_handler,
+ .lost_interrupt = ATA_OP_NULL,
+ .post_internal_cmd = pdc_post_internal_cmd,
+
.port_start = pdc_port_start,
+
+ .sff_tf_load = pdc_tf_load_mmio,
+ .sff_exec_command = pdc_exec_command_mmio,
+ .sff_irq_clear = pdc20621_irq_clear,
};
static const struct ata_port_info pdc_port_info[] = {
@@ -310,14 +319,6 @@ static int pdc_port_start(struct ata_port *ap)
return 0;
}
-static void pdc_20621_phy_reset(struct ata_port *ap)
-{
- VPRINTK("ENTER\n");
- ap->cbl = ATA_CBL_SATA;
- ata_port_probe(ap);
- ata_bus_reset(ap);
-}
-
static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
unsigned int portno,
unsigned int total_len)
@@ -859,40 +860,115 @@ static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
return IRQ_RETVAL(handled);
}
-static void pdc_eng_timeout(struct ata_port *ap)
+static void pdc_freeze(struct ata_port *ap)
{
- u8 drv_stat;
- struct ata_host *host = ap->host;
- struct ata_queued_cmd *qc;
- unsigned long flags;
+ void __iomem *mmio = ap->ioaddr.cmd_addr;
+ u32 tmp;
- DPRINTK("ENTER\n");
+ /* FIXME: if all 4 ATA engines are stopped, also stop HDMA engine */
- spin_lock_irqsave(&host->lock, flags);
+ tmp = readl(mmio + PDC_CTLSTAT);
+ tmp |= PDC_MASK_INT;
+ tmp &= ~PDC_DMA_ENABLE;
+ writel(tmp, mmio + PDC_CTLSTAT);
+ readl(mmio + PDC_CTLSTAT); /* flush */
+}
- qc = ata_qc_from_tag(ap, ap->link.active_tag);
+static void pdc_thaw(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->ioaddr.cmd_addr;
+ void __iomem *mmio_base;
+ u32 tmp;
- switch (qc->tf.protocol) {
- case ATA_PROT_DMA:
- case ATA_PROT_NODATA:
- ata_port_printk(ap, KERN_ERR, "command timeout\n");
- qc->err_mask |= __ac_err_mask(ata_wait_idle(ap));
- break;
+ /* FIXME: start HDMA engine, if zero ATA engines running */
- default:
- drv_stat = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
+ /* reading SEQ mask register clears IRQ */
+ mmio_base = ap->host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
+ readl(mmio_base + PDC_20621_SEQMASK);
- ata_port_printk(ap, KERN_ERR,
- "unknown timeout, cmd 0x%x stat 0x%x\n",
- qc->tf.command, drv_stat);
+ /* turn IRQ back on */
+ tmp = readl(mmio + PDC_CTLSTAT);
+ tmp &= ~PDC_MASK_INT;
+ writel(tmp, mmio + PDC_CTLSTAT);
+ readl(mmio + PDC_CTLSTAT); /* flush */
+}
- qc->err_mask |= ac_err_mask(drv_stat);
- break;
+static void pdc_reset_port(struct ata_port *ap)
+{
+ void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT;
+ unsigned int i;
+ u32 tmp;
+
+ /* FIXME: handle HDMA copy engine */
+
+ for (i = 11; i > 0; i--) {
+ tmp = readl(mmio);
+ if (tmp & PDC_RESET)
+ break;
+
+ udelay(100);
+
+ tmp |= PDC_RESET;
+ writel(tmp, mmio);
}
- spin_unlock_irqrestore(&host->lock, flags);
- ata_eh_qc_complete(qc);
- DPRINTK("EXIT\n");
+ tmp &= ~PDC_RESET;
+ writel(tmp, mmio);
+ readl(mmio); /* flush */
+}
+
+static int pdc_softreset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
+{
+ pdc_reset_port(link->ap);
+ return ata_sff_softreset(link, class, deadline);
+}
+
+static void pdc_error_handler(struct ata_port *ap)
+{
+ if (!(ap->pflags & ATA_PFLAG_FROZEN))
+ pdc_reset_port(ap);
+
+ ata_std_error_handler(ap);
+}
+
+static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ /* make DMA engine forget about the failed command */
+ if (qc->flags & ATA_QCFLAG_FAILED)
+ pdc_reset_port(ap);
+}
+
+static int pdc_check_atapi_dma(struct ata_queued_cmd *qc)
+{
+ u8 *scsicmd = qc->scsicmd->cmnd;
+ int pio = 1; /* atapi dma off by default */
+
+ /* Whitelist commands that may use DMA. */
+ switch (scsicmd[0]) {
+ case WRITE_12:
+ case WRITE_10:
+ case WRITE_6:
+ case READ_12:
+ case READ_10:
+ case READ_6:
+ case 0xad: /* READ_DVD_STRUCTURE */
+ case 0xbe: /* READ_CD */
+ pio = 0;
+ }
+ /* -45150 (FFFF4FA2) to -1 (FFFFFFFF) shall use PIO mode */
+ if (scsicmd[0] == WRITE_10) {
+ unsigned int lba =
+ (scsicmd[2] << 24) |
+ (scsicmd[3] << 16) |
+ (scsicmd[4] << 8) |
+ scsicmd[5];
+ if (lba >= 0xFFFF4FA2)
+ pio = 1;
+ }
+ return pio;
}
static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)