[PATCH v5] libata: pata_samsung: Add Samsung PATA controller driver
Juergen Schindele
schindele at nentec.de
Tue Jun 29 09:29:43 EDT 2010
Am Dienstag, 29. Juni 2010 schrieb Kukjin Kim:
> From: Abhilash Kesavan <a.kesavan at samsung.com>
>
> Adds support for the Samsung PATA controller. This driver is based on the
> Libata subsystem and references the earlier patches sent for IDE subsystem.
Dear Kukjin Kim,
as i had trouble with CF / trueIDE on PXA, have you read this thread ??
<http://www.spinics.net/lists/arm-kernel/msg91461.html>
Are you sure you dont need a "dcache_flush-page" ???
Its just a thought to help you avoid problems.
Bye
> Signed-off-by: Abhilash Kesavan <a.kesavan at samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim at samsung.com>
> Cc: Ben Dooks <ben-linux at fluff.org>
> ---
> drivers/ata/Kconfig | 9 +
> drivers/ata/Makefile | 1 +
> drivers/ata/pata_samsung_cf.c | 728 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 738 insertions(+), 0 deletions(-)
> create mode 100644 drivers/ata/pata_samsung_cf.c
>
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index 73f8833..51e28e7 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -796,6 +796,15 @@ config PATA_RZ1000
>
> If unsure, say N.
>
> +config PATA_SAMSUNG_CF
> + tristate "Samsung SoC PATA support"
> + depends on SAMSUNG_DEV_IDE
> + help
> + This option enables basic support for Samsung's S3C/S5P board
> + PATA controllers via the new ATA layer
> +
> + If unsure, say N.
> +
> config PATA_WINBOND_VLB
> tristate "Winbond W83759A VLB PATA support (Experimental)"
> depends on ISA && EXPERIMENTAL
> diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
> index 7ef89d7..9576776 100644
> --- a/drivers/ata/Makefile
> +++ b/drivers/ata/Makefile
> @@ -87,6 +87,7 @@ obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o
> obj-$(CONFIG_PATA_QDI) += pata_qdi.o
> obj-$(CONFIG_PATA_RB532) += pata_rb532_cf.o
> obj-$(CONFIG_PATA_RZ1000) += pata_rz1000.o
> +obj-$(CONFIG_PATA_SAMSUNG_CF) += pata_samsung_cf.o
> obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o
>
> # Should be last but two libata driver
> diff --git a/drivers/ata/pata_samsung_cf.c b/drivers/ata/pata_samsung_cf.c
> new file mode 100644
> index 0000000..26b96c3
> --- /dev/null
> +++ b/drivers/ata/pata_samsung_cf.c
> @@ -0,0 +1,728 @@
> +/*
> + * Copyright (c) 2010 Samsung Electronics Co., Ltd.
> + * http://www.samsung.com
> + *
> + * PATA driver for Samsung SoCs.
> + * Supports CF Interface in True IDE mode. Currently only PIO mode has been
> + * implemented; UDMA support has to be added.
> + *
> + * Based on:
> + * PATA driver for AT91SAM9260 Static Memory Controller
> + * PATA driver for Toshiba SCC controller
> + *
> + * 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/clk.h>
> +#include <linux/libata.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <plat/ata.h>
> +#include <plat/regs-ata.h>
> +
> +#define DRV_NAME "pata_samsung_cf"
> +#define DRV_VERSION "0.1"
> +
> +enum s3c_cpu_type {
> + TYPE_S3C64XX,
> + TYPE_S5PC100,
> + TYPE_S5PV210,
> +};
> +
> +/*
> + * struct s3c_ide_info - S3C PATA instance.
> + * @clk: The clock resource for this controller.
> + * @ide_addr: The area mapped for the hardware registers.
> + * @sfr_addr: The area mapped for the special function registers.
> + * @irq: The IRQ number we are using.
> + * @cpu_type: The exact type of this controller.
> + * @fifo_status_reg: The ATA_FIFO_STATUS register offset.
> + */
> +struct s3c_ide_info {
> + struct clk *clk;
> + void __iomem *ide_addr;
> + void __iomem *sfr_addr;
> + unsigned int irq;
> + enum s3c_cpu_type cpu_type;
> + unsigned int fifo_status_reg;
> +};
> +
> +static void pata_s3c_set_endian(void __iomem *s3c_ide_regbase, u8 mode)
> +{
> + u32 reg = readl(s3c_ide_regbase + S3C_ATA_CFG);
> + reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
> + writel(reg, s3c_ide_regbase + S3C_ATA_CFG);
> +}
> +
> +static void pata_s3c_cfg_mode(void __iomem *s3c_ide_sfrbase)
> +{
> + /* Select true-ide as the internal operating mode */
> + writel(readl(s3c_ide_sfrbase + S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,
> + s3c_ide_sfrbase + S3C_CFATA_MUX);
> +}
> +
> +static unsigned long
> +pata_s3c_setup_timing(struct s3c_ide_info *info, const struct ata_timing *ata)
> +{
> + int t1 = ata->setup;
> + int t2 = ata->act8b;
> + int t2i = ata->rec8b;
> + ulong piotime;
> +
> + piotime = ((t2i & 0xff) << 12) | ((t2 & 0xff) << 4) | (t1 & 0xf);
> +
> + return piotime;
> +}
> +
> +static void pata_s3c_set_piomode(struct ata_port *ap, struct ata_device *adev)
> +{
> + struct s3c_ide_info *info = ap->host->private_data;
> + struct ata_timing timing;
> + int cycle_time;
> + ulong ata_cfg = readl(info->ide_addr + S3C_ATA_CFG);
> + ulong piotime;
> +
> + /* Enables IORDY if mode requires it */
> + if (ata_pio_need_iordy(adev))
> + ata_cfg |= S3C_ATA_CFG_IORDYEN;
> + else
> + ata_cfg &= ~S3C_ATA_CFG_IORDYEN;
> +
> + cycle_time = (int)(1000000000UL / clk_get_rate(info->clk));
> +
> + ata_timing_compute(adev, adev->pio_mode, &timing,
> + cycle_time * 1000, 0);
> +
> + piotime = pata_s3c_setup_timing(info, &timing);
> +
> + writel(ata_cfg, info->ide_addr + S3C_ATA_CFG);
> + writel(piotime, info->ide_addr + S3C_ATA_PIO_TIME);
> +}
> +
> +/*
> + * Waits until the IDE controller is able to perform next read/write
> + * operation to the disk. Needed for 64XX series boards only.
> + */
> +static int wait_for_host_ready(struct s3c_ide_info *info)
> +{
> + ulong timeout;
> + void __iomem *fifo_reg = info->ide_addr + info->fifo_status_reg;
> +
> + /* wait for maximum of 20 msec */
> + timeout = jiffies + msecs_to_jiffies(20);
> + while (time_before(jiffies, timeout)) {
> + if ((readl(fifo_reg) >> 28) == 0)
> + return 0;
> + }
> + return -EBUSY;
> +}
> +
> +/*
> + * Writes to one of the task file registers.
> + */
> +static void ata_outb(struct ata_host *host, u8 addr, void __iomem *reg)
> +{
> + struct s3c_ide_info *info = host->private_data;
> +
> + wait_for_host_ready(info);
> + writeb(addr, reg);
> +}
> +
> +/*
> + * Reads from one of the task file registers.
> + */
> +static u8 ata_inb(struct ata_host *host, void __iomem *reg)
> +{
> + struct s3c_ide_info *info = host->private_data;
> + u8 temp;
> +
> + wait_for_host_ready(info);
> + (void) readb(reg);
> + wait_for_host_ready(info);
> + temp = readb(info->ide_addr + S3C_ATA_PIO_RDATA);
> + return temp;
> +}
> +
> +/*
> + * pata_s3c_tf_load - send taskfile registers to host controller
> + */
> +static void pata_s3c_tf_load(struct ata_port *ap,
> + const struct ata_taskfile *tf)
> +{
> + struct ata_ioports *ioaddr = &ap->ioaddr;
> + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
> +
> + if (tf->ctl != ap->last_ctl) {
> + ata_outb(ap->host, tf->ctl, ioaddr->ctl_addr);
> + ap->last_ctl = tf->ctl;
> + ata_wait_idle(ap);
> + }
> +
> + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
> + ata_outb(ap->host, tf->hob_feature, ioaddr->feature_addr);
> + ata_outb(ap->host, tf->hob_nsect, ioaddr->nsect_addr);
> + ata_outb(ap->host, tf->hob_lbal, ioaddr->lbal_addr);
> + ata_outb(ap->host, tf->hob_lbam, ioaddr->lbam_addr);
> + ata_outb(ap->host, tf->hob_lbah, ioaddr->lbah_addr);
> + }
> +
> + if (is_addr) {
> + ata_outb(ap->host, tf->feature, ioaddr->feature_addr);
> + ata_outb(ap->host, tf->nsect, ioaddr->nsect_addr);
> + ata_outb(ap->host, tf->lbal, ioaddr->lbal_addr);
> + ata_outb(ap->host, tf->lbam, ioaddr->lbam_addr);
> + ata_outb(ap->host, tf->lbah, ioaddr->lbah_addr);
> + }
> +
> + if (tf->flags & ATA_TFLAG_DEVICE)
> + ata_outb(ap->host, tf->device, ioaddr->device_addr);
> +
> + ata_wait_idle(ap);
> +}
> +
> +/*
> + * pata_s3c_tf_read - input device's ATA taskfile shadow registers
> + */
> +static void pata_s3c_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
> +{
> + struct ata_ioports *ioaddr = &ap->ioaddr;
> +
> + tf->feature = ata_inb(ap->host, ioaddr->error_addr);
> + tf->nsect = ata_inb(ap->host, ioaddr->nsect_addr);
> + tf->lbal = ata_inb(ap->host, ioaddr->lbal_addr);
> + tf->lbam = ata_inb(ap->host, ioaddr->lbam_addr);
> + tf->lbah = ata_inb(ap->host, ioaddr->lbah_addr);
> + tf->device = ata_inb(ap->host, ioaddr->device_addr);
> +
> + if (tf->flags & ATA_TFLAG_LBA48) {
> + iowrite8(tf->ctl | ATA_HOB, ioaddr->ctl_addr);
> + tf->hob_feature = ata_inb(ap->host, ioaddr->error_addr);
> + tf->hob_nsect = ata_inb(ap->host, ioaddr->nsect_addr);
> + tf->hob_lbal = ata_inb(ap->host, ioaddr->lbal_addr);
> + tf->hob_lbam = ata_inb(ap->host, ioaddr->lbam_addr);
> + tf->hob_lbah = ata_inb(ap->host, ioaddr->lbah_addr);
> + iowrite8(tf->ctl, ioaddr->ctl_addr);
> + ap->last_ctl = tf->ctl;
> + }
> +}
> +
> +/*
> + * pata_s3c_exec_command - issue ATA command to host controller
> + */
> +static void pata_s3c_exec_command(struct ata_port *ap,
> + const struct ata_taskfile *tf)
> +{
> + ata_outb(ap->host, tf->command, ap->ioaddr.command_addr);
> + ata_sff_pause(ap);
> +}
> +
> +/*
> + * pata_s3c_check_status - Read device status register
> + */
> +static u8 pata_s3c_check_status(struct ata_port *ap)
> +{
> + return ata_inb(ap->host, ap->ioaddr.status_addr);
> +}
> +
> +/*
> + * pata_s3c_check_altstatus - Read alternate device status register
> + */
> +static u8 pata_s3c_check_altstatus(struct ata_port *ap)
> +{
> + return ata_inb(ap->host, ap->ioaddr.altstatus_addr);
> +}
> +
> +/*
> + * pata_s3c_data_xfer - Transfer data by PIO
> + */
> +unsigned int pata_s3c_data_xfer(struct ata_device *dev, unsigned char *buf,
> + unsigned int buflen, int rw)
> +{
> + struct ata_port *ap = dev->link->ap;
> + struct s3c_ide_info *info = ap->host->private_data;
> + void __iomem *data_addr = ap->ioaddr.data_addr;
> + unsigned int words = buflen >> 1, i;
> + u16 *data_ptr = (u16 *)buf;
> +
> + /* Requires wait same as in ata_inb/ata_outb */
> + if (rw == READ)
> + for (i = 0; i < words; i++, data_ptr++) {
> + wait_for_host_ready(info);
> + (void) readw(data_addr);
> + wait_for_host_ready(info);
> + *data_ptr = readw(info->ide_addr
> + + S3C_ATA_PIO_RDATA);
> + }
> + else
> + for (i = 0; i < words; i++, data_ptr++) {
> + wait_for_host_ready(info);
> + writew(*data_ptr, data_addr);
> + }
> +
> + if (buflen & 0x01)
> + dev_err(ap->dev, "unexpected trailing data\n");
> +
> + return words << 1;
> +}
> +
> +/*
> + * pata_s3c_dev_select - Select device on ATA bus
> + */
> +static void pata_s3c_dev_select(struct ata_port *ap, unsigned int device)
> +{
> + u8 tmp = ATA_DEVICE_OBS;
> +
> + if (device != 0)
> + tmp |= ATA_DEV1;
> +
> + ata_outb(ap->host, tmp, ap->ioaddr.device_addr);
> + ata_sff_pause(ap);
> +}
> +
> +/*
> + * pata_s3c_devchk - PATA device presence detection
> + */
> +static unsigned int pata_s3c_devchk(struct ata_port *ap,
> + unsigned int device)
> +{
> + struct ata_ioports *ioaddr = &ap->ioaddr;
> + u8 nsect, lbal;
> +
> + pata_s3c_dev_select(ap, device);
> +
> + ata_outb(ap->host, 0x55, ioaddr->nsect_addr);
> + ata_outb(ap->host, 0xaa, ioaddr->lbal_addr);
> +
> + ata_outb(ap->host, 0xaa, ioaddr->nsect_addr);
> + ata_outb(ap->host, 0x55, ioaddr->lbal_addr);
> +
> + ata_outb(ap->host, 0x55, ioaddr->nsect_addr);
> + ata_outb(ap->host, 0xaa, ioaddr->lbal_addr);
> +
> + nsect = ata_inb(ap->host, ioaddr->nsect_addr);
> + lbal = ata_inb(ap->host, ioaddr->lbal_addr);
> +
> + if ((nsect == 0x55) && (lbal == 0xaa))
> + return 1; /* we found a device */
> +
> + return 0; /* nothing found */
> +}
> +
> +/*
> + * pata_s3c_wait_after_reset - wait for devices to become ready after reset
> + */
> +static int pata_s3c_wait_after_reset(struct ata_link *link,
> + unsigned int devmask, unsigned long deadline)
> +{
> + struct ata_port *ap = link->ap;
> + struct ata_ioports *ioaddr = &ap->ioaddr;
> + unsigned int dev0 = devmask & (1 << 0);
> + unsigned int dev1 = devmask & (1 << 1);
> + int rc, ret = 0;
> +
> + msleep(ATA_WAIT_AFTER_RESET);
> +
> + /* always check readiness of the master device */
> + rc = ata_sff_wait_ready(link, deadline);
> + /* -ENODEV means the odd clown forgot the D7 pulldown resistor
> + * and TF status is 0xff, bail out on it too.
> + */
> + if (rc)
> + return rc;
> +
> + /* if device 1 was found in ata_devchk, wait for register
> + * access briefly, then wait for BSY to clear.
> + */
> + if (dev1) {
> + int i;
> +
> + pata_s3c_dev_select(ap, 1);
> +
> + /* Wait for register access. Some ATAPI devices fail
> + * to set nsect/lbal after reset, so don't waste too
> + * much time on it. We're gonna wait for !BSY anyway.
> + */
> + for (i = 0; i < 2; i++) {
> + u8 nsect, lbal;
> +
> + nsect = ata_inb(ap->host, ioaddr->nsect_addr);
> + lbal = ata_inb(ap->host, ioaddr->lbal_addr);
> + if ((nsect == 1) && (lbal == 1))
> + break;
> + msleep(50); /* give drive a breather */
> + }
> +
> + rc = ata_sff_wait_ready(link, deadline);
> + if (rc) {
> + if (rc != -ENODEV)
> + return rc;
> + ret = rc;
> + }
> + }
> +
> + /* is all this really necessary? */
> + pata_s3c_dev_select(ap, 0);
> + if (dev1)
> + pata_s3c_dev_select(ap, 1);
> + if (dev0)
> + pata_s3c_dev_select(ap, 0);
> +
> + return ret;
> +}
> +
> +/*
> + * pata_s3c_bus_softreset - PATA device software reset
> + */
> +static unsigned int pata_s3c_bus_softreset(struct ata_port *ap,
> + unsigned int devmask, unsigned long deadline)
> +{
> + struct ata_ioports *ioaddr = &ap->ioaddr;
> +
> + /* software reset. causes dev0 to be selected */
> + ata_outb(ap->host, ap->ctl, ioaddr->ctl_addr);
> + udelay(20);
> + ata_outb(ap->host, ap->ctl | ATA_SRST, ioaddr->ctl_addr);
> + udelay(20);
> + ata_outb(ap->host, ap->ctl, ioaddr->ctl_addr);
> + ap->last_ctl = ap->ctl;
> +
> + return pata_s3c_wait_after_reset(&ap->link, devmask, deadline);
> +}
> +
> +/*
> + * pata_s3c_softreset - reset host port via ATA SRST
> + */
> +static int pata_s3c_softreset(struct ata_link *link, unsigned int *classes,
> + unsigned long deadline)
> +{
> + struct ata_port *ap = link->ap;
> + unsigned int devmask = 0;
> + int rc;
> + u8 err;
> +
> + /* determine if device 0/1 are present */
> + if (pata_s3c_devchk(ap, 0))
> + devmask |= (1 << 0);
> +
> + /* select device 0 again */
> + pata_s3c_dev_select(ap, 0);
> +
> + /* issue bus reset */
> + rc = pata_s3c_bus_softreset(ap, devmask, deadline);
> + /* if link is occupied, -ENODEV too is an error */
> + if (rc && rc != -ENODEV) {
> + ata_link_printk(link, KERN_ERR, "SRST failed (errno=%d)\n", rc);
> + return rc;
> + }
> +
> + /* determine by signature whether we have ATA or ATAPI devices */
> + classes[0] = ata_sff_dev_classify(&ap->link.device[0],
> + devmask & (1 << 0), &err);
> +
> + return 0;
> +}
> +
> +/*
> + * pata_s3c_set_devctl - Write device control register
> + */
> +static void pata_s3c_set_devctl(struct ata_port *ap, u8 ctl)
> +{
> + ata_outb(ap->host, ctl, ap->ioaddr.ctl_addr);
> +}
> +
> +static struct scsi_host_template pata_s3c_sht = {
> + ATA_PIO_SHT(DRV_NAME),
> +};
> +
> +static struct ata_port_operations pata_s3c_port_ops = {
> + .inherits = &ata_sff_port_ops,
> + .sff_check_status = pata_s3c_check_status,
> + .sff_check_altstatus = pata_s3c_check_altstatus,
> + .sff_tf_load = pata_s3c_tf_load,
> + .sff_tf_read = pata_s3c_tf_read,
> + .sff_data_xfer = pata_s3c_data_xfer,
> + .sff_exec_command = pata_s3c_exec_command,
> + .sff_dev_select = pata_s3c_dev_select,
> + .sff_set_devctl = pata_s3c_set_devctl,
> + .softreset = pata_s3c_softreset,
> + .set_piomode = pata_s3c_set_piomode,
> +};
> +
> +static struct ata_port_operations pata_s5p_port_ops = {
> + .inherits = &ata_sff_port_ops,
> + .set_piomode = pata_s3c_set_piomode,
> +};
> +
> +static void pata_s3c_enable(void *s3c_ide_regbase, bool state)
> +{
> + u32 temp = readl(s3c_ide_regbase + S3C_ATA_CTRL);
> + temp = state ? (temp | 1) : (temp & ~1);
> + writel(temp, s3c_ide_regbase + S3C_ATA_CTRL);
> +}
> +
> +static irqreturn_t pata_s3c_irq(int irq, void *dev_instance)
> +{
> + struct ata_host *host = dev_instance;
> + struct s3c_ide_info *info = host->private_data;
> + u32 reg;
> +
> + reg = readl(info->ide_addr + S3C_ATA_IRQ);
> + writel(reg, info->ide_addr + S3C_ATA_IRQ);
> +
> + return ata_sff_interrupt(irq, dev_instance);
> +}
> +
> +static void pata_s3c_hwinit(struct s3c_ide_info *info,
> + struct s3c_ide_platdata *pdata)
> +{
> + switch (info->cpu_type) {
> + case TYPE_S3C64XX:
> + /* Configure as big endian */
> + pata_s3c_cfg_mode(info->sfr_addr);
> + pata_s3c_set_endian(info->ide_addr, 1);
> + pata_s3c_enable(info->ide_addr, true);
> + msleep(100);
> +
> + /* Remove IRQ Status */
> + writel(0x1f, info->ide_addr + S3C_ATA_IRQ);
> + writel(0x1b, info->ide_addr + S3C_ATA_IRQ_MSK);
> + break;
> +
> + case TYPE_S5PC100:
> + pata_s3c_cfg_mode(info->sfr_addr);
> + /* FALLTHROUGH */
> +
> + case TYPE_S5PV210:
> + /* Configure as little endian */
> + pata_s3c_set_endian(info->ide_addr, 0);
> + pata_s3c_enable(info->ide_addr, true);
> + msleep(100);
> +
> + /* Remove IRQ Status */
> + writel(0x3f, info->ide_addr + S3C_ATA_IRQ);
> + writel(0x3f, info->ide_addr + S3C_ATA_IRQ_MSK);
> + break;
> +
> + default:
> + BUG();
> + }
> +}
> +
> +static int __init pata_s3c_probe(struct platform_device *pdev)
> +{
> + struct s3c_ide_platdata *pdata = pdev->dev.platform_data;
> + struct device *dev = &pdev->dev;
> + struct s3c_ide_info *info;
> + struct resource *res;
> + struct ata_port *ap;
> + struct ata_host *host;
> + enum s3c_cpu_type cpu_type;
> + int ret;
> +
> + cpu_type = platform_get_device_id(pdev)->driver_data;
> +
> + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
> + if (!info) {
> + dev_err(dev, "failed to allocate memory for device data\n");
> + return -ENOMEM;
> + }
> +
> + info->irq = platform_get_irq(pdev, 0);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (res == NULL) {
> + dev_err(dev, "failed to get mem resource\n");
> + return -EINVAL;
> + }
> +
> + if (!devm_request_mem_region(dev, res->start,
> + resource_size(res), DRV_NAME)) {
> + dev_err(dev, "error requesting register region\n");
> + return -EBUSY;
> + }
> +
> + info->ide_addr = devm_ioremap(dev, res->start, resource_size(res));
> + if (!info->ide_addr) {
> + dev_err(dev, "failed to map IO base address\n");
> + return -ENOMEM;
> + }
> +
> + info->clk = clk_get(&pdev->dev, "cfcon");
> + if (IS_ERR(info->clk)) {
> + dev_err(dev, "failed to get access to cf controller clock\n");
> + ret = PTR_ERR(info->clk);
> + info->clk = NULL;
> + return ret;
> + }
> +
> + clk_enable(info->clk);
> +
> + /* init ata host */
> + host = ata_host_alloc(dev, 1);
> + if (!host) {
> + dev_err(dev, "failed to allocate ide host\n");
> + ret = -ENOMEM;
> + goto stop_clk;
> + }
> +
> + ap = host->ports[0];
> + ap->flags |= ATA_FLAG_MMIO;
> + ap->pio_mask = ATA_PIO4;
> +
> + if (cpu_type == TYPE_S3C64XX) {
> + ap->ops = &pata_s3c_port_ops;
> + info->sfr_addr = info->ide_addr + 0x1800;
> + info->ide_addr += 0x1900;
> + info->fifo_status_reg = 0x94;
> + } else if (cpu_type == TYPE_S5PC100) {
> + ap->ops = &pata_s5p_port_ops;
> + info->sfr_addr = info->ide_addr + 0x1800;
> + info->ide_addr += 0x1900;
> + info->fifo_status_reg = 0x84;
> + } else {
> + ap->ops = &pata_s5p_port_ops;
> + info->fifo_status_reg = 0x84;
> + }
> +
> + info->cpu_type = cpu_type;
> +
> + if (info->irq <= 0) {
> + ap->flags |= ATA_FLAG_PIO_POLLING;
> + info->irq = 0;
> + ata_port_desc(ap, "no IRQ, using PIO polling\n");
> + }
> +
> + ap->ioaddr.cmd_addr = info->ide_addr + S3C_ATA_CMD;
> + ap->ioaddr.data_addr = info->ide_addr + S3C_ATA_PIO_DTR;
> + ap->ioaddr.error_addr = info->ide_addr + S3C_ATA_PIO_FED;
> + ap->ioaddr.feature_addr = info->ide_addr + S3C_ATA_PIO_FED;
> + ap->ioaddr.nsect_addr = info->ide_addr + S3C_ATA_PIO_SCR;
> + ap->ioaddr.lbal_addr = info->ide_addr + S3C_ATA_PIO_LLR;
> + ap->ioaddr.lbam_addr = info->ide_addr + S3C_ATA_PIO_LMR;
> + ap->ioaddr.lbah_addr = info->ide_addr + S3C_ATA_PIO_LHR;
> + ap->ioaddr.device_addr = info->ide_addr + S3C_ATA_PIO_DVR;
> + ap->ioaddr.status_addr = info->ide_addr + S3C_ATA_PIO_CSD;
> + ap->ioaddr.command_addr = info->ide_addr + S3C_ATA_PIO_CSD;
> + ap->ioaddr.altstatus_addr = info->ide_addr + S3C_ATA_PIO_DAD;
> + ap->ioaddr.ctl_addr = info->ide_addr + S3C_ATA_PIO_DAD;
> +
> + ata_port_desc(ap, "mmio cmd 0x%llx ",
> + (unsigned long long)res->start);
> +
> + host->private_data = info;
> +
> + if (pdata && pdata->setup_gpio)
> + pdata->setup_gpio();
> +
> + /* Set endianness and enable the interface */
> + pata_s3c_hwinit(info, pdata);
> +
> + platform_set_drvdata(pdev, host);
> +
> + return ata_host_activate(host, info->irq,
> + info->irq ? pata_s3c_irq : NULL,
> + 0, &pata_s3c_sht);
> +
> +stop_clk:
> + clk_disable(info->clk);
> + clk_put(info->clk);
> + return ret;
> +}
> +
> +static int __exit pata_s3c_remove(struct platform_device *pdev)
> +{
> + struct ata_host *host = platform_get_drvdata(pdev);
> + struct s3c_ide_info *info;
> +
> + info = host->private_data;
> + ata_host_detach(host);
> +
> + clk_disable(info->clk);
> + clk_put(info->clk);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int pata_s3c_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ata_host *host = platform_get_drvdata(pdev);
> + pm_message_t state = PMSG_SUSPEND;
> +
> + return ata_host_suspend(host, state);
> +}
> +
> +static int pata_s3c_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct ata_host *host = platform_get_drvdata(pdev);
> + struct s3c_ide_platdata *pdata = pdev->dev.platform_data;
> + struct s3c_ide_info *info;
> +
> + info = host->private_data;
> +
> + pata_s3c_hwinit(info, pdata);
> + ata_host_resume(host);
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops pata_s3c_pm_ops = {
> + .suspend = pata_s3c_suspend,
> + .resume = pata_s3c_resume,
> +};
> +#endif
> +
> +/* driver device registration */
> +static struct platform_device_id pata_s3c_driver_ids[] = {
> + {
> + .name = "s3c64xx-pata",
> + .driver_data = TYPE_S3C64XX,
> + }, {
> + .name = "s5pc100-pata",
> + .driver_data = TYPE_S5PC100,
> + }, {
> + .name = "s5pv210-pata",
> + .driver_data = TYPE_S5PV210,
> + },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(platform, pata_s3c_driver_ids);
> +
> +static struct platform_driver pata_s3c_driver = {
> + .remove = __exit_p(pata_s3c_remove),
> + .id_table = pata_s3c_driver_ids,
> + .driver = {
> + .name = DRV_NAME,
> + .owner = THIS_MODULE,
> +#ifdef CONFIG_PM
> + .pm = &pata_s3c_pm_ops,
> +#endif
> + },
> +};
> +
> +static int __init pata_s3c_init(void)
> +{
> + return platform_driver_probe(&pata_s3c_driver, pata_s3c_probe);
> +}
> +
> +static void __exit pata_s3c_exit(void)
> +{
> + platform_driver_unregister(&pata_s3c_driver);
> +}
> +
> +module_init(pata_s3c_init);
> +module_exit(pata_s3c_exit);
> +
> +MODULE_AUTHOR("Abhilash Kesavan, <a.kesavan at samsung.com>");
> +MODULE_DESCRIPTION("low-level driver for Samsung PATA controller");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRV_VERSION);
--
--------------------------------------------------------------
Jürgen Schindele
Software-Entwicklung
PSI NENTEC GmbH
Greschbachstr. 12
76229 Karlsruhe
Deutschland
Telefon: +49 721 94249-51
Telefax: +49 721 94249-10
E-Mail: schindele at nentec.de
WEB: www.nentec.de
Geschäftsführung: Klaus Becker, Roland Knapp
Sitz der Gesellschaft: Karlsruhe
Handelsregister: Amtsgericht Mannheim HRB 107658
Diese E-Mail enthält vertrauliche oder rechtlich geschützte Informationen.
Wenn Sie nicht der vorgesehene Empfänger sind, informieren Sie bitte sofort
den Absender und löschen Sie diese E-Mail. Das unbefugte Kopieren dieser E-Mail
oder die unbefugte Weitergabe der enthaltenen Informationen ist nicht gestattet.
The information contained in this message is confidential or protected by law.
If you are not the intended recipient, please contact the sender and delete this
message. Any unauthorised copying of this message or unauthorised distribution of
the information contained herein is prohibited.
--------------------------------------------------------------
More information about the linux-arm-kernel
mailing list