All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-10-08  6:59 thomas.ab
  2009-10-09  0:59 ` jassi brar
  2009-10-12  8:55 ` Marc Zyngier
  0 siblings, 2 replies; 12+ messages in thread
From: thomas.ab @ 2009-10-08  6:59 UTC (permalink / raw)
  To: linux-samsung-soc; +Cc: Thomas Abraham, Abhilash Kesavan

From: Thomas Abraham <thomas.ab@samsung.com>

This patch adds Samsung S3C IDE controller driver. This IDE controller
currently supports PIO mode. UDMA mode support will be added later.

Note: This patch depends on the following patch set.
[PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE controller driver

Signed-off-by: Abhilash Kesavan <a.kesavan@samsung.com>
Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
---
 drivers/ide/Kconfig    |   14 ++
 drivers/ide/Makefile   |    1 +
 drivers/ide/ide-proc.c |    1 +
 drivers/ide/s3c-ide.c  |  486 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/ide.h    |    2 +-
 5 files changed, 503 insertions(+), 1 deletions(-)
 create mode 100644 drivers/ide/s3c-ide.c

diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 9a5d0aa..1c9580d 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -292,6 +292,20 @@ config BLK_DEV_IDEPNP
 	  would like the kernel to automatically detect and activate
 	  it, say Y here.
 
+config BLK_DEV_IDE_S3C
+        tristate "Samsung S3C IDE Controller"
+	depends on PLAT_S3C64XX
+        help
+          Say Y here if you want to support using CF controller on SMDK(Samsung)
+	  development board. It will be configured for true IDE mode.
+
+config BLK_DEV_IDE_S3C_PIO
+	bool "Use PIO Mode"
+	depends on BLK_DEV_IDE_S3C
+	select IDE_XFER_MODE
+	help
+	  Say Y here if you want to support PIO Mode.
+
 config BLK_DEV_IDEDMA_SFF
 	bool
 
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 81df925..a64a723 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_BLK_DEV_SIIMAGE)		+= siimage.o
 obj-$(CONFIG_BLK_DEV_SIS5513)		+= sis5513.o
 obj-$(CONFIG_BLK_DEV_SL82C105)		+= sl82c105.o
 obj-$(CONFIG_BLK_DEV_SLC90E66)		+= slc90e66.o
+obj-$(CONFIG_BLK_DEV_IDE_S3C)		+= s3c-ide.o
 obj-$(CONFIG_BLK_DEV_TC86C001)		+= tc86c001.o
 obj-$(CONFIG_BLK_DEV_TRIFLEX)		+= triflex.o
 obj-$(CONFIG_BLK_DEV_TRM290)		+= trm290.o
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 3242698..d18478f 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -51,6 +51,7 @@ static int proc_ide_read_imodel
 	case ide_au1xxx:	name = "au1xxx";	break;
 	case ide_palm3710:      name = "palm3710";      break;
 	case ide_acorn:		name = "acorn";		break;
+	case ide_s3c: 		name = "s3c-ide";	break;
 	default:		name = "(unknown)";	break;
 	}
 	len = sprintf(page, "%s\n", name);
