diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.26/0019-s3c_mci.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches-2.6.26/0019-s3c_mci.patch.patch | 1704 |
1 files changed, 0 insertions, 1704 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.26/0019-s3c_mci.patch.patch b/target/linux/s3c24xx/patches-2.6.26/0019-s3c_mci.patch.patch deleted file mode 100755 index 8f033d566..000000000 --- a/target/linux/s3c24xx/patches-2.6.26/0019-s3c_mci.patch.patch +++ /dev/null @@ -1,1704 +0,0 @@ -From ed9a479d4e02e6415e4242211266845013ecad77 Mon Sep 17 00:00:00 2001 -From: mokopatches <mokopatches@openmoko.org> -Date: Wed, 16 Jul 2008 14:44:50 +0100 -Subject: [PATCH] s3c_mci.patch - This is a MMC/SD driver for the Samsung S3C24xx SD/MMC controller, originally - developed years ago by Thomas Kleffel <tk@maintech.de>. - -Due to time restraints, he had no time to further maintain the driver and -follow the mainline Linux changes in the SD/MMC stack. - -With his authorization, I have taken over the task of making it compliant to -the current mainline SD/MMC API and take care of the mainline kernel merge. - -After a potential kernel inclusion, we would co-maintain the driver. - -Acked-by: Thomas Kleffel <tk@maintech.de> -Signed-off-by: Harald Welte <laforge@gnumonks.org> ---- - arch/arm/mach-s3c2412/s3c2412.c | 3 + - arch/arm/mach-s3c2440/s3c2440.c | 3 + - arch/arm/mach-s3c2442/s3c2442.c | 4 + - drivers/mmc/host/Kconfig | 11 + - drivers/mmc/host/Makefile | 2 +- - drivers/mmc/host/s3cmci.c | 1419 +++++++++++++++++++++++++++++++ - drivers/mmc/host/s3cmci.h | 69 ++ - include/asm-arm/arch-s3c2410/mci.h | 13 + - include/asm-arm/arch-s3c2410/regs-sdi.h | 22 +- - 9 files changed, 1542 insertions(+), 4 deletions(-) - create mode 100644 drivers/mmc/host/s3cmci.c - create mode 100644 drivers/mmc/host/s3cmci.h - create mode 100644 include/asm-arm/arch-s3c2410/mci.h - -diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c -index 98a0de9..ee4436d 100644 ---- a/arch/arm/mach-s3c2412/s3c2412.c -+++ b/arch/arm/mach-s3c2412/s3c2412.c -@@ -216,5 +216,8 @@ int __init s3c2412_init(void) - { - printk("S3C2412: Initialising architecture\n"); - -+ /* make sure SD/MMC driver can distinguish 2412 from 2410 */ -+ s3c_device_sdi.name = "s3c2412-sdi"; -+ - return sysdev_register(&s3c2412_sysdev); - } -diff --git a/arch/arm/mach-s3c2440/s3c2440.c b/arch/arm/mach-s3c2440/s3c2440.c -index 90e1da6..6c18cbd 100644 ---- a/arch/arm/mach-s3c2440/s3c2440.c -+++ b/arch/arm/mach-s3c2440/s3c2440.c -@@ -46,6 +46,9 @@ int __init s3c2440_init(void) - s3c_device_wdt.resource[1].start = IRQ_S3C2440_WDT; - s3c_device_wdt.resource[1].end = IRQ_S3C2440_WDT; - -+ /* make sure SD/MMC driver can distinguish 2440 from 2410 */ -+ s3c_device_sdi.name = "s3c2440-sdi"; -+ - /* register our system device for everything else */ - - return sysdev_register(&s3c2440_sysdev); -diff --git a/arch/arm/mach-s3c2442/s3c2442.c b/arch/arm/mach-s3c2442/s3c2442.c -index fbf8264..fbfb330 100644 ---- a/arch/arm/mach-s3c2442/s3c2442.c -+++ b/arch/arm/mach-s3c2442/s3c2442.c -@@ -21,6 +21,7 @@ - - #include <asm/plat-s3c24xx/s3c2442.h> - #include <asm/plat-s3c24xx/cpu.h> -+#include <asm/plat-s3c24xx/devs.h> - - static struct sys_device s3c2442_sysdev = { - .cls = &s3c2442_sysclass, -@@ -30,5 +31,8 @@ int __init s3c2442_init(void) - { - printk("S3C2442: Initialising architecture\n"); - -+ /* make sure SD/MMC driver can distinguish 2440 from 2410 */ -+ s3c_device_sdi.name = "s3c2440-sdi"; -+ - return sysdev_register(&s3c2442_sysdev); - } -diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig -index dead617..fd59856 100644 ---- a/drivers/mmc/host/Kconfig -+++ b/drivers/mmc/host/Kconfig -@@ -130,3 +130,14 @@ config MMC_SPI - - If unsure, or if your system has no SPI master driver, say N. - -+config MMC_S3C -+ tristate "Samsung S3C24xx SD/MMC Card Interface support" -+ depends on ARCH_S3C2410 && MMC -+ help -+ This selects a driver for the MCI interface found in -+ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. -+ If you have a board based on one of those and a MMC/SD -+ slot, say Y or M here. -+ -+ If unsure, say N. -+ -diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile -index 3877c87..071d256 100644 ---- a/drivers/mmc/host/Makefile -+++ b/drivers/mmc/host/Makefile -@@ -17,4 +17,4 @@ obj-$(CONFIG_MMC_OMAP) += omap.o - obj-$(CONFIG_MMC_AT91) += at91_mci.o - obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o - obj-$(CONFIG_MMC_SPI) += mmc_spi.o -- -+obj-$(CONFIG_MMC_S3C) += s3cmci.o -diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c -new file mode 100644 -index 0000000..edba055 ---- /dev/null -+++ b/drivers/mmc/host/s3cmci.c -@@ -0,0 +1,1419 @@ -+/* -+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver -+ * -+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de> -+ * Copyright (C) 2007 Harald Welte <laforge@gnumonks.org> -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+#include <linux/module.h> -+#include <linux/dma-mapping.h> -+#include <linux/clk.h> -+#include <linux/mmc/host.h> -+#include <linux/platform_device.h> -+#include <linux/irq.h> -+ -+#include <asm/dma.h> -+#include <asm/dma-mapping.h> -+ -+#include <asm/io.h> -+#include <asm/arch/regs-sdi.h> -+#include <asm/arch/regs-gpio.h> -+#include <asm/arch/mci.h> -+#include <asm/arch/dma.h> -+ -+#include "s3cmci.h" -+ -+#define DRIVER_NAME "s3c-mci" -+ -+enum dbg_channels { -+ dbg_err = (1 << 0), -+ dbg_debug = (1 << 1), -+ dbg_info = (1 << 2), -+ dbg_irq = (1 << 3), -+ dbg_sg = (1 << 4), -+ dbg_dma = (1 << 5), -+ dbg_pio = (1 << 6), -+ dbg_fail = (1 << 7), -+ dbg_conf = (1 << 8), -+}; -+ -+static const int dbgmap_err = dbg_err | dbg_fail; -+static const int dbgmap_info = dbg_info | dbg_conf; -+static const int dbgmap_debug = dbg_debug; -+ -+#define dbg(host, channels, args...) \ -+ do { \ -+ if (dbgmap_err & channels) \ -+ dev_err(&host->pdev->dev, args); \ -+ else if (dbgmap_info & channels) \ -+ dev_info(&host->pdev->dev, args);\ -+ else if (dbgmap_debug & channels) \ -+ dev_dbg(&host->pdev->dev, args); \ -+ } while (0) -+ -+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) -+ -+static struct s3c2410_dma_client s3cmci_dma_client = { -+ .name = "s3c-mci", -+}; -+ -+static void finalize_request(struct s3cmci_host *host); -+static void s3cmci_send_request(struct mmc_host *mmc); -+static void s3cmci_reset(struct s3cmci_host *host); -+ -+#ifdef CONFIG_MMC_DEBUG -+static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix) -+{ -+ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; -+ u32 datcon, datcnt, datsta, fsta, imask; -+ -+ con = readl(host->base + S3C2410_SDICON); -+ pre = readl(host->base + S3C2410_SDIPRE); -+ cmdarg = readl(host->base + S3C2410_SDICMDARG); -+ cmdcon = readl(host->base + S3C2410_SDICMDCON); -+ cmdsta = readl(host->base + S3C2410_SDICMDSTAT); -+ r0 = readl(host->base + S3C2410_SDIRSP0); -+ r1 = readl(host->base + S3C2410_SDIRSP1); -+ r2 = readl(host->base + S3C2410_SDIRSP2); -+ r3 = readl(host->base + S3C2410_SDIRSP3); -+ timer = readl(host->base + S3C2410_SDITIMER); -+ bsize = readl(host->base + S3C2410_SDIBSIZE); -+ datcon = readl(host->base + S3C2410_SDIDCON); -+ datcnt = readl(host->base + S3C2410_SDIDCNT); -+ datsta = readl(host->base + S3C2410_SDIDSTA); -+ fsta = readl(host->base + S3C2410_SDIFSTA); -+ imask = readl(host->base + host->sdiimsk); -+ -+ dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n", -+ prefix, con, pre, timer); -+ -+ dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n", -+ prefix, cmdcon, cmdarg, cmdsta); -+ -+ dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]" -+ " DSTA:[%08x] DCNT:[%08x]\n", -+ prefix, datcon, fsta, datsta, datcnt); -+ -+ dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]" -+ " R2:[%08x] R3:[%08x]\n", -+ prefix, r0, r1, r2, r3); -+} -+ -+static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd, -+ int stop) -+{ -+ snprintf(host->dbgmsg_cmd, 300, -+ "#%u%s op:CMD%d arg:0x%08x flags:0x08%x retries:%u", -+ host->ccnt, (stop?" (STOP)":""), cmd->opcode, -+ cmd->arg, cmd->flags, cmd->retries); -+ -+ if (cmd->data) { -+ snprintf(host->dbgmsg_dat, 300, -+ "#%u bsize:%u blocks:%u bytes:%u", -+ host->dcnt, cmd->data->blksz, -+ cmd->data->blocks, -+ cmd->data->blocks * cmd->data->blksz); -+ } else { -+ host->dbgmsg_dat[0] = '\0'; -+ } -+} -+ -+static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd, -+ int fail) -+{ -+ unsigned int dbglvl = fail?dbg_fail:dbg_debug; -+ -+ if (!cmd) -+ return; -+ -+ if (cmd->error == 0) -+ dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n", -+ host->dbgmsg_cmd, cmd->resp[0]); -+ else -+ dbg(host, dbglvl, "CMD[FAIL(%d)] %s Status:%s\n", -+ cmd->error, host->dbgmsg_cmd, host->status); -+ -+ if (!cmd->data) -+ return; -+ -+ if (cmd->data->error == 0) -+ dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat); -+ else -+ dbg(host, dbglvl, "DAT[FAIL(%d)] %s DCNT:0x%08x\n", -+ cmd->data->error, host->dbgmsg_dat, -+ readl(host->base + S3C2410_SDIDCNT)); -+} -+#endif /* CONFIG_MMC_DEBUG */ -+ -+static inline u32 enable_imask(struct s3cmci_host *host, u32 imask) -+{ -+ u32 newmask; -+ -+ newmask = readl(host->base + host->sdiimsk); -+ newmask |= imask; -+ -+ writel(newmask, host->base + host->sdiimsk); -+ -+ return newmask; -+} -+ -+static inline u32 disable_imask(struct s3cmci_host *host, u32 imask) -+{ -+ u32 newmask; -+ -+ newmask = readl(host->base + host->sdiimsk); -+ newmask &= ~imask; -+ -+ writel(newmask, host->base + host->sdiimsk); -+ -+ return newmask; -+} -+ -+static inline void clear_imask(struct s3cmci_host *host) -+{ -+ writel(0, host->base + host->sdiimsk); -+} -+ -+static inline int get_data_buffer(struct s3cmci_host *host, -+ u32 *bytes, u8 **pointer) -+{ -+ struct scatterlist *sg; -+ -+ if (host->pio_active == XFER_NONE) -+ return -EINVAL; -+ -+ if ((!host->mrq) || (!host->mrq->data)) -+ return -EINVAL; -+ -+ if (host->pio_sgptr >= host->mrq->data->sg_len) { -+ dbg(host, dbg_debug, "no more buffers (%i/%i)\n", -+ host->pio_sgptr, host->mrq->data->sg_len); -+ return -EBUSY; -+ } -+ sg = &host->mrq->data->sg[host->pio_sgptr]; -+ -+ *bytes = sg->length; -+ *pointer = page_address(sg_page(sg)) + sg->offset; -+ -+ host->pio_sgptr++; -+ -+ dbg(host, dbg_sg, "new buffer (%i/%i)\n", -+ host->pio_sgptr, host->mrq->data->sg_len); -+ -+ return 0; -+} -+ -+#define FIFO_FILL(host) (readl(host->base + S3C2410_SDIFSTA) & \ -+ S3C2410_SDIFSTA_COUNTMASK) -+#define FIFO_FREE(host) (63 - (readl(host->base + S3C2410_SDIFSTA) \ -+ & S3C2410_SDIFSTA_COUNTMASK)) -+ -+static inline void do_pio_read(struct s3cmci_host *host) -+{ -+ int res; -+ int fifo; -+ void __iomem *from_ptr; -+ -+ /* write real prescaler to host, it might be set slow to fix */ -+ writel(host->prescaler, host->base + S3C2410_SDIPRE); -+ -+ from_ptr = host->base + host->sdidata; -+ -+ while ((fifo = FIFO_FILL(host))) { -+ if (!host->pio_bytes) { -+ res = get_data_buffer(host, &host->pio_bytes, -+ &host->pio_ptr); -+ if (res) { -+ host->pio_active = XFER_NONE; -+ host->complete_what = COMPLETION_FINALIZE; -+ -+ dbg(host, dbg_pio, "pio_read(): " -+ "complete (no more data).\n"); -+ return; -+ } -+ -+ dbg(host, dbg_pio, "pio_read(): new target: " -+ "[%i]@[%p]\n", host->pio_bytes, host->pio_ptr); -+ } -+ -+ dbg(host, dbg_pio, "pio_read(): fifo:[%02i] " -+ "buffer:[%03i] dcnt:[%08X]\n", fifo, host->pio_bytes, -+ readl(host->base + S3C2410_SDIDCNT)); -+ -+ if (fifo > host->pio_bytes) -+ fifo = host->pio_bytes; -+ -+ host->pio_bytes -= fifo; -+ host->pio_count += fifo; -+ -+ /* we might have an unaligned start of data */ -+ while (((unsigned long)host->pio_ptr & 0x03) && fifo) { -+ *(host->pio_ptr++) = readb(host->base + host->sdidata_b); -+ fifo--; -+ } -+ -+ /* and a major chunk of data in the middle */ -+ for (; fifo >= 4; fifo -=4) { -+ *(u32 *) host->pio_ptr = readl(from_ptr); -+ host->pio_ptr+= 4; -+ } -+ -+ /* as well as some non-modulo-four trailer */ -+ while (fifo) { -+ *(host->pio_ptr++) = readb(host->base + host->sdidata_b); -+ fifo--; -+ } -+ } -+ -+ if (!host->pio_bytes) { -+ res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr); -+ if (res) { -+ dbg(host, dbg_pio, "pio_read(): " -+ "complete (no more buffers).\n"); -+ host->pio_active = XFER_NONE; -+ host->complete_what = COMPLETION_FINALIZE; -+ -+ return; -+ } -+ } -+ -+ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF -+ | S3C2410_SDIIMSK_RXFIFOLAST); -+} -+ -+static inline void do_pio_write(struct s3cmci_host *host) -+{ -+ int res; -+ int fifo; -+ -+ void __iomem *to_ptr; -+ -+ to_ptr = host->base + host->sdidata; -+ -+ while ((fifo = FIFO_FREE(host))) { -+ if (!host->pio_bytes) { -+ res = get_data_buffer(host, &host->pio_bytes, -+ &host->pio_ptr); -+ if (res) { -+ dbg(host, dbg_pio, "pio_write(): " -+ "complete (no more data).\n"); -+ host->pio_active = XFER_NONE; -+ -+ return; -+ } -+ -+ dbg(host, dbg_pio, "pio_write(): " -+ "new source: [%i]@[%p]\n", -+ host->pio_bytes, host->pio_ptr); -+ -+ } -+ -+ if (fifo > host->pio_bytes) -+ fifo = host->pio_bytes; -+ -+ host->pio_bytes -= fifo; -+ host->pio_count += fifo; -+ -+ /* we might have an unaligned start of data */ -+ while (((unsigned long)host->pio_ptr & 0x03) && fifo) { -+ writeb(*(host->pio_ptr++), host->base + host->sdidata_b); -+ fifo--; -+ } -+ -+ /* and a major chunk of data in the middle */ -+ for (; fifo >= 4; fifo -=4) { -+ writel(*(u32 *) host->pio_ptr, to_ptr); -+ host->pio_ptr += 4; -+ } -+ -+ /* as well as some non-modulo-four trailer */ -+ while (fifo) { -+ writeb(*(host->pio_ptr++), host->base + host->sdidata_b); -+ fifo--; -+ } -+ } -+ -+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); -+} -+ -+static void pio_tasklet(unsigned long data) -+{ -+ struct s3cmci_host *host = (struct s3cmci_host *) data; -+ -+ disable_irq(host->irq); -+ -+ if (host->pio_active == XFER_WRITE) -+ do_pio_write(host); -+ -+ if (host->pio_active == XFER_READ) -+ do_pio_read(host); -+ -+ if (host->complete_what == COMPLETION_FINALIZE) { -+ clear_imask(host); -+ if (host->pio_active != XFER_NONE) { -+ dbg(host, dbg_err, "unfinished %s " -+ "- pio_count:[%u] pio_bytes:[%u]\n", -+ (host->pio_active == XFER_READ)?"read":"write", -+ host->pio_count, host->pio_bytes); -+ -+ host->mrq->data->error = -EIO; -+ } -+ -+ finalize_request(host); -+ } else -+ enable_irq(host->irq); -+} -+ -+/* -+ * ISR for SDI Interface IRQ -+ * Communication between driver and ISR works as follows: -+ * host->mrq points to current request -+ * host->complete_what tells ISR when the request is considered done -+ * COMPLETION_CMDSENT when the command was sent -+ * COMPLETION_RSPFIN when a response was received -+ * COMPLETION_XFERFINISH when the data transfer is finished -+ * COMPLETION_XFERFINISH_RSPFIN both of the above. -+ * host->complete_request is the completion-object the driver waits for -+ * -+ * 1) Driver sets up host->mrq and host->complete_what -+ * 2) Driver prepares the transfer -+ * 3) Driver enables interrupts -+ * 4) Driver starts transfer -+ * 5) Driver waits for host->complete_rquest -+ * 6) ISR checks for request status (errors and success) -+ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error -+ * 7) ISR completes host->complete_request -+ * 8) ISR disables interrupts -+ * 9) Driver wakes up and takes care of the request -+ * -+ * Note: "->error"-fields are expected to be set to 0 before the request -+ * was issued by mmc.c - therefore they are only set, when an error -+ * contition comes up -+ */ -+ -+static irqreturn_t s3cmci_irq(int irq, void *dev_id) -+{ -+ struct s3cmci_host *host; -+ struct mmc_command *cmd; -+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk; -+ u32 mci_cclear, mci_dclear; -+ unsigned long iflags; -+ int cardint = 0; -+ -+ host = (struct s3cmci_host *)dev_id; -+ -+ spin_lock_irqsave(&host->complete_lock, iflags); -+ -+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT); -+ mci_dsta = readl(host->base + S3C2410_SDIDSTA); -+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT); -+ mci_fsta = readl(host->base + S3C2410_SDIFSTA); -+ mci_imsk = readl(host->base + host->sdiimsk); -+ mci_cclear = 0; -+ mci_dclear = 0; -+ -+ if ((host->complete_what == COMPLETION_NONE) || -+ (host->complete_what == COMPLETION_FINALIZE)) { -+ host->status = "nothing to complete"; -+ clear_imask(host); -+ goto irq_out; -+ } -+ -+ if (!host->mrq) { -+ host->status = "no active mrq"; -+ clear_imask(host); -+ goto irq_out; -+ } -+ -+ cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd; -+ -+ if (!cmd) { -+ host->status = "no active cmd"; -+ clear_imask(host); -+ goto irq_out; -+ } -+ -+ if (!host->dodma) { -+ if ((host->pio_active == XFER_WRITE) && -+ (mci_fsta & S3C2410_SDIFSTA_TFDET)) { -+ -+ disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); -+ tasklet_schedule(&host->pio_tasklet); -+ host->status = "pio tx"; -+ } -+ -+ if ((host->pio_active == XFER_READ) && -+ (mci_fsta & S3C2410_SDIFSTA_RFDET)) { -+ -+ disable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | -+ S3C2410_SDIIMSK_RXFIFOLAST); -+ -+ tasklet_schedule(&host->pio_tasklet); -+ host->status = "pio rx"; -+ } -+ } -+ -+ if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { -+ cmd->error = -ETIMEDOUT; -+ host->status = "error: command timeout"; -+ goto fail_transfer; -+ } -+ -+ if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) { -+ if (host->complete_what == COMPLETION_CMDSENT) { -+ host->status = "ok: command sent"; -+ goto close_transfer; -+ } -+ -+ mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT; -+ } -+ -+ if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) { -+ if (cmd->flags & MMC_RSP_CRC) { -+ if (host->mrq->cmd->flags & MMC_RSP_136) { -+ dbg(host, dbg_irq, "fixup for chip bug: " -+ "ignore CRC fail with long rsp\n"); -+ } else { -+ cmd->error = -EILSEQ; -+ host->status = "error: bad command crc"; -+ goto fail_transfer; -+ } -+ } -+ -+ mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL; -+ } -+ -+ if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) { -+ if (host->complete_what == COMPLETION_RSPFIN) { -+ host->status = "ok: command response received"; -+ goto close_transfer; -+ } -+ -+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) -+ host->complete_what = COMPLETION_XFERFINISH; -+ -+ mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN; -+ } -+ -+ /* errors handled after this point are only relevant -+ when a data transfer is in progress */ -+ -+ if (!cmd->data) -+ goto clear_status_bits; -+ -+ /* Check for FIFO failure */ -+ if (host->is2440) { -+ if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) { -+ host->mrq->data->error = -EIO; -+ host->status = "error: 2440 fifo failure"; -+ goto fail_transfer; -+ } -+ } else { -+ if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) { -+ cmd->data->error = -EIO; -+ host->status = "error: fifo failure"; -+ goto fail_transfer; -+ } -+ } -+ -+ if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) { -+ cmd->data->error = -EILSEQ; -+ host->status = "error: bad data crc (outgoing)"; -+ goto fail_transfer; -+ } -+ -+ if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) { -+ cmd->data->error = -EIO; -+ host->status = "error: bad data crc (incoming)"; -+ goto fail_transfer; -+ } -+ -+ if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) { -+ cmd->data->error = -ETIMEDOUT; -+ host->status = "error: data timeout"; -+ goto fail_transfer; -+ } -+ -+ if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) { -+ if (host->complete_what == COMPLETION_XFERFINISH) { -+ host->status = "ok: data transfer completed"; -+ goto close_transfer; -+ } -+ -+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) -+ host->complete_what = COMPLETION_RSPFIN; -+ -+ mci_dclear |= S3C2410_SDIDSTA_XFERFINISH; -+ } -+ -+ if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) { -+ cardint = 1; -+ mci_dclear |= S3C2410_SDIDSTA_SDIOIRQDETECT; -+ } -+ -+clear_status_bits: -+ writel(mci_cclear, host->base + S3C2410_SDICMDSTAT); -+ writel(mci_dclear, host->base + S3C2410_SDIDSTA); -+ -+ goto irq_out; -+ -+fail_transfer: -+ host->pio_active = XFER_NONE; -+ -+close_transfer: -+ host->complete_what = COMPLETION_FINALIZE; -+ -+ clear_imask(host); -+ tasklet_schedule(&host->pio_tasklet); -+ -+ goto irq_out; -+ -+irq_out: -+ dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x " -+ "fsta:0x%08x dcnt:0x%08x status:%s.\n", -+ mci_csta, mci_dsta, mci_fsta, -+ mci_dcnt, host->status); -+ -+ spin_unlock_irqrestore(&host->complete_lock, iflags); -+ -+ /* We have to delay this as it calls back into the driver */ -+ if (cardint) -+ mmc_signal_sdio_irq(host->mmc); -+ -+ return IRQ_HANDLED; -+ -+} -+ -+/* -+ * ISR for the CardDetect Pin -+*/ -+ -+static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) -+{ -+ struct s3cmci_host *host = (struct s3cmci_host *)dev_id; -+ -+ dbg(host, dbg_irq, "card detect\n"); -+ -+ mmc_detect_change(host->mmc, 500); -+ -+ return IRQ_HANDLED; -+} -+ -+void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id, -+ int size, enum s3c2410_dma_buffresult result) -+{ -+ unsigned long iflags; -+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt; -+ struct s3cmci_host *host = (struct s3cmci_host *)buf_id; -+ -+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT); -+ mci_dsta = readl(host->base + S3C2410_SDIDSTA); -+ mci_fsta = readl(host->base + S3C2410_SDIFSTA); -+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT); -+ -+ if ((!host->mrq) || (!host->mrq) || (!host->mrq->data)) -+ return; -+ -+ if (!host->dmatogo) -+ return; -+ -+ spin_lock_irqsave(&host->complete_lock, iflags); -+ -+ if (result != S3C2410_RES_OK) { -+ dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x " -+ "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n", -+ mci_csta, mci_dsta, mci_fsta, -+ mci_dcnt, result, host->dmatogo); -+ -+ goto fail_request; -+ } -+ -+ host->dmatogo--; -+ if (host->dmatogo) { -+ dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] " -+ "DCNT:[%08x] toGo:%u\n", -+ size, mci_dsta, mci_dcnt, host->dmatogo); -+ -+ goto out; -+ } -+ -+ dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", -+ size, mci_dsta, mci_dcnt); -+ -+ host->complete_what = COMPLETION_FINALIZE; -+ -+out: -+ tasklet_schedule(&host->pio_tasklet); -+ spin_unlock_irqrestore(&host->complete_lock, iflags); -+ return; -+ -+ -+fail_request: -+ host->mrq->data->error = -EIO; -+ host->complete_what = COMPLETION_FINALIZE; -+ writel(0, host->base + host->sdiimsk); -+ goto out; -+ -+} -+ -+static void finalize_request(struct s3cmci_host *host) -+{ -+ struct mmc_request *mrq = host->mrq; -+ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd; -+ int debug_as_failure = 0; -+ -+ if (host->complete_what != COMPLETION_FINALIZE) -+ return; -+ -+ if (!mrq) -+ return; -+ -+ if (cmd->data && (cmd->error == 0) && -+ (cmd->data->error == 0)) { -+ -+ if (host->dodma && (!host->dma_complete)) { -+ dbg(host, dbg_dma, "DMA Missing!\n"); -+ return; -+ } -+ } -+ -+ /* Read response */ -+ cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0); -+ cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1); -+ cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2); -+ cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3); -+ -+ /* reset clock speed, as it could still be set low for */ -+ writel(host->prescaler, host->base + S3C2410_SDIPRE); -+ -+ if (cmd->error) -+ debug_as_failure = 1; -+ -+ if (cmd->data && cmd->data->error) -+ debug_as_failure = 1; -+ -+#ifdef CONFIG_MMC_DEBUG -+ dbg_dumpcmd(host, cmd, debug_as_failure); -+#endif -+ /* Cleanup controller */ -+ writel(0, host->base + S3C2410_SDICMDARG); -+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); -+ writel(0, host->base + S3C2410_SDICMDCON); -+ writel(0, host->base + host->sdiimsk); -+ -+ if (cmd->data && cmd->error) -+ cmd->data->error = cmd->error; -+ -+ if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) { -+ host->cmd_is_stop = 1; -+ s3cmci_send_request(host->mmc); -+ return; -+ } -+ -+ /* If we have no data transfer we are finished here */ -+ if (!mrq->data) -+ goto request_done; -+ -+ /* Calulate the amout of bytes transfer, but only if there was -+ * no error */ -+ if (mrq->data->error == 0) -+ mrq->data->bytes_xfered = -+ (mrq->data->blocks * mrq->data->blksz); -+ else -+ mrq->data->bytes_xfered = 0; -+ -+ /* If we had an error while transfering data we flush the -+ * DMA channel and the fifo to clear out any garbage */ -+ if (mrq->data->error) { -+ if (host->dodma) -+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); -+ -+ if (host->is2440) { -+ /* Clear failure register and reset fifo */ -+ writel(S3C2440_SDIFSTA_FIFORESET | -+ S3C2440_SDIFSTA_FIFOFAIL, -+ host->base + S3C2410_SDIFSTA); -+ } else { -+ u32 mci_con; -+ -+ /* reset fifo */ -+ mci_con = readl(host->base + S3C2410_SDICON); -+ mci_con |= S3C2410_SDICON_FIFORESET; -+ -+ writel(mci_con, host->base + S3C2410_SDICON); -+ } -+ } -+ -+request_done: -+ host->complete_what = COMPLETION_NONE; -+ host->mrq = NULL; -+ mmc_request_done(host->mmc, mrq); -+} -+ -+ -+void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source) -+{ -+ static int setup_ok; -+ static enum s3c2410_dmasrc last_source = -1; -+ -+ if (last_source == source) -+ return; -+ -+ last_source = source; -+ -+ s3c2410_dma_devconfig(host->dma, source, 3, -+ host->mem->start + host->sdidata); -+ -+ if (!setup_ok) { -+ s3c2410_dma_config(host->dma, 4, -+ (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI)); -+ s3c2410_dma_set_buffdone_fn(host->dma, -+ s3cmci_dma_done_callback); -+ s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); -+ setup_ok = 1; -+ } -+} -+ -+static void s3cmci_send_command(struct s3cmci_host *host, -+ struct mmc_command *cmd) -+{ -+ u32 ccon, imsk; -+ -+ imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT | -+ S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT | -+ S3C2410_SDIIMSK_RESPONSECRC; -+ -+ enable_imask(host, imsk); -+ -+ if (cmd->data) -+ host->complete_what = COMPLETION_XFERFINISH_RSPFIN; -+ else if (cmd->flags & MMC_RSP_PRESENT) -+ host->complete_what = COMPLETION_RSPFIN; -+ else -+ host->complete_what = COMPLETION_CMDSENT; -+ -+ writel(cmd->arg, host->base + S3C2410_SDICMDARG); -+ -+ ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX; -+ ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; -+ -+ if (cmd->flags & MMC_RSP_PRESENT) -+ ccon |= S3C2410_SDICMDCON_WAITRSP; -+ -+ if (cmd->flags & MMC_RSP_136) -+ ccon |= S3C2410_SDICMDCON_LONGRSP; -+ -+ writel(ccon, host->base + S3C2410_SDICMDCON); -+} -+ -+static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data) -+{ -+ u32 dcon, imsk, stoptries = 3; -+ -+ /* write DCON register */ -+ -+ if (!data) { -+ writel(0, host->base + S3C2410_SDIDCON); -+ return 0; -+ } -+ -+ while (readl(host->base + S3C2410_SDIDSTA) & -+ (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { -+ -+ dbg(host, dbg_err, -+ "mci_setup_data() transfer stillin progress.\n"); -+ -+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON); -+ s3cmci_reset(host); -+ -+ if (0 == (stoptries--)) { -+#ifdef CONFIG_MMC_DEBUG -+ dbg_dumpregs(host, "DRF"); -+#endif -+ -+ return -EINVAL; -+ } -+ } -+ -+ dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK; -+ -+ if (host->dodma) -+ dcon |= S3C2410_SDIDCON_DMAEN; -+ -+ if (host->bus_width == MMC_BUS_WIDTH_4) -+ dcon |= S3C2410_SDIDCON_WIDEBUS; -+ -+ if (!(data->flags & MMC_DATA_STREAM)) -+ dcon |= S3C2410_SDIDCON_BLOCKMODE; -+ -+ if (data->flags & MMC_DATA_WRITE) { -+ dcon |= S3C2410_SDIDCON_TXAFTERRESP; -+ dcon |= S3C2410_SDIDCON_XFER_TXSTART; -+ } -+ -+ if (data->flags & MMC_DATA_READ) { -+ dcon |= S3C2410_SDIDCON_RXAFTERCMD; -+ dcon |= S3C2410_SDIDCON_XFER_RXSTART; -+ } -+ -+ if (host->is2440) { -+ dcon |= S3C2440_SDIDCON_DS_WORD; -+ dcon |= S3C2440_SDIDCON_DATSTART; -+ } -+ -+ writel(dcon, host->base + S3C2410_SDIDCON); -+ -+ /* write BSIZE register */ -+ -+ writel(data->blksz, host->base + S3C2410_SDIBSIZE); -+ -+ /* add to IMASK register */ -+ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | -+ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; -+ -+ enable_imask(host, imsk); -+ -+ /* write TIMER register */ -+ -+ if (host->is2440) { -+ writel(0x007FFFFF, host->base + S3C2410_SDITIMER); -+ } else { -+ writel(0x0000FFFF, host->base + S3C2410_SDITIMER); -+ -+ /* FIX: set slow clock to prevent timeouts on read */ -+ if (data->flags & MMC_DATA_READ) -+ writel(0xFF, host->base + S3C2410_SDIPRE); -+ } -+ -+ return 0; -+} -+ -+static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) -+{ -+ int rw = (data->flags & MMC_DATA_WRITE)?1:0; -+ -+ if (rw != ((data->flags & MMC_DATA_READ)?0:1)) -+ return -EINVAL; -+ -+ host->pio_sgptr = 0; -+ host->pio_bytes = 0; -+ host->pio_count = 0; -+ host->pio_active = rw?XFER_WRITE:XFER_READ; -+ -+ if (rw) { -+ do_pio_write(host); -+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); -+ } else { -+ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF -+ | S3C2410_SDIIMSK_RXFIFOLAST); -+ } -+ -+ return 0; -+} -+ -+static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) -+{ -+ int dma_len, i; -+ -+ int rw = (data->flags & MMC_DATA_WRITE)?1:0; -+ -+ if (rw != ((data->flags & MMC_DATA_READ)?0:1)) -+ return -EINVAL; -+ -+ s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW); -+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); -+ -+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, -+ (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE); -+ -+ -+ if (dma_len == 0) -+ return -ENOMEM; -+ -+ host->dma_complete = 0; -+ host->dmatogo = dma_len; -+ -+ for (i = 0; i < dma_len; i++) { -+ int res; -+ -+ dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i, -+ sg_dma_address(&data->sg[i]), -+ sg_dma_len(&data->sg[i])); -+ -+ res = s3c2410_dma_enqueue(host->dma, (void *) host, -+ sg_dma_address(&data->sg[i]), -+ sg_dma_len(&data->sg[i])); -+ -+ if (res) { -+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); -+ return -EBUSY; -+ } -+ } -+ -+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); -+ -+ return 0; -+} -+ -+static void s3cmci_send_request(struct mmc_host *mmc) -+{ -+ struct s3cmci_host *host = mmc_priv(mmc); -+ struct mmc_request *mrq = host->mrq; -+ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd; -+ -+ host->ccnt++; -+#ifdef CONFIG_MMC_DEBUG -+ prepare_dbgmsg(host, cmd, host->cmd_is_stop); -+#endif -+ /* clear command, data and fifo status registers; -+ * Fifo clear only necessary on 2440, but doesn't hurt on 2410 */ -+ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT); -+ writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA); -+ writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA); -+ -+ if (cmd->data) { -+ int res; -+ res = s3cmci_setup_data(host, cmd->data); -+ -+ host->dcnt++; -+ -+ if (res) { -+ cmd->error = -EIO; -+ cmd->data->error = -EIO; -+ -+ mmc_request_done(mmc, mrq); -+ return; -+ } -+ -+ -+ if (host->dodma) -+ res = s3cmci_prepare_dma(host, cmd->data); -+ else -+ res = s3cmci_prepare_pio(host, cmd->data); -+ -+ if (res) { -+ cmd->error = -EIO; -+ cmd->data->error = -EIO; -+ -+ mmc_request_done(mmc, mrq); -+ return; -+ } -+ -+ } -+ -+ s3cmci_send_command(host, cmd); -+ enable_irq(host->irq); -+} -+ -+static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq) -+{ -+ struct s3cmci_host *host = mmc_priv(mmc); -+ -+ host->cmd_is_stop = 0; -+ host->mrq = mrq; -+ -+ s3cmci_send_request(mmc); -+} -+ -+static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -+{ -+ struct s3cmci_host *host = mmc_priv(mmc); -+ u32 mci_psc, mci_con; -+ -+ /* Set power */ -+ mci_con = readl(host->base + S3C2410_SDICON); -+ switch (ios->power_mode) { -+ case MMC_POWER_ON: -+ case MMC_POWER_UP: -+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK); -+ s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD); -+ s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0); -+ s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1); -+ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2); -+ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3); -+ -+ if (host->pdata->set_power) -+ host->pdata->set_power(ios->power_mode, ios->vdd); -+ -+ if (!host->is2440) -+ mci_con |= S3C2410_SDICON_FIFORESET; -+ -+ break; -+ -+ case MMC_POWER_OFF: -+ default: -+ s3c2410_gpio_setpin(S3C2410_GPE5, 0); -+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP); -+ -+ if (host->pdata->set_power) -+ host->pdata->set_power(ios->power_mode, ios->vdd); -+ -+ if (host->is2440) -+ mci_con |= S3C2440_SDICON_SDRESET; -+ -+ break; -+ } -+ -+ /* Set clock */ -+ for (mci_psc = 0; mci_psc < 255; mci_psc++) { -+ host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1)); -+ -+ if (host->real_rate <= ios->clock) -+ break; -+ } -+ -+ if (mci_psc > 255) -+ mci_psc = 255; -+ host->prescaler = mci_psc; -+ -+ writel(host->prescaler, host->base + S3C2410_SDIPRE); -+ -+ /* If requested clock is 0, real_rate will be 0, too */ -+ if (ios->clock == 0) -+ host->real_rate = 0; -+ -+ /* Set CLOCK_ENABLE */ -+ if (ios->clock) -+ mci_con |= S3C2410_SDICON_CLOCKTYPE; -+ else -+ mci_con &= ~S3C2410_SDICON_CLOCKTYPE; -+ -+ writel(mci_con, host->base + S3C2410_SDICON); -+ -+ if ((ios->power_mode == MMC_POWER_ON) -+ || (ios->power_mode == MMC_POWER_UP)) { -+ -+ dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n", -+ host->real_rate/1000, ios->clock/1000); -+ } else { -+ dbg(host, dbg_conf, "powered down.\n"); -+ } -+ -+ host->bus_width = ios->bus_width; -+ -+} -+ -+static void s3cmci_reset(struct s3cmci_host *host) -+{ -+ u32 con = readl(host->base + S3C2410_SDICON); -+ -+ con |= S3C2440_SDICON_SDRESET; -+ -+ writel(con, host->base + S3C2410_SDICON); -+} -+ -+static int s3cmci_get_ro(struct mmc_host *mmc) -+{ -+ struct s3cmci_host *host = mmc_priv(mmc); -+ -+ if (host->pdata->gpio_wprotect == 0) -+ return 0; -+ -+ return s3c2410_gpio_getpin(host->pdata->gpio_wprotect); -+} -+ -+static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable) -+{ -+ struct s3cmci_host *host = mmc_priv(mmc); -+ -+ if (enable) -+ enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); -+ else -+ disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ); -+} -+ -+static struct mmc_host_ops s3cmci_ops = { -+ .request = s3cmci_request, -+ .set_ios = s3cmci_set_ios, -+ .get_ro = s3cmci_get_ro, -+ .enable_sdio_irq = s3cmci_enable_sdio_irq, -+}; -+ -+static struct s3c24xx_mci_pdata s3cmci_def_pdata = { -+ .do_dma = 0, -+ .gpio_detect = 0, -+ .set_power = NULL, -+ .ocr_avail = MMC_VDD_32_33, -+}; -+ -+static int s3cmci_probe(struct platform_device *pdev, int is2440) -+{ -+ struct mmc_host *mmc; -+ struct s3cmci_host *host; -+ -+ int ret; -+ -+ mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); -+ if (!mmc) { -+ ret = -ENOMEM; -+ goto probe_out; -+ } -+ -+ host = mmc_priv(mmc); -+ host->mmc = mmc; -+ host->pdev = pdev; -+ -+ host->pdata = pdev->dev.platform_data; -+ if (!host->pdata) { -+ pdev->dev.platform_data = &s3cmci_def_pdata; -+ host->pdata = &s3cmci_def_pdata; -+ } -+ -+ spin_lock_init(&host->complete_lock); -+ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); -+ if (is2440) { -+ host->is2440 = 1; -+ host->sdiimsk = S3C2440_SDIIMSK; -+ host->sdidata = S3C2440_SDIDATA; -+ host->sdidata_b = S3C2440_SDIDATA_BYTE; -+ host->clk_div = 1; -+ } else { -+ host->is2440 = 0; -+ host->sdiimsk = S3C2410_SDIIMSK; -+ host->sdidata = S3C2410_SDIDATA; -+ host->sdidata_b = S3C2410_SDIDATA_BYTE; -+ host->clk_div = 2; -+ } -+ host->dodma = host->pdata->do_dma; -+ host->complete_what = COMPLETION_NONE; -+ host->pio_active = XFER_NONE; -+ -+ host->dma = DMACH_SDI; -+ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect); -+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ); -+ -+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!host->mem) { -+ dev_err(&pdev->dev, -+ "failed to get io memory region resouce.\n"); -+ -+ ret = -ENOENT; -+ goto probe_free_host; -+ } -+ -+ host->mem = request_mem_region(host->mem->start, -+ RESSIZE(host->mem), pdev->name); -+ -+ if (!host->mem) { -+ dev_err(&pdev->dev, "failed to request io memory region.\n"); -+ ret = -ENOENT; -+ goto probe_free_host; -+ } -+ -+ host->base = ioremap(host->mem->start, RESSIZE(host->mem)); -+ if (host->base == 0) { -+ dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); -+ ret = -EINVAL; -+ goto probe_free_mem_region; -+ } -+ -+ host->irq = platform_get_irq(pdev, 0); -+ if (host->irq == 0) { -+ dev_err(&pdev->dev, "failed to get interrupt resouce.\n"); -+ ret = -EINVAL; -+ goto probe_iounmap; -+ } -+ -+ if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) { -+ dev_err(&pdev->dev, "failed to request mci interrupt.\n"); -+ ret = -ENOENT; -+ goto probe_iounmap; -+ } -+ -+ disable_irq(host->irq); -+ -+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ); -+ set_irq_type(host->irq_cd, IRQT_BOTHEDGE); -+ -+ if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) { -+ dev_err(&pdev->dev, -+ "failed to request card detect interrupt.\n"); -+ -+ ret = -ENOENT; -+ goto probe_free_irq; -+ } -+ -+ if (host->pdata->gpio_wprotect) -+ s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect, -+ S3C2410_GPIO_INPUT); -+ -+ if (s3c2410_dma_request(host->dma, &s3cmci_dma_client, NULL)) { -+ dev_err(&pdev->dev, "unable to get DMA channel.\n"); -+ ret = -EBUSY; -+ goto probe_free_irq_cd; -+ } -+ -+ host->clk = clk_get(&pdev->dev, "sdi"); -+ if (IS_ERR(host->clk)) { -+ dev_err(&pdev->dev, "failed to find clock source.\n"); -+ ret = PTR_ERR(host->clk); -+ host->clk = NULL; -+ goto probe_free_host; -+ } -+ -+ ret = clk_enable(host->clk); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to enable clock source.\n"); -+ goto clk_free; -+ } -+ -+ host->clk_rate = clk_get_rate(host->clk); -+ -+ mmc->ops = &s3cmci_ops; -+ mmc->ocr_avail = host->pdata->ocr_avail; -+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; -+ mmc->f_min = host->clk_rate / (host->clk_div * 256); -+ mmc->f_max = host->clk_rate / host->clk_div; -+ -+ mmc->max_blk_count = 4095; -+ mmc->max_blk_size = 4095; -+ mmc->max_req_size = 4095 * 512; -+ mmc->max_seg_size = mmc->max_req_size; -+ -+ mmc->max_phys_segs = 128; -+ mmc->max_hw_segs = 128; -+ -+ dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u " -+ "irq_cd:%u dma:%u.\n", (host->is2440?"2440":""), -+ host->base, host->irq, host->irq_cd, host->dma); -+ -+ ret = mmc_add_host(mmc); -+ if (ret) { -+ dev_err(&pdev->dev, "failed to add mmc host.\n"); -+ goto free_dmabuf; -+ } -+ -+ platform_set_drvdata(pdev, mmc); -+ -+ dev_info(&pdev->dev, "initialisation done.\n"); -+ return 0; -+ -+ free_dmabuf: -+ clk_disable(host->clk); -+ -+ clk_free: -+ clk_put(host->clk); -+ -+ probe_free_irq_cd: -+ free_irq(host->irq_cd, host); -+ -+ probe_free_irq: -+ free_irq(host->irq, host); -+ -+ probe_iounmap: -+ iounmap(host->base); -+ -+ probe_free_mem_region: -+ release_mem_region(host->mem->start, RESSIZE(host->mem)); -+ -+ probe_free_host: -+ mmc_free_host(mmc); -+ probe_out: -+ return ret; -+} -+ -+static int s3cmci_remove(struct platform_device *pdev) -+{ -+ struct mmc_host *mmc = platform_get_drvdata(pdev); -+ struct s3cmci_host *host = mmc_priv(mmc); -+ -+ mmc_remove_host(mmc); -+ clk_disable(host->clk); -+ clk_put(host->clk); -+ s3c2410_dma_free(host->dma, &s3cmci_dma_client); -+ free_irq(host->irq_cd, host); -+ free_irq(host->irq, host); -+ iounmap(host->base); -+ release_mem_region(host->mem->start, RESSIZE(host->mem)); -+ mmc_free_host(mmc); -+ -+ return 0; -+} -+ -+static int s3cmci_probe_2410(struct platform_device *dev) -+{ -+ return s3cmci_probe(dev, 0); -+} -+ -+static int s3cmci_probe_2412(struct platform_device *dev) -+{ -+ return s3cmci_probe(dev, 1); -+} -+ -+static int s3cmci_probe_2440(struct platform_device *dev) -+{ -+ return s3cmci_probe(dev, 1); -+} -+ -+#ifdef CONFIG_PM -+ -+static int s3cmci_suspend(struct platform_device *dev, pm_message_t state) -+{ -+ struct mmc_host *mmc = platform_get_drvdata(dev); -+ -+ return mmc_suspend_host(mmc, state); -+} -+ -+static int s3cmci_resume(struct platform_device *dev) -+{ -+ struct mmc_host *mmc = platform_get_drvdata(dev); -+ -+ return mmc_resume_host(mmc); -+} -+ -+#else /* CONFIG_PM */ -+#define s3cmci_suspend NULL -+#define s3cmci_resume NULL -+#endif /* CONFIG_PM */ -+ -+ -+static struct platform_driver s3cmci_driver_2410 = { -+ .driver.name = "s3c2410-sdi", -+ .probe = s3cmci_probe_2410, -+ .remove = s3cmci_remove, -+ .suspend = s3cmci_suspend, -+ .resume = s3cmci_resume, -+}; -+ -+static struct platform_driver s3cmci_driver_2412 = { -+ .driver.name = "s3c2412-sdi", -+ .probe = s3cmci_probe_2412, -+ .remove = s3cmci_remove, -+ .suspend = s3cmci_suspend, -+ .resume = s3cmci_resume, -+}; -+ -+static struct platform_driver s3cmci_driver_2440 = { -+ .driver.name = "s3c2440-sdi", -+ .probe = s3cmci_probe_2440, -+ .remove = s3cmci_remove, -+ .suspend = s3cmci_suspend, -+ .resume = s3cmci_resume, -+}; -+ -+ -+static int __init s3cmci_init(void) -+{ -+ platform_driver_register(&s3cmci_driver_2410); -+ platform_driver_register(&s3cmci_driver_2412); -+ platform_driver_register(&s3cmci_driver_2440); -+ return 0; -+} -+ -+static void __exit s3cmci_exit(void) -+{ -+ platform_driver_unregister(&s3cmci_driver_2410); -+ platform_driver_unregister(&s3cmci_driver_2412); -+ platform_driver_unregister(&s3cmci_driver_2440); -+} -+ -+module_init(s3cmci_init); -+module_exit(s3cmci_exit); -+ -+MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); -+MODULE_LICENSE("GPL"); -+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>"); -diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h -new file mode 100644 -index 0000000..9644b45 ---- /dev/null -+++ b/drivers/mmc/host/s3cmci.h -@@ -0,0 +1,69 @@ -+/* -+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver -+ * -+ * Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved. -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License version 2 as -+ * published by the Free Software Foundation. -+ */ -+ -+enum s3cmci_waitfor { -+ COMPLETION_NONE, -+ COMPLETION_FINALIZE, -+ COMPLETION_CMDSENT, -+ COMPLETION_RSPFIN, -+ COMPLETION_XFERFINISH, -+ COMPLETION_XFERFINISH_RSPFIN, -+}; -+ -+struct s3cmci_host { -+ struct platform_device *pdev; -+ struct s3c24xx_mci_pdata *pdata; -+ struct mmc_host *mmc; -+ struct resource *mem; -+ struct clk *clk; -+ void __iomem *base; -+ int irq; -+ int irq_cd; -+ int dma; -+ -+ unsigned long clk_rate; -+ unsigned long clk_div; -+ unsigned long real_rate; -+ u8 prescaler; -+ -+ int is2440; -+ unsigned sdiimsk; -+ unsigned sdidata; -+ unsigned sdidata_b; -+ int dodma; -+ -+ int dmatogo; -+ -+ struct mmc_request *mrq; -+ int cmd_is_stop; -+ -+ spinlock_t complete_lock; -+ enum s3cmci_waitfor complete_what; -+ -+ int dma_complete; -+ -+ u32 pio_sgptr; -+ u32 pio_bytes; -+ u32 pio_count; -+ u8 *pio_ptr; -+#define XFER_NONE 0 -+#define XFER_READ 1 -+#define XFER_WRITE 2 -+ u32 pio_active; -+ -+ int bus_width; -+ -+ char dbgmsg_cmd[301]; -+ char dbgmsg_dat[301]; -+ char *status; -+ -+ unsigned int ccnt, dcnt; -+ struct tasklet_struct pio_tasklet; -+}; -diff --git a/include/asm-arm/arch-s3c2410/mci.h b/include/asm-arm/arch-s3c2410/mci.h -new file mode 100644 -index 0000000..24e6cd1 ---- /dev/null -+++ b/include/asm-arm/arch-s3c2410/mci.h -@@ -0,0 +1,13 @@ -+#ifndef _ARCH_MCI_H -+#define _ARCH_MCI_H -+ -+struct s3c24xx_mci_pdata { -+ unsigned int gpio_detect; -+ unsigned int gpio_wprotect; -+ unsigned long ocr_avail; -+ unsigned int do_dma; -+ void (*set_power)(unsigned char power_mode, -+ unsigned short vdd); -+}; -+ -+#endif /* _ARCH_NCI_H */ -diff --git a/include/asm-arm/arch-s3c2410/regs-sdi.h b/include/asm-arm/arch-s3c2410/regs-sdi.h -index bb9d30b..4bb1ec5 100644 ---- a/include/asm-arm/arch-s3c2410/regs-sdi.h -+++ b/include/asm-arm/arch-s3c2410/regs-sdi.h -@@ -28,9 +28,17 @@ - #define S3C2410_SDIDCNT (0x30) - #define S3C2410_SDIDSTA (0x34) - #define S3C2410_SDIFSTA (0x38) -+ - #define S3C2410_SDIDATA (0x3C) -+#define S3C2410_SDIDATA_BYTE (0x3C) - #define S3C2410_SDIIMSK (0x40) - -+#define S3C2440_SDIDATA (0x40) -+#define S3C2440_SDIDATA_BYTE (0x48) -+#define S3C2440_SDIIMSK (0x3C) -+ -+#define S3C2440_SDICON_SDRESET (1<<8) -+#define S3C2440_SDICON_MMCCLOCK (1<<5) - #define S3C2410_SDICON_BYTEORDER (1<<4) - #define S3C2410_SDICON_SDIOIRQ (1<<3) - #define S3C2410_SDICON_RWAITEN (1<<2) -@@ -42,7 +50,8 @@ - #define S3C2410_SDICMDCON_LONGRSP (1<<10) - #define S3C2410_SDICMDCON_WAITRSP (1<<9) - #define S3C2410_SDICMDCON_CMDSTART (1<<8) --#define S3C2410_SDICMDCON_INDEX (0xff) -+#define S3C2410_SDICMDCON_SENDERHOST (1<<6) -+#define S3C2410_SDICMDCON_INDEX (0x3f) - - #define S3C2410_SDICMDSTAT_CRCFAIL (1<<12) - #define S3C2410_SDICMDSTAT_CMDSENT (1<<11) -@@ -51,6 +60,9 @@ - #define S3C2410_SDICMDSTAT_XFERING (1<<8) - #define S3C2410_SDICMDSTAT_INDEX (0xff) - -+#define S3C2440_SDIDCON_DS_BYTE (0<<22) -+#define S3C2440_SDIDCON_DS_HALFWORD (1<<22) -+#define S3C2440_SDIDCON_DS_WORD (2<<22) - #define S3C2410_SDIDCON_IRQPERIOD (1<<21) - #define S3C2410_SDIDCON_TXAFTERRESP (1<<20) - #define S3C2410_SDIDCON_RXAFTERCMD (1<<19) -@@ -59,6 +71,7 @@ - #define S3C2410_SDIDCON_WIDEBUS (1<<16) - #define S3C2410_SDIDCON_DMAEN (1<<15) - #define S3C2410_SDIDCON_STOP (1<<14) -+#define S3C2440_SDIDCON_DATSTART (1<<14) - #define S3C2410_SDIDCON_DATMODE (3<<12) - #define S3C2410_SDIDCON_BLKNUM (0x7ff) - -@@ -68,6 +81,7 @@ - #define S3C2410_SDIDCON_XFER_RXSTART (2<<12) - #define S3C2410_SDIDCON_XFER_TXSTART (3<<12) - -+#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF) - #define S3C2410_SDIDCNT_BLKNUM_SHIFT (12) - - #define S3C2410_SDIDSTA_RDYWAITREQ (1<<10) -@@ -82,10 +96,12 @@ - #define S3C2410_SDIDSTA_TXDATAON (1<<1) - #define S3C2410_SDIDSTA_RXDATAON (1<<0) - -+#define S3C2440_SDIFSTA_FIFORESET (1<<16) -+#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */ - #define S3C2410_SDIFSTA_TFDET (1<<13) - #define S3C2410_SDIFSTA_RFDET (1<<12) --#define S3C2410_SDIFSTA_TXHALF (1<<11) --#define S3C2410_SDIFSTA_TXEMPTY (1<<10) -+#define S3C2410_SDIFSTA_TFHALF (1<<11) -+#define S3C2410_SDIFSTA_TFEMPTY (1<<10) - #define S3C2410_SDIFSTA_RFLAST (1<<9) - #define S3C2410_SDIFSTA_RFFULL (1<<8) - #define S3C2410_SDIFSTA_RFHALF (1<<7) --- -1.5.6.3 - |