diff options
Diffstat (limited to 'target/linux/coldfire/patches/037-Add-ColdFire-MCF54455-PATA-interface-support.patch')
-rw-r--r-- | target/linux/coldfire/patches/037-Add-ColdFire-MCF54455-PATA-interface-support.patch | 934 |
1 files changed, 934 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/037-Add-ColdFire-MCF54455-PATA-interface-support.patch b/target/linux/coldfire/patches/037-Add-ColdFire-MCF54455-PATA-interface-support.patch new file mode 100644 index 0000000000..730086eba1 --- /dev/null +++ b/target/linux/coldfire/patches/037-Add-ColdFire-MCF54455-PATA-interface-support.patch @@ -0,0 +1,934 @@ +From 8e46c06091fd87904205a977be3c784e3ac61e95 Mon Sep 17 00:00:00 2001 +From: Jingchang Lu <b35083@freescale.com> +Date: Thu, 4 Aug 2011 09:59:48 +0800 +Subject: [PATCH 37/52] Add ColdFire MCF54455 PATA interface support + +ColdFire MCF54455 parallel ATA controller support +both uDMA and PIO mode, this driver implements all. + +Signed-off-by: Jingchang Lu <b35083@freescale.com> +--- + arch/m68k/include/asm/pata_fsl.h | 17 + + drivers/ata/Kconfig | 23 +- + drivers/ata/Makefile | 1 + + drivers/ata/pata_fsl.c | 844 ++++++++++++++++++++++++++++++++++++++ + 4 files changed, 884 insertions(+), 1 deletions(-) + create mode 100644 arch/m68k/include/asm/pata_fsl.h + create mode 100644 drivers/ata/pata_fsl.c + +--- /dev/null ++++ b/arch/m68k/include/asm/pata_fsl.h +@@ -0,0 +1,17 @@ ++/* ++ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ */ ++ ++#ifndef _ASM_M68K_PATA_FSL_H ++#define _ASM_M68K_PATA_FSL_H ++ ++/* ATA mapped IO address translate function */ ++extern unsigned int io_ata_virt2phys(void *x); ++extern void *io_ata_phys2virt(unsigned int x); ++ ++ ++#endif +--- a/drivers/ata/Kconfig ++++ b/drivers/ata/Kconfig +@@ -14,7 +14,7 @@ menuconfig ATA + tristate "Serial ATA and Parallel ATA drivers" + depends on HAS_IOMEM + depends on BLOCK +- depends on !(M32R || M68K) || BROKEN ++ depends on !(M32R) || BROKEN + select SCSI + ---help--- + If you want to use a ATA hard disk, ATA tape drive, ATA CD-ROM or +@@ -687,6 +687,27 @@ config PATA_WINBOND + + If unsure, say N. + ++config PATA_FSL ++ tristate "Freescale on-chip PATA support" ++ depends on (ARCH_MX3 || ARCH_MX27 || PPC_512x || M54455) ++ help ++ Some Freescale processors SOC have parallel ATA controller, ++ such as ColdFire MCF54455. ++ ++ Say Y here if you wish to use the on-chip ATA interface. ++ ++ If you are unsure, say N to this. ++ ++config FSL_PATA_USE_DMA ++ bool "Freescale PATA eDMA support" ++ depends on PATA_FSL && COLDFIRE_EDMA ++ default y ++ help ++ This option enables the uDMA support over PATA interface ++ which can improve performance than PIO mode for read and write. ++ ++ If unsure, say Y. ++ + endif # ATA_BMDMA + + comment "PIO-only SFF controllers" +--- a/drivers/ata/Makefile ++++ b/drivers/ata/Makefile +@@ -72,6 +72,7 @@ obj-$(CONFIG_PATA_TOSHIBA) += pata_picco + obj-$(CONFIG_PATA_TRIFLEX) += pata_triflex.o + obj-$(CONFIG_PATA_VIA) += pata_via.o + obj-$(CONFIG_PATA_WINBOND) += pata_sl82c105.o ++obj-$(CONFIG_PATA_FSL) += pata_fsl.o + + # SFF PIO only + obj-$(CONFIG_PATA_AT32) += pata_at32.o +--- /dev/null ++++ b/drivers/ata/pata_fsl.c +@@ -0,0 +1,844 @@ ++/* ++ * Freescale integrated PATA driver ++ * ++ * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. All Rights Reserved. ++ * ++ * Description: ++ * This driver is for Coldfire MCF54455 on-chip ATA module. ++ * ++ * This is free software; you can redistribute it and/or modify it ++ * under the GNU General Public License as published by the Free ++ * Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/blkdev.h> ++#include <scsi/scsi_host.h> ++#include <linux/ata.h> ++#include <linux/libata.h> ++#include <linux/platform_device.h> ++#include <linux/fsl_devices.h> ++#ifdef CONFIG_FSL_PATA_USE_DMA ++#include <asm/mcf_edma.h> ++#endif ++#include <asm/pata_fsl.h> ++ ++#define DRV_NAME "pata_fsl" ++#define DRV_VERSION "1.0" ++ ++#ifdef CONFIG_M54455 ++#define WRITE_ATA8(val, reg) \ ++ __raw_writeb(val, (ata_regs + reg)); ++#define WRITE_ATA16(val, reg) \ ++ __raw_writew(val, (ata_regs + reg)); ++#else ++#define WRITE_ATA8(val, reg) \ ++ __raw_writel(val, (ata_regs + reg)); ++#define WRITE_ATA16(val, reg) \ ++ __raw_writel(val, (ata_regs + reg)); ++#endif ++ ++#define MAX_FSL_SG 256 /* MCF_EDMA_TCD_PER_CHAN */ ++ ++struct pata_fsl_priv { ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ int ultra; ++#endif ++ u8 *fsl_ata_regs; ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ int dma_rchan; ++ int dma_wchan; ++ int dma_done; ++ int dma_dir; ++#if 0 ++ int nsg; ++ struct fsl_edma_requestbuf reqbuf[MAX_FSL_SG]; ++#endif ++#endif ++}; ++ ++enum { ++ /* various constants */ ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ FSL_ATA_MAX_SG_LEN = 65534, ++#endif ++ ++ /* offsets to registers */ ++ ++ FSL_ATA_TIMING_REGS = 0x00, ++ FSL_ATA_FIFO_FILL = 0x20, ++ FSL_ATA_CONTROL = 0x24, ++ FSL_ATA_INT_PEND = 0x28, ++ FSL_ATA_INT_EN = 0x2C, ++ FSL_ATA_INT_CLEAR = 0x30, ++ FSL_ATA_FIFO_ALARM = 0x34, ++ FSL_ATA_DRIVE_DATA = 0xA0, ++ FSL_ATA_DRIVE_CONTROL = 0xD8, ++ ++ /* bits within FSL_ATA_CONTROL */ ++ ++ FSL_ATA_CTRL_FIFO_RST_B = 0x80, ++ FSL_ATA_CTRL_ATA_RST_B = 0x40, ++ FSL_ATA_CTRL_FIFO_TX_EN = 0x20, ++ FSL_ATA_CTRL_FIFO_RCV_EN = 0x10, ++ FSL_ATA_CTRL_DMA_PENDING = 0x08, ++ FSL_ATA_CTRL_DMA_ULTRA = 0x04, ++ FSL_ATA_CTRL_DMA_WRITE = 0x02, ++ FSL_ATA_CTRL_IORDY_EN = 0x01, ++ ++ /* bits within the interrupt control registers */ ++ ++ FSL_ATA_INTR_ATA_INTRQ1 = 0x80, ++ FSL_ATA_INTR_FIFO_UNDERFLOW = 0x40, ++ FSL_ATA_INTR_FIFO_OVERFLOW = 0x20, ++ FSL_ATA_INTR_CTRL_IDLE = 0x10, ++ FSL_ATA_INTR_ATA_INTRQ2 = 0x08, ++}; ++ ++/* ++ * This structure contains the timing parameters for ++ * ATA bus timing in the 5 PIO modes. The timings ++ * are in nanoseconds, and are converted to clock ++ * cycles before being stored in the ATA controller ++ * timing registers. ++ */ ++static struct { ++ short t0, t1, t2_8, t2_16, t2i, t4, t9, tA; ++} pio_specs[] = { ++ [0] = { ++ .t0 = 600, .t1 = 70, .t2_8 = 290, .t2_16 = 165, .t2i = 0, ++ .t4 = 30, .t9 = 20, .tA = 50 ++ }, ++ [1] = { ++ .t0 = 383, .t1 = 50, .t2_8 = 290, .t2_16 = 125, .t2i = 0, ++ .t4 = 20, .t9 = 15, .tA = 50 ++ }, ++ [2] = { ++ .t0 = 240, .t1 = 30, .t2_8 = 290, .t2_16 = 100, .t2i = 0, ++ .t4 = 15, .t9 = 10, .tA = 50 ++ }, ++ [3] = { ++ .t0 = 180, .t1 = 30, .t2_8 = 80, .t2_16 = 80, .t2i = 0, ++ .t4 = 10, .t9 = 10, .tA = 50 ++ }, ++ [4] = { ++ .t0 = 120, .t1 = 25, .t2_8 = 70, .t2_16 = 70, .t2i = 0, ++ .t4 = 10, .t9 = 10, .tA = 50 ++ }, ++}; ++ ++#define NR_PIO_SPECS (sizeof pio_specs / sizeof pio_specs[0]) ++ ++/* ++ * This structure contains the timing parameters for ++ * ATA bus timing in the 3 MDMA modes. The timings ++ * are in nanoseconds, and are converted to clock ++ * cycles before being stored in the ATA controller ++ * timing registers. ++ */ ++static struct { ++ short t0M, tD, tH, tJ, tKW, tM, tN, tJNH; ++} mdma_specs[] = { ++ [0] = { ++ .t0M = 480, .tD = 215, .tH = 20, .tJ = 20, .tKW = 215, ++ .tM = 50, .tN = 15, .tJNH = 20 ++ }, ++ [1] = { ++ .t0M = 150, .tD = 80, .tH = 15, .tJ = 5, .tKW = 50, ++ .tM = 30, .tN = 10, .tJNH = 15 ++ }, ++ [2] = { ++ .t0M = 120, .tD = 70, .tH = 10, .tJ = 5, .tKW = 25, ++ .tM = 25, .tN = 10, .tJNH = 10 ++ }, ++}; ++ ++#define NR_MDMA_SPECS (sizeof mdma_specs / sizeof mdma_specs[0]) ++ ++/* ++ * This structure contains the timing parameters for ++ * ATA bus timing in the 6 UDMA modes. The timings ++ * are in nanoseconds, and are converted to clock ++ * cycles before being stored in the ATA controller ++ * timing registers. ++ */ ++static struct { ++ short t2CYC, tCYC, tDS, tDH, tDVS, tDVH, tCVS, tCVH, tFS_min, tLI_max, ++ tMLI, tAZ, tZAH, tENV_min, tSR, tRFS, tRP, tACK, tSS, tDZFS; ++} udma_specs[] = { ++ [0] = { ++ .t2CYC = 235, .tCYC = 114, .tDS = 15, .tDH = 5, .tDVS = 70, ++ .tDVH = 6, .tCVS = 70, .tCVH = 6, .tFS_min = 0, ++ .tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 50, .tRFS = 75, .tRP = 160, ++ .tACK = 20, .tSS = 50, .tDZFS = 80 ++ }, ++ [1] = { ++ .t2CYC = 156, .tCYC = 75, .tDS = 10, .tDH = 5, .tDVS = 48, ++ .tDVH = 6, .tCVS = 48, .tCVH = 6, .tFS_min = 0, ++ .tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 30, .tRFS = 70, .tRP = 125, ++ .tACK = 20, .tSS = 50, .tDZFS = 63 ++ }, ++ [2] = { ++ .t2CYC = 117, .tCYC = 55, .tDS = 7, .tDH = 5, .tDVS = 34, ++ .tDVH = 6, .tCVS = 34, .tCVH = 6, .tFS_min = 0, ++ .tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 20, .tRFS = 60, .tRP = 100, ++ .tACK = 20, .tSS = 50, .tDZFS = 47 ++ }, ++ [3] = { ++ .t2CYC = 86, .tCYC = 39, .tDS = 7, .tDH = 5, .tDVS = 20, ++ .tDVH = 6, .tCVS = 20, .tCVH = 6, .tFS_min = 0, ++ .tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 20, .tRFS = 60, .tRP = 100, ++ .tACK = 20, .tSS = 50, .tDZFS = 35 ++ }, ++ [4] = { ++ .t2CYC = 57, .tCYC = 25, .tDS = 5, .tDH = 5, .tDVS = 7, ++ .tDVH = 6, .tCVS = 7, .tCVH = 6, .tFS_min = 0, ++ .tLI_max = 100, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 50, .tRFS = 60, .tRP = 100, ++ .tACK = 20, .tSS = 50, .tDZFS = 25 ++ }, ++ [5] = { ++ .t2CYC = 38, .tCYC = 17, .tDS = 4, .tDH = 5, .tDVS = 5, ++ .tDVH = 6, .tCVS = 10, .tCVH = 10, .tFS_min = 0, ++ .tLI_max = 75, .tMLI = 20, .tAZ = 10, .tZAH = 20, ++ .tENV_min = 20, .tSR = 20, .tRFS = 50, .tRP = 85, ++ .tACK = 20, .tSS = 50, .tDZFS = 40 ++ }, ++}; ++ ++#define NR_UDMA_SPECS (sizeof udma_specs / sizeof udma_specs[0]) ++ ++struct fsl_ata_time_regs { ++ u8 time_off, time_on, time_1, time_2w; ++ u8 time_2r, time_ax, time_pio_rdx, time_4; ++ u8 time_9, time_m, time_jn, time_d; ++ u8 time_k, time_ack, time_env, time_rpx; ++ u8 time_zah, time_mlix, time_dvh, time_dzfs; ++ u8 time_dvs, time_cvh, time_ss, time_cyc; ++} __packed; ++ ++ ++static void update_timing_config(struct fsl_ata_time_regs *tp, ++ struct ata_host *host) ++{ ++ u32 __iomem *lp = (u32 __iomem *)tp; ++ struct pata_fsl_priv *priv = host->private_data; ++ u32 __iomem *ctlp = (u32 __iomem *)priv->fsl_ata_regs; ++ int i; ++ ++ /* ++ * JKM - this could have endianess issues on BE depending ++ * on how the controller is glued to the bus -- probably ++ * should rewrite this to write byte at a time. ++ */ ++ for (i = 0; i < 6; i++) { ++ __raw_writel(*lp, ctlp); ++ lp++; ++ ctlp++; ++ } ++} ++ ++/*! ++ * Calculate values for the ATA bus timing registers and store ++ * them into the hardware. ++ * ++ * @param xfer_mode specifies XFER xfer_mode ++ * @param pdev specifies platform_device ++ * ++ * @return EINVAL speed out of range, or illegal mode ++ */ ++static int set_ata_bus_timing(u8 xfer_mode, struct platform_device *pdev) ++{ ++ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *) ++ pdev->dev.platform_data; ++ struct ata_host *host = dev_get_drvdata(&pdev->dev); ++ ++ /* get the bus clock cycle time, in ns */ ++ int T = 1 * 1000 * 1000 * 1000 / plat->get_clk_rate(); ++ struct fsl_ata_time_regs tr = {0}; ++ DPRINTK("clk_rate = %d T = %d\n", plat->get_clk_rate(), T); ++ ++ /* ++ * every mode gets the same t_off and t_on ++ */ ++ tr.time_off = 3; ++ tr.time_on = 3; ++ ++ if (xfer_mode >= XFER_UDMA_0) { ++ int speed = xfer_mode - XFER_UDMA_0; ++ if (speed >= NR_UDMA_SPECS) ++ return -EINVAL; ++ ++ tr.time_ack = (udma_specs[speed].tACK + T) / T; ++ tr.time_env = (udma_specs[speed].tENV_min + T) / T; ++ tr.time_rpx = (udma_specs[speed].tRP + T) / T + 2; ++ ++ tr.time_zah = (udma_specs[speed].tZAH + T) / T; ++ tr.time_mlix = (udma_specs[speed].tMLI + T) / T; ++ tr.time_dvh = (udma_specs[speed].tDVH + T) / T + 1; ++ tr.time_dzfs = (udma_specs[speed].tDZFS + T) / T; ++ ++ tr.time_dvs = (udma_specs[speed].tDVS + T) / T; ++ tr.time_cvh = (udma_specs[speed].tCVH + T) / T; ++ tr.time_ss = (udma_specs[speed].tSS + T) / T; ++ tr.time_cyc = (udma_specs[speed].tCYC + T) / T; ++ } else if (xfer_mode >= XFER_MW_DMA_0) { ++ int speed = xfer_mode - XFER_MW_DMA_0; ++ if (speed >= NR_MDMA_SPECS) ++ return -EINVAL; ++ ++ tr.time_m = (mdma_specs[speed].tM + T) / T; ++ tr.time_jn = (mdma_specs[speed].tJNH + T) / T; ++ tr.time_d = (mdma_specs[speed].tD + T) / T; ++ ++ tr.time_k = (mdma_specs[speed].tKW + T) / T; ++ } else { ++ int speed = xfer_mode - XFER_PIO_0; ++ if (speed >= NR_PIO_SPECS) ++ return -EINVAL; ++ ++ tr.time_1 = (pio_specs[speed].t1 + T) / T; ++ tr.time_2w = (pio_specs[speed].t2_8 + T) / T; ++ ++ tr.time_2r = (pio_specs[speed].t2_8 + T) / T; ++ tr.time_ax = (pio_specs[speed].tA + T) / T + 2; ++ tr.time_pio_rdx = 1; ++ tr.time_4 = (pio_specs[speed].t4 + T) / T; ++ ++ tr.time_9 = (pio_specs[speed].t9 + T) / T; ++ } ++ ++ update_timing_config(&tr, host); ++ ++ return 0; ++} ++ ++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)); ++} ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++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)); ++} ++#endif ++ ++static int pata_fsl_port_start(struct ata_port *ap) ++{ ++ return 0; ++} ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ ++static irqreturn_t dma_callback(int channel, void *arg) ++{ ++ struct ata_port *ap = arg; ++ struct pata_fsl_priv *priv = ap->host->private_data; ++ u8 __iomem *ata_regs = priv->fsl_ata_regs; ++ ++ mcf_edma_stop_transfer(channel); ++ priv->dma_done = 1; ++ /* ++ * DMA is finished, so unmask INTRQ from the drive to allow the ++ * normal ISR to fire. ++ */ ++#if 0 ++ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN); ++#else ++ WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN); ++ WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL); ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++static void pata_fsl_bmdma_setup(struct ata_queued_cmd *qc) ++{ ++ int chan; ++ int dma_ultra; ++ u8 ata_control; ++ struct ata_port *ap = qc->ap; ++ struct pata_fsl_priv *priv = ap->host->private_data; ++ u8 __iomem *ata_regs = priv->fsl_ata_regs; ++#if 0 ++ struct scatterlist *sg; ++ struct fsl_edma_requestbuf *pbuf; ++ unsigned int si; ++#endif ++ DPRINTK("ENTER\n"); ++ ++ /* reset the ATA FIFO first */ ++ /* ++ WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B,FSL_ATA_CONTROL); ++ */ ++ 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; ++ } else { ++ chan = priv->dma_rchan; ++ ata_control |= FSL_ATA_CTRL_FIFO_RCV_EN; ++ } ++#if 0 ++ __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); ++#else ++ WRITE_ATA8(ata_control, FSL_ATA_CONTROL); ++ WRITE_ATA8(16/*plat->fifo_alarm*/, FSL_ATA_FIFO_ALARM); ++ WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ1, FSL_ATA_INT_EN); ++#endif ++ /*mb();*/ ++ ++ /* ++ * Set up the DMA completion callback. ++ */ ++ /* ++ * Copy the sg list to an array. ++ */ ++#if 0 ++ priv->nsg = 0; ++ pbuf = priv->reqbuf; ++ ++ for_each_sg(qc->sg, sg, qc->n_elem, si) { ++ ++ /*dma_map_sg(NULL, sg, 1, priv->dma_dir); */ ++ ++ if (priv->dma_dir == DMA_TO_DEVICE) { /* WRITE */ ++ pbuf->saddr = sg->dma_address; ++ pbuf->daddr = (dma_addr_t)(priv->fsl_ata_regs + 0x18); ++ pbuf->soff = 4; ++ pbuf->doff = 0; ++ } else { /* Read */ ++ pbuf->daddr = sg->dma_address; ++ pbuf->saddr = (dma_addr_t)(priv->fsl_ata_regs + 0x18); ++ pbuf->doff = 4; ++ pbuf->soff = 0; ++ } ++ pbuf->attr = MCF_EDMA_TCD_ATTR_SSIZE_32BIT ++ |MCF_EDMA_TCD_ATTR_DSIZE_32BIT; ++ pbuf->minor_loop = 16*4; /* 16 longwords per request*/ ++ pbuf->len = sg_dma_len(sg); ++ ++ pbuf++; ++ priv->nsg++; ++ } ++ ++ BUG_ON(*(unsigned char *)(ata_regs + FSL_ATA_FIFO_FILL)); ++ mcf_edma_sg_config(chan, priv->reqbuf, priv->nsg); ++#else ++ if (priv->dma_dir == DMA_TO_DEVICE) { ++ mcf_edma_sglist_config(chan, qc->sg, qc->n_elem, priv->dma_dir, ++ (dma_addr_t) ++ ((io_ata_virt2phys((void *)priv->fsl_ata_regs)) + 0x18), ++ MCF_EDMA_TCD_ATTR_SSIZE_32BIT ++ | MCF_EDMA_TCD_ATTR_DSIZE_32BIT, ++ 4, 0, 8*4); ++ } else { ++ ++ mcf_edma_sglist_config(chan, qc->sg, qc->n_elem, priv->dma_dir, ++ (dma_addr_t) ++ ((io_ata_virt2phys((void *)priv->fsl_ata_regs)) + 0x18), ++ MCF_EDMA_TCD_ATTR_SSIZE_32BIT ++ | MCF_EDMA_TCD_ATTR_DSIZE_32BIT, ++ 0, 4, 8*4); ++ } ++ ++#endif ++ priv->dma_done = 0; ++ ++ DPRINTK("EXIT\n"); ++ ++} ++ ++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; ++ ++ /* ++ * Start the channel. ++ */ ++ chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan : priv->dma_rchan; ++ ++ mcf_edma_enable_transfer(chan); ++ ++ ap->ops->sff_exec_command(ap, &qc->tf); ++} ++ ++static void pata_fsl_bmdma_stop(struct ata_queued_cmd *qc) ++{ ++ struct ata_port *ap = qc->ap; ++/* ++ int chan; ++ ++ chan = qc->dma_dir == DMA_TO_DEVICE ? priv->dma_wchan : priv->dma_rchan; ++ mcf_edma_stop_transfer(chan); ++*/ ++/* do a dummy read as in ata_bmdma_stop */ ++ ata_sff_dma_pause(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 = mcf_edma_request_channel(MCF_EDMA_CHAN_ATA_RX, ++ dma_callback, ++ NULL, 0x6, ++ (void *)ap, ++ NULL, ++ "MCF 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 = mcf_edma_request_channel(MCF_EDMA_CHAN_ATA_TX, ++ dma_callback, ++ NULL, 0x6, ++ (void *)ap, ++ NULL, ++ "MCF 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; ++ mcf_edma_free_channel(priv->dma_rchan, ap); ++ mcf_edma_free_channel(priv->dma_wchan, ap); ++ kfree(priv); ++} ++#endif /* CONFIG_FSL_PATA_USE_DMA */ ++ ++static void ata_dummy_noret(struct ata_port *ap) { return; } ++ ++static struct scsi_host_template pata_fsl_sht = { ++ ATA_BMDMA_SHT(DRV_NAME), ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ .sg_tablesize = MAX_FSL_SG, ++ .dma_boundary = ATA_DMA_BOUNDARY, ++#endif ++}; ++ ++static struct ata_port_operations pata_fsl_port_ops = { ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ .inherits = &ata_bmdma_port_ops, ++#else ++ .inherits = &ata_sff_port_ops, ++#endif ++ .set_piomode = pata_fsl_set_piomode, ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ .set_dmamode = pata_fsl_set_dmamode, ++#endif ++ .cable_detect = ata_cable_40wire, ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ .bmdma_setup = pata_fsl_bmdma_setup, ++ .bmdma_start = pata_fsl_bmdma_start, ++#endif ++ ++ .sff_data_xfer = ata_sff_data_xfer_noirq, ++ .qc_prep = ata_noop_qc_prep, ++ ++ .port_start = pata_fsl_port_start, ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ .bmdma_stop = pata_fsl_bmdma_stop, ++ .bmdma_status = pata_fsl_bmdma_status, ++#endif ++}; ++ ++static void fsl_setup_port(struct ata_ioports *ioaddr) ++{ ++ unsigned int shift = 2; ++ ++ ioaddr->data_addr = ioaddr->cmd_addr + (ATA_REG_DATA << shift); ++ ioaddr->error_addr = ioaddr->cmd_addr + (ATA_REG_ERR << shift); ++ ioaddr->feature_addr = ioaddr->cmd_addr + (ATA_REG_FEATURE << shift); ++ ioaddr->nsect_addr = ioaddr->cmd_addr + (ATA_REG_NSECT << shift); ++ ioaddr->lbal_addr = ioaddr->cmd_addr + (ATA_REG_LBAL << shift); ++ ioaddr->lbam_addr = ioaddr->cmd_addr + (ATA_REG_LBAM << shift); ++ ioaddr->lbah_addr = ioaddr->cmd_addr + (ATA_REG_LBAH << shift); ++ ioaddr->device_addr = ioaddr->cmd_addr + (ATA_REG_DEVICE << shift); ++ ioaddr->status_addr = ioaddr->cmd_addr + (ATA_REG_STATUS << shift); ++ ioaddr->command_addr = ioaddr->cmd_addr + (ATA_REG_CMD << shift); ++} ++ ++/** ++ * pata_fsl_probe - attach a platform interface ++ * @pdev: platform device ++ * ++ * Register a platform bus integrated ATA host controller ++ * ++ * The 3 platform device resources are used as follows: ++ * ++ * - I/O Base (IORESOURCE_MEM) virt. addr. of ATA controller regs ++ * - CTL Base (IORESOURCE_MEM) unused ++ * - IRQ (IORESOURCE_IRQ) platform IRQ assigned to ATA ++ * ++ */ ++static int __devinit pata_fsl_probe(struct platform_device *pdev) ++{ ++ struct resource *io_res; ++ struct ata_host *host; ++ struct ata_port *ap; ++ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *) ++ pdev->dev.platform_data; ++ struct pata_fsl_priv *priv; ++ u8 *ata_regs; ++ int ret; ++ ++ DPRINTK("ENTER\n"); ++ /* ++ * Get an ata_host structure for this device ++ */ ++ host = ata_host_alloc(&pdev->dev, 1); ++ if (!host) ++ return -ENOMEM; ++ ap = host->ports[0]; ++ /* ++ * Allocate private data ++ */ ++ priv = kzalloc(sizeof(struct pata_fsl_priv), GFP_KERNEL); ++ if (priv == NULL) { ++ /* free(host); */ ++ return -ENOMEM; ++ } ++ host->private_data = priv; ++ ++ /* ++ * Set up resources ++ */ ++ if (unlikely(pdev->num_resources != 3)) { ++ dev_err(&pdev->dev, "invalid number of resources\n"); ++ return -EINVAL; ++ } ++ ++ io_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ata_regs = (u8 *)io_res->start; ++ priv->fsl_ata_regs = ata_regs; ++ ap->ioaddr.cmd_addr = (void *)(ata_regs + FSL_ATA_DRIVE_DATA); ++ ap->ioaddr.ctl_addr = (void *)(ata_regs + FSL_ATA_DRIVE_CONTROL); ++ ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; ++ ap->ops = &pata_fsl_port_ops; ++ ap->pio_mask = 0x3F; ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ ap->mwdma_mask = 0x07; ++ ap->udma_mask = 0x1F; ++/* ap->udma_mask = plat->udma_mask; */ ++/* pata_fsl_sht.sg_tablesize = plat->max_sg; */ ++#else ++ ap->mwdma_mask = 0x00; ++ ap->udma_mask = 0x00; ++#endif ++ fsl_setup_port(&ap->ioaddr); ++ ++ /* ++ * Do platform-specific initialization (e.g. allocate pins, ++ * turn on clock). After this call it is assumed that ++ * plat->get_clk_rate() can be called to calculate ++ * timing. ++ */ ++ if (plat->init && plat->init(pdev)) { ++ /* REVISIT: don't leak what ata_host_alloc() allocated */ ++ return -ENODEV; ++ } ++ ++ /* Deassert the reset bit to enable the interface */ ++ WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL); ++ ++ /* Set initial timing and mode */ ++ set_ata_bus_timing(XFER_PIO_4, pdev); ++ ++#ifdef CONFIG_FSL_PATA_USE_DMA ++ /* get DMA ready */ ++ pata_fsl_dma_init(ap); ++#endif ++ ++ /* ++ * 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. ++ */ ++#if 0 ++ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN); ++#else ++ WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN); ++#endif ++ ++ /* activate */ ++ ret = ata_host_activate(host, platform_get_irq(pdev, 0), ++ ata_sff_interrupt, 0, &pata_fsl_sht); ++ DPRINTK("EXIT ret=%d\n", ret); ++ return ret; ++} ++ ++/** ++ * pata_fsl_remove - unplug a platform interface ++ * @pdev: platform device ++ * ++ * A platform bus ATA device has been unplugged. Perform the needed ++ * cleanup. Also called on module unload for any active devices. ++ */ ++static int __devexit pata_fsl_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct ata_host *host = dev_get_drvdata(dev); ++ struct pata_fsl_priv *priv = host->private_data; ++ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *) ++ pdev->dev.platform_data; ++ u8 *ata_regs = priv->fsl_ata_regs; ++ ++#if 0 ++ __raw_writel(0, ata_regs + FSL_ATA_INT_EN); /* Disable interrupts */ ++#else ++ WRITE_ATA8(0, FSL_ATA_INT_EN); /* Disable interrupts */ ++#endif ++ ++ ata_host_detach(host); ++ ++ if (plat->exit) ++ plat->exit(); ++ ++ kfree(priv); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int pata_fsl_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct ata_host *host = dev_get_drvdata(&pdev->dev); ++ struct pata_fsl_priv *priv = host->private_data; ++ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *) ++ pdev->dev.platform_data; ++ u8 *ata_regs = priv->fsl_ata_regs; ++ ++ /* Disable interrupts. */ ++#if 0 ++ __raw_writel(0, ata_regs + FSL_ATA_INT_EN); ++#else ++ WRITE_ATA8(0, FSL_ATA_INT_EN); ++#endif ++ ++ if (plat->exit) ++ plat->exit(); ++ ++ return 0; ++} ++ ++static int pata_fsl_resume(struct platform_device *pdev) ++{ ++ struct ata_host *host = dev_get_drvdata(&pdev->dev); ++ struct pata_fsl_priv *priv = host->private_data; ++ struct fsl_ata_platform_data *plat = (struct fsl_ata_platform_data *) ++ pdev->dev.platform_data; ++ u8 *ata_regs = priv->fsl_ata_regs; ++ ++ if (plat->init && plat->init(pdev)) ++ return -ENODEV; ++ /* Deassert the reset bit to enable the interface */ ++#if 0 ++ __raw_writel(FSL_ATA_CTRL_ATA_RST_B, ata_regs + FSL_ATA_CONTROL); ++#else ++ WRITE_ATA8(FSL_ATA_CTRL_ATA_RST_B, FSL_ATA_CONTROL); ++#endif ++ ++ /* Set initial timing and mode */ ++ set_ata_bus_timing(XFER_PIO_4, pdev); ++ ++ /* ++ * Enable hardware interrupts. ++ */ ++#if 0 ++ __raw_writel(FSL_ATA_INTR_ATA_INTRQ2, ata_regs + FSL_ATA_INT_EN); ++#else ++ WRITE_ATA8(FSL_ATA_INTR_ATA_INTRQ2, FSL_ATA_INT_EN); ++#endif ++ ++ return 0; ++} ++#endif ++ ++static struct platform_driver pata_fsl_driver = { ++ .probe = pata_fsl_probe, ++ .remove = __devexit_p(pata_fsl_remove), ++#ifdef CONFIG_PM ++ .suspend = pata_fsl_suspend, ++ .resume = pata_fsl_resume, ++#endif ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init pata_fsl_init(void) ++{ ++ int ret; ++ ++ DPRINTK("ENTER\n"); ++ ret = platform_driver_register(&pata_fsl_driver); ++ DPRINTK("EXIT ret=%d\n", ret); ++ return ret; ++} ++ ++static void __exit pata_fsl_exit(void) ++{ ++ platform_driver_unregister(&pata_fsl_driver); ++} ++module_init(pata_fsl_init); ++module_exit(pata_fsl_exit); ++ ++MODULE_AUTHOR("Freescale Semiconductor, Inc."); ++MODULE_DESCRIPTION("low-level driver for Freescale ATA"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); |