Add PATA host controller support for Cirrus Logic EP93xx CPU
v3 cover Add PATA host controller support for Cirrus Logic EP93xx CPU
Hi,
this is a third version of EP93xx PATA driver (PATCH v3 1/3), IDE platform code for ep93xx (PATCH v3 2/3) and IDE support for EDB93xx boards (PATCH v3 3/3).
Patches apply to current mainline (v3.4rc2).
v2:
- don't BUG() in ep93xx_pata_dma_start(), ep93xx_pata_dma_setup(),
- don't use ata_ioports structure for EP93xx IDECTRL register values,
- IORDY timeout handling using jiffies (ep93xx_pata_wait_for_iordy),
- IDE/GPIO pin muxing solved by adding ep93xx_ide_acquire/release_gpio,
- register the driver also for EDB9312 and EDB9315 boards,
- corrected many coding style issues,
- dma initialization changed: request and configure channels in ep93xx_pata_dma_init (was: request in ep93xx_pata_dma_init, configuration in ep93xx_pata_dma_setup - channels were needlessly configured to the same values before each transfer).
v3:
- use custom delay loop (ep93xx_pata_delay()) instead of ndelay(),
- use ata_timing_compute() and ata_timing_merge() to properly calculate timings for the delay loop,
- use single ata_timing struct in ep93xx_pata_data for both IDE register and PIO data transfers,
- IORDY timeout handling using custom delay loop,
- move common parts of code from ep93xx_pata_{read/write}() to ep93xx_pata_rw_begin(), ep93xx_pata_rw_end(),
- introduce ep93xx_pata_{read/write}_{reg/data}() - wrappers which call ep93xx_pata_{read/write}() with proper timings selected,
- corrected delays in ep93xx_pata_{read/write}: time waited for IORDY is added to t2; wait (t0 - t2) or t2i (whichever is greater) after deassertion of DIOW-/DIOR-,
- removed DPRINTK, VPRINTK.
The reason for using custom delay loop is that currently ndelay() has only 1us resolution on ep93xx. Using ndelay() gives us about 6 - 7us total execution time for each IDE register/PIO data transfer. This results in ~250kB/s transfers, no matter what PIO mode is selected. Using custom delay loop, this driver transfers data at ~2,1MB/s (PIO4). Is using such delay loop in the driver acceptable?
Thanks,
RP
[PATCH v3 1/3] PATA host controller driver for ep93xx [PATCH v3 2/3] ep93xx: IDE driver platform support code [PATCH v3 3/3] ep93xx: Add IDE support to edb93xx boards
arch/arm/mach-ep93xx/core.c | 96 + arch/arm/mach-ep93xx/edb93xx.c | 24 arch/arm/mach-ep93xx/include/mach/platform.h | 3 arch/arm/mach-ep93xx/soc.h | 1 drivers/ata/Kconfig | 9 drivers/ata/Makefile | 1 drivers/ata/pata_ep93xx.c | 1044 +++++++++++++++++ 7 files changed, 1178 insertions(+)
PATCH v3 1/3 PATA host controller driver for ep93xx
Add PATA host controller driver for ep93xx.
--- drivers/ata/Kconfig | 9 drivers/ata/Makefile | 1 drivers/ata/pata_ep93xx.c | 1044 ++++++++++++++++++++++++++++++++++++
drivers/ata/pata_ep93xx.c new file +1044 -0
@@ -0,0 +1,1044 @@ +/* +* EP93XX PATA controller driver. +* +* Copyright (c) 2012, Metasoft s.c. +* Rafal Prylowski <[email protected]> +* +* Based on pata_scc.c, pata_icside.c and on earlier version of EP93XX +* PATA driver by Lennert Buytenhek and Alessandro Zummo. +* Read/Write timings, resource management and other improvements +* from driver by Joao Ramos and Bartlomiej Zolnierkiewicz. +* DMA engine support based on spi-ep93xx.c by Mika Westerberg. +* +* Original copyrights: +* +* Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs +* PATA host controller driver. +* +* Copyright (c) 2009, Bartlomiej Zolnierkiewicz +* +* Heavily based on the ep93xx-ide.c driver: +* +* Copyright (c) 2009, Joao Ramos <[email protected]> +* INESC Inovacao (INOV) +* +* EP93XX PATA controller driver. +* Copyright (C) 2007 Lennert Buytenhek <[email protected]> +* +* An ATA driver for the Cirrus Logic EP93xx PATA controller. +* +* Based on an earlier version by Alessandro Zummo, which is: +* Copyright (C) 2006 Tower Technologies +*/ + +#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/delay.h> +#include <linux/dmaengine.h> +#include <linux/ktime.h> + +#include <mach/dma.h> +#include <mach/platform.h> + +#define DRV_NAME "ep93xx-ide" +#define DRV_VERSION "1.0" + +enum { +/* IDE Control Register */ +IDECTRL =0x00 , +IDECTRL_CS0N = (1 <<0 ), +IDECTRL_CS1N = (1 <<1 ), +IDECTRL_DIORN = (1 <<5 ), +IDECTRL_DIOWN = (1 <<6 ), +IDECTRL_INTRQ = (1 <<9 ), +IDECTRL_IORDY = (1 <<10 ), +/* +* the device IDE register to be accessed is selected through +* IDECTRL register's specific bitfields 'DA', 'CS1N' and 'CS0N': +* b4 b3 b2 b1 b0 +* A2 A1 A0 CS1N CS0N +* the values filled in this structure allows the value to be directly +* ORed to the IDECTRL register, hence giving directly the A[2:0] and +* CS1N/CS0N values for each IDE register. +* The values correspond to the transformation: +* ((real IDE address) << 2) | CS1N value << 1 | CS0N value +*/ +IDECTRL_ADDR_CMD =0 +2 ,/* CS1 */ +IDECTRL_ADDR_DATA = (ATA_REG_DATA <<2 ) +2 , +IDECTRL_ADDR_ERROR = (ATA_REG_ERR <<2 ) +2 , +IDECTRL_ADDR_FEATURE = (ATA_REG_FEATURE <<2 ) +2 , +IDECTRL_ADDR_NSECT = (ATA_REG_NSECT <<2 ) +2 , +IDECTRL_ADDR_LBAL = (ATA_REG_LBAL <<2 ) +2 , +IDECTRL_ADDR_LBAM = (ATA_REG_LBAM <<2 ) +2 , +IDECTRL_ADDR_LBAH = (ATA_REG_LBAH <<2 ) +2 , +IDECTRL_ADDR_DEVICE = (ATA_REG_DEVICE <<2 ) +2 , +IDECTRL_ADDR_STATUS = (ATA_REG_STATUS <<2 ) +2 , +IDECTRL_ADDR_COMMAND = (ATA_REG_CMD <<2 ) +2 , +IDECTRL_ADDR_ALTSTATUS = (0x06 <<2 ) +1 ,/* CS0 */ +IDECTRL_ADDR_CTL = (0x06 <<2 ) +1 ,/* CS0 */ + +/* IDE Configuration Register */ +IDECFG =0x04 , +IDECFG_IDEEN = (1 <<0 ), +IDECFG_PIO = (1 <<1 ), +IDECFG_MDMA = (1 <<2 ), +IDECFG_UDMA = (1 <<3 ), +IDECFG_MODE_SHIFT =4 , +IDECFG_MODE_MASK = (0xf <<4 ), +IDECFG_WST_SHIFT =8 , +IDECFG_WST_MASK = (0x3 <<8 ), + +/* MDMA Operation Register */ +IDEMDMAOP =0x08 , + +/* UDMA Operation Register */ +IDEUDMAOP =0x0c , +IDEUDMAOP_UEN = (1 <<0 ), +IDEUDMAOP_RWOP = (1 <<1 ), + +/* PIO/MDMA/UDMA Data Registers */ +IDEDATAOUT =0x10 , +IDEDATAIN =0x14 , +IDEMDMADATAOUT =0x18 , +IDEMDMADATAIN =0x1c , +IDEUDMADATAOUT =0x20 , +IDEUDMADATAIN =0x24 , + +/* UDMA Status Register */ +IDEUDMASTS =0x28 , +IDEUDMASTS_DMAIDE = (1 <<16 ), +IDEUDMASTS_INTIDE = (1 <<17 ), +IDEUDMASTS_SBUSY = (1 <<18 ), +IDEUDMASTS_NDO = (1 <<24 ), +IDEUDMASTS_NDI = (1 <<25 ), +IDEUDMASTS_N4X = (1 <<26 ), + +/* UDMA Debug Status Register */ +IDEUDMADEBUG =0x2c , +}; + +struct ep93xx_pata_data { +const struct platform_device *pdev; +void __iomem *ide_base; +struct ata_timing t; +bool iordy; + +unsigned long udma_in_phys; +unsigned long udma_out_phys; + +struct dma_chan *dma_rx_channel; +struct ep93xx_dma_data dma_rx_data; +struct dma_chan *dma_tx_channel; +struct ep93xx_dma_data dma_tx_data; +}; + +static void ep93xx_pata_clear_regs (void __iomem *base) +{ +writel (IDECTRL_CS0N |IDECTRL_CS1N |IDECTRL_DIORN | +IDECTRL_DIOWN , base +IDECTRL ); + +writel (0 , base +IDECFG ); +writel (0 , base +IDEMDMAOP ); +writel (0 , base +IDEUDMAOP ); +writel (0 , base +IDEDATAOUT ); +writel (0 , base +IDEDATAIN ); +writel (0 , base +IDEMDMADATAOUT ); +writel (0 , base +IDEMDMADATAIN ); +writel (0 , base +IDEUDMADATAOUT ); +writel (0 , base +IDEUDMADATAIN ); +writel (0 , base +IDEUDMADEBUG ); +} + +static bool ep93xx_pata_check_iordy (void __iomem *base) +{ +return !!(readl (base +IDECTRL ) &IDECTRL_IORDY ); +} + +/* +* According to EP93xx User's Guide, WST field of IDECFG specifies number +* of HCLK cycles to hold the data bus after a PIO write operation. +* It should be programmed to guarantee following delays: +* +* PIO Mode [ns] +* 0 30 +* 1 20 +* 2 15 +* 3 10 +* 4 5 +* +* Maximum possible value for HCLK is 100MHz. +*/ +static int ep93xx_pata_get_wst (int pio_mode) +{ +int val; + +if (pio_mode ==0 ) + val =3 ; +else if (pio_mode <3 ) + val =2 ; +else + val =1 ; + +return val <<IDECFG_WST_SHIFT ; +} + +static void ep93xx_pata_enable_pio (void __iomem *base,int pio_mode) +{ +writel (IDECFG_IDEEN |IDECFG_PIO | +ep93xx_pata_get_wst (pio_mode) | + (pio_mode <<IDECFG_MODE_SHIFT ), base +IDECFG ); +} + +/* +* Based on delay loop found in mach-pxa/mp900.c. +* +* Single iteration should take 5 cpu cycles. This is 25ns assuming the +* fastest ep93xx cpu speed (200MHz) and is better optimized for PIO4 timings +* than eg. 20ns. +*/ +static void ep93xx_pata_delay (unsigned long count) +{ + __asm__volatile ( +"0:\n" +"mov r0, r0\n" +"subs %0, %1, #1\n" +"bge 0b\n" + :"=r" (count) + :"0" (count) + ); +} + +static unsigned long ep93xx_pata_wait_for_iordy (void __iomem *base, +unsigned long t2) +{ +/* +* According to ATA specification, IORDY pin can be first sampled +* tA = 35ns after activation of DIOR-/DIOW-. Maximum IORDY pulse +* width is tB = 1250ns. +* +* We are already t2 delay loop iterations after activation of +* DIOR-/DIOW-, so we set timeout to (1250 + 35) / 25 - t2 additional +* delay loop iterations. +*/ +unsigned long start = (1250 +35 ) /25 - t2; +unsigned long counter = start; + +while (!ep93xx_pata_check_iordy (base) && counter--) +ep93xx_pata_delay (1 ); +return start - counter; +} + +/* common part at start of ep93xx_pata_read/write() */ +static void ep93xx_pata_rw_begin (void __iomem *base,unsigned long addr, +unsigned long t1) +{ +writel (IDECTRL_DIOWN |IDECTRL_DIORN | addr, base +IDECTRL ); +ep93xx_pata_delay (t1); +} + +/* common part@end of ep93xx_pata_read/write() */ +static void ep93xx_pata_rw_end (void __iomem *base,unsigned long addr, +bool iordy,unsigned long t0,unsigned long t2, +unsigned long t2i) +{ +ep93xx_pata_delay (t2); +/* lengthen t2 if needed */ +if (iordy) + t2 +=ep93xx_pata_wait_for_iordy (base, t2); +writel (IDECTRL_DIOWN |IDECTRL_DIORN | addr, base +IDECTRL ); +if (t0 > t2 && t0 - t2 > t2i) +ep93xx_pata_delay (t0 - t2); +else +ep93xx_pata_delay (t2i); +} + +static u16 ep93xx_pata_read (struct ep93xx_pata_data *drv_data, +unsigned long addr, +bool reg) +{ +void __iomem *base = drv_data->ide_base; +const struct ata_timing *t = &drv_data->t; +unsigned long t0 = reg ? t->cyc8b : t->cycle; +unsigned long t2 = reg ? t->act8b : t->active; +unsigned long t2i = reg ? t->rec8b : t->recover; + +ep93xx_pata_rw_begin (base, addr, t->setup); +writel (IDECTRL_DIOWN | addr, base +IDECTRL ); +/* +* The IDEDATAIN register is loaded from the DD pins at the positive +* edge of the DIORN signal. (EP93xx UG p27-14) +*/ +ep93xx_pata_rw_end (base, addr, drv_data->iordy, t0, t2, t2i); +return readl (base +IDEDATAIN ); +} + +/* IDE register read */ +static u16 ep93xx_pata_read_reg (struct ep93xx_pata_data *drv_data, +unsigned long addr) +{ +return ep93xx_pata_read (drv_data, addr, true); +} + +/* PIO data read */ +static u16 ep93xx_pata_read_data (struct ep93xx_pata_data *drv_data, +unsigned long addr) +{ +return ep93xx_pata_read (drv_data, addr, false); +} + +static void ep93xx_pata_write (struct ep93xx_pata_data *drv_data, +u16 value,unsigned long addr, +bool reg) +{ +void __iomem *base = drv_data->ide_base; +const struct ata_timing *t = &drv_data->t; +unsigned long t0 = reg ? t->cyc8b : t->cycle; +unsigned long t2 = reg ? t->act8b : t->active; +unsigned long t2i = reg ? t->rec8b : t->recover; + +ep93xx_pata_rw_begin (base, addr, t->setup); +/* +* Value from IDEDATAOUT register is driven onto the DD pins when +* DIOWN is low. (EP93xx UG p27-13) +*/ +writel (value, base +IDEDATAOUT ); +writel (IDECTRL_DIORN | addr, base +IDECTRL ); +ep93xx_pata_rw_end (base, addr, drv_data->iordy, t0, t2, t2i); +} + +/* IDE register write */ +static void ep93xx_pata_write_reg (struct ep93xx_pata_data *drv_data, +u16 value,unsigned long addr) +{ +ep93xx_pata_write (drv_data, value, addr, true); +} + +/* PIO data write */ +static void ep93xx_pata_write_data (struct ep93xx_pata_data *drv_data, +u16 value,unsigned long addr) +{ +ep93xx_pata_write (drv_data, value, addr, false); +} + +static void ep93xx_pata_set_piomode (struct ata_port *ap, +struct ata_device *adev) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; +struct ata_device *pair =ata_dev_pair (adev); +/* +* Calculate timings for the delay loop, assuming ep93xx cpu speed +* is 200MHz (maximum possible for ep93xx). If actual cpu speed is +* slower, we will wait a bit longer in each delay. +* Additional division of cpu speed by 5, because single iteration +* of our delay loop takes 5 cpu cycles (25ns). +*/ +unsigned long T =1000000 / (200 /5 ); + +ata_timing_compute (adev, adev->pio_mode, &drv_data->t,T ,0 ); +if (pair && pair->pio_mode) { +struct ata_timing t; +ata_timing_compute (pair, pair->pio_mode, &t,T ,0 ); +ata_timing_merge (&t, &drv_data->t, &drv_data->t, +ATA_TIMING_SETUP |ATA_TIMING_8BIT ); + } + drv_data->iordy =ata_pio_need_iordy (adev); + +ep93xx_pata_enable_pio (drv_data->ide_base, + adev->pio_mode -XFER_PIO_0 ); +} + +/* Note: original code is ata_sff_check_status */ +static u8 ep93xx_pata_check_status (struct ata_port *ap) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +return ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_STATUS ); +} + +static u8 ep93xx_pata_check_altstatus (struct ata_port *ap) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +return ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_ALTSTATUS ); +} + +/* Note: original code is ata_sff_tf_load */ +static void ep93xx_pata_tf_load (struct ata_port *ap, +const struct ata_taskfile *tf) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; +unsigned int is_addr = tf->flags &ATA_TFLAG_ISADDR ; + +if (tf->ctl != ap->last_ctl) { +ep93xx_pata_write_reg (drv_data, tf->ctl,IDECTRL_ADDR_CTL ); + ap->last_ctl = tf->ctl; +ata_wait_idle (ap); + } + +if (is_addr && (tf->flags &ATA_TFLAG_LBA48 )) { +ep93xx_pata_write_reg (drv_data, tf->hob_feature, +IDECTRL_ADDR_FEATURE ); +ep93xx_pata_write_reg (drv_data, tf->hob_nsect, +IDECTRL_ADDR_NSECT ); +ep93xx_pata_write_reg (drv_data, tf->hob_lbal, +IDECTRL_ADDR_LBAL ); +ep93xx_pata_write_reg (drv_data, tf->hob_lbam, +IDECTRL_ADDR_LBAM ); +ep93xx_pata_write_reg (drv_data, tf->hob_lbah, +IDECTRL_ADDR_LBAH ); + } + +if (is_addr) { +ep93xx_pata_write_reg (drv_data, tf->feature, +IDECTRL_ADDR_FEATURE ); +ep93xx_pata_write_reg (drv_data, tf->nsect,IDECTRL_ADDR_NSECT ); +ep93xx_pata_write_reg (drv_data, tf->lbal,IDECTRL_ADDR_LBAL ); +ep93xx_pata_write_reg (drv_data, tf->lbam,IDECTRL_ADDR_LBAM ); +ep93xx_pata_write_reg (drv_data, tf->lbah,IDECTRL_ADDR_LBAH ); + } + +if (tf->flags &ATA_TFLAG_DEVICE ) +ep93xx_pata_write_reg (drv_data, tf->device, +IDECTRL_ADDR_DEVICE ); + +ata_wait_idle (ap); +} + +/* Note: original code is ata_sff_tf_read */ +static void ep93xx_pata_tf_read (struct ata_port *ap,struct ata_taskfile *tf) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + + tf->command =ep93xx_pata_check_status (ap); + tf->feature =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_FEATURE ); + tf->nsect =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_NSECT ); + tf->lbal =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_LBAL ); + tf->lbam =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_LBAM ); + tf->lbah =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_LBAH ); + tf->device =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_DEVICE ); + +if (tf->flags &ATA_TFLAG_LBA48 ) { +ep93xx_pata_write_reg (drv_data, tf->ctl |ATA_HOB , +IDECTRL_ADDR_CTL ); + tf->hob_feature =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_FEATURE ); + tf->hob_nsect =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_NSECT ); + tf->hob_lbal =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_LBAL ); + tf->hob_lbam =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_LBAM ); + tf->hob_lbah =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_LBAH ); +ep93xx_pata_write_reg (drv_data, tf->ctl,IDECTRL_ADDR_CTL ); + ap->last_ctl = tf->ctl; + } +} + +/* Note: original code is ata_sff_exec_command */ +static void ep93xx_pata_exec_command (struct ata_port *ap, +const struct ata_taskfile *tf) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +ep93xx_pata_write_reg (drv_data, tf->command, +IDECTRL_ADDR_COMMAND ); +ata_sff_pause (ap); +} + +/* Note: original code is ata_sff_dev_select */ +static void ep93xx_pata_dev_select (struct ata_port *ap,unsigned int device) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; +u8 tmp =ATA_DEVICE_OBS ; + +if (device !=0 ) + tmp |=ATA_DEV1 ; + +ep93xx_pata_write_reg (drv_data, tmp,IDECTRL_ADDR_DEVICE ); +ata_sff_pause (ap);/* needed; also flushes, for mmio */ +} + +/* Note: original code is ata_sff_set_devctl */ +static void ep93xx_pata_set_devctl (struct ata_port *ap,u8 ctl) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +ep93xx_pata_write_reg (drv_data, ctl,IDECTRL_ADDR_CTL ); +} + +/* Note: original code is ata_sff_data_xfer */ +static unsigned int ep93xx_pata_data_xfer (struct ata_device *adev, +unsigned char *buf, +unsigned int buflen,int rw) +{ +struct ata_port *ap = adev->link->ap; +struct ep93xx_pata_data *drv_data = ap->host->private_data; +u16 *data = (u16 *)buf; +unsigned int words = buflen >>1 ; + +/* Transfer multiple of 2 bytes */ +while (words--) +if (rw ==READ ) + *data++ =cpu_to_le16 ( +ep93xx_pata_read_data ( + drv_data,IDECTRL_ADDR_DATA )); +else +ep93xx_pata_write_data (drv_data,le16_to_cpu (*data++), +IDECTRL_ADDR_DATA ); + +/* Transfer trailing 1 byte, if any. */ +if (unlikely (buflen &0x01 )) { +unsigned char pad[2 ] = { }; + + buf += buflen -1 ; + +if (rw ==READ ) { + *pad =cpu_to_le16 ( +ep93xx_pata_read_data ( + drv_data,IDECTRL_ADDR_DATA )); + *buf = pad[0 ]; + }else { + pad[0 ] = *buf; +ep93xx_pata_write_data (drv_data,le16_to_cpu (*pad), +IDECTRL_ADDR_DATA ); + } + words++; + } + +return words <<1 ; +} + +/* Note: original code is ata_devchk */ +static bool ep93xx_pata_device_is_present (struct ata_port *ap, +unsigned int device) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; +u8 nsect, lbal; + + ap->ops->sff_dev_select (ap, device); + +ep93xx_pata_write_reg (drv_data,0x55 ,IDECTRL_ADDR_NSECT ); +ep93xx_pata_write_reg (drv_data,0xaa ,IDECTRL_ADDR_LBAL ); + +ep93xx_pata_write_reg (drv_data,0xaa ,IDECTRL_ADDR_NSECT ); +ep93xx_pata_write_reg (drv_data,0x55 ,IDECTRL_ADDR_LBAL ); + +ep93xx_pata_write_reg (drv_data,0x55 ,IDECTRL_ADDR_NSECT ); +ep93xx_pata_write_reg (drv_data,0xaa ,IDECTRL_ADDR_LBAL ); + + nsect =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_NSECT ); + lbal =ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_LBAL ); + +if ((nsect ==0x55 ) && (lbal ==0xaa )) +return true; + +return false; +} + +/* Note: original code is ata_sff_wait_after_reset */ +static int ep93xx_pata_wait_after_reset (struct ata_link *link, +unsigned int devmask, +unsigned long deadline) +{ +struct ata_port *ap = link->ap; +struct ep93xx_pata_data *drv_data = ap->host->private_data; +unsigned int dev0 = devmask & (1 <<0 ); +unsigned int dev1 = devmask & (1 <<1 ); +int rc, ret =0 ; + +ata_msleep (ap,ATA_WAIT_AFTER_RESET ); + +/* always check readiness of the master device */ + rc =ata_sff_wait_ready (link, deadline); +/* +* -ENODEV means the odd clown forgot the D7 pulldown resistor +* and TF status is 0xff, bail out on it too. +*/ +if (rc) +return rc; + +/* +* if device 1 was found in ata_devchk, wait for register +* access briefly, then wait for BSY to clear. +*/ +if (dev1) { +int i; + + ap->ops->sff_dev_select (ap,1 ); + +/* +* Wait for register access. Some ATAPI devices fail +* to set nsect/lbal after reset, so don't waste too +* much time on it. We're gonna wait for !BSY anyway. +*/ +for (i =0 ; i <2 ; i++) { +u8 nsect, lbal; + + nsect =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_NSECT ); + lbal =ep93xx_pata_read_reg (drv_data, +IDECTRL_ADDR_LBAL ); +if (nsect ==1 && lbal ==1 ) +break ; +msleep (50 );/* give drive a breather */ + } + + rc =ata_sff_wait_ready (link, deadline); +if (rc) { +if (rc != -ENODEV ) +return rc; + ret = rc; + } + } +/* is all this really necessary? */ + ap->ops->sff_dev_select (ap,0 ); +if (dev1) + ap->ops->sff_dev_select (ap,1 ); +if (dev0) + ap->ops->sff_dev_select (ap,0 ); + +return ret; +} + +/* Note: original code is ata_bus_softreset */ +static int ep93xx_pata_bus_softreset (struct ata_port *ap,unsigned int devmask, +unsigned long deadline) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +ep93xx_pata_write_reg (drv_data, ap->ctl,IDECTRL_ADDR_CTL ); +udelay (20 );/* FIXME: flush */ +ep93xx_pata_write_reg (drv_data, ap->ctl |ATA_SRST ,IDECTRL_ADDR_CTL ); +udelay (20 );/* FIXME: flush */ +ep93xx_pata_write_reg (drv_data, ap->ctl,IDECTRL_ADDR_CTL ); + ap->last_ctl = ap->ctl; + +return ep93xx_pata_wait_after_reset (&ap->link, devmask, deadline); +} + +static void ep93xx_pata_release_dma (struct ep93xx_pata_data *drv_data) +{ +if (drv_data->dma_rx_channel) { +dma_release_channel (drv_data->dma_rx_channel); + drv_data->dma_rx_channel =NULL ; + } +if (drv_data->dma_tx_channel) { +dma_release_channel (drv_data->dma_tx_channel); + drv_data->dma_tx_channel =NULL ; + } +} + +static bool ep93xx_pata_dma_filter (struct dma_chan *chan,void *filter_param) +{ +if (ep93xx_dma_chan_is_m2p (chan)) +return false; + + chan->private = filter_param; +return true; +} + +static void ep93xx_pata_dma_init (struct ep93xx_pata_data *drv_data) +{ +const struct platform_device *pdev = drv_data->pdev; +dma_cap_mask_t mask; +struct dma_slave_config conf; + +dma_cap_zero (mask); +dma_cap_set (DMA_SLAVE , mask); + +/* +* Request two channels for IDE. Another possibility would be +* to request only one channel, and reprogram it's direction at +* start of new transfer. +*/ + drv_data->dma_rx_data.port =EP93XX_DMA_IDE ; + drv_data->dma_rx_data.direction =DMA_FROM_DEVICE ; + drv_data->dma_rx_data.name ="ep93xx-pata-rx" ; + drv_data->dma_rx_channel =dma_request_channel (mask, + ep93xx_pata_dma_filter, &drv_data->dma_rx_data); +if (!drv_data->dma_rx_channel) +return ; + + drv_data->dma_tx_data.port =EP93XX_DMA_IDE ; + drv_data->dma_tx_data.direction =DMA_TO_DEVICE ; + drv_data->dma_tx_data.name ="ep93xx-pata-tx" ; + drv_data->dma_tx_channel =dma_request_channel (mask, + ep93xx_pata_dma_filter, &drv_data->dma_tx_data); +if (!drv_data->dma_tx_channel) { +dma_release_channel (drv_data->dma_rx_channel); +return ; + } + +/* Configure receive channel direction and source address */ +memset (&conf,0 ,sizeof (conf)); + conf.direction =DMA_FROM_DEVICE ; + conf.src_addr = drv_data->udma_in_phys; + conf.src_addr_width =DMA_SLAVE_BUSWIDTH_4_BYTES ; +if (dmaengine_slave_config (drv_data->dma_rx_channel, &conf)) { +dev_err (&pdev->dev,"failed to configure rx dma channel\n" ); +ep93xx_pata_release_dma (drv_data); +return ; + } + +/* Configure transmit channel direction and destination address */ +memset (&conf,0 ,sizeof (conf)); + conf.direction =DMA_TO_DEVICE ; + conf.dst_addr = drv_data->udma_out_phys; + conf.dst_addr_width =DMA_SLAVE_BUSWIDTH_4_BYTES ; +if (dmaengine_slave_config (drv_data->dma_tx_channel, &conf)) { +dev_err (&pdev->dev,"failed to configure tx dma channel\n" ); +ep93xx_pata_release_dma (drv_data); + } +} + +static void ep93xx_pata_dma_start (struct ata_queued_cmd *qc) +{ +struct dma_async_tx_descriptor *txd; +struct ep93xx_pata_data *drv_data = qc->ap->host->private_data; +void __iomem *base = drv_data->ide_base; +struct ata_device *adev = qc->dev; +u32 v = qc->dma_dir ==DMA_TO_DEVICE ?IDEUDMAOP_RWOP :0 ; +struct dma_chan *channel = qc->dma_dir ==DMA_TO_DEVICE + ? drv_data->dma_tx_channel : drv_data->dma_rx_channel; + + txd = channel->device->device_prep_slave_sg (channel, qc->sg, + qc->n_elem, qc->dma_dir,DMA_CTRL_ACK ,NULL ); +if (!txd) { +dev_err (qc->ap->dev,"failed to prepare slave for sg dma\n" ); +return ; + } + txd->callback =NULL ; + txd->callback_param =NULL ; + +if (dmaengine_submit (txd) <0 ) { +dev_err (qc->ap->dev,"failed to submit dma transfer\n" ); +return ; + } +dma_async_issue_pending (channel); + +/* +* When enabling UDMA operation, IDEUDMAOP register needs to be +* programmed in three step sequence: +* 1) set or clear the RWOP bit, +* 2) perform dummy read of the register, +* 3) set the UEN bit. +*/ +writel (v, base +IDEUDMAOP ); +readl (base +IDEUDMAOP ); +writel (v |IDEUDMAOP_UEN , base +IDEUDMAOP ); + +writel (IDECFG_IDEEN |IDECFG_UDMA | + ((adev->xfer_mode -XFER_UDMA_0 ) <<IDECFG_MODE_SHIFT ), + base +IDECFG ); +} + +static void ep93xx_pata_dma_stop (struct ata_queued_cmd *qc) +{ +struct ep93xx_pata_data *drv_data = qc->ap->host->private_data; +void __iomem *base = drv_data->ide_base; + +/* terminate all dma transfers, if not yet finished */ +dmaengine_terminate_all (drv_data->dma_rx_channel); +dmaengine_terminate_all (drv_data->dma_tx_channel); + +/* +* To properly stop IDE-DMA, IDEUDMAOP register must to be cleared +* and IDECTRL register must be set to default value. +*/ +writel (0 , base +IDEUDMAOP ); +writel (readl (base +IDECTRL ) |IDECTRL_DIOWN |IDECTRL_DIORN | +IDECTRL_CS0N |IDECTRL_CS1N , base +IDECTRL ); + +ep93xx_pata_enable_pio (drv_data->ide_base, + qc->dev->pio_mode -XFER_PIO_0 ); + +ata_sff_dma_pause (qc->ap); +} + +static void ep93xx_pata_dma_setup (struct ata_queued_cmd *qc) +{ + qc->ap->ops->sff_exec_command (qc->ap, &qc->tf); +} + +static u8 ep93xx_pata_dma_status (struct ata_port *ap) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; +u32 val =readl (drv_data->ide_base +IDEUDMASTS ); + +/* +* UDMA Status Register bits: +* +* DMAIDE - DMA request signal from UDMA state machine, +* INTIDE - INT line generated by UDMA because of errors in the +* state machine, +* SBUSY - UDMA state machine busy, not in idle state, +* NDO - error for data-out not completed, +* NDI - error for data-in not completed, +* N4X - error for data transferred not multiplies of four +* 32-bit words. +* (EP93xx UG p27-17) +*/ +if (val &IDEUDMASTS_NDO || val &IDEUDMASTS_NDI || + val &IDEUDMASTS_N4X || val &IDEUDMASTS_INTIDE ) +return ATA_DMA_ERR ; + +/* read INTRQ (INT[3]) pin input state */ +if (readl (drv_data->ide_base +IDECTRL ) &IDECTRL_INTRQ ) +return ATA_DMA_INTR ; + +if (val &IDEUDMASTS_SBUSY || val &IDEUDMASTS_DMAIDE ) +return ATA_DMA_ACTIVE ; + +return 0 ; +} + +/* Note: original code is ata_sff_softreset */ +static int ep93xx_pata_softreset (struct ata_link *al,unsigned int *classes, +unsigned long deadline) +{ +struct ata_port *ap = al->ap; +unsigned int slave_possible = ap->flags &ATA_FLAG_SLAVE_POSS ; +unsigned int devmask =0 ; +int rc; +u8 err; + +/* determine if device 0/1 are present */ +if (ep93xx_pata_device_is_present (ap,0 )) + devmask |= (1 <<0 ); +if (slave_possible &&ep93xx_pata_device_is_present (ap,1 )) + devmask |= (1 <<1 ); + +/* select device 0 again */ + ap->ops->sff_dev_select (al->ap,0 ); + +/* issue bus reset */ + rc =ep93xx_pata_bus_softreset (ap, devmask, deadline); +/* if link is ocuppied, -ENODEV too is an error */ +if (rc && (rc != -ENODEV ||sata_scr_valid (al))) { +ata_link_printk (al,KERN_ERR ,"SRST failed (errno=%d)\n" , + rc); +return rc; + } + +/* determine by signature whether we have ATA or ATAPI devices */ + classes[0 ] =ata_sff_dev_classify (&al->device[0 ], devmask & (1 <<0 ), + &err); +if (slave_possible && err !=0x81 ) + classes[1 ] =ata_sff_dev_classify (&al->device[1 ], + devmask & (1 <<1 ), &err); + +return 0 ; +} + +/* Note: original code is ata_sff_drain_fifo */ +static void ep93xx_pata_drain_fifo (struct ata_queued_cmd *qc) +{ +int count; +struct ata_port *ap; +struct ep93xx_pata_data *drv_data; + +/* We only need to flush incoming data when a command was running */ +if (qc ==NULL || qc->dma_dir ==DMA_TO_DEVICE ) +return ; + + ap = qc->ap; + drv_data = ap->host->private_data; +/* Drain up to 64K of data before we give up this recovery method */ +for (count =0 ; (ap->ops->sff_check_status (ap) &ATA_DRQ ) + && count <65536 ; count +=2 ) +ep93xx_pata_read_reg (drv_data,IDECTRL_ADDR_DATA ); + +/* Can become DEBUG later */ +if (count) +ata_port_printk (ap,KERN_DEBUG , +"drained %d bytes to clear DRQ.\n" , count); + +} + +static int ep93xx_pata_port_start (struct ata_port *ap) +{ +struct ep93xx_pata_data *drv_data = ap->host->private_data; + +/* +* Set timings to safe values at startup (= number of ns from ATA +* specification), we'll switch to properly calculated values later. +*/ + drv_data->t = *ata_timing_find_mode (XFER_PIO_0 ); +return 0 ; +} + +static struct scsi_host_template ep93xx_pata_sht = { +ATA_BASE_SHT (DRV_NAME ), +/* ep93xx dma implementation limit */ + .sg_tablesize =32 , +/* ep93xx dma can't transfer 65536 bytes at once */ + .dma_boundary =0x7fff , +}; + +static struct ata_port_operations ep93xx_pata_port_ops = { + .inherits = &ata_bmdma_port_ops, + + .qc_prep = ata_noop_qc_prep, + + .softreset = ep93xx_pata_softreset, + .hardreset =ATA_OP_NULL , + + .sff_dev_select = ep93xx_pata_dev_select, + .sff_set_devctl = ep93xx_pata_set_devctl, + .sff_check_status = ep93xx_pata_check_status, + .sff_check_altstatus = ep93xx_pata_check_altstatus, + .sff_tf_load = ep93xx_pata_tf_load, + .sff_tf_read = ep93xx_pata_tf_read, + .sff_exec_command = ep93xx_pata_exec_command, + .sff_data_xfer = ep93xx_pata_data_xfer, + .sff_drain_fifo = ep93xx_pata_drain_fifo, + .sff_irq_clear =ATA_OP_NULL , + + .set_piomode = ep93xx_pata_set_piomode, + + .bmdma_setup = ep93xx_pata_dma_setup, + .bmdma_start = ep93xx_pata_dma_start, + .bmdma_stop = ep93xx_pata_dma_stop, + .bmdma_status = ep93xx_pata_dma_status, + + .cable_detect = ata_cable_unknown, + .port_start = ep93xx_pata_port_start, +}; + +static int __devinit ep93xx_pata_probe (struct platform_device *pdev) +{ +struct ep93xx_pata_data *drv_data; +struct ata_host *host; +struct ata_port *ap; +unsigned int irq; +struct resource *mem_res; +void __iomem *ide_base; +int err; + + err =ep93xx_ide_acquire_gpio (pdev); +if (err) +return err; + +/* INT[3] (IRQ_EP93XX_EXT3) line connected as pull down */ + irq =platform_get_irq (pdev,0 ); +if (irq <0 ) { + err = -ENXIO ; + goto err_rel_gpio; + } + + mem_res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); +if (!mem_res) { + err = -ENXIO ; + goto err_rel_gpio; + } + + ide_base =devm_request_and_ioremap (&pdev->dev, mem_res); +if (!ide_base) { + err = -ENXIO ; + goto err_rel_gpio; + } + + drv_data =devm_kzalloc (&pdev->dev,sizeof (*drv_data),GFP_KERNEL ); +if (!drv_data) { + err = -ENXIO ; + goto err_rel_gpio; + } + +platform_set_drvdata (pdev, drv_data); + drv_data->pdev = pdev; + drv_data->ide_base = ide_base; + drv_data->udma_in_phys = mem_res->start +IDEUDMADATAIN ; + drv_data->udma_out_phys = mem_res->start +IDEUDMADATAOUT ; +ep93xx_pata_dma_init (drv_data); + +/* allocate host */ + host =ata_host_alloc (&pdev->dev,1 ); +if (!host) { + err = -ENXIO ; + goto err_rel_dma; + } + +ep93xx_pata_clear_regs (ide_base); + + host->private_data = drv_data; + + ap = host->ports[0 ]; + ap->dev = &pdev->dev; + ap->ops = &ep93xx_pata_port_ops; + ap->flags |=ATA_FLAG_SLAVE_POSS ; + ap->pio_mask =ATA_PIO4 ; + +/* +* Maximum UDMA modes: +* EP931x rev.E0 - UDMA2 +* EP931x rev.E1 - UDMA3 +* EP931x rev.E2 - UDMA4 +* +* MWDMA support was removed from EP931x rev.E2, +* so this driver supports only UDMA modes. +*/ +if (drv_data->dma_rx_channel && drv_data->dma_tx_channel) { +int chip_rev =ep93xx_chip_revision (); + +if (chip_rev ==EP93XX_CHIP_REV_E1 ) + ap->udma_mask =ATA_UDMA3 ; +else if (chip_rev ==EP93XX_CHIP_REV_E2 ) + ap->udma_mask =ATA_UDMA4 ; +else + ap->udma_mask =ATA_UDMA2 ; + } + +/* defaults, pio 0 */ +ep93xx_pata_enable_pio (ide_base,0 ); + +dev_info (&pdev->dev,"version " DRV_VERSION "\n" ); + +/* activate host */ + err =ata_host_activate (host, irq, ata_bmdma_interrupt,0 , + &ep93xx_pata_sht); +if (err ==0 ) +return 0 ; + +err_rel_dma: +ep93xx_pata_release_dma (drv_data); +err_rel_gpio: +ep93xx_ide_release_gpio (pdev); +return err; +} + +static int __devexit ep93xx_pata_remove(struct platform_device *pdev) +{ +struct ata_host *host =platform_get_drvdata (pdev); +struct ep93xx_pata_data *drv_data = host->private_data; + +ata_host_detach (host); +ep93xx_pata_release_dma (drv_data); +ep93xx_pata_clear_regs (drv_data->ide_base); +ep93xx_ide_release_gpio (pdev); +return 0 ; +} + +static struct platform_driver ep93xx_pata_platform_driver = { + .driver = { + .name =DRV_NAME , + .owner =THIS_MODULE , + }, + .probe = ep93xx_pata_probe, + .remove =__devexit_p (ep93xx_pata_remove), +}; + +module_platform_driver (ep93xx_pata_platform_driver); + +MODULE_AUTHOR ("Alessandro Zummo, Lennert Buytenhek, Joao Ramos, " +"Bartlomiej Zolnierkiewicz, Rafal Prylowski" ); +MODULE_DESCRIPTION ("low-level driver for cirrus ep93xx IDE controller" ); +MODULE_LICENSE ("GPL" ); +MODULE_VERSION (DRV_VERSION ); +MODULE_ALIAS ("platform:pata_ep93xx" );
drivers/ata/Kconfig +9 -0
@@ -416,6 +416,15 @@ config PATA_EFAR If unsure, say N. +config PATA_EP93XX + tristate "Cirrus Logic EP93xx PATA support" + depends on ARCH_EP93XX + help + This option enables support for the PATA controller in + the Cirrus Logic EP9312 and EP9315 ARM CPU. + + If unsure, say N. + config PATA_HPT366 tristate "HPT 366/368 PATA support" depends on PCI
drivers/ata/Makefile +1 -0
@@ -43,6 +43,7 @@ obj-$(CONFIG_PATA_CS5535) += pata_cs5535 obj-$(CONFIG_PATA_CS5536) += pata_cs5536.o obj-$(CONFIG_PATA_CYPRESS) += pata_cypress.o obj-$(CONFIG_PATA_EFAR) += pata_efar.o +obj-$(CONFIG_PATA_EP93XX) += pata_ep93xx.o obj-$(CONFIG_PATA_HPT366) += pata_hpt366.o obj-$(CONFIG_PATA_HPT37X) += pata_hpt37x.o obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o
Jeff Garzik
AppliedPATCH v3 1/3 PATA host controller driver for ep93xx
Quoted 10 lines · Rafal Prylowski
Add PATA host controller driver for ep93xx.
Signed-off-by Rafal Prylowski<[email protected]>Cc Joao Ramos<[email protected]>Cc H Hartley Sweeten<[email protected]>Cc Ryan Mallon<[email protected]>Cc Sergei Shtylyov<[email protected]>Cc Bartlomiej Zolnierkiewicz<[email protected]>
Quoted 4 lines
--- drivers/ata/Kconfig | 9 drivers/ata/Makefile | 1 drivers/ata/pata_ep93xx.c | 1044 ++++++++++++++++++++++++++++++++++++
applied
PATCH v3 2/3 ep93xx: IDE driver platform support code
In reply to Rafal PrylowskiAdd IDE driver platform support code for ep93xx.
--- arch/arm/mach-ep93xx/core.c | 96 +++++++++++++++++ arch/arm/mach-ep93xx/include/mach/platform.h | 3 arch/arm/mach-ep93xx/soc.h | 1
arch/arm/mach-ep93xx/soc.h +1 -0
@@ -69,6 +69,7 @@#define EP93XX_BOOT_ROM_BASE EP93XX_AHB_IOMEM(0x00090000) +#define EP93XX_IDE_PHYS_BASE EP93XX_AHB_PHYS(0x000a0000)#define EP93XX_IDE_BASE EP93XX_AHB_IOMEM(0x000a0000)#define EP93XX_VIC1_BASE EP93XX_AHB_IOMEM(0x000b0000)
arch/arm/mach-ep93xx/core.c +96 -0
102 lines not on this page · raw message
arch/arm/mach-ep93xx/include/mach/platform.h +3 -0
@@ -48,6 +48,9 @@ void ep93xx_register_i2s(void);int ep93xx_i2s_acquire (void );void ep93xx_i2s_release (void );void ep93xx_register_ac97 (void ); +void ep93xx_register_ide (void ); +int ep93xx_ide_acquire_gpio (struct platform_device *pdev); +void ep93xx_ide_release_gpio (struct platform_device *pdev);void ep93xx_init_devices (void );extern struct sys_timer ep93xx_timer;
PATCH v3 2/3 ep93xx: IDE driver platform support code
Quoted 6 lines · Rafal Prylowski
Add IDE driver platform support code for ep93xx.
Signed-off-by Rafal Prylowski <[email protected]>Cc H Hartley Sweeten <[email protected]>Cc Ryan Mallon <[email protected]>
Ryan Mallon
AppliedPATCH v3 2/3 ep93xx: IDE driver platform support code
In reply to Rafal PrylowskiQuoted 6 lines · Rafal Prylowski
Add IDE driver platform support code for ep93xx.
Signed-off-by Rafal Prylowski <[email protected]>Cc H Hartley Sweeten <[email protected]>Cc Ryan Mallon <[email protected]>
Applied to ep93xx-devel. Sorry for the delay.
~Ryan
Quoted 138 lines
--- arch/arm/mach-ep93xx/core.c | 96 +++++++++++++++++ arch/arm/mach-ep93xx/include/mach/platform.h | 3 arch/arm/mach-ep93xx/soc.h | 1Index:
linux-2.6/arch/arm/mach-ep93xx/soc.h
===================================================================arch/arm/mach-ep93xx/soc.h +1
-0@@ -69,6 +69,7 @@#define EP93XX_BOOT_ROM_BASE EP93XX_AHB_IOMEM(0x00090000) +#define EP93XX_IDE_PHYS_BASE EP93XX_AHB_PHYS(0x000a0000)#define EP93XX_IDE_BASE EP93XX_AHB_IOMEM(0x000a0000)#define EP93XX_VIC1_BASE EP93XX_AHB_IOMEM(0x000b0000)Index:
linux-2.6/arch/arm/mach-ep93xx/core.c
===================================================================arch/arm/mach-ep93xx/core.c +96
-0@@ -868,6 +868,102 @@ static struct platform_device ep93xx_wdt .resource = ep93xx_wdt_resources, }; +/************************************************************************* +* EP93xx IDE +*************************************************************************/ +static struct resource ep93xx_ide_resources[] = { +DEFINE_RES_MEM (EP93XX_IDE_PHYS_BASE ,0x38 ), +DEFINE_RES_IRQ (IRQ_EP93XX_EXT3 ), +}; + +static struct platform_device ep93xx_ide_device = { + .name ="ep93xx-ide" , + .id =-1 , + .dev = { + .dma_mask = &ep93xx_ide_device.dev.coherent_dma_mask, + .coherent_dma_mask =DMA_BIT_MASK (32 ), + }, + .num_resources =ARRAY_SIZE (ep93xx_ide_resources), + .resource = ep93xx_ide_resources, +}; + +void __init ep93xx_register_ide (void ) +{ +platform_device_register (&ep93xx_ide_device); +} + +int ep93xx_ide_acquire_gpio (struct platform_device *pdev) +{ +int err; +int i; + + err =gpio_request (EP93XX_GPIO_LINE_EGPIO2 ,dev_name (&pdev->dev)); +if (err) +return err; + err =gpio_request (EP93XX_GPIO_LINE_EGPIO15 ,dev_name (&pdev->dev)); +if (err) + goto fail_egpio15; +for (i =2 ; i <8 ; i++) { + err =gpio_request (EP93XX_GPIO_LINE_E (i),dev_name (&pdev->dev)); +if (err) + goto fail_gpio_e; + } +for (i =4 ; i <8 ; i++) { + err =gpio_request (EP93XX_GPIO_LINE_G (i),dev_name (&pdev->dev)); +if (err) + goto fail_gpio_g; + } +for (i =0 ; i <8 ; i++) { + err =gpio_request (EP93XX_GPIO_LINE_H (i),dev_name (&pdev->dev)); +if (err) + goto fail_gpio_h; + } + +/* GPIO ports E[7:2], G[7:4] and H used by IDE */ +ep93xx_devcfg_clear_bits (EP93XX_SYSCON_DEVCFG_EONIDE | +EP93XX_SYSCON_DEVCFG_GONIDE | +EP93XX_SYSCON_DEVCFG_HONIDE ); +return 0 ; + +fail_gpio_h: +for (--i; i >=0 ; --i) +gpio_free (EP93XX_GPIO_LINE_H (i)); + i =8 ; +fail_gpio_g: +for (--i; i >=4 ; --i) +gpio_free (EP93XX_GPIO_LINE_G (i)); + i =8 ; +fail_gpio_e: +for (--i; i >=2 ; --i) +gpio_free (EP93XX_GPIO_LINE_E (i)); +gpio_free (EP93XX_GPIO_LINE_EGPIO15 ); +fail_egpio15: +gpio_free (EP93XX_GPIO_LINE_EGPIO2 ); +return err; +} +EXPORT_SYMBOL (ep93xx_ide_acquire_gpio); + +void ep93xx_ide_release_gpio (struct platform_device *pdev) +{ +int i; + +for (i =2 ; i <8 ; i++) +gpio_free (EP93XX_GPIO_LINE_E (i)); +for (i =4 ; i <8 ; i++) +gpio_free (EP93XX_GPIO_LINE_G (i)); +for (i =0 ; i <8 ; i++) +gpio_free (EP93XX_GPIO_LINE_H (i)); +gpio_free (EP93XX_GPIO_LINE_EGPIO15 ); +gpio_free (EP93XX_GPIO_LINE_EGPIO2 ); + + +/* GPIO ports E[7:2], G[7:4] and H used by GPIO */ +ep93xx_devcfg_set_bits (EP93XX_SYSCON_DEVCFG_EONIDE | +EP93XX_SYSCON_DEVCFG_GONIDE | +EP93XX_SYSCON_DEVCFG_HONIDE ); +} +EXPORT_SYMBOL (ep93xx_ide_release_gpio); +void __init ep93xx_init_devices (void ) {/* Disallow access to MaverickCrunch initially */ Index:
linux-2.6/arch/arm/mach-ep93xx/include/mach/platform.h
===================================================================arch/arm/mach-ep93xx/include/mach/platform.h +3
-0@@ -48,6 +48,9 @@ void ep93xx_register_i2s(void);int ep93xx_i2s_acquire (void );void ep93xx_i2s_release (void );void ep93xx_register_ac97 (void ); +void ep93xx_register_ide (void ); +int ep93xx_ide_acquire_gpio (struct platform_device *pdev); +void ep93xx_ide_release_gpio (struct platform_device *pdev);void ep93xx_init_devices (void );extern struct sys_timer ep93xx_timer;
PATCH v3 3/3 ep93xx: Add IDE support to edb93xx boards
In reply to Rafal PrylowskiAdd IDE support to edb93xx boards.
--- arch/arm/mach-ep93xx/edb93xx.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
arch/arm/mach-ep93xx/edb93xx.c +24 -0
@@ -233,6 +233,29 @@ static void __init edb93xx_register_fb(v } +/************************************************************************* +* EDB93xx IDE +*************************************************************************/ +static int __init edb93xx_has_ide (void ) +{ +/* +* Although EDB9312 and EDB9315 do have IDE capability, they have +* INTRQ line wired as pull-up, which makes using IDE interface +* problematic. +*/ +return machine_is_edb9312 () || machine_is_edb9315 || +machine_is_edb9315a (); +} + +static void __init edb93xx_register_ide (void ) +{ +if (!edb93xx_has_ide ()) +return ; + +ep93xx_register_ide (); +} + +static void __init edb93xx_init_machine (void ) {ep93xx_init_devices ();
@@ -243,6 +266,7 @@ static void __init edb93xx_init_machine(edb93xx_register_i2s ();edb93xx_register_pwm ();edb93xx_register_fb (); +edb93xx_register_ide (); }
Ryan Mallon
AppliedPATCH v3 3/3 ep93xx: Add IDE support to edb93xx boards
Quoted 7 lines · Rafal Prylowski
Add IDE support to edb93xx boards.
Signed-off-by Rafal Prylowski <[email protected]>Cc H Hartley Sweeten <[email protected]>Cc Ryan Mallon <[email protected]>
Applied to ep93xx-devel. Sorry for the delay. Note, I fixed a minor build error, see below.
~Ryan
Quoted 13 lines
--- arch/arm/mach-ep93xx/edb93xx.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)Index:
linux-2.6/arch/arm/mach-ep93xx/edb93xx.c
===================================================================arch/arm/mach-ep93xx/edb93xx.c +1
-0@@ -233,6 +233,29 @@ static void __init edb93xx_register_fb(v } +/*************************************************************************
arch/arm/mach-ep93xx/edb93xx.c static void __init edb93xx_register_fb(v · line 246
Quoted 10 lines
arch/arm/mach-ep93xx/edb93xx.c +10
-0+* EDB93xx IDE +*************************************************************************/ +static int __init edb93xx_has_ide (void ) +{ +/* +* Although EDB9312 and EDB9315 do have IDE capability, they have +* INTRQ line wired as pull-up, which makes using IDE interface +* problematic. +*/ + return machine_is_edb9312() || machine_is_edb9315 ||
machine_is_edb9315()
Quoted 24 lines
arch/arm/mach-ep93xx/edb93xx.c +13
-0+machine_is_edb9315a (); +} + +static void __init edb93xx_register_ide (void ) +{ +if (!edb93xx_has_ide ()) +return ; + +ep93xx_register_ide (); +} + +static void __init edb93xx_init_machine (void ) {ep93xx_init_devices ();@@ -243,6 +266,7 @@ static void __init edb93xx_init_machine(edb93xx_register_i2s ();edb93xx_register_pwm ();edb93xx_register_fb (); +edb93xx_register_ide (); }