From: Sergei Shtylyov <sshtylyov@ru.mvista.com>
To: Stanislaw Gruszka <stf_xl@wp.pl>
Cc: linux-ide@vger.kernel.org, Andrew Victor <linux@maxim.org.za>,
linux-arm-kernel@lists.arm.linux.org.uk
Subject: Re: [PATCH 2/3] ide: add at91_ide driver
Date: Wed, 04 Feb 2009 15:19:50 +0300 [thread overview]
Message-ID: <498987E6.7040909@ru.mvista.com> (raw)
In-Reply-To: <200902031147.22827.stf_xl@wp.pl>
Hello.
Stanislaw Gruszka wrote:
> This is IDE host driver for AT91SAM9 Static Memory Controller with Compact
> Flash True IDE Mode logic.
>
> Driver have to switch 8/16 bit bus width when accessing Task Tile or Data
> Register. Moreover some extra things need to be done when setting PIO mode.
> Only PIO mode is used, hardware have no DMA support. If interrupt line is
> connected through GPIO extra quirk is needed to cope with fake interrupts.
>
> Signed-off-by: Stanislaw Gruszka <stf_xl@wp.pl>
>
[...]
> diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
> index fb51f0e..6674b9b 100644
> --- a/arch/arm/mach-at91/include/mach/board.h
> +++ b/arch/arm/mach-at91/include/mach/board.h
> @@ -59,6 +59,18 @@ struct at91_cf_data {
> };
> extern void __init at91_add_device_cf(struct at91_cf_data *data);
>
> + /* Compact Flash True IDE mode */
> +struct at91_ide_data {
> + u8 irq_pin; /* the same meaning as for CF */
>
I again have to express my dislike about not passing IRQ the usual
way. Also, see my comments to the platform code.
> + u8 det_pin;
> + u8 rst_pin;
> + u8 chipselect;
> + u8 flags;
> +#define AT91_IDE_SWAP_A0_A2 0x01
> +};
> +
> +extern void __init at91_add_device_ide(struct at91_ide_data *data);
> +
> /* MMC / SD */
> struct at91_mmc_data {
> u8 det_pin; /* card detect IRQ */
> diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
> index 3dad229..b11da5b 100644
> --- a/drivers/ide/Kconfig
> +++ b/drivers/ide/Kconfig
> @@ -721,6 +721,11 @@ config BLK_DEV_IDE_TX4939
> depends on SOC_TX4939
> select BLK_DEV_IDEDMA_SFF
>
> +config BLK_DEV_IDE_AT91
> + tristate "Atmel AT91 IDE support"
>
Please be more specific -- you can't drive AT91RM9200 SMC.
> + depends on ARM && ARCH_AT91
>
Please add "&& !ARCH_AT91RM9200". And maybe "&& !ARCH_AT91X40" too...
> diff --git a/drivers/ide/at91_ide.c b/drivers/ide/at91_ide.c
> new file mode 100644
> index 0000000..3a1f7e0
> --- /dev/null
> +++ b/drivers/ide/at91_ide.c
> @@ -0,0 +1,496 @@
> +/*
> + * IDE host driver for AT91SAM9 Static Memory Controller
>
Why not call the driver 'at91sam9_ide'?
> +/*
> + * AT91 Static Memory Controller
AT91SAM9.
> maps Task File and Data Register
>
> + * at the same address. To distinguish access between these two
It would have been strange if it did it otherwise...
> + * different bus data width is used: 8Bit for Task File, 16Bit for Data I/O
> + *
> + * After initialization we do 8/16 bit flipping (changes in SMC MODE register)
> + * only inside IDE callback routines which are serialized by IDE layer,
> + * so no additional locking needed.
> + */
> +
> +static void init_smc_mode(const u8 chipselect)
> +{
> + at91_sys_write(AT91_SMC_MODE(chipselect), AT91_SMC_READMODE |
> + AT91_SMC_WRITEMODE |
> + AT91_SMC_BAT_SELECT |
> + AT91_SMC_TDF_(0));
>
I'm not sure why are you fixing the dataflow timing again, this time
to 0...
> +}
> +
> +static inline void set_8bit_mode(const u8 chipselect)
> +{
> + unsigned long mode = at91_sys_read(AT91_SMC_MODE(chipselect));
> + mode &= ~AT91_SMC_DBW;
> + mode |= AT91_SMC_DBW_8;
> + at91_sys_write(AT91_SMC_MODE(chipselect), mode);
> + pdbg("%u %08lx\n", chipselect, mode);
> +}
> +
> +static inline void set_16bit_mode(const u8 chipselect)
> +{
> + unsigned long mode = at91_sys_read(AT91_SMC_MODE(chipselect));
> + mode &= ~AT91_SMC_DBW;
> + mode |= AT91_SMC_DBW_16;
> + at91_sys_write(AT91_SMC_MODE(chipselect), mode);
> + pdbg("%u %08lx\n", chipselect, mode);
> +}
>
I'd advice to make this single function because it looks like a code
duplication too much.
> +static void set_smc_timings(const u8 chipselect, const u16 cycle,
> + const u16 setup, const u16 pulse,
> + const u16 data_float, int use_iordy)
>
Have you considered using already present sam9_smc_configure()
instead to avoid the code duplication (it needs to be exported though)?
> +{
> + unsigned long mode;
> +
> + /* setup timings in SMC */
> + at91_sys_write(AT91_SMC_SETUP(chipselect), AT91_SMC_NWESETUP_(setup) |
> + AT91_SMC_NCS_WRSETUP_(0) |
> + AT91_SMC_NRDSETUP_(setup) |
> + AT91_SMC_NCS_RDSETUP_(0));
> + at91_sys_write(AT91_SMC_PULSE(chipselect), AT91_SMC_NWEPULSE_(pulse) |
> + AT91_SMC_NCS_WRPULSE_(cycle) |
> + AT91_SMC_NRDPULSE_(pulse) |
> + AT91_SMC_NCS_RDPULSE_(cycle));
> + at91_sys_write(AT91_SMC_CYCLE(chipselect), AT91_SMC_NWECYCLE_(cycle) |
> + AT91_SMC_NRDCYCLE_(cycle));
> +
> + mode = at91_sys_read(AT91_SMC_MODE(chipselect));
>
Frankly speaking, I don't see why you're reading this register back
if you already know what needs to be set there -- as you've done it in
init_smc_mode().
> +static unsigned int calc_mck_cycles(unsigned int ns, unsigned int mck_hz)
> +{
> + u64 tmp = ns;
> +
> + tmp *= mck_hz;
> + tmp += 1000*1000*1000 - 1; /* round up */
> + do_div(tmp, 1000*1000*1000);
> + return (unsigned int) tmp;
> +}
> +
> +static void apply_timings(const u8 chipselect, const u8 pio,
> + const struct ide_timing *timing, const u16 *ata_id)
> +{
> + unsigned int t0, t1, t2, t6z, th;
> + unsigned int cycle, setup, pulse, data_float;
> + unsigned int mck_hz;
> + struct clk *mck;
> + int use_iordy;
>
[...]
> +
> + use_iordy = 0;
>
Can be done in initializer...
> + if (ata_id) {
> + if (ata_id_is_cfa(ata_id)) {
> + if (pio == 3 || pio == 4)
> + use_iordy = 1;
>
> + } else if (pio >= 3 || ata_id_has_iordy(ata_id))
> + use_iordy = 1;
>
No, you should check for IORDY regardless of CFA and using IORDY
shouldn't be limeited to modes 3 and 4:
if (ata_id_has_iordy(ata_id) && !(ata_id_is_cfa(ata_id) && pio > 4))
use_iodry = 1;
> +static u8 ide_mm_inb(unsigned long port)
> +{
> + return (u8) readb((void __iomem *) port);
>
Explict cast not needed here.
> +void at91_ide_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;
> +
> + if (task->tf_flags & IDE_TFLAG_FLAGGED)
> + HIHI = 0xFF;
> +
> + if (task->tf_flags & IDE_TFLAG_OUT_DATA) {
>
Sigh. Bart, couldn't we drop that stupid flag? I bet nobody ever used it.
> +void at91_ide_tf_read(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;
> +
> + if (task->tf_flags & IDE_TFLAG_IN_DATA) {
>
... and this one too?
> +/*
> + * If interrupt is delivered through GPIO, IRQ are triggered on falling
> + * and raising edge of signal. Whereas IDE device request interrupt on high
> + * level (raising edge in our case). This mean we have fake interrupts, so
> + * we need to check interrupt pin and exit instantly from ISR when line
> + * is on low level.
> + */
> +
> +irqreturn_t at91_irq_handler(int irq, void *dev_id)
> +{
> + int ntries = 8;
> + int pin_val1, pin_val2;
> +
> + /* additional deglitch, line can be noisy in badly designed PCB */
> + do {
> + pin_val1 = at91_get_gpio_value(irq);
> + pin_val2 = at91_get_gpio_value(irq);
> + } while (pin_val1 != pin_val2 && --ntries > 0);
>
I suggest a shorter code:
do {
pin_val = at91_get_gpio_value(irq);
} while (pin_val != at91_get_gpio_value(irq) && --ntries > 0);
> +static int __init at91_ide_probe(struct platform_device *pdev)
> +{
>
[...]
> + host->ports[0]->extra_base = board->chipselect;
>
BTW, we have 2 fields in the struct hwif_s that fit this case better:
config_data and select_data. It's a bit stange that you've selected
extra_base...
MBR, Sergei
next prev parent reply other threads:[~2009-02-04 12:19 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-02-03 10:47 [PATCH 2/3] ide: add at91_ide driver Stanislaw Gruszka
2009-02-04 12:19 ` Sergei Shtylyov [this message]
2009-02-04 14:47 ` Stanislaw Gruszka
2009-02-04 16:04 ` Sergei Shtylyov
2009-02-04 16:08 ` Sergei Shtylyov
2009-02-05 15:01 ` Stanislaw Gruszka
2009-02-05 16:09 ` Sergei Shtylyov
2009-02-05 20:00 ` Andrew Victor
2009-02-05 20:03 ` Sergei Shtylyov
2009-02-06 9:35 ` Stanislaw Gruszka
2009-02-06 10:55 ` Sergei Shtylyov
2009-02-06 16:50 ` Bartlomiej Zolnierkiewicz
2009-02-06 17:20 ` Sergei Shtylyov
2009-02-05 21:23 ` Bartlomiej Zolnierkiewicz
2009-02-05 23:31 ` Sergei Shtylyov
2009-02-06 16:36 ` Bartlomiej Zolnierkiewicz
2009-02-08 0:10 ` Sergei Shtylyov
2009-02-08 11:39 ` Sergei Shtylyov
2009-02-08 22:58 ` Sergei Shtylyov
2009-02-09 19:48 ` Bartlomiej Zolnierkiewicz
2009-02-06 9:30 ` Stanislaw Gruszka
2009-02-06 10:36 ` Sergei Shtylyov
2009-02-06 10:47 ` Stanislaw Gruszka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=498987E6.7040909@ru.mvista.com \
--to=sshtylyov@ru.mvista.com \
--cc=linux-arm-kernel@lists.arm.linux.org.uk \
--cc=linux-ide@vger.kernel.org \
--cc=linux@maxim.org.za \
--cc=stf_xl@wp.pl \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).