diff --git a/drivers/ide/s3c-ide.c b/drivers/ide/s3c-ide.c
new file mode 100644
index 0000000..5ebfe98
--- /dev/null
+++ b/drivers/ide/s3c-ide.c
@@ -0,0 +1,486 @@
+/*
+ * s3c-ide.c - Samsung S3C IDE controller Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ *      http://samsungsemi.com/
+ *
+ * The Samsung S3C IDE controller driver provides low-level support for
+ * interfacing with IDE disks. This supports only PIO mode of operation.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <plat/regs-ide.h>
+#include <plat/ide.h>
+
+/*
+ * This structure represents one instance of the IDE controller driver.
+ */
+struct s3c_ide_device {
+	struct platform_device	*pdev;
+	ide_hwif_t 		*hwif;
+	int 			irq;
+	ulong 			piotime[5];
+	void __iomem            *regbase;
+};
+
+/* Global Declarations */
+static struct s3c_ide_device s3c_ide_dev;
+
+#define ide_writel(value, reg) writel(value, ide_dev->regbase + (reg))
+#define ide_readl(reg) readl(ide_dev->regbase + (reg))
+
+/*
+ * This function waits until the IDE controller is able to perform next
+ * read/write operation to the disk.
+ */
+static void wait_for_host_ready(struct s3c_ide_device *ide_dev)
+{
+	u32 count = 1000000;
+	while (ide_readl(S3C_ATA_FIFO_STATUS) >> 28) {
+		if (!(count--)) {
+			dev_err(&ide_dev->pdev->dev,
+			"ide controller not ready for next taskfile operation");
+			return;
+		}
+	}
+}
+
+/*
+ * This function writes to one of the rask file registers.
+ */
+static void s3c_ide_OUTB(ide_hwif_t *hwif, u8 addr, ulong reg)
+{
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+
+	wait_for_host_ready(ide_dev);
+	__raw_writeb(addr, reg);
+}
+
+/*
+ * This function reads from one of the task file registers.
+ */
+static u8 s3c_ide_INB(ide_hwif_t *hwif, ulong reg)
+{
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+	u8 temp;
+
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(reg);
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	return temp;
+}
+
+/*
+ * The following are ide_tp_ops functions implemented by the IDE
+ * cotnroller driver.
+ */
+static void s3c_ide_exec_command(ide_hwif_t *hwif, u8 cmd)
+{
+	s3c_ide_OUTB(hwif, cmd, hwif->io_ports.command_addr);
+}
+
+static u8 s3c_ide_read_status(ide_hwif_t *hwif)
+{
+	return s3c_ide_INB(hwif, hwif->io_ports.status_addr);
+}
+
+static u8 s3c_ide_read_altstatus(ide_hwif_t *hwif)
+{
+	return s3c_ide_INB(hwif, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+	s3c_ide_OUTB(hwif, ctl, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_dev_select(ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	u8 select = drive->select | ATA_DEVICE_OBS;
+	s3c_ide_OUTB(hwif, select, hwif->io_ports.device_addr);
+}
+
+static void s3c_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(data_addr);
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	}
+}
+
+static void s3c_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		writel(*temp_addr, data_addr);
+	}
+}
+
+static void s3c_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)
+		s3c_ide_OUTB(hwif, tf->feature, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		s3c_ide_OUTB(hwif, tf->nsect, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		s3c_ide_OUTB(hwif, tf->lbal, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		s3c_ide_OUTB(hwif, tf->lbam, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		s3c_ide_OUTB(hwif, tf->lbah, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		s3c_ide_OUTB(hwif, tf->device, io_ports->device_addr);
+}
+
+void s3c_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  = s3c_ide_INB(hwif, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		tf->nsect  = s3c_ide_INB(hwif, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		tf->lbal   = s3c_ide_INB(hwif, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		tf->lbam   = s3c_ide_INB(hwif, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		tf->lbah   = s3c_ide_INB(hwif, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		tf->device = s3c_ide_INB(hwif, io_ports->device_addr);
+}
+
+static void set_ata_enable(struct s3c_ide_device *ide_dev, u8 state)
+{
+	u32 temp = ide_readl(S3C_ATA_CTRL);
+	temp = state ? temp | 1 : temp & ~1;
+	ide_writel(temp , S3C_ATA_CTRL);
+}
+
+static void set_endian_mode(struct s3c_ide_device *ide_dev, u8 mode)
+{
+	u32 reg = ide_readl(S3C_ATA_CFG);
+	reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
+	ide_writel(reg, S3C_ATA_CFG);
+}
+
+/*
+ * This function selects the maximum possible transfer speed.
+ */
+static u8 s3c_ide_ratefilter(u8 speed)
+{
+	return min(speed, (u8)XFER_PIO_4);
+}
+
+/*
+ * This function selects the best possible transfer speed.
+ */
+static void s3c_ide_tune_chipset(ide_drive_t *drive, u8 xferspeed)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)drive->hwif;
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+	u8 speed = s3c_ide_ratefilter(xferspeed);
+
+	/* IORDY is enabled for modes > PIO2 */
+	if (XFER_PIO_0 >= speed && speed <= XFER_PIO_4) {
+		ulong ata_cfg = ide_readl(S3C_ATA_CFG);
+
+		switch (speed) {
+		case XFER_PIO_0:
+		case XFER_PIO_1:
+		case XFER_PIO_2:
+			ata_cfg &= (~S3C_ATA_CFG_IORDYEN);
+			break;
+		case XFER_PIO_3:
+		case XFER_PIO_4:
+			ata_cfg |= S3C_ATA_CFG_IORDYEN;
+			break;
+		}
+
+		ide_writel(ata_cfg, S3C_ATA_CFG);
+		ide_writel(ide_dev->piotime[speed - XFER_PIO_0],
+				S3C_ATA_PIO_TIME);
+	}
+	ide_config_drive_speed(drive, speed);
+}
+
+static void s3c_ide_tune_drive(ide_drive_t *drive, u8 pio)
+{
+	pio = ide_get_best_pio_mode(drive, 255, pio);
+	(void)s3c_ide_tune_chipset(drive, (XFER_PIO_0 + pio));
+}
+
+irqreturn_t s3c_irq_handler(int irq, void *dev_id)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)dev_id;
+	struct s3c_ide_device *ide_dev =
+			(struct s3c_ide_device *)hwif->hwif_data;
+	u32 reg = ide_readl(S3C_ATA_IRQ);
+
+	ide_writel(reg, S3C_ATA_IRQ);
+	return ide_intr(irq, dev_id);
+}
+
+static void s3c_ide_setup_timing_value(struct s3c_ide_device *ide_dev,
+						u32 clk_rate)
+{
+	uint t1, t2, teoc, i;
+	uint pio_t1[5] = { 70, 50, 30, 30, 30 };
+	uint pio_t2[5] = { 290, 290, 290, 80, 70 };
+	uint pio_teoc[5] = { 20, 20, 10, 10, 10 };
+	ulong cycle_time = (uint) (1000000000 / clk_rate);
+
+	for (i = 0; i < 5; i++) {
+		t1 = (pio_t1[i] / cycle_time) & 0x0f;
+		t2 = (pio_t2[i] / cycle_time) & 0xff;
+		teoc = (pio_teoc[i] / cycle_time) & 0xff;
+		ide_dev->piotime[i] = (teoc << 12) | (t2 << 4) | t1;
+	}
+}
+
+static void change_mode_to_ata(struct s3c_ide_device *ide_dev)
+{
+	ide_writel(ide_readl(S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,
+			S3C_CFATA_MUX);
+}
+
+static void init_ide_device(struct s3c_ide_device *ide_dev)
+{
+	change_mode_to_ata(ide_dev);
+	set_endian_mode(ide_dev, 1);
+	set_ata_enable(ide_dev, 1);
+}
+
+static void s3c_ide_setup_ports(struct ide_hw *hw,
+				struct s3c_ide_device *ide_dev)
+{
+	int i;
+	unsigned long *ata_regs = hw->io_ports_array;
+
+	/* S3C IDE controller does not include irq_addr port */
+	for (i = 0; i < IDE_NR_PORTS-1; i++)
+		*ata_regs++ = (ulong)ide_dev->regbase +
+				S3C_ATA_PIO_DTR + (i << 2);
+}
+
+static u8 s3c_cable_detect(ide_hwif_t *hwif)
+{
+	return ATA_CBL_PATA40;
+}
+
+static const struct ide_port_ops s3c_port_ops = {
+	.set_pio_mode = s3c_ide_tune_drive,
+	.set_dma_mode = s3c_ide_tune_chipset,
+	.cable_detect = s3c_cable_detect,
+};
+
+static const struct ide_tp_ops s3c_ide_tp_ops = {
+	.exec_command   = s3c_ide_exec_command,
+	.read_status    = s3c_ide_read_status,
+	.read_altstatus = s3c_ide_read_altstatus,
+	.write_devctl   = s3c_ide_write_devctl,
+	.dev_select     = s3c_ide_dev_select,
+	.tf_load        = s3c_ide_tf_load,
+	.tf_read        = s3c_ide_tf_read,
+	.input_data     = s3c_ide_input_data,
+	.output_data    = s3c_ide_output_data,
+};
+
+
+static const struct ide_port_info s3c_port_info = {
+	.name		= "s3c-ide",
+	.port_ops	= &s3c_port_ops,
+	.tp_ops         = &s3c_ide_tp_ops,
+	.chipset	= ide_s3c,
+	.host_flags	= IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
+				IDE_HFLAG_UNMASK_IRQS,
+	.pio_mask	= ATA_PIO4,
+};
+
+static int __devinit s3c_ide_probe(struct platform_device *pdev)
+{
+	struct resource	*res;
+	struct clk *ide_clk;
+	struct s3c_ide_device *ide_dev = &s3c_ide_dev;
+	struct s3c_ide_platdata *pdata;
+	struct ide_host *host;
+	int ret = 0;
+	struct ide_hw hw, *hws[] = { &hw };
+
+	memset(&s3c_ide_dev, 0, sizeof(s3c_ide_dev));
+	ide_dev->pdev = pdev;
+	pdata = pdev->dev.platform_data;
+	ide_dev->irq = platform_get_irq(pdev, 0);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev, "could not obtain base address \
+					of controller\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (ide_dev->irq < 0) {
+		dev_err(&pdev->dev, "could not obtain irq number\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+		pdev->name)) {
+		dev_err(&pdev->dev, "could not obtain i/o address\n");
+		ret = -EBUSY;
+		goto out;
+	}
+
+	ide_dev->regbase = ioremap(res->start, res->end - res->start + 1);
+	if (ide_dev->regbase == 0) {
+		dev_err(&pdev->dev, "could not remap i/o address\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ide_clk = clk_get(&pdev->dev, "cfcon");
+	if (IS_ERR(ide_clk)) {
+		dev_err(&pdev->dev, "failed to find clock source\n");
+		ret = PTR_ERR(ide_clk);
+		ide_clk = NULL;
+		goto out;
+	}
+
+	if (clk_enable(ide_clk)) {
+		dev_err(&pdev->dev, "failed to enable clock source.\n");
+		goto out;
+	}
+
+	s3c_ide_setup_timing_value(ide_dev, clk_get_rate(ide_clk));
+	if (pdata->setup_gpio)
+		pdata->setup_gpio();
+	init_ide_device(ide_dev);
+
+	ide_writel(0x1f, S3C_ATA_IRQ);
+	ide_writel(0x1b, S3C_ATA_IRQ_MSK);
+
+	memset(&hw, 0, sizeof(hw));
+	s3c_ide_setup_ports(&hw, ide_dev);
+	hw.irq = ide_dev->irq;
+	hw.dev = &pdev->dev;
+
+	host = ide_host_alloc(&s3c_port_info, hws, 1);
+	if (!host) {
+		dev_err(&pdev->dev, "failed to allocate ide host\n");
+		goto out;
+	}
+
+	host->irq_handler = s3c_irq_handler;
+	host->ports[0]->hwif_data = (void *)ide_dev;
+
+	ret = ide_host_register(host, &s3c_port_info, hws);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ide host\n");
+		ide_host_free(host);
+		goto out;
+	}
+
+	s3c_ide_dev.hwif = host->ports[0];
+	platform_set_drvdata(pdev, host);
+	printk(KERN_INFO "S3C IDE driver configured for PIO_ONLY mode\n");
+
+out:
+	return ret;
+}
+
+static int __devexit s3c_ide_remove(struct platform_device *pdev)
+{
+	struct ide_host *host = platform_get_drvdata(pdev);
+	struct resource *res;
+	struct s3c_ide_device *ide_dev = host->ports[0]->hwif_data;
+
+	ide_host_remove(host);
+	iounmap(ide_dev->regbase);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_resource(res);
+	kfree(res);
+
+	return 0;
+}
+
+static struct platform_driver s3c_ide_driver = {
+	.probe		= s3c_ide_probe,
+	.remove		= __devexit_p(s3c_ide_remove),
+	.driver = {
+		.name	= "s3c-ide",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init s3c_ide_init(void)
+{
+	return platform_driver_register(&s3c_ide_driver);
+}
+
+static void __exit s3c_ide_exit(void)
+{
+	platform_driver_unregister(&s3c_ide_driver);
+}
+
+module_init(s3c_ide_init);
+module_exit(s3c_ide_exit);
+
+MODULE_DESCRIPTION("Samsung S3C IDE Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-cfcon");
diff --git a/include/linux/ide.h b/include/linux/ide.h
index edc93a6..ff9eb0e 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -164,7 +164,7 @@ enum {		ide_unknown,	ide_generic,	ide_pci,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd65xx,	ide_umc8672,	ide_ht6560b,
 		ide_4drives,	ide_pmac,	ide_acorn,
-		ide_au1xxx,	ide_palm3710
+		ide_au1xxx,	ide_palm3710,	ide_s3c
 };
 
 typedef u8 hwif_chipset_t;
-- 
1.5.3.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-10-08  6:59 [PATCH] S3C: ide: Add Samsung S3C IDE controller driver thomas.ab
@ 2009-10-09  0:59 ` jassi brar
  2009-10-12  8:41   ` Thomas Abraham
  2009-10-12  8:55 ` Marc Zyngier
  1 sibling, 1 reply; 12+ messages in thread
From: jassi brar @ 2009-10-09  0:59 UTC (permalink / raw)
  To: thomas.ab; +Cc: linux-samsung-soc, Abhilash Kesavan

On Thu, Oct 8, 2009 at 3:59 PM,  <thomas.ab@samsung.com> wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> diff --git a/drivers/ide/s3c-ide.c b/drivers/ide/s3c-ide.c
> new file mode 100644
> index 0000000..5ebfe98
> --- /dev/null
> +++ b/drivers/ide/s3c-ide.c
> @@ -0,0 +1,486 @@
> +/*
> + * s3c-ide.c - Samsung S3C IDE controller Driver
> + *
> + * Copyright (C) 2009 Samsung Electronics
> + *      http://samsungsemi.com/
> + *
> + * The Samsung S3C IDE controller driver provides low-level support for
> + * interfacing with IDE disks. This supports only PIO mode of operation.
> + *
> + * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
> + */
> +
> +#include <linux/types.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/init.h>
> +#include <linux/ide.h>
> +#include <linux/sysdev.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <plat/regs-ide.h>
> +#include <plat/ide.h>
> +
> +/*
> + * This structure represents one instance of the IDE controller driver.
> + */
> +struct s3c_ide_device {
> +       struct platform_device  *pdev;
> +       ide_hwif_t              *hwif;
> +       int                     irq;
> +       ulong                   piotime[5];
> +       void __iomem            *regbase;
> +};
> +
> +/* Global Declarations */
> +static struct s3c_ide_device s3c_ide_dev;
> +
> +#define ide_writel(value, reg) writel(value, ide_dev->regbase + (reg))
> +#define ide_readl(reg) readl(ide_dev->regbase + (reg))
> +
> +/*
> + * This function waits until the IDE controller is able to perform next
> + * read/write operation to the disk.
> + */
> +static void wait_for_host_ready(struct s3c_ide_device *ide_dev)
> +{
> +       u32 count = 1000000;
> +       while (ide_readl(S3C_ATA_FIFO_STATUS) >> 28) {
> +               if (!(count--)) {
> +                       dev_err(&ide_dev->pdev->dev,
> +                       "ide controller not ready for next taskfile operation");
> +                       return;
> +               }
> +       }
> +}

 I think instead of using a magic number for loop counter, it will be
better to use some timed wait using a counter as in
http://www.spinics.net/lists/arm-kernel/msg73904.html


> +
> +/*
> + * This function writes to one of the rask file registers.
> + */
> +static void s3c_ide_OUTB(ide_hwif_t *hwif, u8 addr, ulong reg)
> +{
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +
> +       wait_for_host_ready(ide_dev);
> +       __raw_writeb(addr, reg);
> +}
> +
> +/*
> + * This function reads from one of the task file registers.
> + */
> +static u8 s3c_ide_INB(ide_hwif_t *hwif, ulong reg)
> +{
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +       u8 temp;
> +
> +       wait_for_host_ready(ide_dev);
> +       temp = __raw_readb(reg);
> +       wait_for_host_ready(ide_dev);
> +       temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
> +       return temp;
> +}

any namespace reason for the caps  _OUTB and _INB ?


> +
> +/*
> + * The following are ide_tp_ops functions implemented by the IDE
> + * cotnroller driver.
> + */
> +static void s3c_ide_exec_command(ide_hwif_t *hwif, u8 cmd)
> +{
> +       s3c_ide_OUTB(hwif, cmd, hwif->io_ports.command_addr);
> +}
> +
> +static u8 s3c_ide_read_status(ide_hwif_t *hwif)
> +{
> +       return s3c_ide_INB(hwif, hwif->io_ports.status_addr);
> +}
> +
> +static u8 s3c_ide_read_altstatus(ide_hwif_t *hwif)
> +{
> +       return s3c_ide_INB(hwif, hwif->io_ports.ctl_addr);
> +}
> +
> +static void s3c_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
> +{
> +       s3c_ide_OUTB(hwif, ctl, hwif->io_ports.ctl_addr);
> +}
> +
> +static void s3c_ide_dev_select(ide_drive_t *drive)
> +{
> +       ide_hwif_t *hwif = drive->hwif;
> +       u8 select = drive->select | ATA_DEVICE_OBS;
> +       s3c_ide_OUTB(hwif, select, hwif->io_ports.device_addr);
> +}
> +
> +static void s3c_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
> +                                       void *buf, unsigned int len)
> +{
> +       ide_hwif_t *hwif = drive->hwif;
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +       struct ide_io_ports *io_ports = &hwif->io_ports;
> +       unsigned long data_addr = io_ports->data_addr;
> +       unsigned int words = (len + 1) >> 1, i;
> +       u16 *temp_addr = (u16 *)buf;
> +
> +       for (i = 0; i < words; i++, temp_addr++) {
> +               wait_for_host_ready(ide_dev);
> +               *temp_addr = __raw_readw(data_addr);
> +               wait_for_host_ready(ide_dev);
> +               *temp_addr = __raw_readw(ide_dev->regbase + S3C_ATA_PIO_RDATA);
> +       }
> +}
> +
> +static void s3c_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
> +                                       void *buf, unsigned int len)
> +{
> +       ide_hwif_t *hwif = drive->hwif;
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +       struct ide_io_ports *io_ports = &hwif->io_ports;
> +       unsigned long data_addr = io_ports->data_addr;
> +       unsigned int words = (len + 1) >> 1, i;
> +       u16 *temp_addr = (u16 *)buf;
> +
> +       for (i = 0; i < words; i++, temp_addr++) {
> +               wait_for_host_ready(ide_dev);
> +               writel(*temp_addr, data_addr);
> +       }
> +}
> +
> +static void s3c_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)
> +               s3c_ide_OUTB(hwif, tf->feature, io_ports->feature_addr);
> +       if (valid & IDE_VALID_NSECT)
> +               s3c_ide_OUTB(hwif, tf->nsect, io_ports->nsect_addr);
> +       if (valid & IDE_VALID_LBAL)
> +               s3c_ide_OUTB(hwif, tf->lbal, io_ports->lbal_addr);
> +       if (valid & IDE_VALID_LBAM)
> +               s3c_ide_OUTB(hwif, tf->lbam, io_ports->lbam_addr);
> +       if (valid & IDE_VALID_LBAH)
> +               s3c_ide_OUTB(hwif, tf->lbah, io_ports->lbah_addr);
> +       if (valid & IDE_VALID_DEVICE)
> +               s3c_ide_OUTB(hwif, tf->device, io_ports->device_addr);
> +}
> +
> +void s3c_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  = s3c_ide_INB(hwif, io_ports->feature_addr);
> +       if (valid & IDE_VALID_NSECT)
> +               tf->nsect  = s3c_ide_INB(hwif, io_ports->nsect_addr);
> +       if (valid & IDE_VALID_LBAL)
> +               tf->lbal   = s3c_ide_INB(hwif, io_ports->lbal_addr);
> +       if (valid & IDE_VALID_LBAM)
> +               tf->lbam   = s3c_ide_INB(hwif, io_ports->lbam_addr);
> +       if (valid & IDE_VALID_LBAH)
> +               tf->lbah   = s3c_ide_INB(hwif, io_ports->lbah_addr);
> +       if (valid & IDE_VALID_DEVICE)
> +               tf->device = s3c_ide_INB(hwif, io_ports->device_addr);
> +}
> +
> +static void set_ata_enable(struct s3c_ide_device *ide_dev, u8 state)
> +{
> +       u32 temp = ide_readl(S3C_ATA_CTRL);
> +       temp = state ? temp | 1 : temp & ~1;
> +       ide_writel(temp , S3C_ATA_CTRL);
> +}
> +
> +static void set_endian_mode(struct s3c_ide_device *ide_dev, u8 mode)
> +{
> +       u32 reg = ide_readl(S3C_ATA_CFG);
> +       reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
> +       ide_writel(reg, S3C_ATA_CFG);
> +}
> +
> +/*
> + * This function selects the maximum possible transfer speed.
> + */
> +static u8 s3c_ide_ratefilter(u8 speed)
> +{
> +       return min(speed, (u8)XFER_PIO_4);
> +}
> +
> +/*
> + * This function selects the best possible transfer speed.
> + */
> +static void s3c_ide_tune_chipset(ide_drive_t *drive, u8 xferspeed)
> +{
> +       ide_hwif_t *hwif = (ide_hwif_t *)drive->hwif;
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +       u8 speed = s3c_ide_ratefilter(xferspeed);
> +
> +       /* IORDY is enabled for modes > PIO2 */
> +       if (XFER_PIO_0 >= speed && speed <= XFER_PIO_4) {
> +               ulong ata_cfg = ide_readl(S3C_ATA_CFG);
> +
> +               switch (speed) {
> +               case XFER_PIO_0:
> +               case XFER_PIO_1:
> +               case XFER_PIO_2:
> +                       ata_cfg &= (~S3C_ATA_CFG_IORDYEN);
> +                       break;
> +               case XFER_PIO_3:
> +               case XFER_PIO_4:
> +                       ata_cfg |= S3C_ATA_CFG_IORDYEN;
> +                       break;
> +               }
> +
> +               ide_writel(ata_cfg, S3C_ATA_CFG);
> +               ide_writel(ide_dev->piotime[speed - XFER_PIO_0],
> +                               S3C_ATA_PIO_TIME);
> +       }
> +       ide_config_drive_speed(drive, speed);
> +}
> +
> +static void s3c_ide_tune_drive(ide_drive_t *drive, u8 pio)
> +{
> +       pio = ide_get_best_pio_mode(drive, 255, pio);
> +       (void)s3c_ide_tune_chipset(drive, (XFER_PIO_0 + pio));
> +}
> +
> +irqreturn_t s3c_irq_handler(int irq, void *dev_id)
> +{
> +       ide_hwif_t *hwif = (ide_hwif_t *)dev_id;
> +       struct s3c_ide_device *ide_dev =
> +                       (struct s3c_ide_device *)hwif->hwif_data;
> +       u32 reg = ide_readl(S3C_ATA_IRQ);
> +
> +       ide_writel(reg, S3C_ATA_IRQ);
> +       return ide_intr(irq, dev_id);
> +}
> +
> +static void s3c_ide_setup_timing_value(struct s3c_ide_device *ide_dev,
> +                                               u32 clk_rate)
> +{
> +       uint t1, t2, teoc, i;
> +       uint pio_t1[5] = { 70, 50, 30, 30, 30 };
> +       uint pio_t2[5] = { 290, 290, 290, 80, 70 };
> +       uint pio_teoc[5] = { 20, 20, 10, 10, 10 };
> +       ulong cycle_time = (uint) (1000000000 / clk_rate);
> +
> +       for (i = 0; i < 5; i++) {
> +               t1 = (pio_t1[i] / cycle_time) & 0x0f;
> +               t2 = (pio_t2[i] / cycle_time) & 0xff;
> +               teoc = (pio_teoc[i] / cycle_time) & 0xff;
> +               ide_dev->piotime[i] = (teoc << 12) | (t2 << 4) | t1;
> +       }
> +}
> +
> +static void change_mode_to_ata(struct s3c_ide_device *ide_dev)
> +{
> +       ide_writel(ide_readl(S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,
> +                       S3C_CFATA_MUX);
> +}
> +
> +static void init_ide_device(struct s3c_ide_device *ide_dev)
> +{
> +       change_mode_to_ata(ide_dev);
> +       set_endian_mode(ide_dev, 1);
> +       set_ata_enable(ide_dev, 1);
> +}
> +
> +static void s3c_ide_setup_ports(struct ide_hw *hw,
> +                               struct s3c_ide_device *ide_dev)
> +{
> +       int i;
> +       unsigned long *ata_regs = hw->io_ports_array;
> +
> +       /* S3C IDE controller does not include irq_addr port */
> +       for (i = 0; i < IDE_NR_PORTS-1; i++)
> +               *ata_regs++ = (ulong)ide_dev->regbase +
> +                               S3C_ATA_PIO_DTR + (i << 2);
> +}
> +
> +static u8 s3c_cable_detect(ide_hwif_t *hwif)
> +{
> +       return ATA_CBL_PATA40;
> +}
> +
> +static const struct ide_port_ops s3c_port_ops = {
> +       .set_pio_mode = s3c_ide_tune_drive,
> +       .set_dma_mode = s3c_ide_tune_chipset,
> +       .cable_detect = s3c_cable_detect,
> +};
> +
> +static const struct ide_tp_ops s3c_ide_tp_ops = {
> +       .exec_command   = s3c_ide_exec_command,
> +       .read_status    = s3c_ide_read_status,
> +       .read_altstatus = s3c_ide_read_altstatus,
> +       .write_devctl   = s3c_ide_write_devctl,
> +       .dev_select     = s3c_ide_dev_select,
> +       .tf_load        = s3c_ide_tf_load,
> +       .tf_read        = s3c_ide_tf_read,
> +       .input_data     = s3c_ide_input_data,
> +       .output_data    = s3c_ide_output_data,
> +};
> +
> +
> +static const struct ide_port_info s3c_port_info = {
> +       .name           = "s3c-ide",
> +       .port_ops       = &s3c_port_ops,
> +       .tp_ops         = &s3c_ide_tp_ops,
> +       .chipset        = ide_s3c,
> +       .host_flags     = IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
> +                               IDE_HFLAG_UNMASK_IRQS,
> +       .pio_mask       = ATA_PIO4,
> +};
> +
> +static int __devinit s3c_ide_probe(struct platform_device *pdev)
> +{
> +       struct resource *res;
> +       struct clk *ide_clk;
> +       struct s3c_ide_device *ide_dev = &s3c_ide_dev;
> +       struct s3c_ide_platdata *pdata;
> +       struct ide_host *host;
> +       int ret = 0;
> +       struct ide_hw hw, *hws[] = { &hw };
> +
> +       memset(&s3c_ide_dev, 0, sizeof(s3c_ide_dev));
> +       ide_dev->pdev = pdev;
> +       pdata = pdev->dev.platform_data;
> +       ide_dev->irq = platform_get_irq(pdev, 0);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (res == NULL) {
> +               dev_err(&pdev->dev, "could not obtain base address \
> +                                       of controller\n");
> +               ret = -ENODEV;
> +               goto out;
> +       }
> +
> +       if (ide_dev->irq < 0) {
> +               dev_err(&pdev->dev, "could not obtain irq number\n");
> +               ret = -ENODEV;
> +               goto out;
> +       }
             check for valid irq shud be done immidiately after trying to get it
and before acquiring any other resource


> +       if (!request_mem_region(res->start, res->end - res->start + 1,
> +               pdev->name)) {
> +               dev_err(&pdev->dev, "could not obtain i/o address\n");
> +               ret = -EBUSY;
> +               goto out;
> +       }
> +
> +       ide_dev->regbase = ioremap(res->start, res->end - res->start + 1);
> +       if (ide_dev->regbase == 0) {
> +               dev_err(&pdev->dev, "could not remap i/o address\n");
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       ide_clk = clk_get(&pdev->dev, "cfcon");
> +       if (IS_ERR(ide_clk)) {
> +               dev_err(&pdev->dev, "failed to find clock source\n");
> +               ret = PTR_ERR(ide_clk);
> +               ide_clk = NULL;
> +               goto out;
> +       }
> +
> +       if (clk_enable(ide_clk)) {
> +               dev_err(&pdev->dev, "failed to enable clock source.\n");
> +               goto out;
> +       }
> +
> +       s3c_ide_setup_timing_value(ide_dev, clk_get_rate(ide_clk));
> +       if (pdata->setup_gpio)
> +               pdata->setup_gpio();
> +       init_ide_device(ide_dev);
> +
> +       ide_writel(0x1f, S3C_ATA_IRQ);
> +       ide_writel(0x1b, S3C_ATA_IRQ_MSK);
> +
> +       memset(&hw, 0, sizeof(hw));
> +       s3c_ide_setup_ports(&hw, ide_dev);
> +       hw.irq = ide_dev->irq;
> +       hw.dev = &pdev->dev;
> +
> +       host = ide_host_alloc(&s3c_port_info, hws, 1);
> +       if (!host) {
> +               dev_err(&pdev->dev, "failed to allocate ide host\n");
> +               goto out;
> +       }
> +
> +       host->irq_handler = s3c_irq_handler;
> +       host->ports[0]->hwif_data = (void *)ide_dev;
> +
> +       ret = ide_host_register(host, &s3c_port_info, hws);
> +       if (ret) {
> +               dev_err(&pdev->dev, "failed to register ide host\n");
> +               ide_host_free(host);
> +               goto out;
> +       }
> +
> +       s3c_ide_dev.hwif = host->ports[0];
> +       platform_set_drvdata(pdev, host);
> +       printk(KERN_INFO "S3C IDE driver configured for PIO_ONLY mode\n");
> +
> +out:
> +       return ret;
> +}
Since you call 'goto out' upon some call failure, the label 'out' shud
 undo all the successful operations before hitting the failure.
It shud rather be out1, out2 etc jumped at from various locations during
initialization.


> +static int __devexit s3c_ide_remove(struct platform_device *pdev)
> +{
> +       struct ide_host *host = platform_get_drvdata(pdev);
> +       struct resource *res;
> +       struct s3c_ide_device *ide_dev = host->ports[0]->hwif_data;
> +
> +       ide_host_remove(host);
> +       iounmap(ide_dev->regbase);
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       release_resource(res);

          It had rather be release_mem_region ?

> +       kfree(res);
> +
> +       return 0;
> +}
> +
> +static struct platform_driver s3c_ide_driver = {
> +       .probe          = s3c_ide_probe,
> +       .remove         = __devexit_p(s3c_ide_remove),
> +       .driver = {
> +               .name   = "s3c-ide",
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
> +static int __init s3c_ide_init(void)
> +{
> +       return platform_driver_register(&s3c_ide_driver);
> +}
> +
> +static void __exit s3c_ide_exit(void)
> +{
> +       platform_driver_unregister(&s3c_ide_driver);
> +}
> +
> +module_init(s3c_ide_init);
> +module_exit(s3c_ide_exit);
> +
> +MODULE_DESCRIPTION("Samsung S3C IDE Controller Driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:s3c-cfcon");
> diff --git a/include/linux/ide.h b/include/linux/ide.h
> index edc93a6..ff9eb0e 100644
> --- a/include/linux/ide.h
> +++ b/include/linux/ide.h
> @@ -164,7 +164,7 @@ enum {              ide_unknown,    ide_generic,    ide_pci,
>                ide_cmd640,     ide_dtc2278,    ide_ali14xx,
>                ide_qd65xx,     ide_umc8672,    ide_ht6560b,
>                ide_4drives,    ide_pmac,       ide_acorn,
> -               ide_au1xxx,     ide_palm3710
> +               ide_au1xxx,     ide_palm3710,   ide_s3c
>  };
>
>  typedef u8 hwif_chipset_t;
> --
> 1.5.3.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-10-09  0:59 ` jassi brar
@ 2009-10-12  8:41   ` Thomas Abraham
  0 siblings, 0 replies; 12+ messages in thread
From: Thomas Abraham @ 2009-10-12  8:41 UTC (permalink / raw)
  To: jassi brar; +Cc: linux-samsung-soc, Abhilash Kesavan

On Fri, Oct 9, 2009 at 9:59 AM, jassi brar <jassisinghbrar@gmail.com> wrote:

>> +static void wait_for_host_ready(struct s3c_ide_device *ide_dev)
>> +{
>> +       u32 count = 1000000;
>> +       while (ide_readl(S3C_ATA_FIFO_STATUS) >> 28) {
>> +               if (!(count--)) {
>> +                       dev_err(&ide_dev->pdev->dev,
>> +                       "ide controller not ready for next taskfile operation");
>> +                       return;
>> +               }
>> +       }
>> +}
>
>  I think instead of using a magic number for loop counter, it will be
> better to use some timed wait using a counter as in
> http://www.spinics.net/lists/arm-kernel/msg73904.html

I will change this to timed wait as in your patch.

>> +static u8 s3c_ide_INB(ide_hwif_t *hwif, ulong reg)
>> +{
>> +       struct s3c_ide_device *ide_dev =
>> +                       (struct s3c_ide_device *)hwif->hwif_data;
>> +       u8 temp;
>> +
>> +       wait_for_host_ready(ide_dev);
>> +       temp = __raw_readb(reg);
>> +       wait_for_host_ready(ide_dev);
>> +       temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
>> +       return temp;
>> +}
>
> any namespace reason for the caps  _OUTB and _INB ?
>

I missed that. There was no namespace issue. I will change this to lowercase.

>> +       if (ide_dev->irq < 0) {
>> +               dev_err(&pdev->dev, "could not obtain irq number\n");
>> +               ret = -ENODEV;
>> +               goto out;
>> +       }
>             check for valid irq shud be done immidiately after trying to get it
> and before acquiring any other resource

Ok. I will fix this.

>> +out:
>> +       return ret;
>> +}
> Since you call 'goto out' upon some call failure, the label 'out' shud
>  undo all the successful operations before hitting the failure.
> It shud rather be out1, out2 etc jumped at from various locations during
> initialization.

I missed this. I will add the undo code here.

Thanks for reviewing the patch. Your comments were very helpful.

Thanks,
Thomas.
--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-10-08  6:59 [PATCH] S3C: ide: Add Samsung S3C IDE controller driver thomas.ab
  2009-10-09  0:59 ` jassi brar
@ 2009-10-12  8:55 ` Marc Zyngier
  1 sibling, 0 replies; 12+ messages in thread
From: Marc Zyngier @ 2009-10-12  8:55 UTC (permalink / raw)
  To: thomas.ab; +Cc: linux-samsung-soc, Abhilash Kesavan

On Thu,  8 Oct 2009 15:59:28 +0900
thomas.ab@samsung.com wrote:

> +irqreturn_t s3c_irq_handler(int irq, void *dev_id)
> +{
> +	ide_hwif_t *hwif = (ide_hwif_t *)dev_id;
> +	struct s3c_ide_device *ide_dev =
> +			(struct s3c_ide_device *)hwif->hwif_data;
> +	u32 reg = ide_readl(S3C_ATA_IRQ);
> +
> +	ide_writel(reg, S3C_ATA_IRQ);
> +	return ide_intr(irq, dev_id);
> +}

On top of what Jassi already mentioned, please make this static.

	M.
-- 
Fast. Cheap. Reliable. Pick two.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-11-01  5:07 ` Thomas Abraham
@ 2009-11-01 12:34   ` Alan Cox
  -1 siblings, 0 replies; 12+ messages in thread
From: Alan Cox @ 2009-11-01 12:34 UTC (permalink / raw)
  Cc: linux-ide, ben-linux, linux-arm-kernel, Thomas Abraham

On Sun,  1 Nov 2009 14:07:09 +0900
Thomas Abraham <thomas.ab@samsung.com> wrote:

> This patch adds Samsung S3C IDE controller driver. This driver supports
> PIO and UDMA modes of data transfer.

drivers/ide is old code in maintenance mode only. New drivers should be
using libata.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-11-01 12:34   ` Alan Cox
  0 siblings, 0 replies; 12+ messages in thread
From: Alan Cox @ 2009-11-01 12:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun,  1 Nov 2009 14:07:09 +0900
Thomas Abraham <thomas.ab@samsung.com> wrote:

> This patch adds Samsung S3C IDE controller driver. This driver supports
> PIO and UDMA modes of data transfer.

drivers/ide is old code in maintenance mode only. New drivers should be
using libata.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-11-01  6:53   ` Jeff Garzik
@ 2009-11-01 10:03     ` David Miller
  -1 siblings, 0 replies; 12+ messages in thread
From: David Miller @ 2009-11-01 10:03 UTC (permalink / raw)
  To: jeff; +Cc: thomas.ab, linux-ide, ben-linux, linux-arm-kernel

From: Jeff Garzik <jeff@garzik.org>
Date: Sun, 01 Nov 2009 01:53:25 -0500

> On 11/01/2009 01:07 AM, Thomas Abraham wrote:
>> This patch adds Samsung S3C IDE controller driver. This driver
>> supports
>> PIO and UDMA modes of data transfer.
>>
>> Note: This patch depends the following patch series.
>> [PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE
>> controller driver
>>
>> Signed-off-by: Abhilash Kesavan<a.kesavan<at>  samsung.com>
>> Signed-off-by: Thomas Abraham<thomas.ab<at>  samsung.com>
> 
> A libata driver would be nice, since we are trying to move away from
> old-IDE.

Yes, I refuse to add new IDE layer drivers to the tree.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-11-01 10:03     ` David Miller
  0 siblings, 0 replies; 12+ messages in thread
From: David Miller @ 2009-11-01 10:03 UTC (permalink / raw)
  To: linux-arm-kernel

From: Jeff Garzik <jeff@garzik.org>
Date: Sun, 01 Nov 2009 01:53:25 -0500

> On 11/01/2009 01:07 AM, Thomas Abraham wrote:
>> This patch adds Samsung S3C IDE controller driver. This driver
>> supports
>> PIO and UDMA modes of data transfer.
>>
>> Note: This patch depends the following patch series.
>> [PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE
>> controller driver
>>
>> Signed-off-by: Abhilash Kesavan<a.kesavan<at>  samsung.com>
>> Signed-off-by: Thomas Abraham<thomas.ab<at>  samsung.com>
> 
> A libata driver would be nice, since we are trying to move away from
> old-IDE.

Yes, I refuse to add new IDE layer drivers to the tree.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
  2009-11-01  5:07 ` Thomas Abraham
@ 2009-11-01  6:53   ` Jeff Garzik
  -1 siblings, 0 replies; 12+ messages in thread
From: Jeff Garzik @ 2009-11-01  6:53 UTC (permalink / raw)
  To: Thomas Abraham; +Cc: linux-ide, ben-linux, linux-arm-kernel

On 11/01/2009 01:07 AM, Thomas Abraham wrote:
> This patch adds Samsung S3C IDE controller driver. This driver supports
> PIO and UDMA modes of data transfer.
>
> Note: This patch depends the following patch series.
> [PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE controller driver
>
> Signed-off-by: Abhilash Kesavan<a.kesavan<at>  samsung.com>
> Signed-off-by: Thomas Abraham<thomas.ab<at>  samsung.com>

A libata driver would be nice, since we are trying to move away from 
old-IDE.

	Jeff





^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-11-01  6:53   ` Jeff Garzik
  0 siblings, 0 replies; 12+ messages in thread
From: Jeff Garzik @ 2009-11-01  6:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/01/2009 01:07 AM, Thomas Abraham wrote:
> This patch adds Samsung S3C IDE controller driver. This driver supports
> PIO and UDMA modes of data transfer.
>
> Note: This patch depends the following patch series.
> [PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE controller driver
>
> Signed-off-by: Abhilash Kesavan<a.kesavan<at>  samsung.com>
> Signed-off-by: Thomas Abraham<thomas.ab<at>  samsung.com>

A libata driver would be nice, since we are trying to move away from 
old-IDE.

	Jeff

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-11-01  5:07 ` Thomas Abraham
  0 siblings, 0 replies; 12+ messages in thread
From: Thomas Abraham @ 2009-11-01  5:07 UTC (permalink / raw)
  To: linux-ide; +Cc: ben-linux, linux-arm-kernel, Thomas Abraham

This patch adds Samsung S3C IDE controller driver. This driver supports
PIO and UDMA modes of data transfer.

Note: This patch depends the following patch series.
[PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE controller driver

Signed-off-by: Abhilash Kesavan <a.kesavan <at> samsung.com>
Signed-off-by: Thomas Abraham <thomas.ab <at> samsung.com>
---
 drivers/ide/Kconfig    |    9 +
 drivers/ide/Makefile   |    1 +
 drivers/ide/ide-proc.c |    1 +
 drivers/ide/s3c-ide.c  |  822 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/ide.h    |    2 +-
 5 files changed, 834 insertions(+), 1 deletions(-)
 create mode 100644 drivers/ide/s3c-ide.c

diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 9a5d0aa..35610bc 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -292,6 +292,15 @@ config BLK_DEV_IDEPNP
 	  would like the kernel to automatically detect and activate
 	  it, say Y here.

+config BLK_DEV_IDE_S3C
+	tristate "Samsung S3C IDE Controller"
+	depends on PLAT_S3C64XX
+	select BLK_DEV_IDEDMA
+	help
+	  Say Y here if you want to support onchip CF/IDE controller
+	  in Samsung SoC. It will be configured for True IDE mode with
+	  support for PIO and UDMA mode of data transfer.
+
 config BLK_DEV_IDEDMA_SFF
 	bool

diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 81df925..a64a723 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_BLK_DEV_SIIMAGE)		+= siimage.o
 obj-$(CONFIG_BLK_DEV_SIS5513)		+= sis5513.o
 obj-$(CONFIG_BLK_DEV_SL82C105)		+= sl82c105.o
 obj-$(CONFIG_BLK_DEV_SLC90E66)		+= slc90e66.o
+obj-$(CONFIG_BLK_DEV_IDE_S3C)		+= s3c-ide.o
 obj-$(CONFIG_BLK_DEV_TC86C001)		+= tc86c001.o
 obj-$(CONFIG_BLK_DEV_TRIFLEX)		+= triflex.o
 obj-$(CONFIG_BLK_DEV_TRM290)		+= trm290.o
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 3242698..d18478f 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -51,6 +51,7 @@ static int proc_ide_read_imodel
 	case ide_au1xxx:	name = "au1xxx";	break;
 	case ide_palm3710:      name = "palm3710";      break;
 	case ide_acorn:		name = "acorn";		break;
+	case ide_s3c: 		name = "s3c-ide";	break;
 	default:		name = "(unknown)";	break;
 	}
 	len = sprintf(page, "%s\n", name);
diff --git a/drivers/ide/s3c-ide.c b/drivers/ide/s3c-ide.c
new file mode 100644
index 0000000..027af17
--- /dev/null
+++ b/drivers/ide/s3c-ide.c
@@ -0,0 +1,822 @@
+/*
+ * s3c-ide.c - Samsung S3C IDE controller Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ *      http://samsungsemi.com/
+ *
+ * The Samsung S3C IDE controller driver provides low-level support for
+ * interfacing with IDE disks. This controller driver supports PIO and
+ * UDMA data transfer modes.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <plat/regs-ide.h>
+#include <plat/ide.h>
+
+/*
+ * defines ide controller data transfer commands
+ */
+enum {
+	CMD_STOP,
+	CMD_START,
+	CMD_ABORT,
+	CMD_CONTINUE
+};
+
+/*
+ * defines the transfer class
+ */
+enum {
+	XFER_MODE_PIO_CPU,
+	XFER_MODE_PIO_DMA,
+	XFER_MODE_UDMA
+};
+
+/*
+ * defines the bus state
+ */
+enum {
+	BS_IDLE,
+	BS_BUSYW,
+	BS_PREP,
+	BS_BUSYR,
+	BS_PAUSER,
+	BS_PAUSEW,
+	BS_PAUSER2
+};
+
+struct dma_queue_t {
+	ulong addr;
+	ulong len;
+};
+
+/*
+ * struct s3c_ide_device - instance of ide controller device.
+ */
+struct s3c_ide_device {
+	struct platform_device	*pdev;
+	struct clk		*ide_clk;
+	ide_hwif_t 		*hwif;
+	int 			irq;
+	ulong 			piotime[5];
+	ulong 			udmatime[5];
+	void __iomem            *regbase;
+	u32 			index;	/* current queue index */
+	u32 			queue_size; /* total queue size requested */
+	struct dma_queue_t 	table[PRD_ENTRIES];
+	u32 			dma_mode; /* in DMA session */
+};
+
+static inline void ide_writel(struct s3c_ide_device *dev, u32 value, u32 reg)
+{
+	writel(value, dev->regbase + reg);
+}
+
+static inline u32 ide_readl(struct s3c_ide_device *dev, u32 reg)
+{
+	return readl(dev->regbase + reg);
+}
+
+/*
+ * waits until the IDE controller is able to perform next read/write
+ * operation to the disk.
+ */
+static int wait_for_host_ready(struct s3c_ide_device *ide_dev)
+{
+	ulong timeout;
+
+	/* wait for maximum of 20 msec */
+	timeout = jiffies + msecs_to_jiffies(20);
+	while (time_before(jiffies, timeout)) {
+		if ((ide_readl(ide_dev, S3C_ATA_FIFO_STATUS) >> 28) == 0)
+			return 0;
+	}
+	dev_err(&ide_dev->pdev->dev,
+		"ide controller not ready for next taskfile operation");
+	return -1;
+}
+
+/*
+ * writes to one of the task file registers.
+ */
+static void ide_outb(ide_hwif_t *hwif, u8 addr, ulong reg)
+{
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+
+	wait_for_host_ready(ide_dev);
+	__raw_writeb(addr, reg);
+}
+
+/*
+ * reads from one of the task file registers.
+ */
+static u8 ide_inb(ide_hwif_t *hwif, ulong reg)
+{
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u8 temp;
+
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(reg);
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	return temp;
+}
+
+/*
+ * following are ide_tp_ops functions implemented by the IDE
+ * cotnroller driver.
+ */
+static void s3c_ide_exec_command(ide_hwif_t *hwif, u8 cmd)
+{
+	ide_outb(hwif, cmd, hwif->io_ports.command_addr);
+}
+
+static u8 s3c_ide_read_status(ide_hwif_t *hwif)
+{
+	return ide_inb(hwif, hwif->io_ports.status_addr);
+}
+
+static u8 s3c_ide_read_altstatus(ide_hwif_t *hwif)
+{
+	return ide_inb(hwif, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+	ide_outb(hwif, ctl, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_dev_select(ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	u8 select = drive->select | ATA_DEVICE_OBS;
+	ide_outb(hwif, select, hwif->io_ports.device_addr);
+}
+
+static void s3c_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(data_addr);
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	}
+}
+
+static void s3c_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		writel(*temp_addr, data_addr);
+	}
+}
+
+static void s3c_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)
+		ide_outb(hwif, tf->feature, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		ide_outb(hwif, tf->nsect, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		ide_outb(hwif, tf->lbal, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		ide_outb(hwif, tf->lbam, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		ide_outb(hwif, tf->lbah, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		ide_outb(hwif, tf->device, io_ports->device_addr);
+}
+
+static void s3c_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  = ide_inb(hwif, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		tf->nsect  = ide_inb(hwif, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		tf->lbal   = ide_inb(hwif, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		tf->lbam   = ide_inb(hwif, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		tf->lbah   = ide_inb(hwif, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		tf->device = ide_inb(hwif, io_ports->device_addr);
+}
+
+/*
+ * wait for a specified ide bus state
+ */
+static int wait_for_bus_state(struct s3c_ide_device *ide_dev, u8 state)
+{
+	u32 status, current_state;
+	ulong timeout;
+
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (time_before(jiffies, timeout)) {
+		status = ide_readl(ide_dev, S3C_BUS_FIFO_STATUS);
+		current_state = (status >> 16) & 0x7;
+		if (current_state == state) {
+			if ((state == BS_PAUSER2) || (state == BS_IDLE)) {
+				if (status & 0xFFFF)
+					continue;
+			}
+		}
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * reads a ide disk device register
+ */
+static u8 read_dev_reg(struct s3c_ide_device *ide_dev, u32 reg)
+{
+	u8 temp;
+
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + reg);
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	return temp;
+}
+
+/*
+ * sets the data transfer mode
+ */
+static void set_xfer_mode(struct s3c_ide_device *ide_dev, u8 mode, int rw)
+{
+	u32 reg = ide_readl(ide_dev, S3C_ATA_CFG) & ~(0x39c);
+
+	reg |= mode << 2;
+	if (mode && rw)
+		reg |= 0x10;
+	if (mode == XFER_MODE_UDMA)
+		reg |= 0x380;
+	ide_writel(ide_dev, reg, S3C_ATA_CFG);
+}
+
+static void set_xfer_command(struct s3c_ide_device *ide_dev, u8 cmd)
+{
+	wait_for_host_ready(ide_dev);
+	ide_writel(ide_dev, cmd, S3C_ATA_CMD);
+}
+
+/* Building the Scatter Gather Table */
+static int ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
+{
+	int i, count = 0, nents = cmd->sg_nents;
+	ide_hwif_t *hwif = drive->hwif;
+	struct request *rq = hwif->rq;
+	struct scatterlist *sg;
+	u32 addr_reg, size_reg;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+
+	if (rq_data_dir(rq) == WRITE) {
+		addr_reg = S3C_ATA_SBUF_START;
+		size_reg = S3C_ATA_SBUF_SIZE;
+	} else {
+		addr_reg = S3C_ATA_TBUF_START;
+		size_reg = S3C_ATA_TBUF_SIZE;
+	}
+
+	/* save information for interrupt context */
+	if (nents > 1)
+		ide_dev->dma_mode = 1;
+	if (!nents)
+		return 0;
+
+	/* fill the descriptors */
+	sg = hwif->sg_table;
+	for (i = 0, sg = hwif->sg_table; i < nents && sg_dma_len(sg);
+				 i++, sg++) {
+		ide_dev->table[i].addr = sg_dma_address(sg);
+		ide_dev->table[i].len = sg_dma_len(sg);
+		count += ide_dev->table[i].len;
+	}
+	ide_dev->table[i].addr = 0;
+	ide_dev->table[i].len = 0;
+	ide_dev->queue_size = i;
+
+	ide_writel(ide_dev, ide_dev->table[0].len - 0x1, size_reg);
+	ide_writel(ide_dev, ide_dev->table[0].addr, addr_reg);
+
+	ide_dev->index = 1;
+	ide_writel(ide_dev, count, S3C_ATA_XFR_NUM);
+	return 1;
+}
+
+/*
+ * wait for a specified status of the ide disk status
+ */
+static int wait_for_disk_status(ide_drive_t *drive, u8 status)
+{
+	u8 csd;
+	ulong timeout;
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	timeout = jiffies + msecs_to_jiffies(1000);
+	while (time_before(jiffies, timeout)) {
+		csd = read_dev_reg(ide_dev, S3C_ATA_PIO_CSD);
+		if ((csd == status) || (csd & ATA_ERR))
+			return 0;
+	}
+
+	dev_err(&ide_dev->pdev->dev,
+		"timeout occured while waiting for disk status");
+	return -1;
+}
+
+/*
+ * following are the ide_dma_ops functions implemented by the ide driver
+ */
+static int s3c_ide_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
+{
+	return 0;
+}
+
+static void s3c_ide_dma_host_set(ide_drive_t *drive, int on)
+{
+	return;
+}
+
+static int s3c_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
+{
+	if (!ide_build_dmatable(drive, cmd))
+		return 1;
+
+	drive->waiting_for_dma = 1;
+	return 0;
+}
+
+static void s3c_ide_dma_start(ide_drive_t *drive)
+{
+	struct request *rq = drive->hwif->rq;
+	uint rw = (rq_data_dir(rq) == WRITE);
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	wait_for_disk_status(drive, DRIVE_READY|ATA_DRQ);
+	ide_writel(ide_dev, 0x3, S3C_ATA_IRQ_MSK);
+	set_xfer_mode(ide_dev, XFER_MODE_UDMA, rw);
+	set_xfer_command(ide_dev, CMD_START);
+	return;
+}
+
+static int s3c_ide_dma_end(ide_drive_t *drive)
+{
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	if (wait_for_host_ready(ide_dev))
+		return 1;
+
+	if ((ide_readl(ide_dev, S3C_BUS_FIFO_STATUS) >> 16) == BS_PAUSEW)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+
+	if (wait_for_bus_state(ide_dev, BS_IDLE))
+		return 1;
+
+	ide_writel(ide_dev, 0x3, S3C_ATA_IRQ_MSK);
+	set_xfer_mode(ide_dev, XFER_MODE_PIO_CPU, 0);
+	if (wait_for_disk_status(drive, DRIVE_READY))
+		return 1;
+	drive->waiting_for_dma = 0;
+	return 0;
+}
+
+static void s3c_ide_dma_lostirq(ide_drive_t *drive)
+{
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+	dev_err(&ide_dev->pdev->dev, "irq lost");
+}
+
+
+static int s3c_ide_dma_test_irq(ide_drive_t *drive)
+{
+	return 1;
+}
+
+static void set_ata_enable(struct s3c_ide_device *ide_dev, u8 state)
+{
+	u32 temp = ide_readl(ide_dev, S3C_ATA_CTRL);
+	temp = state ? temp | 1 : temp & ~1;
+	ide_writel(ide_dev, temp , S3C_ATA_CTRL);
+}
+
+static void set_endian_mode(struct s3c_ide_device *ide_dev, u8 mode)
+{
+	u32 reg = ide_readl(ide_dev, S3C_ATA_CFG);
+	reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
+	ide_writel(ide_dev, reg, S3C_ATA_CFG);
+}
+
+/*
+ * This function selects the maximum possible transfer speed.
+ */
+static u8 ide_ratefilter(ide_drive_t *drive, u8 speed)
+{
+	if (drive->media != ide_disk)
+		return min(speed, (u8) XFER_PIO_4);
+
+	switch (speed) {
+	case XFER_UDMA_6:
+	case XFER_UDMA_5:
+	speed = XFER_UDMA_4;
+		break;
+	case XFER_UDMA_4:
+	case XFER_UDMA_3:
+	case XFER_UDMA_2:
+	case XFER_UDMA_1:
+	case XFER_UDMA_0:
+		break;
+	default:
+		speed = min(speed, (u8) XFER_PIO_4);
+		break;
+	}
+	return speed;
+}
+
+/*
+ * This function selects the best possible transfer speed.
+ */
+static void s3c_ide_tune_chipset(ide_drive_t *drive, u8 xferspeed)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u8 speed = ide_ratefilter(drive, xferspeed);
+	u32 ata_cfg;
+
+	/* IORDY is enabled for modes > PIO2 */
+	if (XFER_PIO_0 >= speed && speed <= XFER_PIO_4) {
+		ata_cfg = ide_readl(ide_dev, S3C_ATA_CFG);
+
+		switch (speed) {
+		case XFER_PIO_0:
+		case XFER_PIO_1:
+		case XFER_PIO_2:
+			ata_cfg &= (~S3C_ATA_CFG_IORDYEN);
+			break;
+		case XFER_PIO_3:
+		case XFER_PIO_4:
+			ata_cfg |= S3C_ATA_CFG_IORDYEN;
+			break;
+		}
+		ide_writel(ide_dev, ata_cfg, S3C_ATA_CFG);
+		ide_writel(ide_dev, ide_dev->piotime[speed - XFER_PIO_0],
+				S3C_ATA_PIO_TIME);
+	} else {
+		ide_writel(ide_dev, ide_dev->piotime[0], S3C_ATA_PIO_TIME);
+		ide_writel(ide_dev, ide_dev->udmatime[speed - XFER_UDMA_0],
+				S3C_ATA_UDMA_TIME);
+		set_endian_mode(ide_dev, 1);
+	}
+	ide_config_drive_speed(drive, speed);
+}
+
+static void s3c_ide_tune_drive(ide_drive_t *drive, u8 pio)
+{
+	pio = ide_get_best_pio_mode(drive, 255, pio);
+	(void)s3c_ide_tune_chipset(drive, (XFER_PIO_0 + pio));
+}
+
+static irqreturn_t s3c_irq_handler(int irq, void *dev_id)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)dev_id;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u32 reg = ide_readl(ide_dev, S3C_ATA_IRQ);
+	u32 len, addr;
+	u32 stat;
+
+	ide_writel(ide_dev, reg, S3C_ATA_IRQ);
+	if (ide_dev->dma_mode) {
+		len = ide_dev->table[ide_dev->index].len - 1;
+		addr = ide_dev->table[ide_dev->index].addr;
+		if (reg & 0x10)
+			wait_for_bus_state(ide_dev, BS_PAUSER2);
+		else if (reg & 0x08) {
+			wait_for_bus_state(ide_dev, BS_PAUSEW);
+			ide_writel(ide_dev, len, S3C_ATA_TBUF_SIZE);
+			ide_writel(ide_dev, addr, S3C_ATA_TBUF_START);
+		} else
+			return 1;
+
+		ide_writel(ide_dev, len, S3C_ATA_SBUF_SIZE);
+		ide_writel(ide_dev, addr, S3C_ATA_SBUF_START);
+
+		if (ide_dev->queue_size == ++ide_dev->index)
+			ide_dev->dma_mode = 0;
+
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+		return 1;
+	}
+
+	stat = ide_readl(ide_dev, S3C_BUS_FIFO_STATUS) >> 16;
+	if (stat == BS_PAUSER2)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+	else if (stat == BS_PAUSEW)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+
+	return ide_intr(irq, dev_id);
+}
+
+static void setup_timing_value(struct s3c_ide_device *ide_dev, u32 clk_rate)
+{
+	u32 t1, t2, teoc, i;
+	u32 uTdvh1, uTdvs, uTrp, uTss, uTackenv;
+	ulong cycle_time = (uint)(1000000000 / clk_rate);
+
+	/* transfer timing for PIO mode */
+	uint pio_t1[5] = { 70, 50, 30, 30, 30 };
+	uint pio_t2[5] = { 290, 290, 290, 80, 70 };
+	uint pio_teoc[5] = { 20, 20, 10, 10, 10 };
+
+	/* transfer timing for UDMA mode */
+	uint uUdmaTdvh[5] = { 50, 32, 29, 25, 24 };
+	uint uUdmaTdvs[5] = { 70, 48, 31, 20, 7 };
+	uint uUdmaTrp[5] = { 160, 125, 100, 100, 100 };
+	uint uUdmaTss[5] = { 50, 50, 50, 50, 50 };
+	uint uUdmaTackenvMin[5] = { 20, 20, 20, 20, 20 };
+
+	/* calculate pio_time register value for all PIO modes */
+	for (i = 0; i < 5; i++) {
+		t1 = (pio_t1[i] / cycle_time) & 0x0f;
+		t2 = (pio_t2[i] / cycle_time) & 0xff;
+		teoc = (pio_teoc[i] / cycle_time) & 0xff;
+		ide_dev->piotime[i] = (teoc << 12) | (t2 << 4) | t1;
+	}
+
+	/* calculate udma_time register value for all udma modes */
+	for (i = 0; i < 5; i++) {
+		uTdvh1 = (uUdmaTdvh[i] / cycle_time) & 0x0f;
+		uTdvs = (uUdmaTdvs[i] / cycle_time) & 0xff;
+		uTrp = (uUdmaTrp[i] / cycle_time) & 0xff;
+		uTss = (uUdmaTss[i] / cycle_time) & 0x0f;
+		uTackenv = (uUdmaTackenvMin[i] / cycle_time) & 0x0f;
+		ide_dev->udmatime[i] = (uTdvh1 << 24) | (uTdvs << 16) |
+			(uTrp << 8) | (uTss << 4) | uTackenv;
+	}
+}
+
+static void change_mode_to_ata(struct s3c_ide_device *ide_dev)
+{
+	ide_writel(ide_dev, ide_readl(ide_dev,
+		S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,	S3C_CFATA_MUX);
+}
+
+static void init_ide_device(struct s3c_ide_device *ide_dev)
+{
+	change_mode_to_ata(ide_dev);
+	set_endian_mode(ide_dev, 1);
+	set_ata_enable(ide_dev, 1);
+}
+
+static void ide_setup_ports(struct ide_hw *hw, struct s3c_ide_device *ide_dev)
+{
+	int i;
+	unsigned long *ata_regs = hw->io_ports_array;
+
+	/* S3C IDE controller does not include irq_addr port */
+	for (i = 0; i < IDE_NR_PORTS-1; i++)
+		*ata_regs++ = (ulong)ide_dev->regbase +
+				S3C_ATA_PIO_DTR + (i << 2);
+}
+
+static u8 s3c_cable_detect(ide_hwif_t *hwif)
+{
+	return ATA_CBL_PATA80;
+}
+
+static const struct ide_port_ops s3c_ide_port_ops = {
+	.set_pio_mode = s3c_ide_tune_drive,
+	.set_dma_mode = s3c_ide_tune_chipset,
+	.cable_detect = s3c_cable_detect,
+};
+
+static const struct ide_tp_ops s3c_ide_tp_ops = {
+	.exec_command   = s3c_ide_exec_command,
+	.read_status    = s3c_ide_read_status,
+	.read_altstatus = s3c_ide_read_altstatus,
+	.write_devctl   = s3c_ide_write_devctl,
+	.dev_select     = s3c_ide_dev_select,
+	.tf_load        = s3c_ide_tf_load,
+	.tf_read        = s3c_ide_tf_read,
+	.input_data     = s3c_ide_input_data,
+	.output_data    = s3c_ide_output_data,
+};
+
+static const struct ide_dma_ops s3c_ide_dma_ops = {
+	.dma_host_set 	= s3c_ide_dma_host_set,
+	.dma_setup 	= s3c_ide_dma_setup,
+	.dma_start 	= s3c_ide_dma_start,
+	.dma_end 	= s3c_ide_dma_end,
+	.dma_test_irq 	= s3c_ide_dma_test_irq,
+	.dma_lost_irq 	= s3c_ide_dma_lostirq,
+};
+
+static const struct ide_port_info s3c_port_info = {
+	.name		= "s3c-ide",
+	.init_dma	= s3c_ide_dma_init,
+	.dma_ops        = &s3c_ide_dma_ops,
+	.port_ops	= &s3c_ide_port_ops,
+	.tp_ops         = &s3c_ide_tp_ops,
+	.chipset	= ide_s3c,
+	.host_flags	= IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
+				IDE_HFLAG_UNMASK_IRQS,
+	.pio_mask	= ATA_PIO4,
+	.udma_mask      = ATA_UDMA4
+};
+
+static int __devinit s3c_ide_probe(struct platform_device *pdev)
+{
+	struct resource	*res;
+	struct s3c_ide_device *ide_dev;
+	struct s3c_ide_platdata *pdata = pdev->dev.platform_data;
+	struct ide_host *host;
+	int ret = 0;
+	struct ide_hw hw, *hws[] = { &hw };
+
+	ide_dev = kzalloc(sizeof(struct s3c_ide_device), GFP_KERNEL);
+	if (!ide_dev) {
+		dev_err(&pdev->dev, "no memory for s3c device instance\n");
+		return -ENOMEM;
+	}
+	ide_dev->pdev = pdev;
+
+	ide_dev->irq = platform_get_irq(pdev, 0);
+	if (ide_dev->irq < 0) {
+		dev_err(&pdev->dev, "could not obtain irq number\n");
+		ret = -ENODEV;
+		goto release_device_mem;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev,
+			"could not obtain base address of controller\n");
+		ret = -ENODEV;
+		goto release_device_mem;
+	}
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+		pdev->name)) {
+		dev_err(&pdev->dev, "could not obtain i/o address\n");
+		ret = -EBUSY;
+		goto release_device_mem;
+	}
+
+	ide_dev->regbase = ioremap(res->start, res->end - res->start + 1);
+	if (ide_dev->regbase == 0) {
+		dev_err(&pdev->dev, "could not remap i/o address\n");
+		ret = -ENOMEM;
+		goto release_mem;
+	}
+
+	ide_dev->ide_clk = clk_get(&pdev->dev, "cfcon");
+	if (IS_ERR(ide_dev->ide_clk)) {
+		dev_err(&pdev->dev, "failed to find clock source\n");
+		ret = PTR_ERR(ide_dev->ide_clk);
+		ide_dev->ide_clk = NULL;
+		goto unmap;
+	}
+
+	if (clk_enable(ide_dev->ide_clk)) {
+		dev_err(&pdev->dev, "failed to enable clock source.\n");
+		ret = -ENODEV;
+		goto clkerr;
+	}
+
+	setup_timing_value(ide_dev, clk_get_rate(ide_dev->ide_clk));
+	if (pdata && (pdata->setup_gpio))
+		pdata->setup_gpio();
+	init_ide_device(ide_dev);
+
+	ide_writel(ide_dev, 0x1f, S3C_ATA_IRQ);
+	ide_writel(ide_dev, 0x1b, S3C_ATA_IRQ_MSK);
+
+	memset(&hw, 0, sizeof(hw));
+	ide_setup_ports(&hw, ide_dev);
+	hw.irq = ide_dev->irq;
+	hw.dev = &pdev->dev;
+
+	host = ide_host_alloc(&s3c_port_info, hws, 1);
+	if (!host) {
+		dev_err(&pdev->dev, "failed to allocate ide host\n");
+		ret = -ENOMEM;
+		goto stop_clk;
+	}
+
+	host->irq_handler = s3c_irq_handler;
+	host->ports[0]->hwif_data = (void *)ide_dev;
+	ide_dev->hwif = host->ports[0];
+	platform_set_drvdata(pdev, host);
+
+	ret = ide_host_register(host, &s3c_port_info, hws);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ide host\n");
+		goto dealloc_ide_host;
+	}
+
+	return 0;
+
+dealloc_ide_host:
+	ide_host_free(host);
+stop_clk:
+	clk_disable(ide_dev->ide_clk);
+clkerr:
+	clk_put(ide_dev->ide_clk);
+unmap:
+	iounmap(ide_dev->regbase);
+release_mem:
+	release_mem_region(res->start, res->end - res->start + 1);
+release_device_mem:
+	kfree(ide_dev);
+	return ret;
+}
+
+static int __devexit s3c_ide_remove(struct platform_device *pdev)
+{
+	struct ide_host *host = platform_get_drvdata(pdev);
+	struct resource *res;
+	struct s3c_ide_device *ide_dev = host->ports[0]->hwif_data;
+
+	ide_host_remove(host);
+	iounmap(ide_dev->regbase);
+	clk_disable(ide_dev->ide_clk);
+	clk_put(ide_dev->ide_clk);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+	kfree(ide_dev);
+	return 0;
+}
+
+static struct platform_driver s3c_ide_driver = {
+	.probe		= s3c_ide_probe,
+	.remove		= __devexit_p(s3c_ide_remove),
+	.driver = {
+		.name	= "s3c-ide",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init s3c_ide_init(void)
+{
+	return platform_driver_register(&s3c_ide_driver);
+}
+
+static void __exit s3c_ide_exit(void)
+{
+	platform_driver_unregister(&s3c_ide_driver);
+}
+
+module_init(s3c_ide_init);
+module_exit(s3c_ide_exit);
+
+MODULE_DESCRIPTION("Samsung S3C IDE Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-cfcon");
diff --git a/include/linux/ide.h b/include/linux/ide.h
index edc93a6..ff9eb0e 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -164,7 +164,7 @@ enum {		ide_unknown,	ide_generic,	ide_pci,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd65xx,	ide_umc8672,	ide_ht6560b,
 		ide_4drives,	ide_pmac,	ide_acorn,
-		ide_au1xxx,	ide_palm3710
+		ide_au1xxx,	ide_palm3710,	ide_s3c
 };

 typedef u8 hwif_chipset_t;
-- 
1.5.3.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH] S3C: ide: Add Samsung S3C IDE controller driver
@ 2009-11-01  5:07 ` Thomas Abraham
  0 siblings, 0 replies; 12+ messages in thread
From: Thomas Abraham @ 2009-11-01  5:07 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds Samsung S3C IDE controller driver. This driver supports
PIO and UDMA modes of data transfer.

Note: This patch depends the following patch series.
[PATCH 0/7] S3C64XX: Add platform support for Samsung S3C IDE controller driver

Signed-off-by: Abhilash Kesavan <a.kesavan <at> samsung.com>
Signed-off-by: Thomas Abraham <thomas.ab <at> samsung.com>
---
 drivers/ide/Kconfig    |    9 +
 drivers/ide/Makefile   |    1 +
 drivers/ide/ide-proc.c |    1 +
 drivers/ide/s3c-ide.c  |  822 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/ide.h    |    2 +-
 5 files changed, 834 insertions(+), 1 deletions(-)
 create mode 100644 drivers/ide/s3c-ide.c

diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index 9a5d0aa..35610bc 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -292,6 +292,15 @@ config BLK_DEV_IDEPNP
 	  would like the kernel to automatically detect and activate
 	  it, say Y here.

+config BLK_DEV_IDE_S3C
+	tristate "Samsung S3C IDE Controller"
+	depends on PLAT_S3C64XX
+	select BLK_DEV_IDEDMA
+	help
+	  Say Y here if you want to support onchip CF/IDE controller
+	  in Samsung SoC. It will be configured for True IDE mode with
+	  support for PIO and UDMA mode of data transfer.
+
 config BLK_DEV_IDEDMA_SFF
 	bool

diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index 81df925..a64a723 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_BLK_DEV_SIIMAGE)		+= siimage.o
 obj-$(CONFIG_BLK_DEV_SIS5513)		+= sis5513.o
 obj-$(CONFIG_BLK_DEV_SL82C105)		+= sl82c105.o
 obj-$(CONFIG_BLK_DEV_SLC90E66)		+= slc90e66.o
+obj-$(CONFIG_BLK_DEV_IDE_S3C)		+= s3c-ide.o
 obj-$(CONFIG_BLK_DEV_TC86C001)		+= tc86c001.o
 obj-$(CONFIG_BLK_DEV_TRIFLEX)		+= triflex.o
 obj-$(CONFIG_BLK_DEV_TRM290)		+= trm290.o
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 3242698..d18478f 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -51,6 +51,7 @@ static int proc_ide_read_imodel
 	case ide_au1xxx:	name = "au1xxx";	break;
 	case ide_palm3710:      name = "palm3710";      break;
 	case ide_acorn:		name = "acorn";		break;
+	case ide_s3c: 		name = "s3c-ide";	break;
 	default:		name = "(unknown)";	break;
 	}
 	len = sprintf(page, "%s\n", name);
diff --git a/drivers/ide/s3c-ide.c b/drivers/ide/s3c-ide.c
new file mode 100644
index 0000000..027af17
--- /dev/null
+++ b/drivers/ide/s3c-ide.c
@@ -0,0 +1,822 @@
+/*
+ * s3c-ide.c - Samsung S3C IDE controller Driver
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ *      http://samsungsemi.com/
+ *
+ * The Samsung S3C IDE controller driver provides low-level support for
+ * interfacing with IDE disks. This controller driver supports PIO and
+ * UDMA data transfer modes.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/ide.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <plat/regs-ide.h>
+#include <plat/ide.h>
+
+/*
+ * defines ide controller data transfer commands
+ */
+enum {
+	CMD_STOP,
+	CMD_START,
+	CMD_ABORT,
+	CMD_CONTINUE
+};
+
+/*
+ * defines the transfer class
+ */
+enum {
+	XFER_MODE_PIO_CPU,
+	XFER_MODE_PIO_DMA,
+	XFER_MODE_UDMA
+};
+
+/*
+ * defines the bus state
+ */
+enum {
+	BS_IDLE,
+	BS_BUSYW,
+	BS_PREP,
+	BS_BUSYR,
+	BS_PAUSER,
+	BS_PAUSEW,
+	BS_PAUSER2
+};
+
+struct dma_queue_t {
+	ulong addr;
+	ulong len;
+};
+
+/*
+ * struct s3c_ide_device - instance of ide controller device.
+ */
+struct s3c_ide_device {
+	struct platform_device	*pdev;
+	struct clk		*ide_clk;
+	ide_hwif_t 		*hwif;
+	int 			irq;
+	ulong 			piotime[5];
+	ulong 			udmatime[5];
+	void __iomem            *regbase;
+	u32 			index;	/* current queue index */
+	u32 			queue_size; /* total queue size requested */
+	struct dma_queue_t 	table[PRD_ENTRIES];
+	u32 			dma_mode; /* in DMA session */
+};
+
+static inline void ide_writel(struct s3c_ide_device *dev, u32 value, u32 reg)
+{
+	writel(value, dev->regbase + reg);
+}
+
+static inline u32 ide_readl(struct s3c_ide_device *dev, u32 reg)
+{
+	return readl(dev->regbase + reg);
+}
+
+/*
+ * waits until the IDE controller is able to perform next read/write
+ * operation to the disk.
+ */
+static int wait_for_host_ready(struct s3c_ide_device *ide_dev)
+{
+	ulong timeout;
+
+	/* wait for maximum of 20 msec */
+	timeout = jiffies + msecs_to_jiffies(20);
+	while (time_before(jiffies, timeout)) {
+		if ((ide_readl(ide_dev, S3C_ATA_FIFO_STATUS) >> 28) == 0)
+			return 0;
+	}
+	dev_err(&ide_dev->pdev->dev,
+		"ide controller not ready for next taskfile operation");
+	return -1;
+}
+
+/*
+ * writes to one of the task file registers.
+ */
+static void ide_outb(ide_hwif_t *hwif, u8 addr, ulong reg)
+{
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+
+	wait_for_host_ready(ide_dev);
+	__raw_writeb(addr, reg);
+}
+
+/*
+ * reads from one of the task file registers.
+ */
+static u8 ide_inb(ide_hwif_t *hwif, ulong reg)
+{
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u8 temp;
+
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(reg);
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	return temp;
+}
+
+/*
+ * following are ide_tp_ops functions implemented by the IDE
+ * cotnroller driver.
+ */
+static void s3c_ide_exec_command(ide_hwif_t *hwif, u8 cmd)
+{
+	ide_outb(hwif, cmd, hwif->io_ports.command_addr);
+}
+
+static u8 s3c_ide_read_status(ide_hwif_t *hwif)
+{
+	return ide_inb(hwif, hwif->io_ports.status_addr);
+}
+
+static u8 s3c_ide_read_altstatus(ide_hwif_t *hwif)
+{
+	return ide_inb(hwif, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_write_devctl(ide_hwif_t *hwif, u8 ctl)
+{
+	ide_outb(hwif, ctl, hwif->io_ports.ctl_addr);
+}
+
+static void s3c_ide_dev_select(ide_drive_t *drive)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	u8 select = drive->select | ATA_DEVICE_OBS;
+	ide_outb(hwif, select, hwif->io_ports.device_addr);
+}
+
+static void s3c_ide_input_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(data_addr);
+		wait_for_host_ready(ide_dev);
+		*temp_addr = __raw_readw(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	}
+}
+
+static void s3c_ide_output_data(ide_drive_t *drive, struct ide_cmd *cmd,
+					void *buf, unsigned int len)
+{
+	ide_hwif_t *hwif = drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	struct ide_io_ports *io_ports = &hwif->io_ports;
+	unsigned long data_addr = io_ports->data_addr;
+	unsigned int words = (len + 1) >> 1, i;
+	u16 *temp_addr = (u16 *)buf;
+
+	for (i = 0; i < words; i++, temp_addr++) {
+		wait_for_host_ready(ide_dev);
+		writel(*temp_addr, data_addr);
+	}
+}
+
+static void s3c_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)
+		ide_outb(hwif, tf->feature, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		ide_outb(hwif, tf->nsect, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		ide_outb(hwif, tf->lbal, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		ide_outb(hwif, tf->lbam, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		ide_outb(hwif, tf->lbah, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		ide_outb(hwif, tf->device, io_ports->device_addr);
+}
+
+static void s3c_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  = ide_inb(hwif, io_ports->feature_addr);
+	if (valid & IDE_VALID_NSECT)
+		tf->nsect  = ide_inb(hwif, io_ports->nsect_addr);
+	if (valid & IDE_VALID_LBAL)
+		tf->lbal   = ide_inb(hwif, io_ports->lbal_addr);
+	if (valid & IDE_VALID_LBAM)
+		tf->lbam   = ide_inb(hwif, io_ports->lbam_addr);
+	if (valid & IDE_VALID_LBAH)
+		tf->lbah   = ide_inb(hwif, io_ports->lbah_addr);
+	if (valid & IDE_VALID_DEVICE)
+		tf->device = ide_inb(hwif, io_ports->device_addr);
+}
+
+/*
+ * wait for a specified ide bus state
+ */
+static int wait_for_bus_state(struct s3c_ide_device *ide_dev, u8 state)
+{
+	u32 status, current_state;
+	ulong timeout;
+
+	timeout = jiffies + msecs_to_jiffies(100);
+	while (time_before(jiffies, timeout)) {
+		status = ide_readl(ide_dev, S3C_BUS_FIFO_STATUS);
+		current_state = (status >> 16) & 0x7;
+		if (current_state == state) {
+			if ((state == BS_PAUSER2) || (state == BS_IDLE)) {
+				if (status & 0xFFFF)
+					continue;
+			}
+		}
+		return 0;
+	}
+	return -1;
+}
+
+/*
+ * reads a ide disk device register
+ */
+static u8 read_dev_reg(struct s3c_ide_device *ide_dev, u32 reg)
+{
+	u8 temp;
+
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + reg);
+	wait_for_host_ready(ide_dev);
+	temp = __raw_readb(ide_dev->regbase + S3C_ATA_PIO_RDATA);
+	return temp;
+}
+
+/*
+ * sets the data transfer mode
+ */
+static void set_xfer_mode(struct s3c_ide_device *ide_dev, u8 mode, int rw)
+{
+	u32 reg = ide_readl(ide_dev, S3C_ATA_CFG) & ~(0x39c);
+
+	reg |= mode << 2;
+	if (mode && rw)
+		reg |= 0x10;
+	if (mode == XFER_MODE_UDMA)
+		reg |= 0x380;
+	ide_writel(ide_dev, reg, S3C_ATA_CFG);
+}
+
+static void set_xfer_command(struct s3c_ide_device *ide_dev, u8 cmd)
+{
+	wait_for_host_ready(ide_dev);
+	ide_writel(ide_dev, cmd, S3C_ATA_CMD);
+}
+
+/* Building the Scatter Gather Table */
+static int ide_build_dmatable(ide_drive_t *drive, struct ide_cmd *cmd)
+{
+	int i, count = 0, nents = cmd->sg_nents;
+	ide_hwif_t *hwif = drive->hwif;
+	struct request *rq = hwif->rq;
+	struct scatterlist *sg;
+	u32 addr_reg, size_reg;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+
+	if (rq_data_dir(rq) == WRITE) {
+		addr_reg = S3C_ATA_SBUF_START;
+		size_reg = S3C_ATA_SBUF_SIZE;
+	} else {
+		addr_reg = S3C_ATA_TBUF_START;
+		size_reg = S3C_ATA_TBUF_SIZE;
+	}
+
+	/* save information for interrupt context */
+	if (nents > 1)
+		ide_dev->dma_mode = 1;
+	if (!nents)
+		return 0;
+
+	/* fill the descriptors */
+	sg = hwif->sg_table;
+	for (i = 0, sg = hwif->sg_table; i < nents && sg_dma_len(sg);
+				 i++, sg++) {
+		ide_dev->table[i].addr = sg_dma_address(sg);
+		ide_dev->table[i].len = sg_dma_len(sg);
+		count += ide_dev->table[i].len;
+	}
+	ide_dev->table[i].addr = 0;
+	ide_dev->table[i].len = 0;
+	ide_dev->queue_size = i;
+
+	ide_writel(ide_dev, ide_dev->table[0].len - 0x1, size_reg);
+	ide_writel(ide_dev, ide_dev->table[0].addr, addr_reg);
+
+	ide_dev->index = 1;
+	ide_writel(ide_dev, count, S3C_ATA_XFR_NUM);
+	return 1;
+}
+
+/*
+ * wait for a specified status of the ide disk status
+ */
+static int wait_for_disk_status(ide_drive_t *drive, u8 status)
+{
+	u8 csd;
+	ulong timeout;
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	timeout = jiffies + msecs_to_jiffies(1000);
+	while (time_before(jiffies, timeout)) {
+		csd = read_dev_reg(ide_dev, S3C_ATA_PIO_CSD);
+		if ((csd == status) || (csd & ATA_ERR))
+			return 0;
+	}
+
+	dev_err(&ide_dev->pdev->dev,
+		"timeout occured while waiting for disk status");
+	return -1;
+}
+
+/*
+ * following are the ide_dma_ops functions implemented by the ide driver
+ */
+static int s3c_ide_dma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
+{
+	return 0;
+}
+
+static void s3c_ide_dma_host_set(ide_drive_t *drive, int on)
+{
+	return;
+}
+
+static int s3c_ide_dma_setup(ide_drive_t *drive, struct ide_cmd *cmd)
+{
+	if (!ide_build_dmatable(drive, cmd))
+		return 1;
+
+	drive->waiting_for_dma = 1;
+	return 0;
+}
+
+static void s3c_ide_dma_start(ide_drive_t *drive)
+{
+	struct request *rq = drive->hwif->rq;
+	uint rw = (rq_data_dir(rq) == WRITE);
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	wait_for_disk_status(drive, DRIVE_READY|ATA_DRQ);
+	ide_writel(ide_dev, 0x3, S3C_ATA_IRQ_MSK);
+	set_xfer_mode(ide_dev, XFER_MODE_UDMA, rw);
+	set_xfer_command(ide_dev, CMD_START);
+	return;
+}
+
+static int s3c_ide_dma_end(ide_drive_t *drive)
+{
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+
+	if (wait_for_host_ready(ide_dev))
+		return 1;
+
+	if ((ide_readl(ide_dev, S3C_BUS_FIFO_STATUS) >> 16) == BS_PAUSEW)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+
+	if (wait_for_bus_state(ide_dev, BS_IDLE))
+		return 1;
+
+	ide_writel(ide_dev, 0x3, S3C_ATA_IRQ_MSK);
+	set_xfer_mode(ide_dev, XFER_MODE_PIO_CPU, 0);
+	if (wait_for_disk_status(drive, DRIVE_READY))
+		return 1;
+	drive->waiting_for_dma = 0;
+	return 0;
+}
+
+static void s3c_ide_dma_lostirq(ide_drive_t *drive)
+{
+	struct s3c_ide_device *ide_dev = drive->hwif->hwif_data;
+	dev_err(&ide_dev->pdev->dev, "irq lost");
+}
+
+
+static int s3c_ide_dma_test_irq(ide_drive_t *drive)
+{
+	return 1;
+}
+
+static void set_ata_enable(struct s3c_ide_device *ide_dev, u8 state)
+{
+	u32 temp = ide_readl(ide_dev, S3C_ATA_CTRL);
+	temp = state ? temp | 1 : temp & ~1;
+	ide_writel(ide_dev, temp , S3C_ATA_CTRL);
+}
+
+static void set_endian_mode(struct s3c_ide_device *ide_dev, u8 mode)
+{
+	u32 reg = ide_readl(ide_dev, S3C_ATA_CFG);
+	reg = mode ? (reg & ~S3C_ATA_CFG_SWAP) : (reg | S3C_ATA_CFG_SWAP);
+	ide_writel(ide_dev, reg, S3C_ATA_CFG);
+}
+
+/*
+ * This function selects the maximum possible transfer speed.
+ */
+static u8 ide_ratefilter(ide_drive_t *drive, u8 speed)
+{
+	if (drive->media != ide_disk)
+		return min(speed, (u8) XFER_PIO_4);
+
+	switch (speed) {
+	case XFER_UDMA_6:
+	case XFER_UDMA_5:
+	speed = XFER_UDMA_4;
+		break;
+	case XFER_UDMA_4:
+	case XFER_UDMA_3:
+	case XFER_UDMA_2:
+	case XFER_UDMA_1:
+	case XFER_UDMA_0:
+		break;
+	default:
+		speed = min(speed, (u8) XFER_PIO_4);
+		break;
+	}
+	return speed;
+}
+
+/*
+ * This function selects the best possible transfer speed.
+ */
+static void s3c_ide_tune_chipset(ide_drive_t *drive, u8 xferspeed)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)drive->hwif;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u8 speed = ide_ratefilter(drive, xferspeed);
+	u32 ata_cfg;
+
+	/* IORDY is enabled for modes > PIO2 */
+	if (XFER_PIO_0 >= speed && speed <= XFER_PIO_4) {
+		ata_cfg = ide_readl(ide_dev, S3C_ATA_CFG);
+
+		switch (speed) {
+		case XFER_PIO_0:
+		case XFER_PIO_1:
+		case XFER_PIO_2:
+			ata_cfg &= (~S3C_ATA_CFG_IORDYEN);
+			break;
+		case XFER_PIO_3:
+		case XFER_PIO_4:
+			ata_cfg |= S3C_ATA_CFG_IORDYEN;
+			break;
+		}
+		ide_writel(ide_dev, ata_cfg, S3C_ATA_CFG);
+		ide_writel(ide_dev, ide_dev->piotime[speed - XFER_PIO_0],
+				S3C_ATA_PIO_TIME);
+	} else {
+		ide_writel(ide_dev, ide_dev->piotime[0], S3C_ATA_PIO_TIME);
+		ide_writel(ide_dev, ide_dev->udmatime[speed - XFER_UDMA_0],
+				S3C_ATA_UDMA_TIME);
+		set_endian_mode(ide_dev, 1);
+	}
+	ide_config_drive_speed(drive, speed);
+}
+
+static void s3c_ide_tune_drive(ide_drive_t *drive, u8 pio)
+{
+	pio = ide_get_best_pio_mode(drive, 255, pio);
+	(void)s3c_ide_tune_chipset(drive, (XFER_PIO_0 + pio));
+}
+
+static irqreturn_t s3c_irq_handler(int irq, void *dev_id)
+{
+	ide_hwif_t *hwif = (ide_hwif_t *)dev_id;
+	struct s3c_ide_device *ide_dev = hwif->hwif_data;
+	u32 reg = ide_readl(ide_dev, S3C_ATA_IRQ);
+	u32 len, addr;
+	u32 stat;
+
+	ide_writel(ide_dev, reg, S3C_ATA_IRQ);
+	if (ide_dev->dma_mode) {
+		len = ide_dev->table[ide_dev->index].len - 1;
+		addr = ide_dev->table[ide_dev->index].addr;
+		if (reg & 0x10)
+			wait_for_bus_state(ide_dev, BS_PAUSER2);
+		else if (reg & 0x08) {
+			wait_for_bus_state(ide_dev, BS_PAUSEW);
+			ide_writel(ide_dev, len, S3C_ATA_TBUF_SIZE);
+			ide_writel(ide_dev, addr, S3C_ATA_TBUF_START);
+		} else
+			return 1;
+
+		ide_writel(ide_dev, len, S3C_ATA_SBUF_SIZE);
+		ide_writel(ide_dev, addr, S3C_ATA_SBUF_START);
+
+		if (ide_dev->queue_size == ++ide_dev->index)
+			ide_dev->dma_mode = 0;
+
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+		return 1;
+	}
+
+	stat = ide_readl(ide_dev, S3C_BUS_FIFO_STATUS) >> 16;
+	if (stat == BS_PAUSER2)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+	else if (stat == BS_PAUSEW)
+		ide_writel(ide_dev, CMD_CONTINUE, S3C_ATA_CMD);
+
+	return ide_intr(irq, dev_id);
+}
+
+static void setup_timing_value(struct s3c_ide_device *ide_dev, u32 clk_rate)
+{
+	u32 t1, t2, teoc, i;
+	u32 uTdvh1, uTdvs, uTrp, uTss, uTackenv;
+	ulong cycle_time = (uint)(1000000000 / clk_rate);
+
+	/* transfer timing for PIO mode */
+	uint pio_t1[5] = { 70, 50, 30, 30, 30 };
+	uint pio_t2[5] = { 290, 290, 290, 80, 70 };
+	uint pio_teoc[5] = { 20, 20, 10, 10, 10 };
+
+	/* transfer timing for UDMA mode */
+	uint uUdmaTdvh[5] = { 50, 32, 29, 25, 24 };
+	uint uUdmaTdvs[5] = { 70, 48, 31, 20, 7 };
+	uint uUdmaTrp[5] = { 160, 125, 100, 100, 100 };
+	uint uUdmaTss[5] = { 50, 50, 50, 50, 50 };
+	uint uUdmaTackenvMin[5] = { 20, 20, 20, 20, 20 };
+
+	/* calculate pio_time register value for all PIO modes */
+	for (i = 0; i < 5; i++) {
+		t1 = (pio_t1[i] / cycle_time) & 0x0f;
+		t2 = (pio_t2[i] / cycle_time) & 0xff;
+		teoc = (pio_teoc[i] / cycle_time) & 0xff;
+		ide_dev->piotime[i] = (teoc << 12) | (t2 << 4) | t1;
+	}
+
+	/* calculate udma_time register value for all udma modes */
+	for (i = 0; i < 5; i++) {
+		uTdvh1 = (uUdmaTdvh[i] / cycle_time) & 0x0f;
+		uTdvs = (uUdmaTdvs[i] / cycle_time) & 0xff;
+		uTrp = (uUdmaTrp[i] / cycle_time) & 0xff;
+		uTss = (uUdmaTss[i] / cycle_time) & 0x0f;
+		uTackenv = (uUdmaTackenvMin[i] / cycle_time) & 0x0f;
+		ide_dev->udmatime[i] = (uTdvh1 << 24) | (uTdvs << 16) |
+			(uTrp << 8) | (uTss << 4) | uTackenv;
+	}
+}
+
+static void change_mode_to_ata(struct s3c_ide_device *ide_dev)
+{
+	ide_writel(ide_dev, ide_readl(ide_dev,
+		S3C_CFATA_MUX) | S3C_CFATA_MUX_TRUEIDE,	S3C_CFATA_MUX);
+}
+
+static void init_ide_device(struct s3c_ide_device *ide_dev)
+{
+	change_mode_to_ata(ide_dev);
+	set_endian_mode(ide_dev, 1);
+	set_ata_enable(ide_dev, 1);
+}
+
+static void ide_setup_ports(struct ide_hw *hw, struct s3c_ide_device *ide_dev)
+{
+	int i;
+	unsigned long *ata_regs = hw->io_ports_array;
+
+	/* S3C IDE controller does not include irq_addr port */
+	for (i = 0; i < IDE_NR_PORTS-1; i++)
+		*ata_regs++ = (ulong)ide_dev->regbase +
+				S3C_ATA_PIO_DTR + (i << 2);
+}
+
+static u8 s3c_cable_detect(ide_hwif_t *hwif)
+{
+	return ATA_CBL_PATA80;
+}
+
+static const struct ide_port_ops s3c_ide_port_ops = {
+	.set_pio_mode = s3c_ide_tune_drive,
+	.set_dma_mode = s3c_ide_tune_chipset,
+	.cable_detect = s3c_cable_detect,
+};
+
+static const struct ide_tp_ops s3c_ide_tp_ops = {
+	.exec_command   = s3c_ide_exec_command,
+	.read_status    = s3c_ide_read_status,
+	.read_altstatus = s3c_ide_read_altstatus,
+	.write_devctl   = s3c_ide_write_devctl,
+	.dev_select     = s3c_ide_dev_select,
+	.tf_load        = s3c_ide_tf_load,
+	.tf_read        = s3c_ide_tf_read,
+	.input_data     = s3c_ide_input_data,
+	.output_data    = s3c_ide_output_data,
+};
+
+static const struct ide_dma_ops s3c_ide_dma_ops = {
+	.dma_host_set 	= s3c_ide_dma_host_set,
+	.dma_setup 	= s3c_ide_dma_setup,
+	.dma_start 	= s3c_ide_dma_start,
+	.dma_end 	= s3c_ide_dma_end,
+	.dma_test_irq 	= s3c_ide_dma_test_irq,
+	.dma_lost_irq 	= s3c_ide_dma_lostirq,
+};
+
+static const struct ide_port_info s3c_port_info = {
+	.name		= "s3c-ide",
+	.init_dma	= s3c_ide_dma_init,
+	.dma_ops        = &s3c_ide_dma_ops,
+	.port_ops	= &s3c_ide_port_ops,
+	.tp_ops         = &s3c_ide_tp_ops,
+	.chipset	= ide_s3c,
+	.host_flags	= IDE_HFLAG_MMIO | IDE_HFLAG_NO_IO_32BIT |
+				IDE_HFLAG_UNMASK_IRQS,
+	.pio_mask	= ATA_PIO4,
+	.udma_mask      = ATA_UDMA4
+};
+
+static int __devinit s3c_ide_probe(struct platform_device *pdev)
+{
+	struct resource	*res;
+	struct s3c_ide_device *ide_dev;
+	struct s3c_ide_platdata *pdata = pdev->dev.platform_data;
+	struct ide_host *host;
+	int ret = 0;
+	struct ide_hw hw, *hws[] = { &hw };
+
+	ide_dev = kzalloc(sizeof(struct s3c_ide_device), GFP_KERNEL);
+	if (!ide_dev) {
+		dev_err(&pdev->dev, "no memory for s3c device instance\n");
+		return -ENOMEM;
+	}
+	ide_dev->pdev = pdev;
+
+	ide_dev->irq = platform_get_irq(pdev, 0);
+	if (ide_dev->irq < 0) {
+		dev_err(&pdev->dev, "could not obtain irq number\n");
+		ret = -ENODEV;
+		goto release_device_mem;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(&pdev->dev,
+			"could not obtain base address of controller\n");
+		ret = -ENODEV;
+		goto release_device_mem;
+	}
+
+	if (!request_mem_region(res->start, res->end - res->start + 1,
+		pdev->name)) {
+		dev_err(&pdev->dev, "could not obtain i/o address\n");
+		ret = -EBUSY;
+		goto release_device_mem;
+	}
+
+	ide_dev->regbase = ioremap(res->start, res->end - res->start + 1);
+	if (ide_dev->regbase == 0) {
+		dev_err(&pdev->dev, "could not remap i/o address\n");
+		ret = -ENOMEM;
+		goto release_mem;
+	}
+
+	ide_dev->ide_clk = clk_get(&pdev->dev, "cfcon");
+	if (IS_ERR(ide_dev->ide_clk)) {
+		dev_err(&pdev->dev, "failed to find clock source\n");
+		ret = PTR_ERR(ide_dev->ide_clk);
+		ide_dev->ide_clk = NULL;
+		goto unmap;
+	}
+
+	if (clk_enable(ide_dev->ide_clk)) {
+		dev_err(&pdev->dev, "failed to enable clock source.\n");
+		ret = -ENODEV;
+		goto clkerr;
+	}
+
+	setup_timing_value(ide_dev, clk_get_rate(ide_dev->ide_clk));
+	if (pdata && (pdata->setup_gpio))
+		pdata->setup_gpio();
+	init_ide_device(ide_dev);
+
+	ide_writel(ide_dev, 0x1f, S3C_ATA_IRQ);
+	ide_writel(ide_dev, 0x1b, S3C_ATA_IRQ_MSK);
+
+	memset(&hw, 0, sizeof(hw));
+	ide_setup_ports(&hw, ide_dev);
+	hw.irq = ide_dev->irq;
+	hw.dev = &pdev->dev;
+
+	host = ide_host_alloc(&s3c_port_info, hws, 1);
+	if (!host) {
+		dev_err(&pdev->dev, "failed to allocate ide host\n");
+		ret = -ENOMEM;
+		goto stop_clk;
+	}
+
+	host->irq_handler = s3c_irq_handler;
+	host->ports[0]->hwif_data = (void *)ide_dev;
+	ide_dev->hwif = host->ports[0];
+	platform_set_drvdata(pdev, host);
+
+	ret = ide_host_register(host, &s3c_port_info, hws);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register ide host\n");
+		goto dealloc_ide_host;
+	}
+
+	return 0;
+
+dealloc_ide_host:
+	ide_host_free(host);
+stop_clk:
+	clk_disable(ide_dev->ide_clk);
+clkerr:
+	clk_put(ide_dev->ide_clk);
+unmap:
+	iounmap(ide_dev->regbase);
+release_mem:
+	release_mem_region(res->start, res->end - res->start + 1);
+release_device_mem:
+	kfree(ide_dev);
+	return ret;
+}
+
+static int __devexit s3c_ide_remove(struct platform_device *pdev)
+{
+	struct ide_host *host = platform_get_drvdata(pdev);
+	struct resource *res;
+	struct s3c_ide_device *ide_dev = host->ports[0]->hwif_data;
+
+	ide_host_remove(host);
+	iounmap(ide_dev->regbase);
+	clk_disable(ide_dev->ide_clk);
+	clk_put(ide_dev->ide_clk);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, res->end - res->start + 1);
+	kfree(ide_dev);
+	return 0;
+}
+
+static struct platform_driver s3c_ide_driver = {
+	.probe		= s3c_ide_probe,
+	.remove		= __devexit_p(s3c_ide_remove),
+	.driver = {
+		.name	= "s3c-ide",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init s3c_ide_init(void)
+{
+	return platform_driver_register(&s3c_ide_driver);
+}
+
+static void __exit s3c_ide_exit(void)
+{
+	platform_driver_unregister(&s3c_ide_driver);
+}
+
+module_init(s3c_ide_init);
+module_exit(s3c_ide_exit);
+
+MODULE_DESCRIPTION("Samsung S3C IDE Controller Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-cfcon");
diff --git a/include/linux/ide.h b/include/linux/ide.h
index edc93a6..ff9eb0e 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -164,7 +164,7 @@ enum {		ide_unknown,	ide_generic,	ide_pci,
 		ide_cmd640,	ide_dtc2278,	ide_ali14xx,
 		ide_qd65xx,	ide_umc8672,	ide_ht6560b,
 		ide_4drives,	ide_pmac,	ide_acorn,
-		ide_au1xxx,	ide_palm3710
+		ide_au1xxx,	ide_palm3710,	ide_s3c
 };

 typedef u8 hwif_chipset_t;
-- 
1.5.3.4

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-11-01 12:34 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-10-08  6:59 [PATCH] S3C: ide: Add Samsung S3C IDE controller driver thomas.ab
2009-10-09  0:59 ` jassi brar
2009-10-12  8:41   ` Thomas Abraham
2009-10-12  8:55 ` Marc Zyngier
2009-11-01  5:07 Thomas Abraham
2009-11-01  5:07 ` Thomas Abraham
2009-11-01  6:53 ` Jeff Garzik
2009-11-01  6:53   ` Jeff Garzik
2009-11-01 10:03   ` David Miller
2009-11-01 10:03     ` David Miller
2009-11-01 12:34 ` Alan Cox
2009-11-01 12:34   ` Alan Cox

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.