Subject: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs


Based on the older IDE host driver by Joao Ramos and review comments
for it from Sergei Shtylyov. Not yet tested with the real hardware.

Cc: Joao Ramos <[email protected]>
Cc: H Hartley Sweeten <[email protected]>
Cc: Ryan Mallon <[email protected]>
Cc: Sergei Shtylyov <[email protected]>
Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
---
Depends on "libata: add private driver field to struct ata_device" patch:

http://patchwork.kernel.org/patch/62926/

and also needs "ep93xx: enable/disable gpio pins for ide" one from:

http://thread.gmane.org/gmane.linux.ports.arm.kernel/57688/focus=58730

( Could somebody please tell me if it has been merged already and/or
where one can find the version which applies to more recent kernels? )

Additionally arch/arm specific parts should probably be split from this
patch before the final merge.


Sidenote:

I have neither the hardware nor much personal interest in adding support
for it so I did it mainly cause:

- IDE host driver wasn't merged due to policy change and I hate seeing fair
amount of review work from me & Sergei going into waste. :)

- I was curious about some claims I've been hearing recently so I decided to
verify them in practice (porting the driver to libata was relatively easy
but unfortunately in the process it gained almost 50% in the LOC count..).

so if somebody would like to take over this patch feel free to do it.

arch/arm/mach-ep93xx/core.c | 25
arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h | 1
arch/arm/mach-ep93xx/include/mach/platform.h | 1
drivers/ata/Kconfig | 13
drivers/ata/Makefile | 1
drivers/ata/pata_ep93xx.c | 765 ++++++++++++++++++++++++
6 files changed, 806 insertions(+)

Index: b/arch/arm/mach-ep93xx/core.c
===================================================================
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -714,6 +714,31 @@ void __init ep93xx_register_fb(struct ep
platform_device_register(&ep93xx_fb_device);
}

+static struct resource ep93xx_ide_resources[] = {
+ {
+ .start = EP93XX_IDE_PHYS_BASE,
+ .end = EP93XX_IDE_PHYS_BASE + 0x37,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_EP93XX_EXT3,
+ .end = IRQ_EP93XX_EXT3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ep93xx_ide_device = {
+ .name = "ep93xx-ide",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ep93xx_ide_resources),
+ .resource = ep93xx_ide_resources,
+};
+
+void __init ep93xx_register_ide(void)
+{
+ platform_device_register(&ep93xx_ide_device);
+}
+
extern void ep93xx_gpio_init(void);

void __init ep93xx_init_devices(void)
Index: b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
===================================================================
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -82,6 +82,7 @@
#define EP93XX_BOOT_ROM_BASE EP93XX_AHB_IOMEM(0x00090000)

#define EP93XX_IDE_BASE EP93XX_AHB_IOMEM(0x000a0000)
+#define EP93XX_IDE_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x000a0000)

#define EP93XX_VIC1_BASE EP93XX_AHB_IOMEM(0x000b0000)

Index: b/arch/arm/mach-ep93xx/include/mach/platform.h
===================================================================
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -32,6 +32,7 @@ static inline void ep93xx_devcfg_clear_b
ep93xx_devcfg_set_clear(0x00, bits);
}

+void ep93xx_register_ide(void);
void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
Index: b/drivers/ata/Kconfig
===================================================================
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -739,6 +739,19 @@ config PATA_OF_PLATFORM

If unsure, say N.

