diff options
Diffstat (limited to 'target/linux/s3c24xx/patches/0054-atheros_2_0_hcd.patch.patch')
-rwxr-xr-x | target/linux/s3c24xx/patches/0054-atheros_2_0_hcd.patch.patch | 1637 |
1 files changed, 1637 insertions, 0 deletions
diff --git a/target/linux/s3c24xx/patches/0054-atheros_2_0_hcd.patch.patch b/target/linux/s3c24xx/patches/0054-atheros_2_0_hcd.patch.patch new file mode 100755 index 0000000000..5b5e97674d --- /dev/null +++ b/target/linux/s3c24xx/patches/0054-atheros_2_0_hcd.patch.patch @@ -0,0 +1,1637 @@ +From 3a9e6d01e200d3caab806474dcf870b7fc8c113d Mon Sep 17 00:00:00 2001 +From: mokopatches <mokopatches@openmoko.org> +Date: Fri, 25 Jul 2008 22:21:24 +0100 +Subject: [PATCH] atheros_2_0_hcd.patch + +--- + drivers/sdio/hcd/Kconfig | 14 + + drivers/sdio/hcd/Makefile | 1 + + drivers/sdio/hcd/s3c24xx/Makefile | 2 + + drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.c | 1502 ++++++++++++++++++++++++++++++++ + drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.h | 67 ++ + 5 files changed, 1586 insertions(+), 0 deletions(-) + create mode 100644 drivers/sdio/hcd/Kconfig + create mode 100644 drivers/sdio/hcd/Makefile + create mode 100644 drivers/sdio/hcd/s3c24xx/Makefile + create mode 100644 drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.c + create mode 100644 drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.h + +diff --git a/drivers/sdio/hcd/Kconfig b/drivers/sdio/hcd/Kconfig +new file mode 100644 +index 0000000..e4d8397 +--- /dev/null ++++ b/drivers/sdio/hcd/Kconfig +@@ -0,0 +1,14 @@ ++config SDIO_S3C24XX ++ tristate "Samsung s3c24xx host controller" ++ depends on PLAT_S3C24XX && SDIO ++ default m ++ help ++ good luck. ++ ++config SDIO_S3C24XX_DMA ++ bool "Samsung s3c24xx host controller DMA I/O" ++ depends on SDIO_S3C24XX ++ default n ++ help ++ good luck. ++ +diff --git a/drivers/sdio/hcd/Makefile b/drivers/sdio/hcd/Makefile +new file mode 100644 +index 0000000..e2401e2 +--- /dev/null ++++ b/drivers/sdio/hcd/Makefile +@@ -0,0 +1 @@ ++obj-$(CONFIG_PLAT_S3C24XX) += s3c24xx/ +diff --git a/drivers/sdio/hcd/s3c24xx/Makefile b/drivers/sdio/hcd/s3c24xx/Makefile +new file mode 100644 +index 0000000..d2d099c +--- /dev/null ++++ b/drivers/sdio/hcd/s3c24xx/Makefile +@@ -0,0 +1,2 @@ ++obj-$(CONFIG_PLAT_S3C24XX) += sdio_s3c24xx_hcd.o ++sdio_s3c24xx_hcd-objs := s3c24xx_hcd.o +diff --git a/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.c b/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.c +new file mode 100644 +index 0000000..3c4758b +--- /dev/null ++++ b/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.c +@@ -0,0 +1,1502 @@ ++/* ++ * s3c24xx_hcd.c - Samsung S3C MCI driver, Atheros SDIO API compatible. ++ * ++ * Copyright (C) 2007 by OpenMoko, Inc. ++ * Written by Samuel Ortiz <sameo@openedhand.com> ++ * 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 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., ++ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/interrupt.h> ++#include <linux/list.h> ++#include <linux/dma-mapping.h> ++#include <linux/errno.h> ++#include <linux/platform_device.h> ++#include <linux/device.h> ++#include <linux/clk.h> ++#include <linux/fs.h> ++#include <linux/ioport.h> ++#include <linux/workqueue.h> ++#include <linux/completion.h> ++#include <linux/delay.h> ++#include <linux/seq_file.h> ++#include <linux/debugfs.h> ++ ++#include <linux/sdio/ctsystem.h> ++#include <linux/sdio/sdio_busdriver.h> ++#include <linux/sdio/sdio_lib.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/uaccess.h> ++#include <asm/dma.h> ++#include <asm/dma-mapping.h> ++ ++#include <asm/arch/regs-sdi.h> ++#include <asm/arch/regs-gpio.h> ++#include <asm/arch/mci.h> ++#include <asm/arch/gta02.h> ++ ++#include "s3c24xx_hcd.h" ++ ++#define DESCRIPTION "S3c24xx SDIO host controller" ++#define AUTHOR "Samuel Ortiz <sameo@openedhand.com>" ++ ++#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) ++ ++static struct s3c2410_dma_client s3c24xx_hcd_dma_client = { ++ .name = "s3c24xx_hcd", ++}; ++ ++extern struct platform_device s3c_device_sdi; ++ ++static void dump_request(struct s3c24xx_hcd_context * context) ++{ ++ if (context->hcd.pCurrentRequest != NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("Current Request Command:%d, ARG:0x%8.8X flags: 0x%04x\n", ++ context->hcd.pCurrentRequest->Command, context->hcd.pCurrentRequest->Argument, ++ context->hcd.pCurrentRequest->Flags)); ++ if (IS_SDREQ_DATA_TRANS(context->hcd.pCurrentRequest->Flags)) { ++ DBG_PRINT(SDDBG_ERROR, ("Data %s, Blocks: %d, BlockLen:%d Remaining: %d \n", ++ IS_SDREQ_WRITE_DATA(context->hcd.pCurrentRequest->Flags) ? "WRITE":"READ", ++ context->hcd.pCurrentRequest->BlockCount, ++ context->hcd.pCurrentRequest->BlockLen, ++ context->hcd.pCurrentRequest->DataRemaining)); ++ } ++ } ++} ++ ++static void s3c24xx_dump_regs(struct s3c24xx_hcd_context * context) ++{ ++ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; ++ u32 datcon, datcnt, datsta, fsta, imask; ++ ++ con = readl(context->base + S3C2410_SDICON); ++ pre = readl(context->base + S3C2410_SDIPRE); ++ cmdarg = readl(context->base + S3C2410_SDICMDARG); ++ cmdcon = readl(context->base + S3C2410_SDICMDCON); ++ cmdsta = readl(context->base + S3C2410_SDICMDSTAT); ++ r0 = readl(context->base + S3C2410_SDIRSP0); ++ r1 = readl(context->base + S3C2410_SDIRSP1); ++ r2 = readl(context->base + S3C2410_SDIRSP2); ++ r3 = readl(context->base + S3C2410_SDIRSP3); ++ timer = readl(context->base + S3C2410_SDITIMER); ++ bsize = readl(context->base + S3C2410_SDIBSIZE); ++ datcon = readl(context->base + S3C2410_SDIDCON); ++ datcnt = readl(context->base + S3C2410_SDIDCNT); ++ datsta = readl(context->base + S3C2410_SDIDSTA); ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ imask = readl(context->base + S3C2440_SDIIMSK); ++ ++ printk("SDICON: 0x%08x\n", con); ++ printk("SDIPRE: 0x%08x\n", pre); ++ printk("SDICmdArg: 0x%08x\n", cmdarg); ++ printk("SDICmdCon: 0x%08x\n", cmdcon); ++ printk("SDICmdSta: 0x%08x\n", cmdsta); ++ printk("SDIRSP0: 0x%08x\n", r0); ++ printk("SDIRSP1: 0x%08x\n", r1); ++ printk("SDIRSP2: 0x%08x\n", r2); ++ printk("SDIRSP3: 0x%08x\n", r3); ++ printk("SDIDTimer: 0x%08x\n", timer); ++ printk("SDIBSize: 0x%08x\n", bsize); ++ printk("SDIDatCon: 0x%08x\n", datcon); ++ printk("SDIDatCnt: 0x%08x\n", datcnt); ++ printk("SDIDatSta: 0x%08x\n", datsta); ++ printk("SDIFSta: 0x%08x\n", fsta); ++ printk("SDIIntMsk: 0x%08x\n", imask); ++} ++ ++static inline void s3c24xx_hcd_clear_imask(struct s3c24xx_hcd_context * context) ++{ ++ if (context->int_sdio) { ++ writel(S3C2410_SDIIMSK_SDIOIRQ | S3C2410_SDIIMSK_READWAIT, ++ context->base + S3C2440_SDIIMSK); ++ } else { ++ writel(0, context->base + S3C2440_SDIIMSK); ++ } ++} ++ ++static inline void s3c24xx_hcd_set_imask(struct s3c24xx_hcd_context * context) ++{ ++ writel(context->int_mask, context->base + S3C2440_SDIIMSK); ++} ++ ++ ++static inline void s3c24xx_hcd_clear_dsta(struct s3c24xx_hcd_context * context) ++{ ++ u32 dsta; ++ ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ writel(dsta, context->base + S3C2410_SDIDSTA); ++} ++ ++static inline void s3c24xx_hcd_clear_csta(struct s3c24xx_hcd_context * context) ++{ ++ u32 csta, csta_clear = 0; ++ ++ csta = readl(context->base + S3C2410_SDICMDSTAT); ++ ++ if (csta & S3C2410_SDICMDSTAT_CRCFAIL) ++ csta_clear |= S3C2410_SDICMDSTAT_CRCFAIL; ++ if (csta & S3C2410_SDICMDSTAT_CMDSENT) ++ csta_clear |= S3C2410_SDICMDSTAT_CMDSENT; ++ if (csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) ++ csta_clear |= S3C2410_SDICMDSTAT_CMDTIMEOUT; ++ if (csta & S3C2410_SDICMDSTAT_RSPFIN) ++ csta_clear |= S3C2410_SDICMDSTAT_RSPFIN; ++ ++ writel(csta_clear, context->base + S3C2410_SDICMDSTAT); ++} ++ ++static inline void s3c24xx_hcd_clear_sta(struct s3c24xx_hcd_context * context) ++{ ++ u32 csta, dsta, csta_clear = 0, dsta_clear = 0; ++ ++ csta = readl(context->base + S3C2410_SDICMDSTAT); ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ ++ if (csta & S3C2410_SDICMDSTAT_CRCFAIL) ++ csta_clear |= S3C2410_SDICMDSTAT_CRCFAIL; ++ if (csta & S3C2410_SDICMDSTAT_CMDSENT) ++ csta_clear |= S3C2410_SDICMDSTAT_CMDSENT; ++ if (csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) ++ csta_clear |= S3C2410_SDICMDSTAT_CMDTIMEOUT; ++ if (csta & S3C2410_SDICMDSTAT_RSPFIN) ++ csta_clear |= S3C2410_SDICMDSTAT_RSPFIN; ++ ++ ++ if (dsta & S3C2410_SDIDSTA_RDYWAITREQ) ++ dsta_clear |= S3C2410_SDIDSTA_RDYWAITREQ; ++ if (dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) ++ dsta_clear |= S3C2410_SDIDSTA_SDIOIRQDETECT; ++ if (dsta & S3C2410_SDIDSTA_FIFOFAIL) ++ dsta_clear |= S3C2410_SDIDSTA_FIFOFAIL; ++ if (dsta & S3C2410_SDIDSTA_CRCFAIL) ++ dsta_clear |= S3C2410_SDIDSTA_CRCFAIL; ++ if (dsta & S3C2410_SDIDSTA_RXCRCFAIL) ++ dsta_clear |= S3C2410_SDIDSTA_RXCRCFAIL; ++ if (dsta & S3C2410_SDIDSTA_DATATIMEOUT) ++ dsta_clear |= S3C2410_SDIDSTA_DATATIMEOUT; ++ if (dsta & S3C2410_SDIDSTA_XFERFINISH) ++ dsta_clear |= S3C2410_SDIDSTA_XFERFINISH; ++ if (dsta & S3C2410_SDIDSTA_BUSYFINISH) ++ dsta_clear |= S3C2410_SDIDSTA_BUSYFINISH; ++ if (dsta & S3C2410_SDIDSTA_SBITERR) ++ dsta_clear |= S3C2410_SDIDSTA_SBITERR; ++ ++ writel(csta_clear, context->base + S3C2410_SDICMDSTAT); ++ writel(dsta_clear, context->base + S3C2410_SDIDSTA); ++} ++ ++static inline void s3c24xx_hcd_fifo_reset(struct s3c24xx_hcd_context * context) ++{ ++ u32 fsta; ++ ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ fsta |= S3C2440_SDIFSTA_FIFORESET; ++ writel(fsta, context->base + S3C2410_SDIFSTA); ++} ++ ++#if 0 ++static void s3c24xx_hcd_reset(struct s3c24xx_hcd_context * context) ++{ ++ u32 con, counter; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&context->lock, flags); ++ ++ con = readl(context->base + S3C2410_SDICON); ++ ++ con |= S3C2440_SDICON_SDRESET; ++ ++ writel(con, context->base + S3C2410_SDICON); ++ ++ counter = 1000; ++ while(counter) { ++ con = readl(context->base + S3C2410_SDICON); ++ if (!(con & S3C2440_SDICON_SDRESET)) ++ break; ++ counter--; ++ mdelay(1); ++ } ++ ++ spin_unlock_irqrestore(&context->lock, flags); ++} ++#endif ++ ++static SDIO_STATUS s3c24xx_hcd_clock_enable(struct s3c24xx_hcd_context * context, ++ unsigned int clock_rate, ++ unsigned char enable) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ unsigned long flags; ++ u32 con; ++ ++ spin_lock_irqsave(&context->lock, flags); ++ ++ con = readl(context->base + S3C2410_SDICON); ++ ++ if (enable && clock_rate) { ++ con |= S3C2410_SDICON_CLOCKTYPE; ++ } else { ++ con &= ~S3C2410_SDICON_CLOCKTYPE; ++ } ++ ++ if (clock_rate) { ++ int prescaler; ++ ++ for (prescaler = 0; prescaler < 0xff; prescaler++) { ++ context->device.actual_clock_rate = ++ context->device.max_clock_rate / (prescaler + 1); ++ ++ if (context->device.actual_clock_rate <= clock_rate && ++ context->device.actual_clock_rate <= context->hcd.MaxClockRate) ++ break; ++ } ++ ++ if (prescaler == 0xff) ++ DBG_PRINT(SDDBG_ERROR , ("Using lowest clock rate\n")); ++ ++ writel(prescaler, context->base + S3C2410_SDIPRE); ++ } ++ ++ writel(con, context->base + S3C2410_SDICON); ++ ++ spin_unlock_irqrestore(&context->lock, flags); ++ ++ return SDIOErrorToOSError(status); ++} ++ ++static void s3c24xx_hcd_set_bus_mode(struct s3c24xx_hcd_context *context, ++ PSDCONFIG_BUS_MODE_DATA pMode) ++{ ++ u32 datacon; ++ unsigned long flags; ++ ++ DBG_PRINT(SDDBG_TRACE , ("SetBusMode\n")); ++ ++ spin_lock_irqsave(&context->lock, flags); ++ datacon = readl(context->base + S3C2410_SDIDCON); ++ ++ switch (SDCONFIG_GET_BUSWIDTH(pMode->BusModeFlags)) { ++ case SDCONFIG_BUS_WIDTH_1_BIT: ++ context->bus_width = 1; ++ datacon &= S3C2410_SDIDCON_WIDEBUS; ++ break; ++ case SDCONFIG_BUS_WIDTH_4_BIT: ++ context->bus_width = 4; ++ datacon |= S3C2410_SDIDCON_WIDEBUS; ++ break; ++ default: ++ DBG_PRINT(SDDBG_TRACE , ("Unknown bus width: %d\n", SDCONFIG_GET_BUSWIDTH(pMode->BusModeFlags))); ++ break; ++ } ++ ++ writel(datacon, context->base + S3C2410_SDIDCON); ++ spin_unlock_irqrestore(&context->lock, flags); ++ ++ /* Set clock rate and enable clock */ ++ s3c24xx_hcd_clock_enable(context, pMode->ClockRate, 1); ++ pMode->ActualClockRate = context->device.actual_clock_rate; ++ ++ DBG_PRINT(SDDBG_TRACE , ("BUS mode: %d bits wide, actual clock rate: %d kHz (requested %d kHz)\n", ++ context->bus_width, pMode->ActualClockRate / 1000, pMode->ClockRate / 1000)); ++} ++ ++ ++static void s3c24xx_hcd_dma_complete(struct s3c24xx_hcd_context * context) ++{ ++ u32 dsta, counter, i; ++ PSDREQUEST req; ++ SDIO_STATUS status = SDIO_STATUS_ERROR; ++ ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): No current request\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (context->complete == S3C24XX_HCD_DATA_READ) { ++ /* DMA READ completion */ ++ if (context->latest_xfer_size != req->DataRemaining) { ++ DBG_PRINT(SDDBG_ERROR, ("Unexpected read xfer size: %d <-> %d\n", ++ context->latest_xfer_size, req->DataRemaining)); ++ status = SDIO_STATUS_BUS_WRITE_ERROR; ++ } ++ ++ counter = 0; ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ while (!(dsta & S3C2410_SDIDSTA_XFERFINISH)) { ++ if (counter > 500) { ++ printk("read xfer timed out\n"); ++ s3c24xx_dump_regs(context); ++ memcpy(req->pDataBuffer, context->io_buffer, ++ req->BlockCount * req->BlockLen); ++ printk("Transfer: %dx%d\n", req->BlockCount, req->BlockLen); ++ for (i = 0; i < req->DataRemaining; i++) ++ printk("0x%x ", *(((char *)context->io_buffer) + i)); ++ printk("\n"); ++ status = SDIO_STATUS_BUS_READ_TIMEOUT; ++ goto out; ++ } ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ counter++; ++ mdelay(1); ++ }; ++ ++ dma_sync_single(NULL, context->io_buffer_dma, ++ req->BlockCount * req->BlockLen, DMA_BIDIRECTIONAL); ++ ++ writel(S3C2410_SDIDSTA_XFERFINISH, context->base + S3C2410_SDIDSTA); ++ ++ memcpy(req->pDataBuffer, context->io_buffer, ++ req->BlockCount * req->BlockLen); ++ ++ req->DataRemaining = 0; ++ status = SDIO_STATUS_SUCCESS; ++ ++ } else if (context->complete == S3C24XX_HCD_DATA_WRITE) { ++ /* DMA WRITE completion */ ++ if (context->latest_xfer_size != req->DataRemaining) { ++ DBG_PRINT(SDDBG_ERROR, ("Unexpected write xfer size: %d <-> %d\n", ++ context->latest_xfer_size, req->DataRemaining)); ++ status = SDIO_STATUS_BUS_WRITE_ERROR; ++ } ++ ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ counter = 0; ++ while (!(dsta & S3C2410_SDIDSTA_XFERFINISH)) { ++ if (counter > 500) { ++ printk("write xfer timed out\n"); ++ status = SDIO_STATUS_BUS_WRITE_ERROR; ++ goto out; ++ } ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ counter++; ++ mdelay(1); ++ }; ++ ++ writel(S3C2410_SDIDSTA_XFERFINISH, context->base + S3C2410_SDIDSTA); ++ req->DataRemaining = 0; ++ status = SDIO_STATUS_SUCCESS; ++ } ++ ++ out: ++ req->Status = status; ++} ++ ++static void s3c24xx_hcd_pio_complete(struct s3c24xx_hcd_context * context) ++{ ++ u32 fsta, counter; ++ u8 *ptr; ++ int fifo_count; ++ PSDREQUEST req; ++ SDIO_STATUS status = SDIO_STATUS_ERROR; ++ ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): No current request\n", __FUNCTION__)); ++ return; ++ } ++ ++ ptr = req->pDataBuffer; ++ ++ if (context->complete == S3C24XX_HCD_DATA_READ) { ++ counter = 0; ++ DBG_PRINT(SDDBG_TRACE, ("Data read...")); ++ do { ++ counter++; ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ mdelay(1); ++ if (counter > 1000) { ++ DBG_PRINT(SDDBG_ERROR, ("DATA read timeout\n")); ++ status = SDIO_STATUS_BUS_READ_TIMEOUT; ++ s3c24xx_dump_regs(context); ++ goto out; ++ } ++ } while(!(fsta & S3C2410_SDIFSTA_RFDET)); ++ DBG_PRINT(SDDBG_TRACE, ("RX detected\n")); ++ ++ while (1) { ++ counter = 0; ++ fifo_count = (readl(context->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK); ++ while (!fifo_count) { ++ counter++; ++ mdelay(1); ++ if (counter > 500) { ++ s3c24xx_dump_regs(context); ++ DBG_PRINT(SDDBG_ERROR, ("No more bytes in FIFO\n")); ++ goto out; ++ } ++ fifo_count = (readl(context->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK); ++ } ++ ++ if (fifo_count > req->DataRemaining) { ++ DBG_PRINT(SDDBG_ERROR, ("DATA read, fifo_count %d > expected %d\n", fifo_count, req->DataRemaining)); ++ fifo_count = req->DataRemaining; ++ } ++ ++ req->DataRemaining -= fifo_count; ++ while (fifo_count > 0) { ++ if (context->data_size == 4) ++ *(ptr) = readl(context->base + S3C2440_SDIDATA); ++ else if (context->data_size == 2) ++ *(ptr) = readw(context->base + S3C2440_SDIDATA); ++ else ++ *(ptr) = readb(context->base + S3C2440_SDIDATA); ++ ++ ptr += context->data_size; ++ fifo_count -= context->data_size; ++ ++ } ++ ++ if (!req->DataRemaining) { ++ /* We poll for xfer finish */ ++ counter = 0; ++ while (!(readl(context->base + S3C2410_SDIDSTA) ++ & S3C2410_SDIDSTA_XFERFINISH)) { ++ counter++; ++ mdelay(1); ++ if (counter > 500) { ++ DBG_PRINT(SDDBG_ERROR, ("RX XFERFINISH missing\n")); ++ s3c24xx_dump_regs(context); ++ break; ++ } ++ } ++ ++ status = SDIO_STATUS_SUCCESS; ++ goto out; ++ } ++ } ++ ++ } else if (context->complete == S3C24XX_HCD_DATA_WRITE) { ++ counter = 0; ++ DBG_PRINT(SDDBG_TRACE, ("Data write...")); ++ do { ++ counter++; ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ mdelay(1); ++ if (counter > 1000) { ++ DBG_PRINT(SDDBG_ERROR, ("DATA write timeout\n")); ++ status = SDIO_STATUS_BUS_WRITE_ERROR; ++ goto out; ++ break; ++ } ++ ++ } while(!(fsta & S3C2410_SDIFSTA_TFDET)); ++ DBG_PRINT(SDDBG_TRACE, ("TX detected\n")); ++ ++ while (1) { ++ counter = 0; ++ fifo_count = 63 - (readl(context->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK); ++ while (!fifo_count) { ++ counter++; ++ mdelay(1); ++ if (counter > 500) { ++ s3c24xx_dump_regs(context); ++ DBG_PRINT(SDDBG_ERROR, ("No more space in FIFO\n")); ++ goto out; ++ } ++ fifo_count = 63 - (readl(context->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK); ++ } ++ ++ if (fifo_count > req->DataRemaining) ++ fifo_count = req->DataRemaining; ++ ++ req->DataRemaining -= fifo_count; ++ ++ while (fifo_count > 0) { ++ if (context->data_size == 4) ++ writel(*(ptr), context->base + S3C2440_SDIDATA); ++ else if (context->data_size == 2) ++ writew(*(ptr), context->base + S3C2440_SDIDATA); ++ else ++ writeb(*(ptr), context->base + S3C2440_SDIDATA); ++ ++ ptr += context->data_size; ++ fifo_count -= context->data_size; ++ } ++ ++ if (!req->DataRemaining) { ++ /* We poll for xfer finish */ ++ counter = 0; ++ while (!(readl(context->base + S3C2410_SDIDSTA) ++ & S3C2410_SDIDSTA_XFERFINISH)) { ++ counter++; ++ mdelay(1); ++ if (counter > 500) { ++ DBG_PRINT(SDDBG_ERROR, ("RX XFERFINISH missing\n")); ++ s3c24xx_dump_regs(context); ++ break; ++ } ++ } ++ ++ status = SDIO_STATUS_SUCCESS; ++ goto out; ++ } ++ } ++ ++ } else { ++ DBG_PRINT(SDDBG_ERROR, ("Wrong context: %d\n", context->complete)); ++ } ++ ++ out: ++ req->Status = status; ++} ++ ++static void s3c24xx_hcd_io_work(struct work_struct *work) ++{ ++ PSDREQUEST req; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ struct s3c24xx_hcd_context * context = ++ container_of(work, struct s3c24xx_hcd_context, io_work); ++ ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): No current request\n", __FUNCTION__)); ++ return; ++ } ++ ++ if (req->Status == SDIO_STATUS_BUS_RESP_TIMEOUT) { ++ DBG_PRINT(SDDBG_ERROR, ("### TIMEOUT ###\n")); ++ s3c24xx_dump_regs(context); ++ goto out; ++ } ++ ++ if (context->complete == S3C24XX_HCD_NO_RESPONSE && ++ req->Status == SDIO_STATUS_SUCCESS) { ++ DBG_PRINT(SDDBG_TRACE, ("CMD done, Status: %d\n", req->Status)); ++ printk("CMD done, Status: %d\n", req->Status); ++ goto out; ++ } ++ ++ if ((context->complete == S3C24XX_HCD_RESPONSE_SHORT || ++ context->complete == S3C24XX_HCD_RESPONSE_LONG || ++ context->complete == S3C24XX_HCD_DATA_READ || ++ context->complete == S3C24XX_HCD_DATA_WRITE) && ++ req->Status == SDIO_STATUS_SUCCESS) { ++ u32 resp[4]; ++ ++ /* We need to copy the response data and send it over */ ++ resp[0] = readl(context->base + S3C2410_SDIRSP0); ++ resp[1] = readl(context->base + S3C2410_SDIRSP1); ++ resp[2] = readl(context->base + S3C2410_SDIRSP2); ++ resp[3] = readl(context->base + S3C2410_SDIRSP3); ++ ++ if (GET_SDREQ_RESP_TYPE(req->Flags) != SDREQ_FLAGS_RESP_R2) { ++ DBG_PRINT(SDDBG_TRACE, ("SHORT response: 0x%08x\n", resp[0])); ++ memcpy(&req->Response[1], (u8*)resp, 4); ++ req->Response[5] = (readl(context->base + S3C2410_SDICMDSTAT) & 0xff); ++ } else { ++ printk("LONG response: 0x%08x\n", resp[0]); ++ DBG_PRINT(SDDBG_TRACE, ("LONG response: 0x%08x\n", resp[0])); ++ memcpy(&req->Response[1], (u8*)resp, 16); ++ //req->Response[17] = (readl(context->base + S3C2410_SDICMDSTAT) & 0xff); ++ } ++ ++ /* There is a data stage */ ++ if (context->complete == S3C24XX_HCD_DATA_READ || ++ context->complete == S3C24XX_HCD_DATA_WRITE) { ++ status = SDIO_CheckResponse(&context->hcd, req, ++ SDHCD_CHECK_DATA_TRANS_OK); ++ ++ if (!SDIO_SUCCESS(status)) { ++ DBG_PRINT(SDDBG_ERROR, ++ ("Target not ready for data xfer\n")); ++ return; ++ } ++ ++ if (context->dma_en) { ++ dma_sync_single(NULL, context->io_buffer_dma, ++ req->BlockCount * req->BlockLen, DMA_BIDIRECTIONAL); ++ ++ s3c2410_dma_ctrl(context->dma_channel, S3C2410_DMAOP_START); ++ ++ wait_for_completion(&context->dma_complete); ++ ++ s3c24xx_hcd_dma_complete(context); ++ } else { ++ s3c24xx_hcd_pio_complete(context); ++ } ++ } ++ } ++ ++ out: ++ s3c24xx_hcd_clear_sta(context); ++ s3c24xx_hcd_clear_imask(context); ++ ++ writel(0, context->base + S3C2410_SDICMDARG); ++ writel(0, context->base + S3C2410_SDICMDCON); ++ ++ SDIO_HandleHcdEvent(&context->hcd, EVENT_HCD_TRANSFER_DONE); ++} ++ ++static void s3c24xx_hcd_irq_work(struct work_struct *work) ++{ ++ struct s3c24xx_hcd_context * context = ++ container_of(work, struct s3c24xx_hcd_context, irq_work); ++ ++ disable_irq(context->io_irq); ++ ++ writel(S3C2410_SDIDSTA_SDIOIRQDETECT, context->base + S3C2410_SDIDSTA); ++ ++ SDIO_HandleHcdEvent(&context->hcd, EVENT_HCD_SDIO_IRQ_PENDING); ++ ++ enable_irq(context->io_irq); ++} ++ ++void s3c24xx_hcd_dma_done(struct s3c2410_dma_chan *dma_ch, void *buf_id, ++ int size, enum s3c2410_dma_buffresult result) ++{ ++ struct s3c24xx_hcd_context * context = ++ (struct s3c24xx_hcd_context *) buf_id; ++ ++ if (result != S3C2410_RES_OK) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): DMA xfer failed: %d\n", __FUNCTION__, result)); ++ s3c24xx_dump_regs(context); ++ } ++ ++ context->latest_xfer_size = size; ++ complete(&context->dma_complete); ++} ++ ++static int s3c24xx_hcd_prepare_dma(struct s3c24xx_hcd_context * context) ++{ ++ PSDREQUEST req; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ int read = 0, hwcfg = S3C2410_DISRCC_INC | S3C2410_DISRCC_APB; ++ enum s3c2410_dmasrc source = S3C2410_DMASRC_MEM; ++ ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): No current request\n", __FUNCTION__)); ++ status = SDIO_STATUS_ERROR; ++ } ++ ++ if (!context->dma_en) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): DMA is disabled\n", __FUNCTION__)); ++ status = SDIO_STATUS_ERROR; ++ } ++ ++ if (!IS_SDREQ_DATA_TRANS(req->Flags)) { ++ DBG_PRINT(SDDBG_ERROR, ("%s(): No data to transfer\n", __FUNCTION__)); ++ status = SDIO_STATUS_ERROR; ++ } ++ ++ if(!IS_SDREQ_WRITE_DATA(req->Flags)) { ++ read = 1; ++ source = S3C2410_DMASRC_HW; ++ hwcfg = S3C2410_DISRCC_APB | 1; ++ } else { ++ memcpy(context->io_buffer, req->pDataBuffer, req->DataRemaining); ++ dma_sync_single(NULL, context->io_buffer_dma, ++ req->BlockCount * req->BlockLen, DMA_BIDIRECTIONAL); ++ ++ } ++ ++ s3c2410_dma_devconfig(context->dma_channel, source, hwcfg, ++ (unsigned long)context->mem->start + S3C2440_SDIDATA); ++ ++ s3c2410_dma_config(context->dma_channel, context->data_size, ++ S3C2410_DCON_CH0_SDI); ++ //(S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI)); ++ ++ s3c2410_dma_set_buffdone_fn(context->dma_channel, s3c24xx_hcd_dma_done); ++ ++// s3c2410_dma_setflags(context->dma_channel, S3C2410_DMAF_AUTOSTART); ++ ++ s3c2410_dma_ctrl(context->dma_channel, S3C2410_DMAOP_FLUSH); ++ ++ s3c2410_dma_enqueue(context->dma_channel, context, ++ context->io_buffer_dma, ++ req->DataRemaining); ++ ++ return 0; ++} ++ ++ ++static irqreturn_t s3c24xx_hcd_irq(int irq, void *dev_id) ++{ ++ u32 cmdsta, dsta, fsta; ++ unsigned long flags, trace = 0; ++ PSDREQUEST req; ++ struct s3c24xx_hcd_context * context = ++ (struct s3c24xx_hcd_context *)dev_id; ++ ++ spin_lock_irqsave(&context->lock, flags); ++ ++ s3c24xx_hcd_clear_imask(context); ++ ++ cmdsta = readl(context->base + S3C2410_SDICMDSTAT); ++ dsta = readl(context->base + S3C2410_SDIDSTA); ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ ++ context->cmdsta = cmdsta; ++ context->dsta = dsta; ++ context->fsta = fsta; ++ ++ s3c24xx_hcd_clear_csta(context); ++ ++ if (dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) { ++ writel(S3C2410_SDIDSTA_SDIOIRQDETECT, context->base + S3C2410_SDIDSTA); ++ ++ if (context->int_sdio) { ++ u32 imask; ++ ++ context->int_sdio = 0; ++ ++ imask = readl(context->base + S3C2440_SDIIMSK); ++ imask &= ~S3C2410_SDIIMSK_SDIOIRQ; ++ writel(imask, context->base + S3C2440_SDIIMSK); ++ schedule_work(&context->irq_work); ++ } ++ } ++ ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ DBG_PRINT(SDDBG_TRACE, ("%s(): No current request\n", __FUNCTION__)); ++ goto out; ++ } ++ ++ if (cmdsta & S3C2410_SDICMDSTAT_CMDTIMEOUT) { ++ DBG_PRINT(SDDBG_ERROR, ("TIMEOUT\n")); ++ printk("TIMEOUT\n"); ++ req->Status = SDIO_STATUS_BUS_RESP_TIMEOUT; ++ writel(S3C2410_SDICMDSTAT_CMDTIMEOUT, context->base + S3C2410_SDICMDSTAT); ++ schedule_work(&context->io_work); ++ } ++ ++ if (cmdsta & S3C2410_SDICMDSTAT_CRCFAIL) { ++ DBG_PRINT(SDDBG_ERROR, ("CRCFAIL 0x%x\n", cmdsta)); ++ printk("CRCFAIL 0x%x\n", cmdsta); ++ req->Status = SDIO_STATUS_BUS_RESP_CRC_ERR; ++ dump_request(context); ++ writel(S3C2410_SDICMDSTAT_CRCFAIL, context->base + S3C2410_SDICMDSTAT); ++ schedule_work(&context->io_work); ++ } ++ ++ ++ if (cmdsta & S3C2410_SDICMDSTAT_CMDSENT) { ++ writel(S3C2410_SDICMDSTAT_CMDSENT, context->base + S3C2410_SDICMDSTAT); ++ ++ if (context->complete == S3C24XX_HCD_NO_RESPONSE) { ++ req->Status = SDIO_STATUS_SUCCESS; ++ trace = 1; ++ schedule_work(&context->io_work); ++ } ++ } ++ ++ if (cmdsta & S3C2410_SDICMDSTAT_RSPFIN || ++ (IS_SDREQ_WRITE_DATA(req->Flags) && (fsta & S3C2410_SDIFSTA_TFDET)) || ++ (!IS_SDREQ_WRITE_DATA(req->Flags) && (fsta & S3C2410_SDIFSTA_RFDET))) { ++ ++ writel(S3C2410_SDICMDSTAT_RSPFIN, context->base + S3C2410_SDICMDSTAT); ++ ++ if (context->complete == S3C24XX_HCD_RESPONSE_SHORT || ++ context->complete == S3C24XX_HCD_RESPONSE_LONG || ++ context->complete == S3C24XX_HCD_DATA_READ || ++ context->complete == S3C24XX_HCD_DATA_WRITE) { ++ req->Status = SDIO_STATUS_SUCCESS; ++ if (trace) ++ printk("IO work already scheduled, cmdsta: 0x%x\n", cmdsta); ++ schedule_work(&context->io_work); ++ } ++ } ++ ++ out: ++ if (dsta & S3C2410_SDIDSTA_RDYWAITREQ) { ++ printk("S3C2410_SDIDSTA_RDYWAITREQ\n"); ++ //writel(S3C2410_SDIDSTA_RDYWAITREQ, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_FIFOFAIL) { ++ printk("S3C2410_SDIDSTA_FIFOFAIL\n"); ++ writel(S3C2410_SDIDSTA_FIFOFAIL, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_CRCFAIL) { ++ printk("S3C2410_SDIDSTA_CRCFAIL\n"); ++ writel(S3C2410_SDIDSTA_CRCFAIL, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_RXCRCFAIL) { ++ printk("S3C2410_SDIDSTA_RXCRCFAIL\n"); ++ writel(S3C2410_SDIDSTA_RXCRCFAIL, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_DATATIMEOUT) { ++ printk("S3C2410_SDIDSTA_DATATIMEOUT\n"); ++ writel(S3C2410_SDIDSTA_DATATIMEOUT, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_BUSYFINISH) { ++ printk("S3C2410_SDIDSTA_BUSYFINISH\n"); ++ writel(S3C2410_SDIDSTA_BUSYFINISH, context->base + S3C2410_SDIDSTA); ++ } ++ ++ if (dsta & S3C2410_SDIDSTA_SBITERR) { ++ printk("S3C2410_SDIDSTA_SBIERR\n"); ++ writel(S3C2410_SDIDSTA_SBITERR, context->base + S3C2410_SDIDSTA); ++ } ++ ++ spin_unlock_irqrestore(&context->lock, flags); ++ return IRQ_HANDLED; ++} ++ ++ ++SDIO_STATUS s3c24xx_hcd_config(PSDHCD hcd, PSDCONFIG config) ++{ ++ u32 con, imsk; ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ PSDCONFIG_SDIO_INT_CTRL_DATA int_data; ++ struct s3c24xx_hcd_context * context = (struct s3c24xx_hcd_context *)hcd->pContext; ++ ++ switch (GET_SDCONFIG_CMD(config)){ ++ case SDCONFIG_GET_WP: ++ DBG_PRINT(SDDBG_TRACE, ("config GET_WP\n")); ++ *((SDCONFIG_WP_VALUE *)config->pData) = 0; ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_SEND_INIT_CLOCKS: ++ DBG_PRINT(SDDBG_TRACE, ("config SEND_INIT_CLOCKS\n")); ++ ++ /* We stop/start the clock */ ++ con = readl(context->base + S3C2410_SDICON); ++ ++ con &= ~S3C2410_SDICON_CLOCKTYPE; ++ writel(con, context->base + S3C2410_SDICON); ++ ++ mdelay(100); ++ ++ con |= S3C2410_SDICON_CLOCKTYPE; ++ writel(con, context->base + S3C2410_SDICON); ++ ++ mdelay(100); ++ ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_SDIO_INT_CTRL: ++ DBG_PRINT(SDDBG_TRACE, ("config SDIO_INT_CTRL\n")); ++ int_data = GET_SDCONFIG_CMD_DATA(PSDCONFIG_SDIO_INT_CTRL_DATA, config); ++ ++ if (int_data->SlotIRQEnable & ++ (IRQ_DETECT_1_BIT | IRQ_DETECT_4_BIT | IRQ_DETECT_MULTI_BLK) ) { ++ imsk = readl(context->base + S3C2440_SDIIMSK); ++ ++ if (int_data->SlotIRQEnable) { ++ printk("SDIO_INT_CTRL enable IRQ\n"); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO_INT_CTRL enable IRQ\n")); ++ context->int_sdio = 1; ++ imsk |= S3C2410_SDIIMSK_SDIOIRQ; ++ writel(imsk, context->base + S3C2440_SDIIMSK); ++ } else { ++ printk("SDIO_INT_CTRL disable IRQ\n"); ++ DBG_PRINT(SDDBG_TRACE, ("SDIO_INT_CTRL disable IRQ\n")); ++ context->int_sdio = 0; ++ imsk &= ~S3C2410_SDIIMSK_SDIOIRQ; ++ writel(imsk, context->base + S3C2440_SDIIMSK); ++ } ++ } ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_SDIO_REARM_INT: ++ DBG_PRINT(SDDBG_TRACE, ("config SDIO_REARM_INT\n")); ++ ++ context->int_sdio = 1; ++ imsk = readl(context->base + S3C2440_SDIIMSK); ++ imsk |= S3C2410_SDIIMSK_SDIOIRQ; ++ writel(imsk, context->base + S3C2440_SDIIMSK); ++ ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_FUNC_CHANGE_BUS_MODE: ++ case SDCONFIG_BUS_MODE_CTRL: ++ s3c24xx_hcd_set_bus_mode(context, (PSDCONFIG_BUS_MODE_DATA)(config->pData)); ++ DBG_PRINT(SDDBG_TRACE, ("config BUS_MODE_CTRL\n")); ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_POWER_CTRL: ++ DBG_PRINT(SDDBG_TRACE, ("config POWER_CTRL\n")); ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_GET_HCD_DEBUG: ++ DBG_PRINT(SDDBG_TRACE, ("config GET_HCD_DEBUG\n")); ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ case SDCONFIG_SET_HCD_DEBUG: ++ DBG_PRINT(SDDBG_TRACE, ("config SET_HCD_DEBUG\n")); ++ status = SDIO_STATUS_SUCCESS; ++ break; ++ default: ++ /* invalid request */ ++ DBG_PRINT(SDDBG_ERROR, ("%s() - unsupported command: 0x%X\n", ++ __FUNCTION__, GET_SDCONFIG_CMD(config))); ++ status = SDIO_STATUS_INVALID_PARAMETER; ++ } ++ ++ return SDIOErrorToOSError(status); ++} ++ ++ ++SDIO_STATUS s3c24xx_hcd_request(PSDHCD hcd) ++{ ++ SDIO_STATUS status = SDIO_STATUS_PENDING; ++ PSDREQUEST req; ++ u32 cmdcon, imask; ++ unsigned long flags; ++ struct s3c24xx_hcd_context * context = ++ (struct s3c24xx_hcd_context *)hcd->pContext; ++ ++ req = GET_CURRENT_REQUEST(hcd); ++ DBG_ASSERT(req != NULL); ++ ++ if (req->Flags & SDREQ_FLAGS_DATA_SHORT_TRANSFER) ++ printk("### SHORT TRANSFER ###\n"); ++ ++ spin_lock_irqsave(&context->lock, flags); ++ ++ /* Clear command, data and fifo status registers */ ++ writel(0xFFFFFFFF, context->base + S3C2410_SDICMDSTAT); ++ writel(0xFFFFFFFF, context->base + S3C2410_SDIDSTA); ++ writel(0xFFFFFFFF, context->base + S3C2410_SDIFSTA); ++ ++ /* Enabling irqs */ ++ imask = S3C2410_SDIIMSK_READWAIT; ++ ++ cmdcon = readl(context->base + S3C2410_SDICMDCON); ++ ++ switch (GET_SDREQ_RESP_TYPE(req->Flags)) { ++ case SDREQ_FLAGS_NO_RESP: ++ cmdcon &= ~S3C2410_SDICMDCON_WAITRSP; ++ context->complete = S3C24XX_HCD_NO_RESPONSE; ++ imask |= S3C2410_SDIIMSK_CMDSENT; ++ break; ++ case SDREQ_FLAGS_RESP_R1: ++ case SDREQ_FLAGS_RESP_R1B: ++ case SDREQ_FLAGS_RESP_R3: ++ case SDREQ_FLAGS_RESP_SDIO_R4: ++ case SDREQ_FLAGS_RESP_SDIO_R5: ++ case SDREQ_FLAGS_RESP_R6: ++ cmdcon &= ~S3C2410_SDICMDCON_LONGRSP; ++ cmdcon |= S3C2410_SDICMDCON_WAITRSP; ++ context->complete = S3C24XX_HCD_RESPONSE_SHORT; ++ imask |= S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_RESPONSEND ++ | S3C2410_SDIIMSK_CMDTIMEOUT | S3C2410_SDIIMSK_RESPONSECRC; ++ break; ++ case SDREQ_FLAGS_RESP_R2: ++ cmdcon |= S3C2410_SDICMDCON_LONGRSP; ++ cmdcon |= S3C2410_SDICMDCON_WAITRSP; ++ context->complete = S3C24XX_HCD_RESPONSE_LONG; ++ imask |= S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_RESPONSEND ++ | S3C2410_SDIIMSK_CMDTIMEOUT | S3C2410_SDIIMSK_RESPONSECRC; ++ break; ++ ++ } ++ ++ /* There is a data part */ ++ if (IS_SDREQ_DATA_TRANS(req->Flags)) { ++ u32 dcon = 0; ++ ++ if (readl(context->base + S3C2410_SDIDSTA) & ++ (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) { ++ printk("##### DATA ON: 0x%x ######\n", readl(context->base + S3C2410_SDIDSTA)); ++ } ++ ++ /* Setting timer */ ++ writel(0x7fffff, context->base + S3C2410_SDITIMER); ++ ++ /* Block size */ ++ writel(req->BlockLen, context->base + S3C2410_SDIBSIZE); ++ /* Number of blocks */ ++ dcon |= (0xfff & req->BlockCount); ++ ++ if (context->bus_width == 4) ++ dcon |= S3C2410_SDIDCON_WIDEBUS; ++ ++ req->DataRemaining = req->BlockCount * req->BlockLen; ++ ++ /* Set data size, and start the transfer */ ++ dcon |= S3C2410_SDIDCON_IRQPERIOD; ++ if (!(req->DataRemaining % 4)) { ++ context->data_size = 4; ++ dcon |= S3C2440_SDIDCON_DS_WORD; ++ } else if (!(req->DataRemaining % 2)) { ++ context->data_size = 2; ++ dcon |= S3C2440_SDIDCON_DS_HALFWORD; ++ } else { ++ context->data_size = 1; ++ dcon |= S3C2440_SDIDCON_DS_BYTE; ++ } ++ ++#ifdef CONFIG_SDIO_S3C24XX_DMA ++ if (req->DataRemaining > 16) { ++ context->dma_en = 1; ++ } else ++#endif ++ { ++ context->dma_en = 0; ++ context->data_size = 1; ++ dcon |= S3C2440_SDIDCON_DS_BYTE; ++ } ++ ++ if (context->dma_en) { ++ dcon |= S3C2410_SDIDCON_DMAEN; ++ s3c24xx_hcd_prepare_dma(context); ++ } ++ ++ if (IS_SDREQ_WRITE_DATA(req->Flags)) { ++ /* Data write */ ++ DBG_PRINT(SDDBG_TRACE, ("Start data write, block count=%d, block size=%d\n", ++ req->BlockCount, req->BlockLen)); ++ ++ /* Data configuration: transmit after resp, block mode*/ ++ dcon |= S3C2410_SDIDCON_TXAFTERRESP | S3C2410_SDIDCON_BLOCKMODE; ++ ++ /* This is a write */ ++ dcon |= S3C2410_SDIDCON_XFER_TXSTART; ++ ++ imask |= S3C2410_SDIIMSK_TXFIFOHALF | S3C2410_SDIIMSK_TXFIFOEMPTY | ++ S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | ++ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; ++ ++ context->complete = S3C24XX_HCD_DATA_WRITE; ++ } else { ++ /* Data read */ ++ DBG_PRINT(SDDBG_TRACE, ("Start data read, block count=%d, block size=%d\n", ++ req->BlockCount, req->BlockLen)); ++ ++ /* Data configuration: receive after cmd, block mode*/ ++ dcon |= S3C2410_SDIDCON_RXAFTERCMD | S3C2410_SDIDCON_BLOCKMODE; ++ ++ /* This is a read */ ++ dcon |= S3C2410_SDIDCON_XFER_RXSTART; ++ ++ imask |= S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST | ++ S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC | ++ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH; ++ ++ context->complete = S3C24XX_HCD_DATA_READ; ++ } ++ ++ dcon |= S3C2440_SDIDCON_DATSTART; ++ ++ writel(dcon, context->base + S3C2410_SDIDCON); ++ ++ cmdcon |= S3C2410_SDICMDCON_WITHDATA; ++ ++ } else { ++ cmdcon &= ~S3C2410_SDICMDCON_WITHDATA; ++ } ++ ++ cmdcon |= req->Command & S3C2410_SDICMDCON_INDEX; ++ cmdcon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART; ++ ++ req->Status = SDIO_STATUS_PENDING; ++ ++ if (context->int_sdio) ++ imask |= S3C2410_SDIIMSK_SDIOIRQ; ++ context->int_mask = imask; ++ writel(imask, context->base + S3C2440_SDIIMSK); ++ writel(req->Argument, context->base + S3C2410_SDICMDARG); ++ writel(cmdcon, context->base + S3C2410_SDICMDCON); ++ ++ spin_unlock_irqrestore(&context->lock, flags); ++ ++ return status; ++} ++ ++static int s3c24xx_hcd_hw_init(struct s3c24xx_hcd_context * context) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ u32 con, datacon; ++ ++ /* Clock */ ++ context->device.clock = clk_get(NULL, "sdi"); ++ if (IS_ERR(context->device.clock)) { ++ DBG_PRINT(SDDBG_ERROR, ("Couldn't get clock\n")); ++ status = PTR_ERR(context->device.clock); ++ context->device.clock = NULL; ++ return status; ++ } ++ ++ status = clk_enable(context->device.clock); ++ if (SDIO_IS_ERROR(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("Couldn't get clock\n")); ++ return SDIOErrorToOSError(status); ++ } ++ ++ context->device.max_clock_rate = clk_get_rate(context->device.clock); ++ context->device.actual_clock_rate = context->device.max_clock_rate; ++ ++ /* I/O */ ++ context->mem = request_mem_region(context->mem->start, ++ RESSIZE(context->mem), context->description); ++ ++ if (!context->mem) { ++ DBG_PRINT(SDDBG_ERROR, ("Failed to request io memory region\n")); ++ status = -ENOENT; ++ goto out_disable_clock; ++ } ++ ++ context->base = ioremap(context->mem->start, RESSIZE(context->mem)); ++ if (context->base == 0) { ++ DBG_PRINT(SDDBG_ERROR, ("failed to ioremap() io memory region.\n")); ++ status = -EINVAL; ++ goto out_free_mem_region; ++ } ++ ++ /* IRQ */ ++#if 0 ++ context->cd_irq = s3c2410_gpio_getirq(GTA02v1_GPIO_nSD_DETECT); ++ s3c2410_gpio_cfgpin(GTA02v1_GPIO_nSD_DETECT, S3C2410_GPIO_IRQ); ++ ++ if (request_irq(context->cd_irq, s3c24xx_hcd_cd_irq, 0, context->description, context)) { ++ DBG_PRINT(SDDBG_ERROR, ("failed to request card detect interrupt.\n")); ++ status = -ENOENT; ++ goto out_unmap_mem_region; ++ } ++#endif ++ ++ if (request_irq(context->io_irq, s3c24xx_hcd_irq, 0, context->description, context)) { ++ DBG_PRINT(SDDBG_ERROR, ("failed to request mci interrupt.\n")); ++ status = -ENOENT; ++ goto out_unmap_mem_region; ++ } ++ ++ ++ /* DMA */ ++ context->io_buffer_size = 4 * 4096; ++ context->io_buffer = dma_alloc_writecombine(&context->pdev->dev, ++ context->io_buffer_size, ++ &context->io_buffer_dma, ++ GFP_KERNEL | GFP_DMA); ++ ++ if (context->io_buffer == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("failed to allocate DMA buffer\n")); ++ status = -ENOMEM; ++ goto out_free_irq; ++ ++ } ++ ++ if (s3c2410_dma_request(context->dma_channel, &s3c24xx_hcd_dma_client, NULL)) { ++ DBG_PRINT(SDDBG_ERROR, ("unable to get DMA channel.\n")); ++ status = -ENOENT; ++ goto out_free_dma; ++ } ++ ++ ++ /* Set multiplexing */ ++ 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); ++ ++ con = readl(context->base + S3C2410_SDICON); ++ con |= S3C2410_SDICON_SDIOIRQ; ++ writel(con, context->base + S3C2410_SDICON); ++ ++ datacon = readl(context->base + S3C2410_SDIDCON); ++ datacon |= S3C2410_SDIDCON_WIDEBUS; ++ writel(datacon, context->base + S3C2410_SDIDCON); ++ ++ printk("S3c24xx SDIO: IRQ:%d Detect IRQ:%d DMA channel:%d base@0x%p PCLK@%ld kHz\n", ++ context->io_irq, context->cd_irq, context->dma_channel, context->base, ++ context->device.max_clock_rate/1000); ++ ++ return SDIOErrorToOSError(status); ++ ++ out_free_dma: ++ dma_free_writecombine(&context->pdev->dev,context->io_buffer_size, ++ context->io_buffer, context->io_buffer_dma); ++ ++ out_free_irq: ++ free_irq(context->io_irq, context); ++ ++ out_unmap_mem_region: ++ iounmap(context->base); ++ ++ out_free_mem_region: ++ release_mem_region(context->mem->start, RESSIZE(context->mem)); ++ ++ out_disable_clock: ++ clk_disable(context->device.clock); ++ ++ return SDIOErrorToOSError(status); ++} ++ ++static void s3c24xx_hcd_hw_cleanup(struct s3c24xx_hcd_context * context) ++{ ++ clk_disable(context->device.clock); ++ free_irq(context->io_irq, context); ++ iounmap(context->base); ++ release_mem_region(context->mem->start, RESSIZE(context->mem)); ++ dma_free_writecombine(&context->pdev->dev,context->io_buffer_size, ++ context->io_buffer, context->io_buffer_dma); ++} ++ ++static int s3c24xx_hcd_pnp_probe(struct pnp_dev *pBusDevice, const struct pnp_device_id *pId) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ ++ status = s3c24xx_hcd_hw_init(&hcd_context); ++ if (SDIO_IS_ERROR(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("HW Init failed\n")); ++ return SDIOErrorToOSError(status); ++ } ++ ++ status = SDIO_RegisterHostController(&hcd_context.hcd); ++ if (SDIO_IS_ERROR(status)) { ++ DBG_PRINT(SDDBG_ERROR, ("Host registration failed\n")); ++ s3c24xx_hcd_hw_cleanup(&hcd_context); ++ return SDIOErrorToOSError(status); ++ } ++ ++ /* Our card is built-in, we force the attachement event */ ++ SDIO_HandleHcdEvent(&hcd_context.hcd, EVENT_HCD_ATTACH); ++ ++ return 0; ++} ++ ++static void s3c24xx_hcd_pnp_remove(struct pnp_dev *pBusDevice) ++{ ++} ++ ++/* the driver context data */ ++struct s3c24xx_hcd_context hcd_context = { ++ .description = DESCRIPTION, ++ .hcd.pName = "sdio_s3c24xx", ++ .hcd.Version = CT_SDIO_STACK_VERSION_CODE, ++ .hcd.pModule = THIS_MODULE, ++ /* builtin card, 4 bits bus */ ++ .hcd.Attributes = SDHCD_ATTRIB_BUS_4BIT | SDHCD_ATTRIB_BUS_1BIT | SDHCD_ATTRIB_MULTI_BLK_IRQ, ++ .hcd.SlotNumber = 0, ++ .hcd.MaxSlotCurrent = 500, /* 1/2 amp */ ++ .hcd.SlotVoltageCaps = SLOT_POWER_3_3V, /* 3.3V */ ++ .hcd.SlotVoltagePreferred = SLOT_POWER_3_3V, /* 3.3V */ ++ .hcd.MaxClockRate = 25000000, ++ .hcd.MaxBytesPerBlock = 0xfff, /* 0 - 4095 */ ++ .hcd.MaxBlocksPerTrans = 0xfff, /* 0 - 4095 */ ++ .hcd.pContext = &hcd_context, ++ .hcd.pRequest = s3c24xx_hcd_request, ++ .hcd.pConfigure = s3c24xx_hcd_config, ++ .device.pnp_device.name = "sdio_s3c24xx_hcd", ++ .device.pnp_driver.name = "sdio_s3c24xx_hcd", ++ .device.pnp_driver.probe = s3c24xx_hcd_pnp_probe, ++ .device.pnp_driver.remove = s3c24xx_hcd_pnp_remove, ++}; ++ ++static int s3c24xx_hcd_probe(struct platform_device * pdev) ++{ ++ SDIO_STATUS status = SDIO_STATUS_SUCCESS; ++ struct resource *r = NULL; ++ ++ printk("S3c2440 SDIO Host controller\n"); ++ ++ hcd_context.pdev = pdev; ++ ++ hcd_context.mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (hcd_context.mem == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("No memory region\n")); ++ status = SDIO_STATUS_NO_RESOURCES; ++ goto out; ++ } ++ ++ hcd_context.io_irq = platform_get_irq(pdev, 0); ++ if (hcd_context.io_irq == 0) { ++ DBG_PRINT(SDDBG_ERROR, ("No IRQ\n")); ++ status = SDIO_STATUS_NO_RESOURCES; ++ goto out; ++ } ++ ++ r = platform_get_resource(pdev, IORESOURCE_DMA, 0); ++ if (r == NULL) { ++ DBG_PRINT(SDDBG_ERROR, ("No DMA channel\n")); ++ status = SDIO_STATUS_NO_RESOURCES; ++ goto out; ++ } ++ hcd_context.dma_channel = r->start; ++ hcd_context.dma_en = 0; ++ ++ hcd_context.int_sdio = 0; ++ ++ spin_lock_init(&hcd_context.lock); ++ ++ init_completion(&hcd_context.dma_complete); ++ init_completion(&hcd_context.xfer_complete); ++ ++ INIT_WORK(&hcd_context.io_work, s3c24xx_hcd_io_work); ++ INIT_WORK(&hcd_context.irq_work, s3c24xx_hcd_irq_work); ++ ++ mdelay(100); ++ ++ status = SDIO_BusAddOSDevice(&hcd_context.device.dma, ++ &hcd_context.device.pnp_driver, ++ &hcd_context.device.pnp_device); ++ ++ out: ++ ++ return SDIOErrorToOSError(status); ++} ++ ++/* ++ * module cleanup ++ */ ++static int s3c24xx_hcd_remove(struct platform_device * pdev) { ++ printk("S3C2440 SDIO host controller unloaded\n"); ++ SDIO_BusRemoveOSDevice(&hcd_context.device.pnp_driver, &hcd_context.device.pnp_device); ++ ++ return 0; ++} ++ ++static struct platform_driver s3c24xx_hcd_sdio = ++{ ++ .driver.name = "s3c24xx-sdio", ++ .probe = s3c24xx_hcd_probe, ++ .remove = s3c24xx_hcd_remove, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++static struct dentry *debugfs_dir; ++ ++static int s3c24xx_hcd_debugfs_show(struct seq_file *s, void *data) ++{ ++ PSDREQUEST req; ++ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize; ++ u32 datcon, datcnt, datsta, fsta, imask; ++ struct s3c24xx_hcd_context * context = &hcd_context; ++ ++ ++ con = readl(context->base + S3C2410_SDICON); ++ pre = readl(context->base + S3C2410_SDIPRE); ++ cmdarg = readl(context->base + S3C2410_SDICMDARG); ++ cmdcon = readl(context->base + S3C2410_SDICMDCON); ++ cmdsta = readl(context->base + S3C2410_SDICMDSTAT); ++ r0 = readl(context->base + S3C2410_SDIRSP0); ++ r1 = readl(context->base + S3C2410_SDIRSP1); ++ r2 = readl(context->base + S3C2410_SDIRSP2); ++ r3 = readl(context->base + S3C2410_SDIRSP3); ++ timer = readl(context->base + S3C2410_SDITIMER); ++ bsize = readl(context->base + S3C2410_SDIBSIZE); ++ datcon = readl(context->base + S3C2410_SDIDCON); ++ datcnt = readl(context->base + S3C2410_SDIDCNT); ++ datsta = readl(context->base + S3C2410_SDIDSTA); ++ fsta = readl(context->base + S3C2410_SDIFSTA); ++ imask = readl(context->base + S3C2440_SDIIMSK); ++ ++ seq_printf(s, "SDICON: 0x%08x\n", con); ++ seq_printf(s, "SDIPRE: 0x%08x\n", pre); ++ seq_printf(s, "SDICmdArg: 0x%08x\n", cmdarg); ++ seq_printf(s, "SDICmdCon: 0x%08x\n", cmdcon); ++ seq_printf(s, "SDICmdSta: 0x%08x\n", cmdsta); ++ seq_printf(s, "SDIRSP0: 0x%08x\n", r0); ++ seq_printf(s, "SDIRSP1: 0x%08x\n", r1); ++ seq_printf(s, "SDIRSP2: 0x%08x\n", r2); ++ seq_printf(s, "SDIRSP3: 0x%08x\n", r3); ++ seq_printf(s, "SDIDTimer: 0x%08x\n", timer); ++ seq_printf(s, "SDIBSize: 0x%08x\n", bsize); ++ seq_printf(s, "SDIDatCon: 0x%08x\n", datcon); ++ seq_printf(s, "SDIDatCnt: 0x%08x\n", datcnt); ++ seq_printf(s, "SDIDatSta: 0x%08x\n", datsta); ++ seq_printf(s, "SDIFSta: 0x%08x\n", fsta); ++ seq_printf(s, "SDIIntMsk: 0x%08x\n", imask); ++ seq_printf(s, "\n"); ++ ++ seq_printf(s, "Current REQ: \n"); ++ req = GET_CURRENT_REQUEST(&context->hcd); ++ if (req == NULL) { ++ seq_printf(s, " No current request\n"); ++ } else { ++ seq_printf(s, " Command: %d\n", req->Command); ++ seq_printf(s, " Args: 0x%x\n", req->Argument); ++ seq_printf(s, " Flags: 0x%x\n", req->Flags); ++ seq_printf(s, " %d blocks x %d bytes\n", req->BlockCount, req->BlockLen); ++ seq_printf(s, " %d bytes remaining\n", req->DataRemaining); ++ } ++ ++ seq_printf(s, "Context: \n"); ++ seq_printf(s, " INT mask: 0x%x\n", context->int_mask); ++ seq_printf(s, " sdio INT: %d\n", context->int_sdio); ++ seq_printf(s, " cmdsta: 0x%x\n", context->cmdsta); ++ seq_printf(s, " dsta: 0x%x\n", context->dsta); ++ seq_printf(s, " fsta: 0x%x\n", context->fsta); ++ ++ return 0; ++} ++ ++static int s3c24xx_hcd_debugfs_open(struct inode *inode, ++ struct file *file) ++{ ++ return single_open(file, s3c24xx_hcd_debugfs_show, NULL); ++} ++ ++static const struct file_operations s3c24xx_hcd_debugfs_fops = { ++ .open = s3c24xx_hcd_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++ ++static int s3c24xx_debugfs_init(struct s3c24xx_hcd_context * context) ++{ ++ debugfs_dir = debugfs_create_dir("s3c24xx_sdio", NULL); ++ ++ debugfs_create_file("registers", 0444, debugfs_dir, ++ (void *)context, ++ &s3c24xx_hcd_debugfs_fops); ++ ++ return 0; ++} ++ ++#else ++ ++static int s3c24xx_debugfs_init(struct s3c24xx_hcd_context * context) ++{ ++ return 0; ++} ++ ++#endif ++ ++static int __init s3c24xx_hcd_init(void) ++{ ++ int ret; ++ ++ ret = s3c24xx_debugfs_init(&hcd_context); ++ if (ret) { ++ printk("%s(): debugfs init failed\n", __FUNCTION__); ++ } ++ ++ platform_driver_register(&s3c24xx_hcd_sdio); ++ ++ return 0; ++} ++ ++static void __exit s3c24xx_hcd_exit(void) ++{ ++ platform_driver_unregister(&s3c24xx_hcd_sdio); ++} ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION(DESCRIPTION); ++MODULE_AUTHOR(AUTHOR); ++ ++module_init(s3c24xx_hcd_init); ++module_exit(s3c24xx_hcd_exit); +diff --git a/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.h b/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.h +new file mode 100644 +index 0000000..eb262fc +--- /dev/null ++++ b/drivers/sdio/hcd/s3c24xx/s3c24xx_hcd.h +@@ -0,0 +1,67 @@ ++#ifndef __SDIO_S3C24XX_HCD_H___ ++#define __SDIO_S3C24XX_HCD_H___ ++ ++#define S3C24XX_HCD_NO_RESPONSE 1 ++#define S3C24XX_HCD_RESPONSE_SHORT 2 ++#define S3C24XX_HCD_RESPONSE_LONG 3 ++#define S3C24XX_HCD_DATA_READ 4 ++#define S3C24XX_HCD_DATA_WRITE 5 ++ ++struct s3c24xx_hcd_device { ++ OS_PNPDEVICE pnp_device; /* the OS device for this HCD */ ++ OS_PNPDRIVER pnp_driver; /* the OS driver for this HCD */ ++ SDDMA_DESCRIPTION dma; ++ struct clk * clock; ++ unsigned long max_clock_rate; ++ unsigned long actual_clock_rate; ++}; ++ ++ ++/* driver wide data, this driver only supports one device, ++ * so we include the per device data here also */ ++struct s3c24xx_hcd_context { ++ PTEXT description; /* human readable device decsription */ ++ SDHCD hcd; /* HCD description for bus driver */ ++ struct s3c24xx_hcd_device device; /* the single device's info */ ++ struct platform_device *pdev; ++ struct resource *mem; ++ void __iomem *base; ++ UINT32 io_irq; ++ UINT32 cd_irq; ++ BOOL card_inserted; /* card inserted flag */ ++ BOOL cmd_processed; /* command phase was processed */ ++ UINT32 fifo_depth; /* FIFO depth for the bus mode */ ++ BOOL irq_masked; ++ UINT32 bus_width; ++ UINT32 data_size; /* Word, half word, or byte */ ++ UINT32 latest_xfer_size; ++ ++ void *io_buffer; /* Kernel address */ ++ dma_addr_t io_buffer_dma; /* Bus address */ ++ UINT32 io_buffer_size; ++ UINT32 dma_channel; ++ UINT32 dma_en; ++ struct completion dma_complete; ++ struct completion xfer_complete; ++ ++ UINT32 int_mask; ++ UINT32 int_sdio; /* Do we have SDIO interrupt on ? */ ++ ++ UINT32 complete; ++ ++ UINT32 cmdsta; ++ UINT32 dsta; ++ UINT32 fsta; ++ ++ spinlock_t lock; ++ ++ struct work_struct io_work; ++ struct work_struct irq_work; ++}; ++ ++SDIO_STATUS s3c24xx_hcd_config(PSDHCD hcd, PSDCONFIG config); ++SDIO_STATUS s3c24xx_hcd_request(PSDHCD hcd); ++ ++struct s3c24xx_hcd_context hcd_context; ++ ++#endif +-- +1.5.6.3 + |