summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuinn Jensen <quinn.jensen@freescale.com>2007-10-24 21:24:53 -0600
committerQuinn Jensen <quinn.jensen@freescale.com>2007-10-24 21:24:53 -0600
commitd8749be38fee07bde24f77bedb122c9c2cdf8377 (patch)
tree1b20169bd60e59eff12fc0b6c28042e7d6ca35b3
parentb4d071c2e403c2279e9e1b7216e22cdbe12d43c7 (diff)
Bugzilla 771 - Fix pata_fsl libata driver DMA mode
Patch for Bugzilla 771 - Fix pata_fsl libata driver DMA mode. Full sglist DMA working on linux 2.6.22 for MX27/MX31. Not fast enough on MX27, though. MX31 is as fast as the old mxc_ide driver. Writes are unreliable on MX27 TO1. Beyond UDMA3 on MX27 wasn't 100% reliable. http://www.bitshrine.org/gpp/linux-2.6.22-mx-Bugzilla-771-Fix-pata_fsl-libata-driver-DM.patch
-rw-r--r--arch/arm/mach-mx27/devices.c3
-rw-r--r--arch/arm/mach-mx3/devices.c3
-rw-r--r--drivers/ata/pata_fsl.c187
-rw-r--r--include/linux/fsl_devices.h3
4 files changed, 192 insertions, 4 deletions
diff --git a/arch/arm/mach-mx27/devices.c b/arch/arm/mach-mx27/devices.c
index 202816f62d61..b1ac7c5b24da 100644
--- a/arch/arm/mach-mx27/devices.c
+++ b/arch/arm/mach-mx27/devices.c
@@ -638,6 +638,9 @@ static int ata_get_clk_rate(void)
}
static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = 0x0F, /* the board handles up to UDMA3 */
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
.init = ata_init,
.exit = ata_exit,
.get_clk_rate = ata_get_clk_rate,
diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c
index 779321960591..18dee757dc21 100644
--- a/arch/arm/mach-mx3/devices.c
+++ b/arch/arm/mach-mx3/devices.c
@@ -918,6 +918,9 @@ static int ata_get_clk_rate(void)
}
static struct fsl_ata_platform_data ata_data = {
+ .udma_mask = 0x0F, /* board can handle up to UDMA3 */
+ .fifo_alarm = MXC_IDE_DMA_WATERMARK / 2,
+ .max_sg = MXC_IDE_DMA_BD_NR,
.init = ata_init,
.exit = ata_exit,
.get_clk_rate = ata_get_clk_rate,
diff --git a/drivers/ata/pata_fsl.c b/drivers/ata/pata_fsl.c
index 94e3d054be98..4cf87e38f0fb 100644
--- a/drivers/ata/pata_fsl.c
+++ b/drivers/ata/pata_fsl.c
@@ -24,15 +24,25 @@
#include <linux/libata.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
+#include <asm/dma.h>
#define DRV_NAME "pata_fsl"
#define DRV_VERSION "1.0"
struct pata_fsl_priv {
+ int ultra;
u8 *fsl_ata_regs;
+ int dma_rchan;
+ int dma_wchan;
+ int dma_done;
+ int dma_dir;
};
enum {
+ /* various constants */
+
+ FSL_ATA_MAX_SG_LEN = 65534,
+
/* offsets to registers */
FSL_ATA_TIMING_REGS = 0x00,
@@ -285,11 +295,166 @@ static void pata_fsl_set_piomode(struct ata_port *ap, struct ata_device *adev)
set_ata_bus_timing(adev->pio_mode, to_platform_device(ap->dev));
}
+static void pata_fsl_set_dmamode(struct ata_port *ap, struct ata_device *adev)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ priv->ultra = adev->dma_mode >= XFER_UDMA_0;
+
+ set_ata_bus_timing(adev->dma_mode, to_platform_device(ap->dev));
+}
+
static int pata_fsl_port_start(struct ata_port *ap)
{
return 0;
}
+static void dma_callback(void *arg, int error_status, unsigned int count)
+{
+ struct ata_port *ap = arg;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+
+ priv->dma_done = 1;
+ /*
+ * DMA is finished, so unmask INTRQ from the drive to allow the
+ * normal ISR to fire.
+ */
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
+ mb();
+}
+
+static void pata_fsl_bmdma_setup(struct ata_queued_cmd *qc)
+{
+ int nr_sg = 0;
+ int chan;
+ int dma_mode = 0, dma_ultra;
+ u8 ata_control;
+ struct ata_port *ap = qc->ap;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ u8 *ata_regs = priv->fsl_ata_regs;
+ struct fsl_ata_platform_data *plat = ap->dev->platform_data;
+ struct scatterlist tmp[plat->max_sg], *tsg, *sg;
+ int err;
+
+ priv->dma_dir = qc->dma_dir;
+
+ /*
+ * Configure the on-chip ATA interface hardware.
+ */
+ dma_ultra = priv->ultra ?
+ FSL_ATA_CTRL_DMA_ULTRA : 0;
+
+ ata_control = FSL_ATA_CTRL_FIFO_RST_B |
+ FSL_ATA_CTRL_ATA_RST_B |
+ FSL_ATA_CTRL_DMA_PENDING |
+ dma_ultra;
+
+ if (qc->dma_dir == DMA_TO_DEVICE) {
+ chan = priv->dma_wchan;
+ ata_control |= FSL_ATA_CTRL_FIFO_TX_EN |
+ FSL_ATA_CTRL_DMA_WRITE;
+ dma_mode = DMA_MODE_WRITE;
+ } else {
+ chan = priv->dma_rchan;
+ ata_control |= FSL_ATA_CTRL_FIFO_RCV_EN;
+ dma_mode = DMA_MODE_READ;
+ }
+
+ __raw_writel(ata_control, ata_regs + FSL_ATA_CONTROL);
+ __raw_writel(plat->fifo_alarm, ata_regs + FSL_ATA_FIFO_ALARM);
+ __raw_writel(FSL_ATA_INTR_ATA_INTRQ1, ata_regs + FSL_ATA_INT_EN);
+ mb();
+
+ /*
+ * Set up the DMA completion callback.
+ */
+ mxc_dma_callback_set(chan, dma_callback, (void *)ap);
+
+ /*
+ * Copy the sg list to an array.
+ */
+ tsg = tmp;
+ ata_for_each_sg(sg, qc) {
+ memcpy(tsg, sg, sizeof *sg);
+ tsg++;
+ nr_sg++;
+ }
+
+ err = mxc_dma_sg_config(chan, tmp, nr_sg, 0, dma_mode);
+ if (err) {
+ printk(KERN_ERR "pata_fsl_bmdma_setup: error %d\n", err);
+ }
+}
+
+static void pata_fsl_bmdma_start(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+ struct pata_fsl_priv *priv = ap->host->private_data;
+ int chan;
+ int err;
+
+ /*
+ * Start the channel.
+ */
+ chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan : priv->dma_rchan;
+
+ priv->dma_done = 0;
+
+ err = mxc_dma_enable(chan);
+ if (err) {
+ printk(KERN_ERR "%s: : error %d\n", __func__, err);
+ }
+
+ ap->ops->exec_command(ap, &qc->tf);
+}
+
+static void pata_fsl_bmdma_stop(struct ata_queued_cmd *qc)
+{
+ struct ata_port *ap = qc->ap;
+
+ /* do a dummy read as in ata_bmdma_stop */
+ ata_altstatus(ap);
+}
+
+static u8 pata_fsl_bmdma_status(struct ata_port *ap)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ return priv->dma_done ? ATA_DMA_INTR : 0;
+}
+
+static void pata_fsl_dma_init(struct ata_port *ap)
+{
+ struct pata_fsl_priv *priv = ap->host->private_data;
+
+ priv->dma_rchan = -1;
+ priv->dma_wchan = -1;
+
+ priv->dma_rchan = mxc_dma_request(MXC_DMA_ATA_RX, "MXC ATA RX");
+ if (priv->dma_rchan < 0) {
+ dev_printk(KERN_ERR, ap->dev, "couldn't get RX DMA channel\n");
+ goto err_out;
+ }
+
+ priv->dma_wchan = mxc_dma_request(MXC_DMA_ATA_TX, "MXC ATA TX");
+ if (priv->dma_wchan < 0) {
+ dev_printk(KERN_ERR, ap->dev, "couldn't get TX DMA channel\n");
+ goto err_out;
+ }
+
+ dev_printk(KERN_ERR, ap->dev, "rchan=%d wchan=%d\n", priv->dma_rchan,
+ priv->dma_wchan);
+ return;
+
+err_out:
+ ap->mwdma_mask = 0;
+ ap->udma_mask = 0;
+ mxc_dma_free(priv->dma_rchan);
+ mxc_dma_free(priv->dma_wchan);
+ kfree(priv);
+}
+
static u8 pata_fsl_irq_ack(struct ata_port *ap, unsigned int chk_drq)
{
unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY;
@@ -317,6 +482,7 @@ static struct scsi_host_template pata_fsl_sht = {
.emulated = ATA_SHT_EMULATED,
.use_clustering = ATA_SHT_USE_CLUSTERING,
.proc_name = DRV_NAME,
+ .dma_boundary = FSL_ATA_MAX_SG_LEN,
.slave_configure = ata_scsi_slave_config,
.slave_destroy = ata_scsi_slave_destroy,
.bios_param = ata_std_bios_param,
@@ -324,6 +490,7 @@ static struct scsi_host_template pata_fsl_sht = {
static struct ata_port_operations pata_fsl_port_ops = {
.set_piomode = pata_fsl_set_piomode,
+ .set_dmamode = pata_fsl_set_dmamode,
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
@@ -338,6 +505,9 @@ static struct ata_port_operations pata_fsl_port_ops = {
.post_internal_cmd = ata_bmdma_post_internal_cmd,
.cable_detect = ata_cable_unknown,
+ .bmdma_setup = pata_fsl_bmdma_setup,
+ .bmdma_start = pata_fsl_bmdma_start,
+
.qc_prep = ata_noop_qc_prep,
.qc_issue = ata_qc_issue_prot,
@@ -348,6 +518,9 @@ static struct ata_port_operations pata_fsl_port_ops = {
.irq_ack = pata_fsl_irq_ack,
.port_start = pata_fsl_port_start,
+
+ .bmdma_stop = pata_fsl_bmdma_stop,
+ .bmdma_status = pata_fsl_bmdma_status,
};
static void fsl_setup_port(struct ata_ioports *ioaddr)
@@ -400,7 +573,7 @@ static int __devinit pata_fsl_probe(struct platform_device *pdev)
/*
* Allocate private data
*/
- priv = kmalloc(sizeof (struct pata_fsl_priv), GFP_KERNEL);
+ priv = kzalloc(sizeof (struct pata_fsl_priv), GFP_KERNEL);
if(priv == NULL) {
/* free(host); */
return -ENOMEM;
@@ -423,8 +596,9 @@ static int __devinit pata_fsl_probe(struct platform_device *pdev)
ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr;
ap->ops = &pata_fsl_port_ops;
ap->pio_mask = 0x7F;
- ap->mwdma_mask = 0x00;
- ap->udma_mask = 0x00;
+ ap->mwdma_mask = 0x7F;
+ ap->udma_mask = plat->udma_mask;
+ pata_fsl_sht.sg_tablesize = plat->max_sg;
fsl_setup_port(&ap->ioaddr);
@@ -446,8 +620,13 @@ static int __devinit pata_fsl_probe(struct platform_device *pdev)
/* Set initial timing and mode */
set_ata_bus_timing(XFER_PIO_4, pdev);
+ /* get DMA ready */
+ pata_fsl_dma_init(ap);
+
/*
- * Enable hardware interrupts.
+ * Enable the ATA INTRQ interrupt from the bus, but
+ * only allow the CPU to see it (INTRQ2) at this point.
+ * INTRQ1, which goes to the DMA, will be enabled later.
*/
__raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN);
mb();
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index bf68f2c42ca8..024063603e6c 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -142,6 +142,9 @@ struct fsl_spi_platform_data {
};
struct fsl_ata_platform_data {
+ int udma_mask; /* UDMA modes h/w can handle */
+ int fifo_alarm; /* value for fifo_alarm reg */
+ int max_sg; /* longest sglist h/w can handle */
int (*init)(struct platform_device *pdev);
void (*exit)(void);
int (*get_clk_rate)(void);