| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934 |
- From 8e46c06091fd87904205a977be3c784e3ac61e95 Mon Sep 17 00:00:00 2001
- From: Jingchang Lu <[email protected]>
- 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 <[email protected]>
- ---
- 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);
|