diff options
-rw-r--r-- | drivers/ide/Kconfig | 9 | ||||
-rw-r--r-- | drivers/ide/arm/Makefile | 1 | ||||
-rw-r--r-- | drivers/ide/arm/mxc_ide.c | 1122 | ||||
-rw-r--r-- | drivers/ide/arm/mxc_ide.h | 195 | ||||
-rw-r--r-- | include/asm-arm/ide.h | 5 |
5 files changed, 1331 insertions, 1 deletions
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index b1a9b81c211f..e689ecab1d39 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -864,6 +864,13 @@ config BLK_DEV_IDE_BAST Say Y here if you want to support the onboard IDE channels on the Simtec BAST or the Thorcom VR1000 +config BLK_DEV_IDE_MXC + tristate "Freescale MXC IDE support" + depends on ARM && ( ARCH_MX3 || ARCH_MX27) + help + Say Y here if you want to support the IDE controller on the + Freescale iMX3 processor. + config BLK_DEV_GAYLE bool "Amiga Gayle IDE interface support" depends on AMIGA @@ -1054,7 +1061,7 @@ config BLK_DEV_UMC8672 endif config BLK_DEV_IDEDMA - def_bool BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA + def_bool BLK_DEV_IDEDMA_PCI || BLK_DEV_IDEDMA_PMAC || BLK_DEV_IDEDMA_ICS || BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA || BLK_DEV_IDE_MXC config IDEDMA_IVB bool "IGNORE word93 Validation BITS" diff --git a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile index 6a78f0755f26..0b116dac847d 100644 --- a/drivers/ide/arm/Makefile +++ b/drivers/ide/arm/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o +obj-$(CONFIG_BLK_DEV_IDE_MXC) += mxc_ide.o EXTRA_CFLAGS := -Idrivers/ide diff --git a/drivers/ide/arm/mxc_ide.c b/drivers/ide/arm/mxc_ide.c new file mode 100644 index 000000000000..142702245a5f --- /dev/null +++ b/drivers/ide/arm/mxc_ide.c @@ -0,0 +1,1122 @@ +/* + * linux/drivers/ide/arm/mxc_ide.c + * + * Based on Simtec BAST IDE driver + * Copyright (c) 2003-2004 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*! + * @file mxc_ide.c + * + * @brief ATA driver + * + * @ingroup ATA + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/ide.h> +#include <linux/init.h> +#include <linux/ide.h> +#include <linux/clk.h> + +#include <asm/mach-types.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/delay.h> +#include <asm/hardware.h> +#include <asm/arch/dma.h> +#include "mxc_ide.h" + +extern void gpio_ata_active(void); +extern void gpio_ata_inactive(void); +static int mxc_ide_config_drive(ide_drive_t * drive, u8 xfer_mode); +static void mxc_ide_dma_callback(void *arg, int error, unsigned int count); + +static struct clk *ata_clk; + +/* List of registered interfaces */ +static ide_hwif_t *ifs[1]; + +/* + * 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]) + +/*! + * Calculate values for the ATA bus timing registers and store + * them into the hardware. + * + * @param mode Selects PIO, MDMA or UDMA modes + * + * @param speed Specifies the sub-mode number + * + * @return EINVAL speed out of range, or illegal mode + */ +static int set_ata_bus_timing(int speed, enum ata_mode mode) +{ + /* get the bus clock cycle time, in ns */ + int T = 1 * 1000 * 1000 * 1000 / clk_get_rate(ata_clk); + mxc_ide_time_cfg_t cfg0, cfg1, cfg2, cfg3, cfg4, cfg5; + /* every mode gets the same t_off and t_on */ + + GET_TIME_CFG(&cfg0, MXC_IDE_TIME_OFF); + cfg0.bytes.field1 = 3; + cfg0.bytes.field2 = 3; + SET_TIME_CFG(&cfg0, 3, MXC_IDE_TIME_OFF); + + switch (mode) { + case PIO: + if (speed < 0 || speed >= NR_PIO_SPECS) { + return -EINVAL; + } + cfg0.bytes.field3 = (pio_specs[speed].t1 + T) / T; + cfg0.bytes.field4 = (pio_specs[speed].t2_8 + T) / T; + + cfg1.bytes.field1 = (pio_specs[speed].t2_8 + T) / T; + cfg1.bytes.field2 = (pio_specs[speed].tA + T) / T + 2; + cfg1.bytes.field3 = 1; + cfg1.bytes.field4 = (pio_specs[speed].t4 + T) / T; + + GET_TIME_CFG(&cfg2, MXC_IDE_TIME_9); + cfg2.bytes.field1 = (pio_specs[speed].t9 + T) / T; + + SET_TIME_CFG(&cfg0, 0x0C, MXC_IDE_TIME_OFF); + SET_TIME_CFG(&cfg1, 0x0F, MXC_IDE_TIME_2r); + SET_TIME_CFG(&cfg2, 0x01, MXC_IDE_TIME_9); + break; + case MDMA: + if (speed < 0 || speed >= NR_MDMA_SPECS) { + return -EINVAL; + } + GET_TIME_CFG(&cfg2, MXC_IDE_TIME_9); + GET_TIME_CFG(&cfg3, MXC_IDE_TIME_K); + + cfg2.bytes.field2 = (mdma_specs[speed].tM + T) / T; + cfg2.bytes.field3 = (mdma_specs[speed].tJNH + T) / T; + cfg2.bytes.field4 = (mdma_specs[speed].tD + T) / T; + + cfg3.bytes.field1 = (mdma_specs[speed].tKW + T) / T; + + SET_TIME_CFG(&cfg2, 0x0E, MXC_IDE_TIME_9); + SET_TIME_CFG(&cfg3, 0x01, MXC_IDE_TIME_K); + break; + case UDMA: + if (speed < 0 || speed >= NR_UDMA_SPECS) { + return -EINVAL; + } + + GET_TIME_CFG(&cfg3, MXC_IDE_TIME_K); + + cfg3.bytes.field2 = (udma_specs[speed].tACK + T) / T; + cfg3.bytes.field3 = (udma_specs[speed].tENV_min + T) / T; + cfg3.bytes.field4 = (udma_specs[speed].tRP + T) / T + 2; + + cfg4.bytes.field1 = (udma_specs[speed].tZAH + T) / T; + cfg4.bytes.field2 = (udma_specs[speed].tMLI + T) / T; + cfg4.bytes.field3 = (udma_specs[speed].tDVH + T) / T + 1; + cfg4.bytes.field4 = (udma_specs[speed].tDZFS + T) / T; + + cfg5.bytes.field1 = (udma_specs[speed].tDVS + T) / T; + cfg5.bytes.field2 = (udma_specs[speed].tCVH + T) / T; + cfg5.bytes.field3 = (udma_specs[speed].tSS + T) / T; + cfg5.bytes.field4 = (udma_specs[speed].tCYC + T) / T; + + SET_TIME_CFG(&cfg3, 0x0E, MXC_IDE_TIME_K); + SET_TIME_CFG(&cfg4, 0x0F, MXC_IDE_TIME_ZAH); + SET_TIME_CFG(&cfg5, 0x0F, MXC_IDE_TIME_DVS); + break; + default: + return -EINVAL; + } + return 0; +} + +/*! + * Placeholder for code to make any hardware tweaks + * necessary to select a drive. Currently we are + * not aware of any. + */ +static void mxc_ide_selectproc(ide_drive_t * drive) +{ +} + +/*! + * Called to set the PIO mode. + * + * @param drive Specifies the drive + * @param pio Specifies the PIO mode number desired + */ +static void mxc_ide_tune(ide_drive_t * drive, u8 pio) +{ + set_ata_bus_timing(pio, PIO); +} + +/*! + * Hardware-specific interrupt service routine for the ATA driver, + * called mainly just to dismiss the interrupt at the hardware, and + * to indicate whether there actually was an interrupt pending. + * + * The generic IDE related interrupt handling is done by the IDE layer. + */ +static int mxc_ide_ack_intr(struct hwif_s *hw) +{ + unsigned char status = ATA_RAW_READ(MXC_IDE_INTR_PENDING); + unsigned char enable = ATA_RAW_READ(MXC_IDE_INTR_ENABLE); + + /* + * The only interrupts we can actually dismiss are the FIFO conditions. + * INTRQ comes from the bus, and must be dismissed according to IDE + * protocol, which will be done by the IDE layer, even when DMA + * is invovled (DMA can see it, but can't dismiss it). + */ + ATA_RAW_WRITE(status, MXC_IDE_INTR_CLEAR); + + if (status & enable & ~MXC_IDE_INTR_ATA_INTRQ2) { + printk(KERN_ERR "mxc_ide_ack_intr: unexpected interrupt, " + "status=0x%02X\n", status); + } + + return status ? 1 : 0; +} + +/*! + * Decodes the specified transfer mode and sets both timing and ultra modes + * + * @param drive Specifies the drive + * + * @param xfer_mode Specifies the desired transfer mode + * + * @return EINVAL Illegal mode specified + */ +static int mxc_ide_set_speed(ide_drive_t * drive, u8 xfer_mode) +{ + mxc_ide_private_t *priv = (mxc_ide_private_t *) HWIF(drive)->hwif_data; + + switch (xfer_mode) { + case XFER_UDMA_7: + case XFER_UDMA_6: + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + priv->ultra = 1; + return set_ata_bus_timing(xfer_mode - XFER_UDMA_0, UDMA); + break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + priv->ultra = 0; + return set_ata_bus_timing(xfer_mode - XFER_MW_DMA_0, MDMA); + break; + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + return set_ata_bus_timing(xfer_mode - XFER_PIO_0, PIO); + break; + } + + return -EINVAL; +} + +/*! + * Called when the IDE layer is disabling DMA on a drive. + * + * @param drive Specifies the drive + */ +static void mxc_ide_dma_off_quietly(ide_drive_t * drive) +{ + drive->using_dma = 0; +} + +/*! + * Called by the IDE layer when something goes wrong + * + * @param drive Specifies the drive + * + */ +static void mxc_ide_resetproc(ide_drive_t * drive) +{ + printk(KERN_INFO "%s: resetting ATA controller\n", __func__); + + ATA_RAW_WRITE(0x00, MXC_IDE_ATA_CONTROL); + udelay(100); + ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B, MXC_IDE_ATA_CONTROL); + udelay(100); +} + +/*! + * Turn on DMA for a drive. + * + * @param drive Specifies the drive + * + * @return 0 if successful + */ +static int mxc_ide_dma_on(ide_drive_t * drive) +{ + /* consult the list of known "bad" drives */ + if (__ide_dma_bad_drive(drive)) + return 1; + + /* + * Go to UDMA3 mode. Beyond that you'll no doubt + * need an Ultra-100 cable (the 80 pin type with + * ground leads between all the signals) + */ + printk(KERN_INFO "%s: enabling UDMA3 mode\n", drive->name); + mxc_ide_config_drive(drive, XFER_UDMA_3); + drive->using_dma = 1; + + blk_queue_max_hw_segments(drive->queue, MXC_IDE_DMA_BD_NR); + blk_queue_max_hw_segments(drive->queue, MXC_IDE_DMA_BD_NR); + blk_queue_max_segment_size(drive->queue, MXC_IDE_DMA_BD_SIZE_MAX); + + HWIF(drive)->dma_host_on(drive); + return 0; +} + +/*! + * Turn on DMA if the drive can handle it. + * + * @param drive Specifies the drive + * + * @return 0 if successful + */ +static int mxc_ide_dma_check(ide_drive_t * drive) +{ + struct hd_driveid *id = drive->id; + if (id && (id->capability & 1)) { + /* + * Enable DMA on any drive that has + * UltraDMA (mode 0/1/2/3/4/5/6) enabled + */ + if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f)) + return HWIF(drive)->ide_dma_on(drive); + /* + * Enable DMA on any drive that has mode2 DMA + * (multi or single) enabled + */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || + (id->dma_1word & 0x404) == 0x404) + return HWIF(drive)->ide_dma_on(drive); + + /* Consult the list of known "good" drives */ + if (__ide_dma_good_drive(drive)) + return HWIF(drive)->ide_dma_on(drive); + } + HWIF(drive)->dma_off_quietly(drive); + return 0; +} + +/*! + * The DMA is done, and the drive is done. We'll check the BD array for + * errors, and unmap the scatter-gather list. + * + * @param drive The drive we're servicing + * + * @return 0 means all is well, others means DMA signalled an error , + */ +static int mxc_ide_dma_end(ide_drive_t * drive) +{ + ide_hwif_t *hwif = HWIF(drive); + mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data; + int dma_stat = priv->dma_stat; + + BUG_ON(drive->waiting_for_dma == 0); + drive->waiting_for_dma = 0; + + /* + * We'll unmap the sg table regardless of status. + */ + dma_unmap_sg(priv->dev, hwif->sg_table, hwif->sg_nents, + hwif->sg_dma_direction); + + return dma_stat; +} + +/*! + * The end-of-DMA interrupt handler + * + * @param drive Specifies the drive + * + * @return ide_stopped or ide_started + */ +static ide_startstop_t mxc_ide_dma_intr(ide_drive_t * drive) +{ + u8 stat, dma_stat; + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); + u8 fifo_fill; + + if (!rq) + return ide_stopped; + + fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL); + BUG_ON(fifo_fill); + + dma_stat = hwif->ide_dma_end(drive); + stat = hwif->INB(IDE_STATUS_REG); /* get drive status */ + if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) { + if (dma_stat == MXC_DMA_DONE) { + ide_end_request(drive, 1, rq->nr_sectors); + return ide_stopped; + } + printk(KERN_ERR "%s: mxc_ide_dma_intr: bad DMA status (0x%x)\n", + drive->name, dma_stat); + } + + return ide_error(drive, "mxc_ide_dma_intr", stat); +} + +/*! + * Directs the IDE INTRQ signal to DMA or to the CPU + * + * @param hwif Specifies the IDE controller + * + * @param which \b INTRQ_DMA or \b INTRQ_MCU + * + */ +static void mxc_ide_set_intrq(ide_hwif_t * hwif, intrq_which_t which) +{ + mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data; + unsigned char enable = ATA_RAW_READ(MXC_IDE_INTR_ENABLE); + + switch (which) { + case INTRQ_DMA: + enable &= ~MXC_IDE_INTR_ATA_INTRQ2; + enable |= MXC_IDE_INTR_ATA_INTRQ1; + break; + case INTRQ_MCU: + enable &= ~MXC_IDE_INTR_ATA_INTRQ1; + enable |= MXC_IDE_INTR_ATA_INTRQ2; + break; + } + priv->enable = enable; + + ATA_RAW_WRITE(enable, MXC_IDE_INTR_ENABLE); +} + +/*! + * Helper routine to configure drive speed + * + * @param drive Specifies the drive + * + * @param xfer_mode Specifies the desired transfer mode + * + * @return 0 = success, non-zero otherwise + */ +static int mxc_ide_config_drive(ide_drive_t * drive, u8 xfer_mode) +{ + int err; + ide_hwif_t *hwif = HWIF(drive); + mxc_ide_private_t *priv = (mxc_ide_private_t *) (hwif->hwif_data); + u8 prev = priv->enable; + + mxc_ide_set_speed(drive, xfer_mode); + + /* + * Work around an ADS hardware bug: + * + * OK, ide_config_drive_speed() is the right thing to call but it's + * going to leave an interrupt pending. We have to jump through hoops + * because the ADS board doesn't correctly terminate INTRQ. Instead + * of pulling it low, it pulls it high (asserted), so when the drive + * tri-states it, the MX31 sees it as an interrupt. So we'll disable + * the hardware interrupt here, and restore it a bit later, after we've + * selected the drive again and let it drive INTRQ low for a bit. + * + * On later ADS boards, when presumably the correct INTRQ termination + * is in place, these extra hoops won't be necessary, but they + * shouldn't hurt, either. + */ + priv->enable = 0; + ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE); + + err = ide_config_drive_speed(drive, xfer_mode); + + if (ATA_RAW_READ(MXC_IDE_INTR_PENDING) & + (MXC_IDE_INTR_ATA_INTRQ2 | MXC_IDE_INTR_ATA_INTRQ1)) { + /* + * OK, the 'bad thing' is happening, so we'll clear nIEN to + * get the drive to drive INTRQ, then we'll read status to + * clear any pending interrupt. This sequence gets us by + * the spurious and stuck interrupt we see otherwise. + */ + SELECT_DRIVE(drive); + udelay(2); + hwif->OUTB(0, IDE_CONTROL_REG); + udelay(2); + hwif->INB(IDE_STATUS_REG); + udelay(2); + } + + priv->enable = prev; + ATA_RAW_WRITE(prev, MXC_IDE_INTR_ENABLE); + + return err; +} + +/*! + * Masks drive interrupts temporarily at the hardware level + * + * @param drive Specifies the drive + * + * @param mask 1 = disable interrupts, 0 = re-enable + * + */ +static void mxc_ide_maskproc(ide_drive_t * drive, int mask) +{ + mxc_ide_private_t *priv = + (mxc_ide_private_t *) (HWIF(drive)->hwif_data); + BUG_ON(!priv); + + if (mask) { + ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE); + } else { + ATA_RAW_WRITE(priv->enable, MXC_IDE_INTR_ENABLE); + } +} + +/*! + * DMA completion callback routine. This gets called after the DMA request + * has completed or aborted.All we need to do here is return ownership of + * the drive's INTRQ signal back to the CPU, which will immediately raise + * the interrupt to the IDE layer. + * + * @param arg The drive we're servicing + * @param error The error number of DMA transfer. + * @param count The transfered length. + */ +static void mxc_ide_dma_callback(void *arg, int error, unsigned int count) +{ + ide_hwif_t *hwif = HWIF((ide_drive_t *) arg); + mxc_ide_private_t *priv = (mxc_ide_private_t *) (hwif->hwif_data); + unsigned long fifo_fill; + + /* + * clean the fifo if the fill register is non-zero. + * If the fill register is non-zero, it is incorrect state. + */ + fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL); + while (fifo_fill) { + ATA_RAW_READ(MXC_IDE_FIFO_DATA_32); + fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL); + } + + priv->dma_stat = error; + /* + * Redirect ata_intrq back to us instead of the DMA. + */ + mxc_ide_set_intrq(hwif, INTRQ_MCU); +} + +/*! + * DMA set up. This is called once per DMA request to the drive. It delivers + * the scatter-gather list into the DMA channel and prepares both the ATA + * controller and the DMA engine for the DMA + * transfer. + * + * @param drive The drive we're servicing + * + * @return 0 on success, non-zero otherwise + */ +static int mxc_ide_dma_setup(ide_drive_t * drive) +{ + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist *sg = hwif->sg_table; + mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data; + int dma_ultra = priv->ultra ? MXC_IDE_CTRL_DMA_ULTRA : 0; + int dma_mode = 0; + int chan; + u8 ata_control; + u8 fifo_fill; + + BUG_ON(!rq); + BUG_ON(!priv); + BUG_ON(drive->waiting_for_dma); + + /*Initialize the dma state */ + priv->dma_stat = MXC_DMA_TRANSFER_ERROR; + + /* + * Prepare the ATA controller for the DMA + */ + if (rq_data_dir(rq)) { + chan = priv->dma_write_chan; + ata_control = MXC_IDE_CTRL_FIFO_RST_B | + MXC_IDE_CTRL_ATA_RST_B | + MXC_IDE_CTRL_FIFO_TX_EN | + MXC_IDE_CTRL_DMA_PENDING | + dma_ultra | MXC_IDE_CTRL_DMA_WRITE; + + dma_mode = DMA_MODE_WRITE; + } else { + chan = priv->dma_read_chan; + ata_control = MXC_IDE_CTRL_FIFO_RST_B | + MXC_IDE_CTRL_ATA_RST_B | + MXC_IDE_CTRL_FIFO_RCV_EN | + MXC_IDE_CTRL_DMA_PENDING | dma_ultra; + + dma_mode = DMA_MODE_READ; + } + + /* + * Set up the DMA interrupt callback + */ + mxc_dma_callback_set(chan, mxc_ide_dma_callback, (void *)drive); + + /* + * If the ATA FIFO isn't empty, we shouldn't even be here + */ + fifo_fill = ATA_RAW_READ(MXC_IDE_FIFO_FILL); + BUG_ON(fifo_fill); // $$$ TODO: need better recovery here + + ide_map_sg(drive, rq); + + hwif->sg_dma_direction = rq_data_dir(rq) ? DMA_TO_DEVICE : + DMA_FROM_DEVICE; + + hwif->sg_nents = dma_map_sg(priv->dev, sg, hwif->sg_nents, + hwif->sg_dma_direction); + BUG_ON(!hwif->sg_nents); + BUG_ON(hwif->sg_nents > MXC_IDE_DMA_BD_NR); + + mxc_dma_sg_config(chan, sg, hwif->sg_nents, 0, dma_mode); + + ATA_RAW_WRITE(ata_control, MXC_IDE_ATA_CONTROL); + ATA_RAW_WRITE(MXC_IDE_DMA_WATERMARK / 2, MXC_IDE_FIFO_ALARM); + + /* + * Route ata_intrq to the DMA engine, and not to us. + */ + mxc_ide_set_intrq(hwif, INTRQ_DMA); + + /* + * The DMA and ATA controller are ready to go. + * mxc_ide_dma_start() will start the DMA transfer, + * and mxc_ide_dma_exec_cmd() will tickle the drive, which + * actually initiates the DMA transfer on the ATA bus. + * The ATA controller is DMA slave for both read and write. + */ + BUG_ON(drive->waiting_for_dma); + drive->waiting_for_dma = 1; + + return 0; +} + +/*! + * DMA timeout notification. This gets called when the IDE layer above + * us times out waiting for a request. + * + * @param drive The drive we're servicing + * + * @return 0 to attempt recovery, otherwise, an additional tick count + * to keep waiting + */ +static int mxc_ide_dma_timer_expiry(ide_drive_t * drive) +{ + printk(KERN_ERR "%s: fifo_fill=%d\n", drive->name, + readb(MXC_IDE_FIFO_FILL)); + + mxc_ide_resetproc(NULL); + + if (drive->waiting_for_dma) { + HWIF(drive)->ide_dma_end(drive); + } + + return 0; +} + +/*! + * Called by the IDE layer to start a DMA request on the specified drive. + * The request has already been prepared by \b mxc_ide_dma_setup(). All + * we need to do is pass the command through while specifying our timeout + * handler. + * + * @param drive The drive we're servicing + * + * @param cmd The IDE command for the drive + * + */ +static void mxc_ide_dma_exec_cmd(ide_drive_t * drive, u8 cmd) +{ + ide_execute_command(drive, cmd, mxc_ide_dma_intr, 2 * WAIT_CMD, + mxc_ide_dma_timer_expiry); +} + +/*! + * Called by the IDE layer to start the DMA controller. The request has + * already been prepared by \b mxc_ide_dma_setup(). All we do here + * is tickle the DMA channel. + * + * @param drive The drive we're servicing + * + */ +static void mxc_ide_dma_start(ide_drive_t * drive) +{ + struct request *rq = HWGROUP(drive)->rq; + ide_hwif_t *hwif = HWIF(drive); + mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data; + int chan = rq_data_dir(rq) ? priv->dma_write_chan : priv->dma_read_chan; + + BUG_ON(chan < 0); + + /* + * Tickle the DMA channel. This starts the channel, but it is likely + * that DMA will yield and go idle before the DMA request arrives from + * the drive. Nonetheless, at least the context will be hot. + */ + mxc_dma_enable(chan); +} + +/*! + * There is a race between the DMA interrupt and the timeout interrupt. This + * gets called during the IDE layer's timeout interrupt to see if the DMA + * interrupt has also occured or is pending. + * + * @param drive The drive we're servicing + * + * @return 1 means there is a DMA interrupt pending, 0 otherwise + */ +static int mxc_ide_dma_test_irq(ide_drive_t * drive) +{ + unsigned char status = ATA_RAW_READ(MXC_IDE_INTR_PENDING); + + /* + * We need to test the interrupt status without dismissing any. + */ + + return status & (MXC_IDE_INTR_ATA_INTRQ1 + | MXC_IDE_INTR_ATA_INTRQ2) ? 1 : 0; +} + +/*! + * Called to turn off DMA on this drive's controller, such as when a DMA error + * occured and the IDE layer wants to fail back to PIO mode. We won't do + * anything special, leaving the DMA channel allocated for future attempts. + * + * @param drive The drive we're servicing + */ +static void mxc_ide_dma_host_off(ide_drive_t * drive) +{ +} + +/*! + * Called to turn on DMA on this drive's controller. + * + * @param drive The drive we're servicing + */ +static void mxc_ide_dma_host_on(ide_drive_t * drive) +{ +} + +/*! + * Called for special handling on timeouts. + * + * @param drive The drive we're servicing + * + * @return 0 if successful, non-zero otherwise + */ +static int mxc_ide_dma_timeout(ide_drive_t * drive) +{ + return 0; +} + +/*! + * Called for special handling on lost irq's. + * + * @param drive The drive we're servicing + * + * @return 0 if successful, non-zero otherwise + */ +static int mxc_ide_dma_lost_irq(ide_drive_t * drive) +{ + return 0; +} + +/*! + * Called once per controller to set up DMA + * + * @param hwif Specifies the IDE controller + * + */ +static void mxc_ide_dma_init(ide_hwif_t * hwif) +{ + mxc_ide_private_t *priv = (mxc_ide_private_t *) hwif->hwif_data; + + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + hwif->speedproc = mxc_ide_set_speed; + hwif->resetproc = mxc_ide_resetproc; + hwif->autodma = 0; + + /* + * Allocate and setup the DMA channels + */ + priv->dma_read_chan = mxc_dma_request(MXC_DMA_ATA_RX, "MXC ATA RX"); + if (priv->dma_read_chan < 0) { + printk(KERN_ERR "%s: couldn't get RX DMA channel\n", + hwif->name); + goto err_out; + } + + priv->dma_write_chan = mxc_dma_request(MXC_DMA_ATA_TX, "MXC ATA TX"); + if (priv->dma_write_chan < 0) { + printk(KERN_ERR "%s: couldn't get TX DMA channel\n", + hwif->name); + goto err_out; + } + + printk(KERN_INFO "%s: read chan=%d , write chan=%d \n", + hwif->name, priv->dma_read_chan, priv->dma_write_chan); + + set_ata_bus_timing(0, UDMA); + + /* + * All ready now + */ + hwif->atapi_dma = 1; + hwif->ultra_mask = 0x7f; + hwif->mwdma_mask = 0x07; + hwif->swdma_mask = 0x07; + hwif->udma_four = 1; + + hwif->dma_off_quietly = mxc_ide_dma_off_quietly; + hwif->ide_dma_on = mxc_ide_dma_on; + hwif->ide_dma_check = mxc_ide_dma_check; + hwif->dma_setup = mxc_ide_dma_setup; + hwif->dma_exec_cmd = mxc_ide_dma_exec_cmd; + hwif->dma_start = mxc_ide_dma_start; + hwif->ide_dma_end = mxc_ide_dma_end; + hwif->ide_dma_test_irq = mxc_ide_dma_test_irq; + hwif->dma_host_off = mxc_ide_dma_host_off; + hwif->dma_host_on = mxc_ide_dma_host_on; + hwif->ide_dma_timeout = mxc_ide_dma_timeout; + hwif->ide_dma_lostirq = mxc_ide_dma_lost_irq; + + hwif->hwif_data = (void *)priv; + return; + + err_out: + hwif->atapi_dma = 0; + if (priv->dma_read_chan >= 0) + mxc_dma_free(priv->dma_read_chan); + if (priv->dma_write_chan >= 0) + mxc_dma_free(priv->dma_write_chan); + kfree(priv); + return; +} + +/*! + * MXC-specific IDE registration helper routine. Among other things, + * we tell the IDE layer where our standard IDE drive registers are, + * through the \b io_ports array. The IDE layer sends commands to and + * reads status directly from attached IDE drives through these drive + * registers. + * + * @param base Base address of memory mapped IDE standard drive registers + * + * @param aux Address of the auxilliary ATA control register + * + * @param irq IRQ number for our hardware + * + * @param hwifp Pointer to hwif structure to be updated by the IDE layer + * + * @param priv Pointer to private structure + * + * @return ENODEV if no drives are present, 0 otherwise + */ +static int __init +mxc_ide_register(unsigned long base, unsigned int aux, int irq, + ide_hwif_t ** hwifp, mxc_ide_private_t * priv) +{ + int i = 0; + hw_regs_t hw; + ide_hwif_t *hwif = &ide_hwifs[0]; + const int regsize = 4; + + memset(&hw, 0, sizeof(hw)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (unsigned long)base; + base += regsize; + } + hw.io_ports[IDE_CONTROL_OFFSET] = aux; + hw.irq = irq; + hw.ack_intr = &mxc_ide_ack_intr; + + *hwifp = hwif; + ide_register_hw(&hw, 0, hwifp); + + hwif->selectproc = &mxc_ide_selectproc; + hwif->tuneproc = &mxc_ide_tune; + hwif->maskproc = &mxc_ide_maskproc; + hwif->hwif_data = (void *)priv; + mxc_ide_set_intrq(hwif, INTRQ_MCU); + + /* + * The IDE layer will set hwif->present if we have devices attached, + * if we don't, discard the interface reset the ports. + */ + if (!hwif->present) { + printk(KERN_INFO "ide%d: Bus empty, interface released.\n", + hwif->index); + for (i = IDE_DATA_OFFSET; i <= IDE_CONTROL_OFFSET; ++i) + hwif->io_ports[i] = 0; + hwif->chipset = ide_unknown; + hwif->noprobe = 1; + return -ENODEV; + } + + mxc_ide_dma_init(hwif); + + for (i = 0; i < MAX_DRIVES; i++) { + mxc_ide_dma_check(hwif->drives + i); + } + + return 0; +} + +/*! + * Driver initialization routine. Prepares the hardware for ATA activity. + */ +static int __init mxc_ide_init(void) +{ + int index = 0; + mxc_ide_private_t *priv; + + printk(KERN_INFO + "MXC: IDE driver, (c) 2004-2006 Freescale Semiconductor\n"); + + ata_clk = clk_get(NULL, "ata_clk"); + clk_enable(ata_clk); + ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B, MXC_IDE_ATA_CONTROL); + + /* Select group B pins, and enable the interface */ + gpio_ata_active(); + + /* Set initial timing and mode */ + + set_ata_bus_timing(4, PIO); + + /* Reset the interface */ + + mxc_ide_resetproc(NULL); + + /* + * Enable hardware interrupts. + * INTRQ2 goes to us, so we enable it here, but we'll need to ignore + * it when DMA is doing the transfer. + */ + ATA_RAW_WRITE(MXC_IDE_INTR_ATA_INTRQ2, MXC_IDE_INTR_ENABLE); + + /* + * Allocate a private structure + */ + priv = (mxc_ide_private_t *) kmalloc(sizeof *priv, GFP_KERNEL); + if (priv == NULL) { + ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE); + gpio_ata_inactive(); + return ENOMEM; + } + + memset(priv, 0, sizeof *priv); + priv->dev = NULL; // dma_map_sg() ignores it anyway + priv->dma_read_chan = -1; + priv->dma_write_chan = -1; + + /* + * Now register + */ + + index = + mxc_ide_register(IDE_ARM_IO, IDE_ARM_CTL, IDE_ARM_IRQ, &ifs[0], + priv); + if (index == -1) { + printk(KERN_ERR "Unable to register the MXC IDE driver\n"); + ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE); + gpio_ata_inactive(); + kfree(priv); + return ENODEV; + } +#ifdef ATA_USE_IORDY + /* turn on IORDY protocol */ + + udelay(25); + ATA_RAW_WRITE(MXC_IDE_CTRL_ATA_RST_B | MXC_IDE_CTRL_IORDY_EN, + MXC_IDE_ATA_CONTROL); +#endif + + return 0; +} + +/*! + * Driver exit routine. Clean up. + */ +static void __exit mxc_ide_exit(void) +{ + ide_hwif_t *hwif = ifs[0]; + mxc_ide_private_t *priv; + + BUG_ON(!hwif); + priv = (mxc_ide_private_t *) hwif->hwif_data; + BUG_ON(!priv); + + /* + * Unregister the interface at the IDE layer. This should shut + * down any drives and pending I/O. + */ + ide_unregister(hwif->index); + + /* + * Disable hardware interrupts. + */ + ATA_RAW_WRITE(0, MXC_IDE_INTR_ENABLE); + + /* + * Deallocate DMA channels. Free's BDs and everything. + */ + if (priv->dma_read_chan >= 0) + mxc_dma_free(priv->dma_read_chan); + if (priv->dma_write_chan >= 0) + mxc_dma_free(priv->dma_write_chan); + + /* + * Turn off the clock + */ + clk_disable(ata_clk); + clk_put(ata_clk); + + /* + * Disable the interface + * Free the pins + */ + gpio_ata_inactive(); + + /* + * Free the private structure. + */ + kfree(priv); + hwif->hwif_data = NULL; + +} + +module_init(mxc_ide_init); +module_exit(mxc_ide_exit); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION + ("Freescale MXC IDE (based on Simtec BAST IDE driver by Ben Dooks <ben@simtec.co.uk>)"); diff --git a/drivers/ide/arm/mxc_ide.h b/drivers/ide/arm/mxc_ide.h new file mode 100644 index 000000000000..cf1100b047f1 --- /dev/null +++ b/drivers/ide/arm/mxc_ide.h @@ -0,0 +1,195 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ +#ifndef _MXC_IDE_H_ +#define _MXC_IDE_H_ + +/*! + * @defgroup ATA ATA/IDE Driver + */ + +/*! + * @file mxc_ide.h + * + * @brief MXC ATA/IDE hardware register and bit definitions. + * + * @ingroup ATA + */ + +#define IDE_ARM_IO IO_ADDRESS((ATA_BASE_ADDR + 0xA0) ) +#define IDE_ARM_CTL IO_ADDRESS((ATA_BASE_ADDR + 0xD8) ) +#define IDE_ARM_IRQ INT_ATA + +/* + * Interface control registers + */ + +#define MXC_IDE_FIFO_DATA_32 IO_ADDRESS((ATA_BASE_ADDR + 0x18) ) +#define MXC_IDE_FIFO_DATA_16 IO_ADDRESS((ATA_BASE_ADDR + 0x1C) ) +#define MXC_IDE_FIFO_FILL IO_ADDRESS((ATA_BASE_ADDR + 0x20) ) +#define MXC_IDE_ATA_CONTROL IO_ADDRESS((ATA_BASE_ADDR + 0x24) ) +#define MXC_IDE_INTR_PENDING IO_ADDRESS((ATA_BASE_ADDR + 0x28) ) +#define MXC_IDE_INTR_ENABLE IO_ADDRESS((ATA_BASE_ADDR + 0x2C) ) +#define MXC_IDE_INTR_CLEAR IO_ADDRESS((ATA_BASE_ADDR + 0x30) ) +#define MXC_IDE_FIFO_ALARM IO_ADDRESS((ATA_BASE_ADDR + 0x34) ) + +/* + * Control register bit definitions + */ + +#define MXC_IDE_CTRL_FIFO_RST_B 0x80 +#define MXC_IDE_CTRL_ATA_RST_B 0x40 +#define MXC_IDE_CTRL_FIFO_TX_EN 0x20 +#define MXC_IDE_CTRL_FIFO_RCV_EN 0x10 +#define MXC_IDE_CTRL_DMA_PENDING 0x08 +#define MXC_IDE_CTRL_DMA_ULTRA 0x04 +#define MXC_IDE_CTRL_DMA_WRITE 0x02 +#define MXC_IDE_CTRL_IORDY_EN 0x01 + +/* + * Interrupt registers bit definitions + */ + +#define MXC_IDE_INTR_ATA_INTRQ1 0x80 +#define MXC_IDE_INTR_FIFO_UNDERFLOW 0x40 +#define MXC_IDE_INTR_FIFO_OVERFLOW 0x20 +#define MXC_IDE_INTR_CTRL_IDLE 0x10 +#define MXC_IDE_INTR_ATA_INTRQ2 0x08 + +/* + * timing registers + */ + +#define MXC_IDE_TIME_OFF IO_ADDRESS((ATA_BASE_ADDR + 0x00) ) +#define MXC_IDE_TIME_ON IO_ADDRESS((ATA_BASE_ADDR + 0x01) ) +#define MXC_IDE_TIME_1 IO_ADDRESS((ATA_BASE_ADDR + 0x02) ) +#define MXC_IDE_TIME_2w IO_ADDRESS((ATA_BASE_ADDR + 0x03) ) + +#define MXC_IDE_TIME_2r IO_ADDRESS((ATA_BASE_ADDR + 0x04) ) +#define MXC_IDE_TIME_AX IO_ADDRESS((ATA_BASE_ADDR + 0x05) ) +#define MXC_IDE_TIME_PIO_RDX IO_ADDRESS((ATA_BASE_ADDR + 0x06) ) +#define MXC_IDE_TIME_4 IO_ADDRESS((ATA_BASE_ADDR + 0x07) ) + +#define MXC_IDE_TIME_9 IO_ADDRESS((ATA_BASE_ADDR + 0x08) ) +#define MXC_IDE_TIME_M IO_ADDRESS((ATA_BASE_ADDR + 0x09) ) +#define MXC_IDE_TIME_JN IO_ADDRESS((ATA_BASE_ADDR + 0x0A) ) +#define MXC_IDE_TIME_D IO_ADDRESS((ATA_BASE_ADDR + 0x0B) ) + +#define MXC_IDE_TIME_K IO_ADDRESS((ATA_BASE_ADDR + 0x0C) ) +#define MXC_IDE_TIME_ACK IO_ADDRESS((ATA_BASE_ADDR + 0x0D) ) +#define MXC_IDE_TIME_ENV IO_ADDRESS((ATA_BASE_ADDR + 0x0E) ) +#define MXC_IDE_TIME_RPX IO_ADDRESS((ATA_BASE_ADDR + 0x0F) ) + +#define MXC_IDE_TIME_ZAH IO_ADDRESS((ATA_BASE_ADDR + 0x10) ) +#define MXC_IDE_TIME_MLIX IO_ADDRESS((ATA_BASE_ADDR + 0x11) ) +#define MXC_IDE_TIME_DVH IO_ADDRESS((ATA_BASE_ADDR + 0x12) ) +#define MXC_IDE_TIME_DZFS IO_ADDRESS((ATA_BASE_ADDR + 0x13) ) + +#define MXC_IDE_TIME_DVS IO_ADDRESS((ATA_BASE_ADDR + 0x14) ) +#define MXC_IDE_TIME_CVH IO_ADDRESS((ATA_BASE_ADDR + 0x15) ) +#define MXC_IDE_TIME_SS IO_ADDRESS((ATA_BASE_ADDR + 0x16) ) +#define MXC_IDE_TIME_CYC IO_ADDRESS((ATA_BASE_ADDR + 0x17) ) + +/* + * other facts + */ +#define MXC_IDE_FIFO_SIZE 64 /* DMA FIFO size in halfwords */ +#define MXC_IDE_DMA_BD_SIZE_MAX 0xFC00 /* max size of scatterlist segment */ + +/*! Private data for the drive structure. */ +typedef struct { + struct device *dev; /*!< The device */ + int dma_read_chan; /*!< DMA channel sdma api gave us for reads */ + int dma_write_chan; /*!< DMA channel sdma api gave us for writes */ + int ultra; /*!< Remember when we're in ultra mode */ + int dma_stat; /*!< the state of DMA request */ + u8 enable; /*!< Current hardware interrupt mask */ +} mxc_ide_private_t; + +/*! ATA transfer mode for set_ata_bus_timing() */ +enum ata_mode { + PIO, /*!< Specifies PIO mode */ + MDMA, /*!< Specifies MDMA mode */ + UDMA /*!< Specifies UDMA mode */ +}; + +/*! + * The struct defines the interrupt type which will issued by interrupt singal. + */ +typedef enum { + /*! Enable ATA_INTRQ on the CPU */ + INTRQ_MCU, + + /*! Enable ATA_INTRQ on the DMA engine */ + INTRQ_DMA +} intrq_which_t; + +/*! defines the structure for timing configurations */ + +/*! + * This structure defines the bits in the ATA TIME_CONFIGx + */ +typedef union { + unsigned long config; /* the 32bits fields in TIME_CONFIGx */ + struct { + unsigned char field1; /* the 8bits field for 8bits accessing */ + unsigned char field2; /* the 8bits field for 8bits accessing */ + unsigned char field3; /* the 8bits field for 8bits accessing */ + unsigned char field4; /* the 8bits field for 8bits accessing */ + } bytes; /* the 8bits fields in TIME_CONFIGx */ +} mxc_ide_time_cfg_t; + +#ifdef MXC_ATA_USE_8_BIT_ACCESS /* use 8 bit access */ + +/*!defines the macro for accessing the register */ +#define ATA_RAW_WRITE(v, addr) __raw_writeb(v, addr) +#define ATA_RAW_READ(addr) __raw_readb(addr) + +/*! Get the configuration of TIME_CONFIG0 */ +#define GET_TIME_CFG(t, base) +/*! Set the configuration of TIME_CONFIG0. + * And mask is the mask of valid fields. + * base is the start address of this cofniguration. + */ +#define SET_TIME_CFG(t, mask, base) \ + { \ + if ((mask) & 1) \ + ATA_RAW_WRITE((t)->bytes.field1, (unsigned long)(base)); \ + if ((mask) & 2) \ + ATA_RAW_WRITE((t)->bytes.field2, (unsigned long)(base) + 1); \ + if ((mask) & 4) \ + ATA_RAW_WRITE((t)->bytes.field3, (unsigned long)(base) + 2); \ + if ((mask) & 8) \ + ATA_RAW_WRITE((t)->bytes.field4, (unsigned long)(base) + 3); \ + } + +#else /*MXC_ATA_USE_8_BIT_ACCESS */ + +/*!defines the macro for accessing the register */ +#define ATA_RAW_WRITE(v, addr) __raw_writel(v, addr) +#define ATA_RAW_READ(addr) __raw_readl(addr) +/*! Get the configuration of TIME_CONFIG0 */ +#define GET_TIME_CFG(t, base) \ + { \ + (t)->config = ATA_RAW_READ(base); \ + } + +/*! Set the configuration of TIME_CONFIG0. + * And mask is ignored. base is the start address of this configuration. + */ +#define SET_TIME_CFG(t, mask, base) \ + { \ + ATA_RAW_WRITE((t)->config, base); \ + } +#endif /*MXC_ATA_USE_8_BIT_ACCESS */ + +#endif /* !_MXC_IDE_H_ */ diff --git a/include/asm-arm/ide.h b/include/asm-arm/ide.h index 4f68c8a5a199..68d27b5c1a8d 100644 --- a/include/asm-arm/ide.h +++ b/include/asm-arm/ide.h @@ -31,6 +31,11 @@ #define __ide_mm_outsw(port,addr,len) writesw(port,addr,len) #define __ide_mm_outsl(port,addr,len) writesl(port,addr,len) +#ifdef CONFIG_ARCH_MXC +#define IDE_ARCH_ACK_INTR +#define ide_ack_intr(hwif) ((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1) +#endif /* CONFIG_ARCH_MXC */ + #endif /* __KERNEL__ */ #endif /* __ASMARM_IDE_H */ |