* Re: EP93xx PIO IDE driver proposal
[not found] ` <49D0CAE4.9090306@inov.pt>
@ 2009-03-30 15:34 ` Sergei Shtylyov
2009-05-04 11:24 ` João Ramos
[not found] ` <49D12669.4030207@bluewatersys.com>
1 sibling, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-03-30 15:34 UTC (permalink / raw)
To: João Ramos
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
João Ramos wrote:
> Here is the revised patch according to your comments.
> Most significant changes are:
[...]
> - Removed useless blank lines and white-spaces (Ryan);
Not all of them yet. :-)
> Regards,
> João
>
> Patch follows.
>
> diff -urN linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c
> linux-2.6.29/arch/arm/mach-ep93xx/core.c
> --- linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c 2009-03-23
> 23:12:14.000000000 +0000
> +++ linux-2.6.29/arch/arm/mach-ep93xx/core.c 2009-03-30
> 13:52:04.000000000 +0100
> @@ -537,6 +537,51 @@
> platform_device_register(&ep93xx_i2c_device);
> }
>
> +static struct resource ep93xx_ide_resources[] = {
> + [0] = {
Your code uses 4-space indentation instead of single-tab -- this style is
not acceptable. I'd advise to run the patch thru scripts/checkpatch.pl to find
and fix the coding style mistakes.
> + .start = EP93XX_IDE_PHYS_BASE,
> + .end = EP93XX_IDE_PHYS_BASE + 0x38 - 1,
Why not just 0x37?
Again, your indentation is strange.
> diff -urN linux-2.6.29.orig/drivers/ide/ep93xx-ide-pio.c
> linux-2.6.29/drivers/ide/ep93xx-ide-pio.c
> --- linux-2.6.29.orig/drivers/ide/ep93xx-ide-pio.c 1970-01-01
> 01:00:00.000000000 +0100
> +++ linux-2.6.29/drivers/ide/ep93xx-ide-pio.c 2009-03-30
Please post this part separately to the linux-ide mail list.
I'd suggest to drop that -pio postfix. Many IDE drivers are PIO only,
there's nothing specifal about it. Moreover, it seems like the hardware
supports DMA too, so the driver might be extended to support that too...
> @@ -0,0 +1,666 @@
> +/*
> + * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
> + * PIO IDE host driver.
> + *
> + * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
> + * 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.
> + */
> +
> +
> +#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 <asm/io.h>
> +
> +#include <linux/ide.h>
> +
> +#define MODULE_NAME "ep93xx-ide-pio"
> +
> +/*
> + * 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.
> + */
> +
> +/* IDE Control Register base offset */
What do you mean by "base offset"? I'd suggest to drop this or replace
with mere "offset"...
> +/* Macro for acessing registers using base address and specified offsets */
> +#define IDE_REGISTER(_offset) ((void __iomem *)(base +
> OFFSET_##_offset))
Your patch is line-wrapped.
> +/*
> + * IDE Control Register bit fields
> + */
> +#define IDECTRL_CS0N 0x00000001
> +#define IDECTRL_CS1N 0x00000002
> +#define IDECTRL_DA 0x0000001C
> +#define IDECTRL_DIORN 0x00000020
> +#define IDECTRL_DIOWN 0x00000040
Manually generated PIO cycles? Oh horror... B-)
> +/*
> + * IDE Interface register map default state
> + * (shutdown)
> + */
> +static void
> +ep93xx_ide_pio_clean_regs(unsigned long base)
> +{
> + /* disable IDE interface initially, ensuring that no device is
> selected */
> + writel((IDECTRL_CS0N | IDECTRL_CS1N), IDE_REGISTER(IDECTRL));
> +
> + /* clear IDE registers */
> + writel(0, IDE_REGISTER(IDECFG));
Writing 0 to this register generates an invalid cycle on IDE bus if I
don't mistake (CS0/1 and DIOR/W all asserted), are you sure you want this?
> +/*
> + * EP93xx IDE PIO low-level hardware initialization routine
> + */
> +static void
> +ep93xx_ide_pio_init_hwif(ide_hwif_t *hwif)
> +{
> + unsigned long base = hwif->config_data;
> +
> + /* enforce reset state */
> + ep93xx_ide_pio_clean_regs(base);
> +
> + /* set gpio port E, G and H for IDE */
> + ep93xx_ide_gpio_on_ide();
> +
> + /*
> + * configure IDE interface:
> + * - IDE Master Enable
> + * - Polled IO Operation
> + * - PIO Mode 4 (16.67 MBps)
> + * - 1 Wait State (10 ns)
> + */
> + writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
> + ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
> +}
> +
> +/*
> + * EP93xx IDE PIO low-level byte-read procedure.
> + */
> +static u8
> +ep93xx_ide_pio_readb(unsigned long base, unsigned long addr)
> +{
> + u32 reg;
> +
> + /*
> + * initial state: DIORn=1, DIOWn=1
> + * write address out
> + * select CS configuration
> + */
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIORn low
> + */
> + reg &= ~IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIORn high
> + */
> + reg |= IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * read IDE Data Input Register
> + */
> + return (readl(IDE_REGISTER(IDEDATAIN)) & 0xFF);
Masking is pointless, it's achieved by truncation to 'u8'.
> +}
I don't see what warrants minimum active/recovery time here. And I don't
see how you're supporitng PIO modes 3/4 as you're not checking -IORDY.
> +
> +/*
> + * EP93xx IDE PIO low-level byte-write procedure.
> + */
> +static void
> +ep93xx_ide_pio_writeb(unsigned long base, u8 value, unsigned long addr)
> +{
> + u32 reg;
> +
> + /*
> + * initial state: DIORn=1, DIOWn=1
> + * write address out
> + * select CS configuration
> + */
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * write IDE Data Output Register
> + */
> + writel(value & 0xFF, IDE_REGISTER(IDEDATAOUT));
Masking with 0xFF is pointless.
> +
> + /*
> + * bring DIOWn low
> + */
> + reg &= ~IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIOWn high
> + */
> + reg |= IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
Again, I don't see what warrants minimum active/recovery time and checks
-IORDY...
> +}
> +
> +/*
> + * EP93xx IDE PIO low-level word-read procedure.
> + */
> +static u16
> +ep93xx_ide_pio_readw(unsigned long base, unsigned long addr)
> +{
> + u32 reg;
> +
> + /*
> + * initial state: DIORn=1, DIOWn=1
> + * write address out
> + * select CS configuration
> + */
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIORn low
> + */
> + reg &= ~IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIORn high
> + */
> + reg |= IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * read IDE Data Input Register
> + */
> + return (readl(IDE_REGISTER(IDEDATAIN)) & 0xFFFF);
Masking is pointless. Nothing warrants minimum active/recovery time and
checks -IORDY.
> +}
> +
> +/*
> + * EP93xx IDE PIO low-level word-write procedure.
> + */
> +static void
> +ep93xx_ide_pio_writew(unsigned long base, u8 value, unsigned long addr)
'value' must be 'u16'
> +{
> + u32 reg;
> +
> + /*
> + * initial state: DIORn=1, DIOWn=1
> + * write address out
> + * select CS configuration
> + */
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * write IDE Data Output Register
> + */
> + writel(value & 0xFFFF, IDE_REGISTER(IDEDATAOUT));
Masking with 0xFFFF is pointless.
> +
> + /*
> + * bring DIOWn low
> + */
> + reg &= ~IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +
> + /*
> + * bring DIOWn high
> + */
> + reg |= IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> +}
Nothing warrants minimum active/recovery time and checks -IORDY...
> +/*
> + * EP93xx IDE PIO low-level word buffer-read procedure
> + */
> +static void
> +ep93xx_ide_pio_readsw(unsigned long base, unsigned long addr, void *buf,
> + unsigned int len)
> +{
> + u16 *data = (u16 *)buf;
> +
> + for (;len;len--)
Put spaces after ; please.
> + *data++ = ep93xx_ide_pio_readw(base, addr);
Is this little-endian only code? If not, it's not valid in the big endian
mode...
> +
Empty line not needed here...
> +}
> +
> +/*
> + * EP93xx IDE PIO low-level word buffer-write procedure
> + */
> +static void
> +ep93xx_ide_pio_writesw(unsigned long base, unsigned long addr, void *buf,
> + unsigned int len)
> +{
> + u16 *data = (u16 *)buf;
> +
> + for (;len;len--)
Add spaces after ; please.
[...]
> +static struct ide_port_info ep93xx_ide_pio_port_info = {
> + .name = MODULE_NAME,
> + .init_hwif = ep93xx_ide_pio_init_hwif,
> + .tp_ops = &ep93xx_ide_pio_tp_ops,
> + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
> + IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
> + .pio_mask = ATA_PIO4,
I'm not seeing the proper support for any of the PIO modes in your driver
as yet...
> +static int __devinit ep93xx_ide_pio_probe(struct platform_device *pdev)
> +{
> + int retval;
> +
> + void __iomem *ide_base;
> +
> + struct resource *mem_res;
> + struct resource *irq_res;
> +
> + hw_regs_t hw;
> + hw_regs_t *hws[] = {&hw, NULL, NULL, NULL};
> +
> + struct ide_host *host;
> +
> + /*
> + * collect resources from platform_device structure
> + */
> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem_res) {
> + dev_err(&pdev->dev, "could not retrieve device memory
> resources!\n");
> + retval = -ENXIO;
> + goto fail_return;
> + }
> +
> + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
You could also use platform_get_irq()...
> + if (!irq_res) {
> + dev_err(&pdev->dev, "could not retrieve device IRQ resource!\n");
> + retval = -ENXIO;
> + goto fail_return;
> + }
> +
> + /* request IDE controller registers memory area */
> + if (!request_mem_region(mem_res->start, mem_res->end -
> mem_res->start + 1,
> + MODULE_NAME)) {
> + dev_err(&pdev->dev, "could not request memory resources!\n");
> + retval = -EBUSY;
> + goto fail_return;
Pointless goto/label, you could just instead use:
return retval;
> + }
> +
> + /* map physical memory to virtual memory */
> + ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
> + if (!ide_base) {
> + dev_err(&pdev->dev, "could not map memory resources!\n");
> + retval = -ENOMEM;
> + goto fail_release_mem;
> + }
> +
> + memset(&hw, 0, sizeof(hw));
> +
> + /*
> + * fill in ide_io_ports structure.
> + * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
> + * hence the lowest 3 bits will give us the real address (ranging from
> + * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
> + * values:
> + * CS1n CS0n A2 A1 A0
> + * 1 0 x x x -> IO_ADDR (base 0x10)
> + * 0 1 x x x -> CTL_ADDR (base 0x0E)
> + */
> + ide_std_init_ports(&hw, 0x10, 0x0E);
> +
> + hw.irq = irq_res->start;
> + hw.chipset = ide_generic;
> + hw.dev = &pdev->dev;
> + hw.config = (unsigned long)ide_base;
> +
> + retval = ide_host_add(&ep93xx_ide_pio_port_info, hws, &host);
> + if ( retval ) {
Put no spaces after ( and before ), please.
> +static struct platform_driver ep93xx_ide_pio_driver = {
> + .probe = ep93xx_ide_pio_probe,
> + .remove = __exit_p(ep93xx_ide_pio_remove),
> + .driver = {
> + .name = MODULE_NAME,
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +
> +
Too many empty lines, perhaps?
> +static int __init ep93xx_ide_pio_init(void)
> +{
> + return platform_driver_register(&ep93xx_ide_pio_driver);
> +}
> +module_init(ep93xx_ide_pio_init);
> +
> +static void __exit ep93xx_ide_pio_exit(void)
> +{
> + platform_driver_unregister(&ep93xx_ide_pio_driver);
> +
Empty line not needed here.
> +}
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
[not found] ` <49D12669.4030207@bluewatersys.com>
@ 2009-03-31 10:36 ` Sergei Shtylyov
0 siblings, 0 replies; 59+ messages in thread
From: Sergei Shtylyov @ 2009-03-31 10:36 UTC (permalink / raw)
To: Ryan Mallon
Cc: João Ramos, H Hartley Sweeten, linux-arm-kernel, linux-ide
Hello.
Ryan Mallon wrote:
>> +static void
>> +ep93xx_ide_pio_set_irq(ide_hwif_t *hwif, int on)
>> +{
>> + u8 ctl = ATA_DEVCTL_OBS;
>> +
>> + if (on == 4) { /* hack for SRST */
>> + ctl |= 4;
>> + on &= ~4;
>> + }
>> +
>> + ctl |= on ? 0 : 2;
>>
>
> This function all seems a bit magic/hackish. Can you replace the
> numbers here with #defines to make it clear what is being done.
> You may also want to expand on the comment here to explain why the
> 'hack' is needed.
>
It's standard implementation of this method, and looks the same in
all the other IDE drivers that redefine it. It's going to be gone in
2.6.30, so it's not worth wasting time...
>> +
>> + ep93xx_ide_pio_writeb(hwif->config_data, ctl,
>> hwif->io_ports.ctl_addr);
>> +}
>> +
>> +static void
>> +ep93xx_ide_pio_tf_load(ide_drive_t *drive, ide_task_t *task)
>> +{
>> + ide_hwif_t *hwif = drive->hwif;
>> + struct ide_io_ports *io_ports = &hwif->io_ports;
>> + struct ide_taskfile *tf = &task->tf;
>> + u8 HIHI = (task->tf_flags & IDE_TFLAG_LBA48) ? 0xE0 : 0xEF;
>>
>
> Don't give local variables names with all capitals. What is hihi anyway?
>
He must've copied that from the other drivers, so that question is to
the IDE maintainever rather. :-)
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-03-30 15:34 ` EP93xx PIO IDE driver proposal Sergei Shtylyov
@ 2009-05-04 11:24 ` João Ramos
2009-05-05 12:04 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-04 11:24 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
Hi,
I'm sorry I've been busy all this time and I hadn't got the opportunity
(untill now) to check this.
But now I am committed full-time on this.
Sergei Shtylyov escreveu:
> João Ramos wrote:
>> Here is the revised patch according to your comments.
>
>> Most significant changes are:
> [...]
>> - Removed useless blank lines and white-spaces (Ryan);
>
> Not all of them yet. :-)
I've seen your comments and I fixed those blank lines.
Sorry, old programming habits ;-) .
>
>
>> Regards,
>> João
>>
>> Patch follows.
>>
>> diff -urN linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c
>> linux-2.6.29/arch/arm/mach-ep93xx/core.c
>> --- linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c 2009-03-23
>> 23:12:14.000000000 +0000
>> +++ linux-2.6.29/arch/arm/mach-ep93xx/core.c 2009-03-30
>> 13:52:04.000000000 +0100
>> @@ -537,6 +537,51 @@
>> platform_device_register(&ep93xx_i2c_device);
>> }
>>
>> +static struct resource ep93xx_ide_resources[] = {
>> + [0] = {
>
> Your code uses 4-space indentation instead of single-tab -- this
> style is not acceptable. I'd advise to run the patch thru
> scripts/checkpatch.pl to find and fix the coding style mistakes.
The problem is mainly my mailer settings: it deforms the patch output.
Perhaps next time I should send the patch as an attachment?
Either way, i've run the patch through both Lindent.pl and checkpatch.pl
and fixed some identation issues.
[...]
>
>> +/*
>> + * IDE Interface register map default state
>> + * (shutdown)
>> + */
>> +static void
>> +ep93xx_ide_pio_clean_regs(unsigned long base)
>> +{
>> + /* disable IDE interface initially, ensuring that no device is
>> selected */
>> + writel((IDECTRL_CS0N | IDECTRL_CS1N), IDE_REGISTER(IDECTRL));
>> +
>> + /* clear IDE registers */
>> + writel(0, IDE_REGISTER(IDECFG));
>
> Writing 0 to this register generates an invalid cycle on IDE bus if
> I don't mistake (CS0/1 and DIOR/W all asserted), are you sure you want
> this?
Zero (0) is the default value for the IDE Configuration Register, as
stated in EP93xx User's Guide (Section 27, page 11).
Besides, CS0/1 and DIOR/W lines are controlled through IDE Control Register.
In that case, perhaps it would be best to have it also set to it's
default value, deasserting DIOR/W as well:
writel((IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN | IDECTRL_DIOWN),
IDE_REGISTER(IDECTRL));
>
>> +/*
>> + * EP93xx IDE PIO low-level hardware initialization routine
>> + */
>> +static void
>> +ep93xx_ide_pio_init_hwif(ide_hwif_t *hwif)
>> +{
>> + unsigned long base = hwif->config_data;
>> +
>> + /* enforce reset state */
>> + ep93xx_ide_pio_clean_regs(base);
>> +
>> + /* set gpio port E, G and H for IDE */
>> + ep93xx_ide_gpio_on_ide();
>> +
>> + /*
>> + * configure IDE interface:
>> + * - IDE Master Enable
>> + * - Polled IO Operation
>> + * - PIO Mode 4 (16.67 MBps)
>> + * - 1 Wait State (10 ns)
>> + */
>> + writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
>> + ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
>> +}
>> +
>> +/*
>> + * EP93xx IDE PIO low-level byte-read procedure.
>> + */
>> +static u8
>> +ep93xx_ide_pio_readb(unsigned long base, unsigned long addr)
>> +{
>> + u32 reg;
>> +
>> + /*
>> + * initial state: DIORn=1, DIOWn=1
>> + * write address out
>> + * select CS configuration
>> + */
>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>> + IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> +
>> + /*
>> + * bring DIORn low
>> + */
>> + reg &= ~IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> +
>> + /*
>> + * bring DIORn high
>> + */
>> + reg |= IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> +
>> + /*
>> + * read IDE Data Input Register
>> + */
>> + return (readl(IDE_REGISTER(IDEDATAIN)) & 0xFF);
>
> Masking is pointless, it's achieved by truncation to 'u8'.
>
>> +}
>
> I don't see what warrants minimum active/recovery time here. And I
> don't see how you're supporitng PIO modes 3/4 as you're not checking
> -IORDY.
So, you're saying I should check for the IORDY value at the beginning
and end of each transfer?
What exactly do you mean by 'warrant minimum active/recovery time'?
Should I insert manual delays to ensure timing issues?
[...]
>> +/*
>> + * EP93xx IDE PIO low-level word buffer-read procedure
>> + */
>> +static void
>> +ep93xx_ide_pio_readsw(unsigned long base, unsigned long addr, void
>> *buf,
>> + unsigned int len)
>> +{
>> + u16 *data = (u16 *)buf;
>> +
>> + for (;len;len--)
>
> Put spaces after ; please.
>
>> + *data++ = ep93xx_ide_pio_readw(base, addr);
>
> Is this little-endian only code? If not, it's not valid in the big
> endian mode...
Should I instead use array-indexing code such as:
for (; len; len--, i++)
data[i] = ep93xx_ide_pio_readw(base, addr);
And the same for 'ep93xx_ide_pio_writesw()' function?
[...]
>
>> +static struct ide_port_info ep93xx_ide_pio_port_info = {
>> + .name = MODULE_NAME,
>> + .init_hwif = ep93xx_ide_pio_init_hwif,
>> + .tp_ops = &ep93xx_ide_pio_tp_ops,
>> + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA |
>> IDE_HFLAG_MMIO |
>> + IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
>> + .pio_mask = ATA_PIO4,
>
> I'm not seeing the proper support for any of the PIO modes in your
> driver as yet...
If you mind to explain to me how to, I would appreciate...
>
>> +static int __devinit ep93xx_ide_pio_probe(struct platform_device *pdev)
>> +{
>> + int retval;
>> +
>> + void __iomem *ide_base;
>> +
>> + struct resource *mem_res;
>> + struct resource *irq_res;
>> +
>> + hw_regs_t hw;
>> + hw_regs_t *hws[] = {&hw, NULL, NULL, NULL};
>> +
>> + struct ide_host *host;
>> +
>> + /*
>> + * collect resources from platform_device structure
>> + */
>> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!mem_res) {
>> + dev_err(&pdev->dev, "could not retrieve device memory
>> resources!\n");
>> + retval = -ENXIO;
>> + goto fail_return;
>> + }
>> +
>> + irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>
> You could also use platform_get_irq()...
Fixed.
>
>> + if (!irq_res) {
>> + dev_err(&pdev->dev, "could not retrieve device IRQ
>> resource!\n");
>> + retval = -ENXIO;
>> + goto fail_return;
>> + }
>> + + /* request IDE controller registers memory area */
>> + if (!request_mem_region(mem_res->start, mem_res->end -
>> mem_res->start + 1,
>> + MODULE_NAME)) {
>> + dev_err(&pdev->dev, "could not request memory resources!\n");
>> + retval = -EBUSY;
>> + goto fail_return;
>
> Pointless goto/label, you could just instead use:
>
> return retval;
Fixed.
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-04 11:24 ` João Ramos
@ 2009-05-05 12:04 ` Sergei Shtylyov
2009-05-06 14:17 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-05 12:04 UTC (permalink / raw)
To: João Ramos
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
Hello.
João Ramos wrote:
>>> Patch follows.
>>>
>>> diff -urN linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c
>>> linux-2.6.29/arch/arm/mach-ep93xx/core.c
>>> --- linux-2.6.29.orig/arch/arm/mach-ep93xx/core.c 2009-03-23
>>> 23:12:14.000000000 +0000
>>> +++ linux-2.6.29/arch/arm/mach-ep93xx/core.c 2009-03-30
>>> 13:52:04.000000000 +0100
>>> @@ -537,6 +537,51 @@
>>> platform_device_register(&ep93xx_i2c_device);
>>> }
>>>
>>> +static struct resource ep93xx_ide_resources[] = {
>>> + [0] = {
>> Your code uses 4-space indentation instead of single-tab -- this
>> style is not acceptable. I'd advise to run the patch thru
>> scripts/checkpatch.pl to find and fix the coding style mistakes.
> The problem is mainly my mailer settings: it deforms the patch output.
> Perhaps next time I should send the patch as an attachment?
Well, if the IDE maintainer doesn't mind, I don't either. Just make sure
the attachment will have the MIME type of text/plain.
>>> +/*
>>> + * IDE Interface register map default state
>>> + * (shutdown)
>>> + */
>>> +static void
>>> +ep93xx_ide_pio_clean_regs(unsigned long base)
>>> +{
>>> + /* disable IDE interface initially, ensuring that no device is
>>> selected */
>>> + writel((IDECTRL_CS0N | IDECTRL_CS1N), IDE_REGISTER(IDECTRL));
>>> +
>>> + /* clear IDE registers */
>>> + writel(0, IDE_REGISTER(IDECFG));
>>
>>
>> Writing 0 to this register generates an invalid cycle on IDE bus if
>> I don't mistake (CS0/1 and DIOR/W all asserted), are you sure you want
>> this?
> Zero (0) is the default value for the IDE Configuration Register, as
> stated in EP93xx User's Guide (Section 27, page 11).
> Besides, CS0/1 and DIOR/W lines are controlled through IDE Control
> Register.
Sorry, I seem to have mixed them up...
> In that case, perhaps it would be best to have it also set to it's
> default value, deasserting DIOR/W as well:
> writel((IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN | IDECTRL_DIOWN),
> IDE_REGISTER(IDECTRL));
Yes, that's what I had in mind...
>>> +/*
>>> + * EP93xx IDE PIO low-level hardware initialization routine
>>> + */
>>> +static void
>>> +ep93xx_ide_pio_init_hwif(ide_hwif_t *hwif)
>>> +{
>>> + unsigned long base = hwif->config_data;
>>> +
>>> + /* enforce reset state */
>>> + ep93xx_ide_pio_clean_regs(base);
>>> +
>>> + /* set gpio port E, G and H for IDE */
>>> + ep93xx_ide_gpio_on_ide();
>>> +
>>> + /*
>>> + * configure IDE interface:
>>> + * - IDE Master Enable
>>> + * - Polled IO Operation
>>> + * - PIO Mode 4 (16.67 MBps)
>>> + * - 1 Wait State (10 ns)
>>> + */
>>> + writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
>>> + ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
>>> +}
>>> +
>>> +/*
>>> + * EP93xx IDE PIO low-level byte-read procedure.
>>> + */
>>> +static u8
>>> +ep93xx_ide_pio_readb(unsigned long base, unsigned long addr)
>>> +{
>>> + u32 reg;
>>> +
>>> + /*
>>> + * initial state: DIORn=1, DIOWn=1
>>> + * write address out
>>> + * select CS configuration
>>> + */
>>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>>> + IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> +
>>> + /*
>>> + * bring DIORn low
>>> + */
>>> + reg &= ~IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> +
>>> + /*
>>> + * bring DIORn high
>>> + */
>>> + reg |= IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> +
>>> + /*
>>> + * read IDE Data Input Register
>>> + */
>>> + return (readl(IDE_REGISTER(IDEDATAIN)) & 0xFF);
>> Masking is pointless, it's achieved by truncation to 'u8'.
>>> +}
>> I don't see what warrants minimum active/recovery time here. And I
>> don't see how you're supporitng PIO modes 3/4 as you're not checking
>> -IORDY.
> So, you're saying I should check for the IORDY value at the beginning
> and end of each transfer?
Only before deasserting -DIOx.
> What exactly do you mean by 'warrant minimum active/recovery time'?
> Should I insert manual delays to ensure timing issues?
Yes, since Cirrus seems to have left you with no option other than
bit-banging to emulate I/O cycles, you'll have to use delays...
> [...]
>>> +/*
>>> + * EP93xx IDE PIO low-level word buffer-read procedure
>>> + */
>>> +static void
>>> +ep93xx_ide_pio_readsw(unsigned long base, unsigned long addr, void
>>> *buf,
>>> + unsigned int len)
>>> +{
>>> + u16 *data = (u16 *)buf;
>>> +
>>> + for (;len;len--)
>> Put spaces after ; please.
>>> + *data++ = ep93xx_ide_pio_readw(base, addr);
>> Is this little-endian only code? If not, it's not valid in the big
>> endian mode...
> Should I instead use array-indexing code such as:
> for (; len; len--, i++)
> data[i] = ep93xx_ide_pio_readw(base, addr);
> And the same for 'ep93xx_ide_pio_writesw()' function?
No, it won't change anything. You should use cpu_to_le16() -- unless
somehow ep93xx_ide_pio_readw() always returns little-endian data...
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-05 12:04 ` Sergei Shtylyov
@ 2009-05-06 14:17 ` João Ramos
2009-05-06 17:05 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-06 14:17 UTC (permalink / raw)
To: Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon
Cc: linux-arm-kernel, linux-ide
[-- Attachment #1: Type: text/plain, Size: 1600 bytes --]
Hi.
So here is the revised patch, according to yours comments, to add
support for the IDE host controller in the Cirrus Logic's EP93xx CPUs.
This patch is made against kernel 2.6.30-rc4 (latest release candidate
in Linus's tree).
I've preferred to attach the patch instead of inlining it in this mail,
as my mailer seems to deform the patch output.
Please confirm if the patch is ok.
Sergei: I've added the delays you suggested in the read/write
procedures, accordingly to the delays specified in the processor's user
manual for PIO Mode 4.
These delays really introduce quite a performance loss. Although the
driver is working, these delays make file transfers quite slow; do we
really need to enforce manually these delays?
I mean, on a 200MHz CPU (5ns instruction cycle), can't we assume that
instructions and branches that occur between C code instructions will
suffice to some of these delays (the delays are 25ns + 70ns + 25ns), or
yet, can't we reduce some of these delays assuming some instruction
cycle time is already spent on branches and instructions between reads
and writes to the IDE control registers?
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: linux-2.6.30-rc4-ep93xx-ide.patch --]
[-- Type: text/plain, Size: 19685 bytes --]
Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
This driver currently supports only PIO-4 transfer mode.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
---
diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/core.c linux-2.6.30-rc4/arch/arm/mach-ep93xx/core.c
--- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/core.c 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/core.c 2009-05-06 13:57:56.000000000 +0100
@@ -537,6 +537,49 @@
platform_device_register(&ep93xx_i2c_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);
+}
+
+void ep93xx_ide_on_gpio(int enable)
+{
+ u32 reg;
+
+ reg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
+ if (enable) {
+ reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
+ EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
+ EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
+ } else {
+ reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
+ EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
+ EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
+ }
+ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+ __raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
+}
+
extern void ep93xx_gpio_init(void);
void __init ep93xx_init_devices(void)
@@ -553,6 +596,8 @@
ep93xx_gpio_init();
+ ep93xx_ide_on_gpio(0);
+
amba_device_register(&uart1_device, &iomem_resource);
amba_device_register(&uart2_device, &iomem_resource);
amba_device_register(&uart3_device, &iomem_resource);
diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
--- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-06 13:57:56.000000000 +0100
@@ -78,6 +78,7 @@
#define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000)
#define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000)
+#define EP93XX_IDE_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x000a0000)
#define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000)
@@ -159,6 +160,9 @@
#define EP93XX_SYSCON_CLOCK_SET1 EP93XX_SYSCON_REG(0x20)
#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24)
#define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80)
+#define EP93XX_SYSCON_DEVICE_CONFIG_EONIDE 0x00000100
+#define EP93XX_SYSCON_DEVICE_CONFIG_GONIDE 0x00000400
+#define EP93XX_SYSCON_DEVICE_CONFIG_HONIDE 0x00000800
#define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE 0x00800000
#define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0)
diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/platform.h linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/platform.h
--- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/platform.h 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-06 13:57:56.000000000 +0100
@@ -17,6 +17,8 @@
void ep93xx_init_time(unsigned long);
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_ide(void);
+void ep93xx_ide_on_gpio(int enable);
void ep93xx_init_devices(void);
extern struct sys_timer ep93xx_timer;
diff -urN linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c
--- linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c 2009-05-06 14:48:17.000000000 +0100
@@ -0,0 +1,530 @@
+/*
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * IDE host controller driver.
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ * 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.
+ */
+
+#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/ide.h>
+#include <linux/delay.h>
+
+#define MODULE_NAME "ep93xx-ide"
+
+/*
+ * 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.
+ */
+
+/*
+ * IDE Register offset
+ */
+#define OFFSET_IDECTRL 0x00
+#define OFFSET_IDECFG 0x04
+#define OFFSET_IDEMDMAOP 0x08
+#define OFFSET_IDEUDMAOP 0x0C
+#define OFFSET_IDEDATAOUT 0x10
+#define OFFSET_IDEDATAIN 0x14
+#define OFFSET_IDEMDMADATAOUT 0x18
+#define OFFSET_IDEMDMADATAIN 0x1C
+#define OFFSET_IDEUDMADATAOUT 0x20
+#define OFFSET_IDEUDMADATAIN 0x24
+#define OFFSET_IDEUDMASTS 0x28
+#define OFFSET_IDEUDMADEBUG 0x2C
+#define OFFSET_IDEUDMAWRBUFSTS 0x30
+#define OFFSET_IDEUDMARDBUFSTS 0x34
+
+/*
+ * Macro for acessing registers using base address
+ * and specified offsets
+ */
+#define IDE_REGISTER(_offset) \
+ ((void __iomem *)(base + OFFSET_##_offset))
+
+/* Macro for checking -IORDY line state */
+#define ep93xx_ide_check_iordy() ({ \
+ u32 _reg = readl(IDE_REGISTER(IDECTRL)); \
+ (_reg & IDECTRL_IORDY) ? 1 : 0; \
+})
+
+/*
+ * IDE Control Register bit fields
+ */
+#define IDECTRL_CS0N 0x00000001
+#define IDECTRL_CS1N 0x00000002
+#define IDECTRL_DA 0x0000001C
+#define IDECTRL_DIORN 0x00000020
+#define IDECTRL_DIOWN 0x00000040
+#define IDECTRL_DASPN 0x00000080
+#define IDECTRL_DMARQ 0x00000100
+#define IDECTRL_INTRQ 0x00000200
+#define IDECTRL_IORDY 0x00000400
+
+/*
+ * IDE Configuration Register bit fields
+ */
+#define IDECFG_IDEEN 0x00000001
+#define IDECFG_PIO 0x00000002
+#define IDECFG_MDMA 0x00000004
+#define IDECFG_UDMA 0x00000008
+#define IDECFG_PIO_MODE_0 0x00000000
+#define IDECFG_PIO_MODE_1 0x00000010
+#define IDECFG_PIO_MODE_2 0x00000020
+#define IDECFG_PIO_MODE_3 0x00000030
+#define IDECFG_PIO_MODE_4 0x00000040
+#define IDECFG_MDMA_MODE_0 0x00000000
+#define IDECFG_MDMA_MODE_1 0x00000010
+#define IDECFG_MDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_0 0x00000000
+#define IDECFG_UDMA_MODE_1 0x00000010
+#define IDECFG_UDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_3 0x00000030
+#define IDECFG_UDMA_MODE_4 0x00000040
+#define IDECFG_WST 0x00000300
+
+/*
+ * IDE Interface register map default state
+ * (shutdown)
+ */
+static void ep93xx_ide_clean_regs(unsigned long base)
+{
+ /* disable IDE interface initially */
+ writel((IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
+ IDECTRL_DIOWN), IDE_REGISTER(IDECTRL));
+
+ /* clear IDE registers */
+ writel(0, IDE_REGISTER(IDECFG));
+ writel(0, IDE_REGISTER(IDEMDMAOP));
+ writel(0, IDE_REGISTER(IDEUDMAOP));
+ writel(0, IDE_REGISTER(IDEDATAOUT));
+ writel(0, IDE_REGISTER(IDEDATAIN));
+ writel(0, IDE_REGISTER(IDEMDMADATAOUT));
+ writel(0, IDE_REGISTER(IDEMDMADATAIN));
+ writel(0, IDE_REGISTER(IDEUDMADATAOUT));
+ writel(0, IDE_REGISTER(IDEUDMADATAIN));
+ writel(0, IDE_REGISTER(IDEUDMADEBUG));
+}
+
+/*
+ * EP93xx IDE PIO low-level hardware initialization routine
+ */
+static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
+{
+ unsigned long base = hwif->config_data;
+
+ /* enforce reset state */
+ ep93xx_ide_clean_regs(base);
+
+ /* set gpio port E, G and H for IDE */
+ ep93xx_ide_on_gpio(1);
+
+ /*
+ * configure IDE interface:
+ * - IDE Master Enable
+ * - Polled IO Operation
+ * - PIO Mode 4 (16.67 MBps)
+ * - 1 Wait State (10 ns)
+ */
+ writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
+ ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
+}
+
+static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ reg &= ~IDECTRL_DIORN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(70);
+
+ while (!ep93xx_ide_check_iordy())
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ return readl(IDE_REGISTER(IDEDATAIN));
+}
+
+static void
+ep93xx_ide_writeb(unsigned long base, u8 value, unsigned long addr)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ writel(value, IDE_REGISTER(IDEDATAOUT));
+
+ reg &= ~IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(70);
+
+ while (!ep93xx_ide_check_iordy())
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+}
+
+static u16 ep93xx_ide_readw(unsigned long base, unsigned long addr)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ reg &= ~IDECTRL_DIORN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(70);
+
+ while (!ep93xx_ide_check_iordy())
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ return readl(IDE_REGISTER(IDEDATAIN));
+}
+
+static void
+ep93xx_ide_writew(unsigned long base, u16 value, unsigned long addr)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+
+ writel(value, IDE_REGISTER(IDEDATAOUT));
+
+ reg &= ~IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(70);
+
+ while (!ep93xx_ide_check_iordy())
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ writel(reg, IDE_REGISTER(IDECTRL));
+ ndelay(25);
+}
+
+static void
+ep93xx_ide_readsw(unsigned long base, unsigned long addr, void *buf,
+ unsigned int len)
+{
+ u16 *data = (u16 *) buf;
+
+ for (; len; len--)
+ *data++ = cpu_to_le16(ep93xx_ide_readw(base, addr));
+}
+
+static void
+ep93xx_ide_writesw(unsigned long base, unsigned long addr, void *buf,
+ unsigned int len)
+{
+ u16 *data = (u16 *) buf;
+
+ for (; len; len--)
+ ep93xx_ide_writew(base, le16_to_cpu(*data++), addr);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
+{
+ ep93xx_ide_writeb(hwif->config_data, cmd,
+ hwif->io_ports.command_addr);
+}
+
+static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
+{
+ return ep93xx_ide_readb(hwif->config_data,
+ hwif->io_ports.status_addr);
+}
+
+static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
+{
+ return ep93xx_ide_readb(hwif->config_data, hwif->io_ports.ctl_addr);
+}
+
+static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+ ep93xx_ide_writeb(hwif->config_data, ctl, hwif->io_ports.ctl_addr);
+}
+
+static void ep93xx_ide_dev_select(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 select = drive->select | ATA_DEVICE_OBS;
+
+ ep93xx_ide_writeb(hwif->config_data, select,
+ hwif->io_ports.device_addr);
+}
+
+static void
+ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+
+ if (valid & IDE_VALID_FEATURE)
+ ep93xx_ide_writeb(hwif->config_data, tf->feature,
+ io_ports->feature_addr);
+ if (valid & IDE_VALID_NSECT)
+ ep93xx_ide_writeb(hwif->config_data, tf->nsect,
+ io_ports->nsect_addr);
+ if (valid & IDE_VALID_LBAL)
+ ep93xx_ide_writeb(hwif->config_data, tf->lbal,
+ io_ports->lbal_addr);
+ if (valid & IDE_VALID_LBAM)
+ ep93xx_ide_writeb(hwif->config_data, tf->lbam,
+ io_ports->lbam_addr);
+ if (valid & IDE_VALID_LBAH)
+ ep93xx_ide_writeb(hwif->config_data, tf->lbah,
+ io_ports->lbah_addr);
+ if (valid & IDE_VALID_DEVICE)
+ ep93xx_ide_writeb(hwif->config_data, tf->device,
+ io_ports->device_addr);
+}
+
+static void
+ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+
+ if (valid & IDE_VALID_ERROR)
+ tf->error =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->feature_addr);
+ if (valid & IDE_VALID_NSECT)
+ tf->nsect =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->nsect_addr);
+ if (valid & IDE_VALID_LBAL)
+ tf->lbal =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->lbal_addr);
+ if (valid & IDE_VALID_LBAM)
+ tf->lbam =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->lbam_addr);
+ if (valid & IDE_VALID_LBAH)
+ tf->lbah =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->lbah_addr);
+ if (valid & IDE_VALID_DEVICE)
+ tf->device =
+ ep93xx_ide_readb(hwif->config_data,
+ io_ports->device_addr);
+}
+
+static void
+ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+
+ ep93xx_ide_readsw(hwif->config_data, io_ports->data_addr, buf,
+ words);
+}
+
+static void
+ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd, void *buf,
+ unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+
+ ep93xx_ide_writesw(hwif->config_data, io_ports->data_addr, buf,
+ words);
+}
+
+static struct ide_tp_ops ep93xx_ide_tp_ops = {
+ .exec_command = ep93xx_ide_exec_command,
+ .read_status = ep93xx_ide_read_status,
+ .read_altstatus = ep93xx_ide_read_altstatus,
+ .write_devctl = ep93xx_ide_write_devctl,
+ .dev_select = ep93xx_ide_dev_select,
+ .tf_load = ep93xx_ide_tf_load,
+ .tf_read = ep93xx_ide_tf_read,
+ .input_data = ep93xx_ide_input_data,
+ .output_data = ep93xx_ide_output_data,
+};
+
+static struct ide_port_info ep93xx_ide_port_info = {
+ .name = MODULE_NAME,
+ .init_hwif = ep93xx_ide_init_hwif,
+ .tp_ops = &ep93xx_ide_tp_ops,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
+ IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
+ .pio_mask = ATA_PIO4,
+};
+
+static int __devinit ep93xx_ide_probe(struct platform_device *pdev)
+{
+ int retval;
+ int irq;
+ void __iomem *ide_base;
+ struct resource *mem_res;
+ hw_regs_t hw;
+ hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_host *host;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ if (!request_mem_region
+ (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
+ dev_err(&pdev->dev, "could not request memory resources!\n");
+ return -EBUSY;
+ }
+
+ ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(&pdev->dev, "could not map memory resources!\n");
+ retval = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ /*
+ * fill in ide_io_ports structure.
+ * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
+ * hence the lowest 3 bits will give us the real address (ranging from
+ * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
+ * values:
+ * CS1n CS0n A2 A1 A0
+ * 1 0 x x x -> IO_ADDR (base 0x10)
+ * 0 1 x x x -> CTL_ADDR (base 0x0E)
+ */
+ ide_std_init_ports(&hw, 0x10, 0x0E);
+
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+ hw.config = (unsigned long)ide_base;
+
+ retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "unable to add EP93xx IDE host controller!\n");
+ goto fail_unmap;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
+
+ return 0;
+
+fail_unmap:
+ iounmap(ide_base);
+fail_release_mem:
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+ return retval;
+}
+
+static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ struct ide_host *host = pdev->dev.driver_data;
+
+ /* IDE interface reset state */
+ ep93xx_ide_clean_regs(host->ports[0]->config_data);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ ep93xx_ide_on_gpio(0);
+
+ ide_host_remove(host);
+
+ iounmap((void __iomem *)(host->ports[0]->config_data));
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_ide_driver = {
+ .probe = ep93xx_ide_probe,
+ .remove = __exit_p(ep93xx_ide_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ep93xx_ide_init(void)
+{
+ return platform_driver_register(&ep93xx_ide_driver);
+}
+module_init(ep93xx_ide_init);
+
+static void __exit ep93xx_ide_exit(void)
+{
+ platform_driver_unregister(&ep93xx_ide_driver);
+}
+module_exit(ep93xx_ide_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joao Ramos <joao.ramos@inov.pt>");
+MODULE_DESCRIPTION("EP93xx IDE host controller driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ep93xx-ide");
diff -urN linux-2.6.30-rc4.orig/drivers/ide/Kconfig linux-2.6.30-rc4/drivers/ide/Kconfig
--- linux-2.6.30-rc4.orig/drivers/ide/Kconfig 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/Kconfig 2009-05-06 13:57:56.000000000 +0100
@@ -732,6 +732,18 @@
depends on ARM && ARCH_AT91 && !ARCH_AT91RM9200 && !ARCH_AT91X40
select IDE_TIMINGS
+config BLK_DEV_IDE_EP93XX
+ tristate "Cirrus Logic EPxx (EP9312, EP9315) IDE support"
+ depends on ARM && ARCH_EP93XX
+ help
+ This is a host controller driver for IDE hardware included in
+ Cirrus Logic's EP9312 and EP9315 CPUs.
+ Say Y here if you want to enable the IDE host controller support
+ for your machine.
+
+ Choose 'M' to compile this driver as a module; the module will be
+ called 'ep93xx-ide'.
+
config BLK_DEV_IDE_ICSIDE
tristate "ICS IDE interface support"
depends on ARM && ARCH_ACORN
diff -urN linux-2.6.30-rc4.orig/drivers/ide/Makefile linux-2.6.30-rc4/drivers/ide/Makefile
--- linux-2.6.30-rc4.orig/drivers/ide/Makefile 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/Makefile 2009-05-06 13:57:56.000000000 +0100
@@ -117,3 +117,4 @@
obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o
obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o
obj-$(CONFIG_BLK_DEV_IDE_AT91) += at91_ide.o
+obj-$(CONFIG_BLK_DEV_IDE_EP93XX) += ep93xx-ide.o
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-06 14:17 ` João Ramos
@ 2009-05-06 17:05 ` Sergei Shtylyov
2009-05-07 9:36 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-06 17:05 UTC (permalink / raw)
To: João Ramos
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
Hello.
João Ramos wrote:
> So here is the revised patch, according to yours comments, to add
> support for the IDE host controller in the Cirrus Logic's EP93xx CPUs.
> This patch is made against kernel 2.6.30-rc4 (latest release candidate
> in Linus's tree).
> I've preferred to attach the patch instead of inlining it in this mail,
> as my mailer seems to deform the patch output.
> Please confirm if the patch is ok.
It looks OK tab-wise now...
> Sergei: I've added the delays you suggested in the read/write
> procedures, accordingly to the delays specified in the processor's user
> manual for PIO Mode 4.
Why only for PIO mode 4 if you're claiming support for modes 0 thru 4?
> These delays really introduce quite a performance loss. Although the
> driver is working, these delays make file transfers quite slow; do we
> really need to enforce manually these delays?
Unfortunately, yes.
> I mean, on a 200MHz CPU (5ns instruction cycle), can't we assume that
> instructions and branches that occur between C code instructions will
> suffice to some of these delays
You need to accurately measure that, not just assume.
> (the delays are 25ns + 70ns + 25ns), or
> yet, can't we reduce some of these delays assuming some instruction
> cycle time is already spent on branches and instructions between reads
> and writes to the IDE control registers?
We can in principle, but that time should be accurately measured.
> Best regards,
> João Ramos
> ------------------------------------------------------------------------
>
> Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
> This driver currently supports only PIO-4 transfer mode.
It claims support for all modes 0 to 4 nevertheless.
> Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> ---
> diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/core.c linux-2.6.30-rc4/arch/arm/mach-ep93xx/core.c
> --- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/core.c 2009-04-30 05:48:16.000000000 +0100
> +++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/core.c 2009-05-06 13:57:56.000000000 +0100
> @@ -537,6 +537,49 @@
[...]
> diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> --- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-04-30 05:48:16.000000000 +0100
> +++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-06 13:57:56.000000000 +0100
> @@ -78,6 +78,7 @@
[...]
> diff -urN linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/platform.h linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/platform.h
> --- linux-2.6.30-rc4.orig/arch/arm/mach-ep93xx/include/mach/platform.h 2009-04-30 05:48:16.000000000 +0100
> +++ linux-2.6.30-rc4/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-06 13:57:56.000000000 +0100
[...]
Please submit the above parts separately to linux-arm-kernel, as the
platform code doesn't belong to the driver.
> diff -urN linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c
> --- linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c 2009-05-06 14:48:17.000000000 +0100
> @@ -0,0 +1,530 @@
> +/*
> + * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
> + * IDE host controller driver.
> + *
> + * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
> + * INESC Inovacao (INOV)
> + *
[...]
> +
> +/* Macro for checking -IORDY line state */
> +#define ep93xx_ide_check_iordy() ({ \
> + u32 _reg = readl(IDE_REGISTER(IDECTRL)); \
> + (_reg & IDECTRL_IORDY) ? 1 : 0; \
> +})
Make this an inline function, please.
> +
> +/*
> + * IDE Control Register bit fields
> + */
> +#define IDECTRL_CS0N 0x00000001
> +#define IDECTRL_CS1N 0x00000002
> +#define IDECTRL_DA 0x0000001C
> +#define IDECTRL_DIORN 0x00000020
> +#define IDECTRL_DIOWN 0x00000040
> +#define IDECTRL_DASPN 0x00000080
> +#define IDECTRL_DMARQ 0x00000100
> +#define IDECTRL_INTRQ 0x00000200
> +#define IDECTRL_IORDY 0x00000400
> +
> +/*
> + * IDE Configuration Register bit fields
> + */
> +#define IDECFG_IDEEN 0x00000001
> +#define IDECFG_PIO 0x00000002
> +#define IDECFG_MDMA 0x00000004
> +#define IDECFG_UDMA 0x00000008
> +#define IDECFG_PIO_MODE_0 0x00000000
> +#define IDECFG_PIO_MODE_1 0x00000010
> +#define IDECFG_PIO_MODE_2 0x00000020
> +#define IDECFG_PIO_MODE_3 0x00000030
> +#define IDECFG_PIO_MODE_4 0x00000040
> +#define IDECFG_MDMA_MODE_0 0x00000000
> +#define IDECFG_MDMA_MODE_1 0x00000010
> +#define IDECFG_MDMA_MODE_2 0x00000020
> +#define IDECFG_UDMA_MODE_0 0x00000000
> +#define IDECFG_UDMA_MODE_1 0x00000010
> +#define IDECFG_UDMA_MODE_2 0x00000020
> +#define IDECFG_UDMA_MODE_3 0x00000030
> +#define IDECFG_UDMA_MODE_4 0x00000040
> +#define IDECFG_WST 0x00000300
> +
> +/*
> + * IDE Interface register map default state
> + * (shutdown)
> + */
> +static void ep93xx_ide_clean_regs(unsigned long base)
> +{
> + /* disable IDE interface initially */
> + writel((IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
> + IDECTRL_DIOWN), IDE_REGISTER(IDECTRL));
> +
> + /* clear IDE registers */
> + writel(0, IDE_REGISTER(IDECFG));
> + writel(0, IDE_REGISTER(IDEMDMAOP));
> + writel(0, IDE_REGISTER(IDEUDMAOP));
> + writel(0, IDE_REGISTER(IDEDATAOUT));
> + writel(0, IDE_REGISTER(IDEDATAIN));
> + writel(0, IDE_REGISTER(IDEMDMADATAOUT));
> + writel(0, IDE_REGISTER(IDEMDMADATAIN));
> + writel(0, IDE_REGISTER(IDEUDMADATAOUT));
> + writel(0, IDE_REGISTER(IDEUDMADATAIN));
> + writel(0, IDE_REGISTER(IDEUDMADEBUG));
> +}
> +
> +/*
> + * EP93xx IDE PIO low-level hardware initialization routine
> + */
> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
> +{
> + unsigned long base = hwif->config_data;
> +
> + /* enforce reset state */
> + ep93xx_ide_clean_regs(base);
> +
> + /* set gpio port E, G and H for IDE */
> + ep93xx_ide_on_gpio(1);
Shouldn't this be done in the platform code instead?
> +
> + /*
> + * configure IDE interface:
> + * - IDE Master Enable
> + * - Polled IO Operation
> + * - PIO Mode 4 (16.67 MBps)
> + * - 1 Wait State (10 ns)
> + */
> + writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
> + ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
> +}
> +
> +static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + reg &= ~IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(70);
> +
> + while (!ep93xx_ide_check_iordy())
> + cpu_relax();
> +
> + reg |= IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + return readl(IDE_REGISTER(IDEDATAIN));
Hey, how this even works (if the data doesn't get latched somehow)?! You
should read the register right *before* the deassertion of -DIORx! The
minimum data hold time is only 5 ns and the data lines will be tristated
within 30 ns maximum...
[...]
> +static u16 ep93xx_ide_readw(unsigned long base, unsigned long addr)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + reg &= ~IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(70);
> +
> + while (!ep93xx_ide_check_iordy())
> + cpu_relax();
> +
> + reg |= IDECTRL_DIORN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + return readl(IDE_REGISTER(IDEDATAIN));
> +}
I don't see any difference between ep93xx_ide_read[bw](), so why don't
you use a single function?
> +static void
> +ep93xx_ide_writeb(unsigned long base, u8 value, unsigned long addr)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + writel(value, IDE_REGISTER(IDEDATAOUT));
Hum, do you know at which moments this controller starts/stops driving
data lines on the IDE bus? After DIOWx- assertion/deassertion?
> +
> + reg &= ~IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(70);
> +
> + while (!ep93xx_ide_check_iordy())
> + cpu_relax();
> +
> + reg |= IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +}
> +
[...]
> +
> +static void
> +ep93xx_ide_writew(unsigned long base, u16 value, unsigned long addr)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +
> + writel(value, IDE_REGISTER(IDEDATAOUT));
> +
> + reg &= ~IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(70);
> +
> + while (!ep93xx_ide_check_iordy())
> + cpu_relax();
> +
> + reg |= IDECTRL_DIOWN;
> + writel(reg, IDE_REGISTER(IDECTRL));
> + ndelay(25);
> +}
The same question: why we need both ep93xx_ide_write[bw]()?
> +
> +static void
> +ep93xx_ide_readsw(unsigned long base, unsigned long addr, void *buf,
> + unsigned int len)
> +{
> + u16 *data = (u16 *) buf;
> +
> + for (; len; len--)
IMHO, while (len--) fits better...
> + *data++ = cpu_to_le16(ep93xx_ide_readw(base, addr));
> +}
> +
> +
> +/*
> + * EP93xx IDE PIO Transport Operations
> + */
> +
> +static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
> +{
> + ep93xx_ide_writeb(hwif->config_data, cmd,
> + hwif->io_ports.command_addr);
It's preferrable if you do not insert unnecessary spaces in the
multiline statements:
ep93xx_ide_writeb(hwif->config_data, cmd,
hwif->io_ports.command_addr);
This also looks prettier. :-)
> +static void
> +ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + struct ide_io_ports *io_ports = &hwif->io_ports;
> +
> + if (valid & IDE_VALID_ERROR)
> + tf->error =
> + ep93xx_ide_readb(hwif->config_data,
> + io_ports->feature_addr);
Again, tabs are strongly preferred over spaces (and carrying the
statement over to the next line where it's not necessary):
tf->error = ep93xx_ide_readb(hwif->config_data,
io_ports->feature_addr);
> +static int __devinit ep93xx_ide_probe(struct platform_device *pdev)
> +{
> + int retval;
> + int irq;
Stray tab?
[...]
> + retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
> + if (retval) {
> + dev_err(&pdev->dev,
> + "unable to add EP93xx IDE host controller!\n");
s/add/register/, s/host controller/driver/
> + goto fail_unmap;
> + }
> +
> + platform_set_drvdata(pdev, host);
> +
> + dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
> +
> + return 0;
> +
> +fail_unmap:
> + iounmap(ide_base);
> +fail_release_mem:
> + release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
> + return retval;
> +}
> +
> +static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
> +{
> + struct resource *mem_res;
> + struct ide_host *host = pdev->dev.driver_data;
> +
> + /* IDE interface reset state */
Punctuation missing...
> + ep93xx_ide_clean_regs(host->ports[0]->config_data);
> +
> + /* restore back GPIO port E, G and H for GPIO use */
> + ep93xx_ide_on_gpio(0);
> +
> + ide_host_remove(host);
> +
> + iounmap((void __iomem *)(host->ports[0]->config_data));
> +
> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
> +
> + return 0;
> +}
> +
> +static struct platform_driver ep93xx_ide_driver = {
> + .probe = ep93xx_ide_probe,
> + .remove = __exit_p(ep93xx_ide_remove),
> + .driver = {
> + .name = MODULE_NAME,
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init ep93xx_ide_init(void)
> +{
> + return platform_driver_register(&ep93xx_ide_driver);
Since this is not a hotplug driver, you can save some memory on making
ep93xx_ide_probe() __init -- using platform_driver_probe() here instead of
platform_driver_register() and *not* initializing the 'probe' field of the
'struct platform_driver'.
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-06 17:05 ` Sergei Shtylyov
@ 2009-05-07 9:36 ` João Ramos
2009-05-07 11:01 ` João Ramos
` (3 more replies)
0 siblings, 4 replies; 59+ messages in thread
From: João Ramos @ 2009-05-07 9:36 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
Hi,
[...]
>
>
>> Sergei: I've added the delays you suggested in the read/write
>> procedures, accordingly to the delays specified in the processor's
>> user manual for PIO Mode 4.
>
> Why only for PIO mode 4 if you're claiming support for modes 0 thru 4?
The patch currently supports only PIO Mode 4, as it is hardcoded into
the CPU's IDE configuration register:
writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
And also in the ide_port_info struct:
static struct ide_port_info ep93xx_ide_port_info = {
.name = MODULE_NAME,
.init_hwif = ep93xx_ide_init_hwif,
.tp_ops = &ep93xx_ide_tp_ops,
.host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
.pio_mask = ATA_PIO4,
};
So you're saying I should support all PIO modes? If so, I would have to
make conditional code, checking perhaps a module param to sort which PIO
mode to use.
Also, the manual delays should also depend on the PIO mode selected...
It would be done either by a module param (defaulting to PIO Mode 4), or
through a kernel configuration variable...
>
> [...]
>
> Please submit the above parts separately to linux-arm-kernel, as the
> platform code doesn't belong to the driver.
>
Ok.
>
>
>> +
>> +/* Macro for checking -IORDY line state */
>> +#define ep93xx_ide_check_iordy() ({ \
>> + u32 _reg = readl(IDE_REGISTER(IDECTRL)); \
>> + (_reg & IDECTRL_IORDY) ? 1 : 0; \
>> +})
>
> Make this an inline function, please.
Ok.
[...]
>>
>> +
>> +/*
>> + * EP93xx IDE PIO low-level hardware initialization routine
>> + */
>> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
>> +{
>> + unsigned long base = hwif->config_data;
>> +
>> + /* enforce reset state */
>> + ep93xx_ide_clean_regs(base);
>> +
>> + /* set gpio port E, G and H for IDE */
>> + ep93xx_ide_on_gpio(1);
>
> Shouldn't this be done in the platform code instead?
The idea is to make this driver loadable, as suggested earlier by Ryan
and Hartley.
The IDE pins are initially (and by default) set to GPIO function. If the
IDE driver is registered, through specific platform code or by loading
the module at runtime, then the IDE driver cares to configure the IDE
pins for IDE function, returning them to GPIO function once the driver
is unloaded.
I think this is the approach desired by the EP93xx maintainers, correct?
(Ryan? Hartley?)
[...]
>
>>
>> +static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
>> +{
>> + u32 reg;
>> +
>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>> + IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +
>> + reg &= ~IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(70);
>> +
>> + while (!ep93xx_ide_check_iordy())
>> + cpu_relax();
>> +
>> + reg |= IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +
>> + return readl(IDE_REGISTER(IDEDATAIN));
>
> Hey, how this even works (if the data doesn't get latched
> somehow)?! You
> should read the register right *before* the deassertion of -DIORx! The
> minimum data hold time is only 5 ns and the data lines will be tristated
> within 30 ns maximum...
Will be fixed.
>
> [...]
>
>> +static u16 ep93xx_ide_readw(unsigned long base, unsigned long addr)
>> +{
>> + u32 reg;
>> +
>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>> + IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +
>> + reg &= ~IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(70);
>> +
>> + while (!ep93xx_ide_check_iordy())
>> + cpu_relax();
>> +
>> + reg |= IDECTRL_DIORN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +
>> + return readl(IDE_REGISTER(IDEDATAIN));
>> +}
>
> I don't see any difference between ep93xx_ide_read[bw](), so why don't
> you use a single function?
The difference is only the return value, which casts to either u8 or u16
type.
Perhaps there's no need for two different functions, assuming the
high-level code will always cast down the variables to their correct
type (u8, u16).
>
>> +static void
>> +ep93xx_ide_writeb(unsigned long base, u8 value, unsigned long addr)
>> +{
>> + u32 reg;
>> +
>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>> + IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +
>> + writel(value, IDE_REGISTER(IDEDATAOUT));
>
> Hum, do you know at which moments this controller starts/stops driving
> data lines on the IDE bus? After DIOWx- assertion/deassertion?
I will look into that. I based this source code in the CPU's user guide,
which tips a correct procedure for reading/writing in PIO mode.
But I will check that, as I already had some trouble with the user's
guide...
>
>> +
>> + reg &= ~IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(70);
>> +
>> + while (!ep93xx_ide_check_iordy())
>> + cpu_relax();
>> +
>> + reg |= IDECTRL_DIOWN;
>> + writel(reg, IDE_REGISTER(IDECTRL));
>> + ndelay(25);
>> +}
>> +
>
> [...]
>
>>
>
> The same question: why we need both ep93xx_ide_write[bw]()?
Same answer as before. Perhaps there's no need for those.
>
>> +
>> +static void
>> +ep93xx_ide_readsw(unsigned long base, unsigned long addr, void *buf,
>> + unsigned int len)
>> +{
>> + u16 *data = (u16 *) buf;
>> +
>> + for (; len; len--)
>
> IMHO, while (len--) fits better...
Ok.
>
>> + *data++ = cpu_to_le16(ep93xx_ide_readw(base, addr));
>> +}
>> +
>
>> +
>> +/*
>> + * EP93xx IDE PIO Transport Operations
>> + */
>> +
>> +static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
>> +{
>> + ep93xx_ide_writeb(hwif->config_data, cmd,
>> + hwif->io_ports.command_addr);
>
> It's preferrable if you do not insert unnecessary spaces in the
> multiline statements:
>
> ep93xx_ide_writeb(hwif->config_data, cmd,
> hwif->io_ports.command_addr);
>
> This also looks prettier. :-)
Ok.
>
>> +static void
>> +ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf, u8
>> valid)
>> +{
>> + ide_hwif_t *hwif = drive->hwif;
>> + struct ide_io_ports *io_ports = &hwif->io_ports;
>> +
>> + if (valid & IDE_VALID_ERROR)
>> + tf->error =
>> + ep93xx_ide_readb(hwif->config_data,
>
>> + io_ports->feature_addr);
>
> Again, tabs are strongly preferred over spaces (and carrying the
> statement over to the next line where it's not necessary):
>
> tf->error = ep93xx_ide_readb(hwif->config_data,
> io_ports->feature_addr);
Ok.
>
>> +static int __devinit ep93xx_ide_probe(struct platform_device *pdev)
>> +{
>> + int retval;
>> + int irq;
>
> Stray tab?
>
> [...]
Will be fixed.
>
>> + retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
>> + if (retval) {
>> + dev_err(&pdev->dev,
>> + "unable to add EP93xx IDE host controller!\n");
>
> s/add/register/, s/host controller/driver/
Ok.
[...]
> Since this is not a hotplug driver, you can save some memory on
> making ep93xx_ide_probe() __init -- using platform_driver_probe() here
> instead of platform_driver_register() and *not* initializing the
> 'probe' field of the 'struct platform_driver'.
I think Ryan and Hartley would like this driver to be
loadable/unloadable at runtime, as I pointed out earlier in this mail.
I can make the fixes about this, ensuring Ryan and Hartley will both
agree to them.
> MBR, Sergei
>
Regards,
João
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 9:36 ` João Ramos
@ 2009-05-07 11:01 ` João Ramos
2009-05-07 13:53 ` Alan Cox
` (2 subsequent siblings)
3 siblings, 0 replies; 59+ messages in thread
From: João Ramos @ 2009-05-07 11:01 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
>>> +static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
>>> +{
>>> + u32 reg;
>>> +
>>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>>> + IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +
>>> + reg &= ~IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(70);
>>> +
>>> + while (!ep93xx_ide_check_iordy())
>>> + cpu_relax();
>>> +
>>> + reg |= IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +
>>> + return readl(IDE_REGISTER(IDEDATAIN));
>>>
>> Hey, how this even works (if the data doesn't get latched
>> somehow)?! You
>> should read the register right *before* the deassertion of -DIORx! The
>> minimum data hold time is only 5 ns and the data lines will be tristated
>> within 30 ns maximum...
>>
EP93xx User's Guide, Section 27 - 14 (IDEDataIn register Description)
<quote>:
"In PIO mode read operation, this register is the Input Data Registers,
containing the register contents or the data read from the device. The
register is loaded from the DD pins at the positive edge of the DIORn
signal. The register is read-only in this operation. ... "
Meaning, the data is latched from the data bus at the positive edge of
DIORn, and transfered into the IDEDATAIN register.
The above procedure is correct, according to this.
[...]
>
>>> +static void
>>> +ep93xx_ide_writeb(unsigned long base, u8 value, unsigned long addr)
>>> +{
>>> + u32 reg;
>>> +
>>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>>> + IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +
>>> + writel(value, IDE_REGISTER(IDEDATAOUT));
>>>
>> Hum, do you know at which moments this controller starts/stops driving
>> data lines on the IDE bus? After DIOWx- assertion/deassertion?
>>
>
> I will look into that. I based this source code in the CPU's user guide,
> which tips a correct procedure for reading/writing in PIO mode.
> But I will check that, as I already had some trouble with the user's
> guide...
>
EP93xx User's Guide, Section 27 - 13 (IDEDataOut register Description)
<quote>:
"In PIO mode write operation, this register is the Output Data
Registers, containing the register contents or the data to be written to
the device. The register is driven onto the DD pins when DIOWn is low.
The register is both read write in this operation. ..."
Meaning, according to this, the procedure is correct. First we prepare
the data in the IDEDATAOUT register, then we assert DIOWn and data from
IDEDATAOUT is transfered to the data bus.
Regards,
João
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 9:36 ` João Ramos
2009-05-07 11:01 ` João Ramos
@ 2009-05-07 13:53 ` Alan Cox
2009-05-07 15:33 ` João Ramos
2009-05-07 16:52 ` H Hartley Sweeten
2009-05-08 11:23 ` Sergei Shtylyov
3 siblings, 1 reply; 59+ messages in thread
From: Alan Cox @ 2009-05-07 13:53 UTC (permalink / raw)
To: João Ramos
Cc: Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon, linux-arm-kernel,
linux-ide
> So you're saying I should support all PIO modes? If so, I would have to
> make conditional code, checking perhaps a module param to sort which PIO
> mode to use.
If you advertise PIO0-PIO4 as supported the core IDE code will do all the
work on figuring which modes are supported by the attached devices. You
just need to be able to set them.
Alan
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 13:53 ` Alan Cox
@ 2009-05-07 15:33 ` João Ramos
2009-05-08 12:04 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-07 15:33 UTC (permalink / raw)
To: Alan Cox
Cc: Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon, linux-arm-kernel,
linux-ide
Alan Cox escreveu:
>> So you're saying I should support all PIO modes? If so, I would have to
>> make conditional code, checking perhaps a module param to sort which PIO
>> mode to use.
>>
>
> If you advertise PIO0-PIO4 as supported the core IDE code will do all the
> work on figuring which modes are supported by the attached devices. You
> just need to be able to set them.
>
> Alan
>
Ok, so I've been studying that (I was quite lost for a time, I confess,
I'm not that much familiar with the IDE subsystem, so please bear with
me ;-) ).
So I need to set up a hook for 'set_pio_mode()', so that when the IDE
subsystem detects a device and figures the most suitable PIO mode for
the device, it will call the 'set_pio_mode' routine provided by the
driver in order to configure the host controller for that PIO mode.
This also means that my host controller driver should always default to
PIO Mode 0, as the initial host controller setup that is carried out by
the 'init_hwif' routine, allowing devices to be detected. Afterwards,
the IDE subsystem detects the most suitable PIO mode and calls
'set_pio_mode' to change that configuration.
Am I correct on this?
There's just only one issue; normally, I would setup the specific
timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
you look further in the driver, those timings aren't defined through a
memory controller but instead manually enforced by 'ndelay' calls (arghhh).
This means that in my low-level procedures for reading and writing, I
need to have access to the timings (or the struct ide_timing)
corresponding to the PIO mode selected, in order to use the correct delays.
My question is: which is the best way to accomplish this? Declaring a
global struct ide_timing variable pointer that always holds the correct
ide_timing struct to the selected PIO mode? Or should I always check (in
some manner) what is the current PIO mode and then select the adequate
delays?
Best regards,
João
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: EP93xx PIO IDE driver proposal
2009-05-07 9:36 ` João Ramos
2009-05-07 11:01 ` João Ramos
2009-05-07 13:53 ` Alan Cox
@ 2009-05-07 16:52 ` H Hartley Sweeten
2009-05-07 22:09 ` Ryan Mallon
2009-05-08 11:23 ` Sergei Shtylyov
3 siblings, 1 reply; 59+ messages in thread
From: H Hartley Sweeten @ 2009-05-07 16:52 UTC (permalink / raw)
To: João Ramos, Sergei Shtylyov; +Cc: Ryan Mallon, linux-arm-kernel, linux-ide
On Thursday, May 07, 2009 2:36 AM, João Ramos wrote:
>>>
>>> +
>>> +/*
>>> + * EP93xx IDE PIO low-level hardware initialization routine
>>> + */
>>> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
>>> +{
>>> + unsigned long base = hwif->config_data;
>>> +
>>> + /* enforce reset state */
>>> + ep93xx_ide_clean_regs(base);
>>> +
>>> + /* set gpio port E, G and H for IDE */
>>> + ep93xx_ide_on_gpio(1);
>>
>> Shouldn't this be done in the platform code instead?
>
> The idea is to make this driver loadable, as suggested earlier by
> Ryan and Hartley.
> The IDE pins are initially (and by default) set to GPIO function.
> If the IDE driver is registered, through specific platform code or
> by loading the module at runtime, then the IDE driver cares to
> configure the IDE pins for IDE function, returning them to GPIO
> function once the driver is unloaded.
>
> I think this is the approach desired by the EP93xx maintainers,
> correct? (Ryan? Hartley?)
The pins should default to gpio mode and only be set to IDE when this
driver is used.
If the IDE group objects to having the ep93xx_ide_on_gpio() call in
the driver it can be moved to the platform code. I assume if a user
has selected this driver they are not planning on using the pins for
gpio so when the driver is registered the pins could be put into IDE
mode at that time. The drawback is when the driver is a module the
pins will still be unavailable for gpio when the driver is not loaded.
>> Since this is not a hotplug driver, you can save some memory on
>> making ep93xx_ide_probe() __init -- using platform_driver_probe() here
>> instead of platform_driver_register() and *not* initializing the
>> 'probe' field of the 'struct platform_driver'.
>
> I think Ryan and Hartley would like this driver to be
> loadable/unloadable at runtime, as I pointed out earlier in this mail.
> I can make the fixes about this, ensuring Ryan and Hartley will both
> agree to them.
Ryan might have a comment on this. My platform does not use IDE at
this time.
Regards,
Hartley
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 16:52 ` H Hartley Sweeten
@ 2009-05-07 22:09 ` Ryan Mallon
2009-05-07 22:31 ` H Hartley Sweeten
0 siblings, 1 reply; 59+ messages in thread
From: Ryan Mallon @ 2009-05-07 22:09 UTC (permalink / raw)
To: H Hartley Sweeten
Cc: João Ramos, Sergei Shtylyov, linux-arm-kernel, linux-ide
H Hartley Sweeten wrote:
> On Thursday, May 07, 2009 2:36 AM, João Ramos wrote:
>>>> +
>>>> +/*
>>>> + * EP93xx IDE PIO low-level hardware initialization routine
>>>> + */
>>>> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
>>>> +{
>>>> + unsigned long base = hwif->config_data;
>>>> +
>>>> + /* enforce reset state */
>>>> + ep93xx_ide_clean_regs(base);
>>>> +
>>>> + /* set gpio port E, G and H for IDE */
>>>> + ep93xx_ide_on_gpio(1);
>>> Shouldn't this be done in the platform code instead?
>> The idea is to make this driver loadable, as suggested earlier by
>> Ryan and Hartley.
>> The IDE pins are initially (and by default) set to GPIO function.
>> If the IDE driver is registered, through specific platform code or
>> by loading the module at runtime, then the IDE driver cares to
>> configure the IDE pins for IDE function, returning them to GPIO
>> function once the driver is unloaded.
>>
>> I think this is the approach desired by the EP93xx maintainers,
>> correct? (Ryan? Hartley?)
>
> The pins should default to gpio mode and only be set to IDE when this
> driver is used.
>
> If the IDE group objects to having the ep93xx_ide_on_gpio() call in
> the driver it can be moved to the platform code. I assume if a user
> has selected this driver they are not planning on using the pins for
> gpio so when the driver is registered the pins could be put into IDE
> mode at that time. The drawback is when the driver is a module the
> pins will still be unavailable for gpio when the driver is not loaded.
>
>>> Since this is not a hotplug driver, you can save some memory on
>>> making ep93xx_ide_probe() __init -- using platform_driver_probe() here
>>> instead of platform_driver_register() and *not* initializing the
>>> 'probe' field of the 'struct platform_driver'.
>> I think Ryan and Hartley would like this driver to be
>> loadable/unloadable at runtime, as I pointed out earlier in this mail.
>> I can make the fixes about this, ensuring Ryan and Hartley will both
>> agree to them.
>
> Ryan might have a comment on this. My platform does not use IDE at
> this time.
You can (and should) use platform_driver_probe and still have the module
be loadable. For the gpio settings, I think that probably the best
approach is to use gpio_request for each of the gpio pins so that
gpio_lib knows that they are in use and sysfs/debugfs will correctly
show they are assigned to ide, and then call ep93xx_ide_on_gpio, so you
will have something like in your init code:
/* Request gpio bank E */
for (i = 0; i < 7; i++) {
err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
if (err)
goto fail_gpio_e;
err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
if (err)
goto fail_gpio_f;
err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
if (err)
goto fail_gpio_g;
}
ep93xx_ide_on_gpio(1);
return 0;
fail_gpio_g:
free_gpio(EP93XX_GPIO_LINE_F(i);
fail_gpio_f:
free_gpio(EP93XX_GPIO_LINE_E(i);
fail_gpio_e:
for (; i >= 0; --i) {
free_gpio(EP93XX_GPIO_LINE_E(i);
free_gpio(EP93XX_GPIO_LINE_F(i);
free_gpio(EP93XX_GPIO_LINE_G(i);
}
and the following in the release code:
/* Free gpios */
for (i = 0; i < 7; i++) {
gpio_free(EP93XX_GPIO_LINE_E(i));
gpio_free(EP93XX_GPIO_LINE_F(i));
gpio_free(EP93XX_GPIO_LINE_G(i));
}
ep93xx_gpio_on_ide(0);
That way, the module can be loaded at runtime. If any of the gpios are
already in use, then the module load will fail. If not, the gpios will
all be requested and the pins put into alternative function mode. When
the module is removed the gpios will be released and put back into gpio
mode.
If the gpio management is put into the platform code them, as Hartley
said, the E, F and G gpios will all configured for ide regardless of
whether it is actually being used.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon Unit 5, Amuri Park
Phone: +64 3 3779127 404 Barbadoes St
Fax: +64 3 3779135 PO Box 13 889
Email: ryan@bluewatersys.com Christchurch, 8013
Web: http://www.bluewatersys.com New Zealand
Freecall Australia 1800 148 751 USA 1800 261 2934
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: EP93xx PIO IDE driver proposal
2009-05-07 22:09 ` Ryan Mallon
@ 2009-05-07 22:31 ` H Hartley Sweeten
2009-05-07 22:51 ` Ryan Mallon
0 siblings, 1 reply; 59+ messages in thread
From: H Hartley Sweeten @ 2009-05-07 22:31 UTC (permalink / raw)
To: Ryan Mallon; +Cc: João Ramos, Sergei Shtylyov, linux-arm-kernel, linux-ide
On Thursday, May 07, 2009 3:09 PM, Ryan Mallon wrote:
> You can (and should) use platform_driver_probe and still have the
> module be loadable. For the gpio settings, I think that probably
> the best approach is to use gpio_request for each of the gpio pins
> so that gpio_lib knows that they are in use and sysfs/debugfs will
> correctly show they are assigned to ide, and then call
> ep93xx_ide_on_gpio, so you will have something like in your init code:
Hmmm...
How about doing all the request/free's in the ep93xx_ide_on_gpio()
function? That way the platform code can initially call it and set
all the pins into gpio mode (freeing them). Then later when the
IDE driver is loaded it can call the function to try and put the
pins into IDE mode (requesting them). If any of the pins are
unavailable the ep93xx_ide_on_gpio() function can return the
necessary error code preventing the IDE driver from loading. When
the driver is unloaded it just needs to call the core to free
the gpio's.
Something like:
int ep93xx_ide_on_gpio(int enable)
{
u32 reg;
int err;
int i;
reg = __raw_read(EP93XX_SYSCON_DEVICE_CONFIG);
if (enable) {
for (i = 0; i < 8; i++) {
err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
if (err)
goto fail_gpio_e;
err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
if (err)
goto fail_gpio_f;
err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
if (err)
goto fail_gpio_g;
}
reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
} else {
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));
}
reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
}
__raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
__raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
return 0;
fail_gpio_g:
free_gpio(EP93XX_GPIO_LINE_F(i);
fail_gpio_f:
free_gpio(EP93XX_GPIO_LINE_E(i);
fail_gpio_e:
for ( ; i >= 0; --i) {
free_gpio(EP93XX_GPIO_LINE_E(i);
free_gpio(EP93XX_GPIO_LINE_F(i);
free_gpio(EP93XX_GPIO_LINE_G(i);
}
return err;
}
Regards,
Hartley
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 22:31 ` H Hartley Sweeten
@ 2009-05-07 22:51 ` Ryan Mallon
2009-05-07 23:01 ` H Hartley Sweeten
0 siblings, 1 reply; 59+ messages in thread
From: Ryan Mallon @ 2009-05-07 22:51 UTC (permalink / raw)
To: H Hartley Sweeten
Cc: João Ramos, Sergei Shtylyov, linux-arm-kernel, linux-ide
H Hartley Sweeten wrote:
> On Thursday, May 07, 2009 3:09 PM, Ryan Mallon wrote:
>> You can (and should) use platform_driver_probe and still have the
>> module be loadable. For the gpio settings, I think that probably
>> the best approach is to use gpio_request for each of the gpio pins
>> so that gpio_lib knows that they are in use and sysfs/debugfs will
>> correctly show they are assigned to ide, and then call
>> ep93xx_ide_on_gpio, so you will have something like in your init code:
>
> Hmmm...
>
> How about doing all the request/free's in the ep93xx_ide_on_gpio()
> function? That way the platform code can initially call it and set
> all the pins into gpio mode (freeing them). Then later when the
> IDE driver is loaded it can call the function to try and put the
> pins into IDE mode (requesting them). If any of the pins are
> unavailable the ep93xx_ide_on_gpio() function can return the
> necessary error code preventing the IDE driver from loading. When
> the driver is unloaded it just needs to call the core to free
> the gpio's.
>
> Something like:
>
> int ep93xx_ide_on_gpio(int enable)
> {
> u32 reg;
> int err;
> int i;
>
> reg = __raw_read(EP93XX_SYSCON_DEVICE_CONFIG);
>
> if (enable) {
> for (i = 0; i < 8; i++) {
> err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
> if (err)
> goto fail_gpio_e;
> err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
> if (err)
> goto fail_gpio_f;
> err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
> if (err)
> goto fail_gpio_g;
> }
> reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
> } else {
> 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));
> }
> reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
> }
>
> __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
> __raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
Is this going to be modified later to use the ep93xx_syscon_ functions
once they are merged?
> return 0;
>
> fail_gpio_g:
> free_gpio(EP93XX_GPIO_LINE_F(i);
> fail_gpio_f:
> free_gpio(EP93XX_GPIO_LINE_E(i);
> fail_gpio_e:
> for ( ; i >= 0; --i) {
> free_gpio(EP93XX_GPIO_LINE_E(i);
> free_gpio(EP93XX_GPIO_LINE_F(i);
> free_gpio(EP93XX_GPIO_LINE_G(i);
> }
> return err;
> }
Yeah, that makes sense. Keeps the driver nice and clean that way too.
The ep93xx_ide_on_gpio function for core.c should be posted as a
separate patch though.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon Unit 5, Amuri Park
Phone: +64 3 3779127 404 Barbadoes St
Fax: +64 3 3779135 PO Box 13 889
Email: ryan@bluewatersys.com Christchurch, 8013
Web: http://www.bluewatersys.com New Zealand
Freecall Australia 1800 148 751 USA 1800 261 2934
^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: EP93xx PIO IDE driver proposal
2009-05-07 22:51 ` Ryan Mallon
@ 2009-05-07 23:01 ` H Hartley Sweeten
2009-05-07 23:12 ` Ryan Mallon
0 siblings, 1 reply; 59+ messages in thread
From: H Hartley Sweeten @ 2009-05-07 23:01 UTC (permalink / raw)
To: Ryan Mallon; +Cc: João Ramos, Sergei Shtylyov, linux-arm-kernel, linux-ide
On Thursday, May 07, 2009 3:51 PM, Ryan Mallon wrote:
>>> You can (and should) use platform_driver_probe and still have
>>> the module be loadable. For the gpio settings, I think that
>>> probably the best approach is to use gpio_request for each of
>>> the gpio pins so that gpio_lib knows that they are in use and
>>> sysfs/debugfs will correctly show they are assigned to ide,
>>> and then call ep93xx_ide_on_gpio, so you will have something
>>> like in your init code:
>>
>> Hmmm...
>>
>> How about doing all the request/free's in the ep93xx_ide_on_gpio()
>> function? That way the platform code can initially call it and set
>> all the pins into gpio mode (freeing them). Then later when the
>> IDE driver is loaded it can call the function to try and put the
>> pins into IDE mode (requesting them). If any of the pins are
>> unavailable the ep93xx_ide_on_gpio() function can return the
>> necessary error code preventing the IDE driver from loading. When
>> the driver is unloaded it just needs to call the core to free
>> the gpio's.
>>
>> Something like:
>>
>> int ep93xx_ide_on_gpio(int enable)
>> {
>> u32 reg;
>> int err;
>> int i;
>>
>> reg = __raw_read(EP93XX_SYSCON_DEVICE_CONFIG);
>>
>> if (enable) {
>> for (i = 0; i < 8; i++) {
>> err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
>> if (err)
>> goto fail_gpio_e;
>> err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
>> if (err)
>> goto fail_gpio_f;
>> err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
>> if (err)
>> goto fail_gpio_g;
>> }
>> reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>> } else {
>> 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));
>> }
>> reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>> }
>>
>> __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
>> __raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
>
> Is this going to be modified later to use the ep93xx_syscon_ functions
> once they are merged?
Yes, hopefully that occurs sometime soon. Still no responses on it.
>> return 0;
>>
>> fail_gpio_g:
>> free_gpio(EP93XX_GPIO_LINE_F(i);
>> fail_gpio_f:
>> free_gpio(EP93XX_GPIO_LINE_E(i);
>> fail_gpio_e:
>> for ( ; i >= 0; --i) {
>> free_gpio(EP93XX_GPIO_LINE_E(i);
>> free_gpio(EP93XX_GPIO_LINE_F(i);
>> free_gpio(EP93XX_GPIO_LINE_G(i);
>> }
>> return err;
>> }
>
> Yeah, that makes sense. Keeps the driver nice and clean that way too.
> The ep93xx_ide_on_gpio function for core.c should be posted as a
> separate patch though.
>
I agree. Actually that patch needs to go in now so that the port E/F/G
pins will work for normal GPIO. The syscon register defaults at reset with
the bits cleared so the boot state has them set for IDE mode. That drove
me nuts for quite a while...
If wanted I will put together the necessary patch for this.
This function will hopefully get a bit cleaner later. I've been messing
with an addition to the GPIOLIB API to add support for "ports". When/if
that goes in all the for() loops go away and you just need to do a
gpio_port_request(...) to grab the pins and gpio_port_free(...) to put
them back.
Ahh.. If only the days were longer....
Regards,
Hartley
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 23:01 ` H Hartley Sweeten
@ 2009-05-07 23:12 ` Ryan Mallon
2009-05-07 23:32 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Ryan Mallon @ 2009-05-07 23:12 UTC (permalink / raw)
To: H Hartley Sweeten
Cc: João Ramos, Sergei Shtylyov, linux-arm-kernel, linux-ide
H Hartley Sweeten wrote:
> On Thursday, May 07, 2009 3:51 PM, Ryan Mallon wrote:
>>>> You can (and should) use platform_driver_probe and still have
>>>> the module be loadable. For the gpio settings, I think that
>>>> probably the best approach is to use gpio_request for each of
>>>> the gpio pins so that gpio_lib knows that they are in use and
>>>> sysfs/debugfs will correctly show they are assigned to ide,
>>>> and then call ep93xx_ide_on_gpio, so you will have something
>>>> like in your init code:
>>> Hmmm...
>>>
>>> How about doing all the request/free's in the ep93xx_ide_on_gpio()
>>> function? That way the platform code can initially call it and set
>>> all the pins into gpio mode (freeing them). Then later when the
>>> IDE driver is loaded it can call the function to try and put the
>>> pins into IDE mode (requesting them). If any of the pins are
>>> unavailable the ep93xx_ide_on_gpio() function can return the
>>> necessary error code preventing the IDE driver from loading. When
>>> the driver is unloaded it just needs to call the core to free
>>> the gpio's.
>>>
>>> Something like:
>>>
>>> int ep93xx_ide_on_gpio(int enable)
>>> {
>>> u32 reg;
>>> int err;
>>> int i;
>>>
>>> reg = __raw_read(EP93XX_SYSCON_DEVICE_CONFIG);
>>>
>>> if (enable) {
>>> for (i = 0; i < 8; i++) {
>>> err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
>>> if (err)
>>> goto fail_gpio_e;
>>> err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
>>> if (err)
>>> goto fail_gpio_f;
>>> err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
>>> if (err)
>>> goto fail_gpio_g;
>>> }
>>> reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>>> } else {
>>> 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));
>>> }
>>> reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>>> }
>>>
>>> __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
>>> __raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
>> Is this going to be modified later to use the ep93xx_syscon_ functions
>> once they are merged?
>
> Yes, hopefully that occurs sometime soon. Still no responses on it.
>
>>> return 0;
>>>
>>> fail_gpio_g:
>>> free_gpio(EP93XX_GPIO_LINE_F(i);
>>> fail_gpio_f:
>>> free_gpio(EP93XX_GPIO_LINE_E(i);
>>> fail_gpio_e:
>>> for ( ; i >= 0; --i) {
>>> free_gpio(EP93XX_GPIO_LINE_E(i);
>>> free_gpio(EP93XX_GPIO_LINE_F(i);
>>> free_gpio(EP93XX_GPIO_LINE_G(i);
>>> }
>>> return err;
>>> }
>> Yeah, that makes sense. Keeps the driver nice and clean that way too.
>> The ep93xx_ide_on_gpio function for core.c should be posted as a
>> separate patch though.
>>
>
> I agree. Actually that patch needs to go in now so that the port E/F/G
> pins will work for normal GPIO. The syscon register defaults at reset with
> the bits cleared so the boot state has them set for IDE mode. That drove
> me nuts for quite a while...
>
> If wanted I will put together the necessary patch for this.
That would be good. I think that we should default all of the pins to
gpio mode on boot, and then drivers should call into core.c to put pins
in alternative function mode where necessary.
> This function will hopefully get a bit cleaner later. I've been messing
> with an addition to the GPIOLIB API to add support for "ports". When/if
> that goes in all the for() loops go away and you just need to do a
> gpio_port_request(...) to grab the pins and gpio_port_free(...) to put
> them back.
It would be nice if gpiolib had some understanding of alternative
function modes for gpios. However I'm not sure that it can be done in a
generic way that will suit all architectures/chips.
> Ahh.. If only the days were longer....
Agreed :-).
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon Unit 5, Amuri Park
Phone: +64 3 3779127 404 Barbadoes St
Fax: +64 3 3779135 PO Box 13 889
Email: ryan@bluewatersys.com Christchurch, 8013
Web: http://www.bluewatersys.com New Zealand
Freecall Australia 1800 148 751 USA 1800 261 2934
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 23:12 ` Ryan Mallon
@ 2009-05-07 23:32 ` João Ramos
2009-05-07 23:58 ` H Hartley Sweeten
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-07 23:32 UTC (permalink / raw)
To: Ryan Mallon
Cc: H Hartley Sweeten, Sergei Shtylyov, linux-arm-kernel, linux-ide
Ryan Mallon escreveu:
> H Hartley Sweeten wrote:
>
>> On Thursday, May 07, 2009 3:51 PM, Ryan Mallon wrote:
>>
>>>>> You can (and should) use platform_driver_probe and still have
>>>>> the module be loadable. For the gpio settings, I think that
>>>>> probably the best approach is to use gpio_request for each of
>>>>> the gpio pins so that gpio_lib knows that they are in use and
>>>>> sysfs/debugfs will correctly show they are assigned to ide,
>>>>> and then call ep93xx_ide_on_gpio, so you will have something
>>>>> like in your init code:
>>>>>
As for the platform_driver_probe, I will fix that then.
>>>> Hmmm...
>>>>
>>>> How about doing all the request/free's in the ep93xx_ide_on_gpio()
>>>> function? That way the platform code can initially call it and set
>>>> all the pins into gpio mode (freeing them). Then later when the
>>>> IDE driver is loaded it can call the function to try and put the
>>>> pins into IDE mode (requesting them). If any of the pins are
>>>> unavailable the ep93xx_ide_on_gpio() function can return the
>>>> necessary error code preventing the IDE driver from loading. When
>>>> the driver is unloaded it just needs to call the core to free
>>>> the gpio's.
>>>>
>>>> Something like:
>>>>
>>>> int ep93xx_ide_on_gpio(int enable)
>>>> {
>>>> u32 reg;
>>>> int err;
>>>> int i;
>>>>
>>>> reg = __raw_read(EP93XX_SYSCON_DEVICE_CONFIG);
>>>>
>>>> if (enable) {
>>>> for (i = 0; i < 8; i++) {
>>>> err = gpio_request(EP93XX_GPIO_LINE_E(i), "ep93xx_ide");
>>>> if (err)
>>>> goto fail_gpio_e;
>>>> err = gpio_request(EP93XX_GPIO_LINE_F(i), "ep93xx_ide");
>>>> if (err)
>>>> goto fail_gpio_f;
>>>> err = gpio_request(EP93XX_GPIO_LINE_G(i), "ep93xx_ide");
>>>> if (err)
>>>> goto fail_gpio_g;
>>>> }
>>>> reg &= ~(EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>>>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>>>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>>>> } else {
>>>> 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));
>>>> }
>>>> reg |= (EP93XX_SYSCON_DEVICE_CONFIG_EONIDE |
>>>> EP93XX_SYSCON_DEVICE_CONFIG_GONIDE |
>>>> EP93XX_SYSCON_DEVICE_CONFIG_HONIDE);
>>>> }
>>>>
>>>> __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
>>>> __raw_writel(reg, EP93XX_SYSCON_DEVICE_CONFIG);
>>>>
>>> Is this going to be modified later to use the ep93xx_syscon_ functions
>>> once they are merged?
>>>
>> Yes, hopefully that occurs sometime soon. Still no responses on it.
>>
I was hoping for that patch to be merged to use the functions provided
to write Syscon locked registers.
In the meanwhile, as it isn't in the mainline kernel yet, I used this form.
>>
>>>> return 0;
>>>>
>>>> fail_gpio_g:
>>>> free_gpio(EP93XX_GPIO_LINE_F(i);
>>>> fail_gpio_f:
>>>> free_gpio(EP93XX_GPIO_LINE_E(i);
>>>> fail_gpio_e:
>>>> for ( ; i >= 0; --i) {
>>>> free_gpio(EP93XX_GPIO_LINE_E(i);
>>>> free_gpio(EP93XX_GPIO_LINE_F(i);
>>>> free_gpio(EP93XX_GPIO_LINE_G(i);
>>>> }
>>>> return err;
>>>> }
>>>>
>>> Yeah, that makes sense. Keeps the driver nice and clean that way too.
>>> The ep93xx_ide_on_gpio function for core.c should be posted as a
>>> separate patch though.
>>>
>>>
>> I agree. Actually that patch needs to go in now so that the port E/F/G
>> pins will work for normal GPIO. The syscon register defaults at reset with
>> the bits cleared so the boot state has them set for IDE mode. That drove
>> me nuts for quite a while...
>>
>> If wanted I will put together the necessary patch for this.
>>
>
> That would be good. I think that we should default all of the pins to
> gpio mode on boot, and then drivers should call into core.c to put pins
> in alternative function mode where necessary.
>
Hartley, will you handle this then, or do you want me to submit a
separate patch for the renewed 'ep93xx_ide_on_gpio' function?
I can do this and test it already with the IDE patch to see if it's OK.
>
>> This function will hopefully get a bit cleaner later. I've been messing
>> with an addition to the GPIOLIB API to add support for "ports". When/if
>> that goes in all the for() loops go away and you just need to do a
>> gpio_port_request(...) to grab the pins and gpio_port_free(...) to put
>> them back.
>>
>
> It would be nice if gpiolib had some understanding of alternative
> function modes for gpios. However I'm not sure that it can be done in a
> generic way that will suit all architectures/chips.
>
>
>> Ahh.. If only the days were longer....
>>
>
> Agreed :-).
>
> ~Ryan
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
-------------------------------------------------------------------
List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel
FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php
Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php
^ permalink raw reply [flat|nested] 59+ messages in thread
* RE: EP93xx PIO IDE driver proposal
2009-05-07 23:32 ` João Ramos
@ 2009-05-07 23:58 ` H Hartley Sweeten
0 siblings, 0 replies; 59+ messages in thread
From: H Hartley Sweeten @ 2009-05-07 23:58 UTC (permalink / raw)
To: João Ramos, Ryan Mallon; +Cc: Sergei Shtylyov, linux-arm-kernel, linux-ide
On Thursday, May 07, 2009 4:33 PM, João Ramos wrote:
>>>> Yeah, that makes sense. Keeps the driver nice and clean that way
>>>> too. The ep93xx_ide_on_gpio function for core.c should be posted
>>>> as a separate patch though.
>>>>
>>>>
>>> I agree. Actually that patch needs to go in now so that the port
>>> E/F/G pins will work for normal GPIO. The syscon register defaults
>>> at reset with the bits cleared so the boot state has them set for
>>> IDE mode. That drove me nuts for quite a while...
>>>
>>> If wanted I will put together the necessary patch for this.
>>>
>>
>> That would be good. I think that we should default all of the pins to
>> gpio mode on boot, and then drivers should call into core.c to put pins
>> in alternative function mode where necessary.
>>
>
> Hartley, will you handle this then, or do you want me to submit a
> separate patch for the renewed 'ep93xx_ide_on_gpio' function?
> I can do this and test it already with the IDE patch to see if it's OK.
I just posted the necessary patch. Please test that with your IDE patch.
Thanks,
Hartley
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 9:36 ` João Ramos
` (2 preceding siblings ...)
2009-05-07 16:52 ` H Hartley Sweeten
@ 2009-05-08 11:23 ` Sergei Shtylyov
2009-05-08 12:47 ` João Ramos
3 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-08 11:23 UTC (permalink / raw)
To: João Ramos
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
João Ramos wrote:
>>> Sergei: I've added the delays you suggested in the read/write
>>> procedures, accordingly to the delays specified in the processor's
>>> user manual for PIO Mode 4.
>> Why only for PIO mode 4 if you're claiming support for modes 0 thru 4?
> The patch currently supports only PIO Mode 4, as it is hardcoded into
> the CPU's IDE configuration register:
> writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
> ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
> And also in the ide_port_info struct:
> static struct ide_port_info ep93xx_ide_port_info = {
> .name = MODULE_NAME,
> .init_hwif = ep93xx_ide_init_hwif,
> .tp_ops = &ep93xx_ide_tp_ops,
> .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
> IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
> .pio_mask = ATA_PIO4,
Note that the ATA_PIO4 mask means support for PIO modes 0 thru 4, not
just PIO mode 4 (although in the absense of the set_pio_mode() method this
mask hardly matters at all)...
> };
> So you're saying I should support all PIO modes? If so, I would have to
For the safe functining of your IDE driver, yes; you should support at
least PIO0 as a safe bet. Think about CompactFlash -- the older cards don't
even support PIO4, only PIO2 maximum.
> make conditional code, checking perhaps a module param to sort which PIO
> mode to use.
> Also, the manual delays should also depend on the PIO mode selected...
Sure.
> It would be done either by a module param (defaulting to PIO Mode 4), or
> through a kernel configuration variable...
Why? We have set_pio_mode() method in 'struct ide_port_ops' for that.
The IDE core will select the best PIO mode for you based on the drive's
capabilities -- you just need to implement this method.
>>> +
>>> +/*
>>> + * EP93xx IDE PIO low-level hardware initialization routine
>>> + */
>>> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
>>> +{
>>> + unsigned long base = hwif->config_data;
>>> +
>>> + /* enforce reset state */
>>> + ep93xx_ide_clean_regs(base);
>>> +
>>> + /* set gpio port E, G and H for IDE */
>>> + ep93xx_ide_on_gpio(1);
>> Shouldn't this be done in the platform code instead?
> The idea is to make this driver loadable, as suggested earlier by Ryan
> and Hartley.
> The IDE pins are initially (and by default) set to GPIO function. If the
> IDE driver is registered, through specific platform code or by loading
> the module at runtime, then the IDE driver cares to configure the IDE
> pins for IDE function, returning them to GPIO function once the driver
> is unloaded.
I'm not sure you can just "return the pins to GPIO function" since they
will remain connected to the IDE port and will affect its state even being
in GPIO mode... I think you don't have a choice here: they are either always
belong to GPIO (if there's no IDE port) or always belong to IDE (if the IDE
port is present).
> [...]
>>> +static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
>>> +{
>>> + u32 reg;
>>> +
>>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
>>> + IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +
>>> + reg &= ~IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(70);
>>> +
>>> + while (!ep93xx_ide_check_iordy())
>>> + cpu_relax();
>>> +
>>> + reg |= IDECTRL_DIORN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +
>>> + return readl(IDE_REGISTER(IDEDATAIN));
>> Hey, how this even works (if the data doesn't get latched
>> somehow)?! You
>> should read the register right *before* the deassertion of -DIORx! The
>> minimum data hold time is only 5 ns and the data lines will be tristated
>> within 30 ns maximum...
> Will be fixed.
Again, I don't know, maybe the data register is indeed latched by the
controller at the rising edge of -DIOR... because this code most probably
wouldn't work otherwise. Please check the documentation, maybe it's illegal
to read the data before the deassertion of -DIOR. But at least doing it
after 25 ns delay looked too much fishy...
>>> +
>>> + reg &= ~IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(70);
>>> +
>>> + while (!ep93xx_ide_check_iordy())
>>> + cpu_relax();
>>> +
>>> + reg |= IDECTRL_DIOWN;
>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>> + ndelay(25);
>>> +}
>>> +
>> [...]
>> The same question: why we need both ep93xx_ide_write[bw]()?
> Same answer as before. Perhaps there's no need for those.
>> Since this is not a hotplug driver, you can save some memory on
>> making ep93xx_ide_probe() __init -- using platform_driver_probe() here
>> instead of platform_driver_register() and *not* initializing the
>> 'probe' field of the 'struct platform_driver'.
> I think Ryan and Hartley would like this driver to be
> loadable/unloadable at runtime, as I pointed out earlier in this mail.
So what? It'll remain [un]loadable...
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-07 15:33 ` João Ramos
@ 2009-05-08 12:04 ` Bartlomiej Zolnierkiewicz
2009-05-08 12:16 ` João Ramos
2009-05-08 17:28 ` João Ramos
0 siblings, 2 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 12:04 UTC (permalink / raw)
To: João Ramos
Cc: Alan Cox, Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon,
linux-arm-kernel, linux-ide
On Thursday 07 May 2009 17:33:20 João Ramos wrote:
> Alan Cox escreveu:
> >> So you're saying I should support all PIO modes? If so, I would have to
> >> make conditional code, checking perhaps a module param to sort which PIO
> >> mode to use.
> >>
> >
> > If you advertise PIO0-PIO4 as supported the core IDE code will do all the
> > work on figuring which modes are supported by the attached devices. You
> > just need to be able to set them.
> >
> > Alan
> >
>
> Ok, so I've been studying that (I was quite lost for a time, I confess,
> I'm not that much familiar with the IDE subsystem, so please bear with
> me ;-) ).
>
> So I need to set up a hook for 'set_pio_mode()', so that when the IDE
> subsystem detects a device and figures the most suitable PIO mode for
> the device, it will call the 'set_pio_mode' routine provided by the
> driver in order to configure the host controller for that PIO mode.
>
> This also means that my host controller driver should always default to
> PIO Mode 0, as the initial host controller setup that is carried out by
> the 'init_hwif' routine, allowing devices to be detected. Afterwards,
> the IDE subsystem detects the most suitable PIO mode and calls
> 'set_pio_mode' to change that configuration.
>
> Am I correct on this?
Yes! :)
There is still a room for improvement though -- it would be better to fix
IDE core to set PIO0 before probing devices for all host controllers.
Moreover it seems that doing it this way would allow us to remove ->init_hwif
method from this driver and do all necessary setup in ep93xx_ide_probe()
(this controller is a single port one so theoretically there shouldn't be
a need for having per-port ->init_hwif implementation).
> There's just only one issue; normally, I would setup the specific
> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
> you look further in the driver, those timings aren't defined through a
> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
> This means that in my low-level procedures for reading and writing, I
> need to have access to the timings (or the struct ide_timing)
> corresponding to the PIO mode selected, in order to use the correct delays.
>
> My question is: which is the best way to accomplish this? Declaring a
> global struct ide_timing variable pointer that always holds the correct
> ide_timing struct to the selected PIO mode? Or should I always check (in
> some manner) what is the current PIO mode and then select the adequate
> delays?
I think that the setting variable pointer in ->set_pio_mode method would
work best. Seems like the existing drive_data field of ide_drive_t is well
suited for this purpose (however it may be worth to convert it to 'void *'
type while we are it).
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 12:04 ` Bartlomiej Zolnierkiewicz
@ 2009-05-08 12:16 ` João Ramos
2009-05-08 12:40 ` Bartlomiej Zolnierkiewicz
2009-05-08 17:28 ` João Ramos
1 sibling, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-08 12:16 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz
Cc: Alan Cox, Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon,
linux-arm-kernel, linux-ide
Bartlomiej Zolnierkiewicz escreveu:
> On Thursday 07 May 2009 17:33:20 João Ramos wrote:
>
>> Alan Cox escreveu:
>>
>>>> So you're saying I should support all PIO modes? If so, I would have to
>>>> make conditional code, checking perhaps a module param to sort which PIO
>>>> mode to use.
>>>>
>>>>
>>> If you advertise PIO0-PIO4 as supported the core IDE code will do all the
>>> work on figuring which modes are supported by the attached devices. You
>>> just need to be able to set them.
>>>
>>> Alan
>>>
>>>
>> Ok, so I've been studying that (I was quite lost for a time, I confess,
>> I'm not that much familiar with the IDE subsystem, so please bear with
>> me ;-) ).
>>
>> So I need to set up a hook for 'set_pio_mode()', so that when the IDE
>> subsystem detects a device and figures the most suitable PIO mode for
>> the device, it will call the 'set_pio_mode' routine provided by the
>> driver in order to configure the host controller for that PIO mode.
>>
>> This also means that my host controller driver should always default to
>> PIO Mode 0, as the initial host controller setup that is carried out by
>> the 'init_hwif' routine, allowing devices to be detected. Afterwards,
>> the IDE subsystem detects the most suitable PIO mode and calls
>> 'set_pio_mode' to change that configuration.
>>
>> Am I correct on this?
>>
>
> Yes! :)
>
> There is still a room for improvement though -- it would be better to fix
> IDE core to set PIO0 before probing devices for all host controllers.
>
> Moreover it seems that doing it this way would allow us to remove ->init_hwif
> method from this driver and do all necessary setup in ep93xx_ide_probe()
> (this controller is a single port one so theoretically there shouldn't be
> a need for having per-port ->init_hwif implementation).
>
So, I should remove the 'init_hwif' hook, and all the host controller
setup would be made in the driver's probe method, correct?
>
>> There's just only one issue; normally, I would setup the specific
>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
>> you look further in the driver, those timings aren't defined through a
>> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
>> This means that in my low-level procedures for reading and writing, I
>> need to have access to the timings (or the struct ide_timing)
>> corresponding to the PIO mode selected, in order to use the correct delays.
>>
>> My question is: which is the best way to accomplish this? Declaring a
>> global struct ide_timing variable pointer that always holds the correct
>> ide_timing struct to the selected PIO mode? Or should I always check (in
>> some manner) what is the current PIO mode and then select the adequate
>> delays?
>>
>
> I think that the setting variable pointer in ->set_pio_mode method would
> work best. Seems like the existing drive_data field of ide_drive_t is well
> suited for this purpose (however it may be worth to convert it to 'void *'
> type while we are it).
>
Ok, so I will set an struct ide_timing pointer in the 'set_pio_mode'
method, which will later be used by my low-level read/write procedures
to check the adequate IDE timings.
> Thanks,
> Bart
>
>
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 12:16 ` João Ramos
@ 2009-05-08 12:40 ` Bartlomiej Zolnierkiewicz
2009-05-08 13:30 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 12:40 UTC (permalink / raw)
To: João Ramos
Cc: Alan Cox, Sergei Shtylyov, H Hartley Sweeten, Ryan Mallon,
linux-arm-kernel, linux-ide
On Friday 08 May 2009 14:16:55 João Ramos wrote:
> Bartlomiej Zolnierkiewicz escreveu:
> > On Thursday 07 May 2009 17:33:20 João Ramos wrote:
> >
> >> Alan Cox escreveu:
> >>
> >>>> So you're saying I should support all PIO modes? If so, I would have to
> >>>> make conditional code, checking perhaps a module param to sort which PIO
> >>>> mode to use.
> >>>>
> >>>>
> >>> If you advertise PIO0-PIO4 as supported the core IDE code will do all the
> >>> work on figuring which modes are supported by the attached devices. You
> >>> just need to be able to set them.
> >>>
> >>> Alan
> >>>
> >>>
> >> Ok, so I've been studying that (I was quite lost for a time, I confess,
> >> I'm not that much familiar with the IDE subsystem, so please bear with
> >> me ;-) ).
> >>
> >> So I need to set up a hook for 'set_pio_mode()', so that when the IDE
> >> subsystem detects a device and figures the most suitable PIO mode for
> >> the device, it will call the 'set_pio_mode' routine provided by the
> >> driver in order to configure the host controller for that PIO mode.
> >>
> >> This also means that my host controller driver should always default to
> >> PIO Mode 0, as the initial host controller setup that is carried out by
> >> the 'init_hwif' routine, allowing devices to be detected. Afterwards,
> >> the IDE subsystem detects the most suitable PIO mode and calls
> >> 'set_pio_mode' to change that configuration.
> >>
> >> Am I correct on this?
> >>
> >
> > Yes! :)
> >
> > There is still a room for improvement though -- it would be better to fix
> > IDE core to set PIO0 before probing devices for all host controllers.
> >
> > Moreover it seems that doing it this way would allow us to remove ->init_hwif
> > method from this driver and do all necessary setup in ep93xx_ide_probe()
> > (this controller is a single port one so theoretically there shouldn't be
> > a need for having per-port ->init_hwif implementation).
> >
>
> So, I should remove the 'init_hwif' hook, and all the host controller
> setup would be made in the driver's probe method, correct?
Yes, that would be preferred (of course given that you fix IDE core to do
initial PIO0 setup first).
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 11:23 ` Sergei Shtylyov
@ 2009-05-08 12:47 ` João Ramos
0 siblings, 0 replies; 59+ messages in thread
From: João Ramos @ 2009-05-08 12:47 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: H Hartley Sweeten, Ryan Mallon, linux-arm-kernel, linux-ide
Sergei Shtylyov escreveu:
> João Ramos wrote:
>
>>>> Sergei: I've added the delays you suggested in the read/write
>>>> procedures, accordingly to the delays specified in the processor's
>>>> user manual for PIO Mode 4.
>
>>> Why only for PIO mode 4 if you're claiming support for modes 0
>>> thru 4?
>
>> The patch currently supports only PIO Mode 4, as it is hardcoded into
>> the CPU's IDE configuration register:
>
>> writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_4 |
>> ((1 << 8) & IDECFG_WST), IDE_REGISTER(IDECFG));
>
>> And also in the ide_port_info struct:
>
>> static struct ide_port_info ep93xx_ide_port_info = {
>> .name = MODULE_NAME,
>> .init_hwif = ep93xx_ide_init_hwif,
>> .tp_ops = &ep93xx_ide_tp_ops,
>> .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
>> IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
>> .pio_mask = ATA_PIO4,
>
> Note that the ATA_PIO4 mask means support for PIO modes 0 thru 4,
> not just PIO mode 4 (although in the absense of the set_pio_mode()
> method this mask hardly matters at all)...
I've already seen that, as stated in my earlier mails. Now I've got the
full picture about the IDE subsystem ;-) .
>
>> };
>
>> So you're saying I should support all PIO modes? If so, I would have to
>
> For the safe functining of your IDE driver, yes; you should support
> at least PIO0 as a safe bet. Think about CompactFlash -- the older
> cards don't even support PIO4, only PIO2 maximum.
I've already done that, implementing the 'pio_set_mode' method.
>
>> make conditional code, checking perhaps a module param to sort which
>> PIO mode to use.
>
>
>> Also, the manual delays should also depend on the PIO mode selected...
>
> Sure.
As discussed with Bartlomiej Zolnierkiewicz, I will use struct
ide_timing to figure out the correct timings for each PIO mode, and use
the adequate delays in my read/write functions.
>
>> It would be done either by a module param (defaulting to PIO Mode 4),
>> or through a kernel configuration variable...
>
> Why? We have set_pio_mode() method in 'struct ide_port_ops' for that.
> The IDE core will select the best PIO mode for you based on the
> drive's capabilities -- you just need to implement this method.
Now I got it ;-) . Sorry, I'm quite inexperient in the IDE subsystem
(and in kernel programming) so it took me a while to figure that out.
But I've done that and I've already tested it, and it works.
I am just finishing some other fixes and I will submit a new patch soon.
>
>>>> +
>>>> +/*
>>>> + * EP93xx IDE PIO low-level hardware initialization routine
>>>> + */
>>>> +static void ep93xx_ide_init_hwif(ide_hwif_t *hwif)
>>>> +{
>>>> + unsigned long base = hwif->config_data;
>>>> +
>>>> + /* enforce reset state */
>>>> + ep93xx_ide_clean_regs(base);
>>>> +
>>>> + /* set gpio port E, G and H for IDE */
>>>> + ep93xx_ide_on_gpio(1);
>
>>> Shouldn't this be done in the platform code instead?
>
>> The idea is to make this driver loadable, as suggested earlier by
>> Ryan and Hartley.
>> The IDE pins are initially (and by default) set to GPIO function. If
>> the IDE driver is registered, through specific platform code or by
>> loading the module at runtime, then the IDE driver cares to configure
>> the IDE pins for IDE function, returning them to GPIO function once
>> the driver is unloaded.
>
> I'm not sure you can just "return the pins to GPIO function" since
> they will remain connected to the IDE port and will affect its state
> even being in GPIO mode... I think you don't have a choice here: they
> are either always belong to GPIO (if there's no IDE port) or always
> belong to IDE (if the IDE port is present).
The point is that user's may select (or not) the IDE driver.
Some patches are already in the arm-linux mailing list for this; by
default all pins are configured for GPIO function, and they remain that
way if no IDE option is selected.
If the IDE driver is selected and compiled (and running), the driver
will then care to claim the IDE pins to the IDE function, through a
machine-specific core call (such as ep93xx_ide_on_gpio).
This approach works for both systems with and without IDE driver selected.
>
>> [...]
>
>>>> +static u8 ep93xx_ide_readb(unsigned long base, unsigned long addr)
>>>> +{
>>>> + u32 reg;
>>>> +
>>>> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) |
>>>> IDECTRL_DIORN |
>>>> + IDECTRL_DIOWN;
>>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>>> + ndelay(25);
>>>> +
>>>> + reg &= ~IDECTRL_DIORN;
>>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>>> + ndelay(70);
>>>> +
>>>> + while (!ep93xx_ide_check_iordy())
>>>> + cpu_relax();
>>>> +
>>>> + reg |= IDECTRL_DIORN;
>>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>>> + ndelay(25);
>>>> +
>>>> + return readl(IDE_REGISTER(IDEDATAIN));
>
>>> Hey, how this even works (if the data doesn't get latched
>>> somehow)?! You
>>> should read the register right *before* the deassertion of -DIORx! The
>>> minimum data hold time is only 5 ns and the data lines will be
>>> tristated
>>> within 30 ns maximum...
>
>> Will be fixed.
>
> Again, I don't know, maybe the data register is indeed latched by
> the controller at the rising edge of -DIOR... because this code most
> probably wouldn't work otherwise. Please check the documentation,
> maybe it's illegal to read the data before the deassertion of -DIOR.
> But at least doing it after 25 ns delay looked too much fishy...
Please check my earliest emails; i've described the behaviour of the IDE
controller.
>
>>>> +
>>>> + reg &= ~IDECTRL_DIOWN;
>>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>>> + ndelay(70);
>>>> +
>>>> + while (!ep93xx_ide_check_iordy())
>>>> + cpu_relax();
>>>> +
>>>> + reg |= IDECTRL_DIOWN;
>>>> + writel(reg, IDE_REGISTER(IDECTRL));
>>>> + ndelay(25);
>>>> +}
>>>> +
>
>>> [...]
>
>>> The same question: why we need both ep93xx_ide_write[bw]()?
>
>> Same answer as before. Perhaps there's no need for those.
>
>>> Since this is not a hotplug driver, you can save some memory on
>>> making ep93xx_ide_probe() __init -- using platform_driver_probe()
>>> here instead of platform_driver_register() and *not* initializing
>>> the 'probe' field of the 'struct platform_driver'.
>
>> I think Ryan and Hartley would like this driver to be
>> loadable/unloadable at runtime, as I pointed out earlier in this mail.
>
> So what? It'll remain [un]loadable...
I've also came to that conclusion (now).
Gosh, I really need to study harder my "Linux Device Drivers" book ;-) .
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 12:40 ` Bartlomiej Zolnierkiewicz
@ 2009-05-08 13:30 ` Sergei Shtylyov
2009-05-08 14:09 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-08 13:30 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz
Cc: João Ramos, Alan Cox, H Hartley Sweeten, Ryan Mallon,
linux-arm-kernel, linux-ide
Bartlomiej Zolnierkiewicz wrote:
>>>>>>So you're saying I should support all PIO modes? If so, I would have to
>>>>>>make conditional code, checking perhaps a module param to sort which PIO
>>>>>>mode to use.
>>>>>If you advertise PIO0-PIO4 as supported the core IDE code will do all the
>>>>>work on figuring which modes are supported by the attached devices. You
>>>>>just need to be able to set them.
>>>>>Alan
>>>>Ok, so I've been studying that (I was quite lost for a time, I confess,
>>>>I'm not that much familiar with the IDE subsystem, so please bear with
>>>>me ;-) ).
>>>>So I need to set up a hook for 'set_pio_mode()', so that when the IDE
>>>>subsystem detects a device and figures the most suitable PIO mode for
>>>>the device, it will call the 'set_pio_mode' routine provided by the
>>>>driver in order to configure the host controller for that PIO mode.
>>>>This also means that my host controller driver should always default to
>>>>PIO Mode 0, as the initial host controller setup that is carried out by
>>>>the 'init_hwif' routine, allowing devices to be detected. Afterwards,
>>>>the IDE subsystem detects the most suitable PIO mode and calls
>>>>'set_pio_mode' to change that configuration.
>>>>Am I correct on this?
>>>Yes! :)
>>>There is still a room for improvement though -- it would be better to fix
>>>IDE core to set PIO0 before probing devices for all host controllers.
>>>Moreover it seems that doing it this way would allow us to remove ->init_hwif
>>>method from this driver and do all necessary setup in ep93xx_ide_probe()
>>>(this controller is a single port one so theoretically there shouldn't be
>>>a need for having per-port ->init_hwif implementation).
>>So, I should remove the 'init_hwif' hook, and all the host controller
>>setup would be made in the driver's probe method, correct?
> Yes, that would be preferred (of course given that you fix IDE core to do
> initial PIO0 setup first).
Er, it's not that easy... Think about older CompactFlash cards (and
ancient drives of course) that don't support setting explicit PIO modes
(only the default one) since they don't support IORDY... and also
ide_config_drive_speed() expects drive->id to be already filled in... So I
guess you mean just calling set_pio_mode() prior to probing -- without
setting the drive's own mode?
> Thanks,
> Bart
WBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 13:30 ` Sergei Shtylyov
@ 2009-05-08 14:09 ` Bartlomiej Zolnierkiewicz
0 siblings, 0 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 14:09 UTC (permalink / raw)
To: Sergei Shtylyov
Cc: João Ramos, Alan Cox, H Hartley Sweeten, Ryan Mallon,
linux-arm-kernel, linux-ide
On Friday 08 May 2009 15:30:00 Sergei Shtylyov wrote:
> Bartlomiej Zolnierkiewicz wrote:
>
> >>>>>>So you're saying I should support all PIO modes? If so, I would have to
> >>>>>>make conditional code, checking perhaps a module param to sort which PIO
> >>>>>>mode to use.
>
> >>>>>If you advertise PIO0-PIO4 as supported the core IDE code will do all the
> >>>>>work on figuring which modes are supported by the attached devices. You
> >>>>>just need to be able to set them.
>
> >>>>>Alan
>
> >>>>Ok, so I've been studying that (I was quite lost for a time, I confess,
> >>>>I'm not that much familiar with the IDE subsystem, so please bear with
> >>>>me ;-) ).
>
> >>>>So I need to set up a hook for 'set_pio_mode()', so that when the IDE
> >>>>subsystem detects a device and figures the most suitable PIO mode for
> >>>>the device, it will call the 'set_pio_mode' routine provided by the
> >>>>driver in order to configure the host controller for that PIO mode.
>
> >>>>This also means that my host controller driver should always default to
> >>>>PIO Mode 0, as the initial host controller setup that is carried out by
> >>>>the 'init_hwif' routine, allowing devices to be detected. Afterwards,
> >>>>the IDE subsystem detects the most suitable PIO mode and calls
> >>>>'set_pio_mode' to change that configuration.
>
> >>>>Am I correct on this?
>
> >>>Yes! :)
>
> >>>There is still a room for improvement though -- it would be better to fix
> >>>IDE core to set PIO0 before probing devices for all host controllers.
>
> >>>Moreover it seems that doing it this way would allow us to remove ->init_hwif
> >>>method from this driver and do all necessary setup in ep93xx_ide_probe()
> >>>(this controller is a single port one so theoretically there shouldn't be
> >>>a need for having per-port ->init_hwif implementation).
>
> >>So, I should remove the 'init_hwif' hook, and all the host controller
> >>setup would be made in the driver's probe method, correct?
>
> > Yes, that would be preferred (of course given that you fix IDE core to do
> > initial PIO0 setup first).
>
> Er, it's not that easy... Think about older CompactFlash cards (and
> ancient drives of course) that don't support setting explicit PIO modes
> (only the default one) since they don't support IORDY... and also
> ide_config_drive_speed() expects drive->id to be already filled in... So I
> guess you mean just calling set_pio_mode() prior to probing -- without
> setting the drive's own mode?
Yes, I mean setting PIO0 _only_ on host.
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 12:04 ` Bartlomiej Zolnierkiewicz
2009-05-08 12:16 ` João Ramos
@ 2009-05-08 17:28 ` João Ramos
2009-05-08 18:02 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:01 ` João Ramos
1 sibling, 2 replies; 59+ messages in thread
From: João Ramos @ 2009-05-08 17:28 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
>
> Yes! :)
>
> There is still a room for improvement though -- it would be better to fix
> IDE core to set PIO0 before probing devices for all host controllers.
>
> Moreover it seems that doing it this way would allow us to remove ->init_hwif
> method from this driver and do all necessary setup in ep93xx_ide_probe()
> (this controller is a single port one so theoretically there shouldn't be
> a need for having per-port ->init_hwif implementation).
>
So after all this discussion ;-) , my driver will have no 'init_hwif'
method, and the setup code will be on 'ep93xx_ide_probe', which will
configure entirely the IDE host controller.
Moreover, this initial configuration will setup the controller to work
at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
the controller will configure itself according to the PIO mode reported
by the IDE core.
Can I proceed this way?
>
>> There's just only one issue; normally, I would setup the specific
>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
>> you look further in the driver, those timings aren't defined through a
>> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
>> This means that in my low-level procedures for reading and writing, I
>> need to have access to the timings (or the struct ide_timing)
>> corresponding to the PIO mode selected, in order to use the correct delays.
>>
>> My question is: which is the best way to accomplish this? Declaring a
>> global struct ide_timing variable pointer that always holds the correct
>> ide_timing struct to the selected PIO mode? Or should I always check (in
>> some manner) what is the current PIO mode and then select the adequate
>> delays?
>>
>
> I think that the setting variable pointer in ->set_pio_mode method would
> work best. Seems like the existing drive_data field of ide_drive_t is well
> suited for this purpose (however it may be worth to convert it to 'void *'
> type while we are it).
>
Did you mean 'drive_data' field, or 'driver_data' field?
'drive_data' field is an unsigned int value; I guess you meant
'driver_data' field as it is a (void *) field, so I can define it as a
pointer to the correct 'struct ide_timing'.
Regards,
João
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 17:28 ` João Ramos
@ 2009-05-08 18:02 ` Bartlomiej Zolnierkiewicz
2009-05-08 18:16 ` João Ramos
2009-05-12 16:01 ` João Ramos
1 sibling, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 18:02 UTC (permalink / raw)
To: João Ramos; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
On Friday 08 May 2009 19:28:22 João Ramos wrote:
>
> >
> > Yes! :)
> >
> > There is still a room for improvement though -- it would be better to fix
> > IDE core to set PIO0 before probing devices for all host controllers.
> >
> > Moreover it seems that doing it this way would allow us to remove ->init_hwif
> > method from this driver and do all necessary setup in ep93xx_ide_probe()
> > (this controller is a single port one so theoretically there shouldn't be
> > a need for having per-port ->init_hwif implementation).
> >
>
> So after all this discussion ;-) , my driver will have no 'init_hwif'
> method, and the setup code will be on 'ep93xx_ide_probe', which will
> configure entirely the IDE host controller.
> Moreover, this initial configuration will setup the controller to work
> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
> the controller will configure itself according to the PIO mode reported
> by the IDE core.
>
> Can I proceed this way?
Well, yes. Though I hoped that you would at least give a try to fixing
IDE core to program PIO0 initially for all host drivers that implement
->set_pio_mode method...
> >
> >> There's just only one issue; normally, I would setup the specific
> >> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
> >> you look further in the driver, those timings aren't defined through a
> >> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
> >> This means that in my low-level procedures for reading and writing, I
> >> need to have access to the timings (or the struct ide_timing)
> >> corresponding to the PIO mode selected, in order to use the correct delays.
> >>
> >> My question is: which is the best way to accomplish this? Declaring a
> >> global struct ide_timing variable pointer that always holds the correct
> >> ide_timing struct to the selected PIO mode? Or should I always check (in
> >> some manner) what is the current PIO mode and then select the adequate
> >> delays?
> >>
> >
> > I think that the setting variable pointer in ->set_pio_mode method would
> > work best. Seems like the existing drive_data field of ide_drive_t is well
> > suited for this purpose (however it may be worth to convert it to 'void *'
> > type while we are it).
> >
>
> Did you mean 'drive_data' field, or 'driver_data' field?
> 'drive_data' field is an unsigned int value; I guess you meant
> 'driver_data' field as it is a (void *) field, so I can define it as a
> pointer to the correct 'struct ide_timing'.
That is why I hinted that you may need to convert 'drive_data' to
'void *' type first. You may also try to use 'driver_data' instead
but you will discover rather quickly that you shouldn't do this... ;)
'driver_data' is for use by IDE core and IDE device drivers.
'drive_data' is for use by IDE host drivers.
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 18:02 ` Bartlomiej Zolnierkiewicz
@ 2009-05-08 18:16 ` João Ramos
2009-05-08 18:55 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-08 18:16 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
Bartlomiej Zolnierkiewicz escreveu:
> On Friday 08 May 2009 19:28:22 João Ramos wrote:
>
>>> Yes! :)
>>>
>>> There is still a room for improvement though -- it would be better to fix
>>> IDE core to set PIO0 before probing devices for all host controllers.
>>>
>>> Moreover it seems that doing it this way would allow us to remove ->init_hwif
>>> method from this driver and do all necessary setup in ep93xx_ide_probe()
>>> (this controller is a single port one so theoretically there shouldn't be
>>> a need for having per-port ->init_hwif implementation).
>>>
>>>
>> So after all this discussion ;-) , my driver will have no 'init_hwif'
>> method, and the setup code will be on 'ep93xx_ide_probe', which will
>> configure entirely the IDE host controller.
>> Moreover, this initial configuration will setup the controller to work
>> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
>> the controller will configure itself according to the PIO mode reported
>> by the IDE core.
>>
>> Can I proceed this way?
>>
>
> Well, yes. Though I hoped that you would at least give a try to fixing
> IDE core to program PIO0 initially for all host drivers that implement
> ->set_pio_mode method...
>
Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
Maybe with a little help, but I can try. You mean, when the host driver
is registered (ide_host_register, or ide_host_add that later calls
ide_host_register), maybe in the 'ide_init_port' method (sorry, I need
some guidance here...) check if the 'set_pio_mode' method is
implemented, and after initializing each port (d->init_hwif(hwif))
default it to PIO Mode 0, calling set_pio_mode method.
Is this correct? Sorry, has I stated earlier, I'm wasn't familiar with
the IDE susbsystem untill I wrote this patch; but I'm willing to
contribute in any way I can, so please, bear with me on this :-) .
>
>>>
>>>
>>>> There's just only one issue; normally, I would setup the specific
>>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
>>>> you look further in the driver, those timings aren't defined through a
>>>> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
>>>> This means that in my low-level procedures for reading and writing, I
>>>> need to have access to the timings (or the struct ide_timing)
>>>> corresponding to the PIO mode selected, in order to use the correct delays.
>>>>
>>>> My question is: which is the best way to accomplish this? Declaring a
>>>> global struct ide_timing variable pointer that always holds the correct
>>>> ide_timing struct to the selected PIO mode? Or should I always check (in
>>>> some manner) what is the current PIO mode and then select the adequate
>>>> delays?
>>>>
>>>>
>>> I think that the setting variable pointer in ->set_pio_mode method would
>>> work best. Seems like the existing drive_data field of ide_drive_t is well
>>> suited for this purpose (however it may be worth to convert it to 'void *'
>>> type while we are it).
>>>
>>>
>> Did you mean 'drive_data' field, or 'driver_data' field?
>> 'drive_data' field is an unsigned int value; I guess you meant
>> 'driver_data' field as it is a (void *) field, so I can define it as a
>> pointer to the correct 'struct ide_timing'.
>>
>
> That is why I hinted that you may need to convert 'drive_data' to
> 'void *' type first. You may also try to use 'driver_data' instead
> but you will discover rather quickly that you shouldn't do this... ;)
>
> 'driver_data' is for use by IDE core and IDE device drivers.
>
> 'drive_data' is for use by IDE host drivers.
>
And this conversion is made by my driver code, or should I fix directly
in the ide_drive_t structure?
Regards,
João Ramos
> Thanks,
> Bart
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 18:16 ` João Ramos
@ 2009-05-08 18:55 ` Bartlomiej Zolnierkiewicz
2009-05-08 20:24 ` joao.ramos
2009-05-11 13:20 ` João Ramos
0 siblings, 2 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 18:55 UTC (permalink / raw)
To: João Ramos; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
On Friday 08 May 2009 20:16:56 João Ramos wrote:
> Bartlomiej Zolnierkiewicz escreveu:
> > On Friday 08 May 2009 19:28:22 João Ramos wrote:
> >
> >>> Yes! :)
> >>>
> >>> There is still a room for improvement though -- it would be better to fix
> >>> IDE core to set PIO0 before probing devices for all host controllers.
> >>>
> >>> Moreover it seems that doing it this way would allow us to remove ->init_hwif
> >>> method from this driver and do all necessary setup in ep93xx_ide_probe()
> >>> (this controller is a single port one so theoretically there shouldn't be
> >>> a need for having per-port ->init_hwif implementation).
> >>>
> >>>
> >> So after all this discussion ;-) , my driver will have no 'init_hwif'
> >> method, and the setup code will be on 'ep93xx_ide_probe', which will
> >> configure entirely the IDE host controller.
> >> Moreover, this initial configuration will setup the controller to work
> >> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
> >> the controller will configure itself according to the PIO mode reported
> >> by the IDE core.
> >>
> >> Can I proceed this way?
> >>
> >
> > Well, yes. Though I hoped that you would at least give a try to fixing
> > IDE core to program PIO0 initially for all host drivers that implement
> > ->set_pio_mode method...
> >
>
> Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
> Maybe with a little help, but I can try. You mean, when the host driver
> is registered (ide_host_register, or ide_host_add that later calls
> ide_host_register), maybe in the 'ide_init_port' method (sorry, I need
> some guidance here...) check if the 'set_pio_mode' method is
> implemented, and after initializing each port (d->init_hwif(hwif))
> default it to PIO Mode 0, calling set_pio_mode method.
ide_init_port() seems OK but I think that ide_port_init_devices()
[it is called after ide_init_port()] would be a bit safer and more
flexible (some host drivers may also require special ->init_dev
setup first) and the check for ->set_pio_mode method presence can
be done just before actually using the method, i.e.
const struct ide_port_ops *port_ops = hwif->port_ops;
if (port_ops && port_ops->set_pio_mode)
port_ops->set_pio_mode(...)
> Is this correct? Sorry, has I stated earlier, I'm wasn't familiar with
> the IDE susbsystem untill I wrote this patch; but I'm willing to
> contribute in any way I can, so please, bear with me on this :-) .
Sure, nobody starts from the expert level and not all maintainers are
into "prove the maintainer wrong" elitist's idiocy. ;)
> >
> >>>
> >>>
> >>>> There's just only one issue; normally, I would setup the specific
> >>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
> >>>> you look further in the driver, those timings aren't defined through a
> >>>> memory controller but instead manually enforced by 'ndelay' calls (arghhh).
> >>>> This means that in my low-level procedures for reading and writing, I
> >>>> need to have access to the timings (or the struct ide_timing)
> >>>> corresponding to the PIO mode selected, in order to use the correct delays.
> >>>>
> >>>> My question is: which is the best way to accomplish this? Declaring a
> >>>> global struct ide_timing variable pointer that always holds the correct
> >>>> ide_timing struct to the selected PIO mode? Or should I always check (in
> >>>> some manner) what is the current PIO mode and then select the adequate
> >>>> delays?
> >>>>
> >>>>
> >>> I think that the setting variable pointer in ->set_pio_mode method would
> >>> work best. Seems like the existing drive_data field of ide_drive_t is well
> >>> suited for this purpose (however it may be worth to convert it to 'void *'
> >>> type while we are it).
> >>>
> >>>
> >> Did you mean 'drive_data' field, or 'driver_data' field?
> >> 'drive_data' field is an unsigned int value; I guess you meant
> >> 'driver_data' field as it is a (void *) field, so I can define it as a
> >> pointer to the correct 'struct ide_timing'.
> >>
> >
> > That is why I hinted that you may need to convert 'drive_data' to
> > 'void *' type first. You may also try to use 'driver_data' instead
> > but you will discover rather quickly that you shouldn't do this... ;)
> >
> > 'driver_data' is for use by IDE core and IDE device drivers.
> >
> > 'drive_data' is for use by IDE host drivers.
> >
>
> And this conversion is made by my driver code, or should I fix directly
> in the ide_drive_t structure?
The latter -- ide_drive_t is the place needing fixing.
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 18:55 ` Bartlomiej Zolnierkiewicz
@ 2009-05-08 20:24 ` joao.ramos
2009-05-08 21:01 ` Sergei Shtylyov
2009-05-11 13:20 ` João Ramos
1 sibling, 1 reply; 59+ messages in thread
From: joao.ramos @ 2009-05-08 20:24 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
Quoting Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>:
> On Friday 08 May 2009 20:16:56 João Ramos wrote:
>> Bartlomiej Zolnierkiewicz escreveu:
>> > On Friday 08 May 2009 19:28:22 João Ramos wrote:
>> >
>> >>> Yes! :)
>> >>>
>> >>> There is still a room for improvement though -- it would be
>> better to fix
>> >>> IDE core to set PIO0 before probing devices for all host controllers.
>> >>>
>> >>> Moreover it seems that doing it this way would allow us to
>> remove ->init_hwif
>> >>> method from this driver and do all necessary setup in ep93xx_ide_probe()
>> >>> (this controller is a single port one so theoretically there
>> shouldn't be
>> >>> a need for having per-port ->init_hwif implementation).
>> >>>
>> >>>
>> >> So after all this discussion ;-) , my driver will have no 'init_hwif'
>> >> method, and the setup code will be on 'ep93xx_ide_probe', which will
>> >> configure entirely the IDE host controller.
>> >> Moreover, this initial configuration will setup the controller to work
>> >> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
>> >> the controller will configure itself according to the PIO mode reported
>> >> by the IDE core.
>> >>
>> >> Can I proceed this way?
>> >>
>> >
>> > Well, yes. Though I hoped that you would at least give a try to fixing
>> > IDE core to program PIO0 initially for all host drivers that implement
>> > ->set_pio_mode method...
>> >
>>
>> Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
>> Maybe with a little help, but I can try. You mean, when the host driver
>> is registered (ide_host_register, or ide_host_add that later calls
>> ide_host_register), maybe in the 'ide_init_port' method (sorry, I need
>> some guidance here...) check if the 'set_pio_mode' method is
>> implemented, and after initializing each port (d->init_hwif(hwif))
>> default it to PIO Mode 0, calling set_pio_mode method.
>
> ide_init_port() seems OK but I think that ide_port_init_devices()
> [it is called after ide_init_port()] would be a bit safer and more
> flexible (some host drivers may also require special ->init_dev
> setup first) and the check for ->set_pio_mode method presence can
> be done just before actually using the method, i.e.
>
> const struct ide_port_ops *port_ops = hwif->port_ops;
>
> if (port_ops && port_ops->set_pio_mode)
> port_ops->set_pio_mode(...)
Ok. I'll will give that a try and I'll report back with a proposed
patch for it.
>
>> Is this correct? Sorry, has I stated earlier, I'm wasn't familiar with
>> the IDE susbsystem untill I wrote this patch; but I'm willing to
>> contribute in any way I can, so please, bear with me on this :-) .
>
> Sure, nobody starts from the expert level and not all maintainers are
> into "prove the maintainer wrong" elitist's idiocy. ;)
>
>> >
>> >>>
>> >>>
>> >>>> There's just only one issue; normally, I would setup the specific
>> >>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However, if
>> >>>> you look further in the driver, those timings aren't defined through a
>> >>>> memory controller but instead manually enforced by 'ndelay'
>> calls (arghhh).
>> >>>> This means that in my low-level procedures for reading and writing, I
>> >>>> need to have access to the timings (or the struct ide_timing)
>> >>>> corresponding to the PIO mode selected, in order to use the
>> correct delays.
>> >>>>
>> >>>> My question is: which is the best way to accomplish this? Declaring a
>> >>>> global struct ide_timing variable pointer that always holds the correct
>> >>>> ide_timing struct to the selected PIO mode? Or should I always
>> check (in
>> >>>> some manner) what is the current PIO mode and then select the adequate
>> >>>> delays?
>> >>>>
>> >>>>
>> >>> I think that the setting variable pointer in ->set_pio_mode method would
>> >>> work best. Seems like the existing drive_data field of
>> ide_drive_t is well
>> >>> suited for this purpose (however it may be worth to convert it
>> to 'void *'
>> >>> type while we are it).
>> >>>
>> >>>
>> >> Did you mean 'drive_data' field, or 'driver_data' field?
>> >> 'drive_data' field is an unsigned int value; I guess you meant
>> >> 'driver_data' field as it is a (void *) field, so I can define it as a
>> >> pointer to the correct 'struct ide_timing'.
>> >>
>> >
>> > That is why I hinted that you may need to convert 'drive_data' to
>> > 'void *' type first. You may also try to use 'driver_data' instead
>> > but you will discover rather quickly that you shouldn't do this... ;)
>> >
>> > 'driver_data' is for use by IDE core and IDE device drivers.
>> >
>> > 'drive_data' is for use by IDE host drivers.
>> >
>>
>> And this conversion is made by my driver code, or should I fix directly
>> in the ide_drive_t structure?
>
> The latter -- ide_drive_t is the place needing fixing.
Same: I will fix that and propose a patch.
Regards,
João Ramos
>
> Thanks,
> Bart
>
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 20:24 ` joao.ramos
@ 2009-05-08 21:01 ` Sergei Shtylyov
2009-05-08 22:07 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-08 21:01 UTC (permalink / raw)
To: joao.ramos; +Cc: Bartlomiej Zolnierkiewicz, Alan Cox, linux-ide
Hello.
joao.ramos@inov.pt wrote:
>>> Is this correct? Sorry, has I stated earlier, I'm wasn't familiar with
>>> the IDE susbsystem untill I wrote this patch; but I'm willing to
>>> contribute in any way I can, so please, bear with me on this :-) .
>>
>> Sure, nobody starts from the expert level and not all maintainers are
>> into "prove the maintainer wrong" elitist's idiocy. ;)
>>
>>> >
>>> >>>
>>> >>>
>>> >>>> There's just only one issue; normally, I would setup the specific
>>> >>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook.
>>> However, if
>>> >>>> you look further in the driver, those timings aren't defined
>>> through a
>>> >>>> memory controller but instead manually enforced by 'ndelay'
>>> calls (arghhh).
>>> >>>> This means that in my low-level procedures for reading and
>>> writing, I
>>> >>>> need to have access to the timings (or the struct ide_timing)
>>> >>>> corresponding to the PIO mode selected, in order to use the
>>> correct delays.
>>> >>>>
>>> >>>> My question is: which is the best way to accomplish this?
>>> Declaring a
>>> >>>> global struct ide_timing variable pointer that always holds the
>>> correct
>>> >>>> ide_timing struct to the selected PIO mode? Or should I always
>>> check (in
>>> >>>> some manner) what is the current PIO mode and then select the
>>> adequate
>>> >>>> delays?
>>> >>>>
>>> >>>>
>>> >>> I think that the setting variable pointer in ->set_pio_mode
>>> method would
>>> >>> work best. Seems like the existing drive_data field of
>>> ide_drive_t is well
>>> >>> suited for this purpose (however it may be worth to convert it
>>> to 'void *'
>>> >>> type while we are it).
>>> >>>
>>> >>>
>>> >> Did you mean 'drive_data' field, or 'driver_data' field?
>>> >> 'drive_data' field is an unsigned int value; I guess you meant
>>> >> 'driver_data' field as it is a (void *) field, so I can define it
>>> as a
>>> >> pointer to the correct 'struct ide_timing'.
>>> >>
>>> >
>>> > That is why I hinted that you may need to convert 'drive_data' to
>>> > 'void *' type first. You may also try to use 'driver_data' instead
>>> > but you will discover rather quickly that you shouldn't do this... ;)
>>> >
>>> > 'driver_data' is for use by IDE core and IDE device drivers.
>>> >
>>> > 'drive_data' is for use by IDE host drivers.
>>> >
>>>
>>> And this conversion is made by my driver code, or should I fix directly
>>> in the ide_drive_t structure?
>>
>> The latter -- ide_drive_t is the place needing fixing.
>
>
> Same: I will fix that and propose a patch.
Hey, don't do this please! This is actually a bad idea as some
drivers (e.g. sl82c105) use drive_data as an integer entity. Bart, stop
giving such advices please. :-)
> Regards,
> João Ramos
>
>> Thanks,
>> Bart
WBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 21:01 ` Sergei Shtylyov
@ 2009-05-08 22:07 ` Bartlomiej Zolnierkiewicz
2009-05-11 11:10 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-08 22:07 UTC (permalink / raw)
To: Sergei Shtylyov; +Cc: joao.ramos, Alan Cox, linux-ide
On Friday 08 May 2009 23:01:13 Sergei Shtylyov wrote:
> Hello.
>
> joao.ramos@inov.pt wrote:
>
> >>> Is this correct? Sorry, has I stated earlier, I'm wasn't familiar with
> >>> the IDE susbsystem untill I wrote this patch; but I'm willing to
> >>> contribute in any way I can, so please, bear with me on this :-) .
> >>
> >> Sure, nobody starts from the expert level and not all maintainers are
> >> into "prove the maintainer wrong" elitist's idiocy. ;)
> >>
> >>> >
> >>> >>>
> >>> >>>
> >>> >>>> There's just only one issue; normally, I would setup the specific
> >>> >>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook.
> >>> However, if
> >>> >>>> you look further in the driver, those timings aren't defined
> >>> through a
> >>> >>>> memory controller but instead manually enforced by 'ndelay'
> >>> calls (arghhh).
> >>> >>>> This means that in my low-level procedures for reading and
> >>> writing, I
> >>> >>>> need to have access to the timings (or the struct ide_timing)
> >>> >>>> corresponding to the PIO mode selected, in order to use the
> >>> correct delays.
> >>> >>>>
> >>> >>>> My question is: which is the best way to accomplish this?
> >>> Declaring a
> >>> >>>> global struct ide_timing variable pointer that always holds the
> >>> correct
> >>> >>>> ide_timing struct to the selected PIO mode? Or should I always
> >>> check (in
> >>> >>>> some manner) what is the current PIO mode and then select the
> >>> adequate
> >>> >>>> delays?
> >>> >>>>
> >>> >>>>
> >>> >>> I think that the setting variable pointer in ->set_pio_mode
> >>> method would
> >>> >>> work best. Seems like the existing drive_data field of
> >>> ide_drive_t is well
> >>> >>> suited for this purpose (however it may be worth to convert it
> >>> to 'void *'
> >>> >>> type while we are it).
> >>> >>>
> >>> >>>
> >>> >> Did you mean 'drive_data' field, or 'driver_data' field?
> >>> >> 'drive_data' field is an unsigned int value; I guess you meant
> >>> >> 'driver_data' field as it is a (void *) field, so I can define it
> >>> as a
> >>> >> pointer to the correct 'struct ide_timing'.
> >>> >>
> >>> >
> >>> > That is why I hinted that you may need to convert 'drive_data' to
> >>> > 'void *' type first. You may also try to use 'driver_data' instead
> >>> > but you will discover rather quickly that you shouldn't do this... ;)
> >>> >
> >>> > 'driver_data' is for use by IDE core and IDE device drivers.
> >>> >
> >>> > 'drive_data' is for use by IDE host drivers.
> >>> >
> >>>
> >>> And this conversion is made by my driver code, or should I fix directly
> >>> in the ide_drive_t structure?
> >>
> >> The latter -- ide_drive_t is the place needing fixing.
> >
> >
> > Same: I will fix that and propose a patch.
>
> Hey, don't do this please! This is actually a bad idea as some
> drivers (e.g. sl82c105) use drive_data as an integer entity. Bart, stop
> giving such advices please. :-)
I'm not sure what you're getting at with "such advices"... :)
We need to cast somewhere anyway so we may as well cast from 'void *' to
'unsigned int' where needed and not the other way around (or rather from
'unsigned int' to 'struct ide_timing *') -- which would be an ugly hack
and could cause maintainability/portability problems later.
BTW it seems like a good occasion to add ide_{get,set}_drivedata() helpers
(ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and thus make
internal IDE API more coherent.
[ Yes, I know that we may get away with s/unsigned int/unsigned long/
and casting it to 'struct ide_timing *' for now but it always better
to at least consider more elegant solution first... ]
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 22:07 ` Bartlomiej Zolnierkiewicz
@ 2009-05-11 11:10 ` João Ramos
2009-05-12 16:49 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-11 11:10 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
[-- Attachment #1: Type: text/plain, Size: 2147 bytes --]
>>> Same: I will fix that and propose a patch.
>>>
>> Hey, don't do this please! This is actually a bad idea as some
>> drivers (e.g. sl82c105) use drive_data as an integer entity. Bart, stop
>> giving such advices please. :-)
>>
>
> I'm not sure what you're getting at with "such advices"... :)
>
> We need to cast somewhere anyway so we may as well cast from 'void *' to
> 'unsigned int' where needed and not the other way around (or rather from
> 'unsigned int' to 'struct ide_timing *') -- which would be an ugly hack
> and could cause maintainability/portability problems later.
>
> BTW it seems like a good occasion to add ide_{get,set}_drivedata() helpers
> (ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and thus make
> internal IDE API more coherent.
>
> [ Yes, I know that we may get away with s/unsigned int/unsigned long/
> and casting it to 'struct ide_timing *' for now but it always better
> to at least consider more elegant solution first... ]
>
> Thanks,
> Bart
>
>
Here is a proposed patch to fix 'drive_data' field to a 'void *' type.
I've also added the 'ide_{get,set}_drivedata' helpers you hinted, and
I've fixed all the host drivers which used the 'drive_data' field,
making them use the new 'ide_{get,set}_drivedata' helpers and casting
them accordingly to both the new 'drive_data' type and the type of value
being stored.
I've attached the patch to avoid line-wrapping and deformed output.
If you can, please test the patch with the fixed drivers, I can't test
those since I don't have hardware for it.
However, I will test for compilation.
Please comment, and if there's any fix to be made, just let me know.
Regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: ide_drive_data.patch --]
[-- Type: text/plain, Size: 13823 bytes --]
Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *' type,
allowing a wider range of values/types to be stored in this field.
Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
the 'drive_data' field.
Fixed all host drivers to maintain coherency with the change in the 'drive_data'
field type.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
---
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 80b777e..c8df34c 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -140,10 +140,11 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
if (hwif->channel) {
ide_drive_t *pair = ide_get_pair_dev(drive);
- drive->drive_data = setup_count;
+ ide_set_drivedata(drive, (void *) setup_count);
if (pair)
- setup_count = max_t(u8, setup_count, pair->drive_data);
+ setup_count = max_t(u8, setup_count,
+ (u8) ide_get_drivedata(pair));
}
if (setup_count > 5) /* shouldn't actually happen... */
diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c
index 0332a95..f9e349e 100644
--- a/drivers/ide/cs5536.c
+++ b/drivers/ide/cs5536.c
@@ -143,6 +143,8 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
0x99, 0x92, 0x90, 0x22, 0x20,
};
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
+
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
@@ -152,8 +154,9 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
if (pair)
cmd_pio = min(pio, ide_get_best_pio_mode(pair, 255, 4));
- drive->drive_data &= (IDE_DRV_MASK << 8);
- drive->drive_data |= drv_timings[pio];
+ drivedata &= (IDE_DRV_MASK << 8);
+ drivedata |= drv_timings[pio];
+ ide_set_drivedata(drive, (void *) drivedata);
cs5536_program_dtc(drive, drv_timings[pio]);
@@ -184,6 +187,8 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
0x67, 0x21, 0x20,
};
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
+
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
u32 etc;
@@ -195,8 +200,9 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
etc |= udma_timings[mode - XFER_UDMA_0] << dshift;
} else { /* MWDMA */
etc &= ~(IDE_ETC_UDMA_MASK << dshift);
- drive->drive_data &= IDE_DRV_MASK;
- drive->drive_data |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ drivedata &= IDE_DRV_MASK;
+ drivedata |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ ide_set_drivedata(drive, (void *) drivedata);
}
cs5536_write(pdev, ETC, etc);
@@ -204,9 +210,11 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
static void cs5536_dma_start(ide_drive_t *drive)
{
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
+
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data >> 8);
+ (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, drivedata >> 8);
ide_dma_start(drive);
}
@@ -214,10 +222,11 @@ static void cs5536_dma_start(ide_drive_t *drive)
static int cs5536_dma_end(ide_drive_t *drive)
{
int ret = ide_dma_end(drive);
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data & IDE_DRV_MASK);
+ (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, drivedata & IDE_DRV_MASK);
return ret;
}
diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
index 2fb0f29..158aeae 100644
--- a/drivers/ide/ht6560b.c
+++ b/drivers/ide/ht6560b.c
@@ -44,7 +44,12 @@
* bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
*/
#define HT_CONFIG_PORT 0x3e6
-#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
+
+static inline u8 HT_CONFIG(ide_drive_t *drive)
+{
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
+ return (drivedata & 0xff00) >> 8;
+}
/*
* FIFO + PREFETCH (both a/b-model)
*/
@@ -90,7 +95,12 @@
* Active Time for each drive. Smaller value gives higher speed.
* In case of failures you should probably fall back to a higher value.
*/
-#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
+static inline u8 HT_TIMING(ide_drive_t *drive)
+{
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
+ return drivedata & 0x00ff;
+}
+
#define HT_TIMING_DEFAULT 0xff
/*
@@ -244,6 +254,7 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
{
unsigned long flags;
int t = HT_PREFETCH_MODE << 8;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
spin_lock_irqsave(&ht6560b_lock, flags);
@@ -251,14 +262,16 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
* Prefetch mode and unmask irq seems to conflict
*/
if (state) {
- drive->drive_data |= t; /* enable prefetch mode */
+ drivedata |= t; /* enable prefetch mode */
drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
drive->dev_flags &= ~IDE_DFLAG_UNMASK;
} else {
- drive->drive_data &= ~t; /* disable prefetch mode */
+ drivedata &= ~t; /* disable prefetch mode */
drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
}
+ ide_set_drivedata(drive, (void *) drivedata);
+
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -270,6 +283,7 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
unsigned long flags;
u8 timing;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
switch (pio) {
case 8: /* set prefetch off */
@@ -281,8 +295,9 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
timing = ht_pio2timings(drive, pio);
spin_lock_irqsave(&ht6560b_lock, flags);
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ drivedata &= 0xff00;
+ drivedata |= timing;
+ ide_set_drivedata(drive, (void *) drivedata);
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -299,7 +314,7 @@ static void __init ht6560b_init_dev(ide_drive_t *drive)
if (hwif->channel)
t |= (HT_SECONDARY_IF << 8);
- drive->drive_data = t;
+ ide_set_drivedata(drive, (void *)t);
}
static int probe_ht6560b;
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
index 4e16ce6..c96f87a 100644
--- a/drivers/ide/icside.c
+++ b/drivers/ide/icside.c
@@ -267,10 +267,11 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
- drive->drive_data = cycle_time;
+ ide_set_drivedata(drive, (void *)cycle_time);
printk("%s: %s selected (peak %dMB/s)\n", drive->name,
- ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+ ide_xfer_verbose(xfer_mode),
+ 2000 / (int) ide_get_drivedata(drive));
}
static const struct ide_port_ops icside_v6_port_ops = {
@@ -332,7 +333,7 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
/*
* Select the correct timing for this drive.
*/
- set_dma_speed(ec->dma, drive->drive_data);
+ set_dma_speed(ec->dma, (int) ide_get_drivedata(drive));
/*
* Tell the DMA engine about the SG table and
diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c
index 6048eda..d545f01 100644
--- a/drivers/ide/opti621.c
+++ b/drivers/ide/opti621.c
@@ -139,6 +139,8 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
ide_drive_t *pair = ide_get_pair_dev(drive);
unsigned long flags;
u8 tim, misc, addr_pio = pio, clk;
+ int drivedata = XFER_PIO_0 + pio;
+ int pair_drivedata;
/* DRDY is default 2 (by OPTi Databook) */
static const u8 addr_timings[2][5] = {
@@ -150,11 +152,12 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
{ 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
};
- drive->drive_data = XFER_PIO_0 + pio;
+ ide_set_drivedata(drive, (void *) drivedata);
if (pair) {
- if (pair->drive_data && pair->drive_data < drive->drive_data)
- addr_pio = pair->drive_data - XFER_PIO_0;
+ pair_drivedata = (int) ide_get_drivedata(pair);
+ if (pair_drivedata && pair_drivedata < drivedata)
+ addr_pio = pair_drivedata - XFER_PIO_0;
}
spin_lock_irqsave(&opti621_lock, flags);
diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
index c9a1349..2cd350c 100644
--- a/drivers/ide/qd65xx.c
+++ b/drivers/ide/qd65xx.c
@@ -180,8 +180,11 @@ static int qd_find_disk_type (ide_drive_t *drive,
static void qd_set_timing (ide_drive_t *drive, u8 timing)
{
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ int drivedata = (int) ide_get_drivedata(drive);
+
+ drivedata &= 0xff00;
+ drivedata |= timing;
+ ide_set_drivedata(drive, (void *)drivedata);
printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
}
@@ -291,8 +294,9 @@ static void __init qd6500_init_dev(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
+ int drivedata = QD6500_DEF_DATA;
- drive->drive_data = QD6500_DEF_DATA;
+ ide_set_drivedata(drive, (void *) drivedata);
}
static void __init qd6580_init_dev(ide_drive_t *drive)
@@ -301,6 +305,7 @@ static void __init qd6580_init_dev(ide_drive_t *drive)
u16 t1, t2;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
+ int drivedata = (drive->dn & 1) ? t2 : t1;
if (hwif->host_flags & IDE_HFLAG_SINGLE) {
t1 = QD6580_DEF_DATA;
@@ -308,7 +313,7 @@ static void __init qd6580_init_dev(ide_drive_t *drive)
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- drive->drive_data = (drive->dn & 1) ? t2 : t1;
+ ide_set_drivedata(drive, (void *) drivedata);
}
static const struct ide_tp_ops qd65xx_tp_ops = {
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
index b0a4606..5e42eaa 100644
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -76,6 +76,7 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
int reg = 0x44 + drive->dn * 4;
u16 drv_ctrl;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
drv_ctrl = get_pio_timings(drive, pio);
@@ -83,8 +84,9 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
* Store the PIO timings so that we can restore them
* in case DMA will be turned off...
*/
- drive->drive_data &= 0xffff0000;
- drive->drive_data |= drv_ctrl;
+ drivedata &= 0xffff0000;
+ drivedata |= drv_ctrl;
+ ide_set_drivedata(drive, (void *) drivedata);
pci_write_config_word(dev, reg, drv_ctrl);
pci_read_config_word (dev, reg, &drv_ctrl);
@@ -101,6 +103,7 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
{
static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
u16 drv_ctrl;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
drive->name, ide_xfer_verbose(speed)));
@@ -111,8 +114,9 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
* Store the DMA timings so that we can actually program
* them when DMA will be turned on...
*/
- drive->drive_data &= 0x0000ffff;
- drive->drive_data |= (unsigned long)drv_ctrl << 16;
+ drivedata &= 0x0000ffff;
+ drivedata |= (unsigned int)drv_ctrl << 16;
+ ide_set_drivedata(drive, (void *) drivedata);
}
/*
@@ -181,10 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
int reg = 0x44 + drive->dn * 4;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
DBG(("%s(drive:%s)\n", __func__, drive->name));
- pci_write_config_word(dev, reg, drive->drive_data >> 16);
+ pci_write_config_word(dev, reg, drivedata >> 16);
sl82c105_reset_host(dev);
ide_dma_start(drive);
@@ -204,12 +209,13 @@ static int sl82c105_dma_end(ide_drive_t *drive)
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
int reg = 0x44 + drive->dn * 4;
int ret;
+ unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
DBG(("%s(drive:%s)\n", __func__, drive->name));
ret = ide_dma_end(drive);
- pci_write_config_word(dev, reg, drive->drive_data);
+ pci_write_config_word(dev, reg, drivedata);
return ret;
}
diff --git a/include/linux/ide.h b/include/linux/ide.h
index ff65fff..4ab9859 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -565,7 +565,7 @@ struct ide_drive_s {
unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
unsigned int cyl; /* "real" number of cyls */
- unsigned int drive_data; /* used by set_pio_mode/dev_select() */
+ void *drive_data; /* used by set_pio_mode/dev_select() */
unsigned int failures; /* current failure count */
unsigned int max_failures; /* maximum allowed failure count */
u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
@@ -1570,6 +1570,16 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive)
return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
}
+static inline void *ide_get_drivedata(ide_drive_t *drive)
+{
+ return drive->drive_data;
+}
+
+static inline void ide_set_drivedata(ide_drive_t *drive, void *data)
+{
+ drive->drive_data = data;
+}
+
#define ide_port_for_each_dev(i, dev, port) \
for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
^ permalink raw reply related [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 18:55 ` Bartlomiej Zolnierkiewicz
2009-05-08 20:24 ` joao.ramos
@ 2009-05-11 13:20 ` João Ramos
2009-05-12 16:41 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:57 ` Sergei Shtylyov
1 sibling, 2 replies; 59+ messages in thread
From: João Ramos @ 2009-05-11 13:20 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
[-- Attachment #1: Type: text/plain, Size: 1748 bytes --]
>>>>
>>>>
>>> Well, yes. Though I hoped that you would at least give a try to fixing
>>> IDE core to program PIO0 initially for all host drivers that implement
>>> ->set_pio_mode method...
>>>
>>>
>> Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
>> Maybe with a little help, but I can try. You mean, when the host driver
>> is registered (ide_host_register, or ide_host_add that later calls
>> ide_host_register), maybe in the 'ide_init_port' method (sorry, I need
>> some guidance here...) check if the 'set_pio_mode' method is
>> implemented, and after initializing each port (d->init_hwif(hwif))
>> default it to PIO Mode 0, calling set_pio_mode method.
>>
>
> ide_init_port() seems OK but I think that ide_port_init_devices()
> [it is called after ide_init_port()] would be a bit safer and more
> flexible (some host drivers may also require special ->init_dev
> setup first) and the check for ->set_pio_mode method presence can
> be done just before actually using the method, i.e.
>
> const struct ide_port_ops *port_ops = hwif->port_ops;
>
> if (port_ops && port_ops->set_pio_mode)
> port_ops->set_pio_mode(...)
>
>
Ok, so the patch for this is attached in this e-mail.
Please confirm and make your comments.
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: set_pio_mode.patch --]
[-- Type: text/plain, Size: 917 bytes --]
Initially set PIO Mode 0 for all host drivers that use PIO transfer mode
(and export a 'set_pio_mode' method) before the IDE core figures out
the most suited PIO mode for the attached device.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
---
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 7f264ed..f92cac4 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1031,6 +1031,13 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
if (port_ops && port_ops->init_dev)
port_ops->init_dev(drive);
+
+ /*
+ * if driver uses PIO mode, default to PIO Mode 0 before we
+ * figure out the most suited mode for the attached device
+ */
+ if (port_ops && port_ops->set_pio_mode)
+ port_ops->set_pio_mode(drive, 0);
}
}
^ permalink raw reply related [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-08 17:28 ` João Ramos
2009-05-08 18:02 ` Bartlomiej Zolnierkiewicz
@ 2009-05-12 16:01 ` João Ramos
2009-05-12 16:30 ` Bartlomiej Zolnierkiewicz
1 sibling, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-12 16:01 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
João Ramos escreveu:
>
>>
>> Yes! :)
>>
>> There is still a room for improvement though -- it would be better to
>> fix
>> IDE core to set PIO0 before probing devices for all host controllers.
>>
>> Moreover it seems that doing it this way would allow us to remove
>> ->init_hwif
>> method from this driver and do all necessary setup in ep93xx_ide_probe()
>> (this controller is a single port one so theoretically there
>> shouldn't be
>> a need for having per-port ->init_hwif implementation).
>>
>
> So after all this discussion ;-) , my driver will have no 'init_hwif'
> method, and the setup code will be on 'ep93xx_ide_probe', which will
> configure entirely the IDE host controller.
> Moreover, this initial configuration will setup the controller to work
> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
> the controller will configure itself according to the PIO mode
> reported by the IDE core.
>
> Can I proceed this way?
>
>>
>>> There's just only one issue; normally, I would setup the specific
>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However,
>>> if you look further in the driver, those timings aren't defined
>>> through a memory controller but instead manually enforced by
>>> 'ndelay' calls (arghhh).
>>> This means that in my low-level procedures for reading and writing,
>>> I need to have access to the timings (or the struct ide_timing)
>>> corresponding to the PIO mode selected, in order to use the correct
>>> delays.
>>>
>>> My question is: which is the best way to accomplish this? Declaring
>>> a global struct ide_timing variable pointer that always holds the
>>> correct ide_timing struct to the selected PIO mode? Or should I
>>> always check (in some manner) what is the current PIO mode and then
>>> select the adequate delays?
>>>
>>
>> I think that the setting variable pointer in ->set_pio_mode method would
>> work best. Seems like the existing drive_data field of ide_drive_t
>> is well
>> suited for this purpose (however it may be worth to convert it to
>> 'void *'
>> type while we are it).
>>
Are you sure I can do this safely?
Using the patch i've sent earlier, I am using the 'drive_data' field
(now converted to void * type) to store the struct ide_timing pointer
that holds the adequate timings for the selected PIO mode.
This is working, and the fix you suggested works, but sometimes I get a
null pointer dereference I can't seem to figure why.
As I needed to define low-level read/write procedures, I've defined the
entire ide_tp_ops structure with my own provided methods.
For the tf_load, tf_read, input_data and output_data methods, the fix is
easy since I have an ide_drive_t structure pointer as a parameter, so I
access the timing structure using:
struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
However, for the remaining methods (exec_command, read_status,
read_altstatus, write_devctl and dev_select), I only have access to an
ide_hwif_t pointer, so in order to get access to the containing
ide_drive_t and then to the struct ide_timing pointer stored before, I do:
ide_drive_t *drive = (ide_drive_t *) container_of(&hwif, ide_drive_t, hwif);
struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
And this seems to work, however at some point, after a while I get a
kernel Oops pointing out a null pointer dereference.
Can someone help me here?
Is there a better way to retrieve the ide_drive_t pointer from the
ide_hwif_t structure?
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 16:01 ` João Ramos
@ 2009-05-12 16:30 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:45 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-12 16:30 UTC (permalink / raw)
To: João Ramos; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
On Tuesday 12 May 2009 18:01:53 João Ramos wrote:
> João Ramos escreveu:
> >
> >>
> >> Yes! :)
> >>
> >> There is still a room for improvement though -- it would be better to
> >> fix
> >> IDE core to set PIO0 before probing devices for all host controllers.
> >>
> >> Moreover it seems that doing it this way would allow us to remove
> >> ->init_hwif
> >> method from this driver and do all necessary setup in ep93xx_ide_probe()
> >> (this controller is a single port one so theoretically there
> >> shouldn't be
> >> a need for having per-port ->init_hwif implementation).
> >>
> >
> > So after all this discussion ;-) , my driver will have no 'init_hwif'
> > method, and the setup code will be on 'ep93xx_ide_probe', which will
> > configure entirely the IDE host controller.
> > Moreover, this initial configuration will setup the controller to work
> > at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
> > the controller will configure itself according to the PIO mode
> > reported by the IDE core.
> >
> > Can I proceed this way?
> >
> >>
> >>> There's just only one issue; normally, I would setup the specific
> >>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However,
> >>> if you look further in the driver, those timings aren't defined
> >>> through a memory controller but instead manually enforced by
> >>> 'ndelay' calls (arghhh).
> >>> This means that in my low-level procedures for reading and writing,
> >>> I need to have access to the timings (or the struct ide_timing)
> >>> corresponding to the PIO mode selected, in order to use the correct
> >>> delays.
> >>>
> >>> My question is: which is the best way to accomplish this? Declaring
> >>> a global struct ide_timing variable pointer that always holds the
> >>> correct ide_timing struct to the selected PIO mode? Or should I
> >>> always check (in some manner) what is the current PIO mode and then
> >>> select the adequate delays?
> >>>
> >>
> >> I think that the setting variable pointer in ->set_pio_mode method would
> >> work best. Seems like the existing drive_data field of ide_drive_t
> >> is well
> >> suited for this purpose (however it may be worth to convert it to
> >> 'void *'
> >> type while we are it).
> >>
>
> Are you sure I can do this safely?
>
> Using the patch i've sent earlier, I am using the 'drive_data' field
> (now converted to void * type) to store the struct ide_timing pointer
> that holds the adequate timings for the selected PIO mode.
> This is working, and the fix you suggested works, but sometimes I get a
> null pointer dereference I can't seem to figure why.
> As I needed to define low-level read/write procedures, I've defined the
> entire ide_tp_ops structure with my own provided methods.
> For the tf_load, tf_read, input_data and output_data methods, the fix is
> easy since I have an ide_drive_t structure pointer as a parameter, so I
> access the timing structure using:
>
> struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
>
> However, for the remaining methods (exec_command, read_status,
> read_altstatus, write_devctl and dev_select), I only have access to an
> ide_hwif_t pointer, so in order to get access to the containing
> ide_drive_t and then to the struct ide_timing pointer stored before, I do:
>
> ide_drive_t *drive = (ide_drive_t *) container_of(&hwif, ide_drive_t, hwif);
This doesn't look correct.
> struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
>
> And this seems to work, however at some point, after a while I get a
> kernel Oops pointing out a null pointer dereference.
>
> Can someone help me here?
> Is there a better way to retrieve the ide_drive_t pointer from the
> ide_hwif_t structure?
hwif->devices[0] / hwif->devices[1]
However, I see the problem -- we need timing data also for command PIO.
Sergei, seems like we should just stuff pointer to command PIO timings
(which would be maximum PIO supported by both devices on the port) into
hwif->hwif_data and use it everywhere except ->*put_data methods?
Or maybe there is some better way to do it?
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-11 13:20 ` João Ramos
@ 2009-05-12 16:41 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:57 ` Sergei Shtylyov
1 sibling, 0 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-12 16:41 UTC (permalink / raw)
To: João Ramos; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
On Monday 11 May 2009 15:20:33 João Ramos wrote:
>
> >>>>
> >>>>
> >>> Well, yes. Though I hoped that you would at least give a try to fixing
> >>> IDE core to program PIO0 initially for all host drivers that implement
> >>> ->set_pio_mode method...
> >>>
> >>>
> >> Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
> >> Maybe with a little help, but I can try. You mean, when the host driver
> >> is registered (ide_host_register, or ide_host_add that later calls
> >> ide_host_register), maybe in the 'ide_init_port' method (sorry, I need
> >> some guidance here...) check if the 'set_pio_mode' method is
> >> implemented, and after initializing each port (d->init_hwif(hwif))
> >> default it to PIO Mode 0, calling set_pio_mode method.
> >>
> >
> > ide_init_port() seems OK but I think that ide_port_init_devices()
> > [it is called after ide_init_port()] would be a bit safer and more
> > flexible (some host drivers may also require special ->init_dev
> > setup first) and the check for ->set_pio_mode method presence can
> > be done just before actually using the method, i.e.
> >
> > const struct ide_port_ops *port_ops = hwif->port_ops;
> >
> > if (port_ops && port_ops->set_pio_mode)
> > port_ops->set_pio_mode(...)
> >
> >
>
> Ok, so the patch for this is attached in this e-mail.
>
> Please confirm and make your comments.
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1031,6 +1031,13 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
if (port_ops && port_ops->init_dev)
port_ops->init_dev(drive);
+
+ /*
+ * if driver uses PIO mode, default to PIO Mode 0 before we
+ * figure out the most suited mode for the attached device
+ */
+ if (port_ops && port_ops->set_pio_mode)
+ port_ops->set_pio_mode(drive, 0);
}
}
Looks good but please move this into a separate ide_port_for_each_dev()
loop (some host drivers require full initial setup of both devices on
the port before ->set_pio_mode can be called).
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 16:30 ` Bartlomiej Zolnierkiewicz
@ 2009-05-12 16:45 ` João Ramos
0 siblings, 0 replies; 59+ messages in thread
From: João Ramos @ 2009-05-12 16:45 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Alan Cox, Sergei Shtylyov, linux-ide
Bartlomiej Zolnierkiewicz escreveu:
> On Tuesday 12 May 2009 18:01:53 João Ramos wrote:
>
>> João Ramos escreveu:
>>
>>>> Yes! :)
>>>>
>>>> There is still a room for improvement though -- it would be better to
>>>> fix
>>>> IDE core to set PIO0 before probing devices for all host controllers.
>>>>
>>>> Moreover it seems that doing it this way would allow us to remove
>>>> ->init_hwif
>>>> method from this driver and do all necessary setup in ep93xx_ide_probe()
>>>> (this controller is a single port one so theoretically there
>>>> shouldn't be
>>>> a need for having per-port ->init_hwif implementation).
>>>>
>>>>
>>> So after all this discussion ;-) , my driver will have no 'init_hwif'
>>> method, and the setup code will be on 'ep93xx_ide_probe', which will
>>> configure entirely the IDE host controller.
>>> Moreover, this initial configuration will setup the controller to work
>>> at PIO Mode 0. Later on, the 'set_pio_mode' method will be called and
>>> the controller will configure itself according to the PIO mode
>>> reported by the IDE core.
>>>
>>> Can I proceed this way?
>>>
>>>
>>>>
>>>>
>>>>> There's just only one issue; normally, I would setup the specific
>>>>> timings (t0, t1, t2, t2i, etc) in the 'pio_set_mode' hook. However,
>>>>> if you look further in the driver, those timings aren't defined
>>>>> through a memory controller but instead manually enforced by
>>>>> 'ndelay' calls (arghhh).
>>>>> This means that in my low-level procedures for reading and writing,
>>>>> I need to have access to the timings (or the struct ide_timing)
>>>>> corresponding to the PIO mode selected, in order to use the correct
>>>>> delays.
>>>>>
>>>>> My question is: which is the best way to accomplish this? Declaring
>>>>> a global struct ide_timing variable pointer that always holds the
>>>>> correct ide_timing struct to the selected PIO mode? Or should I
>>>>> always check (in some manner) what is the current PIO mode and then
>>>>> select the adequate delays?
>>>>>
>>>>>
>>>> I think that the setting variable pointer in ->set_pio_mode method would
>>>> work best. Seems like the existing drive_data field of ide_drive_t
>>>> is well
>>>> suited for this purpose (however it may be worth to convert it to
>>>> 'void *'
>>>> type while we are it).
>>>>
>>>>
>> Are you sure I can do this safely?
>>
>> Using the patch i've sent earlier, I am using the 'drive_data' field
>> (now converted to void * type) to store the struct ide_timing pointer
>> that holds the adequate timings for the selected PIO mode.
>> This is working, and the fix you suggested works, but sometimes I get a
>> null pointer dereference I can't seem to figure why.
>> As I needed to define low-level read/write procedures, I've defined the
>> entire ide_tp_ops structure with my own provided methods.
>> For the tf_load, tf_read, input_data and output_data methods, the fix is
>> easy since I have an ide_drive_t structure pointer as a parameter, so I
>> access the timing structure using:
>>
>> struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
>>
>> However, for the remaining methods (exec_command, read_status,
>> read_altstatus, write_devctl and dev_select), I only have access to an
>> ide_hwif_t pointer, so in order to get access to the containing
>> ide_drive_t and then to the struct ide_timing pointer stored before, I do:
>>
>> ide_drive_t *drive = (ide_drive_t *) container_of(&hwif, ide_drive_t, hwif);
>>
>
> This doesn't look correct.
>
>
>> struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
>>
>> And this seems to work, however at some point, after a while I get a
>> kernel Oops pointing out a null pointer dereference.
>>
>> Can someone help me here?
>> Is there a better way to retrieve the ide_drive_t pointer from the
>> ide_hwif_t structure?
>>
>
> hwif->devices[0] / hwif->devices[1]
>
Well, it was a poorly attempt to make it work after trying with
'hwif->cur_dev' :-) .
I thought of using the way you recommended; I didn't use it because: 1 -
didn't know which index to use; 2 - looked like an 'ugly' way to do it...
> However, I see the problem -- we need timing data also for command PIO.
>
> Sergei, seems like we should just stuff pointer to command PIO timings
> (which would be maximum PIO supported by both devices on the port) into
> hwif->hwif_data and use it everywhere except ->*put_data methods?
>
> Or maybe there is some better way to do it?
>
> Thanks,
> Bart
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-11 11:10 ` João Ramos
@ 2009-05-12 16:49 ` Sergei Shtylyov
2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-12 16:49 UTC (permalink / raw)
To: João Ramos; +Cc: Bartlomiej Zolnierkiewicz, Alan Cox, linux-ide
Hello.
João Ramos wrote:
>> I'm not sure what you're getting at with "such advices"... :)
>> We need to cast somewhere anyway so we may as well cast from 'void *' to
>> 'unsigned int' where needed and not the other way around (or rather from
>> 'unsigned int' to 'struct ide_timing *') -- which would be an ugly hack
>> and could cause maintainability/portability problems later.
>> BTW it seems like a good occasion to add ide_{get,set}_drivedata()
>> helpers
>> (ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and thus
>> make
>> internal IDE API more coherent.
>> [ Yes, I know that we may get away with s/unsigned int/unsigned long/
>> and casting it to 'struct ide_timing *' for now but it always better
>> to at least consider more elegant solution first... ]
Changing a lot of drivers is more elegant than not? Doubt it.
>> Thanks,
>> Bart
> Here is a proposed patch to fix 'drive_data' field to a 'void *' type.
> I've also added the 'ide_{get,set}_drivedata' helpers you hinted, and
> I've fixed all the host drivers which used the 'drive_data' field,
> making them use the new 'ide_{get,set}_drivedata' helpers and casting
> them accordingly to both the new 'drive_data' type and the type of value
> being stored.
> I've attached the patch to avoid line-wrapping and deformed output.
> If you can, please test the patch with the fixed drivers, I can't test
> those since I don't have hardware for it.
> However, I will test for compilation.
> Please comment, and if there's any fix to be made, just let me know.
> Regards,
> João Ramos
> ------------------------------------------------------------------------
>
> Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *' type,
> allowing a wider range of values/types to be stored in this field.
> Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
> the 'drive_data' field.
> Fixed all host drivers to maintain coherency with the change in the 'drive_data'
> field type.
>
> Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
> Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
>
> ---
>
> diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
> index 80b777e..c8df34c 100644
> --- a/drivers/ide/cmd64x.c
> +++ b/drivers/ide/cmd64x.c
> @@ -140,10 +140,11 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
> if (hwif->channel) {
> ide_drive_t *pair = ide_get_pair_dev(drive);
>
> - drive->drive_data = setup_count;
> + ide_set_drivedata(drive, (void *) setup_count);
>
> if (pair)
> - setup_count = max_t(u8, setup_count, pair->drive_data);
> + setup_count = max_t(u8, setup_count,
> + (u8) ide_get_drivedata(pair));
You've over-indented it and the (u8) cast is not needed with max_t()
which performs the cast internally.
Personally, I prefer this style for the multi-line statement:
setup_count = max_t(u8, setup_count,
ide_get_drivedata(pair));
> diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c
> index 0332a95..f9e349e 100644
> --- a/drivers/ide/cs5536.c
> +++ b/drivers/ide/cs5536.c
> @@ -143,6 +143,8 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
> 0x99, 0x92, 0x90, 0x22, 0x20,
> };
>
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Call it 'timings' please.
> +
> struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
> ide_drive_t *pair = ide_get_pair_dev(drive);
> int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
[...]
> @@ -184,6 +187,8 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
> 0x67, 0x21, 0x20,
> };
>
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
And 'timings' again...
> +
> struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
> int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
> u32 etc;
[...]
> @@ -204,9 +210,11 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
>
> static void cs5536_dma_start(ide_drive_t *drive)
> {
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
And again...
> +
> if (drive->current_speed < XFER_UDMA_0 &&
> - (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
> - cs5536_program_dtc(drive, drive->drive_data >> 8);
> + (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
> + cs5536_program_dtc(drive, drivedata >> 8);
>
> ide_dma_start(drive);
> }
> @@ -214,10 +222,11 @@ static void cs5536_dma_start(ide_drive_t *drive)
> static int cs5536_dma_end(ide_drive_t *drive)
> {
> int ret = ide_dma_end(drive);
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Here too...
>
> if (drive->current_speed < XFER_UDMA_0 &&
> - (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
> - cs5536_program_dtc(drive, drive->drive_data & IDE_DRV_MASK);
> + (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
> + cs5536_program_dtc(drive, drivedata & IDE_DRV_MASK);
>
> return ret;
> }
> diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
> index 2fb0f29..158aeae 100644
> --- a/drivers/ide/ht6560b.c
> +++ b/drivers/ide/ht6560b.c
> @@ -44,7 +44,12 @@
> * bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
> */
> #define HT_CONFIG_PORT 0x3e6
> -#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
> +
> +static inline u8 HT_CONFIG(ide_drive_t *drive)
> +{
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Call it 'config' (or maybe just 'data' or 'info') and insert an empty
line after declaration please.
> + return (drivedata & 0xff00) >> 8;
> +}
> /*
> * FIFO + PREFETCH (both a/b-model)
> */
> @@ -90,7 +95,12 @@
> * Active Time for each drive. Smaller value gives higher speed.
> * In case of failures you should probably fall back to a higher value.
> */
> -#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
> +static inline u8 HT_TIMING(ide_drive_t *drive)
> +{
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Same here.
> + return drivedata & 0x00ff;
> +}
> +
> #define HT_TIMING_DEFAULT 0xff
>
> /*
> @@ -244,6 +254,7 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
> {
> unsigned long flags;
> int t = HT_PREFETCH_MODE << 8;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Call 'config' (or maybe just 'data' or 'info') please.
> @@ -270,6 +283,7 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
> {
> unsigned long flags;
> u8 timing;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Same here...
[...]
> @@ -299,7 +314,7 @@ static void __init ht6560b_init_dev(ide_drive_t *drive)
> if (hwif->channel)
> t |= (HT_SECONDARY_IF << 8);
>
> - drive->drive_data = t;
> + ide_set_drivedata(drive, (void *)t);
Oh, they call it just 't'! Nice name too. :-D
> diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
> index 4e16ce6..c96f87a 100644
> --- a/drivers/ide/icside.c
> +++ b/drivers/ide/icside.c
> @@ -267,10 +267,11 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
> if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
> cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
>
> - drive->drive_data = cycle_time;
> + ide_set_drivedata(drive, (void *)cycle_time);
>
> printk("%s: %s selected (peak %dMB/s)\n", drive->name,
> - ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
> + ide_xfer_verbose(xfer_mode),
> + 2000 / (int) ide_get_drivedata(drive));
> }
I see that you're not consistent about that space after the typeacst...
either put it there or not, not both. :-)
> diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c
> index 6048eda..d545f01 100644
> --- a/drivers/ide/opti621.c
> +++ b/drivers/ide/opti621.c
> @@ -139,6 +139,8 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
> ide_drive_t *pair = ide_get_pair_dev(drive);
> unsigned long flags;
> u8 tim, misc, addr_pio = pio, clk;
> + int drivedata = XFER_PIO_0 + pio;
Please name it 'mode'...
> + int pair_drivedata;
... and 'pair_mode'.
> @@ -150,11 +152,12 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
> { 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
> };
>
> - drive->drive_data = XFER_PIO_0 + pio;
> + ide_set_drivedata(drive, (void *) drivedata);
>
> if (pair) {
> - if (pair->drive_data && pair->drive_data < drive->drive_data)
> - addr_pio = pair->drive_data - XFER_PIO_0;
> + pair_drivedata = (int) ide_get_drivedata(pair);
> + if (pair_drivedata && pair_drivedata < drivedata)
> + addr_pio = pair_drivedata - XFER_PIO_0;
> }
>
> spin_lock_irqsave(&opti621_lock, flags);
> diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
> index c9a1349..2cd350c 100644
> --- a/drivers/ide/qd65xx.c
> +++ b/drivers/ide/qd65xx.c
> @@ -180,8 +180,11 @@ static int qd_find_disk_type (ide_drive_t *drive,
>
> static void qd_set_timing (ide_drive_t *drive, u8 timing)
> {
> - drive->drive_data &= 0xff00;
> - drive->drive_data |= timing;
> + int drivedata = (int) ide_get_drivedata(drive);
Well, please call it just 'data' (or even 't') in this case.
> +
> + drivedata &= 0xff00;
> + drivedata |= timing;
> + ide_set_drivedata(drive, (void *)drivedata);
Oh, inconsistent spacing again! :-)
> diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
> index b0a4606..5e42eaa 100644
> --- a/drivers/ide/sl82c105.c
> +++ b/drivers/ide/sl82c105.c
> @@ -76,6 +76,7 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
> struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
> int reg = 0x44 + drive->dn * 4;
> u16 drv_ctrl;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Please call it 'timings'.
> @@ -101,6 +103,7 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
> {
> static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
> u16 drv_ctrl;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
Here too...
> @@ -181,10 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive)
> ide_hwif_t *hwif = drive->hwif;
> struct pci_dev *dev = to_pci_dev(hwif->dev);
> int reg = 0x44 + drive->dn * 4;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
And here...
> @@ -204,12 +209,13 @@ static int sl82c105_dma_end(ide_drive_t *drive)
> struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
> int reg = 0x44 + drive->dn * 4;
> int ret;
> + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
And here...
> diff --git a/include/linux/ide.h b/include/linux/ide.h
> index ff65fff..4ab9859 100644
> --- a/include/linux/ide.h
> +++ b/include/linux/ide.h
> @@ -565,7 +565,7 @@ struct ide_drive_s {
>
> unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
> unsigned int cyl; /* "real" number of cyls */
> - unsigned int drive_data; /* used by set_pio_mode/dev_select() */
> + void *drive_data; /* used by set_pio_mode/dev_select() */
Please don't break the alignment of the 2nd column.
> unsigned int failures; /* current failure count */
> unsigned int max_failures; /* maximum allowed failure count */
> u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
> @@ -1570,6 +1570,16 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive)
> return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
> }
>
> +static inline void *ide_get_drivedata(ide_drive_t *drive)
> +{
> + return drive->drive_data;
> +}
> +
> +static inline void ide_set_drivedata(ide_drive_t *drive, void *data)
> +{
> + drive->drive_data = data;
> +}
> +
> #define ide_port_for_each_dev(i, dev, port) \
> for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
>
And don't tell me this patch is "elegant"... :-/
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-11 13:20 ` João Ramos
2009-05-12 16:41 ` Bartlomiej Zolnierkiewicz
@ 2009-05-12 16:57 ` Sergei Shtylyov
1 sibling, 0 replies; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-12 16:57 UTC (permalink / raw)
To: João Ramos; +Cc: Bartlomiej Zolnierkiewicz, Alan Cox, linux-ide
Hello.
João Ramos wrote:
>>>> Well, yes. Though I hoped that you would at least give a try to fixing
>>>> IDE core to program PIO0 initially for all host drivers that implement
>>>> ->set_pio_mode method...
>>> Sorry, I didn't noticed your hint... Sure, I can give it a try ;-)
>>> Maybe with a little help, but I can try. You mean, when the host
>>> driver is registered (ide_host_register, or ide_host_add that later
>>> calls ide_host_register), maybe in the 'ide_init_port' method (sorry,
>>> I need some guidance here...) check if the 'set_pio_mode' method is
>>> implemented, and after initializing each port (d->init_hwif(hwif))
>>> default it to PIO Mode 0, calling set_pio_mode method.
>> ide_init_port() seems OK but I think that ide_port_init_devices()
>> [it is called after ide_init_port()] would be a bit safer and more
>> flexible (some host drivers may also require special ->init_dev
>> setup first) and the check for ->set_pio_mode method presence can
>> be done just before actually using the method, i.e.
>> const struct ide_port_ops *port_ops = hwif->port_ops;
>> if (port_ops && port_ops->set_pio_mode)
>> port_ops->set_pio_mode(...)
> Ok, so the patch for this is attached in this e-mail.
> Please confirm and make your comments.
> Best regards,
> João Ramos
> ------------------------------------------------------------------------
>
> Initially set PIO Mode 0 for all host drivers that use PIO transfer mode
All drivers have to use the PIO mode (at least to transfer the taskfile
-- and for some PIO only commands too).
> (and export a 'set_pio_mode' method)
s/export/have/
> before the IDE core figures out
> the most suited PIO mode for the attached device.
> Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
> Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
> Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Alan is hardly much interested in the IDE patches. :-)
> ---
> diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
> index 7f264ed..f92cac4 100644
> --- a/drivers/ide/ide-probe.c
> +++ b/drivers/ide/ide-probe.c
> @@ -1031,6 +1031,13 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
>
> if (port_ops && port_ops->init_dev)
> port_ops->init_dev(drive);
> +
> + /*
> + * if driver uses PIO mode,
This passage is pointless.
> default to PIO Mode 0 before we
> + * figure out the most suited mode for the attached device
> + */
> + if (port_ops && port_ops->set_pio_mode)
> + port_ops->set_pio_mode(drive, 0);
> }
> }
>
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 16:49 ` Sergei Shtylyov
@ 2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
2009-05-13 11:01 ` João Ramos
` (2 more replies)
0 siblings, 3 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-12 17:23 UTC (permalink / raw)
To: Sergei Shtylyov; +Cc: João Ramos, Alan Cox, linux-ide
Sergei, thanks for reviewing it.
João, all Sergei's points are valid and here are few more ;)
On Tuesday 12 May 2009 18:49:37 Sergei Shtylyov wrote:
> Hello.
>
> João Ramos wrote:
>
> >> I'm not sure what you're getting at with "such advices"... :)
>
> >> We need to cast somewhere anyway so we may as well cast from 'void *' to
> >> 'unsigned int' where needed and not the other way around (or rather from
> >> 'unsigned int' to 'struct ide_timing *') -- which would be an ugly hack
> >> and could cause maintainability/portability problems later.
>
> >> BTW it seems like a good occasion to add ide_{get,set}_drivedata()
> >> helpers
> >> (ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and thus
> >> make
> >> internal IDE API more coherent.
>
> >> [ Yes, I know that we may get away with s/unsigned int/unsigned long/
> >> and casting it to 'struct ide_timing *' for now but it always better
> >> to at least consider more elegant solution first... ]
>
> Changing a lot of drivers is more elegant than not? Doubt it.
>
> >> Thanks,
> >> Bart
>
> > Here is a proposed patch to fix 'drive_data' field to a 'void *' type.
> > I've also added the 'ide_{get,set}_drivedata' helpers you hinted, and
>
> > I've fixed all the host drivers which used the 'drive_data' field,
> > making them use the new 'ide_{get,set}_drivedata' helpers and casting
> > them accordingly to both the new 'drive_data' type and the type of value
> > being stored.
>
> > I've attached the patch to avoid line-wrapping and deformed output.
>
> > If you can, please test the patch with the fixed drivers, I can't test
> > those since I don't have hardware for it.
> > However, I will test for compilation.
>
> > Please comment, and if there's any fix to be made, just let me know.
>
> > Regards,
> > João Ramos
>
> > ------------------------------------------------------------------------
> >
> > Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *' type,
> > allowing a wider range of values/types to be stored in this field.
> > Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
> > the 'drive_data' field.
> > Fixed all host drivers to maintain coherency with the change in the 'drive_data'
> > field type.
> >
> > Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> > Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
> > Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
> > Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
> >
> > ---
> >
> > diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
> > index 80b777e..c8df34c 100644
> > --- a/drivers/ide/cmd64x.c
> > +++ b/drivers/ide/cmd64x.c
> > @@ -140,10 +140,11 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
> > if (hwif->channel) {
> > ide_drive_t *pair = ide_get_pair_dev(drive);
> >
> > - drive->drive_data = setup_count;
> > + ide_set_drivedata(drive, (void *) setup_count);
(void *)setup_count would be a preferred coding style
[this holds for all such constructs in the patch]
> > if (pair)
> > - setup_count = max_t(u8, setup_count, pair->drive_data);
> > + setup_count = max_t(u8, setup_count,
> > + (u8) ide_get_drivedata(pair));
>
> You've over-indented it and the (u8) cast is not needed with max_t()
> which performs the cast internally.
> Personally, I prefer this style for the multi-line statement:
>
> setup_count = max_t(u8, setup_count,
> ide_get_drivedata(pair));
>
> > diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c
> > index 0332a95..f9e349e 100644
> > --- a/drivers/ide/cs5536.c
> > +++ b/drivers/ide/cs5536.c
> > @@ -143,6 +143,8 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > 0x99, 0x92, 0x90, 0x22, 0x20,
> > };
> >
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Call it 'timings' please.
and please move it after 'cshift' variable below
> > +
> > struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
> > ide_drive_t *pair = ide_get_pair_dev(drive);
> > int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
> [...]
> > @@ -184,6 +187,8 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
> > 0x67, 0x21, 0x20,
> > };
> >
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> And 'timings' again...
>
> > +
> > struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
> > int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
> > u32 etc;
> [...]
> > @@ -204,9 +210,11 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
> >
> > static void cs5536_dma_start(ide_drive_t *drive)
> > {
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> And again...
>
> > +
> > if (drive->current_speed < XFER_UDMA_0 &&
> > - (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
> > - cs5536_program_dtc(drive, drive->drive_data >> 8);
> > + (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
> > + cs5536_program_dtc(drive, drivedata >> 8);
> >
> > ide_dma_start(drive);
> > }
> > @@ -214,10 +222,11 @@ static void cs5536_dma_start(ide_drive_t *drive)
> > static int cs5536_dma_end(ide_drive_t *drive)
> > {
> > int ret = ide_dma_end(drive);
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Here too...
>
> >
> > if (drive->current_speed < XFER_UDMA_0 &&
> > - (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
> > - cs5536_program_dtc(drive, drive->drive_data & IDE_DRV_MASK);
> > + (drivedata >> 8) != (drivedata & IDE_DRV_MASK))
> > + cs5536_program_dtc(drive, drivedata & IDE_DRV_MASK);
> >
> > return ret;
> > }
> > diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
> > index 2fb0f29..158aeae 100644
> > --- a/drivers/ide/ht6560b.c
> > +++ b/drivers/ide/ht6560b.c
> > @@ -44,7 +44,12 @@
> > * bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
> > */
> > #define HT_CONFIG_PORT 0x3e6
> > -#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
> > +
> > +static inline u8 HT_CONFIG(ide_drive_t *drive)
> > +{
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Call it 'config' (or maybe just 'data' or 'info') and insert an empty
> line after declaration please.
Actually this one can be done without 'drivedata' variable.
> > + return (drivedata & 0xff00) >> 8;
> > +}
> > /*
> > * FIFO + PREFETCH (both a/b-model)
> > */
> > @@ -90,7 +95,12 @@
> > * Active Time for each drive. Smaller value gives higher speed.
> > * In case of failures you should probably fall back to a higher value.
> > */
> > -#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
> > +static inline u8 HT_TIMING(ide_drive_t *drive)
> > +{
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Same here.
this one also
> > + return drivedata & 0x00ff;
> > +}
> > +
> > #define HT_TIMING_DEFAULT 0xff
> >
> > /*
> > @@ -244,6 +254,7 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
> > {
> > unsigned long flags;
> > int t = HT_PREFETCH_MODE << 8;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Call 'config' (or maybe just 'data' or 'info') please.
>
> > @@ -270,6 +283,7 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > {
> > unsigned long flags;
> > u8 timing;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Same here...
>
> [...]
>
> > @@ -299,7 +314,7 @@ static void __init ht6560b_init_dev(ide_drive_t *drive)
> > if (hwif->channel)
> > t |= (HT_SECONDARY_IF << 8);
> >
> > - drive->drive_data = t;
> > + ide_set_drivedata(drive, (void *)t);
>
> Oh, they call it just 't'! Nice name too. :-D
>
> > diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
> > index 4e16ce6..c96f87a 100644
> > --- a/drivers/ide/icside.c
> > +++ b/drivers/ide/icside.c
> > @@ -267,10 +267,11 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
> > if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
> > cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
> >
> > - drive->drive_data = cycle_time;
> > + ide_set_drivedata(drive, (void *)cycle_time);
> >
> > printk("%s: %s selected (peak %dMB/s)\n", drive->name,
> > - ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
> > + ide_xfer_verbose(xfer_mode),
> > + 2000 / (int) ide_get_drivedata(drive));
> > }
>
> I see that you're not consistent about that space after the typeacst...
> either put it there or not, not both. :-)
>
> > diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c
> > index 6048eda..d545f01 100644
> > --- a/drivers/ide/opti621.c
> > +++ b/drivers/ide/opti621.c
> > @@ -139,6 +139,8 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > ide_drive_t *pair = ide_get_pair_dev(drive);
> > unsigned long flags;
> > u8 tim, misc, addr_pio = pio, clk;
> > + int drivedata = XFER_PIO_0 + pio;
>
> Please name it 'mode'...
>
> > + int pair_drivedata;
>
> ... and 'pair_mode'.
>
> > @@ -150,11 +152,12 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > { 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
> > };
> >
> > - drive->drive_data = XFER_PIO_0 + pio;
> > + ide_set_drivedata(drive, (void *) drivedata);
> >
> > if (pair) {
> > - if (pair->drive_data && pair->drive_data < drive->drive_data)
> > - addr_pio = pair->drive_data - XFER_PIO_0;
> > + pair_drivedata = (int) ide_get_drivedata(pair);
> > + if (pair_drivedata && pair_drivedata < drivedata)
> > + addr_pio = pair_drivedata - XFER_PIO_0;
> > }
> >
> > spin_lock_irqsave(&opti621_lock, flags);
> > diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
> > index c9a1349..2cd350c 100644
> > --- a/drivers/ide/qd65xx.c
> > +++ b/drivers/ide/qd65xx.c
> > @@ -180,8 +180,11 @@ static int qd_find_disk_type (ide_drive_t *drive,
> >
> > static void qd_set_timing (ide_drive_t *drive, u8 timing)
> > {
> > - drive->drive_data &= 0xff00;
> > - drive->drive_data |= timing;
> > + int drivedata = (int) ide_get_drivedata(drive);
>
> Well, please call it just 'data' (or even 't') in this case.
>
> > +
> > + drivedata &= 0xff00;
> > + drivedata |= timing;
> > + ide_set_drivedata(drive, (void *)drivedata);
>
> Oh, inconsistent spacing again! :-)
also:
@@ -291,8 +294,9 @@ static void __init qd6500_init_dev(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
+ int drivedata = QD6500_DEF_DATA;
- drive->drive_data = QD6500_DEF_DATA;
+ ide_set_drivedata(drive, (void *) drivedata);
No need for drivedata variable.
static void __init qd6580_init_dev(ide_drive_t *drive)
@@ -301,6 +305,7 @@ static void __init qd6580_init_dev(ide_drive_t *drive)
u16 t1, t2;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
+ int drivedata = (drive->dn & 1) ? t2 : t1;
dito, plus t2 and t1 are not initialized yet
if (hwif->host_flags & IDE_HFLAG_SINGLE) {
t1 = QD6580_DEF_DATA;
@@ -308,7 +313,7 @@ static void __init qd6580_init_dev(ide_drive_t *drive)
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- drive->drive_data = (drive->dn & 1) ? t2 : t1;
+ ide_set_drivedata(drive, (void *) drivedata);
}
> > diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
> > index b0a4606..5e42eaa 100644
> > --- a/drivers/ide/sl82c105.c
> > +++ b/drivers/ide/sl82c105.c
> > @@ -76,6 +76,7 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
> > int reg = 0x44 + drive->dn * 4;
> > u16 drv_ctrl;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Please call it 'timings'.
>
> > @@ -101,6 +103,7 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
> > {
> > static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
> > u16 drv_ctrl;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> Here too...
>
> > @@ -181,10 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive)
> > ide_hwif_t *hwif = drive->hwif;
> > struct pci_dev *dev = to_pci_dev(hwif->dev);
> > int reg = 0x44 + drive->dn * 4;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> And here...
>
> > @@ -204,12 +209,13 @@ static int sl82c105_dma_end(ide_drive_t *drive)
> > struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
> > int reg = 0x44 + drive->dn * 4;
> > int ret;
> > + unsigned int drivedata = (unsigned int) ide_get_drivedata(drive);
>
> And here...
>
> > diff --git a/include/linux/ide.h b/include/linux/ide.h
> > index ff65fff..4ab9859 100644
> > --- a/include/linux/ide.h
> > +++ b/include/linux/ide.h
> > @@ -565,7 +565,7 @@ struct ide_drive_s {
> >
> > unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
> > unsigned int cyl; /* "real" number of cyls */
> > - unsigned int drive_data; /* used by set_pio_mode/dev_select() */
> > + void *drive_data; /* used by set_pio_mode/dev_select() */
>
> Please don't break the alignment of the 2nd column.
>
> > unsigned int failures; /* current failure count */
> > unsigned int max_failures; /* maximum allowed failure count */
> > u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
> > @@ -1570,6 +1570,16 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive)
> > return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
> > }
> >
> > +static inline void *ide_get_drivedata(ide_drive_t *drive)
> > +{
> > + return drive->drive_data;
> > +}
> > +
> > +static inline void ide_set_drivedata(ide_drive_t *drive, void *data)
> > +{
> > + drive->drive_data = data;
> > +}
> > +
> > #define ide_port_for_each_dev(i, dev, port) \
> > for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
> >
>
> And don't tell me this patch is "elegant"... :-/
Well, it is makes code more coherent and more maintainable...
Moreover those inline helpers serve as a part of documentation (though
real DocBook documentation would also be nice) for host drivers authors.
Either this or we should remove ide_{get,set}_hwifdata() and allow
host drivers to poke directly also at hwif->hwif_data...
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
@ 2009-05-13 11:01 ` João Ramos
2009-05-17 15:20 ` Bartlomiej Zolnierkiewicz
2009-05-13 14:18 ` João Ramos
2009-05-14 16:30 ` Sergei Shtylyov
2 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-13 11:01 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
[-- Attachment #1: Type: text/plain, Size: 534 bytes --]
Here's the revised patches, according to the changes you suggested.
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: ide_drive_data.patch --]
[-- Type: text/plain, Size: 13110 bytes --]
Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *'
type, allowing a wider range of values/types to be stored in this field.
Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
the 'drive_data' field.
Fixed all host drivers to maintain coherency with the change in the
'drive_data' field type.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
---
diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c
index 80b777e..986842e 100644
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -140,10 +140,10 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
if (hwif->channel) {
ide_drive_t *pair = ide_get_pair_dev(drive);
- drive->drive_data = setup_count;
+ ide_set_drivedata(drive, (void *)setup_count);
if (pair)
- setup_count = max_t(u8, setup_count, pair->drive_data);
+ setup_count = max_t(u8, setup_count, ide_get_drivedata(pair));
}
if (setup_count > 5) /* shouldn't actually happen... */
diff --git a/drivers/ide/cs5536.c b/drivers/ide/cs5536.c
index 0332a95..d4440bd 100644
--- a/drivers/ide/cs5536.c
+++ b/drivers/ide/cs5536.c
@@ -146,14 +146,16 @@ static void cs5536_set_pio_mode(ide_drive_t *drive, const u8 pio)
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
u32 cast;
u8 cmd_pio = pio;
if (pair)
cmd_pio = min(pio, ide_get_best_pio_mode(pair, 255, 4));
- drive->drive_data &= (IDE_DRV_MASK << 8);
- drive->drive_data |= drv_timings[pio];
+ timings &= (IDE_DRV_MASK << 8);
+ timings |= drv_timings[pio];
+ ide_set_drivedata(drive, (void *)timings);
cs5536_program_dtc(drive, drv_timings[pio]);
@@ -184,6 +186,8 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
0x67, 0x21, 0x20,
};
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
+
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
u32 etc;
@@ -195,8 +199,9 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
etc |= udma_timings[mode - XFER_UDMA_0] << dshift;
} else { /* MWDMA */
etc &= ~(IDE_ETC_UDMA_MASK << dshift);
- drive->drive_data &= IDE_DRV_MASK;
- drive->drive_data |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ timings &= IDE_DRV_MASK;
+ timings |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ ide_set_drivedata(drive, (void *)timings);
}
cs5536_write(pdev, ETC, etc);
@@ -204,9 +209,11 @@ static void cs5536_set_dma_mode(ide_drive_t *drive, const u8 mode)
static void cs5536_dma_start(ide_drive_t *drive)
{
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
+
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data >> 8);
+ (timings >> 8) != (timings & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, timings >> 8);
ide_dma_start(drive);
}
@@ -214,10 +221,11 @@ static void cs5536_dma_start(ide_drive_t *drive)
static int cs5536_dma_end(ide_drive_t *drive)
{
int ret = ide_dma_end(drive);
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data & IDE_DRV_MASK);
+ (timings >> 8) != (timings & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, timings & IDE_DRV_MASK);
return ret;
}
diff --git a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c
index 2fb0f29..5d94af3 100644
--- a/drivers/ide/ht6560b.c
+++ b/drivers/ide/ht6560b.c
@@ -44,7 +44,12 @@
* bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
*/
#define HT_CONFIG_PORT 0x3e6
-#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
+
+static inline u8 HT_CONFIG(ide_drive_t *drive)
+{
+ return ((unsigned int)ide_get_drivedata(drive) & 0xff00) >> 8;
+}
+
/*
* FIFO + PREFETCH (both a/b-model)
*/
@@ -90,7 +95,11 @@
* Active Time for each drive. Smaller value gives higher speed.
* In case of failures you should probably fall back to a higher value.
*/
-#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
+static inline u8 HT_TIMING(ide_drive_t *drive)
+{
+ return (unsigned int)ide_get_drivedata(drive) & 0x00ff;
+}
+
#define HT_TIMING_DEFAULT 0xff
/*
@@ -244,6 +253,7 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
{
unsigned long flags;
int t = HT_PREFETCH_MODE << 8;
+ unsigned int config = (unsigned int)ide_get_drivedata(drive);
spin_lock_irqsave(&ht6560b_lock, flags);
@@ -251,14 +261,16 @@ static void ht_set_prefetch(ide_drive_t *drive, u8 state)
* Prefetch mode and unmask irq seems to conflict
*/
if (state) {
- drive->drive_data |= t; /* enable prefetch mode */
+ config |= t; /* enable prefetch mode */
drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
drive->dev_flags &= ~IDE_DFLAG_UNMASK;
} else {
- drive->drive_data &= ~t; /* disable prefetch mode */
+ config &= ~t; /* disable prefetch mode */
drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
}
+ ide_set_drivedata(drive, (void *)config);
+
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -270,6 +282,7 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
unsigned long flags;
u8 timing;
+ unsigned int config = (unsigned int)ide_get_drivedata(drive);
switch (pio) {
case 8: /* set prefetch off */
@@ -281,8 +294,9 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
timing = ht_pio2timings(drive, pio);
spin_lock_irqsave(&ht6560b_lock, flags);
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ config &= 0xff00;
+ config |= timing;
+ ide_set_drivedata(drive, (void *)config);
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -299,7 +313,7 @@ static void __init ht6560b_init_dev(ide_drive_t *drive)
if (hwif->channel)
t |= (HT_SECONDARY_IF << 8);
- drive->drive_data = t;
+ ide_set_drivedata(drive, (void *)t);
}
static int probe_ht6560b;
diff --git a/drivers/ide/icside.c b/drivers/ide/icside.c
index 4e16ce6..709a5bd 100644
--- a/drivers/ide/icside.c
+++ b/drivers/ide/icside.c
@@ -267,10 +267,11 @@ static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
- drive->drive_data = cycle_time;
+ ide_set_drivedata(drive, (void *)cycle_time);
printk("%s: %s selected (peak %dMB/s)\n", drive->name,
- ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+ ide_xfer_verbose(xfer_mode),
+ 2000 / (int)ide_get_drivedata(drive));
}
static const struct ide_port_ops icside_v6_port_ops = {
@@ -332,7 +333,7 @@ static int icside_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
/*
* Select the correct timing for this drive.
*/
- set_dma_speed(ec->dma, drive->drive_data);
+ set_dma_speed(ec->dma, (int)ide_get_drivedata(drive));
/*
* Tell the DMA engine about the SG table and
diff --git a/drivers/ide/opti621.c b/drivers/ide/opti621.c
index 6048eda..d94d0f1 100644
--- a/drivers/ide/opti621.c
+++ b/drivers/ide/opti621.c
@@ -139,6 +139,8 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
ide_drive_t *pair = ide_get_pair_dev(drive);
unsigned long flags;
u8 tim, misc, addr_pio = pio, clk;
+ int mode = XFER_PIO_0 + pio;
+ int pair_mode;
/* DRDY is default 2 (by OPTi Databook) */
static const u8 addr_timings[2][5] = {
@@ -150,11 +152,12 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
{ 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
};
- drive->drive_data = XFER_PIO_0 + pio;
+ ide_set_drivedata(drive, (void *)mode);
if (pair) {
- if (pair->drive_data && pair->drive_data < drive->drive_data)
- addr_pio = pair->drive_data - XFER_PIO_0;
+ pair_mode = (int)ide_get_drivedata(pair);
+ if (pair_mode && pair_mode < mode)
+ addr_pio = pair_mode - XFER_PIO_0;
}
spin_lock_irqsave(&opti621_lock, flags);
diff --git a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c
index c9a1349..689d413 100644
--- a/drivers/ide/qd65xx.c
+++ b/drivers/ide/qd65xx.c
@@ -180,8 +180,11 @@ static int qd_find_disk_type (ide_drive_t *drive,
static void qd_set_timing (ide_drive_t *drive, u8 timing)
{
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ int data = (int)ide_get_drivedata(drive);
+
+ data &= 0xff00;
+ data |= timing;
+ ide_set_drivedata(drive, (void *)data);
printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
}
@@ -292,7 +295,7 @@ static void __init qd6500_init_dev(ide_drive_t *drive)
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
- drive->drive_data = QD6500_DEF_DATA;
+ ide_set_drivedata(drive, (void *)QD6500_DEF_DATA);
}
static void __init qd6580_init_dev(ide_drive_t *drive)
@@ -308,7 +311,7 @@ static void __init qd6580_init_dev(ide_drive_t *drive)
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- drive->drive_data = (drive->dn & 1) ? t2 : t1;
+ ide_set_drivedata(drive, (void *)((drive->dn & 1) ? t2 : t1));
}
static const struct ide_tp_ops qd65xx_tp_ops = {
diff --git a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c
index b0a4606..2d2c016 100644
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -76,6 +76,7 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
int reg = 0x44 + drive->dn * 4;
u16 drv_ctrl;
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
drv_ctrl = get_pio_timings(drive, pio);
@@ -83,8 +84,9 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
* Store the PIO timings so that we can restore them
* in case DMA will be turned off...
*/
- drive->drive_data &= 0xffff0000;
- drive->drive_data |= drv_ctrl;
+ timings &= 0xffff0000;
+ timings |= drv_ctrl;
+ ide_set_drivedata(drive, (void *)timings);
pci_write_config_word(dev, reg, drv_ctrl);
pci_read_config_word (dev, reg, &drv_ctrl);
@@ -101,6 +103,7 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
{
static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
u16 drv_ctrl;
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
drive->name, ide_xfer_verbose(speed)));
@@ -111,8 +114,9 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
* Store the DMA timings so that we can actually program
* them when DMA will be turned on...
*/
- drive->drive_data &= 0x0000ffff;
- drive->drive_data |= (unsigned long)drv_ctrl << 16;
+ timings &= 0x0000ffff;
+ timings |= (unsigned int)drv_ctrl << 16;
+ ide_set_drivedata(drive, (void *)timings);
}
/*
@@ -181,10 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
int reg = 0x44 + drive->dn * 4;
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
DBG(("%s(drive:%s)\n", __func__, drive->name));
- pci_write_config_word(dev, reg, drive->drive_data >> 16);
+ pci_write_config_word(dev, reg, timings >> 16);
sl82c105_reset_host(dev);
ide_dma_start(drive);
@@ -204,12 +209,13 @@ static int sl82c105_dma_end(ide_drive_t *drive)
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
int reg = 0x44 + drive->dn * 4;
int ret;
+ unsigned int timings = (unsigned int)ide_get_drivedata(drive);
DBG(("%s(drive:%s)\n", __func__, drive->name));
ret = ide_dma_end(drive);
- pci_write_config_word(dev, reg, drive->drive_data);
+ pci_write_config_word(dev, reg, timings);
return ret;
}
diff --git a/include/linux/ide.h b/include/linux/ide.h
index ff65fff..9bcd3a5 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -565,7 +565,7 @@ struct ide_drive_s {
unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
unsigned int cyl; /* "real" number of cyls */
- unsigned int drive_data; /* used by set_pio_mode/dev_select() */
+ void *drive_data; /* used by set_pio_mode/dev_select() */
unsigned int failures; /* current failure count */
unsigned int max_failures; /* maximum allowed failure count */
u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
@@ -1570,6 +1570,16 @@ static inline ide_drive_t *ide_get_pair_dev(ide_drive_t *drive)
return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
}
+static inline void *ide_get_drivedata(ide_drive_t *drive)
+{
+ return drive->drive_data;
+}
+
+static inline void ide_set_drivedata(ide_drive_t *drive, void *data)
+{
+ drive->drive_data = data;
+}
+
#define ide_port_for_each_dev(i, dev, port) \
for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
[-- Attachment #3: set_pio_mode.patch --]
[-- Type: text/plain, Size: 931 bytes --]
Initially set PIO Mode 0 for all host drivers that have a 'set_pio_mode'
method before the IDE core figures out the most suited PIO mode for the
attached device.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
---
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 7f264ed..7d614d8 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -1032,6 +1032,15 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
if (port_ops && port_ops->init_dev)
port_ops->init_dev(drive);
}
+
+ ide_port_for_each_dev(i, drive, hwif) {
+ /*
+ * default to PIO Mode 0 before we figure out
+ * the most suited mode for the attached device
+ */
+ if (port_ops && port_ops->set_pio_mode)
+ port_ops->set_pio_mode(drive, 0);
+ }
}
static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
^ permalink raw reply related [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
2009-05-13 11:01 ` João Ramos
@ 2009-05-13 14:18 ` João Ramos
2009-05-14 19:44 ` Bartlomiej Zolnierkiewicz
2009-05-14 16:30 ` Sergei Shtylyov
2 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-13 14:18 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz, Sergei Shtylyov, Alan Cox; +Cc: linux-ide
[-- Attachment #1: Type: text/plain, Size: 825 bytes --]
Hi,
In attachment I send the revised patch for the EP93xx CPU IDE controller
(well, not entirely, only the IDE bit, I letf out the platform code as
suggested), changed and fixed according to all the comments you made before.
This driver depends on the previous patches I sent (ide_drive_data patch
and set_pio_mode patch).
Please make your suggestions.
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: linux-2.6.30-rc4-ep93xx-ide.patch --]
[-- Type: text/plain, Size: 16892 bytes --]
diff -urN linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c
--- linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c 2009-05-13 14:47:02.000000000 +0100
@@ -0,0 +1,528 @@
+/*
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * IDE host controller driver.
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ * 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.
+ */
+
+#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/ide.h>
+#include <linux/delay.h>
+
+#define MODULE_NAME "ep93xx-ide"
+
+/*
+ * 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.
+ */
+
+/*
+ * IDE Register offset
+ */
+#define IDECTRL 0x00
+#define IDECFG 0x04
+#define IDEMDMAOP 0x08
+#define IDEUDMAOP 0x0C
+#define IDEDATAOUT 0x10
+#define IDEDATAIN 0x14
+#define IDEMDMADATAOUT 0x18
+#define IDEMDMADATAIN 0x1C
+#define IDEUDMADATAOUT 0x20
+#define IDEUDMADATAIN 0x24
+#define IDEUDMASTS 0x28
+#define IDEUDMADEBUG 0x2C
+#define IDEUDMAWRBUFSTS 0x30
+#define IDEUDMARDBUFSTS 0x34
+
+/*
+ * IDE Control Register bit fields
+ */
+#define IDECTRL_CS0N 0x00000001
+#define IDECTRL_CS1N 0x00000002
+#define IDECTRL_DA 0x0000001C
+#define IDECTRL_DIORN 0x00000020
+#define IDECTRL_DIOWN 0x00000040
+#define IDECTRL_DASPN 0x00000080
+#define IDECTRL_DMARQ 0x00000100
+#define IDECTRL_INTRQ 0x00000200
+#define IDECTRL_IORDY 0x00000400
+
+/*
+ * IDE Configuration Register bit fields
+ */
+#define IDECFG_IDEEN 0x00000001
+#define IDECFG_PIO 0x00000002
+#define IDECFG_MDMA 0x00000004
+#define IDECFG_UDMA 0x00000008
+#define IDECFG_PIO_MODE_0 0x00000000
+#define IDECFG_PIO_MODE_1 0x00000010
+#define IDECFG_PIO_MODE_2 0x00000020
+#define IDECFG_PIO_MODE_3 0x00000030
+#define IDECFG_PIO_MODE_4 0x00000040
+#define IDECFG_MDMA_MODE_0 0x00000000
+#define IDECFG_MDMA_MODE_1 0x00000010
+#define IDECFG_MDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_0 0x00000000
+#define IDECFG_UDMA_MODE_1 0x00000010
+#define IDECFG_UDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_3 0x00000030
+#define IDECFG_UDMA_MODE_4 0x00000040
+#define IDECFG_WST 0x00000300
+
+/*
+ * readl/writel helpers to access internal registers using
+ * an ioremapped cookie and the specified IDE register offset
+ */
+
+static inline u32 ep93xx_readl(unsigned long base, u8 offset)
+{
+ return readl((void __iomem *)(base + offset));
+}
+
+static inline void ep93xx_writel(u32 value, unsigned long base, u8 offset)
+{
+ writel(value, (void __iomem *)(base + offset));
+}
+
+/*
+ * Check whether IORDY is asserted or not.
+ */
+static inline int ep93xx_ide_check_iordy(unsigned long base)
+{
+ u32 reg = ep93xx_readl(base, IDECTRL);
+
+ return (reg & IDECTRL_IORDY) ? 1 : 0;
+}
+
+/*
+ * IDE Interface register map default state
+ * (shutdown)
+ */
+static void ep93xx_ide_clean_regs(unsigned long base)
+{
+ /* disable IDE interface initially */
+ ep93xx_writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
+ IDECTRL_DIOWN, base, IDECTRL);
+
+ /* clear IDE registers */
+ ep93xx_writel(0, base, IDECFG);
+ ep93xx_writel(0, base, IDEMDMAOP);
+ ep93xx_writel(0, base, IDEUDMAOP);
+ ep93xx_writel(0, base, IDEDATAOUT);
+ ep93xx_writel(0, base, IDEDATAIN);
+ ep93xx_writel(0, base, IDEMDMADATAOUT);
+ ep93xx_writel(0, base, IDEMDMADATAIN);
+ ep93xx_writel(0, base, IDEUDMADATAOUT);
+ ep93xx_writel(0, base, IDEUDMADATAIN);
+ ep93xx_writel(0, base, IDEUDMADEBUG);
+}
+
+static u16 ep93xx_ide_read(unsigned long base, unsigned long addr,
+ struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->setup);
+
+ reg &= ~IDECTRL_DIORN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->recover);
+
+ return ep93xx_readl(base, IDEDATAIN);
+}
+
+static void ep93xx_ide_write(unsigned long base, u16 value,
+ unsigned long addr, struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->setup);
+
+ ep93xx_writel(value, base, IDEDATAOUT);
+
+ reg &= ~IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->recover);
+}
+
+static void ep93xx_ide_readsw(unsigned long base, unsigned long addr,
+ void *buf, unsigned int len, struct ide_timing *timing)
+{
+ u16 *data = (u16 *) buf;
+
+ while (len--)
+ *data++ = cpu_to_le16(ep93xx_ide_read(base, addr, timing));
+}
+
+static void ep93xx_ide_writesw(unsigned long base, unsigned long addr,
+ void *buf, unsigned int len, struct ide_timing *timing)
+{
+ u16 *data = (u16 *) buf;
+
+ while (len--)
+ ep93xx_ide_write(base, le16_to_cpu(*data++), addr, timing);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, cmd, hwif->io_ports.command_addr, t);
+}
+
+static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ return ep93xx_ide_read(hwif->config_data, hwif->io_ports.status_addr, t);
+}
+
+static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ return ep93xx_ide_read(hwif->config_data, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, ctl, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_dev_select(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 select = drive->select | ATA_DEVICE_OBS;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, select, hwif->io_ports.device_addr, t);
+}
+
+static void
+ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ if (valid & IDE_VALID_FEATURE)
+ ep93xx_ide_write(hwif->config_data, tf->feature,
+ io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ ep93xx_ide_write(hwif->config_data, tf->nsect,
+ io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ ep93xx_ide_write(hwif->config_data, tf->lbal,
+ io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ ep93xx_ide_write(hwif->config_data, tf->lbam,
+ io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ ep93xx_ide_write(hwif->config_data, tf->lbah,
+ io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ ep93xx_ide_write(hwif->config_data, tf->device,
+ io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ if (valid & IDE_VALID_ERROR)
+ tf->error = ep93xx_ide_read(hwif->config_data,
+ io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ tf->nsect = ep93xx_ide_read(hwif->config_data,
+ io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ tf->lbal = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ tf->lbam = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ tf->lbah = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ tf->device = ep93xx_ide_read(hwif->config_data,
+ io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_readsw(hwif->config_data, io_ports->data_addr, buf, words, t);
+}
+
+static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_writesw(hwif->config_data, io_ports->data_addr, buf, words, t);
+}
+
+static struct ide_tp_ops ep93xx_ide_tp_ops = {
+ .exec_command = ep93xx_ide_exec_command,
+ .read_status = ep93xx_ide_read_status,
+ .read_altstatus = ep93xx_ide_read_altstatus,
+ .write_devctl = ep93xx_ide_write_devctl,
+ .dev_select = ep93xx_ide_dev_select,
+ .tf_load = ep93xx_ide_tf_load,
+ .tf_read = ep93xx_ide_tf_read,
+ .input_data = ep93xx_ide_input_data,
+ .output_data = ep93xx_ide_output_data,
+};
+
+static void ep93xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+{
+ struct ide_timing *timing;
+ u32 reg = IDECFG_IDEEN | IDECFG_PIO;
+
+ timing = ide_timing_find_mode(XFER_PIO_0 + pio);
+ /*
+ * store the adequate PIO mode timings in the ide_drive_t
+ * 'drive_data' field, to be used later by ep93xx_ide_{read,write}
+ */
+ ide_set_drivedata(drive, (void *)timing);
+
+ /* 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;
+ }
+ ep93xx_writel(reg, drive->hwif->config_data, IDECFG);
+}
+
+static struct ide_port_ops ep93xx_ide_port_ops = {
+ .set_pio_mode = ep93xx_set_pio_mode,
+};
+
+static struct ide_port_info ep93xx_ide_port_info = {
+ .name = MODULE_NAME,
+ .tp_ops = &ep93xx_ide_tp_ops,
+ .port_ops = &ep93xx_ide_port_ops,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
+ IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
+ IDE_HFLAG_NO_UNMASK_IRQS,
+ .pio_mask = ATA_PIO4,
+};
+
+static int __init ep93xx_ide_probe(struct platform_device *pdev)
+{
+ int retval;
+ int irq;
+ void __iomem *ide_base;
+ struct resource *mem_res;
+ hw_regs_t hw;
+ hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_host *host;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ if (!request_mem_region
+ (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
+ dev_err(&pdev->dev, "could not request memory resources!\n");
+ return -EBUSY;
+ }
+
+ ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(&pdev->dev, "could not map memory resources!\n");
+ retval = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ /*
+ * fill in ide_io_ports structure.
+ * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
+ * hence the lowest 3 bits will give us the real address (ranging from
+ * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
+ * values:
+ * CS1n CS0n A2 A1 A0
+ * 1 0 x x x -> IO_ADDR (base 0x10)
+ * 0 1 x x x -> CTL_ADDR (base 0x0E)
+ */
+ ide_std_init_ports(&hw, 0x10, 0x0E);
+
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+ hw.config = (unsigned long)ide_base;
+
+ /* enforce EP93xx IDE controller reset state */
+ ep93xx_ide_clean_regs(hw.config);
+
+ /* set gpio port E, G and H for IDE */
+ ep93xx_ide_aquire_gpios();
+
+ /*
+ * configure IDE interface:
+ * - IDE Master Enable
+ * - Polled IO Operation
+ * - PIO Mode 0 -> 3.33 MBps
+ * - 3 Wait States -> 30 ns
+ */
+ ep93xx_writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_0 |
+ ((3 << 8) & IDECFG_WST), hw.config, IDECFG);
+
+ retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "unable to register EP93xx IDE driver!\n");
+ goto fail_unmap;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
+
+ return 0;
+
+fail_unmap:
+ iounmap(ide_base);
+fail_release_mem:
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+ return retval;
+}
+
+static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ struct ide_host *host = pdev->dev.driver_data;
+
+ /* reset state */
+ ep93xx_ide_clean_regs(host->ports[0]->config_data);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ ep93xx_ide_release_gpios();
+
+ ide_host_remove(host);
+
+ iounmap((void __iomem *)(host->ports[0]->config_data));
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_ide_driver = {
+ .remove = __exit_p(ep93xx_ide_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ep93xx_ide_init(void)
+{
+ return platform_driver_probe(&ep93xx_ide_driver, ep93xx_ide_probe);
+}
+module_init(ep93xx_ide_init);
+
+static void __exit ep93xx_ide_exit(void)
+{
+ platform_driver_unregister(&ep93xx_ide_driver);
+}
+module_exit(ep93xx_ide_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joao Ramos <joao.ramos@inov.pt>");
+MODULE_DESCRIPTION("EP93xx IDE host controller driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ep93xx-ide");
diff -urN linux-2.6.30-rc4.orig/drivers/ide/Kconfig linux-2.6.30-rc4/drivers/ide/Kconfig
--- linux-2.6.30-rc4.orig/drivers/ide/Kconfig 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/Kconfig 2009-05-13 14:41:39.000000000 +0100
@@ -732,6 +732,19 @@
depends on ARM && ARCH_AT91 && !ARCH_AT91RM9200 && !ARCH_AT91X40
select IDE_TIMINGS
+config BLK_DEV_IDE_EP93XX
+ tristate "Cirrus Logic EPxx (EP9312, EP9315) IDE support"
+ depends on ARM && ARCH_EP93XX
+ select IDE_TIMINGS
+ help
+ This is a host controller driver for IDE hardware included in
+ Cirrus Logic's EP9312 and EP9315 CPUs.
+ Say Y here if you want to enable the IDE host controller support
+ for your machine.
+
+ Choose 'M' to compile this driver as a module; the module will be
+ called 'ep93xx-ide'.
+
config BLK_DEV_IDE_ICSIDE
tristate "ICS IDE interface support"
depends on ARM && ARCH_ACORN
diff -urN linux-2.6.30-rc4.orig/drivers/ide/Makefile linux-2.6.30-rc4/drivers/ide/Makefile
--- linux-2.6.30-rc4.orig/drivers/ide/Makefile 2009-04-30 05:48:16.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/Makefile 2009-05-13 14:41:39.000000000 +0100
@@ -117,3 +117,4 @@
obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o
obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o
obj-$(CONFIG_BLK_DEV_IDE_AT91) += at91_ide.o
+obj-$(CONFIG_BLK_DEV_IDE_EP93XX) += ep93xx-ide.o
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
2009-05-13 11:01 ` João Ramos
2009-05-13 14:18 ` João Ramos
@ 2009-05-14 16:30 ` Sergei Shtylyov
2009-05-14 16:36 ` Sergei Shtylyov
2 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-14 16:30 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: João Ramos, Alan Cox, linux-ide
Hello.
Bartlomiej Zolnierkiewicz wrote:
>>>>I'm not sure what you're getting at with "such advices"... :)
>>>>We need to cast somewhere anyway so we may as well cast from 'void *' to
>>>>'unsigned int' where needed and not the other way around (or rather from
>>>>'unsigned int' to 'struct ide_timing *') -- which would be an ugly hack
>>>>and could cause maintainability/portability problems later.
>>>>BTW it seems like a good occasion to add ide_{get,set}_drivedata()
>>>>helpers
>>>>(ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and thus
>>>>make
>>>>internal IDE API more coherent.
>>>>[ Yes, I know that we may get away with s/unsigned int/unsigned long/
>>>> and casting it to 'struct ide_timing *' for now but it always better
>>>> to at least consider more elegant solution first... ]
>> Changing a lot of drivers is more elegant than not? Doubt it.
>> And don't tell me this patch is "elegant"... :-/
> Well, it is makes code more coherent and more maintainable...
> Moreover those inline helpers serve as a part of documentation (though
> real DocBook documentation would also be nice) for host drivers authors.
> Either this or we should remove ide_{get,set}_hwifdata() and allow
> host drivers to poke directly also at hwif->hwif_data...
Seems like a good idea. I'm not sure why these fields should be special.
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-14 16:30 ` Sergei Shtylyov
@ 2009-05-14 16:36 ` Sergei Shtylyov
2009-05-14 18:58 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-14 16:36 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: João Ramos, Alan Cox, linux-ide
Hello, I wrote:
>>>>> I'm not sure what you're getting at with "such advices"... :)
>>>>> We need to cast somewhere anyway so we may as well cast from 'void
>>>>> *' to
>>>>> 'unsigned int' where needed and not the other way around (or rather
>>>>> from
>>>>> 'unsigned int' to 'struct ide_timing *') -- which would be an ugly
>>>>> hack
>>>>> and could cause maintainability/portability problems later.
>>>>> BTW it seems like a good occasion to add ide_{get,set}_drivedata()
>>>>> helpers
>>>>> (ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and
>>>>> thus make
>>>>> internal IDE API more coherent.
>>>>> [ Yes, I know that we may get away with s/unsigned int/unsigned long/
>>>>> and casting it to 'struct ide_timing *' for now but it always better
>>>>> to at least consider more elegant solution first... ]
>>> Changing a lot of drivers is more elegant than not? Doubt it.
>>> And don't tell me this patch is "elegant"... :-/
>> Well, it is makes code more coherent and more maintainable...
>> Moreover those inline helpers serve as a part of documentation (though
>> real DocBook documentation would also be nice) for host drivers authors.
>> Either this or we should remove ide_{get,set}_hwifdata() and allow
>> host drivers to poke directly also at hwif->hwif_data...
Besides, some drivers like siimage.c do poke hwif->hwif_data directly.
> Seems like a good idea. I'm not sure why these fields should be special.
While there are a number of 'ide_hwif_t' fields like 'select_data' or
'config_data' that are being poked directly...
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-14 16:36 ` Sergei Shtylyov
@ 2009-05-14 18:58 ` Bartlomiej Zolnierkiewicz
0 siblings, 0 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-14 18:58 UTC (permalink / raw)
To: Sergei Shtylyov; +Cc: João Ramos, Alan Cox, linux-ide
On Thursday 14 May 2009 18:36:39 Sergei Shtylyov wrote:
> Hello, I wrote:
>
> >>>>> I'm not sure what you're getting at with "such advices"... :)
>
> >>>>> We need to cast somewhere anyway so we may as well cast from 'void
> >>>>> *' to
> >>>>> 'unsigned int' where needed and not the other way around (or rather
> >>>>> from
> >>>>> 'unsigned int' to 'struct ide_timing *') -- which would be an ugly
> >>>>> hack
> >>>>> and could cause maintainability/portability problems later.
>
> >>>>> BTW it seems like a good occasion to add ide_{get,set}_drivedata()
> >>>>> helpers
> >>>>> (ala existing ide_{get,set}_hwifdata() ones) to <linux/ide.h> and
> >>>>> thus make
> >>>>> internal IDE API more coherent.
>
> >>>>> [ Yes, I know that we may get away with s/unsigned int/unsigned long/
> >>>>> and casting it to 'struct ide_timing *' for now but it always better
> >>>>> to at least consider more elegant solution first... ]
>
> >>> Changing a lot of drivers is more elegant than not? Doubt it.
>
> >>> And don't tell me this patch is "elegant"... :-/
>
> >> Well, it is makes code more coherent and more maintainable...
>
> >> Moreover those inline helpers serve as a part of documentation (though
> >> real DocBook documentation would also be nice) for host drivers authors.
>
> >> Either this or we should remove ide_{get,set}_hwifdata() and allow
> >> host drivers to poke directly also at hwif->hwif_data...
>
> Besides, some drivers like siimage.c do poke hwif->hwif_data directly.
siimage.c seems to be the only driver doing it so this is rather exception
from the rule...
[ modulo one occurrence in scc_pata.c but this driver uses inline helpers ]
> > Seems like a good idea. I'm not sure why these fields should be special.
>
> While there are a number of 'ide_hwif_t' fields like 'select_data' or
> 'config_data' that are being poked directly...
I think that it would be better to remove these extra fields and stop
pretending that they are used for storing similar things across different
host drivers (cause they are not and things stored there are host driver
specific)...
If host driver needs more "local storage" it should allocate private data
structures and use ide_{get,set}_hwifdata() to access them (like it821x.c).
With a bit of invention[1] it seems to be entirely possible to switch all
current users (maybe except tc86c001.c, though I'm not sure) of ->config_data
and ->select_data to just use ->hwif_data...
Once I find some time I'll try to cook some patches to see how it looks
(unless of course somebody beats me to it)...
[1] i.e.:
* scc_pata.c: we may just use pci_get_drvdata()
(+ none of hwif->config_data users is in the hot-path)
* trm290.c: after removing dead (maybe even since the initial merge
years ago) DMA support hwif->select_data will no longer be needed
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-13 14:18 ` João Ramos
@ 2009-05-14 19:44 ` Bartlomiej Zolnierkiewicz
2009-05-15 17:01 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-14 19:44 UTC (permalink / raw)
To: João Ramos; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
On Wednesday 13 May 2009 16:18:39 João Ramos wrote:
> Hi,
>
> In attachment I send the revised patch for the EP93xx CPU IDE controller
> (well, not entirely, only the IDE bit, I letf out the platform code as
> suggested), changed and fixed according to all the comments you made before.
>
> This driver depends on the previous patches I sent (ide_drive_data patch
> and set_pio_mode patch).
>
> Please make your suggestions.
I still need to go over previous patches but here are some comments against
ep93xx-ide.c itself...
diff -urN linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c
--- linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c 2009-05-13 14:47:02.000000000 +0100
@@ -0,0 +1,528 @@
+/*
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * IDE host controller driver.
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ * 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.
+ */
+
+#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/ide.h>
+#include <linux/delay.h>
+
+#define MODULE_NAME "ep93xx-ide"
+
+/*
+ * 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.
+ */
+
+/*
+ * IDE Register offset
+ */
+#define IDECTRL 0x00
+#define IDECFG 0x04
+#define IDEMDMAOP 0x08
+#define IDEUDMAOP 0x0C
+#define IDEDATAOUT 0x10
+#define IDEDATAIN 0x14
+#define IDEMDMADATAOUT 0x18
+#define IDEMDMADATAIN 0x1C
+#define IDEUDMADATAOUT 0x20
+#define IDEUDMADATAIN 0x24
+#define IDEUDMASTS 0x28
+#define IDEUDMADEBUG 0x2C
+#define IDEUDMAWRBUFSTS 0x30
+#define IDEUDMARDBUFSTS 0x34
+
+/*
+ * IDE Control Register bit fields
+ */
+#define IDECTRL_CS0N 0x00000001
+#define IDECTRL_CS1N 0x00000002
+#define IDECTRL_DA 0x0000001C
+#define IDECTRL_DIORN 0x00000020
+#define IDECTRL_DIOWN 0x00000040
+#define IDECTRL_DASPN 0x00000080
+#define IDECTRL_DMARQ 0x00000100
+#define IDECTRL_INTRQ 0x00000200
+#define IDECTRL_IORDY 0x00000400
+
+/*
+ * IDE Configuration Register bit fields
+ */
+#define IDECFG_IDEEN 0x00000001
+#define IDECFG_PIO 0x00000002
+#define IDECFG_MDMA 0x00000004
+#define IDECFG_UDMA 0x00000008
+#define IDECFG_PIO_MODE_0 0x00000000
+#define IDECFG_PIO_MODE_1 0x00000010
+#define IDECFG_PIO_MODE_2 0x00000020
+#define IDECFG_PIO_MODE_3 0x00000030
+#define IDECFG_PIO_MODE_4 0x00000040
+#define IDECFG_MDMA_MODE_0 0x00000000
+#define IDECFG_MDMA_MODE_1 0x00000010
+#define IDECFG_MDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_0 0x00000000
+#define IDECFG_UDMA_MODE_1 0x00000010
+#define IDECFG_UDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_3 0x00000030
+#define IDECFG_UDMA_MODE_4 0x00000040
+#define IDECFG_WST 0x00000300
+
+/*
+ * readl/writel helpers to access internal registers using
+ * an ioremapped cookie and the specified IDE register offset
+ */
+
+static inline u32 ep93xx_readl(unsigned long base, u8 offset)
+{
+ return readl((void __iomem *)(base + offset));
+}
+
+static inline void ep93xx_writel(u32 value, unsigned long base, u8 offset)
+{
+ writel(value, (void __iomem *)(base + offset));
+}
Hmm, what do we need these wrappers for exactly?
Please remove them.
+/*
+ * Check whether IORDY is asserted or not.
+ */
+static inline int ep93xx_ide_check_iordy(unsigned long base)
+{
+ u32 reg = ep93xx_readl(base, IDECTRL);
+
+ return (reg & IDECTRL_IORDY) ? 1 : 0;
+}
+
+/*
+ * IDE Interface register map default state
+ * (shutdown)
+ */
+static void ep93xx_ide_clean_regs(unsigned long base)
+{
+ /* disable IDE interface initially */
+ ep93xx_writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
+ IDECTRL_DIOWN, base, IDECTRL);
+
+ /* clear IDE registers */
+ ep93xx_writel(0, base, IDECFG);
+ ep93xx_writel(0, base, IDEMDMAOP);
+ ep93xx_writel(0, base, IDEUDMAOP);
+ ep93xx_writel(0, base, IDEDATAOUT);
+ ep93xx_writel(0, base, IDEDATAIN);
+ ep93xx_writel(0, base, IDEMDMADATAOUT);
+ ep93xx_writel(0, base, IDEMDMADATAIN);
+ ep93xx_writel(0, base, IDEUDMADATAOUT);
+ ep93xx_writel(0, base, IDEUDMADATAIN);
+ ep93xx_writel(0, base, IDEUDMADEBUG);
+}
+
+static u16 ep93xx_ide_read(unsigned long base, unsigned long addr,
+ struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->setup);
+
+ reg &= ~IDECTRL_DIORN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->recover);
+
+ return ep93xx_readl(base, IDEDATAIN);
+}
+
+static void ep93xx_ide_write(unsigned long base, u16 value,
+ unsigned long addr, struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
+ IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->setup);
+
+ ep93xx_writel(value, base, IDEDATAOUT);
+
+ reg &= ~IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ ep93xx_writel(reg, base, IDECTRL);
+ ndelay(timing->recover);
+}
+
+static void ep93xx_ide_readsw(unsigned long base, unsigned long addr,
+ void *buf, unsigned int len, struct ide_timing *timing)
+{
+ u16 *data = (u16 *) buf;
+
+ while (len--)
+ *data++ = cpu_to_le16(ep93xx_ide_read(base, addr, timing));
+}
+
+static void ep93xx_ide_writesw(unsigned long base, unsigned long addr,
+ void *buf, unsigned int len, struct ide_timing *timing)
+{
+ u16 *data = (u16 *) buf;
+
+ while (len--)
+ ep93xx_ide_write(base, le16_to_cpu(*data++), addr, timing);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
+{
+ ide_drive_t *drive = hwif->devices[0];
Actually I was suggesting something else:
in ->set_pio_mode do something like:
ide_drive_t *pair = ide_get_pair_dev(drive);
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
struct ide_timing *cmd_t = t;
if (pair)
struct ide_timing *pair_t = ide_get_drivedata(pair);
if (pair_t && pair_t->mode < t->mode)
cmd_t = pair_t;
}
ide_set_drivedata(drive, (void *)t);
ide_set_hwifdata(hwif, (void *)cmd_t);
so you can later use ide_get_hwifdata() in everything except
ep93xx_ide_readsw() and ep93xx_ide_write().
It allows proper support of two devices on the port and doesn't access
hwif->devices directly.
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, cmd, hwif->io_ports.command_addr, t);
+}
+
+static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ return ep93xx_ide_read(hwif->config_data, hwif->io_ports.status_addr, t);
+}
+
+static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ return ep93xx_ide_read(hwif->config_data, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+ ide_drive_t *drive = hwif->devices[0];
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, ctl, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_dev_select(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 select = drive->select | ATA_DEVICE_OBS;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_write(hwif->config_data, select, hwif->io_ports.device_addr, t);
+}
+
+static void
+ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ if (valid & IDE_VALID_FEATURE)
+ ep93xx_ide_write(hwif->config_data, tf->feature,
+ io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ ep93xx_ide_write(hwif->config_data, tf->nsect,
+ io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ ep93xx_ide_write(hwif->config_data, tf->lbal,
+ io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ ep93xx_ide_write(hwif->config_data, tf->lbam,
+ io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ ep93xx_ide_write(hwif->config_data, tf->lbah,
+ io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ ep93xx_ide_write(hwif->config_data, tf->device,
+ io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ if (valid & IDE_VALID_ERROR)
+ tf->error = ep93xx_ide_read(hwif->config_data,
+ io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ tf->nsect = ep93xx_ide_read(hwif->config_data,
+ io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ tf->lbal = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ tf->lbam = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ tf->lbah = ep93xx_ide_read(hwif->config_data,
+ io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ tf->device = ep93xx_ide_read(hwif->config_data,
+ io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_readsw(hwif->config_data, io_ports->data_addr, buf, words, t);
ep93xx_ide_readsw() is used only here so it makes sense to inline its
content here
+}
+
+static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
+
+ ep93xx_ide_writesw(hwif->config_data, io_ports->data_addr, buf, words, t);
ditto
+}
+
+static struct ide_tp_ops ep93xx_ide_tp_ops = {
+ .exec_command = ep93xx_ide_exec_command,
+ .read_status = ep93xx_ide_read_status,
+ .read_altstatus = ep93xx_ide_read_altstatus,
+ .write_devctl = ep93xx_ide_write_devctl,
+ .dev_select = ep93xx_ide_dev_select,
+ .tf_load = ep93xx_ide_tf_load,
+ .tf_read = ep93xx_ide_tf_read,
+ .input_data = ep93xx_ide_input_data,
+ .output_data = ep93xx_ide_output_data,
+};
Indentation:
.exec_command = ep93xx_ide_exec_command,
.read_status = ep93xx_ide_read_status,
...
+static void ep93xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
+{
+ struct ide_timing *timing;
+ u32 reg = IDECFG_IDEEN | IDECFG_PIO;
+
+ timing = ide_timing_find_mode(XFER_PIO_0 + pio);
+ /*
+ * store the adequate PIO mode timings in the ide_drive_t
+ * 'drive_data' field, to be used later by ep93xx_ide_{read,write}
+ */
+ ide_set_drivedata(drive, (void *)timing);
+
+ /* 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;
+ }
+ ep93xx_writel(reg, drive->hwif->config_data, IDECFG);
+}
+
+static struct ide_port_ops ep93xx_ide_port_ops = {
+ .set_pio_mode = ep93xx_set_pio_mode,
+};
+
+static struct ide_port_info ep93xx_ide_port_info = {
+ .name = MODULE_NAME,
+ .tp_ops = &ep93xx_ide_tp_ops,
+ .port_ops = &ep93xx_ide_port_ops,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
+ IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
+ IDE_HFLAG_NO_UNMASK_IRQS,
+ .pio_mask = ATA_PIO4,
+};
Indentation.
+static int __init ep93xx_ide_probe(struct platform_device *pdev)
+{
+ int retval;
+ int irq;
+ void __iomem *ide_base;
+ struct resource *mem_res;
+ hw_regs_t hw;
+ hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_host *host;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ if (!request_mem_region
+ (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
+ dev_err(&pdev->dev, "could not request memory resources!\n");
+ return -EBUSY;
+ }
+
+ ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(&pdev->dev, "could not map memory resources!\n");
+ retval = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ /*
+ * fill in ide_io_ports structure.
+ * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
+ * hence the lowest 3 bits will give us the real address (ranging from
+ * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
+ * values:
+ * CS1n CS0n A2 A1 A0
+ * 1 0 x x x -> IO_ADDR (base 0x10)
+ * 0 1 x x x -> CTL_ADDR (base 0x0E)
+ */
+ ide_std_init_ports(&hw, 0x10, 0x0E);
Why not just setup the real addresses here instead of using
ide_std_init_ports()?
It seems that it would make driver simpler and get rid of >config_data use.
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+ hw.config = (unsigned long)ide_base;
+
+ /* enforce EP93xx IDE controller reset state */
+ ep93xx_ide_clean_regs(hw.config);
+
+ /* set gpio port E, G and H for IDE */
+ ep93xx_ide_aquire_gpios();
+
+ /*
+ * configure IDE interface:
+ * - IDE Master Enable
+ * - Polled IO Operation
+ * - PIO Mode 0 -> 3.33 MBps
+ * - 3 Wait States -> 30 ns
+ */
+ ep93xx_writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_0 |
+ ((3 << 8) & IDECFG_WST), hw.config, IDECFG);
+
+ retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "unable to register EP93xx IDE driver!\n");
+ goto fail_unmap;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
+
+ return 0;
+
+fail_unmap:
+ iounmap(ide_base);
+fail_release_mem:
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+ return retval;
+}
+
+static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ struct ide_host *host = pdev->dev.driver_data;
+
+ /* reset state */
+ ep93xx_ide_clean_regs(host->ports[0]->config_data);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ ep93xx_ide_release_gpios();
+
+ ide_host_remove(host);
+
+ iounmap((void __iomem *)(host->ports[0]->config_data));
'host' access but 'host' is already gone...
IOW host->ports[0]->config_data needs to be cached in local variable
before ide_host_remove() is called...
Thanks,
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-14 19:44 ` Bartlomiej Zolnierkiewicz
@ 2009-05-15 17:01 ` João Ramos
2009-05-17 16:16 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-15 17:01 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
Bartlomiej Zolnierkiewicz escreveu:
> On Wednesday 13 May 2009 16:18:39 João Ramos wrote:
>
>> Hi,
>>
>> In attachment I send the revised patch for the EP93xx CPU IDE controller
>> (well, not entirely, only the IDE bit, I letf out the platform code as
>> suggested), changed and fixed according to all the comments you made before.
>>
>> This driver depends on the previous patches I sent (ide_drive_data patch
>> and set_pio_mode patch).
>>
>> Please make your suggestions.
>>
>
> I still need to go over previous patches but here are some comments against
> ep93xx-ide.c itself...
>
> diff -urN linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c
> --- linux-2.6.30-rc4.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.30-rc4/drivers/ide/ep93xx-ide.c 2009-05-13 14:47:02.000000000 +0100
> @@ -0,0 +1,528 @@
> +/*
> + * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
> + * IDE host controller driver.
> + *
> + * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
> + * 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.
> + */
> +
> +#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/ide.h>
> +#include <linux/delay.h>
> +
> +#define MODULE_NAME "ep93xx-ide"
> +
> +/*
> + * 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.
> + */
> +
> +/*
> + * IDE Register offset
> + */
> +#define IDECTRL 0x00
> +#define IDECFG 0x04
> +#define IDEMDMAOP 0x08
> +#define IDEUDMAOP 0x0C
> +#define IDEDATAOUT 0x10
> +#define IDEDATAIN 0x14
> +#define IDEMDMADATAOUT 0x18
> +#define IDEMDMADATAIN 0x1C
> +#define IDEUDMADATAOUT 0x20
> +#define IDEUDMADATAIN 0x24
> +#define IDEUDMASTS 0x28
> +#define IDEUDMADEBUG 0x2C
> +#define IDEUDMAWRBUFSTS 0x30
> +#define IDEUDMARDBUFSTS 0x34
> +
> +/*
> + * IDE Control Register bit fields
> + */
> +#define IDECTRL_CS0N 0x00000001
> +#define IDECTRL_CS1N 0x00000002
> +#define IDECTRL_DA 0x0000001C
> +#define IDECTRL_DIORN 0x00000020
> +#define IDECTRL_DIOWN 0x00000040
> +#define IDECTRL_DASPN 0x00000080
> +#define IDECTRL_DMARQ 0x00000100
> +#define IDECTRL_INTRQ 0x00000200
> +#define IDECTRL_IORDY 0x00000400
> +
> +/*
> + * IDE Configuration Register bit fields
> + */
> +#define IDECFG_IDEEN 0x00000001
> +#define IDECFG_PIO 0x00000002
> +#define IDECFG_MDMA 0x00000004
> +#define IDECFG_UDMA 0x00000008
> +#define IDECFG_PIO_MODE_0 0x00000000
> +#define IDECFG_PIO_MODE_1 0x00000010
> +#define IDECFG_PIO_MODE_2 0x00000020
> +#define IDECFG_PIO_MODE_3 0x00000030
> +#define IDECFG_PIO_MODE_4 0x00000040
> +#define IDECFG_MDMA_MODE_0 0x00000000
> +#define IDECFG_MDMA_MODE_1 0x00000010
> +#define IDECFG_MDMA_MODE_2 0x00000020
> +#define IDECFG_UDMA_MODE_0 0x00000000
> +#define IDECFG_UDMA_MODE_1 0x00000010
> +#define IDECFG_UDMA_MODE_2 0x00000020
> +#define IDECFG_UDMA_MODE_3 0x00000030
> +#define IDECFG_UDMA_MODE_4 0x00000040
> +#define IDECFG_WST 0x00000300
> +
> +/*
> + * readl/writel helpers to access internal registers using
> + * an ioremapped cookie and the specified IDE register offset
> + */
> +
> +static inline u32 ep93xx_readl(unsigned long base, u8 offset)
> +{
> + return readl((void __iomem *)(base + offset));
> +}
> +
> +static inline void ep93xx_writel(u32 value, unsigned long base, u8 offset)
> +{
> + writel(value, (void __iomem *)(base + offset));
> +}
>
> Hmm, what do we need these wrappers for exactly?
>
> Please remove them.
>
I just added those to increase code readability, so that I wouldn't have
to do a '(void __iomem *)(base + offset)' in every readl/writel call.
But I can remove it, no problem.
> +/*
> + * Check whether IORDY is asserted or not.
> + */
> +static inline int ep93xx_ide_check_iordy(unsigned long base)
> +{
> + u32 reg = ep93xx_readl(base, IDECTRL);
> +
> + return (reg & IDECTRL_IORDY) ? 1 : 0;
> +}
> +
> +/*
> + * IDE Interface register map default state
> + * (shutdown)
> + */
> +static void ep93xx_ide_clean_regs(unsigned long base)
> +{
> + /* disable IDE interface initially */
> + ep93xx_writel(IDECTRL_CS0N | IDECTRL_CS1N | IDECTRL_DIORN |
> + IDECTRL_DIOWN, base, IDECTRL);
> +
> + /* clear IDE registers */
> + ep93xx_writel(0, base, IDECFG);
> + ep93xx_writel(0, base, IDEMDMAOP);
> + ep93xx_writel(0, base, IDEUDMAOP);
> + ep93xx_writel(0, base, IDEDATAOUT);
> + ep93xx_writel(0, base, IDEDATAIN);
> + ep93xx_writel(0, base, IDEMDMADATAOUT);
> + ep93xx_writel(0, base, IDEMDMADATAIN);
> + ep93xx_writel(0, base, IDEUDMADATAOUT);
> + ep93xx_writel(0, base, IDEUDMADATAIN);
> + ep93xx_writel(0, base, IDEUDMADEBUG);
> +}
> +
> +static u16 ep93xx_ide_read(unsigned long base, unsigned long addr,
> + struct ide_timing *timing)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->setup);
> +
> + reg &= ~IDECTRL_DIORN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->active);
> +
> + while (!ep93xx_ide_check_iordy(base))
> + cpu_relax();
> +
> + reg |= IDECTRL_DIORN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->recover);
> +
> + return ep93xx_readl(base, IDEDATAIN);
> +}
> +
> +static void ep93xx_ide_write(unsigned long base, u16 value,
> + unsigned long addr, struct ide_timing *timing)
> +{
> + u32 reg;
> +
> + reg = ((addr & 0x07) << 2) | ((addr >> 3) & 0x03) | IDECTRL_DIORN |
> + IDECTRL_DIOWN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->setup);
> +
> + ep93xx_writel(value, base, IDEDATAOUT);
> +
> + reg &= ~IDECTRL_DIOWN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->active);
> +
> + while (!ep93xx_ide_check_iordy(base))
> + cpu_relax();
> +
> + reg |= IDECTRL_DIOWN;
> + ep93xx_writel(reg, base, IDECTRL);
> + ndelay(timing->recover);
> +}
> +
> +static void ep93xx_ide_readsw(unsigned long base, unsigned long addr,
> + void *buf, unsigned int len, struct ide_timing *timing)
> +{
> + u16 *data = (u16 *) buf;
> +
> + while (len--)
> + *data++ = cpu_to_le16(ep93xx_ide_read(base, addr, timing));
> +}
> +
> +static void ep93xx_ide_writesw(unsigned long base, unsigned long addr,
> + void *buf, unsigned int len, struct ide_timing *timing)
> +{
> + u16 *data = (u16 *) buf;
> +
> + while (len--)
> + ep93xx_ide_write(base, le16_to_cpu(*data++), addr, timing);
> +}
> +
> +/*
> + * EP93xx IDE PIO Transport Operations
> + */
> +
> +static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
> +{
> + ide_drive_t *drive = hwif->devices[0];
>
> Actually I was suggesting something else:
>
> in ->set_pio_mode do something like:
>
> ide_drive_t *pair = ide_get_pair_dev(drive);
> struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
> struct ide_timing *cmd_t = t;
>
> if (pair)
> struct ide_timing *pair_t = ide_get_drivedata(pair);
>
> if (pair_t && pair_t->mode < t->mode)
> cmd_t = pair_t;
> }
>
> ide_set_drivedata(drive, (void *)t);
> ide_set_hwifdata(hwif, (void *)cmd_t);
>
> so you can later use ide_get_hwifdata() in everything except
> ep93xx_ide_readsw() and ep93xx_ide_write().
>
Ok.
> It allows proper support of two devices on the port and doesn't access
> hwif->devices directly.
>
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + ep93xx_ide_write(hwif->config_data, cmd, hwif->io_ports.command_addr, t);
> +}
> +
> +static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
> +{
> + ide_drive_t *drive = hwif->devices[0];
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + return ep93xx_ide_read(hwif->config_data, hwif->io_ports.status_addr, t);
> +}
> +
> +static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
> +{
> + ide_drive_t *drive = hwif->devices[0];
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + return ep93xx_ide_read(hwif->config_data, hwif->io_ports.ctl_addr, t);
> +}
> +
> +static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
> +{
> + ide_drive_t *drive = hwif->devices[0];
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + ep93xx_ide_write(hwif->config_data, ctl, hwif->io_ports.ctl_addr, t);
> +}
> +
> +static void ep93xx_ide_dev_select(ide_drive_t *drive)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + u8 select = drive->select | ATA_DEVICE_OBS;
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + ep93xx_ide_write(hwif->config_data, select, hwif->io_ports.device_addr, t);
> +}
> +
> +static void
> +ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf, u8 valid)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + struct ide_io_ports *io_ports = &hwif->io_ports;
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + if (valid & IDE_VALID_FEATURE)
> + ep93xx_ide_write(hwif->config_data, tf->feature,
> + io_ports->feature_addr, t);
> + if (valid & IDE_VALID_NSECT)
> + ep93xx_ide_write(hwif->config_data, tf->nsect,
> + io_ports->nsect_addr, t);
> + if (valid & IDE_VALID_LBAL)
> + ep93xx_ide_write(hwif->config_data, tf->lbal,
> + io_ports->lbal_addr, t);
> + if (valid & IDE_VALID_LBAM)
> + ep93xx_ide_write(hwif->config_data, tf->lbam,
> + io_ports->lbam_addr, t);
> + if (valid & IDE_VALID_LBAH)
> + ep93xx_ide_write(hwif->config_data, tf->lbah,
> + io_ports->lbah_addr, t);
> + if (valid & IDE_VALID_DEVICE)
> + ep93xx_ide_write(hwif->config_data, tf->device,
> + io_ports->device_addr, t);
> +}
> +
> +static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
> + u8 valid)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + struct ide_io_ports *io_ports = &hwif->io_ports;
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + if (valid & IDE_VALID_ERROR)
> + tf->error = ep93xx_ide_read(hwif->config_data,
> + io_ports->feature_addr, t);
> + if (valid & IDE_VALID_NSECT)
> + tf->nsect = ep93xx_ide_read(hwif->config_data,
> + io_ports->nsect_addr, t);
> + if (valid & IDE_VALID_LBAL)
> + tf->lbal = ep93xx_ide_read(hwif->config_data,
> + io_ports->lbal_addr, t);
> + if (valid & IDE_VALID_LBAM)
> + tf->lbam = ep93xx_ide_read(hwif->config_data,
> + io_ports->lbam_addr, t);
> + if (valid & IDE_VALID_LBAH)
> + tf->lbah = ep93xx_ide_read(hwif->config_data,
> + io_ports->lbah_addr, t);
> + if (valid & IDE_VALID_DEVICE)
> + tf->device = ep93xx_ide_read(hwif->config_data,
> + io_ports->device_addr, t);
> +}
> +
> +static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
> + void *buf, unsigned int len)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + struct ide_io_ports *io_ports = &hwif->io_ports;
> + unsigned int words = (len + 1) >> 1;
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + ep93xx_ide_readsw(hwif->config_data, io_ports->data_addr, buf, words, t);
>
> ep93xx_ide_readsw() is used only here so it makes sense to inline its
> content here
>
Ok.
> +}
> +
> +static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
> + void *buf, unsigned int len)
> +{
> + ide_hwif_t *hwif = drive->hwif;
> + struct ide_io_ports *io_ports = &hwif->io_ports;
> + unsigned int words = (len + 1) >> 1;
> + struct ide_timing *t = (struct ide_timing *) ide_get_drivedata(drive);
> +
> + ep93xx_ide_writesw(hwif->config_data, io_ports->data_addr, buf, words, t);
>
> ditto
>
> +}
> +
> +static struct ide_tp_ops ep93xx_ide_tp_ops = {
> + .exec_command = ep93xx_ide_exec_command,
> + .read_status = ep93xx_ide_read_status,
> + .read_altstatus = ep93xx_ide_read_altstatus,
> + .write_devctl = ep93xx_ide_write_devctl,
> + .dev_select = ep93xx_ide_dev_select,
> + .tf_load = ep93xx_ide_tf_load,
> + .tf_read = ep93xx_ide_tf_read,
> + .input_data = ep93xx_ide_input_data,
> + .output_data = ep93xx_ide_output_data,
> +};
>
> Indentation:
>
> .exec_command = ep93xx_ide_exec_command,
> .read_status = ep93xx_ide_read_status,
> ...
>
> +static void ep93xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
> +{
> + struct ide_timing *timing;
> + u32 reg = IDECFG_IDEEN | IDECFG_PIO;
> +
> + timing = ide_timing_find_mode(XFER_PIO_0 + pio);
> + /*
> + * store the adequate PIO mode timings in the ide_drive_t
> + * 'drive_data' field, to be used later by ep93xx_ide_{read,write}
> + */
> + ide_set_drivedata(drive, (void *)timing);
> +
> + /* 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;
> + }
> + ep93xx_writel(reg, drive->hwif->config_data, IDECFG);
> +}
> +
> +static struct ide_port_ops ep93xx_ide_port_ops = {
> + .set_pio_mode = ep93xx_set_pio_mode,
> +};
> +
> +static struct ide_port_info ep93xx_ide_port_info = {
> + .name = MODULE_NAME,
> + .tp_ops = &ep93xx_ide_tp_ops,
> + .port_ops = &ep93xx_ide_port_ops,
> + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
> + IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
> + IDE_HFLAG_NO_UNMASK_IRQS,
> + .pio_mask = ATA_PIO4,
> +};
>
> Indentation.
>
> +static int __init ep93xx_ide_probe(struct platform_device *pdev)
> +{
> + int retval;
> + int irq;
> + void __iomem *ide_base;
> + struct resource *mem_res;
> + hw_regs_t hw;
> + hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
> + struct ide_host *host;
> +
> + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!mem_res) {
> + dev_err(&pdev->dev,
> + "could not retrieve device memory resources!\n");
> + return -ENXIO;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0) {
> + dev_err(&pdev->dev,
> + "could not retrieve device IRQ resource!\n");
> + return -ENXIO;
> + }
> +
> + if (!request_mem_region
> + (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
> + dev_err(&pdev->dev, "could not request memory resources!\n");
> + return -EBUSY;
> + }
> +
> + ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
> + if (!ide_base) {
> + dev_err(&pdev->dev, "could not map memory resources!\n");
> + retval = -ENOMEM;
> + goto fail_release_mem;
> + }
> +
> + memset(&hw, 0, sizeof(hw));
> +
> + /*
> + * fill in ide_io_ports structure.
> + * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
> + * hence the lowest 3 bits will give us the real address (ranging from
> + * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
> + * values:
> + * CS1n CS0n A2 A1 A0
> + * 1 0 x x x -> IO_ADDR (base 0x10)
> + * 0 1 x x x -> CTL_ADDR (base 0x0E)
> + */
> + ide_std_init_ports(&hw, 0x10, 0x0E);
>
> Why not just setup the real addresses here instead of using
> ide_std_init_ports()?
>
The IDE register's address are specified through bitfields in the EP93xx
IDECTRL register, as described in the above comment.
The 'workaround' in the addresses is just to ease manipulation of those
bitfields while writing the register.
I suppose I could write the register's address in the hw->io_ports so
that the value would be directly ORed to the IDECTRL register, if that
is what you suggest.
> It seems that it would make driver simpler and get rid of >config_data use.
>
No. The config_data is used to store the ioremapped cookie of the IDE
registers base address. I need that address to access the CPUs IDE
registers.
There's no connection between this address (which maps to CPU's internal
memory, allowing to acess the IDE registers) and the actual IDE
addresses, which are defined through bitfields in the IDE control register.
Yeah, I know, weird IDE host controller... :-)
> + hw.irq = irq;
> + hw.chipset = ide_generic;
> + hw.dev = &pdev->dev;
> + hw.config = (unsigned long)ide_base;
> +
> + /* enforce EP93xx IDE controller reset state */
> + ep93xx_ide_clean_regs(hw.config);
> +
> + /* set gpio port E, G and H for IDE */
> + ep93xx_ide_aquire_gpios();
> +
> + /*
> + * configure IDE interface:
> + * - IDE Master Enable
> + * - Polled IO Operation
> + * - PIO Mode 0 -> 3.33 MBps
> + * - 3 Wait States -> 30 ns
> + */
> + ep93xx_writel(IDECFG_IDEEN | IDECFG_PIO | IDECFG_PIO_MODE_0 |
> + ((3 << 8) & IDECFG_WST), hw.config, IDECFG);
> +
> + retval = ide_host_add(&ep93xx_ide_port_info, hws, &host);
> + if (retval) {
> + dev_err(&pdev->dev,
> + "unable to register EP93xx IDE driver!\n");
> + goto fail_unmap;
> + }
> +
> + platform_set_drvdata(pdev, host);
> +
> + dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
> +
> + return 0;
> +
> +fail_unmap:
> + iounmap(ide_base);
> +fail_release_mem:
> + release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
> + return retval;
> +}
> +
> +static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
> +{
> + struct resource *mem_res;
> + struct ide_host *host = pdev->dev.driver_data;
> +
> + /* reset state */
> + ep93xx_ide_clean_regs(host->ports[0]->config_data);
> +
> + /* restore back GPIO port E, G and H for GPIO use */
> + ep93xx_ide_release_gpios();
> +
> + ide_host_remove(host);
> +
> + iounmap((void __iomem *)(host->ports[0]->config_data));
>
> 'host' access but 'host' is already gone...
>
> IOW host->ports[0]->config_data needs to be cached in local variable
> before ide_host_remove() is called...
>
Ups... You're right on this; I didn't spot this since my test platform
doesn't use this driver as a loadable/unloadable module.
I will fix that also.
Thanks for your comments.
Best regards,
João
> Thanks,
> Bart
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-13 11:01 ` João Ramos
@ 2009-05-17 15:20 ` Bartlomiej Zolnierkiewicz
2009-05-22 17:52 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-17 15:20 UTC (permalink / raw)
To: João Ramos; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
On Wednesday 13 May 2009 13:01:20 João Ramos wrote:
> Here's the revised patches, according to the changes you suggested.
Thanks, I applied set_pio_mode.patch with the following minor fixup:
@@ -1035,7 +1035,7 @@
ide_port_for_each_dev(i, drive, hwif) {
/*
- * default to PIO Mode 0 before we figure out
+ * default to PIO Mode 0 before we figure out
* the most suited mode for the attached device
*/
if (port_ops && port_ops->set_pio_mode)
and will apply the following version of ide_drive_data.patch
(unless there are some major complains):
From: Joao Ramos <joao.ramos@inov.pt>
Subject: ide: do not access ide_drive_t 'drive_data' field directly
Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *'
type, allowing a wider range of values/types to be stored in this field.
Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
the 'drive_data' field.
Fixed all host drivers to maintain coherency with the change in the
'drive_data' field type.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
[bart: fix qd65xx build, cast to 'unsigned long', minor Coding Style fixups]
Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
---
drivers/ide/cmd64x.c | 8 +++++---
drivers/ide/cs5536.c | 23 +++++++++++++++--------
drivers/ide/ht6560b.c | 33 ++++++++++++++++++++++++---------
drivers/ide/icside.c | 10 ++++++----
drivers/ide/opti621.c | 8 +++++---
drivers/ide/qd65xx.c | 11 +++++++----
drivers/ide/qd65xx.h | 11 +++++++++--
drivers/ide/sl82c105.c | 18 ++++++++++++------
include/linux/ide.h | 12 +++++++++++-
9 files changed, 94 insertions(+), 40 deletions(-)
Index: b/drivers/ide/cmd64x.c
===================================================================
--- a/drivers/ide/cmd64x.c
+++ b/drivers/ide/cmd64x.c
@@ -118,8 +118,9 @@ static void cmd64x_tune_pio(ide_drive_t
ide_hwif_t *hwif = drive->hwif;
struct pci_dev *dev = to_pci_dev(hwif->dev);
struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ unsigned long setup_count;
unsigned int cycle_time;
- u8 setup_count, arttim = 0;
+ u8 arttim = 0;
static const u8 setup_values[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0};
static const u8 arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23};
@@ -140,10 +141,11 @@ static void cmd64x_tune_pio(ide_drive_t
if (hwif->channel) {
ide_drive_t *pair = ide_get_pair_dev(drive);
- drive->drive_data = setup_count;
+ ide_set_drivedata(drive, (void *)setup_count);
if (pair)
- setup_count = max_t(u8, setup_count, pair->drive_data);
+ setup_count = max_t(u8, setup_count,
+ (unsigned long)ide_get_drivedata(pair));
}
if (setup_count > 5) /* shouldn't actually happen... */
Index: b/drivers/ide/cs5536.c
===================================================================
--- a/drivers/ide/cs5536.c
+++ b/drivers/ide/cs5536.c
@@ -146,14 +146,16 @@ static void cs5536_set_pio_mode(ide_driv
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
ide_drive_t *pair = ide_get_pair_dev(drive);
int cshift = (drive->dn & 1) ? IDE_CAST_D1_SHIFT : IDE_CAST_D0_SHIFT;
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
u32 cast;
u8 cmd_pio = pio;
if (pair)
cmd_pio = min(pio, ide_get_best_pio_mode(pair, 255, 4));
- drive->drive_data &= (IDE_DRV_MASK << 8);
- drive->drive_data |= drv_timings[pio];
+ timings &= (IDE_DRV_MASK << 8);
+ timings |= drv_timings[pio];
+ ide_set_drivedata(drive, (void *)timings);
cs5536_program_dtc(drive, drv_timings[pio]);
@@ -186,6 +188,7 @@ static void cs5536_set_dma_mode(ide_driv
struct pci_dev *pdev = to_pci_dev(drive->hwif->dev);
int dshift = (drive->dn & 1) ? IDE_D1_SHIFT : IDE_D0_SHIFT;
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
u32 etc;
cs5536_read(pdev, ETC, &etc);
@@ -195,8 +198,9 @@ static void cs5536_set_dma_mode(ide_driv
etc |= udma_timings[mode - XFER_UDMA_0] << dshift;
} else { /* MWDMA */
etc &= ~(IDE_ETC_UDMA_MASK << dshift);
- drive->drive_data &= IDE_DRV_MASK;
- drive->drive_data |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ timings &= IDE_DRV_MASK;
+ timings |= mwdma_timings[mode - XFER_MW_DMA_0] << 8;
+ ide_set_drivedata(drive, (void *)timings);
}
cs5536_write(pdev, ETC, etc);
@@ -204,9 +208,11 @@ static void cs5536_set_dma_mode(ide_driv
static void cs5536_dma_start(ide_drive_t *drive)
{
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
+
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data >> 8);
+ (timings >> 8) != (timings & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, timings >> 8);
ide_dma_start(drive);
}
@@ -214,10 +220,11 @@ static void cs5536_dma_start(ide_drive_t
static int cs5536_dma_end(ide_drive_t *drive)
{
int ret = ide_dma_end(drive);
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
if (drive->current_speed < XFER_UDMA_0 &&
- (drive->drive_data >> 8) != (drive->drive_data & IDE_DRV_MASK))
- cs5536_program_dtc(drive, drive->drive_data & IDE_DRV_MASK);
+ (timings >> 8) != (timings & IDE_DRV_MASK))
+ cs5536_program_dtc(drive, timings & IDE_DRV_MASK);
return ret;
}
Index: b/drivers/ide/ht6560b.c
===================================================================
--- a/drivers/ide/ht6560b.c
+++ b/drivers/ide/ht6560b.c
@@ -44,7 +44,12 @@
* bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?)
*/
#define HT_CONFIG_PORT 0x3e6
-#define HT_CONFIG(drivea) (u8)(((drivea)->drive_data & 0xff00) >> 8)
+
+static inline u8 HT_CONFIG(ide_drive_t *drive)
+{
+ return ((unsigned long)ide_get_drivedata(drive) & 0xff00) >> 8;
+}
+
/*
* FIFO + PREFETCH (both a/b-model)
*/
@@ -90,7 +95,11 @@
* Active Time for each drive. Smaller value gives higher speed.
* In case of failures you should probably fall back to a higher value.
*/
-#define HT_TIMING(drivea) (u8)((drivea)->drive_data & 0x00ff)
+static inline u8 HT_TIMING(ide_drive_t *drive)
+{
+ return (unsigned long)ide_get_drivedata(drive) & 0x00ff;
+}
+
#define HT_TIMING_DEFAULT 0xff
/*
@@ -242,23 +251,27 @@ static DEFINE_SPINLOCK(ht6560b_lock);
*/
static void ht_set_prefetch(ide_drive_t *drive, u8 state)
{
- unsigned long flags;
+ unsigned long flags, config;
int t = HT_PREFETCH_MODE << 8;
spin_lock_irqsave(&ht6560b_lock, flags);
+ config = (unsigned long)ide_get_drivedata(drive);
+
/*
* Prefetch mode and unmask irq seems to conflict
*/
if (state) {
- drive->drive_data |= t; /* enable prefetch mode */
+ config |= t; /* enable prefetch mode */
drive->dev_flags |= IDE_DFLAG_NO_UNMASK;
drive->dev_flags &= ~IDE_DFLAG_UNMASK;
} else {
- drive->drive_data &= ~t; /* disable prefetch mode */
+ config &= ~t; /* disable prefetch mode */
drive->dev_flags &= ~IDE_DFLAG_NO_UNMASK;
}
+ ide_set_drivedata(drive, (void *)config);
+
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -268,7 +281,7 @@ static void ht_set_prefetch(ide_drive_t
static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
- unsigned long flags;
+ unsigned long flags, config;
u8 timing;
switch (pio) {
@@ -281,8 +294,10 @@ static void ht6560b_set_pio_mode(ide_dri
timing = ht_pio2timings(drive, pio);
spin_lock_irqsave(&ht6560b_lock, flags);
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ config = (unsigned long)ide_get_drivedata(drive);
+ config &= 0xff00;
+ config |= timing;
+ ide_set_drivedata(drive, (void *)config);
spin_unlock_irqrestore(&ht6560b_lock, flags);
#ifdef DEBUG
@@ -299,7 +314,7 @@ static void __init ht6560b_init_dev(ide_
if (hwif->channel)
t |= (HT_SECONDARY_IF << 8);
- drive->drive_data = t;
+ ide_set_drivedata(drive, (void *)t);
}
static int probe_ht6560b;
Index: b/drivers/ide/icside.c
===================================================================
--- a/drivers/ide/icside.c
+++ b/drivers/ide/icside.c
@@ -236,7 +236,8 @@ static const struct ide_port_ops icside_
*/
static void icside_set_dma_mode(ide_drive_t *drive, const u8 xfer_mode)
{
- int cycle_time, use_dma_info = 0;
+ unsigned long cycle_time;
+ int use_dma_info = 0;
switch (xfer_mode) {
case XFER_MW_DMA_2:
@@ -267,10 +268,11 @@ static void icside_set_dma_mode(ide_driv
if (use_dma_info && drive->id[ATA_ID_EIDE_DMA_TIME] > cycle_time)
cycle_time = drive->id[ATA_ID_EIDE_DMA_TIME];
- drive->drive_data = cycle_time;
+ ide_set_drivedata(drive, (void *)cycle_time);
printk("%s: %s selected (peak %dMB/s)\n", drive->name,
- ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data);
+ ide_xfer_verbose(xfer_mode),
+ 2000 / (unsigned long)ide_get_drivedata(drive));
}
static const struct ide_port_ops icside_v6_port_ops = {
@@ -332,7 +334,7 @@ static int icside_dma_setup(ide_drive_t
/*
* Select the correct timing for this drive.
*/
- set_dma_speed(ec->dma, drive->drive_data);
+ set_dma_speed(ec->dma, (unsigned long)ide_get_drivedata(drive));
/*
* Tell the DMA engine about the SG table and
Index: b/drivers/ide/opti621.c
===================================================================
--- a/drivers/ide/opti621.c
+++ b/drivers/ide/opti621.c
@@ -138,6 +138,7 @@ static void opti621_set_pio_mode(ide_dri
ide_hwif_t *hwif = drive->hwif;
ide_drive_t *pair = ide_get_pair_dev(drive);
unsigned long flags;
+ unsigned long mode = XFER_PIO_0 + pio, pair_mode;
u8 tim, misc, addr_pio = pio, clk;
/* DRDY is default 2 (by OPTi Databook) */
@@ -150,11 +151,12 @@ static void opti621_set_pio_mode(ide_dri
{ 0x48, 0x34, 0x21, 0x10, 0x10 } /* 25 MHz */
};
- drive->drive_data = XFER_PIO_0 + pio;
+ ide_set_drivedata(drive, (void *)mode);
if (pair) {
- if (pair->drive_data && pair->drive_data < drive->drive_data)
- addr_pio = pair->drive_data - XFER_PIO_0;
+ pair_mode = (unsigned long)ide_get_drivedata(pair);
+ if (pair_mode && pair_mode < mode)
+ addr_pio = pair_mode - XFER_PIO_0;
}
spin_lock_irqsave(&opti621_lock, flags);
Index: b/drivers/ide/qd65xx.c
===================================================================
--- a/drivers/ide/qd65xx.c
+++ b/drivers/ide/qd65xx.c
@@ -180,8 +180,11 @@ static int qd_find_disk_type (ide_drive_
static void qd_set_timing (ide_drive_t *drive, u8 timing)
{
- drive->drive_data &= 0xff00;
- drive->drive_data |= timing;
+ unsigned long data = (unsigned long)ide_get_drivedata(drive);
+
+ data &= 0xff00;
+ data |= timing;
+ ide_set_drivedata(drive, (void *)data);
printk(KERN_DEBUG "%s: %#x\n", drive->name, timing);
}
@@ -292,7 +295,7 @@ static void __init qd6500_init_dev(ide_d
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
- drive->drive_data = QD6500_DEF_DATA;
+ ide_set_drivedata(drive, (void *)QD6500_DEF_DATA);
}
static void __init qd6580_init_dev(ide_drive_t *drive)
@@ -308,7 +311,7 @@ static void __init qd6580_init_dev(ide_d
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- drive->drive_data = (drive->dn & 1) ? t2 : t1;
+ ide_set_drivedata(drive, (void *)((drive->dn & 1) ? t2 : t1));
}
static const struct ide_tp_ops qd65xx_tp_ops = {
Index: b/drivers/ide/qd65xx.h
===================================================================
--- a/drivers/ide/qd65xx.h
+++ b/drivers/ide/qd65xx.h
@@ -31,8 +31,15 @@
#define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff)
-#define QD_TIMING(drive) (u8)(((drive)->drive_data) & 0x00ff)
-#define QD_TIMREG(drive) (u8)((((drive)->drive_data) & 0xff00) >> 8)
+static inline u8 QD_TIMING(ide_drive_t *drive)
+{
+ return (unsigned long)ide_get_drivedata(drive) & 0x00ff;
+}
+
+static inline u8 QD_TIMREG(ide_drive_t *drive)
+{
+ return ((unsigned long)ide_get_drivedata(drive) & 0xff00) >> 8;
+}
#define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08))
#define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00))
Index: b/drivers/ide/sl82c105.c
===================================================================
--- a/drivers/ide/sl82c105.c
+++ b/drivers/ide/sl82c105.c
@@ -74,6 +74,7 @@ static unsigned int get_pio_timings(ide_
static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
int reg = 0x44 + drive->dn * 4;
u16 drv_ctrl;
@@ -83,8 +84,9 @@ static void sl82c105_set_pio_mode(ide_dr
* Store the PIO timings so that we can restore them
* in case DMA will be turned off...
*/
- drive->drive_data &= 0xffff0000;
- drive->drive_data |= drv_ctrl;
+ timings &= 0xffff0000;
+ timings |= drv_ctrl;
+ ide_set_drivedata(drive, (void *)timings);
pci_write_config_word(dev, reg, drv_ctrl);
pci_read_config_word (dev, reg, &drv_ctrl);
@@ -100,6 +102,7 @@ static void sl82c105_set_pio_mode(ide_dr
static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed)
{
static u16 mwdma_timings[] = {0x0707, 0x0201, 0x0200};
+ unsigned long timings = (unsigned long)ide_get_drivedata(drive);
u16 drv_ctrl;
DBG(("sl82c105_tune_chipset(drive:%s, speed:%s)\n",
@@ -111,8 +114,9 @@ static void sl82c105_set_dma_mode(ide_dr
* Store the DMA timings so that we can actually program
* them when DMA will be turned on...
*/
- drive->drive_data &= 0x0000ffff;
- drive->drive_data |= (unsigned long)drv_ctrl << 16;
+ timings &= 0x0000ffff;
+ timings |= (unsigned long)drv_ctrl << 16;
+ ide_set_drivedata(drive, (void *)timings);
}
/*
@@ -184,7 +188,8 @@ static void sl82c105_dma_start(ide_drive
DBG(("%s(drive:%s)\n", __func__, drive->name));
- pci_write_config_word(dev, reg, drive->drive_data >> 16);
+ pci_write_config_word(dev, reg,
+ (unsigned long)ide_get_drivedata(drive) >> 16);
sl82c105_reset_host(dev);
ide_dma_start(drive);
@@ -209,7 +214,8 @@ static int sl82c105_dma_end(ide_drive_t
ret = ide_dma_end(drive);
- pci_write_config_word(dev, reg, drive->drive_data);
+ pci_write_config_word(dev, reg,
+ (unsigned long)ide_get_drivedata(drive));
return ret;
}
Index: b/include/linux/ide.h
===================================================================
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -559,7 +559,7 @@ struct ide_drive_s {
unsigned int bios_cyl; /* BIOS/fdisk/LILO number of cyls */
unsigned int cyl; /* "real" number of cyls */
- unsigned int drive_data; /* used by set_pio_mode/dev_select() */
+ void *drive_data; /* used by set_pio_mode/dev_select() */
unsigned int failures; /* current failure count */
unsigned int max_failures; /* maximum allowed failure count */
u64 probed_capacity;/* initial reported media capacity (ide-cd only currently) */
@@ -1567,6 +1567,16 @@ static inline ide_drive_t *ide_get_pair_
return (peer->dev_flags & IDE_DFLAG_PRESENT) ? peer : NULL;
}
+static inline void *ide_get_drivedata(ide_drive_t *drive)
+{
+ return drive->drive_data;
+}
+
+static inline void ide_set_drivedata(ide_drive_t *drive, void *data)
+{
+ drive->drive_data = data;
+}
+
#define ide_port_for_each_dev(i, dev, port) \
for ((i) = 0; ((dev) = (port)->devices[i]) || (i) < MAX_DRIVES; (i)++)
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-15 17:01 ` João Ramos
@ 2009-05-17 16:16 ` Bartlomiej Zolnierkiewicz
2009-05-18 13:49 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-17 16:16 UTC (permalink / raw)
To: João Ramos; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
On Friday 15 May 2009 19:01:11 João Ramos wrote:
[...]
> > +/*
> > + * readl/writel helpers to access internal registers using
> > + * an ioremapped cookie and the specified IDE register offset
> > + */
> > +
> > +static inline u32 ep93xx_readl(unsigned long base, u8 offset)
> > +{
> > + return readl((void __iomem *)(base + offset));
> > +}
> > +
> > +static inline void ep93xx_writel(u32 value, unsigned long base, u8 offset)
> > +{
> > + writel(value, (void __iomem *)(base + offset));
> > +}
> >
> > Hmm, what do we need these wrappers for exactly?
> >
> > Please remove them.
> >
> I just added those to increase code readability, so that I wouldn't have
> to do a '(void __iomem *)(base + offset)' in every readl/writel call.
> But I can remove it, no problem.
If readability is harmed by such casts you can always add a local variable
to alleviate for it, i.e.:
void __iomem *base = (unsigned long)__base;
However I don't think a lot of such tricks would be needed after fixing function
arguments to pass 'void __iomem *base' around and not 'unsigned long base'
> > +/*
> > + * Check whether IORDY is asserted or not.
> > + */
> > +static inline int ep93xx_ide_check_iordy(unsigned long base)
like here
> > +{
> > + u32 reg = ep93xx_readl(base, IDECTRL);
> > +
> > + return (reg & IDECTRL_IORDY) ? 1 : 0;
> > +}
[...]
> > + /*
> > + * fill in ide_io_ports structure.
> > + * we use magic numbers 0x10 for io_addr and 0x0E for ctl_addr,
> > + * hence the lowest 3 bits will give us the real address (ranging from
> > + * 0 to 7) and the subsequent 2 bits will give us the CS1n and CS0n
> > + * values:
> > + * CS1n CS0n A2 A1 A0
> > + * 1 0 x x x -> IO_ADDR (base 0x10)
> > + * 0 1 x x x -> CTL_ADDR (base 0x0E)
> > + */
> > + ide_std_init_ports(&hw, 0x10, 0x0E);
> >
> > Why not just setup the real addresses here instead of using
> > ide_std_init_ports()?
> >
>
> The IDE register's address are specified through bitfields in the EP93xx
> IDECTRL register, as described in the above comment.
> The 'workaround' in the addresses is just to ease manipulation of those
> bitfields while writing the register.
Ok, so there is really no reason to use ide_std_init_ports().
> I suppose I could write the register's address in the hw->io_ports so
> that the value would be directly ORed to the IDECTRL register, if that
> is what you suggest.
Please try it.
> > It seems that it would make driver simpler and get rid of >config_data use.
> >
>
> No. The config_data is used to store the ioremapped cookie of the IDE
> registers base address. I need that address to access the CPUs IDE
> registers.
Please note that ep93xx_ide_{read,write}() are always passed
hwif->config_data as 'unsigned long base' argument so the first
argument should become 'ide_hwif_t *hwif' with:
unsigned long base = hwif->config_data
inside ep93xx_ide_{read,write}().
Also how's about using host->priv_data instead of hwif->config_data?
[ By using ide_host_alloc()+ide_host_register() instead of ide_host_add()
and later obtaining host->priv_data through hwif->host->priv_data. ]
> There's no connection between this address (which maps to CPU's internal
> memory, allowing to acess the IDE registers) and the actual IDE
> addresses, which are defined through bitfields in the IDE control register.
> Yeah, I know, weird IDE host controller... :-)
Indeed. :)
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-17 16:16 ` Bartlomiej Zolnierkiewicz
@ 2009-05-18 13:49 ` João Ramos
2009-05-19 13:06 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-18 13:49 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
[-- Attachment #1: Type: text/plain, Size: 589 bytes --]
Hi,
Here is the revised patch according to Bart's suggestions in previous
emails.
The patch is against linux-2.6.30-rc6.
Best regards,
João Ramos
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: ep93xx-ide.patch --]
[-- Type: text/x-patch, Size: 16989 bytes --]
Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ryan Mallon <ryan@bluewatersys.com>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
---
diff -urN linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c
--- linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c 2009-05-18 14:40:58.000000000 +0100
@@ -0,0 +1,524 @@
+/*
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * IDE host controller driver.
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ * 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.
+ */
+
+#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/ide.h>
+#include <linux/delay.h>
+
+#define MODULE_NAME "ep93xx-ide"
+
+/*
+ * 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.
+ */
+
+/*
+ * IDE Register offset
+ */
+#define IDECTRL 0x00
+#define IDECFG 0x04
+#define IDEMDMAOP 0x08
+#define IDEUDMAOP 0x0C
+#define IDEDATAOUT 0x10
+#define IDEDATAIN 0x14
+#define IDEMDMADATAOUT 0x18
+#define IDEMDMADATAIN 0x1C
+#define IDEUDMADATAOUT 0x20
+#define IDEUDMADATAIN 0x24
+#define IDEUDMASTS 0x28
+#define IDEUDMADEBUG 0x2C
+#define IDEUDMAWRBUFSTS 0x30
+#define IDEUDMARDBUFSTS 0x34
+
+/*
+ * IDE Control Register bit fields
+ */
+#define IDECTRL_CS0N 0x00000001
+#define IDECTRL_CS1N 0x00000002
+#define IDECTRL_DA 0x0000001C
+#define IDECTRL_DIORN 0x00000020
+#define IDECTRL_DIOWN 0x00000040
+#define IDECTRL_DASPN 0x00000080
+#define IDECTRL_DMARQ 0x00000100
+#define IDECTRL_INTRQ 0x00000200
+#define IDECTRL_IORDY 0x00000400
+
+/*
+ * IDE Configuration Register bit fields
+ */
+#define IDECFG_IDEEN 0x00000001
+#define IDECFG_PIO 0x00000002
+#define IDECFG_MDMA 0x00000004
+#define IDECFG_UDMA 0x00000008
+#define IDECFG_PIO_MODE_0 0x00000000
+#define IDECFG_PIO_MODE_1 0x00000010
+#define IDECFG_PIO_MODE_2 0x00000020
+#define IDECFG_PIO_MODE_3 0x00000030
+#define IDECFG_PIO_MODE_4 0x00000040
+#define IDECFG_MDMA_MODE_0 0x00000000
+#define IDECFG_MDMA_MODE_1 0x00000010
+#define IDECFG_MDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_0 0x00000000
+#define IDECFG_UDMA_MODE_1 0x00000010
+#define IDECFG_UDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_3 0x00000030
+#define IDECFG_UDMA_MODE_4 0x00000040
+#define IDECFG_WST 0x00000300
+
+/*
+ * Check whether IORDY is asserted or not.
+ */
+static inline int ep93xx_ide_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 ep93xx_ide_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 ep93xx_ide_read(void __iomem *base, unsigned long addr,
+ struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->setup);
+
+ reg &= ~IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->recover);
+
+ return readl(base + IDEDATAIN);
+}
+
+static void ep93xx_ide_write(void __iomem *base, u16 value,
+ unsigned long addr, struct ide_timing *timing)
+{
+ u32 reg;
+
+ reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->setup);
+
+ writel(value, base + IDEDATAOUT);
+
+ reg &= ~IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(timing->recover);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ ep93xx_ide_write(base, cmd, hwif->io_ports.command_addr, t);
+}
+
+static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ return ep93xx_ide_read(base, hwif->io_ports.status_addr, t);
+}
+
+static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ return ep93xx_ide_read(base, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ ep93xx_ide_write(base, ctl, hwif->io_ports.ctl_addr, t);
+}
+
+static void ep93xx_ide_dev_select(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ u8 select = drive->select | ATA_DEVICE_OBS;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ ep93xx_ide_write(base, select, hwif->io_ports.device_addr, t);
+}
+
+static void ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ if (valid & IDE_VALID_FEATURE)
+ ep93xx_ide_write(base, tf->feature, io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ ep93xx_ide_write(base, tf->nsect, io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ ep93xx_ide_write(base, tf->lbal, io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ ep93xx_ide_write(base, tf->lbam, io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ ep93xx_ide_write(base, tf->lbah, io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ ep93xx_ide_write(base, tf->device, io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ if (valid & IDE_VALID_ERROR)
+ tf->error = ep93xx_ide_read(base, io_ports->feature_addr, t);
+ if (valid & IDE_VALID_NSECT)
+ tf->nsect = ep93xx_ide_read(base, io_ports->nsect_addr, t);
+ if (valid & IDE_VALID_LBAL)
+ tf->lbal = ep93xx_ide_read(base, io_ports->lbal_addr, t);
+ if (valid & IDE_VALID_LBAM)
+ tf->lbam = ep93xx_ide_read(base, io_ports->lbam_addr, t);
+ if (valid & IDE_VALID_LBAH)
+ tf->lbah = ep93xx_ide_read(base, io_ports->lbah_addr, t);
+ if (valid & IDE_VALID_DEVICE)
+ tf->device = ep93xx_ide_read(base, io_ports->device_addr, t);
+}
+
+static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ u16 *data = (u16 *)buf;
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ while (words--)
+ *data++ = cpu_to_le16(ep93xx_ide_read(base,
+ io_ports->data_addr, t));
+}
+
+static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ u16 *data = (u16 *)buf;
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned int words = (len + 1) >> 1;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ while (words--)
+ ep93xx_ide_write(base, le16_to_cpu(*data++),
+ io_ports->data_addr, t);
+}
+
+static struct ide_tp_ops ep93xx_ide_tp_ops = {
+ .exec_command = ep93xx_ide_exec_command,
+ .read_status = ep93xx_ide_read_status,
+ .read_altstatus = ep93xx_ide_read_altstatus,
+ .write_devctl = ep93xx_ide_write_devctl,
+ .dev_select = ep93xx_ide_dev_select,
+ .tf_load = ep93xx_ide_tf_load,
+ .tf_read = ep93xx_ide_tf_read,
+ .input_data = ep93xx_ide_input_data,
+ .output_data = ep93xx_ide_output_data,
+};
+
+static void ep93xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+{
+ ide_drive_t *pair = ide_get_pair_dev(drive);
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ struct ide_timing *cmd_t = t;
+ u32 reg = IDECFG_IDEEN | IDECFG_PIO;
+ void __iomem *base = drive->hwif->host->host_priv;
+
+ if (pair) {
+ struct ide_timing *pair_t = ide_get_drivedata(pair);
+
+ if (pair_t && pair_t->mode < t->mode)
+ cmd_t = pair_t;
+ }
+
+ /*
+ * store the adequate PIO mode timings, to be used later
+ * by ep93xx_ide_{read,write}
+ */
+ ide_set_drivedata(drive, (void *)t);
+ ide_set_hwifdata(drive->hwif, (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);
+}
+
+static struct ide_port_ops ep93xx_ide_port_ops = {
+ .set_pio_mode = ep93xx_ide_set_pio_mode,
+};
+
+static struct ide_port_info ep93xx_ide_port_info = {
+ .name = MODULE_NAME,
+ .tp_ops = &ep93xx_ide_tp_ops,
+ .port_ops = &ep93xx_ide_port_ops,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
+ IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
+ IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
+ .pio_mask = ATA_PIO4,
+};
+
+static int __init ep93xx_ide_probe(struct platform_device *pdev)
+{
+ int retval;
+ int irq;
+ void __iomem *ide_base;
+ struct resource *mem_res;
+ hw_regs_t hw;
+ hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_host *host;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ if (!request_mem_region
+ (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
+ dev_err(&pdev->dev, "could not request memory resources!\n");
+ return -EBUSY;
+ }
+
+ ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(&pdev->dev, "could not map memory resources!\n");
+ retval = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ /*
+ * fill in ide_io_ports structure.
+ * 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
+ */
+ hw.io_ports.data_addr = 0x02;
+ hw.io_ports.error_addr = 0x06;
+ hw.io_ports.nsect_addr = 0x0A;
+ hw.io_ports.lbal_addr = 0x0E;
+ hw.io_ports.lbam_addr = 0x12;
+ hw.io_ports.lbah_addr = 0x16;
+ hw.io_ports.device_addr = 0x1A;
+ hw.io_ports.command_addr = 0x1E;
+ hw.io_ports.ctl_addr = 0x01;
+
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+
+ /* enforce EP93xx IDE controller reset state */
+ ep93xx_ide_clean_regs(ide_base);
+
+ /* set gpio port E, G and H for IDE */
+ ep93xx_ide_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);
+
+ host = ide_host_alloc(&ep93xx_ide_port_info, hws);
+ if (!host) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for EP93xx IDE host!\n");
+ retval = -ENOMEM;
+ goto fail_unmap;
+ }
+
+ host->host_priv = ide_base;
+
+ retval = ide_host_register(host, &ep93xx_ide_port_info, hws);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "unable to register EP93xx IDE driver!\n");
+ goto fail_free_host;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
+
+ return 0;
+
+fail_free_host:
+ ide_host_free(host);
+fail_unmap:
+ iounmap(ide_base);
+fail_release_mem:
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+ return retval;
+}
+
+static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
+{
+ struct resource *mem_res;
+ struct ide_host *host = pdev->dev.driver_data;
+ void __iomem *base = host->host_priv;
+
+ /* reset state */
+ ep93xx_ide_clean_regs(host->host_priv);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ ep93xx_ide_release_gpios();
+
+ ide_host_remove(host);
+
+ iounmap(base);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_ide_driver = {
+ .remove = __exit_p(ep93xx_ide_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ep93xx_ide_init(void)
+{
+ return platform_driver_probe(&ep93xx_ide_driver, ep93xx_ide_probe);
+}
+module_init(ep93xx_ide_init);
+
+static void __exit ep93xx_ide_exit(void)
+{
+ platform_driver_unregister(&ep93xx_ide_driver);
+}
+module_exit(ep93xx_ide_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joao Ramos <joao.ramos@inov.pt>");
+MODULE_DESCRIPTION("EP93xx IDE host controller driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ep93xx-ide");
diff -urN linux-2.6.30-rc6.orig/drivers/ide/Kconfig linux-2.6.30-rc6/drivers/ide/Kconfig
--- linux-2.6.30-rc6.orig/drivers/ide/Kconfig 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/Kconfig 2009-05-18 14:40:16.000000000 +0100
@@ -732,6 +732,19 @@
depends on ARM && ARCH_AT91 && !ARCH_AT91RM9200 && !ARCH_AT91X40
select IDE_TIMINGS
+config BLK_DEV_IDE_EP93XX
+ tristate "Cirrus Logic EPxx (EP9312, EP9315) IDE support"
+ depends on ARM && ARCH_EP93XX
+ select IDE_TIMINGS
+ help
+ This is a host controller driver for IDE hardware included in
+ Cirrus Logic's EP9312 and EP9315 CPUs.
+ Say Y here if you want to enable the IDE host controller support
+ for your machine.
+
+ Choose 'M' to compile this driver as a module; the module will be
+ called 'ep93xx-ide'.
+
config BLK_DEV_IDE_ICSIDE
tristate "ICS IDE interface support"
depends on ARM && ARCH_ACORN
diff -urN linux-2.6.30-rc6.orig/drivers/ide/Makefile linux-2.6.30-rc6/drivers/ide/Makefile
--- linux-2.6.30-rc6.orig/drivers/ide/Makefile 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/Makefile 2009-05-18 14:40:16.000000000 +0100
@@ -117,3 +117,4 @@
obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o
obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o
obj-$(CONFIG_BLK_DEV_IDE_AT91) += at91_ide.o
+obj-$(CONFIG_BLK_DEV_IDE_EP93XX) += ep93xx-ide.o
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-18 13:49 ` João Ramos
@ 2009-05-19 13:06 ` Bartlomiej Zolnierkiewicz
2009-05-19 13:20 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-19 13:06 UTC (permalink / raw)
To: João Ramos; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
On Monday 18 May 2009 15:49:15 João Ramos wrote:
> Hi,
>
> Here is the revised patch according to Bart's suggestions in previous
> emails.
> The patch is against linux-2.6.30-rc6.
Thanks. I see that we can still cleanup the code a bit by folding current
ep93xx_ide_{read,write}() into __ep93xx_ide_{read,write}() and then adding
wrappers on top of it.
I think that the patch would the best to show the idea in this case (it is
completely untested, it may not build).
[ While at it I also changed data transfer functions to use drive's timings
and fixed ->remove method to unregister IDE host before doing host specific
cleanup. These fixes should be applied regardless of the cleanup change. ]
Please review/integrate/test and then resubmit the final patch.
---
drivers/ide/ep93xx-ide.c | 111 ++++++++++++++++++++++-------------------------
1 file changed, 54 insertions(+), 57 deletions(-)
Index: b/drivers/ide/ep93xx-ide.c
===================================================================
--- a/drivers/ide/ep93xx-ide.c
+++ b/drivers/ide/ep93xx-ide.c
@@ -126,50 +126,67 @@ static void ep93xx_ide_clean_regs(void _
writel(0, base + IDEUDMADEBUG);
}
-static u16 ep93xx_ide_read(void __iomem *base, unsigned long addr,
- struct ide_timing *timing)
+static u16 __ep93xx_ide_read(void __iomem *base, unsigned long addr,
+ struct ide_timing *t)
{
u32 reg;
reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
writel(reg, base + IDECTRL);
- ndelay(timing->setup);
+ ndelay(t->setup);
reg &= ~IDECTRL_DIORN;
writel(reg, base + IDECTRL);
- ndelay(timing->active);
+ ndelay(t->active);
while (!ep93xx_ide_check_iordy(base))
cpu_relax();
reg |= IDECTRL_DIORN;
writel(reg, base + IDECTRL);
- ndelay(timing->recover);
+ ndelay(t->recover);
return readl(base + IDEDATAIN);
}
-static void ep93xx_ide_write(void __iomem *base, u16 value,
- unsigned long addr, struct ide_timing *timing)
+static inline u16 ep93xx_ide_read(ide_hwif_t *hwif, unsigned long addr)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ return __ep93xx_ide_read(base, addr, t);
+}
+
+static void __ep93xx_ide_write(void __iomem *base, u16 value,
+ unsigned long addr, struct ide_timing *t)
{
u32 reg;
reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
writel(reg, base + IDECTRL);
- ndelay(timing->setup);
+ ndelay(t->setup);
writel(value, base + IDEDATAOUT);
reg &= ~IDECTRL_DIOWN;
writel(reg, base + IDECTRL);
- ndelay(timing->active);
+ ndelay(t->active);
while (!ep93xx_ide_check_iordy(base))
cpu_relax();
reg |= IDECTRL_DIOWN;
writel(reg, base + IDECTRL);
- ndelay(timing->recover);
+ ndelay(t->recover);
+}
+
+static inline void ep93xx_ide_write(ide_hwif_t *hwif, u16 value,
+ unsigned long addr)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ __ep93xx_ide_write(base, value, addr, t);
}
/*
@@ -178,88 +195,70 @@ static void ep93xx_ide_write(void __iome
static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
{
- void __iomem *base = hwif->host->host_priv;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
-
- ep93xx_ide_write(base, cmd, hwif->io_ports.command_addr, t);
+ ep93xx_ide_write(hwif, cmd, hwif->io_ports.command_addr);
}
static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
{
- void __iomem *base = hwif->host->host_priv;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
-
- return ep93xx_ide_read(base, hwif->io_ports.status_addr, t);
+ return ep93xx_ide_read(whif, hwif->io_ports.status_addr);
}
static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
{
- void __iomem *base = hwif->host->host_priv;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
-
- return ep93xx_ide_read(base, hwif->io_ports.ctl_addr, t);
+ return ep93xx_ide_read(hwif, hwif->io_ports.ctl_addr);
}
static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
{
- void __iomem *base = hwif->host->host_priv;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
-
- ep93xx_ide_write(base, ctl, hwif->io_ports.ctl_addr, t);
+ ep93xx_ide_write(hwif, ctl, hwif->io_ports.ctl_addr);
}
static void ep93xx_ide_dev_select(ide_drive_t *drive)
{
ide_hwif_t *hwif = drive->hwif;
- void __iomem *base = hwif->host->host_priv;
u8 select = drive->select | ATA_DEVICE_OBS;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
- ep93xx_ide_write(base, select, hwif->io_ports.device_addr, t);
+ ep93xx_ide_write(hwif, select, hwif->io_ports.device_addr);
}
static void ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf,
u8 valid)
{
ide_hwif_t *hwif = drive->hwif;
- void __iomem *base = hwif->host->host_priv;
struct ide_io_ports *io_ports = &hwif->io_ports;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
if (valid & IDE_VALID_FEATURE)
- ep93xx_ide_write(base, tf->feature, io_ports->feature_addr, t);
+ ep93xx_ide_write(hwif, tf->feature, io_ports->feature_addr);
if (valid & IDE_VALID_NSECT)
- ep93xx_ide_write(base, tf->nsect, io_ports->nsect_addr, t);
+ ep93xx_ide_write(hwif, tf->nsect, io_ports->nsect_addr);
if (valid & IDE_VALID_LBAL)
- ep93xx_ide_write(base, tf->lbal, io_ports->lbal_addr, t);
+ ep93xx_ide_write(hwif, tf->lbal, io_ports->lbal_addr);
if (valid & IDE_VALID_LBAM)
- ep93xx_ide_write(base, tf->lbam, io_ports->lbam_addr, t);
+ ep93xx_ide_write(hwif, tf->lbam, io_ports->lbam_addr);
if (valid & IDE_VALID_LBAH)
- ep93xx_ide_write(base, tf->lbah, io_ports->lbah_addr, t);
+ ep93xx_ide_write(hwif, tf->lbah, io_ports->lbah_addr);
if (valid & IDE_VALID_DEVICE)
- ep93xx_ide_write(base, tf->device, io_ports->device_addr, t);
+ ep93xx_ide_write(hwif, tf->device, io_ports->device_addr);
}
static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
u8 valid)
{
ide_hwif_t *hwif = drive->hwif;
- void __iomem *base = hwif->host->host_priv;
struct ide_io_ports *io_ports = &hwif->io_ports;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
if (valid & IDE_VALID_ERROR)
- tf->error = ep93xx_ide_read(base, io_ports->feature_addr, t);
+ tf->error = ep93xx_ide_read(hwif, io_ports->feature_addr);
if (valid & IDE_VALID_NSECT)
- tf->nsect = ep93xx_ide_read(base, io_ports->nsect_addr, t);
+ tf->nsect = ep93xx_ide_read(hwif, io_ports->nsect_addr);
if (valid & IDE_VALID_LBAL)
- tf->lbal = ep93xx_ide_read(base, io_ports->lbal_addr, t);
+ tf->lbal = ep93xx_ide_read(hwif, io_ports->lbal_addr);
if (valid & IDE_VALID_LBAM)
- tf->lbam = ep93xx_ide_read(base, io_ports->lbam_addr, t);
+ tf->lbam = ep93xx_ide_read(hwif, io_ports->lbam_addr);
if (valid & IDE_VALID_LBAH)
- tf->lbah = ep93xx_ide_read(base, io_ports->lbah_addr, t);
+ tf->lbah = ep93xx_ide_read(hwif, io_ports->lbah_addr);
if (valid & IDE_VALID_DEVICE)
- tf->device = ep93xx_ide_read(base, io_ports->device_addr, t);
+ tf->device = ep93xx_ide_read(hwif, io_ports->device_addr);
}
static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
@@ -268,13 +267,12 @@ static void ep93xx_ide_input_data(ide_dr
u16 *data = (u16 *)buf;
ide_hwif_t *hwif = drive->hwif;
void __iomem *base = hwif->host->host_priv;
- struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned long data_addr = hwif->io_ports.data_addr;
+ struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
unsigned int words = (len + 1) >> 1;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
while (words--)
- *data++ = cpu_to_le16(ep93xx_ide_read(base,
- io_ports->data_addr, t));
+ *data++ = cpu_to_le16(__ep93xx_ide_read(base, data_addr, t);
}
static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
@@ -283,13 +281,12 @@ static void ep93xx_ide_output_data(ide_d
u16 *data = (u16 *)buf;
ide_hwif_t *hwif = drive->hwif;
void __iomem *base = hwif->host->host_priv;
- struct ide_io_ports *io_ports = &hwif->io_ports;
+ unsigned long data_addr = hwif->io_ports.data_addr;
+ struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
unsigned int words = (len + 1) >> 1;
- struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
while (words--)
- ep93xx_ide_write(base, le16_to_cpu(*data++),
- io_ports->data_addr, t);
+ __ep93xx_ide_write(base, le16_to_cpu(*data++), data_addr, t);
}
static struct ide_tp_ops ep93xx_ide_tp_ops = {
@@ -477,18 +474,18 @@ fail_release_mem:
static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
{
- struct resource *mem_res;
struct ide_host *host = pdev->dev.driver_data;
void __iomem *base = host->host_priv;
+ struct resource *mem_res;
+
+ ide_host_remove(host);
/* reset state */
- ep93xx_ide_clean_regs(host->host_priv);
+ ep93xx_ide_clean_regs(base);
/* restore back GPIO port E, G and H for GPIO use */
ep93xx_ide_release_gpios();
- ide_host_remove(host);
-
iounmap(base);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-19 13:06 ` Bartlomiej Zolnierkiewicz
@ 2009-05-19 13:20 ` João Ramos
2009-05-19 13:56 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-19 13:20 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
Ok, I will apply the changes, test them and then resubmit the final patch.
By the way, I will submit the final patch through 'arm-linux' mailing
list, if that is OK to you, since the full patch has platform-specific
dependencies.
I mean, as long as we iterate with the IDE part of it untill it is good
for submission from you, I can submit it in 'arm-linux' with your
acknowledge, right?
Thank you for your revisions.
Best regards,
João Ramos
Bartlomiej Zolnierkiewicz escreveu:
> On Monday 18 May 2009 15:49:15 João Ramos wrote:
>
>> Hi,
>>
>> Here is the revised patch according to Bart's suggestions in previous
>> emails.
>> The patch is against linux-2.6.30-rc6.
>>
>
> Thanks. I see that we can still cleanup the code a bit by folding current
> ep93xx_ide_{read,write}() into __ep93xx_ide_{read,write}() and then adding
> wrappers on top of it.
>
> I think that the patch would the best to show the idea in this case (it is
> completely untested, it may not build).
>
> [ While at it I also changed data transfer functions to use drive's timings
> and fixed ->remove method to unregister IDE host before doing host specific
> cleanup. These fixes should be applied regardless of the cleanup change. ]
>
> Please review/integrate/test and then resubmit the final patch.
>
> ---
> drivers/ide/ep93xx-ide.c | 111 ++++++++++++++++++++++-------------------------
> 1 file changed, 54 insertions(+), 57 deletions(-)
>
> Index: b/drivers/ide/ep93xx-ide.c
> ===================================================================
> --- a/drivers/ide/ep93xx-ide.c
> +++ b/drivers/ide/ep93xx-ide.c
> @@ -126,50 +126,67 @@ static void ep93xx_ide_clean_regs(void _
> writel(0, base + IDEUDMADEBUG);
> }
>
> -static u16 ep93xx_ide_read(void __iomem *base, unsigned long addr,
> - struct ide_timing *timing)
> +static u16 __ep93xx_ide_read(void __iomem *base, unsigned long addr,
> + struct ide_timing *t)
> {
> u32 reg;
>
> reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->setup);
> + ndelay(t->setup);
>
> reg &= ~IDECTRL_DIORN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->active);
> + ndelay(t->active);
>
> while (!ep93xx_ide_check_iordy(base))
> cpu_relax();
>
> reg |= IDECTRL_DIORN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->recover);
> + ndelay(t->recover);
>
> return readl(base + IDEDATAIN);
> }
>
> -static void ep93xx_ide_write(void __iomem *base, u16 value,
> - unsigned long addr, struct ide_timing *timing)
> +static inline u16 ep93xx_ide_read(ide_hwif_t *hwif, unsigned long addr)
> +{
> + void __iomem *base = hwif->host->host_priv;
> + struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> +
> + return __ep93xx_ide_read(base, addr, t);
> +}
> +
> +static void __ep93xx_ide_write(void __iomem *base, u16 value,
> + unsigned long addr, struct ide_timing *t)
> {
> u32 reg;
>
> reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->setup);
> + ndelay(t->setup);
>
> writel(value, base + IDEDATAOUT);
>
> reg &= ~IDECTRL_DIOWN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->active);
> + ndelay(t->active);
>
> while (!ep93xx_ide_check_iordy(base))
> cpu_relax();
>
> reg |= IDECTRL_DIOWN;
> writel(reg, base + IDECTRL);
> - ndelay(timing->recover);
> + ndelay(t->recover);
> +}
> +
> +static inline void ep93xx_ide_write(ide_hwif_t *hwif, u16 value,
> + unsigned long addr)
> +{
> + void __iomem *base = hwif->host->host_priv;
> + struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> +
> + __ep93xx_ide_write(base, value, addr, t);
> }
>
> /*
> @@ -178,88 +195,70 @@ static void ep93xx_ide_write(void __iome
>
> static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
> {
> - void __iomem *base = hwif->host->host_priv;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> -
> - ep93xx_ide_write(base, cmd, hwif->io_ports.command_addr, t);
> + ep93xx_ide_write(hwif, cmd, hwif->io_ports.command_addr);
> }
>
> static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
> {
> - void __iomem *base = hwif->host->host_priv;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> -
> - return ep93xx_ide_read(base, hwif->io_ports.status_addr, t);
> + return ep93xx_ide_read(whif, hwif->io_ports.status_addr);
> }
>
> static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
> {
> - void __iomem *base = hwif->host->host_priv;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> -
> - return ep93xx_ide_read(base, hwif->io_ports.ctl_addr, t);
> + return ep93xx_ide_read(hwif, hwif->io_ports.ctl_addr);
> }
>
> static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
> {
> - void __iomem *base = hwif->host->host_priv;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
> -
> - ep93xx_ide_write(base, ctl, hwif->io_ports.ctl_addr, t);
> + ep93xx_ide_write(hwif, ctl, hwif->io_ports.ctl_addr);
> }
>
> static void ep93xx_ide_dev_select(ide_drive_t *drive)
> {
> ide_hwif_t *hwif = drive->hwif;
> - void __iomem *base = hwif->host->host_priv;
> u8 select = drive->select | ATA_DEVICE_OBS;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
>
> - ep93xx_ide_write(base, select, hwif->io_ports.device_addr, t);
> + ep93xx_ide_write(hwif, select, hwif->io_ports.device_addr);
> }
>
> static void ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf,
> u8 valid)
> {
> ide_hwif_t *hwif = drive->hwif;
> - void __iomem *base = hwif->host->host_priv;
> struct ide_io_ports *io_ports = &hwif->io_ports;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
>
> if (valid & IDE_VALID_FEATURE)
> - ep93xx_ide_write(base, tf->feature, io_ports->feature_addr, t);
> + ep93xx_ide_write(hwif, tf->feature, io_ports->feature_addr);
> if (valid & IDE_VALID_NSECT)
> - ep93xx_ide_write(base, tf->nsect, io_ports->nsect_addr, t);
> + ep93xx_ide_write(hwif, tf->nsect, io_ports->nsect_addr);
> if (valid & IDE_VALID_LBAL)
> - ep93xx_ide_write(base, tf->lbal, io_ports->lbal_addr, t);
> + ep93xx_ide_write(hwif, tf->lbal, io_ports->lbal_addr);
> if (valid & IDE_VALID_LBAM)
> - ep93xx_ide_write(base, tf->lbam, io_ports->lbam_addr, t);
> + ep93xx_ide_write(hwif, tf->lbam, io_ports->lbam_addr);
> if (valid & IDE_VALID_LBAH)
> - ep93xx_ide_write(base, tf->lbah, io_ports->lbah_addr, t);
> + ep93xx_ide_write(hwif, tf->lbah, io_ports->lbah_addr);
> if (valid & IDE_VALID_DEVICE)
> - ep93xx_ide_write(base, tf->device, io_ports->device_addr, t);
> + ep93xx_ide_write(hwif, tf->device, io_ports->device_addr);
> }
>
> static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
> u8 valid)
> {
> ide_hwif_t *hwif = drive->hwif;
> - void __iomem *base = hwif->host->host_priv;
> struct ide_io_ports *io_ports = &hwif->io_ports;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
>
> if (valid & IDE_VALID_ERROR)
> - tf->error = ep93xx_ide_read(base, io_ports->feature_addr, t);
> + tf->error = ep93xx_ide_read(hwif, io_ports->feature_addr);
> if (valid & IDE_VALID_NSECT)
> - tf->nsect = ep93xx_ide_read(base, io_ports->nsect_addr, t);
> + tf->nsect = ep93xx_ide_read(hwif, io_ports->nsect_addr);
> if (valid & IDE_VALID_LBAL)
> - tf->lbal = ep93xx_ide_read(base, io_ports->lbal_addr, t);
> + tf->lbal = ep93xx_ide_read(hwif, io_ports->lbal_addr);
> if (valid & IDE_VALID_LBAM)
> - tf->lbam = ep93xx_ide_read(base, io_ports->lbam_addr, t);
> + tf->lbam = ep93xx_ide_read(hwif, io_ports->lbam_addr);
> if (valid & IDE_VALID_LBAH)
> - tf->lbah = ep93xx_ide_read(base, io_ports->lbah_addr, t);
> + tf->lbah = ep93xx_ide_read(hwif, io_ports->lbah_addr);
> if (valid & IDE_VALID_DEVICE)
> - tf->device = ep93xx_ide_read(base, io_ports->device_addr, t);
> + tf->device = ep93xx_ide_read(hwif, io_ports->device_addr);
> }
>
> static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
> @@ -268,13 +267,12 @@ static void ep93xx_ide_input_data(ide_dr
> u16 *data = (u16 *)buf;
> ide_hwif_t *hwif = drive->hwif;
> void __iomem *base = hwif->host->host_priv;
> - struct ide_io_ports *io_ports = &hwif->io_ports;
> + unsigned long data_addr = hwif->io_ports.data_addr;
> + struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
> unsigned int words = (len + 1) >> 1;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
>
> while (words--)
> - *data++ = cpu_to_le16(ep93xx_ide_read(base,
> - io_ports->data_addr, t));
> + *data++ = cpu_to_le16(__ep93xx_ide_read(base, data_addr, t);
> }
>
> static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
> @@ -283,13 +281,12 @@ static void ep93xx_ide_output_data(ide_d
> u16 *data = (u16 *)buf;
> ide_hwif_t *hwif = drive->hwif;
> void __iomem *base = hwif->host->host_priv;
> - struct ide_io_ports *io_ports = &hwif->io_ports;
> + unsigned long data_addr = hwif->io_ports.data_addr;
> + struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
> unsigned int words = (len + 1) >> 1;
> - struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
>
> while (words--)
> - ep93xx_ide_write(base, le16_to_cpu(*data++),
> - io_ports->data_addr, t);
> + __ep93xx_ide_write(base, le16_to_cpu(*data++), data_addr, t);
> }
>
> static struct ide_tp_ops ep93xx_ide_tp_ops = {
> @@ -477,18 +474,18 @@ fail_release_mem:
>
> static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
> {
> - struct resource *mem_res;
> struct ide_host *host = pdev->dev.driver_data;
> void __iomem *base = host->host_priv;
> + struct resource *mem_res;
> +
> + ide_host_remove(host);
>
> /* reset state */
> - ep93xx_ide_clean_regs(host->host_priv);
> + ep93xx_ide_clean_regs(base);
>
> /* restore back GPIO port E, G and H for GPIO use */
> ep93xx_ide_release_gpios();
>
> - ide_host_remove(host);
> -
> iounmap(base);
>
> mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-19 13:20 ` João Ramos
@ 2009-05-19 13:56 ` Bartlomiej Zolnierkiewicz
2009-05-19 14:05 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-05-19 13:56 UTC (permalink / raw)
To: João Ramos; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
On Tuesday 19 May 2009 15:20:00 João Ramos wrote:
> Ok, I will apply the changes, test them and then resubmit the final patch.
> By the way, I will submit the final patch through 'arm-linux' mailing
> list, if that is OK to you, since the full patch has platform-specific
> dependencies.
> I mean, as long as we iterate with the IDE part of it untill it is good
> for submission from you, I can submit it in 'arm-linux' with your
> acknowledge, right?
It is good to post it also to linux-arm for additional review & better merge
coordination but the patch itself should go through IDE tree and linux-ide.
[ It is IDE host driver, has also IDE dependencies (ide_{set,get}_drivedata()
patch) and there are some other pending IDE changes that will require minor
updates to the patch. ]
Just ping me after platform-specific part is in and I'll push the driver
to Linus (of course given that everybody is happy with the final version).
Thanks.
Bart
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-19 13:56 ` Bartlomiej Zolnierkiewicz
@ 2009-05-19 14:05 ` João Ramos
2009-05-19 15:50 ` João Ramos
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-19 14:05 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
Bartlomiej Zolnierkiewicz escreveu:
> On Tuesday 19 May 2009 15:20:00 João Ramos wrote:
>
>> Ok, I will apply the changes, test them and then resubmit the final patch.
>> By the way, I will submit the final patch through 'arm-linux' mailing
>> list, if that is OK to you, since the full patch has platform-specific
>> dependencies.
>> I mean, as long as we iterate with the IDE part of it untill it is good
>> for submission from you, I can submit it in 'arm-linux' with your
>> acknowledge, right?
>>
>
> It is good to post it also to linux-arm for additional review & better merge
> coordination but the patch itself should go through IDE tree and linux-ide.
>
> [ It is IDE host driver, has also IDE dependencies (ide_{set,get}_drivedata()
> patch) and there are some other pending IDE changes that will require minor
> updates to the patch. ]
>
> Just ping me after platform-specific part is in and I'll push the driver
> to Linus (of course given that everybody is happy with the final version).
>
> Thanks.
> Bart
>
Ok, will do so.
João
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-19 14:05 ` João Ramos
@ 2009-05-19 15:50 ` João Ramos
2009-06-06 15:26 ` Sergei Shtylyov
0 siblings, 1 reply; 59+ messages in thread
From: João Ramos @ 2009-05-19 15:50 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: Sergei Shtylyov, Alan Cox, linux-ide
[-- Attachment #1: Type: text/plain, Size: 2080 bytes --]
Ok, I've done the fixes you suggested and tested the patch.
It is working fine, and I'm sending you (once again) the patch attached
to this e-mail.
In the meanwhile, I will post the entire patch to the 'arm-linux'
mailing list, iterating untill I get a final patch for the platform
dependent part.
Then, I will send the full patch back at linux-ide for submission.
I'll keep you both (Bart, Sergei) posted (CC) about iterations in the
'arm-linux' mailing list.
Best regards,
João Ramos
João Ramos escreveu:
> Bartlomiej Zolnierkiewicz escreveu:
>> On Tuesday 19 May 2009 15:20:00 João Ramos wrote:
>>
>>> Ok, I will apply the changes, test them and then resubmit the final
>>> patch.
>>> By the way, I will submit the final patch through 'arm-linux'
>>> mailing list, if that is OK to you, since the full patch has
>>> platform-specific dependencies.
>>> I mean, as long as we iterate with the IDE part of it untill it is
>>> good for submission from you, I can submit it in 'arm-linux' with
>>> your acknowledge, right?
>>>
>>
>> It is good to post it also to linux-arm for additional review &
>> better merge
>> coordination but the patch itself should go through IDE tree and
>> linux-ide.
>>
>> [ It is IDE host driver, has also IDE dependencies
>> (ide_{set,get}_drivedata()
>> patch) and there are some other pending IDE changes that will
>> require minor
>> updates to the patch. ]
>>
>> Just ping me after platform-specific part is in and I'll push the driver
>> to Linus (of course given that everybody is happy with the final
>> version).
>>
>> Thanks.
>> Bart
>>
>
> Ok, will do so.
>
> João
>
>
--
************************************************************************
João Ramos <joao.ramos@inov.pt>
INOV INESC Inovação - ESTG Leiria
Escola Superior de Tecnologia e Gestão de Leiria
Edíficio C1, Campus 2
Morro do Lena, Alto do Vieiro
Leiria
2411-901 Leiria
Portugal
Tel: +351244843424
Fax: +351244843424
************************************************************************
[-- Attachment #2: linux-2.6.30-rc6-ep93xx-ide.patch --]
[-- Type: text/x-patch, Size: 18841 bytes --]
Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
Cc: Ryan Mallon <ryan@bluewatersys.com>
Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
---
diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c
--- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c 2009-05-19 16:03:36.000000000 +0100
@@ -537,6 +537,31 @@
platform_device_register(&ep93xx_i2c_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)
diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
--- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-19 16:03:36.000000000 +0100
@@ -78,6 +78,7 @@
#define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000)
#define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000)
+#define EP93XX_IDE_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x000a0000)
#define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000)
diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h
--- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-19 16:03:36.000000000 +0100
@@ -15,6 +15,7 @@
void ep93xx_map_io(void);
void ep93xx_init_irq(void);
void ep93xx_init_time(unsigned long);
+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_init_devices(void);
diff -urN linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c
--- linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c 2009-05-19 16:06:33.000000000 +0100
@@ -0,0 +1,521 @@
+/*
+ * Support for Cirrus Logic's EP93xx (EP9312, EP9315) CPUs
+ * IDE host controller driver.
+ *
+ * Copyright (c) 2009, Joao Ramos <joao.ramos@inov.pt>
+ * 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.
+ */
+
+#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/ide.h>
+#include <linux/delay.h>
+
+#define MODULE_NAME "ep93xx-ide"
+
+/*
+ * 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.
+ */
+
+/*
+ * IDE Register offset
+ */
+#define IDECTRL 0x00
+#define IDECFG 0x04
+#define IDEMDMAOP 0x08
+#define IDEUDMAOP 0x0C
+#define IDEDATAOUT 0x10
+#define IDEDATAIN 0x14
+#define IDEMDMADATAOUT 0x18
+#define IDEMDMADATAIN 0x1C
+#define IDEUDMADATAOUT 0x20
+#define IDEUDMADATAIN 0x24
+#define IDEUDMASTS 0x28
+#define IDEUDMADEBUG 0x2C
+#define IDEUDMAWRBUFSTS 0x30
+#define IDEUDMARDBUFSTS 0x34
+
+/*
+ * IDE Control Register bit fields
+ */
+#define IDECTRL_CS0N 0x00000001
+#define IDECTRL_CS1N 0x00000002
+#define IDECTRL_DA 0x0000001C
+#define IDECTRL_DIORN 0x00000020
+#define IDECTRL_DIOWN 0x00000040
+#define IDECTRL_DASPN 0x00000080
+#define IDECTRL_DMARQ 0x00000100
+#define IDECTRL_INTRQ 0x00000200
+#define IDECTRL_IORDY 0x00000400
+
+/*
+ * IDE Configuration Register bit fields
+ */
+#define IDECFG_IDEEN 0x00000001
+#define IDECFG_PIO 0x00000002
+#define IDECFG_MDMA 0x00000004
+#define IDECFG_UDMA 0x00000008
+#define IDECFG_PIO_MODE_0 0x00000000
+#define IDECFG_PIO_MODE_1 0x00000010
+#define IDECFG_PIO_MODE_2 0x00000020
+#define IDECFG_PIO_MODE_3 0x00000030
+#define IDECFG_PIO_MODE_4 0x00000040
+#define IDECFG_MDMA_MODE_0 0x00000000
+#define IDECFG_MDMA_MODE_1 0x00000010
+#define IDECFG_MDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_0 0x00000000
+#define IDECFG_UDMA_MODE_1 0x00000010
+#define IDECFG_UDMA_MODE_2 0x00000020
+#define IDECFG_UDMA_MODE_3 0x00000030
+#define IDECFG_UDMA_MODE_4 0x00000040
+#define IDECFG_WST 0x00000300
+
+/*
+ * Check whether IORDY is asserted or not.
+ */
+static inline int ep93xx_ide_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 ep93xx_ide_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 __ep93xx_ide_read(void __iomem *base, unsigned long addr,
+ struct ide_timing *t)
+{
+ u32 reg;
+
+ reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->setup);
+
+ reg &= ~IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIORN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->recover);
+
+ return readl(base + IDEDATAIN);
+}
+
+static inline u16 ep93xx_ide_read(ide_hwif_t *hwif, unsigned long addr)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ return __ep93xx_ide_read(base, addr, t);
+}
+
+static void __ep93xx_ide_write(void __iomem *base, u16 value,
+ unsigned long addr, struct ide_timing *t)
+{
+ u32 reg;
+
+ reg = 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->active);
+
+ while (!ep93xx_ide_check_iordy(base))
+ cpu_relax();
+
+ reg |= IDECTRL_DIOWN;
+ writel(reg, base + IDECTRL);
+ ndelay(t->recover);
+}
+
+static inline void ep93xx_ide_write(ide_hwif_t *hwif, u16 value,
+ unsigned long addr)
+{
+ void __iomem *base = hwif->host->host_priv;
+ struct ide_timing *t = (struct ide_timing *)ide_get_hwifdata(hwif);
+
+ __ep93xx_ide_write(base, value, addr, t);
+}
+
+/*
+ * EP93xx IDE PIO Transport Operations
+ */
+
+static void ep93xx_ide_exec_command(struct hwif_s *hwif, u8 cmd)
+{
+ ep93xx_ide_write(hwif, cmd, hwif->io_ports.command_addr);
+}
+
+static u8 ep93xx_ide_read_status(ide_hwif_t *hwif)
+{
+ return ep93xx_ide_read(hwif, hwif->io_ports.status_addr);
+}
+
+static u8 ep93xx_ide_read_altstatus(ide_hwif_t *hwif)
+{
+ return ep93xx_ide_read(hwif, hwif->io_ports.ctl_addr);
+}
+
+static void ep93xx_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+ ep93xx_ide_write(hwif, ctl, hwif->io_ports.ctl_addr);
+}
+
+static void ep93xx_ide_dev_select(ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u8 select = drive->select | ATA_DEVICE_OBS;
+
+ ep93xx_ide_write(hwif, select, hwif->io_ports.device_addr);
+}
+
+static void ep93xx_ide_tf_load(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+
+ if (valid & IDE_VALID_FEATURE)
+ ep93xx_ide_write(hwif, tf->feature, io_ports->feature_addr);
+ if (valid & IDE_VALID_NSECT)
+ ep93xx_ide_write(hwif, tf->nsect, io_ports->nsect_addr);
+ if (valid & IDE_VALID_LBAL)
+ ep93xx_ide_write(hwif, tf->lbal, io_ports->lbal_addr);
+ if (valid & IDE_VALID_LBAM)
+ ep93xx_ide_write(hwif, tf->lbam, io_ports->lbam_addr);
+ if (valid & IDE_VALID_LBAH)
+ ep93xx_ide_write(hwif, tf->lbah, io_ports->lbah_addr);
+ if (valid & IDE_VALID_DEVICE)
+ ep93xx_ide_write(hwif, tf->device, io_ports->device_addr);
+}
+
+static void ep93xx_ide_tf_read(ide_drive_t *drive, struct ide_taskfile *tf,
+ u8 valid)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ struct ide_io_ports *io_ports = &hwif->io_ports;
+
+ if (valid & IDE_VALID_ERROR)
+ tf->error = ep93xx_ide_read(hwif, io_ports->feature_addr);
+ if (valid & IDE_VALID_NSECT)
+ tf->nsect = ep93xx_ide_read(hwif, io_ports->nsect_addr);
+ if (valid & IDE_VALID_LBAL)
+ tf->lbal = ep93xx_ide_read(hwif, io_ports->lbal_addr);
+ if (valid & IDE_VALID_LBAM)
+ tf->lbam = ep93xx_ide_read(hwif, io_ports->lbam_addr);
+ if (valid & IDE_VALID_LBAH)
+ tf->lbah = ep93xx_ide_read(hwif, io_ports->lbah_addr);
+ if (valid & IDE_VALID_DEVICE)
+ tf->device = ep93xx_ide_read(hwif, io_ports->device_addr);
+}
+
+static void ep93xx_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ u16 *data = (u16 *)buf;
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ unsigned long data_addr = hwif->io_ports.data_addr;
+ struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
+ unsigned int words = (len + 1) >> 1;
+
+ while (words--)
+ *data++ = cpu_to_le16(__ep93xx_ide_read(base, data_addr, t));
+}
+
+static void ep93xx_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+ void *buf, unsigned int len)
+{
+ u16 *data = (u16 *)buf;
+ ide_hwif_t *hwif = drive->hwif;
+ void __iomem *base = hwif->host->host_priv;
+ unsigned long data_addr = hwif->io_ports.data_addr;
+ struct ide_timing *t = (struct ide_timing *)ide_get_drivedata(drive);
+ unsigned int words = (len + 1) >> 1;
+
+ while (words--)
+ __ep93xx_ide_write(base, le16_to_cpu(*data++), data_addr, t);
+}
+
+static struct ide_tp_ops ep93xx_ide_tp_ops = {
+ .exec_command = ep93xx_ide_exec_command,
+ .read_status = ep93xx_ide_read_status,
+ .read_altstatus = ep93xx_ide_read_altstatus,
+ .write_devctl = ep93xx_ide_write_devctl,
+ .dev_select = ep93xx_ide_dev_select,
+ .tf_load = ep93xx_ide_tf_load,
+ .tf_read = ep93xx_ide_tf_read,
+ .input_data = ep93xx_ide_input_data,
+ .output_data = ep93xx_ide_output_data,
+};
+
+static void ep93xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
+{
+ ide_drive_t *pair = ide_get_pair_dev(drive);
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ struct ide_timing *cmd_t = t;
+ u32 reg = IDECFG_IDEEN | IDECFG_PIO;
+ void __iomem *base = drive->hwif->host->host_priv;
+
+ if (pair) {
+ struct ide_timing *pair_t = ide_get_drivedata(pair);
+
+ if (pair_t && pair_t->mode < t->mode)
+ cmd_t = pair_t;
+ }
+
+ /*
+ * store the adequate PIO mode timings, to be used later
+ * by ep93xx_ide_{read,write}
+ */
+ ide_set_drivedata(drive, (void *)t);
+ ide_set_hwifdata(drive->hwif, (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);
+}
+
+static struct ide_port_ops ep93xx_ide_port_ops = {
+ .set_pio_mode = ep93xx_ide_set_pio_mode,
+};
+
+static struct ide_port_info ep93xx_ide_port_info = {
+ .name = MODULE_NAME,
+ .tp_ops = &ep93xx_ide_tp_ops,
+ .port_ops = &ep93xx_ide_port_ops,
+ .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
+ IDE_HFLAG_NO_DMA | IDE_HFLAG_MMIO |
+ IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_UNMASK_IRQS,
+ .pio_mask = ATA_PIO4,
+};
+
+static int __init ep93xx_ide_probe(struct platform_device *pdev)
+{
+ int retval;
+ int irq;
+ void __iomem *ide_base;
+ struct resource *mem_res;
+ hw_regs_t hw;
+ hw_regs_t *hws[] = { &hw, NULL, NULL, NULL };
+ struct ide_host *host;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_res) {
+ dev_err(&pdev->dev,
+ "could not retrieve device memory resources!\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "could not retrieve device IRQ resource!\n");
+ return -ENXIO;
+ }
+
+ if (!request_mem_region
+ (mem_res->start, mem_res->end - mem_res->start + 1, MODULE_NAME)) {
+ dev_err(&pdev->dev, "could not request memory resources!\n");
+ return -EBUSY;
+ }
+
+ ide_base = ioremap(mem_res->start, mem_res->end - mem_res->start + 1);
+ if (!ide_base) {
+ dev_err(&pdev->dev, "could not map memory resources!\n");
+ retval = -ENOMEM;
+ goto fail_release_mem;
+ }
+
+ memset(&hw, 0, sizeof(hw));
+
+ /*
+ * fill in ide_io_ports structure.
+ * 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
+ */
+ hw.io_ports.data_addr = 0x02;
+ hw.io_ports.error_addr = 0x06;
+ hw.io_ports.nsect_addr = 0x0A;
+ hw.io_ports.lbal_addr = 0x0E;
+ hw.io_ports.lbam_addr = 0x12;
+ hw.io_ports.lbah_addr = 0x16;
+ hw.io_ports.device_addr = 0x1A;
+ hw.io_ports.command_addr = 0x1E;
+ hw.io_ports.ctl_addr = 0x01;
+
+ hw.irq = irq;
+ hw.chipset = ide_generic;
+ hw.dev = &pdev->dev;
+
+ /* enforce EP93xx IDE controller reset state */
+ ep93xx_ide_clean_regs(ide_base);
+
+ /* set gpio port E, G and H for IDE */
+ ep93xx_ide_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);
+
+ host = ide_host_alloc(&ep93xx_ide_port_info, hws);
+ if (!host) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for EP93xx IDE host!\n");
+ retval = -ENOMEM;
+ goto fail_unmap;
+ }
+
+ host->host_priv = ide_base;
+
+ retval = ide_host_register(host, &ep93xx_ide_port_info, hws);
+ if (retval) {
+ dev_err(&pdev->dev,
+ "unable to register EP93xx IDE driver!\n");
+ goto fail_free_host;
+ }
+
+ platform_set_drvdata(pdev, host);
+
+ dev_info(&pdev->dev, "EP93xx IDE host controller driver initialized\n");
+
+ return 0;
+
+fail_free_host:
+ ide_host_free(host);
+fail_unmap:
+ iounmap(ide_base);
+fail_release_mem:
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+ return retval;
+}
+
+static int __devexit ep93xx_ide_remove(struct platform_device *pdev)
+{
+ struct ide_host *host = pdev->dev.driver_data;
+ void __iomem *base = host->host_priv;
+ struct resource *mem_res;
+
+ ide_host_remove(host);
+
+ /* reset state */
+ ep93xx_ide_clean_regs(base);
+
+ /* restore back GPIO port E, G and H for GPIO use */
+ ep93xx_ide_release_gpios();
+
+ iounmap(base);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem_res->start, mem_res->end - mem_res->start + 1);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_ide_driver = {
+ .remove = __exit_p(ep93xx_ide_remove),
+ .driver = {
+ .name = MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init ep93xx_ide_init(void)
+{
+ return platform_driver_probe(&ep93xx_ide_driver, ep93xx_ide_probe);
+}
+module_init(ep93xx_ide_init);
+
+static void __exit ep93xx_ide_exit(void)
+{
+ platform_driver_unregister(&ep93xx_ide_driver);
+}
+module_exit(ep93xx_ide_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joao Ramos <joao.ramos@inov.pt>");
+MODULE_DESCRIPTION("EP93xx IDE host controller driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:ep93xx-ide");
diff -urN linux-2.6.30-rc6.orig/drivers/ide/Kconfig linux-2.6.30-rc6/drivers/ide/Kconfig
--- linux-2.6.30-rc6.orig/drivers/ide/Kconfig 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/Kconfig 2009-05-19 16:03:36.000000000 +0100
@@ -732,6 +732,19 @@
depends on ARM && ARCH_AT91 && !ARCH_AT91RM9200 && !ARCH_AT91X40
select IDE_TIMINGS
+config BLK_DEV_IDE_EP93XX
+ tristate "Cirrus Logic EPxx (EP9312, EP9315) IDE support"
+ depends on ARM && ARCH_EP93XX
+ select IDE_TIMINGS
+ help
+ This is a host controller driver for IDE hardware included in
+ Cirrus Logic's EP9312 and EP9315 CPUs.
+ Say Y here if you want to enable the IDE host controller support
+ for your machine.
+
+ Choose 'M' to compile this driver as a module; the module will be
+ called 'ep93xx-ide'.
+
config BLK_DEV_IDE_ICSIDE
tristate "ICS IDE interface support"
depends on ARM && ARCH_ACORN
diff -urN linux-2.6.30-rc6.orig/drivers/ide/Makefile linux-2.6.30-rc6/drivers/ide/Makefile
--- linux-2.6.30-rc6.orig/drivers/ide/Makefile 2009-05-16 05:12:57.000000000 +0100
+++ linux-2.6.30-rc6/drivers/ide/Makefile 2009-05-19 16:03:36.000000000 +0100
@@ -117,3 +117,4 @@
obj-$(CONFIG_BLK_DEV_IDE_TX4938) += tx4938ide.o
obj-$(CONFIG_BLK_DEV_IDE_TX4939) += tx4939ide.o
obj-$(CONFIG_BLK_DEV_IDE_AT91) += at91_ide.o
+obj-$(CONFIG_BLK_DEV_IDE_EP93XX) += ep93xx-ide.o
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-17 15:20 ` Bartlomiej Zolnierkiewicz
@ 2009-05-22 17:52 ` Sergei Shtylyov
0 siblings, 0 replies; 59+ messages in thread
From: Sergei Shtylyov @ 2009-05-22 17:52 UTC (permalink / raw)
To: Bartlomiej Zolnierkiewicz; +Cc: João Ramos, Alan Cox, linux-ide
Hello.
Bartlomiej Zolnierkiewicz wrote:
>>Here's the revised patches, according to the changes you suggested.
> Thanks, I applied set_pio_mode.patch with the following minor fixup:
> @@ -1035,7 +1035,7 @@
>
> ide_port_for_each_dev(i, drive, hwif) {
> /*
> - * default to PIO Mode 0 before we figure out
> + * default to PIO Mode 0 before we figure out
> * the most suited mode for the attached device
> */
> if (port_ops && port_ops->set_pio_mode)
> and will apply the following version of ide_drive_data.patch
> (unless there are some major complains):
> From: Joao Ramos <joao.ramos@inov.pt>
> Subject: ide: do not access ide_drive_t 'drive_data' field directly
> Change ide_drive_t 'drive_data' field from 'unsigned int' type to 'void *'
> type, allowing a wider range of values/types to be stored in this field.
> Added 'ide_get_drivedata' and 'ide_set_drivedata' helpers to get and set
> the 'drive_data' field.
> Fixed all host drivers to maintain coherency with the change in the
> 'drive_data' field type.
> Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> [bart: fix qd65xx build, cast to 'unsigned long', minor Coding Style fixups]
> Signed-off-by: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
Acked-by: Sergei Shtylyov <sshtylyov@ru.montavista.com>
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-05-19 15:50 ` João Ramos
@ 2009-06-06 15:26 ` Sergei Shtylyov
2009-06-22 10:01 ` Bartlomiej Zolnierkiewicz
0 siblings, 1 reply; 59+ messages in thread
From: Sergei Shtylyov @ 2009-06-06 15:26 UTC (permalink / raw)
To: João Ramos; +Cc: Bartlomiej Zolnierkiewicz, Alan Cox, linux-ide
Hello.
João Ramos wrote:
> Ok, I've done the fixes you suggested and tested the patch.
> It is working fine, and I'm sending you (once again) the patch
> attached to this e-mail.
>
> In the meanwhile, I will post the entire patch to the 'arm-linux'
> mailing list, iterating untill I get a final patch for the platform
> dependent part.
> Then, I will send the full patch back at linux-ide for submission.
I'm not sure why you again joined the driver patch and the platfrorm
code patch together...
>>>> Ok, I will apply the changes, test them and then resubmit the final
>>>> patch.
>>>> By the way, I will submit the final patch through 'arm-linux'
>>>> mailing list, if that is OK to you, since the full patch has
>>>> platform-specific dependencies.
>>>> I mean, as long as we iterate with the IDE part of it untill it is
>>>> good for submission from you, I can submit it in 'arm-linux' with
>>>> your acknowledge, right?
>>>
>>> It is good to post it also to linux-arm for additional review &
>>> better merge
>>> coordination but the patch itself should go through IDE tree and
>>> linux-ide.
>>>
>>> [ It is IDE host driver, has also IDE dependencies
>>> (ide_{set,get}_drivedata()
>>> patch) and there are some other pending IDE changes that will
>>> require minor
>>> updates to the patch. ]
>>>
>>> Just ping me after platform-specific part is in and I'll push the
>>> driver
>>> to Linus (of course given that everybody is happy with the final
>>> version).
I'm not happy yet. :-)
[...]
> Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
>
> Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
> Cc: Ryan Mallon <ryan@bluewatersys.com>
> Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
> Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
>
> ---
>
> diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c
> --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c 2009-05-16 05:12:57.000000000 +0100
> +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c 2009-05-19 16:03:36.000000000 +0100
> @@ -537,6 +537,31 @@
> platform_device_register(&ep93xx_i2c_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)
> diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-16 05:12:57.000000000 +0100
> +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-19 16:03:36.000000000 +0100
> @@ -78,6 +78,7 @@
> #define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000)
>
> #define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000)
> +#define EP93XX_IDE_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x000a0000)
>
> #define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000)
>
> diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h
> --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-16 05:12:57.000000000 +0100
> +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-19 16:03:36.000000000 +0100
> @@ -15,6 +15,7 @@
> void ep93xx_map_io(void);
> void ep93xx_init_irq(void);
> void ep93xx_init_time(unsigned long);
> +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_init_devices(void);
> diff -urN linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c
> --- linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c 2009-05-19 16:06:33.000000000 +0100
> @@ -0,0 +1,521 @@
>
[...]
> +static u16 __ep93xx_ide_read(void __iomem *base, unsigned long addr,
> + struct ide_timing *t)
> +{
> + u32 reg;
> +
> + reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
> + writel(reg, base + IDECTRL);
> + ndelay(t->setup);
> +
> + reg &= ~IDECTRL_DIORN;
> + writel(reg, base + IDECTRL);
> + ndelay(t->active);
> +
> + while (!ep93xx_ide_check_iordy(base))
> + cpu_relax();
> +
> + reg |= IDECTRL_DIORN;
> + writel(reg, base + IDECTRL);
> + ndelay(t->recover);
>
No, actually it should be t->cycle - t->active - t->setup. t->recover
is a only a minimum recovery time, and you need to respect t->cycle minimum.
> +static void __ep93xx_ide_write(void __iomem *base, u16 value,
> + unsigned long addr, struct ide_timing *t)
> +{
> + u32 reg;
> +
> + reg = 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->active);
> +
> + while (!ep93xx_ide_check_iordy(base))
> + cpu_relax();
> +
> + reg |= IDECTRL_DIOWN;
> + writel(reg, base + IDECTRL);
> + ndelay(t->recover);
>
Same here...
> +static void ep93xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
> +{
> + ide_drive_t *pair = ide_get_pair_dev(drive);
> + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
> + struct ide_timing *cmd_t = t;
> + u32 reg = IDECFG_IDEEN | IDECFG_PIO;
> + void __iomem *base = drive->hwif->host->host_priv;
> +
> + if (pair) {
> + struct ide_timing *pair_t = ide_get_drivedata(pair);
> +
> + if (pair_t && pair_t->mode < t->mode)
> + cmd_t = pair_t;
>
No, it should be like in other drivers:
if (pair) {
<http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l169>
u8 mode = ide_get_best_pio_mode(pair, 255, 4);
<http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l170>
<http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l171>
if (mode < pio)
<http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l172>
cmd_t = ide_timing_find_mode(XFER_PIO_0 + mode) ;
<http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l173>
}
> + }
> +
> + /*
> + * store the adequate PIO mode timings, to be used later
> + * by ep93xx_ide_{read,write}
> + */
> + ide_set_drivedata(drive, (void *)t);
> + ide_set_hwifdata(drive->hwif, (void *)cmd_t);
> +
> + /* reconfigure IDE controller according to PIO mode */
> + switch (pio) {
> + case 4:
> + reg |= IDECFG_PIO_MODE_4 | ((1 << 8) & IDECFG_WST);
>
What does WST field mean? I also don't understand what purpose could
serve setting the PIO mode here if you ensure to apply all the necessary
manually...
> +static struct ide_port_ops ep93xx_ide_port_ops = {
> + .set_pio_mode = ep93xx_ide_set_pio_mode,
> +};
> +
> +static struct ide_port_info ep93xx_ide_port_info = {
> + .name = MODULE_NAME,
> + .tp_ops = &ep93xx_ide_tp_ops,
> + .port_ops = &ep93xx_ide_port_ops,
> + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
>
Is the latter really necessary?
> + /*
> + * fill in ide_io_ports structure.
> + * 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
> + */
> + hw.io_ports.data_addr = 0x02;
> + hw.io_ports.error_addr = 0x06;
> + hw.io_ports.nsect_addr = 0x0A;
> + hw.io_ports.lbal_addr = 0x0E;
> + hw.io_ports.lbam_addr = 0x12;
> + hw.io_ports.lbah_addr = 0x16;
>
>
Hm, where's this empty line from? :-)
> + hw.io_ports.device_addr = 0x1A;
> + hw.io_ports.command_addr = 0x1E;
> + hw.io_ports.ctl_addr = 0x01;
MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
* Re: EP93xx PIO IDE driver proposal
2009-06-06 15:26 ` Sergei Shtylyov
@ 2009-06-22 10:01 ` Bartlomiej Zolnierkiewicz
0 siblings, 0 replies; 59+ messages in thread
From: Bartlomiej Zolnierkiewicz @ 2009-06-22 10:01 UTC (permalink / raw)
To: Sergei Shtylyov, João Ramos; +Cc: Alan Cox, linux-ide, David Miller
Hi João,
I merged all preparatory patches into Linus tree but there has been IDE
Maintainer and policy change so I would suggest hurrying up with the final
version of the patch (I think that it will be still fine for 2.6.31 but
the final decision is up to David now).
On Saturday 06 June 2009 17:26:31 Sergei Shtylyov wrote:
> Hello.
>
> João Ramos wrote:
>
> > Ok, I've done the fixes you suggested and tested the patch.
> > It is working fine, and I'm sending you (once again) the patch
> > attached to this e-mail.
> >
> > In the meanwhile, I will post the entire patch to the 'arm-linux'
> > mailing list, iterating untill I get a final patch for the platform
> > dependent part.
> > Then, I will send the full patch back at linux-ide for submission.
>
> I'm not sure why you again joined the driver patch and the platfrorm
> code patch together...
>
> >>>> Ok, I will apply the changes, test them and then resubmit the final
> >>>> patch.
> >>>> By the way, I will submit the final patch through 'arm-linux'
> >>>> mailing list, if that is OK to you, since the full patch has
> >>>> platform-specific dependencies.
> >>>> I mean, as long as we iterate with the IDE part of it untill it is
> >>>> good for submission from you, I can submit it in 'arm-linux' with
> >>>> your acknowledge, right?
> >>>
> >>> It is good to post it also to linux-arm for additional review &
> >>> better merge
> >>> coordination but the patch itself should go through IDE tree and
> >>> linux-ide.
> >>>
> >>> [ It is IDE host driver, has also IDE dependencies
> >>> (ide_{set,get}_drivedata()
> >>> patch) and there are some other pending IDE changes that will
> >>> require minor
> >>> updates to the patch. ]
> >>>
> >>> Just ping me after platform-specific part is in and I'll push the
> >>> driver
> >>> to Linus (of course given that everybody is happy with the final
> >>> version).
>
> I'm not happy yet. :-)
>
> [...]
>
> > Add IDE host controller support for Cirrus Logic's EP93xx CPUs.
> >
> > Signed-off-by: Joao Ramos <joao.ramos@inov.pt>
> > Cc: H Hartley Sweeten <hsweeten@visionengravers.com>
> > Cc: Ryan Mallon <ryan@bluewatersys.com>
> > Cc: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com>
> > Cc: Sergei Shtylyov <sshtylyov@ru.montavista.com>
> >
> > ---
> >
> > diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c
> > --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/core.c 2009-05-16 05:12:57.000000000 +0100
> > +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/core.c 2009-05-19 16:03:36.000000000 +0100
> > @@ -537,6 +537,31 @@
> > platform_device_register(&ep93xx_i2c_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)
> > diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
> > --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-16 05:12:57.000000000 +0100
> > +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h 2009-05-19 16:03:36.000000000 +0100
> > @@ -78,6 +78,7 @@
> > #define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000)
> >
> > #define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000)
> > +#define EP93XX_IDE_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x000a0000)
> >
> > #define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000)
> >
> > diff -urN linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h
> > --- linux-2.6.30-rc6.orig/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-16 05:12:57.000000000 +0100
> > +++ linux-2.6.30-rc6/arch/arm/mach-ep93xx/include/mach/platform.h 2009-05-19 16:03:36.000000000 +0100
> > @@ -15,6 +15,7 @@
> > void ep93xx_map_io(void);
> > void ep93xx_init_irq(void);
> > void ep93xx_init_time(unsigned long);
> > +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_init_devices(void);
> > diff -urN linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c
> > --- linux-2.6.30-rc6.orig/drivers/ide/ep93xx-ide.c 1970-01-01 01:00:00.000000000 +0100
> > +++ linux-2.6.30-rc6/drivers/ide/ep93xx-ide.c 2009-05-19 16:06:33.000000000 +0100
> > @@ -0,0 +1,521 @@
> >
> [...]
> > +static u16 __ep93xx_ide_read(void __iomem *base, unsigned long addr,
> > + struct ide_timing *t)
> > +{
> > + u32 reg;
> > +
> > + reg = addr | IDECTRL_DIORN | IDECTRL_DIOWN;
> > + writel(reg, base + IDECTRL);
> > + ndelay(t->setup);
> > +
> > + reg &= ~IDECTRL_DIORN;
> > + writel(reg, base + IDECTRL);
> > + ndelay(t->active);
> > +
> > + while (!ep93xx_ide_check_iordy(base))
> > + cpu_relax();
> > +
> > + reg |= IDECTRL_DIORN;
> > + writel(reg, base + IDECTRL);
> > + ndelay(t->recover);
> >
>
> No, actually it should be t->cycle - t->active - t->setup. t->recover
> is a only a minimum recovery time, and you need to respect t->cycle minimum.
>
> > +static void __ep93xx_ide_write(void __iomem *base, u16 value,
> > + unsigned long addr, struct ide_timing *t)
> > +{
> > + u32 reg;
> > +
> > + reg = 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->active);
> > +
> > + while (!ep93xx_ide_check_iordy(base))
> > + cpu_relax();
> > +
> > + reg |= IDECTRL_DIOWN;
> > + writel(reg, base + IDECTRL);
> > + ndelay(t->recover);
> >
>
> Same here...
>
> > +static void ep93xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
> > +{
> > + ide_drive_t *pair = ide_get_pair_dev(drive);
> > + struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
> > + struct ide_timing *cmd_t = t;
> > + u32 reg = IDECFG_IDEEN | IDECFG_PIO;
> > + void __iomem *base = drive->hwif->host->host_priv;
> > +
> > + if (pair) {
> > + struct ide_timing *pair_t = ide_get_drivedata(pair);
> > +
> > + if (pair_t && pair_t->mode < t->mode)
> > + cmd_t = pair_t;
> >
>
> No, it should be like in other drivers:
>
> if (pair) {
> <http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l169>
> u8 mode = ide_get_best_pio_mode(pair, 255, 4);
>
> <http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l170>
>
> <http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l171>
> if (mode < pio)
> <http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l172>
> cmd_t = ide_timing_find_mode(XFER_PIO_0 + mode) ;
> <http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=blob;f=drivers/ide/palm_bk3710.c;h=09d813d313f4a44986a2f358d8ddc08c619533f1;hb=HEAD#l173>
> }
>
>
> > + }
> > +
> > + /*
> > + * store the adequate PIO mode timings, to be used later
> > + * by ep93xx_ide_{read,write}
> > + */
> > + ide_set_drivedata(drive, (void *)t);
> > + ide_set_hwifdata(drive->hwif, (void *)cmd_t);
> > +
> > + /* reconfigure IDE controller according to PIO mode */
> > + switch (pio) {
> > + case 4:
> > + reg |= IDECFG_PIO_MODE_4 | ((1 << 8) & IDECFG_WST);
> >
>
> What does WST field mean? I also don't understand what purpose could
> serve setting the PIO mode here if you ensure to apply all the necessary
> manually...
>
> > +static struct ide_port_ops ep93xx_ide_port_ops = {
> > + .set_pio_mode = ep93xx_ide_set_pio_mode,
> > +};
> > +
> > +static struct ide_port_info ep93xx_ide_port_info = {
> > + .name = MODULE_NAME,
> > + .tp_ops = &ep93xx_ide_tp_ops,
> > + .port_ops = &ep93xx_ide_port_ops,
> > + .host_flags = IDE_HFLAG_SINGLE | IDE_HFLAG_POST_SET_MODE |
> >
>
> Is the latter really necessary?
>
> > + /*
> > + * fill in ide_io_ports structure.
> > + * 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
> > + */
> > + hw.io_ports.data_addr = 0x02;
> > + hw.io_ports.error_addr = 0x06;
> > + hw.io_ports.nsect_addr = 0x0A;
> > + hw.io_ports.lbal_addr = 0x0E;
> > + hw.io_ports.lbam_addr = 0x12;
> > + hw.io_ports.lbah_addr = 0x16;
> >
> >
>
> Hm, where's this empty line from? :-)
>
> > + hw.io_ports.device_addr = 0x1A;
> > + hw.io_ports.command_addr = 0x1E;
> > + hw.io_ports.ctl_addr = 0x01;
>
> MBR, Sergei
^ permalink raw reply [flat|nested] 59+ messages in thread
end of thread, other threads:[~2009-06-22 11:43 UTC | newest]
Thread overview: 59+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <49CCD7C4.8000207@inov.pt>
[not found] ` <49CFDD8F.1030306@bluewatersys.com>
[not found] ` <BD79186B4FD85F4B8E60E381CAEE1909014E2E09@mi8nycmail19.Mi8.com>
[not found] ` <49D0CAE4.9090306@inov.pt>
2009-03-30 15:34 ` EP93xx PIO IDE driver proposal Sergei Shtylyov
2009-05-04 11:24 ` João Ramos
2009-05-05 12:04 ` Sergei Shtylyov
2009-05-06 14:17 ` João Ramos
2009-05-06 17:05 ` Sergei Shtylyov
2009-05-07 9:36 ` João Ramos
2009-05-07 11:01 ` João Ramos
2009-05-07 13:53 ` Alan Cox
2009-05-07 15:33 ` João Ramos
2009-05-08 12:04 ` Bartlomiej Zolnierkiewicz
2009-05-08 12:16 ` João Ramos
2009-05-08 12:40 ` Bartlomiej Zolnierkiewicz
2009-05-08 13:30 ` Sergei Shtylyov
2009-05-08 14:09 ` Bartlomiej Zolnierkiewicz
2009-05-08 17:28 ` João Ramos
2009-05-08 18:02 ` Bartlomiej Zolnierkiewicz
2009-05-08 18:16 ` João Ramos
2009-05-08 18:55 ` Bartlomiej Zolnierkiewicz
2009-05-08 20:24 ` joao.ramos
2009-05-08 21:01 ` Sergei Shtylyov
2009-05-08 22:07 ` Bartlomiej Zolnierkiewicz
2009-05-11 11:10 ` João Ramos
2009-05-12 16:49 ` Sergei Shtylyov
2009-05-12 17:23 ` Bartlomiej Zolnierkiewicz
2009-05-13 11:01 ` João Ramos
2009-05-17 15:20 ` Bartlomiej Zolnierkiewicz
2009-05-22 17:52 ` Sergei Shtylyov
2009-05-13 14:18 ` João Ramos
2009-05-14 19:44 ` Bartlomiej Zolnierkiewicz
2009-05-15 17:01 ` João Ramos
2009-05-17 16:16 ` Bartlomiej Zolnierkiewicz
2009-05-18 13:49 ` João Ramos
2009-05-19 13:06 ` Bartlomiej Zolnierkiewicz
2009-05-19 13:20 ` João Ramos
2009-05-19 13:56 ` Bartlomiej Zolnierkiewicz
2009-05-19 14:05 ` João Ramos
2009-05-19 15:50 ` João Ramos
2009-06-06 15:26 ` Sergei Shtylyov
2009-06-22 10:01 ` Bartlomiej Zolnierkiewicz
2009-05-14 16:30 ` Sergei Shtylyov
2009-05-14 16:36 ` Sergei Shtylyov
2009-05-14 18:58 ` Bartlomiej Zolnierkiewicz
2009-05-11 13:20 ` João Ramos
2009-05-12 16:41 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:57 ` Sergei Shtylyov
2009-05-12 16:01 ` João Ramos
2009-05-12 16:30 ` Bartlomiej Zolnierkiewicz
2009-05-12 16:45 ` João Ramos
2009-05-07 16:52 ` H Hartley Sweeten
2009-05-07 22:09 ` Ryan Mallon
2009-05-07 22:31 ` H Hartley Sweeten
2009-05-07 22:51 ` Ryan Mallon
2009-05-07 23:01 ` H Hartley Sweeten
2009-05-07 23:12 ` Ryan Mallon
2009-05-07 23:32 ` João Ramos
2009-05-07 23:58 ` H Hartley Sweeten
2009-05-08 11:23 ` Sergei Shtylyov
2009-05-08 12:47 ` João Ramos
[not found] ` <49D12669.4030207@bluewatersys.com>
2009-03-31 10:36 ` Sergei Shtylyov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).