diff options
Diffstat (limited to 'target/linux/s3c24xx/patches-2.6.24/1064-atheros_2_0_hcd.patch.patch')
-rw-r--r-- | target/linux/s3c24xx/patches-2.6.24/1064-atheros_2_0_hcd.patch.patch | 1637 |
1 files changed, 0 insertions, 1637 deletions
diff --git a/target/linux/s3c24xx/patches-2.6.24/1064-atheros_2_0_hcd.patch.patch b/target/linux/s3c24xx/patches-2.6.24/1064-atheros_2_0_hcd.patch.patch deleted file mode 100644 index b927a183c..000000000 --- a/target/linux/s3c24xx/patches-2.6.24/1064-atheros_2_0_hcd.patch.patch +++ /dev/null @@ -1,1637 +0,0 @@ -From ba3a75feb93ededf6f5bcc950bb207a4a59e1dc7 Mon Sep 17 00:00:00 2001 -From: mokopatches <mokopatches@openmoko.org> -Date: Sun, 13 Apr 2008 07:23:55 +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.5 - |