diff options
Diffstat (limited to 'drivers/ata/sata_sx4.c')
| -rw-r--r-- | drivers/ata/sata_sx4.c | 255 |
1 files changed, 151 insertions, 104 deletions
diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index ec04b8d3c79..39b5de60a1f 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -1,7 +1,7 @@ /* * sata_sx4.c - Promise SATA * - * Maintained by: Jeff Garzik <jgarzik@pobox.com> + * Maintained by: Tejun Heo <tj@kernel.org> * Please ALWAYS copy linux-ide@vger.kernel.org * on emails. * @@ -81,7 +81,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/pci.h> -#include <linux/init.h> +#include <linux/slab.h> #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -193,6 +193,7 @@ enum { PDC_TIMER_MASK_INT, }; +#define ECC_ERASE_BUF_SZ (128 * 1024) struct pdc_port_priv { u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512]; @@ -213,8 +214,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 +235,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,30 +249,33 @@ 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[] = { /* board_20621 */ { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_SRST | ATA_FLAG_MMIO | - ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, - .pio_mask = 0x1f, /* pio0-4 */ - .mwdma_mask = 0x07, /* mwdma0-2 */ + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_ATAPI | + ATA_FLAG_PIO_POLLING, + .pio_mask = ATA_PIO4, + .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA6, .port_ops = &pdc_20621_ops, }, @@ -291,11 +300,6 @@ static int pdc_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; struct pdc_port_priv *pp; - int rc; - - rc = ata_port_start(ap); - if (rc) - return rc; pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); if (!pp) @@ -310,17 +314,8 @@ 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) +static inline void pdc20621_ata_sg(u8 *buf, unsigned int portno, + unsigned int total_len) { u32 addr; unsigned int dw = PDC_DIMM_APKT_PRD >> 2; @@ -340,9 +335,8 @@ static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf, buf32[dw], buf32[dw + 1]); } -static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf, - unsigned int portno, - unsigned int total_len) +static inline void pdc20621_host_sg(u8 *buf, unsigned int portno, + unsigned int total_len) { u32 addr; unsigned int dw = PDC_DIMM_HPKT_PRD >> 2; @@ -489,10 +483,10 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc) /* * Build ATA, host DMA packets */ - pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + pdc20621_host_sg(&pp->dimm_buf[0], portno, total_len); pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno); - pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len); + pdc20621_ata_sg(&pp->dimm_buf[0], portno, total_len); i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno); if (qc->tf.flags & ATA_TFLAG_LBA48) @@ -686,8 +680,11 @@ static void pdc20621_packet_start(struct ata_queued_cmd *qc) static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc) { switch (qc->tf.protocol) { - case ATA_PROT_DMA: case ATA_PROT_NODATA: + if (qc->tf.flags & ATA_TFLAG_POLLING) + break; + /*FALLTHROUGH*/ + case ATA_PROT_DMA: pdc20621_packet_start(qc); return 0; @@ -786,12 +783,7 @@ static inline unsigned int pdc20621_host_intr(struct ata_port *ap, static void pdc20621_irq_clear(struct ata_port *ap) { - struct ata_host *host = ap->host; - void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; - - mmio += PDC_CHIP0_OFS; - - readl(mmio + PDC_20621_SEQMASK); + ioread8(ap->ioaddr.status_addr); } static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance) @@ -839,8 +831,7 @@ static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance) ap = host->ports[port_no]; tmp = mask & (1 << i); VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp); - if (tmp && ap && - !(ap->flags & ATA_FLAG_DISABLED)) { + if (tmp && ap) { struct ata_queued_cmd *qc; qc = ata_qc_from_tag(ap, ap->link.active_tag); @@ -859,46 +850,119 @@ 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; + 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); + /* clear IRQ */ + ioread8(ap->ioaddr.status_addr); + + /* turn IRQ back on */ + tmp = readl(mmio + PDC_CTLSTAT); + tmp &= ~PDC_MASK_INT; + writel(tmp, mmio + PDC_CTLSTAT); + readl(mmio + PDC_CTLSTAT); /* flush */ +} - ata_port_printk(ap, KERN_ERR, - "unknown timeout, cmd 0x%x stat 0x%x\n", - qc->tf.command, drv_stat); +static void pdc_reset_port(struct ata_port *ap) +{ + void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT; + unsigned int i; + u32 tmp; - qc->err_mask |= ac_err_mask(drv_stat); - break; + /* 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_sff_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) { WARN_ON(tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); + tf->protocol == ATAPI_PROT_DMA); ata_sff_tf_load(ap, tf); } @@ -906,7 +970,7 @@ 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) { WARN_ON(tf->protocol == ATA_PROT_DMA || - tf->protocol == ATA_PROT_NODATA); + tf->protocol == ATAPI_PROT_DMA); ata_sff_exec_command(ap, tf); } @@ -956,8 +1020,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, idx++; dist = ((long) (window_size - (offset + size))) >= 0 ? size : (long) (window_size - offset); - memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4), - dist); + memcpy_fromio(psource, dimm_mmio + offset / 4, dist); psource += dist; size -= dist; @@ -966,8 +1029,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - window_size / 4); + memcpy_fromio(psource, dimm_mmio, window_size / 4); psource += window_size; size -= window_size; idx++; @@ -978,8 +1040,7 @@ static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, readl(mmio + PDC_GENERAL_CTLR); writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR); readl(mmio + PDC_DIMM_WINDOW_CTLR); - memcpy_fromio((char *) psource, (char *) (dimm_mmio), - size / 4); + memcpy_fromio(psource, dimm_mmio, size / 4); } } #endif @@ -1208,7 +1269,6 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host) { int speed, size, length; u32 addr, spd0, pci_status; - u32 tmp = 0; u32 time_period = 0; u32 tcount = 0; u32 ticks = 0; @@ -1323,14 +1383,17 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host) pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { + void *buf; VPRINTK("Start ECC initialization\n"); addr = 0; length = size * 1024 * 1024; + buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL); while (addr < length) { - pdc20621_put_to_dimm(host, (void *) &tmp, addr, - sizeof(u32)); - addr += sizeof(u32); + pdc20621_put_to_dimm(host, buf, addr, + ECC_ERASE_BUF_SZ); + addr += ECC_ERASE_BUF_SZ; } + kfree(buf); VPRINTK("Finish ECC initialization\n"); } return 0; @@ -1371,15 +1434,13 @@ static void pdc_20621_init(struct ata_host *host) static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { - static int printed_version; const struct ata_port_info *ppi[] = { &pdc_port_info[ent->driver_data], NULL }; struct ata_host *host; struct pdc_host_priv *hpriv; int i, rc; - if (!printed_version++) - dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + ata_print_version_once(&pdev->dev, DRV_VERSION); /* allocate host */ host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); @@ -1431,24 +1492,10 @@ static int pdc_sata_init_one(struct pci_dev *pdev, IRQF_SHARED, &pdc_sata_sht); } - -static int __init pdc_sata_init(void) -{ - return pci_register_driver(&pdc_sata_pci_driver); -} - - -static void __exit pdc_sata_exit(void) -{ - pci_unregister_driver(&pdc_sata_pci_driver); -} - +module_pci_driver(pdc_sata_pci_driver); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Promise SATA low-level driver"); MODULE_LICENSE("GPL"); MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl); MODULE_VERSION(DRV_VERSION); - -module_init(pdc_sata_init); -module_exit(pdc_sata_exit); |