+config PATA_EP93XX
+ tristate "Cirrus Logic EPxx (EP9312, EP9315) PATA support (Experimental)"
+ depends on ARM && ARCH_EP93XXA && EXPERIMENTAL
+ help
+ This is a host controller driver for PATA hardware included in
+ Cirrus Logic's EP9312 and EP9315 CPUs.
+
+ Say Y here if you want to enable the PATA host controller support
+ for your machine.
+
+ Choose 'M' to compile this driver as a module; the module will be
+ called 'pata_ep93xx'.
+
config PATA_ICSIDE
tristate "Acorn ICS PATA support"
depends on ARM && ARCH_ACORN
Index: b/drivers/ata/Makefile
===================================================================
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_PATA_OCTEON_CF) += pata_oct
obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o
obj-$(CONFIG_PATA_AT91) += pata_at91.o
obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
+obj-$(CONFIG_PATA_EP93XX) += pata_ep93xx.o
obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o
# Should be last but two libata driver
obj-$(CONFIG_PATA_ACPI) += pata_acpi.o
Index: b/drivers/ata/pata_ep93xx.c
===================================================================
--- /dev/null
+++ b/drivers/ata/pata_ep93xx.c
@@ -0,0 +1,765 @@
+/*
+ * 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)
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO:
+ * - PIO setup needs to be verified with the documentation
+ * - MWDMA and UDMA support
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/libata.h>
+#include <linux/delay.h>
+
+#define DRV_NAME "pata_ep93xx"
+
+/*
+ * Configuration and usage of the IDE device is made through the
+ * IDE Interface Register Map (EP93xx User's Guide, Section 27).
+ *
+ * This section holds common registers and flag definitions for
+ * that interface.
+ */
+
+enum {
+ /*
+ * IDE Register offset
+ */
+ IDECTRL = 0x00,
+ IDECFG = 0x04,
+ IDEMDMAOP = 0x08,
+ IDEUDMAOP = 0x0C,
+ IDEDATAOUT = 0x10,
+ IDEDATAIN = 0x14,
+ IDEMDMADATAOUT = 0x18,
+ IDEMDMADATAIN = 0x1C,
+ IDEUDMADATAOUT = 0x20,
+ IDEUDMADATAIN = 0x24,
+ IDEUDMASTS = 0x28,
+ IDEUDMADEBUG = 0x2C,
+ IDEUDMAWRBUFSTS = 0x30,
+ IDEUDMARDBUFSTS = 0x34,
+
+ /*
+ * IDE Control Register bit fields
+ */
+ IDECTRL_CS0N = 0x00000001,
+ IDECTRL_CS1N = 0x00000002,
+ IDECTRL_DA = 0x0000001C,
+ IDECTRL_DIORN = 0x00000020,
+ IDECTRL_DIOWN = 0x00000040,
+ IDECTRL_DASPN = 0x00000080,
+ IDECTRL_DMARQ = 0x00000100,
+ IDECTRL_INTRQ = 0x00000200,
+ IDECTRL_IORDY = 0x00000400,
+
+ /*
+ * IDE Configuration Register bit fields
+ */
+ IDECFG_IDEEN = 0x00000001,
+ IDECFG_PIO = 0x00000002,
+ IDECFG_MDMA = 0x00000004,
+ IDECFG_UDMA = 0x00000008,
+ IDECFG_PIO_MODE_0 = 0x00000000,
+ IDECFG_PIO_MODE_1 = 0x00000010,
+ IDECFG_PIO_MODE_2 = 0x00000020,
+ IDECFG_PIO_MODE_3 = 0x00000030,
+ IDECFG_PIO_MODE_4 = 0x00000040,
+ IDECFG_MDMA_MODE_0 = 0x00000000,
+ IDECFG_MDMA_MODE_1 = 0x00000010,
+ IDECFG_MDMA_MODE_2 = 0x00000020,
+ IDECFG_UDMA_MODE_0 = 0x00000000,
+ IDECFG_UDMA_MODE_1 = 0x00000010,
+ IDECFG_UDMA_MODE_2 = 0x00000020,
+ IDECFG_UDMA_MODE_3 = 0x00000030,
+ IDECFG_UDMA_MODE_4 = 0x00000040,
+ IDECFG_WST = 0x00000300,
+};
+
+/* hack to make it build without gpio support */
+/*
+extern void pata_ep93xx_aquire_gpios(void);
+extern void pata_ep93xx_release_gpios(void);
+*/
+
+static struct scsi_host_template pata_ep93xx_sht = {
+ ATA_PIO_SHT(DRV_NAME),
+};
+
+static void pata_ep93xx_set_piomode(struct ata_port *ap,
+ struct ata_device *adev)
+{
+ void __iomem *base = ap->host->private_data;
+ struct ata_device *pair = ata_dev_pair(adev);
+ const struct ata_timing *t = ata_timing_find_mode(adev->pio_mode);
+ const struct ata_timing *cmd_t = t;
+ u32 reg = IDECFG_IDEEN | IDECFG_PIO;
+ u8 pio = adev->pio_mode - XFER_PIO_0;
+
+ if (pair && pair->pio_mode < adev->pio_mode)
+ cmd_t = ata_timing_find_mode(pair->pio_mode);
+
+ /*
+ * store the adequate PIO mode timings, to be used later
+ * by pata_ep93xx_{read,write}
+ */
+ adev->private_data = (void *)t;
+ ap->private_data = (void *)cmd_t;
+
+ /* reconfigure IDE controller according to PIO mode (?) */
+ switch (pio) {
+ case 4:
+ reg |= IDECFG_PIO_MODE_4 | ((1 << 8) & IDECFG_WST);
+ break;
+ case 3:
+ reg |= IDECFG_PIO_MODE_3 | ((1 << 8) & IDECFG_WST);
+ break;
+ case 2:
+ reg |= IDECFG_PIO_MODE_2 | ((2 << 8) & IDECFG_WST);
+ break;
+ case 1:
+ reg |= IDECFG_PIO_MODE_1 | ((2 << 8) & IDECFG_WST);
+ break;
+ case 0:
+ default:
+ reg |= IDECFG_PIO_MODE_0 | ((3 << 8) & IDECFG_WST);
+ break;
+ }
+ writel(reg, base + IDECFG);
+}
+
+/*
+ * Check whether IORDY is asserted or not.
+ */
+static inline int pata_ep93xx_check_iordy(void __iomem *base)
+{
+ u32 reg = readl(base + IDECTRL);
+
+ return (reg & IDECTRL_IORDY) ? 1 : 0;
+}
+
+/*
+ * IDE Interface register map default state
+ * (shutdown)
+ */
+static void pata_ep93xx_clean_regs(void __iomem *base)
+{
+ /* disable IDE interface initially */
+ writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
+ IDECTRL_DIOWN, base + IDECTRL);
+
+ /* clear IDE registers */
+ 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 u16 __pata_ep93xx_read(void __iomem *base, void *addr,
+ struct ata_timing *t)
+{
+ u32 reg;
+
+ reg = (unsigned long)addr | IDECTRL_DIORN | IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->setup);
+
+ reg &= ~IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->act8b);
+
+ while (!pata_ep93xx_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->cyc8b - t->act8b - t->setup);
+
+ return readl(base + IDEDATAIN);
+}
+
+static inline u16 pata_ep93xx_read(struct ata_port *ap, void *addr)
+{
+ void __iomem *base = ap->host->private_data;
+ struct ata_timing *t = (struct ata_timing *)ap->private_data;
+
+ return __pata_ep93xx_read(base, addr, t);
+}
+
+static void __pata_ep93xx_write(void __iomem *base, u16 value,
+ void *addr, struct ata_timing *t)
+{
+ u32 reg;
+
+ reg = (unsigned long)addr | IDECTRL_DIORN | IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->setup);
+
+ writel(value, base + IDEDATAOUT);
+
+ reg &= ~IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->act8b);
+
+ while (!pata_ep93xx_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->cyc8b - t->act8b - t->setup);
+}
+
+static inline void pata_ep93xx_write(struct ata_port *ap, u16 value,
+ void *addr)
+{
+ void __iomem *base = ap->host->private_data;
+ struct ata_timing *t = (struct ata_timing *)ap->private_data;
+
+ __pata_ep93xx_write(base, value, addr, t);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static u8 pata_ep93xx_check_status(struct ata_port *ap)
+{
+ return pata_ep93xx_read(ap, ap->ioaddr.status_addr);
+}
+
+static u8 pata_ep93xx_check_altstatus(struct ata_port *ap)
+{
+ return pata_ep93xx_read(ap, ap->ioaddr.ctl_addr);
+}
+
+static void pata_ep93xx_tf_load(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
+
+ if (tf->ctl != ap->last_ctl) {
+ pata_ep93xx_write(ap, tf->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ ata_wait_idle(ap);
+ }
+
+ if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
+ pata_ep93xx_write(ap, tf->hob_feature, ioaddr->feature_addr);
+ pata_ep93xx_write(ap, tf->hob_nsect, ioaddr->nsect_addr);
+ pata_ep93xx_write(ap, tf->hob_lbal, ioaddr->lbal_addr);
+ pata_ep93xx_write(ap, tf->hob_lbam, ioaddr->lbam_addr);
+ pata_ep93xx_write(ap, tf->hob_lbah, ioaddr->lbah_addr);
+ VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n",
+ tf->hob_feature,
+ tf->hob_nsect,
+ tf->hob_lbal,
+ tf->hob_lbam,
+ tf->hob_lbah);
+ }
+
+ if (is_addr) {
+ pata_ep93xx_write(ap, tf->feature, ioaddr->feature_addr);
+ pata_ep93xx_write(ap, tf->nsect, ioaddr->nsect_addr);
+ pata_ep93xx_write(ap, tf->lbal, ioaddr->lbal_addr);
+ pata_ep93xx_write(ap, tf->lbam, ioaddr->lbam_addr);
+ pata_ep93xx_write(ap, tf->lbah, ioaddr->lbah_addr);
+ VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n",
+ tf->feature,
+ tf->nsect,
+ tf->lbal,
+ tf->lbam,
+ tf->lbah);
+ }
+
+ if (tf->flags & ATA_TFLAG_DEVICE) {
+ pata_ep93xx_write(ap, tf->device, ioaddr->device_addr);
+ VPRINTK("device 0x%X\n", tf->device);
+ }
+
+ ata_wait_idle(ap);
+}
+
+static void pata_ep93xx_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ tf->command = pata_ep93xx_check_status(ap);
+ tf->feature = pata_ep93xx_read(ap, ioaddr->feature_addr);
+ tf->nsect = pata_ep93xx_read(ap, ioaddr->nsect_addr);
+ tf->lbal = pata_ep93xx_read(ap, ioaddr->lbal_addr);
+ tf->lbam = pata_ep93xx_read(ap, ioaddr->lbam_addr);
+ tf->lbah = pata_ep93xx_read(ap, ioaddr->lbah_addr);
+ tf->device = pata_ep93xx_read(ap, ioaddr->device_addr);
+
+ if (tf->flags & ATA_TFLAG_LBA48) {
+ pata_ep93xx_write(ap, tf->ctl | ATA_HOB, ioaddr->ctl_addr);
+ tf->hob_feature = pata_ep93xx_read(ap, ioaddr->feature_addr);
+ tf->hob_nsect = pata_ep93xx_read(ap, ioaddr->nsect_addr);
+ tf->hob_lbal = pata_ep93xx_read(ap, ioaddr->lbal_addr);
+ tf->hob_lbam = pata_ep93xx_read(ap, ioaddr->lbam_addr);
+ tf->hob_lbah = pata_ep93xx_read(ap, ioaddr->lbah_addr);
+ pata_ep93xx_write(ap, tf->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = tf->ctl;
+ }
+}
+
+static void pata_ep93xx_exec_command(struct ata_port *ap,
+ const struct ata_taskfile *tf)
+{
+ DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command);
+
+ pata_ep93xx_write(ap, tf->command, ap->ioaddr.command_addr);
+ ata_sff_pause(ap);
+}
+
+static void pata_ep93xx_dev_select(struct ata_port *ap, unsigned int device)
+{
+ u8 tmp;
+
+ if (device == 0)
+ tmp = ATA_DEVICE_OBS;
+ else
+ tmp = ATA_DEVICE_OBS | ATA_DEV1;
+
+ pata_ep93xx_write(ap, tmp, ap->ioaddr.device_addr);
+ ata_sff_pause(ap); /* needed; also flushes, for mmio */
+}
+
+static u8 pata_ep93xx_irq_on(struct ata_port *ap)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 tmp;
+
+ ap->ctl &= ~ATA_NIEN;
+ ap->last_ctl = ap->ctl;
+
+ if (ioaddr->ctl_addr)
+ pata_ep93xx_write(ap, ap->ctl, ioaddr->ctl_addr);
+ tmp = ata_wait_idle(ap);
+
+ ap->ops->sff_irq_clear(ap);
+
+ return tmp;
+}
+
+unsigned int pata_ep93xx_data_xfer(struct ata_device *adev, unsigned char *buf,
+ unsigned int buflen, int rw)
+{
+ struct ata_port *ap = adev->link->ap;
+ void __iomem *base = ap->host->private_data;
+ void *data_addr = ap->ioaddr.data_addr;
+ u16 *data = (u16 *)buf;
+ struct ata_timing *t = adev->private_data;
+ unsigned long flags;
+ unsigned int words = buflen >> 1;
+
+ local_irq_save(flags);
+
+ /* Transfer multiple of 2 bytes */
+ if (rw == READ) {
+ while (words--)
+ *data++ = cpu_to_le16(
+ __pata_ep93xx_read(base, data_addr, t));
+ } else {
+ while (words--)
+ __pata_ep93xx_write(base, le16_to_cpu(*data++),
+ data_addr, t);
+ }
+
+ /* Transfer trailing byte, if any. */
+ if (unlikely(buflen & 0x01)) {
+ unsigned char pad[2];
+
+ /* Point buf to the tail of buffer */
+ buf += buflen - 1;
+
+ if (rw == READ) {
+ *pad = cpu_to_le16(
+ __pata_ep93xx_read(base, data_addr, t));
+ *buf = pad[0];
+ } else {
+ pad[0] = *buf;
+ __pata_ep93xx_write(base, le16_to_cpu(*pad),
+ data_addr, t);
+ }
+ words++;
+ }
+
+ local_irq_restore(flags);
+
+ return words << 1;
+}
+
+static void pata_ep93xx_freeze(struct ata_port *ap)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ ap->ctl |= ATA_NIEN;
+ ap->last_ctl = ap->ctl;
+
+ if (ioaddr->ctl_addr)
+ pata_ep93xx_write(ap, ap->ctl, ioaddr->ctl_addr);
+
+ /* Under certain circumstances, some controllers raise IRQ on
+ * ATA_NIEN manipulation. Also, many controllers fail to mask
+ * previously pending IRQ on ATA_NIEN assertion. Clear it.
+ */
+ ap->ops->sff_check_status(ap);
+
+ ap->ops->sff_irq_clear(ap);
+}
+
+static unsigned int pata_ep93xx_devchk(struct ata_port *ap,
+ unsigned int device)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ u8 nsect, lbal;
+
+ ap->ops->sff_dev_select(ap, device);
+
+ pata_ep93xx_write(ap, 0x55, ioaddr->nsect_addr);
+ pata_ep93xx_write(ap, 0xaa, ioaddr->lbal_addr);
+
+ pata_ep93xx_write(ap, 0xaa, ioaddr->nsect_addr);
+ pata_ep93xx_write(ap, 0x55, ioaddr->lbal_addr);
+
+ pata_ep93xx_write(ap, 0x55, ioaddr->nsect_addr);
+ pata_ep93xx_write(ap, 0xaa, ioaddr->lbal_addr);
+
+ nsect = pata_ep93xx_read(ap, ioaddr->nsect_addr);
+ lbal = pata_ep93xx_read(ap, ioaddr->lbal_addr);
+
+ if (nsect == 0x55 && lbal == 0xaa)
+ return 1; /* we found a device */
+
+ return 0; /* nothing found */
+}
+
+static int pata_ep93xx_wait_after_reset(struct ata_link *link,
+ unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+ unsigned int dev0 = devmask & (1 << 0);
+ unsigned int dev1 = devmask & (1 << 1);
+ int rc, ret = 0;
+
+ msleep(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 = pata_ep93xx_read(ap, ioaddr->nsect_addr);
+ lbal = pata_ep93xx_read(ap, ioaddr->lbal_addr);
+ 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;
+}
+
+static int pata_ep93xx_bus_softreset(struct ata_port *ap, unsigned int devmask,
+ unsigned long deadline)
+{
+ struct ata_ioports *ioaddr = &ap->ioaddr;
+
+ DPRINTK("ata%u: bus reset via SRST\n", ap->print_id);
+
+ /* software reset. causes dev0 to be selected */
+ pata_ep93xx_write(ap, ap->ctl, ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ pata_ep93xx_write(ap, ap->ctl | ATA_SRST, ioaddr->ctl_addr);
+ udelay(20); /* FIXME: flush */
+ pata_ep93xx_write(ap, ap->ctl, ioaddr->ctl_addr);
+ ap->last_ctl = ap->ctl;
+
+ /* wait the port to become ready */
+ return pata_ep93xx_wait_after_reset(&ap->link, devmask, deadline);
+}
+
+static int pata_ep93xx_softreset(struct ata_link *link, unsigned int *classes,
+ unsigned long deadline)
+{
+ struct ata_port *ap = link->ap;
+ unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
+ unsigned int devmask = 0;
+ int rc;
+ u8 err;
+
+ DPRINTK("ENTER\n");
+
+ /* determine if device 0/1 are present */
+ if (pata_ep93xx_devchk(ap, 0))
+ devmask |= (1 << 0);
+ if (slave_possible && pata_ep93xx_devchk(ap, 1))
+ devmask |= (1 << 1);
+
+ /* select device 0 again */
+ ap->ops->sff_dev_select(ap, 0);
+
+ /* issue bus reset */
+ DPRINTK("about to softreset, devmask=%x\n", devmask);
+ rc = pata_ep93xx_bus_softreset(ap, devmask, deadline);
+ /* if link is occupied, -ENODEV too is an error */
+ if (rc && (rc != -ENODEV || sata_scr_valid(link))) {
+ ata_link_printk(link, 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(&link->device[0],
+ devmask & (1 << 0), &err);
+ if (slave_possible && err != 0x81)
+ classes[1] = ata_sff_dev_classify(&link->device[1],
+ devmask & (1 << 1), &err);
+
+ DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]);
+ return 0;
+}
+
+static void pata_ep93xx_postreset(struct ata_link *link, unsigned int *classes)
+{
+ struct ata_port *ap = link->ap;
+
+ ata_std_postreset(link, classes);
+
+ /* is double-select really necessary? */
+ if (classes[0] != ATA_DEV_NONE)
+ ap->ops->sff_dev_select(ap, 1);
+ if (classes[1] != ATA_DEV_NONE)
+ ap->ops->sff_dev_select(ap, 0);
+
+ /* bail out if no device is present */
+ if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) {
+ DPRINTK("EXIT, no device\n");
+ return;
+ }
+
+ /* set up device control */
+ if (ap->ioaddr.ctl_addr) {
+ pata_ep93xx_write(ap, ap->ctl, ap->ioaddr.ctl_addr);
+ ap->last_ctl = ap->ctl;
+ }
+}
+
+static struct ata_port_operations pata_ep93xx_port_ops = {
+ .inherits = &ata_sff_port_ops,
+
+ .set_piomode = pata_ep93xx_set_piomode,
+
+ .sff_tf_load = pata_ep93xx_tf_load,
+ .sff_tf_read = pata_ep93xx_tf_read,
+ .sff_exec_command = pata_ep93xx_exec_command,
+ .sff_check_status = pata_ep93xx_check_status,
+ .sff_check_altstatus = pata_ep93xx_check_altstatus,
+ .sff_dev_select = pata_ep93xx_dev_select,
+
+ .sff_irq_on = pata_ep93xx_irq_on,
+ .sff_data_xfer = pata_ep93xx_data_xfer,
+
+ .freeze = pata_ep93xx_freeze,
+ .softreset = pata_ep93xx_softreset,
+ .postreset = pata_ep93xx_postreset,
+};
+
+static int __init pata_ep93xx_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *mem_res;
+ void __iomem *ide_base;
+ struct ata_host *host;
+ struct ata_port *ap;
+ int irq;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(dev, "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ ide_base = devm_ioremap(dev, mem_res->start,
+ mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(dev, "could not map memory resources!\n");
+ return -ENOMEM;
+ }
+
+ host = ata_host_alloc(dev, 1);
+ if (!host) {
+ dev_err(dev, "failed to allocate memory for EP93xx ATA host\n");
+ return -ENOMEM;
+ }
+
+ host->private_data = ide_base;
+
+ ap = host->ports[0];
+ ap->ops = &pata_ep93xx_port_ops;
+ ap->flags |= ATA_FLAG_SLAVE_POSS;
+ ap->pio_mask = ATA_PIO4;
+
+ /*
+ * 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
+ */
+ ap->ioaddr.data_addr = (void *)0x02;
+ ap->ioaddr.error_addr = (void *)0x06;
+ ap->ioaddr.feature_addr = (void *)0x06;
+ ap->ioaddr.nsect_addr = (void *)0x0A;
+ ap->ioaddr.lbal_addr = (void *)0x0E;
+ ap->ioaddr.lbam_addr = (void *)0x12;
+ ap->ioaddr.lbah_addr = (void *)0x16;
+ ap->ioaddr.device_addr = (void *)0x1A;
+ ap->ioaddr.status_addr = (void *)0x1E;
+ ap->ioaddr.command_addr = (void *)0x1E;
+ ap->ioaddr.ctl_addr = (void *)0x01;
+
+ ata_port_desc(ap, "mmio 0x%llx", (unsigned long long)mem_res->start);
+
+ /* enforce EP93xx IDE controller reset state */
+ pata_ep93xx_clean_regs(ide_base);
+
+ /* set gpio port E, G and H for IDE */
+ pata_ep93xx_aquire_gpios();
+
+ /*
+ * configure IDE interface:
+ * - IDE Master Enable
+ * - Polled IO Operation
+ * - PIO Mode 0 -> 3.33 MBps
+ * - 3 Wait States -> 30 ns
+ */
+ writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_0 |
+ ((3 << 8) & IDECFG_WST), ide_base + IDECFG);
+
+ dev_info(dev, "EP93xx PATA host controller driver initialized\n");
+
+ return ata_host_activate(host, irq, ata_sff_interrupt, 0,
+ &pata_ep93xx_sht);
+}
+
+static int __devexit pata_ep93xx_remove(struct platform_device *pdev)
+{
+ struct ata_host *host = dev_get_drvdata(&pdev->dev);
+ void __iomem *base = host->private_data;
+
+ ata_host_detach(host);
+
+ /* reset state */
+ pata_ep93xx_clean_regs(base);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ pata_ep93xx_release_gpios();
+
+ return 0;
+}
+
+static struct platform_driver pata_ep93xx_driver = {
+ .remove = __exit_p(pata_ep93xx_remove),
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pata_ep93xx_init(void)
+{
+ return platform_driver_probe(&pata_ep93xx_driver, pata_ep93xx_probe);
+}
+
+static void __exit pata_ep93xx_exit(void)
+{
+ platform_driver_unregister(&pata_ep93xx_driver);
+}
+
+module_init(pata_ep93xx_init);
+module_exit(pata_ep93xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joao Ramos, Bartlomiej Zolnierkiewicz");
+MODULE_DESCRIPTION("EP93xx PATA host controller driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pata_ep93xx");


2009-11-26 17:02:17

by Alan

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs


> +static void pata_ep93xx_set_piomode(struct ata_port *ap,
> + struct ata_device *adev)
> +{
> + void __iomem *base = ap->host->private_data;
> + struct ata_device *pair = ata_dev_pair(adev);
> + const struct ata_timing *t = ata_timing_find_mode(adev->pio_mode);
> + const struct ata_timing *cmd_t = t;
> + u32 reg = IDECFG_IDEEN | IDECFG_PIO;
> + u8 pio = adev->pio_mode - XFER_PIO_0;
> +
> + if (pair && pair->pio_mode < adev->pio_mode)
> + cmd_t = ata_timing_find_mode(pair->pio_mode);
> +
> + /*
> + * store the adequate PIO mode timings, to be used later
> + * by pata_ep93xx_{read,write}
> + */
> + adev->private_data = (void *)t;
> + ap->private_data = (void *)cmd_t;
> +
> + /* reconfigure IDE controller according to PIO mode (?) */

This would probably be better in both drivers as a simple table. It would
then not be a switch but collapse the entire pile into a single

writel(pio_timing[pio], base + IDECFG);


> +static u16 __pata_ep93xx_read(void __iomem *base, void *addr,
> + struct ata_timing *t)
> +{
> + u32 reg;
> +
> + reg = (unsigned long)addr | IDECTRL_DIORN | IDECTRL_DIOWN;
> + writel(reg, base + IDECTRL);
> + ndelay(t->setup);

This implies a bus with no posting, which seems to the case from the
other driver code.

> + while (!pata_ep93xx_check_iordy(base))
> + cpu_relax();

And if the drive was hot unplugged this is doom incorporated (plus we
might not be using an IORDY mode.

>
> + while (!pata_ep93xx_check_iordy(base))
> + cpu_relax();

Ditto


I don't see anything wrong in the conversion.

The size thing is true - libata tried to abstract at a higher level which
in turn avoids lots of computed branches to one liners and lets the
compiler inline stuff better (eg all the private read/write methods into
a single call free tf_* etc). It could I guess provide an "awkward
case" skeleton tf_* and other sets of functions that were passed inb/outb
ops. One for Jeff to ponder ?



Alan

2009-11-26 19:45:41

by Ryan Mallon

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

Bartlomiej Zolnierkiewicz wrote:
> Based on the older IDE host driver by Joao Ramos and review comments
> for it from Sergei Shtylyov. Not yet tested with the real hardware.

>
> Sidenote:
>
> I have neither the hardware nor much personal interest in adding support
> for it so I did it mainly cause:
>
> - IDE host driver wasn't merged due to policy change and I hate seeing fair
> amount of review work from me & Sergei going into waste. :)
>
> - I was curious about some claims I've been hearing recently so I decided to
> verify them in practice (porting the driver to libata was relatively easy
> but unfortunately in the process it gained almost 50% in the LOC count..).
>
> so if somebody would like to take over this patch feel free to do it.

Thanks for doing this. I'll see if I can dig out a hard-drive and give
this a test. I had some comments, but I will fix them up myself if I get
round to testing this.

~Ryan

--
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon 5 Amuri Park, 404 Barbadoes St
[email protected] PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934

2009-12-02 00:53:32

by Ryan Mallon

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

Bartlomiej Zolnierkiewicz wrote:
> Based on the older IDE host driver by Joao Ramos and review comments
> for it from Sergei Shtylyov. Not yet tested with the real hardware.
>

Hi Bartlomiej,

I have got as far as patching this into my kernel and doing a build test
(still need to find a hard-disk to test). I got some build errors, see
below:

> +static void pata_ep93xx_set_piomode(struct ata_port *ap,
> + struct ata_device *adev)
> +{
> + void __iomem *base = ap->host->private_data;
> + struct ata_device *pair = ata_dev_pair(adev);
> + const struct ata_timing *t = ata_timing_find_mode(adev->pio_mode);
> + const struct ata_timing *cmd_t = t;
> + u32 reg = IDECFG_IDEEN | IDECFG_PIO;
> + u8 pio = adev->pio_mode - XFER_PIO_0;
> +
> + if (pair && pair->pio_mode < adev->pio_mode)
> + cmd_t = ata_timing_find_mode(pair->pio_mode);
> +
> + /*
> + * store the adequate PIO mode timings, to be used later
> + * by pata_ep93xx_{read,write}
> + */
> + adev->private_data = (void *)t;
> + ap->private_data = (void *)cmd_t;

struct ata_device has no member called private_data. Do we need to store
both t and cmd_t, or can we just store cmd_t in ap->private_data?

If we need both, can we have something like this (not sure if the member
names are sensible):

struct ep93xx_ata_timing {
struct ata_timing *dev_timing;
struct ata_timing *pair_timing;
};

and store that struct in ap?

> +unsigned int pata_ep93xx_data_xfer(struct ata_device *adev, unsigned char *buf,
> + unsigned int buflen, int rw)
> +{
> + struct ata_port *ap = adev->link->ap;
> + void __iomem *base = ap->host->private_data;
> + void *data_addr = ap->ioaddr.data_addr;
> + u16 *data = (u16 *)buf;
> + struct ata_timing *t = adev->private_data;

This is the only place that uses the ata_timing structure stored in
adev->private_data, can this be changed to:

struct ata_timing *t = ap->private_data;

~Ryan

--
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon 5 Amuri Park, 404 Barbadoes St
[email protected] PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934

Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

On Wednesday 02 December 2009 01:53:53 am Ryan Mallon wrote:
> Bartlomiej Zolnierkiewicz wrote:
> > Based on the older IDE host driver by Joao Ramos and review comments
> > for it from Sergei Shtylyov. Not yet tested with the real hardware.
> >
>
> Hi Bartlomiej,
>
> I have got as far as patching this into my kernel and doing a build test
> (still need to find a hard-disk to test). I got some build errors, see
> below:

Hi,

Many thanks for picking this driver up.

The following preparatory libata one-liner is needed to make it build:

http://patchwork.kernel.org/patch/62926/

and you may also need to update ep93xx ide gpio patch:

http://thread.gmane.org/gmane.linux.ports.arm.kernel/57688/focus=58730

to current kernels to make the hardware work.

--
Bartlomiej Zolnierkiewicz

2009-12-02 01:16:19

by Hartley Sweeten

[permalink] [raw]
Subject: RE: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

On Tuesday, December 01, 2009 6:07 PM, Bartlomiej Zolnierkiewicz wrote:
> On Wednesday 02 December 2009 01:53:53 am Ryan Mallon wrote:
>> Bartlomiej Zolnierkiewicz wrote:
>>> Based on the older IDE host driver by Joao Ramos and review comments
>>> for it from Sergei Shtylyov. Not yet tested with the real hardware.
>>>
>>
>> Hi Bartlomiej,
>>
>> I have got as far as patching this into my kernel and doing a build test
>> (still need to find a hard-disk to test). I got some build errors, see
>> below:
>
> Hi,
>
> Many thanks for picking this driver up.
>
> The following preparatory libata one-liner is needed to make it build:
>
> http://patchwork.kernel.org/patch/62926/
>
> and you may also need to update ep93xx ide gpio patch:
>
> http://thread.gmane.org/gmane.linux.ports.arm.kernel/57688/focus=58730
>
> to current kernels to make the hardware work.

Ryan,

I updated the core parts of this patch with the ide gpio stuff. This will
patch cleanly to 2.6.32-rc8 but will have problems after the 2.6.33 merge
due to the core keypad support already in Russell's tree.

But, for what it's worth....

Signed-off-by: H Hartley Sweeten <[email protected]>

---

diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index b4357c3..7c12c71 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -728,6 +728,91 @@ void __init ep93xx_register_fb(struct ep93xxfb_mach_info *data)
platform_device_register(&ep93xx_fb_device);
}

+
+/*************************************************************************
+ * EP93xx ide peripheral handling
+ *************************************************************************/
+static struct resource ep93xx_ide_resources[] = {
+ {
+ .start = EP93XX_IDE_PHYS_BASE,
+ .end = EP93XX_IDE_PHYS_BASE + 0x38 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = IRQ_EP93XX_EXT3,
+ .end = IRQ_EP93XX_EXT3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device ep93xx_ide_device = {
+ .name = "ep93xx-ide",
+ .id = -1,
+ .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_aquire_gpio(struct platform_device *pdev)
+{
+ int i, err;
+
+ for (i = 0; i < 8; i++) {
+ err = gpio_request(EP93XX_GPIO_LINE_E(i),
+ dev_name(&pdev->dev));
+ if (err)
+ goto fail_gpio_e;
+ err = gpio_request(EP93XX_GPIO_LINE_F(i),
+ dev_name(&pdev->dev));
+ if (err)
+ goto fail_gpio_f;
+ err = gpio_request(EP93XX_GPIO_LINE_G(i),
+ dev_name(&pdev->dev));
+ if (err)
+ goto fail_gpio_g;
+ }
+
+ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_EONIDE |
+ EP93XX_SYSCON_DEVCFG_GONIDE |
+ EP93XX_SYSCON_DEVCFG_HONIDE);
+
+ return 0;
+
+fail_gpio_g:
+ gpio_free(EP93XX_GPIO_LINE_F(i));
+fail_gpio_f:
+ gpio_free(EP93XX_GPIO_LINE_E(i));
+fail_gpio_e:
+ for ( ; i >= 0; --i) {
+ gpio_free(EP93XX_GPIO_LINE_E(i));
+ gpio_free(EP93XX_GPIO_LINE_F(i));
+ gpio_free(EP93XX_GPIO_LINE_G(i));
+ }
+ return err;
+}
+EXPORT_SYMBOL(ep93xx_ide_aquire_gpio);
+
+void ep93xx_ide_release_gpio(struct platform_device *pdev)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ gpio_free(EP93XX_GPIO_LINE_E(i));
+ gpio_free(EP93XX_GPIO_LINE_F(i));
+ gpio_free(EP93XX_GPIO_LINE_G(i));
+ }
+
+ ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_EONIDE |
+ EP93XX_SYSCON_DEVCFG_GONIDE |
+ EP93XX_SYSCON_DEVCFG_HONIDE);
+}
+EXPORT_SYMBOL(ep93xx_ide_release_gpio);
+
+
extern void ep93xx_gpio_init(void);

void __init ep93xx_init_devices(void)
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index b1f937e..bda1930 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -83,6 +83,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)
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index 469fd96..43e8f4d 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -39,6 +39,9 @@ void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
void ep93xx_register_pwm(int pwm0, int pwm1);
int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
void ep93xx_pwm_release_gpio(struct platform_device *pdev);
+void ep93xx_register_ide(void);
+int ep93xx_ide_aquire_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;

2009-12-02 01:25:49

by Ryan Mallon

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

H Hartley Sweeten wrote:
> On Tuesday, December 01, 2009 6:07 PM, Bartlomiej Zolnierkiewicz wrote:
>> On Wednesday 02 December 2009 01:53:53 am Ryan Mallon wrote:
>>> Bartlomiej Zolnierkiewicz wrote:
>>>> Based on the older IDE host driver by Joao Ramos and review comments
>>>> for it from Sergei Shtylyov. Not yet tested with the real hardware.
>>>>
>>> Hi Bartlomiej,
>>>
>>> I have got as far as patching this into my kernel and doing a build test
>>> (still need to find a hard-disk to test). I got some build errors, see
>>> below:
>> Hi,
>>
>> Many thanks for picking this driver up.
>>
>> The following preparatory libata one-liner is needed to make it build:
>>
>> http://patchwork.kernel.org/patch/62926/
>>
>> and you may also need to update ep93xx ide gpio patch:
>>
>> http://thread.gmane.org/gmane.linux.ports.arm.kernel/57688/focus=58730
>>
>> to current kernels to make the hardware work.
>
> Ryan,
>
> I updated the core parts of this patch with the ide gpio stuff. This will
> patch cleanly to 2.6.32-rc8 but will have problems after the 2.6.33 merge
> due to the core keypad support already in Russell's tree.
>
> But, for what it's worth....

Thanks. I had planned reposting this as a patch series for the driver
and the core stuff once I get it working. At my current pace, I think it
will be after 2.6.33 :-).

~Ryan

--
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon 5 Amuri Park, 404 Barbadoes St
[email protected] PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934

2009-12-14 21:40:13

by Ryan Mallon

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

Bartlomiej Zolnierkiewicz wrote:
> On Wednesday 02 December 2009 01:53:53 am Ryan Mallon wrote:
>
>> Bartlomiej Zolnierkiewicz wrote:
>>
>>> Based on the older IDE host driver by Joao Ramos and review comments
>>> for it from Sergei Shtylyov. Not yet tested with the real hardware.
>>>
>>>
>> Hi Bartlomiej,
>>
>> I have got as far as patching this into my kernel and doing a build test
>> (still need to find a hard-disk to test). I got some build errors, see
>> below:
>>
>
> Hi,
>
> Many thanks for picking this driver up.
>
Okay, it now patches in cleanly. However, I get the following crash on boot:

ep93xx-ide ep93xx-ide: EP93xx PATA host controller driver initialized
ep93xx-ide: __pata_ep93xx_write - base = ce070000, value = a, addr = 1, t = 0
Unable to handle kernel NULL pointer dereference at virtual address 00000002
pgd = c0004000
[00000002] *pgd=00000000
Internal error: Oops: 5 [#1]
last sysfs file:
Modules linked in:
CPU: 0 Not tainted (2.6.32-06794-g17ded11-dirty #544)
PC is at __pata_ep93xx_write+0x38/0xc4
LR is at __pata_ep93xx_write+0x34/0xc4
pc : [<c0189dac>] lr : [<c0189da8>] psr: 60000093
sp : cd017e70 ip : 00000c3b fp : 00000000
r10: cd12a754 r9 : c0351140 r8 : 00000000
r7 : 00000000 r6 : ce070000 r5 : 0000000a r4 : 00000061
r3 : ce070000 r2 : cd017e64 r1 : c0301bda r0 : 00000054
Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment kernel
Control: c000717f Table: c0004000 DAC: 00000017
Process swapper (pid: 1, stack limit = 0xcd016270)
Stack: (0xcd017e70 to 0xcd018000)
7e60: 00000001 00000000 00000001 cd124000
7e80: cd124000 00000000 00000000 c018a148 cd124000 c018180c a0000013 c0181918
7ea0: cd12a750 cd124000 00000000 c01772fc cd12a750 00000028 c0187e6c c0351140
7ec0: c0344220 cd12a750 00000028 c0187e6c 00000000 c0177edc cd12a750 00000028
7ee0: c0187e6c 00000000 c0351140 c0344220 c0344218 ce070000 cd12a750 00000028
7f00: c0344e68 c0014508 c0351140 00000000 c0344220 c0344220 c0351024 cd12a7a0
7f20: c0350428 00000000 00000000 c0164904 c0344220 c0163a78 c0344220 c0344254
7f40: c0351024 cd12a7a0 c0350428 c0163b88 00000000 c0163b2c c0351024 c0163330
7f60: cd0084a8 cd00fb40 c001b3dc c0351024 c0351024 c0162c48 c02e4755 c02e4755
7f80: ffffffff c001b3dc c0351010 c0351024 c0014354 00000000 00000000 c0163e54
7fa0: c001b3dc c0351010 00000000 c0014354 00000000 c0164bec c001b3dc 00000000
7fc0: 00000000 c002038c c0014354 00000000 c035d740 c001b494 c001b3dc 00000000
7fe0: 00000000 00000000 00000000 c0008578 00000000 c0021ea0 c504c134 c504c136
[<c0189dac>] (__pata_ep93xx_write+0x38/0xc4) from [<c018a148>] (pata_ep93xx_freeze+0x40/0x6c)
[<c018a148>] (pata_ep93xx_freeze+0x40/0x6c) from [<c018180c>] (__ata_port_freeze+0x38/0x50)
[<c018180c>] (__ata_port_freeze+0x38/0x50) from [<c0181918>] (ata_eh_freeze_port+0x2c/0x3c)
[<c0181918>] (ata_eh_freeze_port+0x2c/0x3c) from [<c01772fc>] (ata_host_start+0x12c/0x1a4)
[<c01772fc>] (ata_host_start+0x12c/0x1a4) from [<c0177edc>] (ata_host_activate+0x1c/0xe8)
[<c0177edc>] (ata_host_activate+0x1c/0xe8) from [<c0014508>] (pata_ep93xx_probe+0x1a0/0x1c8)
[<c0014508>] (pata_ep93xx_probe+0x1a0/0x1c8) from [<c0164904>] (platform_drv_probe+0x18/0x1c)
[<c0164904>] (platform_drv_probe+0x18/0x1c) from [<c0163a78>] (driver_probe_device+0xa8/0x15c)
[<c0163a78>] (driver_probe_device+0xa8/0x15c) from [<c0163b88>] (__driver_attach+0x5c/0x7c)
[<c0163b88>] (__driver_attach+0x5c/0x7c) from [<c0163330>] (bus_for_each_dev+0x48/0x78)
[<c0163330>] (bus_for_each_dev+0x48/0x78) from [<c0162c48>] (bus_add_driver+0x9c/0x220)
[<c0162c48>] (bus_add_driver+0x9c/0x220) from [<c0163e54>] (driver_register+0xa4/0x130)
[<c0163e54>] (driver_register+0xa4/0x130) from [<c0164bec>] (platform_driver_probe+0x18/0x8c)
[<c0164bec>] (platform_driver_probe+0x18/0x8c) from [<c002038c>] (do_one_initcall+0x5c/0x1b4)
[<c002038c>] (do_one_initcall+0x5c/0x1b4) from [<c0008578>] (kernel_init+0x94/0x10c)
[<c0008578>] (kernel_init+0x94/0x10c) from [<c0021ea0>] (kernel_thread_exit+0x0/0x8)
Code: e58dc000 e58d7004 eb03cb2e e5864000 (e1d700b2)
---[ end trace ed0490e29e490c57 ]---


I have added some of my own debugging. The problem appears to be that
__pata_ep93xx_write gets called from probe (via ata_host_activate), but
ap->private_data (ata_timing) is still null. The timing private_data is
set by pata_ep93xx_set_piomode, but that needs adev->pio_mode set, but I
don't know where this happens. I assume the ATA core handles this. Do I
need to call pata_ep93xx_set_piomode from pata_ep93xx_probe before
ata_host_activate, or should the private_data timing be set to some
default in the probe?

Thanks,
~Ryan

--
Bluewater Systems Ltd - ARM Technology Solution Centre

Ryan Mallon 5 Amuri Park, 404 Barbadoes St
[email protected] PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934

2009-12-14 22:06:52

by Jeff Garzik

[permalink] [raw]
Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

On 12/14/2009 04:40 PM, Ryan Mallon wrote:
> I have added some of my own debugging. The problem appears to be that
> __pata_ep93xx_write gets called from probe (via ata_host_activate), but
> ap->private_data (ata_timing) is still null. The timing private_data is
> set by pata_ep93xx_set_piomode, but that needs adev->pio_mode set, but I
> don't know where this happens. I assume the ATA core handles this. Do I
> need to call pata_ep93xx_set_piomode from pata_ep93xx_probe before
> ata_host_activate, or should the private_data timing be set to some
> default in the probe?


ap->private_data is traditionally initialized in the ->port_start() hook.

Likely, you should follow other drivers and allocate a useful data
structure containing useful default values, which can then be adjusted
in other hooks such as ->set_dmamode() or ->set_piomode()

Jeff


Subject: Re: [PATCH] add PATA host controller support for Cirrus Logic's EP93xx CPUs

On Monday 14 December 2009 11:06:34 pm Jeff Garzik wrote:
> On 12/14/2009 04:40 PM, Ryan Mallon wrote:
> > I have added some of my own debugging. The problem appears to be that
> > __pata_ep93xx_write gets called from probe (via ata_host_activate), but
> > ap->private_data (ata_timing) is still null. The timing private_data is
> > set by pata_ep93xx_set_piomode, but that needs adev->pio_mode set, but I
> > don't know where this happens. I assume the ATA core handles this. Do I
> > need to call pata_ep93xx_set_piomode from pata_ep93xx_probe before
> > ata_host_activate, or should the private_data timing be set to some
> > default in the probe?
>
>
> ap->private_data is traditionally initialized in the ->port_start() hook.

Ryan, this incremental patch should fix the OOPS (it adds ->port_start
implementation which sets the default 'port PIO mode' to PIO0 so we have
sane default setting before device probing takes place and ->set_piomode
takes over), please retest the driver once you have a minute..

[ Thanks for debugging the problem and sorry for the delay.. ]

---
drivers/ata/pata_ep93xx.c | 11 +++++++++++
1 file changed, 11 insertions(+)

Index: b/drivers/ata/pata_ep93xx.c
===================================================================
--- a/drivers/ata/pata_ep93xx.c
+++ b/drivers/ata/pata_ep93xx.c
@@ -613,6 +613,15 @@ static void pata_ep93xx_postreset(struct
}
}

+static int pata_ep93xx_port_start(struct ata_port *ap)
+{
+ const struct ata_timing *cmd_t = ata_timing_find_mode(XFER_PIO_0);
+
+ ap->private_data = (void *)cmd_t;
+
+ return ata_sff_port_start(ap);
+}
+
static struct ata_port_operations pata_ep93xx_port_ops = {
.inherits = &ata_sff_port_ops,

@@ -631,6 +640,8 @@ static struct ata_port_operations pata_e
.freeze = pata_ep93xx_freeze,
.softreset = pata_ep93xx_softreset,
.postreset = pata_ep93xx_postreset,
+
+ .port_start = pata_ep93xx_port_start,
};

static int __init pata_ep93xx_probe(struct platform_device *pdev)