diff options
Diffstat (limited to 'drivers/ata/sata_dwc_460ex.c')
| -rw-r--r-- | drivers/ata/sata_dwc_460ex.c | 212 |
1 files changed, 139 insertions, 73 deletions
diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index 6cf57c5c2b5..0bb2cabd219 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -29,8 +29,9 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/libata.h> @@ -40,8 +41,12 @@ #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> +/* These two are defined in "libata.h" */ +#undef DRV_NAME +#undef DRV_VERSION + #define DRV_NAME "sata-dwc" -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.3" /* SATA DMA driver Globals */ #define DMA_NUM_CHANS 1 @@ -155,6 +160,7 @@ enum { /* Assign HW handshaking interface (x) to destination / source peripheral */ #define DMA_CFG_HW_HS_DEST(int_num) (((int_num) & 0xF) << 11) #define DMA_CFG_HW_HS_SRC(int_num) (((int_num) & 0xF) << 7) +#define DMA_CFG_HW_CH_PRIOR(int_num) (((int_num) & 0xF) << 5) #define DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master)) /* @@ -315,6 +321,7 @@ struct sata_dwc_host_priv { u32 dma_interrupt_count; struct ahb_dma_regs *sata_dma_regs; struct device *dwc_dev; + int dma_channel; }; struct sata_dwc_host_priv host_pvt; /* @@ -333,11 +340,47 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, void __iomem *addr, int dir); static void dma_dwc_xfer_start(int dma_ch); +static const char *get_prot_descript(u8 protocol) +{ + switch ((enum ata_tf_protocols)protocol) { + case ATA_PROT_NODATA: + return "ATA no data"; + case ATA_PROT_PIO: + return "ATA PIO"; + case ATA_PROT_DMA: + return "ATA DMA"; + case ATA_PROT_NCQ: + return "ATA NCQ"; + case ATAPI_PROT_NODATA: + return "ATAPI no data"; + case ATAPI_PROT_PIO: + return "ATAPI PIO"; + case ATAPI_PROT_DMA: + return "ATAPI DMA"; + default: + return "unknown"; + } +} + +static const char *get_dma_dir_descript(int dma_dir) +{ + switch ((enum dma_data_direction)dma_dir) { + case DMA_BIDIRECTIONAL: + return "bidirectional"; + case DMA_TO_DEVICE: + return "to device"; + case DMA_FROM_DEVICE: + return "from device"; + default: + return "none"; + } +} + static void sata_dwc_tf_dump(struct ata_taskfile *tf) { dev_vdbg(host_pvt.dwc_dev, "taskfile cmd: 0x%02x protocol: %s flags:" - "0x%lx device: %x\n", tf->command, ata_get_cmd_descript\ - (tf->protocol), tf->flags, tf->device); + "0x%lx device: %x\n", tf->command, + get_prot_descript(tf->protocol), tf->flags, tf->device); dev_vdbg(host_pvt.dwc_dev, "feature: 0x%02x nsect: 0x%x lbal: 0x%x " "lbam: 0x%x lbah: 0x%x\n", tf->feature, tf->nsect, tf->lbal, tf->lbam, tf->lbah); @@ -350,7 +393,7 @@ static void sata_dwc_tf_dump(struct ata_taskfile *tf) /* * Function: get_burst_length_encode * arguments: datalength: length in bytes of data - * returns value to be programmed in register corrresponding to data length + * returns value to be programmed in register corresponding to data length * This value is effectively the log(base 2) of the length */ static int get_burst_length_encode(int datalength) @@ -398,15 +441,12 @@ static void clear_chan_interrupts(int c) */ static int dma_request_channel(void) { - int i; - - for (i = 0; i < DMA_NUM_CHANS; i++) { - if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) &\ - DMA_CHANNEL(i))) - return i; - } - dev_err(host_pvt.dwc_dev, "%s NO channel chan_en: 0x%08x\n", __func__, - in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low))); + /* Check if the channel is not currently in use */ + if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) & + DMA_CHANNEL(host_pvt.dma_channel))) + return host_pvt.dma_channel; + dev_err(host_pvt.dwc_dev, "%s Channel %d is currently in use\n", + __func__, host_pvt.dma_channel); return -1; } @@ -421,8 +461,7 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) int chan; u32 tfr_reg, err_reg; unsigned long flags; - struct sata_dwc_device *hsdev = - (struct sata_dwc_device *)hsdev_instance; + struct sata_dwc_device *hsdev = hsdev_instance; struct ata_host *host = (struct ata_host *)hsdev->host; struct ata_port *ap; struct sata_dwc_device_port *hsdevp; @@ -442,7 +481,8 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n", tfr_reg, err_reg, hsdevp->dma_pending[tag], port); - for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + chan = host_pvt.dma_channel; + if (chan >= 0) { /* Check for end-of-transfer interrupt. */ if (tfr_reg & DMA_CHANNEL(chan)) { /* @@ -495,9 +535,9 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance) static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq) { int retval = 0; - int chan; + int chan = host_pvt.dma_channel; - for (chan = 0; chan < DMA_NUM_CHANS; chan++) { + if (chan >= 0) { /* Unmask error interrupt */ out_le32(&(host_pvt.sata_dma_regs)->interrupt_mask.error.low, DMA_ENABLE_CHAN(chan)); @@ -536,7 +576,10 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, int fis_len = 0; dma_addr_t next_llp; int bl; + int sms_val, dms_val; + sms_val = 0; + dms_val = 1 + host_pvt.dma_channel; dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x" " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, (u32)dmadr_addr); @@ -596,8 +639,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, lli[idx].ctl.low = cpu_to_le32( DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) | - DMA_CTL_SMS(0) | - DMA_CTL_DMS(1) | + DMA_CTL_SMS(sms_val) | + DMA_CTL_DMS(dms_val) | DMA_CTL_SRC_MSIZE(bl) | DMA_CTL_DST_MSIZE(bl) | DMA_CTL_SINC_NOCHANGE | @@ -612,8 +655,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, lli[idx].ctl.low = cpu_to_le32( DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) | - DMA_CTL_SMS(1) | - DMA_CTL_DMS(0) | + DMA_CTL_SMS(dms_val) | + DMA_CTL_DMS(sms_val) | DMA_CTL_SRC_MSIZE(bl) | DMA_CTL_DST_MSIZE(bl) | DMA_CTL_DINC_NOCHANGE | @@ -705,8 +748,10 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, /* Program the CFG register. */ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.high), + DMA_CFG_HW_HS_SRC(dma_ch) | DMA_CFG_HW_HS_DEST(dma_ch) | DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ); - out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), 0); + out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), + DMA_CFG_HW_CH_PRIOR(dma_ch)); /* Program the address of the linked list */ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].llp.low), @@ -715,7 +760,7 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, /* Program the CTL register with src enable / dst enable */ out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].ctl.low), DMA_CTL_LLP_SRCEN | DMA_CTL_LLP_DSTEN); - return 0; + return dma_ch; } /* @@ -727,11 +772,15 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems, static void dma_dwc_exit(struct sata_dwc_device *hsdev) { dev_dbg(host_pvt.dwc_dev, "%s:\n", __func__); - if (host_pvt.sata_dma_regs) + if (host_pvt.sata_dma_regs) { iounmap(host_pvt.sata_dma_regs); + host_pvt.sata_dma_regs = NULL; + } - if (hsdev->irq_dma) + if (hsdev->irq_dma) { free_irq(hsdev->irq_dma, hsdev); + hsdev->irq_dma = 0; + } } /* @@ -967,7 +1016,7 @@ static irqreturn_t sata_dwc_isr(int irq, void *dev_instance) } dev_dbg(ap->dev, "%s non-NCQ cmd interrupt, protocol: %s\n", - __func__, ata_get_cmd_descript(qc->tf.protocol)); + __func__, get_prot_descript(qc->tf.protocol)); DRVSTILLBUSY: if (ata_is_dma(qc->tf.protocol)) { /* @@ -1057,7 +1106,7 @@ DRVSTILLBUSY: /* Process completed command */ dev_dbg(ap->dev, "%s NCQ command, protocol: %s\n", __func__, - ata_get_cmd_descript(qc->tf.protocol)); + get_prot_descript(qc->tf.protocol)); if (ata_is_dma(qc->tf.protocol)) { host_pvt.dma_interrupt_count++; if (hsdevp->dma_pending[tag] == \ @@ -1142,8 +1191,8 @@ static void sata_dwc_dma_xfer_complete(struct ata_port *ap, u32 check_status) if (tag > 0) { dev_info(ap->dev, "%s tag=%u cmd=0x%02x dma dir=%s proto=%s " "dmacr=0x%08x\n", __func__, qc->tag, qc->tf.command, - ata_get_cmd_descript(qc->dma_dir), - ata_get_cmd_descript(qc->tf.protocol), + get_dma_dir_descript(qc->dma_dir), + get_prot_descript(qc->tf.protocol), in_le32(&(hsdev->sata_dwc_regs->dmacr))); } #endif @@ -1286,7 +1335,7 @@ static int sata_dwc_port_start(struct ata_port *ap) dev_err(ap->dev, "%s: dma_alloc_coherent failed\n", __func__); err = -ENOMEM; - goto CLEANUP; + goto CLEANUP_ALLOC; } } @@ -1306,15 +1355,13 @@ static int sata_dwc_port_start(struct ata_port *ap) /* Clear any error bits before libata starts issuing commands */ clear_serror(); ap->private_data = hsdevp; + dev_dbg(ap->dev, "%s: done\n", __func__); + return 0; +CLEANUP_ALLOC: + kfree(hsdevp); CLEANUP: - if (err) { - sata_dwc_port_stop(ap); - dev_dbg(ap->dev, "%s: fail\n", __func__); - } else { - dev_dbg(ap->dev, "%s: done\n", __func__); - } - + dev_dbg(ap->dev, "%s: fail. ap->id = %d\n", __func__, ap->print_id); return err; } @@ -1354,7 +1401,7 @@ static void sata_dwc_exec_command_by_tag(struct ata_port *ap, struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); dev_dbg(ap->dev, "%s cmd(0x%02x): %s tag=%d\n", __func__, tf->command, - ata_get_cmd_descript(tf), tag); + ata_get_cmd_descript(tf->command), tag); spin_lock_irqsave(&ap->host->lock, flags); hsdevp->cmd_issued[tag] = cmd_issued; @@ -1413,7 +1460,7 @@ static void sata_dwc_bmdma_start_by_tag(struct ata_queued_cmd *qc, u8 tag) dev_dbg(ap->dev, "%s qc=%p tag: %x cmd: 0x%02x dma_dir: %s " "start_dma? %x\n", __func__, qc, tag, qc->tf.command, - ata_get_cmd_descript(qc->dma_dir), start_dma); + get_dma_dir_descript(qc->dma_dir), start_dma); sata_dwc_tf_dump(&(qc->tf)); if (start_dma) { @@ -1462,10 +1509,9 @@ static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) int dma_chan; struct sata_dwc_device *hsdev = HSDEV_FROM_AP(ap); struct sata_dwc_device_port *hsdevp = HSDEVP_FROM_AP(ap); - int err; dev_dbg(ap->dev, "%s: port=%d dma dir=%s n_elem=%d\n", - __func__, ap->port_no, ata_get_cmd_descript(qc->dma_dir), + __func__, ap->port_no, get_dma_dir_descript(qc->dma_dir), qc->n_elem); dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag], @@ -1474,7 +1520,7 @@ static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) dmadr), qc->dma_dir); if (dma_chan < 0) { dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n", - __func__, err); + __func__, dma_chan); return; } hsdevp->dma_chan[tag] = dma_chan; @@ -1491,8 +1537,8 @@ static unsigned int sata_dwc_qc_issue(struct ata_queued_cmd *qc) dev_info(ap->dev, "%s ap id=%d cmd(0x%02x)=%s qc tag=%d " "prot=%s ap active_tag=0x%08x ap sactive=0x%08x\n", __func__, ap->print_id, qc->tf.command, - ata_get_cmd_descript(&qc->tf), - qc->tag, ata_get_cmd_descript(qc->tf.protocol), + ata_get_cmd_descript(qc->tf.command), + qc->tag, get_prot_descript(qc->tf.protocol), ap->link.active_tag, ap->link.sactive); #endif @@ -1533,7 +1579,7 @@ static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) #ifdef DEBUG_NCQ if (qc->tag > 0) dev_info(qc->ap->dev, "%s: qc->tag=%d ap->active_tag=0x%08x\n", - __func__, tag, qc->ap->link.active_tag); + __func__, qc->tag, qc->ap->link.active_tag); return ; #endif @@ -1541,10 +1587,31 @@ static void sata_dwc_qc_prep(struct ata_queued_cmd *qc) static void sata_dwc_error_handler(struct ata_port *ap) { - ap->link.flags |= ATA_LFLAG_NO_HRST; ata_sff_error_handler(ap); } +int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); + int ret; + + ret = sata_sff_hardreset(link, class, deadline); + + sata_dwc_enable_interrupts(hsdev); + + /* Reconfigure the DMA control register */ + out_le32(&hsdev->sata_dwc_regs->dmacr, + SATA_DWC_DMACR_TXRXCH_CLEAR); + + /* Reconfigure the DMA Burst Transaction Size register */ + out_le32(&hsdev->sata_dwc_regs->dbtsr, + SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) | + SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT)); + + return ret; +} + /* * scsi mid-layer and libata interface structures */ @@ -1564,6 +1631,7 @@ static struct ata_port_operations sata_dwc_ops = { .inherits = &ata_sff_port_ops, .error_handler = sata_dwc_error_handler, + .hardreset = sata_dwc_hardreset, .qc_prep = sata_dwc_qc_prep, .qc_issue = sata_dwc_qc_issue, @@ -1580,16 +1648,14 @@ static struct ata_port_operations sata_dwc_ops = { static const struct ata_port_info sata_dwc_port_info[] = { { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_NCQ, - .pio_mask = 0x1f, /* pio 0-4 */ + .flags = ATA_FLAG_SATA | ATA_FLAG_NCQ, + .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &sata_dwc_ops, }, }; -static int sata_dwc_probe(struct platform_device *ofdev, - const struct of_device_id *match) +static int sata_dwc_probe(struct platform_device *ofdev) { struct sata_dwc_device *hsdev; u32 idr, versionr; @@ -1600,15 +1666,23 @@ static int sata_dwc_probe(struct platform_device *ofdev, struct ata_host *host; struct ata_port_info pi = sata_dwc_port_info[0]; const struct ata_port_info *ppi[] = { &pi, NULL }; + struct device_node *np = ofdev->dev.of_node; + u32 dma_chan; /* Allocate DWC SATA device */ - hsdev = kmalloc(sizeof(*hsdev), GFP_KERNEL); + hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); if (hsdev == NULL) { dev_err(&ofdev->dev, "kmalloc failed for hsdev\n"); err = -ENOMEM; - goto error_out; + goto error; } - memset(hsdev, 0, sizeof(*hsdev)); + + if (of_property_read_u32(np, "dma-channel", &dma_chan)) { + dev_warn(&ofdev->dev, "no dma-channel property set." + " Use channel 0\n"); + dma_chan = 0; + } + host_pvt.dma_channel = dma_chan; /* Ioremap SATA registers */ base = of_iomap(ofdev->dev.of_node, 0); @@ -1616,7 +1690,7 @@ static int sata_dwc_probe(struct platform_device *ofdev, dev_err(&ofdev->dev, "ioremap failed for SATA register" " address\n"); err = -ENODEV; - goto error_out; + goto error_kmalloc; } hsdev->reg_base = base; dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); @@ -1629,7 +1703,7 @@ static int sata_dwc_probe(struct platform_device *ofdev, if (!host) { dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n"); err = -ENOMEM; - goto error_out; + goto error_iomap; } host->private_data = hsdev; @@ -1697,8 +1771,11 @@ error_out: /* Free SATA DMA resources */ dma_dwc_exit(hsdev); - if (base) - iounmap(base); +error_iomap: + iounmap(base); +error_kmalloc: + kfree(hsdev); +error: return err; } @@ -1727,7 +1804,7 @@ static const struct of_device_id sata_dwc_match[] = { }; MODULE_DEVICE_TABLE(of, sata_dwc_match); -static struct of_platform_driver sata_dwc_driver = { +static struct platform_driver sata_dwc_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, @@ -1737,18 +1814,7 @@ static struct of_platform_driver sata_dwc_driver = { .remove = sata_dwc_remove, }; -static int __init sata_dwc_init(void) -{ - return of_register_platform_driver(&sata_dwc_driver); -} - -static void __exit sata_dwc_exit(void) -{ - of_unregister_platform_driver(&sata_dwc_driver); -} - -module_init(sata_dwc_init); -module_exit(sata_dwc_exit); +module_platform_driver(sata_dwc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Miesfeld <mmiesfeld@amcc.com>"); |
