diff options
Diffstat (limited to 'drivers/ide/s3c2443-ide.c')
-rw-r--r-- | drivers/ide/s3c2443-ide.c | 477 |
1 files changed, 477 insertions, 0 deletions
diff --git a/drivers/ide/s3c2443-ide.c b/drivers/ide/s3c2443-ide.c new file mode 100644 index 000000000000..4de1a537fb4e --- /dev/null +++ b/drivers/ide/s3c2443-ide.c @@ -0,0 +1,477 @@ +/* linux/drivers/ide/s3c2443-ide.c + * + * Copyright (c) 2009 Digi Internationa Inc. + * http://www.digi.com + * + * Partially based on previous driver by Seung-Chull, Suh <sc.suh@samsung.com> + * Copyright(C) Samsung Electronics 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/ide.h> +#include <linux/blkdev.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <mach/map.h> +#include <mach/gpio.h> +#include <mach/regs-gpio.h> +#include <mach/regs-gpioj.h> +#include <mach/regs-bus.h> +#include <mach/regs-cfata.h> +#include <mach/gpio.h> + +#define DRV_NAME "s3c2443-ide" + +MODULE_AUTHOR("Pedro Perez de Heredia <pedro.perez@digi.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("S3C2443 IDE driver"); + +struct _s3c2443_ide_hwif { + ide_hwif_t *hwif; + int irq; + void __iomem *membase; + struct platform_device *dev; + struct clk *clk; +}; + +static struct _s3c2443_ide_hwif s3c2443_ide_hwif; + +static inline void s3c2443_wait_until_ready(void) +{ + u32 i, reg; + + for (i = 0; i < 100000; i++) { + reg = readl(s3c2443_ide_hwif.membase + S3C2443_ATA_FIFO_STATUS); + if ((reg >> 28) == 0) + return; + } +} + +static __inline__ u8 s3c2443_ide_readb(unsigned long port) +{ + s3c2443_wait_until_ready(); + (void)readb(port); + s3c2443_wait_until_ready(); + return readb(s3c2443_ide_hwif.membase + S3C2443_ATA_PIO_RDATA); +} + +static __inline__ u16 s3c2443_ide_readw(unsigned long port) +{ + s3c2443_wait_until_ready(); + (void)readw(port); + s3c2443_wait_until_ready(); + return readw(s3c2443_ide_hwif.membase + S3C2443_ATA_PIO_RDATA); +} + +static __inline__ void s3c2443_ide_writeb(u8 val, unsigned long port) +{ + s3c2443_wait_until_ready(); + writeb(val, port); +} + +static __inline__ void s3c2443_ide_writew(u16 val, unsigned long port) +{ + s3c2443_wait_until_ready(); + writew(val, port); +} + +static void s3c2443_ide_exec_command(ide_hwif_t *hwif, u8 cmd) +{ + s3c2443_ide_writeb(cmd, hwif->io_ports.command_addr); +} + +static u8 s3c2443_read_status(ide_hwif_t *hwif) +{ + return s3c2443_ide_readb(hwif->io_ports.status_addr); +} + +static u8 s3c2443_read_altstatus(ide_hwif_t *hwif) +{ + return s3c2443_ide_readb(hwif->io_ports.ctl_addr); +} + +static void s3c2443_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; + s3c2443_ide_writeb(ctl, hwif->io_ports.ctl_addr); +} + +static void s3c2443_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) { + u16 data = (tf->hob_data << 8) | tf->data; + s3c2443_ide_writew (data, io_ports->data_addr); + } + + if (task->tf_flags & IDE_TFLAG_OUT_HOB_FEATURE) + s3c2443_ide_writeb(tf->hob_feature, io_ports->feature_addr); + if (task->tf_flags & IDE_TFLAG_OUT_HOB_NSECT) + s3c2443_ide_writeb(tf->hob_nsect, io_ports->nsect_addr); + if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAL) + s3c2443_ide_writeb(tf->hob_lbal, io_ports->lbal_addr); + if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAM) + s3c2443_ide_writeb(tf->hob_lbam, io_ports->lbam_addr); + if (task->tf_flags & IDE_TFLAG_OUT_HOB_LBAH) + s3c2443_ide_writeb(tf->hob_lbah, io_ports->lbah_addr); + + if (task->tf_flags & IDE_TFLAG_OUT_FEATURE) + s3c2443_ide_writeb(tf->feature, io_ports->feature_addr); + if (task->tf_flags & IDE_TFLAG_OUT_NSECT) + s3c2443_ide_writeb(tf->nsect, io_ports->nsect_addr); + if (task->tf_flags & IDE_TFLAG_OUT_LBAL) + s3c2443_ide_writeb(tf->lbal, io_ports->lbal_addr); + if (task->tf_flags & IDE_TFLAG_OUT_LBAM) + s3c2443_ide_writeb(tf->lbam, io_ports->lbam_addr); + if (task->tf_flags & IDE_TFLAG_OUT_LBAH) + s3c2443_ide_writeb(tf->lbah, io_ports->lbah_addr); + + if (task->tf_flags & IDE_TFLAG_OUT_DEVICE) + s3c2443_ide_writeb((tf->device & HIHI) | drive->select, + io_ports->device_addr); +} + +static void s3c2443_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) { + u16 data; + + data = s3c2443_ide_readw(io_ports->data_addr); + + tf->data = data & 0xff; + tf->hob_data = (data >> 8) & 0xff; + } + + /* be sure we're looking at the low order bits */ + s3c2443_ide_writeb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr); + + if (task->tf_flags & IDE_TFLAG_IN_FEATURE) + tf->feature = s3c2443_ide_readb(io_ports->feature_addr); + if (task->tf_flags & IDE_TFLAG_IN_NSECT) + tf->nsect = s3c2443_ide_readb(io_ports->nsect_addr); + if (task->tf_flags & IDE_TFLAG_IN_LBAL) + tf->lbal = s3c2443_ide_readb(io_ports->lbal_addr); + if (task->tf_flags & IDE_TFLAG_IN_LBAM) + tf->lbam = s3c2443_ide_readb(io_ports->lbam_addr); + if (task->tf_flags & IDE_TFLAG_IN_LBAH) + tf->lbah = s3c2443_ide_readb(io_ports->lbah_addr); + if (task->tf_flags & IDE_TFLAG_IN_DEVICE) + tf->device = s3c2443_ide_readb(io_ports->device_addr); + + if (task->tf_flags & IDE_TFLAG_LBA48) { + s3c2443_ide_writeb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr); + + if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE) + tf->hob_feature = s3c2443_ide_readb(io_ports->feature_addr); + if (task->tf_flags & IDE_TFLAG_IN_HOB_NSECT) + tf->hob_nsect = s3c2443_ide_readb(io_ports->nsect_addr); + if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAL) + tf->hob_lbal = s3c2443_ide_readb(io_ports->lbal_addr); + if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAM) + tf->hob_lbam = s3c2443_ide_readb(io_ports->lbam_addr); + if (task->tf_flags & IDE_TFLAG_IN_HOB_LBAH) + tf->hob_lbah = s3c2443_ide_readb(io_ports->lbah_addr); + } +} + +static void s3c2443_input_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + u16 *buffer = (u16 *)buf; + + /* round up, it should be power of 2 */ + len = (len + 1) >> 1; + + while (len--) { + *buffer++ = s3c2443_ide_readw(io_ports->data_addr); + } +} + +static void s3c2443_output_data(ide_drive_t *drive, struct request *rq, void *buf, + unsigned int len) +{ + ide_hwif_t *hwif = drive->hwif; + struct ide_io_ports *io_ports = &hwif->io_ports; + u16 *buffer = (u16 *)buf; + + len >>= 1; + + while (len--) { + s3c2443_ide_writew(*buffer++, io_ports->data_addr); + } +} + +static const struct ide_tp_ops s3c2443_tp_ops = { + .exec_command = s3c2443_ide_exec_command, + .read_status = s3c2443_read_status, + .read_altstatus = s3c2443_read_altstatus, + .set_irq = s3c2443_set_irq, + .tf_load = s3c2443_tf_load, + .tf_read = s3c2443_tf_read, + .input_data = s3c2443_input_data, + .output_data = s3c2443_output_data, +}; + +static const struct ide_port_info s3c2443_port_info = { + .tp_ops = &s3c2443_tp_ops, + .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA | \ + IDE_HFLAG_NO_IO_32BIT, +}; + +static int s3c2443_ide_ack_intr(ide_hwif_t *hwif) +{ + u32 reg = 0, tout = 100; + + while (tout--) { + reg = readl(s3c2443_ide_hwif.membase + S3C2443_ATA_IRQ); + if (reg) { + writel(reg, s3c2443_ide_hwif.membase + S3C2443_ATA_IRQ); + break; + } + } + + return (int)reg; +} + +static int s3c2443_ide_hw_config(struct _s3c2443_ide_hwif *hw) +{ + int ret = 0; + u32 reg; + void __iomem *ebicon; + + ebicon = ioremap(S3C2443_PA_EBI + 0x8, 0x4); + if (ebicon == NULL) { + ret =-EBUSY; + goto err_unmap; + } + + /* Configure EBI bank 2 and 3 for the CF interface */ + reg = readl(ebicon); + reg |= S3C2443_EBICON_BANK3_CFG | S3C2443_EBICON_BANK2_CFG; + writel(reg, ebicon); + + /* Configure the IO lines*/ + s3c2443_gpio_cfgpin(S3C2410_GPG15, S3C2443_GPG15_CF_PWR); + s3c2443_gpio_cfgpin(S3C2410_GPG14, S3C2443_GPG14_CF_RESET); + s3c2443_gpio_cfgpin(S3C2410_GPG13, S3C2443_GPG13_CF_nREG); + s3c2443_gpio_cfgpin(S3C2410_GPG12, S3C2443_GPG12_nINPACK); + s3c2443_gpio_cfgpin(S3C2410_GPG11, S3C2443_GPG11_CF_nIREQ); + + s3c2443_gpio_cfgpin(S3C2410_GPA10, 0); + s3c2443_gpio_cfgpin(S3C2410_GPA11, 1); + s3c2443_gpio_cfgpin(S3C2410_GPA12, 1); + s3c2443_gpio_cfgpin(S3C2410_GPA15, 1); + + /* Clear the card detect condition */ + reg = readl(S3C24XX_MISCCR) & ~S3C2443_MISCCR_nCD_CF; + writel(reg, S3C24XX_MISCCR); + + /* Output Port disabled, Card power off, ATA mode */ + writel(0x07, hw->membase + S3C2443_MUX_REG); + mdelay(10); + /* Output Port enable, Card power off, ATA mode */ + writel(0x03, hw->membase + S3C2443_MUX_REG); + mdelay(10); + /* Output Port enable, Card power on, ATA mode */ + writel(0x01, hw->membase + S3C2443_MUX_REG); + mdelay(500); /* wait for 500ms */ + + writel(0x1C238, hw->membase + S3C2443_ATA_PIO_TIME); + writel(0x20B1362, hw->membase + S3C2443_ATA_UDMA_TIME); + + /* Enable ATA */ + reg = readl(hw->membase + S3C2443_ATA_CONTROL); + writel(reg | 0x1, hw->membase + S3C2443_ATA_CONTROL); + + mdelay(200); + /* remove IRQ Status and enable only ATA device interrupt */ + writel(0x1f, hw->membase + S3C2443_ATA_IRQ); + writel(0x1b, hw->membase + S3C2443_ATA_IRQ_MASK); + + mdelay(200); + +err_unmap: + iounmap(ebicon); + return ret; +} + + +static int __devinit s3c2443_ide_probe(struct platform_device *pdev) +{ + int ret = 0, i; + struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif; + hw_regs_t hw, *hws[] = {&hw, NULL, NULL, NULL}; + struct resource *res; + struct ide_host *host; + + memset(&s3c2443_ide_hwif, 0, sizeof(struct _s3c2443_ide_hwif)); + + shwif->dev = pdev; + + /* find and map our resources */ + shwif->irq = platform_get_irq(pdev, 0); + if (shwif->irq < 0) { + pr_debug("%s: %s, can not get IORESOURCE_IRQ\n", DRV_NAME, __func__); + ret =-ENOENT; + goto error; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + pr_debug("%s: %s, can not get IORESOURCE_MEM\n", DRV_NAME, __func__); + ret = -ENOENT; + goto error; + } + + if (!request_mem_region(res->start, res->end - res->start + 1, + pdev->name)) { + pr_debug("%s: %s,request_mem_region failed\n", DRV_NAME, __func__); + ret = -EBUSY; + goto error; + } + + shwif->membase = ioremap(res->start, res->end - res->start + 1); + if (shwif->membase == NULL) { + pr_debug("%s: %s, can not map IO space at 0x%08x\n", DRV_NAME, __func__, res->start); + ret = -ENOMEM; + goto error_map; + } + + shwif->clk = clk_get(&pdev->dev, "cfc"); + if (IS_ERR(shwif->clk)) { + pr_debug("%s: %s, failed to find clock source\n", DRV_NAME, __func__); + ret = PTR_ERR(shwif->clk); + goto error_clock; + } + + if ((ret = clk_enable(shwif->clk))) { + pr_debug("%s: %s, failed to enable clock\n", DRV_NAME, __func__); + goto error_clk_en; + } + + ret = s3c2443_ide_hw_config(shwif); + if (ret) { + pr_debug("%s: %s, failed to configure IDE hardware\n", DRV_NAME, __func__); + goto error_hwcfg; + } + + memset(&hw, 0, sizeof(hw)); + + for (i = 0; i < 9; i++) + hw.io_ports_array[i] = (unsigned long)(shwif->membase + S3C2443_ATA_PIO_DTR + (i * 4)); + hw.io_ports.ctl_addr = (unsigned long)(shwif->membase + S3C2443_ATA_PIO_DTR + 8 * 4); + hw.irq = shwif->irq; + hw.ack_intr = s3c2443_ide_ack_intr; + hw.chipset = ide_generic; + hw.dev = &pdev->dev; + + ret = ide_host_add(&s3c2443_port_info, hws, &host); + if (ret) { + pr_debug("%s: %s, failed add IDE host device\n", DRV_NAME, __func__); + goto error_add; + } + + shwif->hwif = host->ports[0]; + platform_set_drvdata(pdev, host); + + printk(KERN_INFO "S3C2443 IDE driver\n"); + return 0; + +error_add: + /* XXX unconfig hardware here ? */ +error_hwcfg: + clk_disable(shwif->clk); +error_clk_en: + clk_put(shwif->clk); +error_clock: + iounmap(shwif->membase); +error_map: + release_mem_region(res->start, res->end - res->start + 1); +error: + return ret; +} + +static int __devexit s3c2443_ide_remove(struct platform_device *pdev) +{ + struct ide_host *host = dev_get_drvdata(&pdev->dev); + struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif; + struct resource *res; + + ide_host_remove(host); + + clk_disable(shwif->clk); + clk_put(shwif->clk); + + iounmap(shwif->membase); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res != NULL) + release_mem_region(res->start, res->end - res->start + 1); + + return 0; +} + +#ifdef CONFIG_PM +static int s3c2443_ide_resume(struct platform_device *dev) +{ + struct _s3c2443_ide_hwif *shwif = &s3c2443_ide_hwif; + + return s3c2443_ide_hw_config(shwif); +} +#else +# define s3c2443_ide_suspend NULL +# define s3c2443_ide_resume NULL +#endif + + +static struct platform_driver s3c2443_ide_driver = { + .probe = s3c2443_ide_probe, + .remove = __devexit_p(s3c2443_ide_remove), + .resume = s3c2443_ide_resume, + .driver = { + .name = "s3c2443-ide", + .owner = THIS_MODULE, + }, +}; + +static int __init s3c2443_ide_init(void) +{ + return platform_driver_register(&s3c2443_ide_driver); +} + +static void __exit s3c2443_ide_exit(void) +{ + platform_driver_unregister(&s3c2443_ide_driver); +} + +module_init(s3c2443_ide_init); +module_exit(s3c2443_ide_exit); + |