* [patch 00/15] S3C24XX SD/MMC driver series
@ 2008-06-06 15:51 Ben Dooks
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
` (14 more replies)
0 siblings, 15 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel; +Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik
This series provides support for the SD/MMC controller
contained in many of the Samsung SoCs. The patches have
been gathered from both the OpenMoko[1] kernel support
series (from various contributors) and my own updates
to update and add functionality.
I would like people to check over this series and ensure
that I have correctly attributed the patch source, and
to any objections to this seriese being submitted for
2.6.27.
The series has been kept seperate due to the fact that
there are several contributors, including myself.
[1] http://www.openmoko.org/
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-07 5:49 ` Andrew Morton
2008-06-14 16:18 ` Pierre Ossman
2008-06-06 15:51 ` [patch 02/15] MMC: S3C24XX MMC/SD driver write fixes From: Harald Welte <laforge@openmoko.org> Ben Dooks
` (13 subsequent siblings)
14 siblings, 2 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3c_mci.patch --]
[-- Type: text/plain, Size: 39914 bytes --]
This is the latest S3C MMC/SD driver by Thomas Kleffel
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/regs-sdi.h
===================================================================
--- linux-2.6.26-rc5-q.orig/include/asm-arm/arch-s3c2410/regs-sdi.h 2008-05-13 10:13:29.000000000 +0100
+++ linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/regs-sdi.h 2008-06-06 15:08:32.000000000 +0100
@@ -28,9 +28,15 @@
#define S3C2410_SDIDCNT (0x30)
#define S3C2410_SDIDSTA (0x34)
#define S3C2410_SDIFSTA (0x38)
+
#define S3C2410_SDIDATA (0x3C)
#define S3C2410_SDIIMSK (0x40)
+#define S3C2440_SDIDATA (0x40)
+#define S3C2440_SDIIMSK (0x3C)
+
+#define S3C2440_SDICON_SDRESET (1<<8)
+#define S3C2440_SDICON_MMCCLOCK (1<<5)
#define S3C2410_SDICON_BYTEORDER (1<<4)
#define S3C2410_SDICON_SDIOIRQ (1<<3)
#define S3C2410_SDICON_RWAITEN (1<<2)
@@ -42,7 +48,8 @@
#define S3C2410_SDICMDCON_LONGRSP (1<<10)
#define S3C2410_SDICMDCON_WAITRSP (1<<9)
#define S3C2410_SDICMDCON_CMDSTART (1<<8)
-#define S3C2410_SDICMDCON_INDEX (0xff)
+#define S3C2410_SDICMDCON_SENDERHOST (1<<6)
+#define S3C2410_SDICMDCON_INDEX (0x3f)
#define S3C2410_SDICMDSTAT_CRCFAIL (1<<12)
#define S3C2410_SDICMDSTAT_CMDSENT (1<<11)
@@ -51,6 +58,9 @@
#define S3C2410_SDICMDSTAT_XFERING (1<<8)
#define S3C2410_SDICMDSTAT_INDEX (0xff)
+#define S3C2440_SDIDCON_DS_BYTE (0<<22)
+#define S3C2440_SDIDCON_DS_HALFWORD (1<<22)
+#define S3C2440_SDIDCON_DS_WORD (2<<22)
#define S3C2410_SDIDCON_IRQPERIOD (1<<21)
#define S3C2410_SDIDCON_TXAFTERRESP (1<<20)
#define S3C2410_SDIDCON_RXAFTERCMD (1<<19)
@@ -59,6 +69,7 @@
#define S3C2410_SDIDCON_WIDEBUS (1<<16)
#define S3C2410_SDIDCON_DMAEN (1<<15)
#define S3C2410_SDIDCON_STOP (1<<14)
+#define S3C2440_SDIDCON_DATSTART (1<<14)
#define S3C2410_SDIDCON_DATMODE (3<<12)
#define S3C2410_SDIDCON_BLKNUM (0x7ff)
@@ -68,6 +79,7 @@
#define S3C2410_SDIDCON_XFER_RXSTART (2<<12)
#define S3C2410_SDIDCON_XFER_TXSTART (3<<12)
+#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
#define S3C2410_SDIDCNT_BLKNUM_SHIFT (12)
#define S3C2410_SDIDSTA_RDYWAITREQ (1<<10)
@@ -82,10 +94,12 @@
#define S3C2410_SDIDSTA_TXDATAON (1<<1)
#define S3C2410_SDIDSTA_RXDATAON (1<<0)
+#define S3C2440_SDIFSTA_FIFORESET (1<<16)
+#define S3C2440_SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
#define S3C2410_SDIFSTA_TFDET (1<<13)
#define S3C2410_SDIFSTA_RFDET (1<<12)
-#define S3C2410_SDIFSTA_TXHALF (1<<11)
-#define S3C2410_SDIFSTA_TXEMPTY (1<<10)
+#define S3C2410_SDIFSTA_TFHALF (1<<11)
+#define S3C2410_SDIFSTA_TFEMPTY (1<<10)
#define S3C2410_SDIFSTA_RFLAST (1<<9)
#define S3C2410_SDIFSTA_RFFULL (1<<8)
#define S3C2410_SDIFSTA_RFHALF (1<<7)
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:31:39.000000000 +0100
@@ -0,0 +1,1337 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+
+#include <asm/io.h>
+#include <asm/arch/regs-sdi.h>
+#include <asm/arch/regs-gpio.h>
+
+#include "s3cmci.h"
+
+#define DRIVER_NAME "s3c-mci"
+
+enum dbg_channels {
+ dbg_err = (1 << 0),
+ dbg_debug = (1 << 1),
+ dbg_info = (1 << 2),
+ dbg_irq = (1 << 3),
+ dbg_sg = (1 << 4),
+ dbg_dma = (1 << 5),
+ dbg_pio = (1 << 6),
+ dbg_fail = (1 << 7),
+ dbg_conf = (1 << 8),
+};
+
+static const int dbgmap_err = dbg_err | dbg_fail;
+static const int dbgmap_info = dbg_info | dbg_conf;
+static const int dbgmap_debug = dbg_debug;
+
+#define dbg(host, channels, args...) \
+ if (dbgmap_err & channels) \
+ dev_err(&host->pdev->dev, args); \
+ else if (dbgmap_info & channels) \
+ dev_info(&host->pdev->dev, args);\
+ else if (dbgmap_debug & channels) \
+ dev_dbg(&host->pdev->dev, args);
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+static struct s3c2410_dma_client s3cmci_dma_client = {
+ .name = "s3c-mci",
+};
+
+static void finalize_request(struct s3cmci_host *host);
+static void s3cmci_send_request(struct mmc_host *mmc);
+static void s3cmci_reset(struct s3cmci_host *host);
+
+#ifdef CONFIG_MMC_DEBUG
+
+static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
+{
+ u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
+ u32 datcon, datcnt, datsta, fsta, imask;
+
+ con = readl(host->base + S3C2410_SDICON);
+ pre = readl(host->base + S3C2410_SDIPRE);
+ cmdarg = readl(host->base + S3C2410_SDICMDARG);
+ cmdcon = readl(host->base + S3C2410_SDICMDCON);
+ cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
+ r0 = readl(host->base + S3C2410_SDIRSP0);
+ r1 = readl(host->base + S3C2410_SDIRSP1);
+ r2 = readl(host->base + S3C2410_SDIRSP2);
+ r3 = readl(host->base + S3C2410_SDIRSP3);
+ timer = readl(host->base + S3C2410_SDITIMER);
+ bsize = readl(host->base + S3C2410_SDIBSIZE);
+ datcon = readl(host->base + S3C2410_SDIDCON);
+ datcnt = readl(host->base + S3C2410_SDIDCNT);
+ datsta = readl(host->base + S3C2410_SDIDSTA);
+ fsta = readl(host->base + S3C2410_SDIFSTA);
+ imask = readl(host->base + host->sdiimsk);
+
+ dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
+ prefix, con, pre, timer);
+
+ dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
+ prefix, cmdcon, cmdarg, cmdsta);
+
+ dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
+ " DSTA:[%08x] DCNT:[%08x]\n",
+ prefix, datcon, fsta, datsta, datcnt);
+
+ dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
+ " R2:[%08x] R3:[%08x]\n",
+ prefix, r0, r1, r2, r3);
+}
+
+static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
+ int stop)
+{
+ snprintf(host->dbgmsg_cmd, 300,
+ "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u",
+ host->ccnt, (stop?" (STOP)":""),
+ cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
+
+ if (cmd->data) {
+ snprintf(host->dbgmsg_dat, 300,
+ "#%u bsize:%u blocks:%u bytes:%u",
+ host->dcnt, cmd->data->blksz,
+ cmd->data->blocks,
+ cmd->data->blocks * cmd->data->blksz);
+ } else {
+ host->dbgmsg_dat[0] = '\0';
+ }
+}
+
+static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
+ int fail)
+{
+ unsigned int dbglvl = fail?dbg_fail:dbg_debug;
+
+ if (!cmd)
+ return;
+
+ if (cmd->error == MMC_ERR_NONE) {
+ dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
+ host->dbgmsg_cmd, cmd->resp[0]);
+ } else {
+ dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
+ cmd->error, host->dbgmsg_cmd, host->status);
+ }
+
+ if (!cmd->data)
+ return;
+
+ if (cmd->data->error == MMC_ERR_NONE) {
+ dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
+ } else {
+ dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
+ cmd->data->error, host->dbgmsg_dat,
+ readl(host->base + S3C2410_SDIDCNT));
+ }
+}
+#endif
+
+static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
+{
+ u32 newmask;
+
+ newmask = readl(host->base + host->sdiimsk);
+ newmask|= imask;
+
+ writel(newmask, host->base + host->sdiimsk);
+
+ return newmask;
+}
+
+static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
+{
+ u32 newmask;
+
+ newmask = readl(host->base + host->sdiimsk);
+ newmask&= ~imask;
+
+ writel(newmask, host->base + host->sdiimsk);
+
+ return newmask;
+}
+
+static inline void clear_imask(struct s3cmci_host *host)
+{
+ writel(0, host->base + host->sdiimsk);
+}
+
+static inline int get_data_buffer(struct s3cmci_host *host,
+ volatile u32 *words, volatile u32 **pointer)
+{
+ struct scatterlist *sg;
+
+ if (host->pio_active == XFER_NONE)
+ return -EINVAL;
+
+ if ((!host->mrq) || (!host->mrq->data))
+ return -EINVAL;
+
+ if (host->pio_sgptr >= host->mrq->data->sg_len) {
+ dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
+ host->pio_sgptr, host->mrq->data->sg_len);
+ return -EBUSY;
+ }
+ sg = &host->mrq->data->sg[host->pio_sgptr];
+
+ *words = sg->length >> 2;
+ *pointer= page_address(sg->page) + sg->offset;
+
+ host->pio_sgptr++;
+
+ dbg(host, dbg_sg, "new buffer (%i/%i)\n",
+ host->pio_sgptr, host->mrq->data->sg_len);
+
+ return 0;
+}
+
+#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
+#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
+
+static inline void do_pio_read(struct s3cmci_host *host)
+{
+ int res;
+ u32 fifo;
+ void __iomem *from_ptr;
+
+ //write real prescaler to host, it might be set slow to fix
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ from_ptr = host->base + host->sdidata;
+
+ while ((fifo = FIFO_FILL(host))) {
+ if (!host->pio_words) {
+ res = get_data_buffer(host, &host->pio_words,
+ &host->pio_ptr);
+ if (res) {
+ host->pio_active = XFER_NONE;
+ host->complete_what = COMPLETION_FINALIZE;
+
+ dbg(host, dbg_pio, "pio_read(): "
+ "complete (no more data).\n");
+ return;
+ }
+
+ dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
+ host->pio_words, host->pio_ptr);
+ }
+
+ dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
+ "buffer:[%03i] dcnt:[%08X]\n",
+ fifo, host->pio_words,
+ readl(host->base + S3C2410_SDIDCNT));
+
+ if (fifo > host->pio_words)
+ fifo = host->pio_words;
+
+ host->pio_words-= fifo;
+ host->pio_count+= fifo;
+
+ while(fifo--) {
+ *(host->pio_ptr++) = readl(from_ptr);
+ }
+ }
+
+ if (!host->pio_words) {
+ res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
+ if (res) {
+ dbg(host, dbg_pio, "pio_read(): "
+ "complete (no more buffers).\n");
+ host->pio_active = XFER_NONE;
+ host->complete_what = COMPLETION_FINALIZE;
+
+ return;
+ }
+ }
+
+ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
+}
+
+static inline void do_pio_write(struct s3cmci_host *host)
+{
+ int res;
+ u32 fifo;
+
+ void __iomem *to_ptr;
+
+ to_ptr = host->base + host->sdidata;
+
+ while ((fifo = FIFO_FREE(host))) {
+ if (!host->pio_words) {
+ res = get_data_buffer(host, &host->pio_words,
+ &host->pio_ptr);
+ if (res) {
+ dbg(host, dbg_pio, "pio_write(): "
+ "complete (no more data).\n");
+ host->pio_active = XFER_NONE;
+
+ return;
+ }
+
+ dbg(host, dbg_pio, "pio_write(): "
+ "new source: [%i]@[%p]\n",
+ host->pio_words, host->pio_ptr);
+
+ }
+
+ if (fifo > host->pio_words)
+ fifo = host->pio_words;
+
+ host->pio_words-= fifo;
+ host->pio_count+= fifo;
+
+ while(fifo--) {
+ writel(*(host->pio_ptr++), to_ptr);
+ }
+ }
+
+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+}
+
+static void pio_tasklet(unsigned long data)
+{
+ struct s3cmci_host *host = (struct s3cmci_host *) data;
+
+
+ if (host->pio_active == XFER_WRITE)
+ do_pio_write(host);
+
+ if (host->pio_active == XFER_READ)
+ do_pio_read(host);
+
+ if (host->complete_what == COMPLETION_FINALIZE) {
+ clear_imask(host);
+ if (host->pio_active != XFER_NONE) {
+ dbg(host, dbg_err, "unfinished %s "
+ "- pio_count:[%u] pio_words:[%u]\n",
+ (host->pio_active == XFER_READ)?"read":"write",
+ host->pio_count, host->pio_words);
+
+ host->mrq->data->error = MMC_ERR_DMA;
+ }
+
+ disable_irq(host->irq);
+ finalize_request(host);
+ }
+}
+
+/*
+ * ISR for SDI Interface IRQ
+ * Communication between driver and ISR works as follows:
+ * host->mrq points to current request
+ * host->complete_what tells the ISR when the request is considered done
+ * COMPLETION_CMDSENT when the command was sent
+ * COMPLETION_RSPFIN when a response was received
+ * COMPLETION_XFERFINISH when the data transfer is finished
+ * COMPLETION_XFERFINISH_RSPFIN both of the above.
+ * host->complete_request is the completion-object the driver waits for
+ *
+ * 1) Driver sets up host->mrq and host->complete_what
+ * 2) Driver prepares the transfer
+ * 3) Driver enables interrupts
+ * 4) Driver starts transfer
+ * 5) Driver waits for host->complete_rquest
+ * 6) ISR checks for request status (errors and success)
+ * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
+ * 7) ISR completes host->complete_request
+ * 8) ISR disables interrupts
+ * 9) Driver wakes up and takes care of the request
+ *
+ * Note: "->error"-fields are expected to be set to 0 before the request
+ * was issued by mmc.c - therefore they are only set, when an error
+ * contition comes up
+ */
+
+static irqreturn_t s3cmci_irq(int irq, void *dev_id)
+{
+ struct s3cmci_host *host;
+ struct mmc_command *cmd;
+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
+ u32 mci_cclear, mci_dclear;
+ unsigned long iflags;
+
+ host = (struct s3cmci_host *)dev_id;
+
+ spin_lock_irqsave(&host->complete_lock, iflags);
+
+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
+ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
+ mci_imsk = readl(host->base + host->sdiimsk);
+ mci_cclear = 0;
+ mci_dclear = 0;
+
+ if ((host->complete_what == COMPLETION_NONE) ||
+ (host->complete_what == COMPLETION_FINALIZE)) {
+ host->status = "nothing to complete";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ if (!host->mrq) {
+ host->status = "no active mrq";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ cmd = host->cmd_is_stop?host->mrq->stop:host->mrq->cmd;
+
+ if (!cmd) {
+ host->status = "no active cmd";
+ clear_imask(host);
+ goto irq_out;
+ }
+
+ if (!host->dodma) {
+ if ((host->pio_active == XFER_WRITE) &&
+ (mci_fsta & S3C2410_SDIFSTA_TFDET)) {
+
+ disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+ tasklet_schedule(&host->pio_tasklet);
+ host->status = "pio tx";
+ }
+
+ if ((host->pio_active == XFER_READ) &&
+ (mci_fsta & S3C2410_SDIFSTA_RFDET)) {
+
+ disable_imask(host,
+ S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
+
+ tasklet_schedule(&host->pio_tasklet);
+ host->status = "pio rx";
+ }
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+ cmd->error = MMC_ERR_TIMEOUT;
+ host->status = "error: command timeout";
+ goto fail_transfer;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+ if (host->complete_what == COMPLETION_CMDSENT) {
+ host->status = "ok: command sent";
+ goto close_transfer;
+ }
+
+ mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+ if (cmd->flags & MMC_RSP_CRC) {
+ cmd->error = MMC_ERR_BADCRC;
+ host->status = "error: bad command crc";
+ goto fail_transfer;
+ }
+
+ mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+ }
+
+ if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
+ if (host->complete_what == COMPLETION_RSPFIN) {
+ host->status = "ok: command response received";
+ goto close_transfer;
+ }
+
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
+ host->complete_what = COMPLETION_XFERFINISH;
+
+ mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
+ }
+
+ /* errors handled after this point are only relevant
+ when a data transfer is in progress */
+
+ if (!cmd->data)
+ goto clear_status_bits;
+
+ /* Check for FIFO failure */
+ if (host->is2440) {
+ if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
+ host->mrq->data->error = MMC_ERR_FIFO;
+ host->status = "error: 2440 fifo failure";
+ goto fail_transfer;
+ }
+ } else {
+ if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+ cmd->data->error = MMC_ERR_FIFO;
+ host->status = "error: fifo failure";
+ goto fail_transfer;
+ }
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+ cmd->data->error = MMC_ERR_BADCRC;
+ host->status = "error: bad data crc (outgoing)";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+ cmd->data->error = MMC_ERR_BADCRC;
+ host->status = "error: bad data crc (incoming)";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+ cmd->data->error = MMC_ERR_TIMEOUT;
+ host->status = "error: data timeout";
+ goto fail_transfer;
+ }
+
+ if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+ if (host->complete_what == COMPLETION_XFERFINISH) {
+ host->status = "ok: data transfer completed";
+ goto close_transfer;
+ }
+
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+ host->complete_what = COMPLETION_RSPFIN;
+ }
+
+ mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
+ }
+
+clear_status_bits:
+ writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
+ writel(mci_dclear, host->base + S3C2410_SDIDSTA);
+
+ goto irq_out;
+
+fail_transfer:
+ host->pio_active = XFER_NONE;
+
+close_transfer:
+ host->complete_what = COMPLETION_FINALIZE;
+
+ clear_imask(host);
+ tasklet_schedule(&host->pio_tasklet);
+
+ goto irq_out;
+
+irq_out:
+ dbg(host, dbg_irq, "csta:0x%08x dsta:0x%08x "
+ "fsta:0x%08x dcnt:0x%08x status:%s.\n",
+ mci_csta, mci_dsta, mci_fsta,
+ mci_dcnt, host->status);
+
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return IRQ_HANDLED;
+
+}
+
+/*
+ * ISR for the CardDetect Pin
+*/
+
+static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id)
+{
+ struct s3cmci_host *host = (struct s3cmci_host *)dev_id;
+
+ dbg(host, dbg_irq, "card detect\n");
+
+ mmc_detect_change(host->mmc, 500);
+
+ return IRQ_HANDLED;
+}
+
+void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
+ int size, enum s3c2410_dma_buffresult result)
+{
+ unsigned long iflags;
+ u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
+ struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
+
+ mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ mci_dsta = readl(host->base + S3C2410_SDIDSTA);
+ mci_fsta = readl(host->base + S3C2410_SDIFSTA);
+ mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
+
+ if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
+ return;
+
+ if (!host->dmatogo)
+ return;
+
+ spin_lock_irqsave(&host->complete_lock, iflags);
+
+ if (result != S3C2410_RES_OK) {
+ dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
+ "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
+ mci_csta, mci_dsta, mci_fsta,
+ mci_dcnt, result, host->dmatogo);
+
+ goto fail_request;
+ }
+
+ host->dmatogo--;
+ if (host->dmatogo) {
+ dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
+ "DCNT:[%08x] toGo:%u\n",
+ size, mci_dsta, mci_dcnt, host->dmatogo);
+
+ goto out;
+ }
+
+ dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
+ size, mci_dsta, mci_dcnt);
+
+ host->complete_what = COMPLETION_FINALIZE;
+
+out:
+ tasklet_schedule(&host->pio_tasklet);
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return;
+
+
+fail_request:
+ host->mrq->data->error = MMC_ERR_DMA;
+ host->complete_what = COMPLETION_FINALIZE;
+ writel(0, host->base + host->sdiimsk);
+ goto out;
+
+}
+
+static void finalize_request(struct s3cmci_host *host)
+{
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
+ int debug_as_failure = 0;
+
+ if (host->complete_what != COMPLETION_FINALIZE)
+ return;
+
+ if (!mrq)
+ return;
+
+ if (cmd->data && (cmd->error == MMC_ERR_NONE) &&
+ (cmd->data->error == MMC_ERR_NONE)) {
+
+ if (host->dodma && (!host->dma_complete)) {
+ dbg(host, dbg_dma, "DMA Missing!\n");
+ return;
+ }
+ }
+
+ // Read response
+ cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
+ cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
+ cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
+ cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
+
+ // reset clock speed, as it could still be set low for
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ if (cmd->error)
+ debug_as_failure = 1;
+
+ if (cmd->data && cmd->data->error)
+ debug_as_failure = 1;
+
+ //if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
+
+#ifdef CONFIG_MMC_DEBUG
+ dbg_dumpcmd(host, cmd, debug_as_failure);
+#endif
+ //Cleanup controller
+ writel(0, host->base + S3C2410_SDICMDARG);
+ writel(0, host->base + S3C2410_SDIDCON);
+ writel(0, host->base + S3C2410_SDICMDCON);
+ writel(0, host->base + host->sdiimsk);
+
+ if (cmd->data && cmd->error)
+ cmd->data->error = cmd->error;
+
+ if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
+ host->cmd_is_stop = 1;
+ s3cmci_send_request(host->mmc);
+ return;
+ }
+
+ // If we have no data transfer we are finished here
+ if (!mrq->data)
+ goto request_done;
+
+ // Calulate the amout of bytes transfer, but only if there was
+ // no error
+ if (mrq->data->error == MMC_ERR_NONE) {
+ mrq->data->bytes_xfered =
+ (mrq->data->blocks * mrq->data->blksz);
+ } else {
+ mrq->data->bytes_xfered = 0;
+ }
+
+ // If we had an error while transfering data we flush the
+ // DMA channel and the fifo to clear out any garbage
+ if (mrq->data->error != MMC_ERR_NONE) {
+ if (host->dodma)
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+ if (host->is2440) {
+ //Clear failure register and reset fifo
+ writel(S3C2440_SDIFSTA_FIFORESET |
+ S3C2440_SDIFSTA_FIFOFAIL,
+ host->base + S3C2410_SDIFSTA);
+ } else {
+ u32 mci_con;
+
+ //reset fifo
+ mci_con = readl(host->base + S3C2410_SDICON);
+ mci_con|= S3C2410_SDICON_FIFORESET;
+
+ writel(mci_con, host->base + S3C2410_SDICON);
+ }
+ }
+
+request_done:
+ host->complete_what = COMPLETION_NONE;
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+
+void s3cmci_dma_setup(struct s3cmci_host *host, enum s3c2410_dmasrc source)
+{
+ static int setup_ok = 0;
+ static enum s3c2410_dmasrc last_source = -1;
+
+ if (last_source == source)
+ return;
+
+ last_source = source;
+
+ s3c2410_dma_devconfig(host->dma, source, 3,
+ host->mem->start + host->sdidata);
+
+ if (!setup_ok) {
+ s3c2410_dma_config(host->dma, 4,
+ (S3C2410_DCON_HWTRIG | S3C2410_DCON_CH0_SDI));
+ s3c2410_dma_set_buffdone_fn(host->dma, s3cmci_dma_done_callback);
+ s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+ setup_ok = 1;
+ }
+}
+
+static void s3cmci_send_command(struct s3cmci_host *host,
+ struct mmc_command *cmd)
+{
+ u32 ccon, imsk;
+
+ imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
+ S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
+ S3C2410_SDIIMSK_RESPONSECRC;
+
+ enable_imask(host, imsk);
+
+ if (cmd->data) {
+ host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
+ } else if (cmd->flags & MMC_RSP_PRESENT) {
+ host->complete_what = COMPLETION_RSPFIN;
+ } else {
+ host->complete_what = COMPLETION_CMDSENT;
+ }
+
+ writel(cmd->arg, host->base + S3C2410_SDICMDARG);
+
+ ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
+ ccon|= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
+
+ if (cmd->flags & MMC_RSP_PRESENT)
+ ccon |= S3C2410_SDICMDCON_WAITRSP;
+
+ if (cmd->flags & MMC_RSP_136)
+ ccon|= S3C2410_SDICMDCON_LONGRSP;
+
+ writel(ccon, host->base + S3C2410_SDICMDCON);
+}
+
+static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
+{
+ u32 dcon, imsk, stoptries=3;
+
+ /* write DCON register */
+
+ if (!data) {
+ writel(0, host->base + S3C2410_SDIDCON);
+ return 0;
+ }
+
+ while(readl(host->base + S3C2410_SDIDSTA) &
+ (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
+
+ dbg(host, dbg_err,
+ "mci_setup_data() transfer stillin progress.\n");
+
+ writel(0, host->base + S3C2410_SDIDCON);
+ s3cmci_reset(host);
+
+ if (0 == (stoptries--)) {
+#ifdef CONFIG_MMC_DEBUG
+ dbg_dumpregs(host, "DRF");
+#endif
+
+ return -EINVAL;
+ }
+ }
+
+ dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
+
+ if (host->dodma) {
+ dcon |= S3C2410_SDIDCON_DMAEN;
+ }
+
+ if (host->bus_width == MMC_BUS_WIDTH_4) {
+ dcon |= S3C2410_SDIDCON_WIDEBUS;
+ }
+
+ if (!(data->flags & MMC_DATA_STREAM)) {
+ dcon |= S3C2410_SDIDCON_BLOCKMODE;
+ }
+
+ if (data->flags & MMC_DATA_WRITE) {
+ dcon |= S3C2410_SDIDCON_TXAFTERRESP;
+ dcon |= S3C2410_SDIDCON_XFER_TXSTART;
+ }
+
+ if (data->flags & MMC_DATA_READ) {
+ dcon |= S3C2410_SDIDCON_RXAFTERCMD;
+ dcon |= S3C2410_SDIDCON_XFER_RXSTART;
+ }
+
+ if (host->is2440) {
+ dcon |= S3C2440_SDIDCON_DS_WORD;
+ dcon |= S3C2440_SDIDCON_DATSTART;
+ }
+
+ writel(dcon, host->base + S3C2410_SDIDCON);
+
+ /* write BSIZE register */
+
+ writel(data->blksz, host->base + S3C2410_SDIBSIZE);
+
+ /* add to IMASK register */
+ imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
+ S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
+
+ enable_imask(host, imsk);
+
+ /* write TIMER register */
+
+ if (host->is2440) {
+ writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
+ } else {
+ writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
+
+ //FIX: set slow clock to prevent timeouts on read
+ if (data->flags & MMC_DATA_READ) {
+ writel(0xFF, host->base + S3C2410_SDIPRE);
+ }
+ }
+
+ //debug_dump_registers(host, "Data setup:");
+
+ return 0;
+}
+
+static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
+{
+ int rw = (data->flags & MMC_DATA_WRITE)?1:0;
+
+ if (rw != ((data->flags & MMC_DATA_READ)?0:1))
+ return -EINVAL;
+
+ host->pio_sgptr = 0;
+ host->pio_words = 0;
+ host->pio_count = 0;
+ host->pio_active = rw?XFER_WRITE:XFER_READ;
+
+ if (rw) {
+ do_pio_write(host);
+ enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
+ } else {
+ enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
+ | S3C2410_SDIIMSK_RXFIFOLAST);
+ }
+
+ return 0;
+}
+
+static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
+{
+ int dma_len, i;
+
+ int rw = (data->flags & MMC_DATA_WRITE)?1:0;
+
+ if (rw != ((data->flags & MMC_DATA_READ)?0:1))
+ return -EINVAL;
+
+ s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+
+ dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
+
+
+ if (dma_len == 0)
+ return -ENOMEM;
+
+ host->dma_complete = 0;
+ host->dmatogo = dma_len;
+
+ for (i = 0; i < dma_len; i++) {
+ int res;
+
+ dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
+ sg_dma_address(&data->sg[i]),
+ sg_dma_len(&data->sg[i]));
+
+ res = s3c2410_dma_enqueue(host->dma, (void *) host,
+ sg_dma_address(&data->sg[i]),
+ sg_dma_len(&data->sg[i]));
+
+ if (res) {
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+ return -EBUSY;
+ }
+ }
+
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
+
+ return 0;
+}
+
+static void s3cmci_send_request(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd_is_stop?mrq->stop:mrq->cmd;
+
+ host->ccnt++;
+#ifdef CONFIG_MMC_DEBUG
+ prepare_dbgmsg(host, cmd, host->cmd_is_stop);
+#endif
+ //Clear command, data and fifo status registers
+ //Fifo clear only necessary on 2440, but doesn't hurt on 2410
+ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+ writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+ writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
+
+ if (cmd->data) {
+ int res;
+ res = s3cmci_setup_data(host, cmd->data);
+
+ host->dcnt++;
+
+ if (res) {
+ cmd->error = MMC_ERR_DMA;
+ cmd->data->error = MMC_ERR_DMA;
+
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+
+ if (host->dodma) {
+ res = s3cmci_prepare_dma(host, cmd->data);
+ } else {
+ res = s3cmci_prepare_pio(host, cmd->data);
+ }
+
+ if (res) {
+ cmd->error = MMC_ERR_DMA;
+ cmd->data->error = MMC_ERR_DMA;
+
+ mmc_request_done(mmc, mrq);
+ return;
+ }
+
+ }
+
+ // Send command
+ s3cmci_send_command(host, cmd);
+
+ // Enable Interrupt
+ enable_irq(host->irq);
+}
+
+static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ host->cmd_is_stop = 0;
+ host->mrq = mrq;
+
+ s3cmci_send_request(mmc);
+}
+
+static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+ u32 mci_psc, mci_con;
+
+ //Set power
+ mci_con = readl(host->base + S3C2410_SDICON);
+ switch(ios->power_mode) {
+ case MMC_POWER_ON:
+ case MMC_POWER_UP:
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_SDCLK);
+ s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_SDCMD);
+ s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_SDDAT0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_SDDAT1);
+ s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
+ s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+
+ if (!host->is2440)
+ mci_con|=S3C2410_SDICON_FIFORESET;
+
+ break;
+
+ case MMC_POWER_OFF:
+ default:
+ s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+
+ if (host->is2440)
+ mci_con|=S3C2440_SDICON_SDRESET;
+
+ break;
+ }
+
+ //Set clock
+ for (mci_psc=0; mci_psc<255; mci_psc++) {
+ host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
+
+ if (host->real_rate <= ios->clock)
+ break;
+ }
+
+ if(mci_psc > 255) mci_psc = 255;
+ host->prescaler = mci_psc;
+
+ writel(host->prescaler, host->base + S3C2410_SDIPRE);
+
+ //If requested clock is 0, real_rate will be 0, too
+ if (ios->clock == 0)
+ host->real_rate = 0;
+
+ //Set CLOCK_ENABLE
+ if (ios->clock)
+ mci_con |= S3C2410_SDICON_CLOCKTYPE;
+ else
+ mci_con &=~S3C2410_SDICON_CLOCKTYPE;
+
+ writel(mci_con, host->base + S3C2410_SDICON);
+
+ if ((ios->power_mode==MMC_POWER_ON)
+ || (ios->power_mode==MMC_POWER_UP)) {
+
+ dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
+ host->real_rate/1000, ios->clock/1000);
+ } else {
+ dbg(host, dbg_conf, "powered down.\n");
+ }
+
+ host->bus_width = ios->bus_width;
+
+}
+
+static void s3cmci_reset(struct s3cmci_host *host)
+{
+ u32 con = readl(host->base + S3C2410_SDICON);
+
+ con |= S3C2440_SDICON_SDRESET;
+
+ writel(con, host->base + S3C2410_SDICON);
+}
+
+static struct mmc_host_ops s3cmci_ops = {
+ .request = s3cmci_request,
+ .set_ios = s3cmci_set_ios,
+};
+
+static int s3cmci_probe(struct platform_device *pdev, int is2440)
+{
+ struct mmc_host *mmc;
+ struct s3cmci_host *host;
+
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto probe_out;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->pdev = pdev;
+
+ spin_lock_init(&host->complete_lock);
+ tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
+ if (is2440) {
+ host->is2440 = 1;
+ host->sdiimsk = S3C2440_SDIIMSK;
+ host->sdidata = S3C2440_SDIDATA;
+ host->clk_div = 1;
+ } else {
+ host->is2440 = 0;
+ host->sdiimsk = S3C2410_SDIIMSK;
+ host->sdidata = S3C2410_SDIDATA;
+ host->clk_div = 2;
+ }
+ host->dodma = 0;
+ host->complete_what = COMPLETION_NONE;
+ host->pio_active = XFER_NONE;
+
+ host->dma = S3CMCI_DMA;
+ host->irq_cd = IRQ_EINT2;
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->mem) {
+ dev_err(&pdev->dev,
+ "failed to get io memory region resouce.\n");
+
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->mem = request_mem_region(host->mem->start,
+ RESSIZE(host->mem), pdev->name);
+
+ if (!host->mem) {
+ dev_err(&pdev->dev, "failed to request io memory region.\n");
+ ret = -ENOENT;
+ goto probe_free_host;
+ }
+
+ host->base = ioremap(host->mem->start, RESSIZE(host->mem));
+ if (host->base == 0) {
+ dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
+ ret = -EINVAL;
+ goto probe_free_mem_region;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq == 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
+ ret = -EINVAL;
+ goto probe_iounmap;
+ }
+
+ if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
+ dev_err(&pdev->dev, "failed to request mci interrupt.\n");
+ ret = -ENOENT;
+ goto probe_iounmap;
+ }
+
+ disable_irq(host->irq);
+
+ s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
+ set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+
+ if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
+ dev_err(&pdev->dev,
+ "failed to request card detect interrupt.\n");
+
+ ret = -ENOENT;
+ goto probe_free_irq;
+ }
+
+ if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+ dev_err(&pdev->dev, "unable to get DMA channel.\n");
+ ret = -EBUSY;
+ goto probe_free_irq_cd;
+ }
+
+ host->clk = clk_get(&pdev->dev, "sdi");
+ if (IS_ERR(host->clk)) {
+ dev_err(&pdev->dev, "failed to find clock source.\n");
+ ret = PTR_ERR(host->clk);
+ host->clk = NULL;
+ goto probe_free_host;
+ }
+
+ if ((ret = clk_enable(host->clk))) {
+ dev_err(&pdev->dev, "failed to enable clock source.\n");
+ goto clk_free;
+ }
+
+ host->clk_rate = clk_get_rate(host->clk);
+
+ mmc->ops = &s3cmci_ops;
+ mmc->ocr_avail = MMC_VDD_32_33;
+ mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->f_min = host->clk_rate / (host->clk_div * 256);
+ mmc->f_max = host->clk_rate / host->clk_div;
+
+ mmc->max_blk_count = 4095;
+ mmc->max_blk_size = 4095;
+ mmc->max_req_size = 4095 * 512;
+ mmc->max_seg_size = mmc->max_req_size;
+
+ mmc->max_phys_segs = 128;
+ mmc->max_hw_segs = 128;
+
+ dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
+ (host->is2440?"2440":""),
+ host->base, host->irq, host->irq_cd, host->dma);
+
+ if ((ret = mmc_add_host(mmc))) {
+ dev_err(&pdev->dev, "failed to add mmc host.\n");
+ goto free_dmabuf;
+ }
+
+ platform_set_drvdata(pdev, mmc);
+
+ dev_info(&pdev->dev,"initialisation done.\n");
+ return 0;
+
+ free_dmabuf:
+ clk_disable(host->clk);
+
+ clk_free:
+ clk_put(host->clk);
+
+ probe_free_irq_cd:
+ free_irq(host->irq_cd, host);
+
+ probe_free_irq:
+ free_irq(host->irq, host);
+
+ probe_iounmap:
+ iounmap(host->base);
+
+ probe_free_mem_region:
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+
+ probe_free_host:
+ mmc_free_host(mmc);
+ probe_out:
+ return ret;
+}
+
+static int s3cmci_remove(struct platform_device *pdev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(pdev);
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+ clk_disable(host->clk);
+ clk_put(host->clk);
+ free_irq(host->irq_cd, host);
+ free_irq(host->irq, host);
+ iounmap(host->base);
+ release_mem_region(host->mem->start, RESSIZE(host->mem));
+ mmc_free_host(mmc);
+
+ return 0;
+}
+
+static int s3cmci_probe_2410(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 0);
+}
+
+static int s3cmci_probe_2412(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 1);
+}
+
+static int s3cmci_probe_2440(struct platform_device *dev)
+{
+ return s3cmci_probe(dev, 1);
+}
+
+#ifdef CONFIG_PM
+
+static int s3cmci_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+
+ return mmc_suspend_host(mmc, state);
+}
+
+static int s3cmci_resume(struct platform_device *dev)
+{
+ struct mmc_host *mmc = platform_get_drvdata(dev);
+
+ return mmc_resume_host(mmc);
+}
+
+#else /* CONFIG_PM */
+#define s3cmci_suspend NULL
+#define s3cmci_resume NULL
+#endif /* CONFIG_PM */
+
+
+static struct platform_driver s3cmci_driver_2410 =
+{
+ .driver.name = "s3c2410-sdi",
+ .probe = s3cmci_probe_2410,
+ .remove = s3cmci_remove,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2412 =
+{
+ .driver.name = "s3c2412-sdi",
+ .probe = s3cmci_probe_2412,
+ .remove = s3cmci_remove,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+static struct platform_driver s3cmci_driver_2440 =
+{
+ .driver.name = "s3c2440-sdi",
+ .probe = s3cmci_probe_2440,
+ .remove = s3cmci_remove,
+ .suspend = s3cmci_suspend,
+ .resume = s3cmci_resume,
+};
+
+
+static int __init s3cmci_init(void)
+{
+ platform_driver_register(&s3cmci_driver_2410);
+ platform_driver_register(&s3cmci_driver_2412);
+ platform_driver_register(&s3cmci_driver_2440);
+ return 0;
+}
+
+static void __exit s3cmci_exit(void)
+{
+ platform_driver_unregister(&s3cmci_driver_2410);
+ platform_driver_unregister(&s3cmci_driver_2412);
+ platform_driver_unregister(&s3cmci_driver_2440);
+}
+
+module_init(s3cmci_init);
+module_exit(s3cmci_exit);
+
+MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
+
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.h 2008-06-06 15:24:37.000000000 +0100
@@ -0,0 +1,71 @@
+/*
+ * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
+ *
+ * Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+//FIXME: DMA Resource management ?!
+#define S3CMCI_DMA 0
+
+enum s3cmci_waitfor {
+ COMPLETION_NONE,
+ COMPLETION_FINALIZE,
+ COMPLETION_CMDSENT,
+ COMPLETION_RSPFIN,
+ COMPLETION_XFERFINISH,
+ COMPLETION_XFERFINISH_RSPFIN,
+};
+
+struct s3cmci_host {
+ struct platform_device *pdev;
+ struct mmc_host *mmc;
+ struct resource *mem;
+ struct clk *clk;
+ void __iomem *base;
+ int irq;
+ int irq_cd;
+ int dma;
+
+ unsigned long clk_rate;
+ unsigned long clk_div;
+ unsigned long real_rate;
+ u8 prescaler;
+
+ int is2440;
+ unsigned sdiimsk;
+ unsigned sdidata;
+ int dodma;
+
+ volatile int dmatogo;
+
+ struct mmc_request *mrq;
+ int cmd_is_stop;
+
+ spinlock_t complete_lock;
+ volatile enum s3cmci_waitfor
+ complete_what;
+
+ volatile int dma_complete;
+
+ volatile u32 pio_sgptr;
+ volatile u32 pio_words;
+ volatile u32 pio_count;
+ volatile u32 *pio_ptr;
+#define XFER_NONE 0
+#define XFER_READ 1
+#define XFER_WRITE 2
+ volatile u32 pio_active;
+
+ int bus_width;
+
+ char dbgmsg_cmd[301];
+ char dbgmsg_dat[301];
+ volatile char *status;
+
+ unsigned int ccnt, dcnt;
+ struct tasklet_struct pio_tasklet;
+};
Index: linux-2.6.26-rc5-q/drivers/mmc/host/Kconfig
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/Kconfig 2008-06-05 15:09:44.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/Kconfig 2008-06-06 15:08:32.000000000 +0100
@@ -130,3 +130,14 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N.
+config MMC_S3C
+ tristate "Samsung S3C SD/MMC Card Interface support"
+ depends on ARCH_S3C2410 && MMC
+ help
+ This selects a driver for the MCI interface found in
+ Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
+ If you have a board based on one of those and a MMC/SD
+ slot, say Y or M here.
+
+ If unsure, say N.
+
Index: linux-2.6.26-rc5-q/drivers/mmc/host/Makefile
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/Makefile 2008-05-13 10:13:29.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/Makefile 2008-06-06 15:25:29.000000000 +0100
@@ -17,4 +17,4 @@ obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
-
+obj-$(CONFIG_MMC_S3C) += s3cmci.o
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 02/15] MMC: S3C24XX MMC/SD driver write fixes From: Harald Welte <laforge@openmoko.org>
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 03/15] MMC: DMA free fix for S3C24XX SD/MMC driver From: OpenMoko SVN " Ben Dooks
` (12 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3cmci_dbg.patch --]
[-- Type: text/plain, Size: 1051 bytes --]
This patch is a workaround of some S3C2410 MMC chip bug
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:31:39.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:45:55.000000000 +0100
@@ -441,11 +441,17 @@ static irqreturn_t s3cmci_irq(int irq, v
if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
if (cmd->flags & MMC_RSP_CRC) {
- cmd->error = MMC_ERR_BADCRC;
- host->status = "error: bad command crc";
- goto fail_transfer;
+ if (host->mrq->cmd->flags & MMC_RSP_136) {
+ dbg(host, dbg_irq,
+ "fixup: ignore CRC fail with long rsp\n");
+ } else {
+#if 0
+ cmd->error = MMC_ERR_BADCRC;
+ host->status = "error: bad command crc";
+ goto fail_transfer;
+#endif
+ }
}
-
mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
}
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 03/15] MMC: DMA free fix for S3C24XX SD/MMC driver From: OpenMoko SVN Harald Welte <laforge@openmoko.org>
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
2008-06-06 15:51 ` [patch 02/15] MMC: S3C24XX MMC/SD driver write fixes From: Harald Welte <laforge@openmoko.org> Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 04/15] MMC: S3C24XX MMC/SD stop fix From: Thomas Kleffel <tk@maintech.de> Ben Dooks
` (11 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3cmci-dma-free.patch --]
[-- Type: text/plain, Size: 755 bytes --]
Bugfix to ensure DMA channel allocated is freed on exit.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:45:55.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:45:59.000000000 +0100
@@ -1245,6 +1245,7 @@ static int s3cmci_remove(struct platform
mmc_remove_host(mmc);
clk_disable(host->clk);
clk_put(host->clk);
+ s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
free_irq(host->irq_cd, host);
free_irq(host->irq, host);
iounmap(host->base);
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 04/15] MMC: S3C24XX MMC/SD stop fix From: Thomas Kleffel <tk@maintech.de>
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (2 preceding siblings ...)
2008-06-06 15:51 ` [patch 03/15] MMC: DMA free fix for S3C24XX SD/MMC driver From: OpenMoko SVN " Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com> Ben Dooks
` (10 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3cmci-stop-fix.patch --]
[-- Type: text/plain, Size: 1174 bytes --]
Fix the request finalisation by ensuring the controller
is stopped.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Harald Welte <laforge@openmoko.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:45:59.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:01.000000000 +0100
@@ -662,7 +662,7 @@ static void finalize_request(struct s3cm
#endif
//Cleanup controller
writel(0, host->base + S3C2410_SDICMDARG);
- writel(0, host->base + S3C2410_SDIDCON);
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
writel(0, host->base + S3C2410_SDICMDCON);
writel(0, host->base + host->sdiimsk);
@@ -789,7 +789,7 @@ static int s3cmci_setup_data(struct s3cm
dbg(host, dbg_err,
"mci_setup_data() transfer stillin progress.\n");
- writel(0, host->base + S3C2410_SDIDCON);
+ writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
s3cmci_reset(host);
if (0 == (stoptries--)) {
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com>
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (3 preceding siblings ...)
2008-06-06 15:51 ` [patch 04/15] MMC: S3C24XX MMC/SD stop fix From: Thomas Kleffel <tk@maintech.de> Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-14 16:20 ` Pierre Ossman
2008-06-06 15:51 ` [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver Ben Dooks
` (9 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3cmci-unfinished-write-fix.patch --]
[-- Type: text/plain, Size: 914 bytes --]
Fix Bug #677 - I/O errors on heavy microSD writes for 2.6.22.x.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:01.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:03.000000000 +0100
@@ -313,6 +313,7 @@ static void pio_tasklet(unsigned long da
{
struct s3cmci_host *host = (struct s3cmci_host *) data;
+ disable_irq(host->irq);
if (host->pio_active == XFER_WRITE)
do_pio_write(host);
@@ -331,9 +332,9 @@ static void pio_tasklet(unsigned long da
host->mrq->data->error = MMC_ERR_DMA;
}
- disable_irq(host->irq);
finalize_request(host);
- }
+ } else
+ enable_irq(host->irq);
}
/*
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (4 preceding siblings ...)
2008-06-06 15:51 ` [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com> Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-14 16:22 ` Pierre Ossman
2008-06-06 15:51 ` [patch 07/15] MMC: Add support to invert S3C24XX write protect line Ben Dooks
` (8 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: thirdparty/openmoko-mci/s3c_mci_platform.patch --]
[-- Type: text/plain, Size: 4903 bytes --]
This patch adds platform data support to the s3mci driver. This allows
flexible board-specific configuration of set_power, card detect and read only
pins.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:03.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:04.000000000 +0100
@@ -22,6 +22,7 @@
#include <asm/io.h>
#include <asm/arch/regs-sdi.h>
#include <asm/arch/regs-gpio.h>
+#include <asm/arch/mci.h>
#include "s3cmci.h"
@@ -1009,6 +1010,9 @@ static void s3cmci_set_ios(struct mmc_ho
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_SDDAT2);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_SDDAT3);
+ if (host->pdata->set_power)
+ host->pdata->set_power(ios->power_mode, ios->vdd);
+
if (!host->is2440)
mci_con|=S3C2410_SDICON_FIFORESET;
@@ -1019,6 +1023,9 @@ static void s3cmci_set_ios(struct mmc_ho
s3c2410_gpio_setpin(S3C2410_GPE5, 0);
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+ if (host->pdata->set_power)
+ host->pdata->set_power(ios->power_mode, ios->vdd);
+
if (host->is2440)
mci_con|=S3C2440_SDICON_SDRESET;
@@ -1072,9 +1079,26 @@ static void s3cmci_reset(struct s3cmci_h
writel(con, host->base + S3C2410_SDICON);
}
+static int s3cmci_get_ro(struct mmc_host *mmc)
+{
+ struct s3cmci_host *host = mmc_priv(mmc);
+
+ if (host->pdata->gpio_wprotect == 0)
+ return 0;
+
+ return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
+}
+
static struct mmc_host_ops s3cmci_ops = {
.request = s3cmci_request,
.set_ios = s3cmci_set_ios,
+ .get_ro = s3cmci_get_ro,
+};
+
+static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
+ .gpio_detect = 0,
+ .set_power = NULL,
+ .ocr_avail = MMC_VDD_32_33,
};
static int s3cmci_probe(struct platform_device *pdev, int is2440)
@@ -1094,6 +1118,12 @@ static int s3cmci_probe(struct platform_
host->mmc = mmc;
host->pdev = pdev;
+ host->pdata = pdev->dev.platform_data;
+ if (!host->pdata) {
+ pdev->dev.platform_data = &s3cmci_def_pdata;
+ host->pdata = &s3cmci_def_pdata;
+ }
+
spin_lock_init(&host->complete_lock);
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
if (is2440) {
@@ -1112,7 +1142,8 @@ static int s3cmci_probe(struct platform_
host->pio_active = XFER_NONE;
host->dma = S3CMCI_DMA;
- host->irq_cd = IRQ_EINT2;
+ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
@@ -1154,7 +1185,7 @@ static int s3cmci_probe(struct platform_
disable_irq(host->irq);
- s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
@@ -1165,6 +1196,10 @@ static int s3cmci_probe(struct platform_
goto probe_free_irq;
}
+ if (host->pdata->gpio_wprotect)
+ s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
+ S3C2410_GPIO_INPUT);
+
if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
dev_err(&pdev->dev, "unable to get DMA channel.\n");
ret = -EBUSY;
@@ -1187,7 +1222,7 @@ static int s3cmci_probe(struct platform_
host->clk_rate = clk_get_rate(host->clk);
mmc->ops = &s3cmci_ops;
- mmc->ocr_avail = MMC_VDD_32_33;
+ mmc->ocr_avail = host->pdata->ocr_avail;
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->f_min = host->clk_rate / (host->clk_div * 256);
mmc->f_max = host->clk_rate / host->clk_div;
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.h
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.h 2008-06-06 15:24:37.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.h 2008-06-06 15:46:04.000000000 +0100
@@ -22,6 +22,7 @@ enum s3cmci_waitfor {
struct s3cmci_host {
struct platform_device *pdev;
+ struct s3c24xx_mci_pdata *pdata;
struct mmc_host *mmc;
struct resource *mem;
struct clk *clk;
Index: linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h 2008-06-06 15:46:04.000000000 +0100
@@ -0,0 +1,12 @@
+#ifndef _ARCH_MCI_H
+#define _ARCH_MCI_H
+
+struct s3c24xx_mci_pdata {
+ unsigned int gpio_detect;
+ unsigned int gpio_wprotect;
+ unsigned long ocr_avail;
+ void (*set_power)(unsigned char power_mode,
+ unsigned short vdd);
+};
+
+#endif /* _ARCH_NCI_H */
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 07/15] MMC: Add support to invert S3C24XX write protect line
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (5 preceding siblings ...)
2008-06-06 15:51 ` [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 08/15] MMC: Ensure host->mrq->data is valid in S3C24XX driver Ben Dooks
` (7 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-roinvert.patch --]
[-- Type: text/plain, Size: 1548 bytes --]
Support for inverting the sense of the MMC driver's write
protect detection line.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:04.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:30.000000000 +0100
@@ -1082,11 +1082,18 @@ static void s3cmci_reset(struct s3cmci_h
static int s3cmci_get_ro(struct mmc_host *mmc)
{
struct s3cmci_host *host = mmc_priv(mmc);
+ struct s3c24xx_mci_pdata *pdata = host->pdata;
+ int ret;
- if (host->pdata->gpio_wprotect == 0)
+ if (pdata->gpio_wprotect == 0)
return 0;
- return s3c2410_gpio_getpin(host->pdata->gpio_wprotect);
+ ret = s3c2410_gpio_getpin(pdata->gpio_wprotect);
+
+ if (pdata->wprotect_invert)
+ ret = !ret;
+
+ return ret;
}
static struct mmc_host_ops s3cmci_ops = {
Index: linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h
===================================================================
--- linux-2.6.26-rc5-q.orig/include/asm-arm/arch-s3c2410/mci.h 2008-06-06 15:46:04.000000000 +0100
+++ linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h 2008-06-06 15:46:30.000000000 +0100
@@ -2,6 +2,8 @@
#define _ARCH_MCI_H
struct s3c24xx_mci_pdata {
+ unsigned int wprotect_invert : 1;
+
unsigned int gpio_detect;
unsigned int gpio_wprotect;
unsigned long ocr_avail;
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 08/15] MMC: Ensure host->mrq->data is valid in S3C24XX driver
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (6 preceding siblings ...)
2008-06-06 15:51 ` [patch 07/15] MMC: Add support to invert S3C24XX write protect line Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX Ben Dooks
` (6 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-piotasklet-crashfix.patch --]
[-- Type: text/plain, Size: 786 bytes --]
Fix a crash if host->mrq->data is NULL on ending a transfer.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:30.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:32.000000000 +0100
@@ -330,7 +330,8 @@ static void pio_tasklet(unsigned long da
(host->pio_active == XFER_READ)?"read":"write",
host->pio_count, host->pio_words);
- host->mrq->data->error = MMC_ERR_DMA;
+ if (host->mrq->data)
+ host->mrq->data->error = MMC_ERR_DMA;
}
finalize_request(host);
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (7 preceding siblings ...)
2008-06-06 15:51 ` [patch 08/15] MMC: Ensure host->mrq->data is valid in S3C24XX driver Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-14 16:24 ` Pierre Ossman
2008-06-06 15:51 ` [patch 10/15] MMC: Fix s3c24xx driver s3c2410_dma_request() return code check Ben Dooks
` (5 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-nocdirq.patch --]
[-- Type: text/plain, Size: 2485 bytes --]
Add support to the S3C24XX MMC driver to have the card detect be on
a pin that is not IRQ capable.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:32.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:34.000000000 +0100
@@ -1150,8 +1150,6 @@ static int s3cmci_probe(struct platform_
host->pio_active = XFER_NONE;
host->dma = S3CMCI_DMA;
- host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
- s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
@@ -1193,15 +1191,23 @@ static int s3cmci_probe(struct platform_
disable_irq(host->irq);
- s3c2410_gpio_cfgpin(host->pdata->gpio_detect, S3C2410_GPIO_IRQ);
- set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+ host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect);
- if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
- dev_err(&pdev->dev,
- "failed to request card detect interrupt.\n");
+ if (host->irq_cd >= 0) {
+ set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
- ret = -ENOENT;
- goto probe_free_irq;
+ if (request_irq(host->irq_cd, s3cmci_irq_cd,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRIVER_NAME, host)) {
+ dev_err(&pdev->dev,
+ "failed to request card detect interrupt.\n");
+
+ ret = -ENOENT;
+ goto probe_free_irq;
+ }
+ } else {
+ dev_warn(&pdev->dev, "host detect has no irq available\n");
+ s3c2410_gpio_cfgpin(host->pdata->gpio_detect,
+ S3C2410_GPIO_INPUT);
}
if (host->pdata->gpio_wprotect)
@@ -1264,7 +1270,8 @@ static int s3cmci_probe(struct platform_
clk_put(host->clk);
probe_free_irq_cd:
- free_irq(host->irq_cd, host);
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
probe_free_irq:
free_irq(host->irq, host);
@@ -1290,8 +1297,11 @@ static int s3cmci_remove(struct platform
clk_disable(host->clk);
clk_put(host->clk);
s3c2410_dma_free(S3CMCI_DMA, &s3cmci_dma_client);
- free_irq(host->irq_cd, host);
+
+ if (host->irq_cd >= 0)
+ free_irq(host->irq_cd, host);
free_irq(host->irq, host);
+
iounmap(host->base);
release_mem_region(host->mem->start, RESSIZE(host->mem));
mmc_free_host(mmc);
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 10/15] MMC: Fix s3c24xx driver s3c2410_dma_request() return code check.
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (8 preceding siblings ...)
2008-06-06 15:51 ` [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver Ben Dooks
` (4 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-dma-retcode-fix.patch --]
[-- Type: text/plain, Size: 990 bytes --]
The driver should be checking for a negative error code from
s3c2410_dma_request(), not non-zero. Newer kernels now return
the DMA channel number that was allocated by the request.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:34.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:35.000000000 +0100
@@ -1214,7 +1214,7 @@ static int s3cmci_probe(struct platform_
s3c2410_gpio_cfgpin(host->pdata->gpio_wprotect,
S3C2410_GPIO_INPUT);
- if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
+ if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) {
dev_err(&pdev->dev, "unable to get DMA channel.\n");
ret = -EBUSY;
goto probe_free_irq_cd;
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (9 preceding siblings ...)
2008-06-06 15:51 ` [patch 10/15] MMC: Fix s3c24xx driver s3c2410_dma_request() return code check Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-14 16:25 ` Pierre Ossman
2008-06-06 15:51 ` [patch 12/15] MMC: S3C24XX MMC/SD driver fixes for sg_ access Ben Dooks
` (3 subsequent siblings)
14 siblings, 1 reply; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-retcode-fixes.patch --]
[-- Type: text/plain, Size: 942 bytes --]
Fix the error codes
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:35.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:28.000000000 +0100
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
+#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
@@ -28,6 +29,12 @@
#define DRIVER_NAME "s3c-mci"
+#define MMC_ERR_NONE (0)
+#define MMC_ERR_DMA EINVAL
+#define MMC_ERR_TIMEOUT ETIMEDOUT
+#define MMC_ERR_BADCRC EILSEQ
+#define MMC_ERR_FIFO EILSEQ
+
enum dbg_channels {
dbg_err = (1 << 0),
dbg_debug = (1 << 1),
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 12/15] MMC: S3C24XX MMC/SD driver fixes for sg_ access.
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (10 preceding siblings ...)
2008-06-06 15:51 ` [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 13/15] MMC: s3cmci: Add MODULE_ALIAS() entries for the platform devices Ben Dooks
` (2 subsequent siblings)
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-2624-fixes.patch --]
[-- Type: text/plain, Size: 702 bytes --]
Fix sg_ usage in the S3C24XX MMC driver.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:28.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:30.000000000 +0100
@@ -204,7 +204,7 @@ static inline int get_data_buffer(struct
sg = &host->mrq->data->sg[host->pio_sgptr];
*words = sg->length >> 2;
- *pointer= page_address(sg->page) + sg->offset;
+ *pointer= sg_virt(sg);
host->pio_sgptr++;
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 13/15] MMC: s3cmci: Add MODULE_ALIAS() entries for the platform devices
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (11 preceding siblings ...)
2008-06-06 15:51 ` [patch 12/15] MMC: S3C24XX MMC/SD driver fixes for sg_ access Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 14/15] MMC: Fix use of msecs where jiffies are needed Ben Dooks
2008-06-06 15:51 ` [patch 15/15] MMC: Add media presence detection to request handling Ben Dooks
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-modalias.patch --]
[-- Type: text/plain, Size: 820 bytes --]
Add MODULE_ALIAS() declerations for all the supported platform
devices for this driver.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:30.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:32.000000000 +0100
@@ -1402,4 +1402,6 @@ module_exit(s3cmci_exit);
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Kleffel <tk@maintech.de>");
-
+MODULE_ALIAS("platform:s3c2410-sdi");
+MODULE_ALIAS("platform:s3c2412-sdi");
+MODULE_ALIAS("platform:s3c2440-sdi");
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 14/15] MMC: Fix use of msecs where jiffies are needed
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (12 preceding siblings ...)
2008-06-06 15:51 ` [patch 13/15] MMC: s3cmci: Add MODULE_ALIAS() entries for the platform devices Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
2008-06-06 15:51 ` [patch 15/15] MMC: Add media presence detection to request handling Ben Dooks
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-detectchangefix.patch --]
[-- Type: text/plain, Size: 759 bytes --]
mmc_detect_change() takes jiffies, not msecs. Convert the
previous value of msecs into jiffies before calling.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:32.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:34.000000000 +0100
@@ -567,7 +567,7 @@ static irqreturn_t s3cmci_irq_cd(int irq
dbg(host, dbg_irq, "card detect\n");
- mmc_detect_change(host->mmc, 500);
+ mmc_detect_change(host->mmc, msecs_to_jiffies(500));
return IRQ_HANDLED;
}
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* [patch 15/15] MMC: Add media presence detection to request handling.
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
` (13 preceding siblings ...)
2008-06-06 15:51 ` [patch 14/15] MMC: Fix use of msecs where jiffies are needed Ben Dooks
@ 2008-06-06 15:51 ` Ben Dooks
14 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-06 15:51 UTC (permalink / raw)
To: linux-kernel
Cc: Pierre Ossman, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: simtec/s3c24xx-sdmmc-add-mediacheck.patch --]
[-- Type: text/plain, Size: 2118 bytes --]
Ensure that we have physical media present before attempting to
send a request to a card. This ensures that we do not get flooded
by errors from commands that can never be completed timing out.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
===================================================================
--- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:34.000000000 +0100
+++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:47:35.000000000 +0100
@@ -991,6 +991,20 @@ static void s3cmci_send_request(struct m
enable_irq(host->irq);
}
+static int s3cmci_card_present(struct s3cmci_host *host)
+{
+ struct s3c24xx_mci_pdata *pdata = host->pdata;
+ int ret;
+
+ /* if we can't detect the presence, then always have card. */
+ if (pdata->gpio_detect == 0)
+ return 1;
+
+ ret = s3c2410_gpio_getpin(pdata->gpio_detect) ? 0 : 1;
+
+ return ret ^ pdata->detect_invert;
+}
+
static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct s3cmci_host *host = mmc_priv(mmc);
@@ -998,7 +1012,12 @@ static void s3cmci_request(struct mmc_ho
host->cmd_is_stop = 0;
host->mrq = mrq;
- s3cmci_send_request(mmc);
+ if (!s3cmci_card_present(host)) {
+ dbg(host, dbg_err, "%s: no medium present\n", __func__);
+ host->mrq->cmd->error = -ENOMEDIUM;
+ mmc_request_done(mmc, mrq);
+ } else
+ s3cmci_send_request(mmc);
}
static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
Index: linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h
===================================================================
--- linux-2.6.26-rc5-q.orig/include/asm-arm/arch-s3c2410/mci.h 2008-06-06 15:46:30.000000000 +0100
+++ linux-2.6.26-rc5-q/include/asm-arm/arch-s3c2410/mci.h 2008-06-06 15:47:35.000000000 +0100
@@ -3,6 +3,7 @@
struct s3c24xx_mci_pdata {
unsigned int wprotect_invert : 1;
+ unsigned int detect_invert : 1; /* set => detect active high. */
unsigned int gpio_detect;
unsigned int gpio_wprotect;
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
@ 2008-06-07 5:49 ` Andrew Morton
2008-06-07 12:22 ` Ben Dooks
2008-06-14 16:18 ` Pierre Ossman
1 sibling, 1 reply; 28+ messages in thread
From: Andrew Morton @ 2008-06-07 5:49 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Pierre Ossman, Harald Welte, Thomas Kleffel,
Roman Moravcik
On Fri, 06 Jun 2008 16:51:18 +0100 Ben Dooks <ben-linux@fluff.org> wrote:
> This is the latest S3C MMC/SD driver by Thomas Kleffel
If Thomas was the author then this patch should have had
From: Thomas Kleffel <tk@maintech.de>
right at the top of the changelog so that the commit is correctly
attributed.
> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Please pass these patches through chechpatch? This one alone generates
total: 40 errors, 29 warnings, 1488 lines checked
many of which you would have fixed had you been aware of them. That's
what checkpatch is for.
>
> ...
>
> +#define dbg(host, channels, args...) \
> + if (dbgmap_err & channels) \
> + dev_err(&host->pdev->dev, args); \
> + else if (dbgmap_info & channels) \
> + dev_info(&host->pdev->dev, args);\
> + else if (dbgmap_debug & channels) \
> + dev_dbg(&host->pdev->dev, args);
> +
> +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
Could be written in C. That would fix the bug wherein this macro
evaluates its argument twice. "resource" is misspelled.
> +static struct s3c2410_dma_client s3cmci_dma_client = {
> + .name = "s3c-mci",
> +};
> +
> +static void finalize_request(struct s3cmci_host *host);
> +static void s3cmci_send_request(struct mmc_host *mmc);
> +static void s3cmci_reset(struct s3cmci_host *host);
> +
> +#ifdef CONFIG_MMC_DEBUG
> +
> +static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
too large to inline.
> +{
> + u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
> + u32 datcon, datcnt, datsta, fsta, imask;
> +
> + con = readl(host->base + S3C2410_SDICON);
> + pre = readl(host->base + S3C2410_SDIPRE);
> + cmdarg = readl(host->base + S3C2410_SDICMDARG);
> + cmdcon = readl(host->base + S3C2410_SDICMDCON);
> + cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
> + r0 = readl(host->base + S3C2410_SDIRSP0);
> + r1 = readl(host->base + S3C2410_SDIRSP1);
> + r2 = readl(host->base + S3C2410_SDIRSP2);
> + r3 = readl(host->base + S3C2410_SDIRSP3);
> + timer = readl(host->base + S3C2410_SDITIMER);
> + bsize = readl(host->base + S3C2410_SDIBSIZE);
> + datcon = readl(host->base + S3C2410_SDIDCON);
> + datcnt = readl(host->base + S3C2410_SDIDCNT);
> + datsta = readl(host->base + S3C2410_SDIDSTA);
> + fsta = readl(host->base + S3C2410_SDIFSTA);
> + imask = readl(host->base + host->sdiimsk);
> +
> + dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
> + prefix, con, pre, timer);
> +
> + dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
> + prefix, cmdcon, cmdarg, cmdsta);
> +
> + dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
> + " DSTA:[%08x] DCNT:[%08x]\n",
> + prefix, datcon, fsta, datsta, datcnt);
> +
> + dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
> + " R2:[%08x] R3:[%08x]\n",
> + prefix, r0, r1, r2, r3);
> +}
> +
>
> ...
>
> +static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
> + int fail)
> +{
> + unsigned int dbglvl = fail?dbg_fail:dbg_debug;
>
Spaces around the ? and :, please.
checkpatch misses this.
> + if (!cmd)
> + return;
> +
> + if (cmd->error == MMC_ERR_NONE) {
> + dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
> + host->dbgmsg_cmd, cmd->resp[0]);
> + } else {
> + dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
> + cmd->error, host->dbgmsg_cmd, host->status);
> + }
> +
> + if (!cmd->data)
> + return;
> +
> + if (cmd->data->error == MMC_ERR_NONE) {
> + dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
> + } else {
> + dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
> + cmd->data->error, host->dbgmsg_dat,
> + readl(host->base + S3C2410_SDIDCNT));
> + }
> +}
> +#endif
> +
> +static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
> +{
> + u32 newmask;
> +
> + newmask = readl(host->base + host->sdiimsk);
> + newmask|= imask;
> +
> + writel(newmask, host->base + host->sdiimsk);
> +
> + return newmask;
> +}
> +
> +static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
> +{
> + u32 newmask;
> +
> + newmask = readl(host->base + host->sdiimsk);
> + newmask&= ~imask;
> +
> + writel(newmask, host->base + host->sdiimsk);
> +
> + return newmask;
> +}
Probably too large to inline. A useful metric is to build the .o file
with and without the `inline' statements and see which results in the
smaller /bin/size output. Smaller is usuall faster. And it's smaller.
> +static inline void clear_imask(struct s3cmci_host *host)
> +{
> + writel(0, host->base + host->sdiimsk);
> +}
> +
> +static inline int get_data_buffer(struct s3cmci_host *host,
> + volatile u32 *words, volatile u32 **pointer)
> +{
> + struct scatterlist *sg;
> +
> + if (host->pio_active == XFER_NONE)
> + return -EINVAL;
> +
> + if ((!host->mrq) || (!host->mrq->data))
> + return -EINVAL;
> +
> + if (host->pio_sgptr >= host->mrq->data->sg_len) {
> + dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
> + host->pio_sgptr, host->mrq->data->sg_len);
> + return -EBUSY;
> + }
> + sg = &host->mrq->data->sg[host->pio_sgptr];
> +
> + *words = sg->length >> 2;
> + *pointer= page_address(sg->page) + sg->offset;
> +
> + host->pio_sgptr++;
> +
> + dbg(host, dbg_sg, "new buffer (%i/%i)\n",
> + host->pio_sgptr, host->mrq->data->sg_len);
> +
> + return 0;
> +}
This one has three callsites!
> +#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
> +#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
These didn't need to be implemented as macros.
> +
> +static inline void do_pio_read(struct s3cmci_host *host)
> +{
> + int res;
> + u32 fifo;
> + void __iomem *from_ptr;
> +
> + //write real prescaler to host, it might be set slow to fix
> + writel(host->prescaler, host->base + S3C2410_SDIPRE);
> +
> + from_ptr = host->base + host->sdidata;
> +
> + while ((fifo = FIFO_FILL(host))) {
> + if (!host->pio_words) {
> + res = get_data_buffer(host, &host->pio_words,
> + &host->pio_ptr);
> + if (res) {
> + host->pio_active = XFER_NONE;
> + host->complete_what = COMPLETION_FINALIZE;
> +
> + dbg(host, dbg_pio, "pio_read(): "
> + "complete (no more data).\n");
> + return;
> + }
> +
> + dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
> + host->pio_words, host->pio_ptr);
> + }
> +
> + dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
> + "buffer:[%03i] dcnt:[%08X]\n",
> + fifo, host->pio_words,
> + readl(host->base + S3C2410_SDIDCNT));
> +
> + if (fifo > host->pio_words)
> + fifo = host->pio_words;
> +
> + host->pio_words-= fifo;
> + host->pio_count+= fifo;
> +
> + while(fifo--) {
> + *(host->pio_ptr++) = readl(from_ptr);
> + }
> + }
> +
> + if (!host->pio_words) {
> + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
> + if (res) {
> + dbg(host, dbg_pio, "pio_read(): "
> + "complete (no more buffers).\n");
> + host->pio_active = XFER_NONE;
> + host->complete_what = COMPLETION_FINALIZE;
> +
> + return;
> + }
> + }
> +
> + enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
> +}
> +
> +static inline void do_pio_write(struct s3cmci_host *host)
> +{
> + int res;
> + u32 fifo;
> +
> + void __iomem *to_ptr;
> +
> + to_ptr = host->base + host->sdidata;
> +
> + while ((fifo = FIFO_FREE(host))) {
> + if (!host->pio_words) {
> + res = get_data_buffer(host, &host->pio_words,
> + &host->pio_ptr);
> + if (res) {
> + dbg(host, dbg_pio, "pio_write(): "
> + "complete (no more data).\n");
> + host->pio_active = XFER_NONE;
> +
> + return;
> + }
> +
> + dbg(host, dbg_pio, "pio_write(): "
> + "new source: [%i]@[%p]\n",
> + host->pio_words, host->pio_ptr);
> +
> + }
> +
> + if (fifo > host->pio_words)
> + fifo = host->pio_words;
> +
> + host->pio_words-= fifo;
> + host->pio_count+= fifo;
> +
> + while(fifo--) {
> + writel(*(host->pio_ptr++), to_ptr);
> + }
> + }
> +
> + enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
> +}
Two callsites :( This is one bloaty driver.
> +static void pio_tasklet(unsigned long data)
> +{
> + struct s3cmci_host *host = (struct s3cmci_host *) data;
> +
> +
> + if (host->pio_active == XFER_WRITE)
> + do_pio_write(host);
> +
> + if (host->pio_active == XFER_READ)
> + do_pio_read(host);
> +
> + if (host->complete_what == COMPLETION_FINALIZE) {
> + clear_imask(host);
> + if (host->pio_active != XFER_NONE) {
> + dbg(host, dbg_err, "unfinished %s "
> + "- pio_count:[%u] pio_words:[%u]\n",
> + (host->pio_active == XFER_READ)?"read":"write",
> + host->pio_count, host->pio_words);
> +
> + host->mrq->data->error = MMC_ERR_DMA;
> + }
> +
> + disable_irq(host->irq);
> + finalize_request(host);
> + }
> +}
I see no tasklet_disable() in the driver. Is there anything to prevent
the tasklet from running after device close, rmmod, etc?
The disable_irq()/enable_irq() on every interrupt will be quite
inefficient and hostile to any device which shares the interrupt. WHy
is it necessary?
> +/*
> + * ISR for SDI Interface IRQ
> + * Communication between driver and ISR works as follows:
> + * host->mrq points to current request
> + * host->complete_what tells the ISR when the request is considered done
> + * COMPLETION_CMDSENT when the command was sent
> + * COMPLETION_RSPFIN when a response was received
> + * COMPLETION_XFERFINISH when the data transfer is finished
> + * COMPLETION_XFERFINISH_RSPFIN both of the above.
> + * host->complete_request is the completion-object the driver waits for
> + *
> + * 1) Driver sets up host->mrq and host->complete_what
> + * 2) Driver prepares the transfer
> + * 3) Driver enables interrupts
> + * 4) Driver starts transfer
> + * 5) Driver waits for host->complete_rquest
> + * 6) ISR checks for request status (errors and success)
> + * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
> + * 7) ISR completes host->complete_request
> + * 8) ISR disables interrupts
> + * 9) Driver wakes up and takes care of the request
> + *
> + * Note: "->error"-fields are expected to be set to 0 before the request
> + * was issued by mmc.c - therefore they are only set, when an error
> + * contition comes up
> + */
> +
> +static irqreturn_t s3cmci_irq(int irq, void *dev_id)
> +{
> + struct s3cmci_host *host;
> + struct mmc_command *cmd;
> + u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
> + u32 mci_cclear, mci_dclear;
> + unsigned long iflags;
> +
> + host = (struct s3cmci_host *)dev_id;
Unneeded and undesirable cast of void*.
> + spin_lock_irqsave(&host->complete_lock, iflags);
> +
> + mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
> + mci_dsta = readl(host->base + S3C2410_SDIDSTA);
> + mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
> + mci_fsta = readl(host->base + S3C2410_SDIFSTA);
> + mci_imsk = readl(host->base + host->sdiimsk);
>
> ...
>
> +void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
> + int size, enum s3c2410_dma_buffresult result)
> +{
> + unsigned long iflags;
> + u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
> + struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
> +
> + mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
> + mci_dsta = readl(host->base + S3C2410_SDIDSTA);
> + mci_fsta = readl(host->base + S3C2410_SDIFSTA);
> + mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
> +
> + if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
> + return;
> +
> + if (!host->dmatogo)
> + return;
Can these things all really happen?
> + spin_lock_irqsave(&host->complete_lock, iflags);
> +
> + if (result != S3C2410_RES_OK) {
> + dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
> + "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
> + mci_csta, mci_dsta, mci_fsta,
> + mci_dcnt, result, host->dmatogo);
> +
> + goto fail_request;
> + }
> +
> + host->dmatogo--;
> + if (host->dmatogo) {
> + dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
> + "DCNT:[%08x] toGo:%u\n",
> + size, mci_dsta, mci_dcnt, host->dmatogo);
> +
> + goto out;
> + }
> +
> + dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
> + size, mci_dsta, mci_dcnt);
> +
> + host->complete_what = COMPLETION_FINALIZE;
> +
> +out:
> + tasklet_schedule(&host->pio_tasklet);
> + spin_unlock_irqrestore(&host->complete_lock, iflags);
> + return;
> +
> +
> +fail_request:
> + host->mrq->data->error = MMC_ERR_DMA;
> + host->complete_what = COMPLETION_FINALIZE;
> + writel(0, host->base + host->sdiimsk);
> + goto out;
> +
> +}
> +
>
> ...
>
> +static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
> +{
> + u32 dcon, imsk, stoptries=3;
> +
> + /* write DCON register */
> +
> + if (!data) {
> + writel(0, host->base + S3C2410_SDIDCON);
> + return 0;
> + }
> +
> + while(readl(host->base + S3C2410_SDIDSTA) &
> + (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
> +
> + dbg(host, dbg_err,
> + "mci_setup_data() transfer stillin progress.\n");
> +
> + writel(0, host->base + S3C2410_SDIDCON);
> + s3cmci_reset(host);
> +
> + if (0 == (stoptries--)) {
Swap 'em, please. It's not necessary.
> +#ifdef CONFIG_MMC_DEBUG
> + dbg_dumpregs(host, "DRF");
> +#endif
> +
> + return -EINVAL;
> + }
> + }
> +
> + dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
> +
> + if (host->dodma) {
> + dcon |= S3C2410_SDIDCON_DMAEN;
> + }
> +
> + if (host->bus_width == MMC_BUS_WIDTH_4) {
> + dcon |= S3C2410_SDIDCON_WIDEBUS;
> + }
> +
> + if (!(data->flags & MMC_DATA_STREAM)) {
> + dcon |= S3C2410_SDIDCON_BLOCKMODE;
> + }
> +
> + if (data->flags & MMC_DATA_WRITE) {
> + dcon |= S3C2410_SDIDCON_TXAFTERRESP;
> + dcon |= S3C2410_SDIDCON_XFER_TXSTART;
> + }
> +
> + if (data->flags & MMC_DATA_READ) {
> + dcon |= S3C2410_SDIDCON_RXAFTERCMD;
> + dcon |= S3C2410_SDIDCON_XFER_RXSTART;
> + }
> +
>
> ...
>
> +static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
> +{
> + int dma_len, i;
> +
> + int rw = (data->flags & MMC_DATA_WRITE)?1:0;
> +
> + if (rw != ((data->flags & MMC_DATA_READ)?0:1))
> + return -EINVAL;
That's pretty clumsy looking.
Setting both MMC_DATA_WRITE and MMC_DATA_READ would be a programming
bug, wouldn't it? If so, BUG would be a more appropriate response.
> + s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
> + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> +
> + dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
> + (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
> +
> +
> + if (dma_len == 0)
> + return -ENOMEM;
> +
> + host->dma_complete = 0;
> + host->dmatogo = dma_len;
> +
> + for (i = 0; i < dma_len; i++) {
> + int res;
> +
> + dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
> + sg_dma_address(&data->sg[i]),
> + sg_dma_len(&data->sg[i]));
> +
> + res = s3c2410_dma_enqueue(host->dma, (void *) host,
> + sg_dma_address(&data->sg[i]),
> + sg_dma_len(&data->sg[i]));
> +
> + if (res) {
> + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> + return -EBUSY;
> + }
> + }
> +
> + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
> +
> + return 0;
> +}
> +
>
> ...
>
> +
> + host->irq = platform_get_irq(pdev, 0);
> + if (host->irq == 0) {
> + dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
> + ret = -EINVAL;
> + goto probe_iounmap;
> + }
> +
> + if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
So these interrupts cannot be shared? The driver will never be used in
an environment where interrupt sharing is possible?
> + dev_err(&pdev->dev, "failed to request mci interrupt.\n");
> + ret = -ENOENT;
> + goto probe_iounmap;
> + }
> +
> + disable_irq(host->irq);
> +
> + s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
> + set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
> +
> + if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
> + dev_err(&pdev->dev,
> + "failed to request card detect interrupt.\n");
> +
> + ret = -ENOENT;
> + goto probe_free_irq;
> + }
> +
> + if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
> + dev_err(&pdev->dev, "unable to get DMA channel.\n");
> + ret = -EBUSY;
> + goto probe_free_irq_cd;
> + }
> +
> + host->clk = clk_get(&pdev->dev, "sdi");
> + if (IS_ERR(host->clk)) {
> + dev_err(&pdev->dev, "failed to find clock source.\n");
> + ret = PTR_ERR(host->clk);
> + host->clk = NULL;
> + goto probe_free_host;
> + }
> +
> + if ((ret = clk_enable(host->clk))) {
> + dev_err(&pdev->dev, "failed to enable clock source.\n");
> + goto clk_free;
> + }
> +
> + host->clk_rate = clk_get_rate(host->clk);
> +
> + mmc->ops = &s3cmci_ops;
> + mmc->ocr_avail = MMC_VDD_32_33;
> + mmc->caps = MMC_CAP_4_BIT_DATA;
> + mmc->f_min = host->clk_rate / (host->clk_div * 256);
> + mmc->f_max = host->clk_rate / host->clk_div;
> +
> + mmc->max_blk_count = 4095;
> + mmc->max_blk_size = 4095;
> + mmc->max_req_size = 4095 * 512;
> + mmc->max_seg_size = mmc->max_req_size;
> +
> + mmc->max_phys_segs = 128;
> + mmc->max_hw_segs = 128;
> +
> + dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
> + (host->is2440?"2440":""),
> + host->base, host->irq, host->irq_cd, host->dma);
> +
> + if ((ret = mmc_add_host(mmc))) {
> + dev_err(&pdev->dev, "failed to add mmc host.\n");
> + goto free_dmabuf;
> + }
> +
> + platform_set_drvdata(pdev, mmc);
> +
> + dev_info(&pdev->dev,"initialisation done.\n");
> + return 0;
> +
> + free_dmabuf:
> + clk_disable(host->clk);
> +
> + clk_free:
> + clk_put(host->clk);
> +
> + probe_free_irq_cd:
> + free_irq(host->irq_cd, host);
> +
> + probe_free_irq:
> + free_irq(host->irq, host);
> +
> + probe_iounmap:
> + iounmap(host->base);
> +
> + probe_free_mem_region:
> + release_mem_region(host->mem->start, RESSIZE(host->mem));
> +
> + probe_free_host:
> + mmc_free_host(mmc);
> + probe_out:
> + return ret;
> +}
> +
>
> ...
>
> +//FIXME: DMA Resource management ?!
?
> +#define S3CMCI_DMA 0
> +
> +enum s3cmci_waitfor {
> + COMPLETION_NONE,
> + COMPLETION_FINALIZE,
> + COMPLETION_CMDSENT,
> + COMPLETION_RSPFIN,
> + COMPLETION_XFERFINISH,
> + COMPLETION_XFERFINISH_RSPFIN,
> +};
> +
> +struct s3cmci_host {
> + struct platform_device *pdev;
> + struct mmc_host *mmc;
> + struct resource *mem;
> + struct clk *clk;
> + void __iomem *base;
> + int irq;
> + int irq_cd;
> + int dma;
> +
> + unsigned long clk_rate;
> + unsigned long clk_div;
> + unsigned long real_rate;
> + u8 prescaler;
> +
> + int is2440;
> + unsigned sdiimsk;
> + unsigned sdidata;
> + int dodma;
> +
> + volatile int dmatogo;
> +
> + struct mmc_request *mrq;
> + int cmd_is_stop;
> +
> + spinlock_t complete_lock;
> + volatile enum s3cmci_waitfor
> + complete_what;
> +
> + volatile int dma_complete;
> +
> + volatile u32 pio_sgptr;
> + volatile u32 pio_words;
> + volatile u32 pio_count;
> + volatile u32 *pio_ptr;
volatiles. checkpatch..
> +#define XFER_NONE 0
> +#define XFER_READ 1
> +#define XFER_WRITE 2
> + volatile u32 pio_active;
> +
> + int bus_width;
> +
> + char dbgmsg_cmd[301];
> + char dbgmsg_dat[301];
> + volatile char *status;
> +
> + unsigned int ccnt, dcnt;
> + struct tasklet_struct pio_tasklet;
> +};
Those problems were really really basic stuff. kernel drivers 101.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-07 5:49 ` Andrew Morton
@ 2008-06-07 12:22 ` Ben Dooks
2008-06-07 19:13 ` Andrew Morton
0 siblings, 1 reply; 28+ messages in thread
From: Ben Dooks @ 2008-06-07 12:22 UTC (permalink / raw)
To: Andrew Morton; +Cc: linux-kernel
On Fri, Jun 06, 2008 at 10:49:28PM -0700, Andrew Morton wrote:
> On Fri, 06 Jun 2008 16:51:18 +0100 Ben Dooks <ben-linux@fluff.org> wrote:
>
> > This is the latest S3C MMC/SD driver by Thomas Kleffel
>
> If Thomas was the author then this patch should have had
>
> From: Thomas Kleffel <tk@maintech.de>
I thought I'd done this for all the relevant patches, will fix thus.
> right at the top of the changelog so that the commit is correctly
> attributed.
>
> > Signed-off-by: Ben Dooks <ben-linux@fluff.org>
>
> Please pass these patches through chechpatch? This one alone generates
>
> total: 40 errors, 29 warnings, 1488 lines checked
>
> many of which you would have fixed had you been aware of them. That's
> what checkpatch is for.
Is it ok to fit another patch after this one to fixup these problems?
> >
> > ...
> >
> > +#define dbg(host, channels, args...) \
> > + if (dbgmap_err & channels) \
> > + dev_err(&host->pdev->dev, args); \
> > + else if (dbgmap_info & channels) \
> > + dev_info(&host->pdev->dev, args);\
> > + else if (dbgmap_debug & channels) \
> > + dev_dbg(&host->pdev->dev, args);
> > +
> > +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
>
> Could be written in C. That would fix the bug wherein this macro
> evaluates its argument twice. "resource" is misspelled.
>
> > +static struct s3c2410_dma_client s3cmci_dma_client = {
> > + .name = "s3c-mci",
> > +};
> > +
> > +static void finalize_request(struct s3cmci_host *host);
> > +static void s3cmci_send_request(struct mmc_host *mmc);
> > +static void s3cmci_reset(struct s3cmci_host *host);
> > +
> > +#ifdef CONFIG_MMC_DEBUG
> > +
> > +static inline void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
>
> too large to inline.
yeah, fix that.
> > +{
> > + u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
> > + u32 datcon, datcnt, datsta, fsta, imask;
> > +
> > + con = readl(host->base + S3C2410_SDICON);
> > + pre = readl(host->base + S3C2410_SDIPRE);
> > + cmdarg = readl(host->base + S3C2410_SDICMDARG);
> > + cmdcon = readl(host->base + S3C2410_SDICMDCON);
> > + cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
> > + r0 = readl(host->base + S3C2410_SDIRSP0);
> > + r1 = readl(host->base + S3C2410_SDIRSP1);
> > + r2 = readl(host->base + S3C2410_SDIRSP2);
> > + r3 = readl(host->base + S3C2410_SDIRSP3);
> > + timer = readl(host->base + S3C2410_SDITIMER);
> > + bsize = readl(host->base + S3C2410_SDIBSIZE);
> > + datcon = readl(host->base + S3C2410_SDIDCON);
> > + datcnt = readl(host->base + S3C2410_SDIDCNT);
> > + datsta = readl(host->base + S3C2410_SDIDSTA);
> > + fsta = readl(host->base + S3C2410_SDIFSTA);
> > + imask = readl(host->base + host->sdiimsk);
> > +
> > + dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
> > + prefix, con, pre, timer);
> > +
> > + dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
> > + prefix, cmdcon, cmdarg, cmdsta);
> > +
> > + dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
> > + " DSTA:[%08x] DCNT:[%08x]\n",
> > + prefix, datcon, fsta, datsta, datcnt);
> > +
> > + dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
> > + " R2:[%08x] R3:[%08x]\n",
> > + prefix, r0, r1, r2, r3);
> > +}
> > +
> >
> > ...
> >
> > +static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
> > + int fail)
> > +{
> > + unsigned int dbglvl = fail?dbg_fail:dbg_debug;
> >
>
> Spaces around the ? and :, please.
fixed.
> checkpatch misses this.
>
> > + if (!cmd)
> > + return;
> > +
> > + if (cmd->error == MMC_ERR_NONE) {
> > + dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
> > + host->dbgmsg_cmd, cmd->resp[0]);
> > + } else {
> > + dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
> > + cmd->error, host->dbgmsg_cmd, host->status);
> > + }
> > +
> > + if (!cmd->data)
> > + return;
> > +
> > + if (cmd->data->error == MMC_ERR_NONE) {
> > + dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
> > + } else {
> > + dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
> > + cmd->data->error, host->dbgmsg_dat,
> > + readl(host->base + S3C2410_SDIDCNT));
> > + }
> > +}
> > +#endif
> > +
> > +static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
> > +{
> > + u32 newmask;
> > +
> > + newmask = readl(host->base + host->sdiimsk);
> > + newmask|= imask;
> > +
> > + writel(newmask, host->base + host->sdiimsk);
> > +
> > + return newmask;
> > +}
> > +
> > +static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
> > +{
> > + u32 newmask;
> > +
> > + newmask = readl(host->base + host->sdiimsk);
> > + newmask&= ~imask;
> > +
> > + writel(newmask, host->base + host->sdiimsk);
> > +
> > + return newmask;
> > +}
>
> Probably too large to inline. A useful metric is to build the .o file
> with and without the `inline' statements and see which results in the
> smaller /bin/size output. Smaller is usuall faster. And it's smaller.
I'll have a look, but with the function pre-ambles and call overheads
I expect these'll stay inlines.
> > +static inline void clear_imask(struct s3cmci_host *host)
> > +{
> > + writel(0, host->base + host->sdiimsk);
> > +}
> > +
> > +static inline int get_data_buffer(struct s3cmci_host *host,
> > + volatile u32 *words, volatile u32 **pointer)
> > +{
> > + struct scatterlist *sg;
> > +
> > + if (host->pio_active == XFER_NONE)
> > + return -EINVAL;
> > +
> > + if ((!host->mrq) || (!host->mrq->data))
> > + return -EINVAL;
> > +
> > + if (host->pio_sgptr >= host->mrq->data->sg_len) {
> > + dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
> > + host->pio_sgptr, host->mrq->data->sg_len);
> > + return -EBUSY;
> > + }
> > + sg = &host->mrq->data->sg[host->pio_sgptr];
> > +
> > + *words = sg->length >> 2;
> > + *pointer= page_address(sg->page) + sg->offset;
> > +
> > + host->pio_sgptr++;
> > +
> > + dbg(host, dbg_sg, "new buffer (%i/%i)\n",
> > + host->pio_sgptr, host->mrq->data->sg_len);
> > +
> > + return 0;
> > +}
>
> This one has three callsites!
Hmm, I'll leave it up to the compiler whether it wants to
try and inline this.
> > +#define FIFO_FILL(host) ((readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK) >> 2)
> > +#define FIFO_FREE(host) ((63 - (readl(host->base + S3C2410_SDIFSTA) & S3C2410_SDIFSTA_COUNTMASK)) >> 2)
>
> These didn't need to be implemented as macros.
these are now inlined.
> > +
> > +static inline void do_pio_read(struct s3cmci_host *host)
> > +{
> > + int res;
> > + u32 fifo;
> > + void __iomem *from_ptr;
> > +
> > + //write real prescaler to host, it might be set slow to fix
> > + writel(host->prescaler, host->base + S3C2410_SDIPRE);
> > +
> > + from_ptr = host->base + host->sdidata;
> > +
> > + while ((fifo = FIFO_FILL(host))) {
> > + if (!host->pio_words) {
> > + res = get_data_buffer(host, &host->pio_words,
> > + &host->pio_ptr);
> > + if (res) {
> > + host->pio_active = XFER_NONE;
> > + host->complete_what = COMPLETION_FINALIZE;
> > +
> > + dbg(host, dbg_pio, "pio_read(): "
> > + "complete (no more data).\n");
> > + return;
> > + }
> > +
> > + dbg(host, dbg_pio, "pio_read(): new target: [%i]@[%p]\n",
> > + host->pio_words, host->pio_ptr);
> > + }
> > +
> > + dbg(host, dbg_pio, "pio_read(): fifo:[%02i] "
> > + "buffer:[%03i] dcnt:[%08X]\n",
> > + fifo, host->pio_words,
> > + readl(host->base + S3C2410_SDIDCNT));
> > +
> > + if (fifo > host->pio_words)
> > + fifo = host->pio_words;
> > +
> > + host->pio_words-= fifo;
> > + host->pio_count+= fifo;
> > +
> > + while(fifo--) {
> > + *(host->pio_ptr++) = readl(from_ptr);
> > + }
> > + }
> > +
> > + if (!host->pio_words) {
> > + res = get_data_buffer(host, &host->pio_words, &host->pio_ptr);
> > + if (res) {
> > + dbg(host, dbg_pio, "pio_read(): "
> > + "complete (no more buffers).\n");
> > + host->pio_active = XFER_NONE;
> > + host->complete_what = COMPLETION_FINALIZE;
> > +
> > + return;
> > + }
> > + }
> > +
> > + enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
> > +}
> > +
> > +static inline void do_pio_write(struct s3cmci_host *host)
> > +{
> > + int res;
> > + u32 fifo;
> > +
> > + void __iomem *to_ptr;
> > +
> > + to_ptr = host->base + host->sdidata;
> > +
> > + while ((fifo = FIFO_FREE(host))) {
> > + if (!host->pio_words) {
> > + res = get_data_buffer(host, &host->pio_words,
> > + &host->pio_ptr);
> > + if (res) {
> > + dbg(host, dbg_pio, "pio_write(): "
> > + "complete (no more data).\n");
> > + host->pio_active = XFER_NONE;
> > +
> > + return;
> > + }
> > +
> > + dbg(host, dbg_pio, "pio_write(): "
> > + "new source: [%i]@[%p]\n",
> > + host->pio_words, host->pio_ptr);
> > +
> > + }
> > +
> > + if (fifo > host->pio_words)
> > + fifo = host->pio_words;
> > +
> > + host->pio_words-= fifo;
> > + host->pio_count+= fifo;
> > +
> > + while(fifo--) {
> > + writel(*(host->pio_ptr++), to_ptr);
> > + }
> > + }
> > +
> > + enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
> > +}
>
> Two callsites :( This is one bloaty driver.
un-inlined.
> > +static void pio_tasklet(unsigned long data)
> > +{
> > + struct s3cmci_host *host = (struct s3cmci_host *) data;
> > +
> > +
> > + if (host->pio_active == XFER_WRITE)
> > + do_pio_write(host);
> > +
> > + if (host->pio_active == XFER_READ)
> > + do_pio_read(host);
> > +
> > + if (host->complete_what == COMPLETION_FINALIZE) {
> > + clear_imask(host);
> > + if (host->pio_active != XFER_NONE) {
> > + dbg(host, dbg_err, "unfinished %s "
> > + "- pio_count:[%u] pio_words:[%u]\n",
> > + (host->pio_active == XFER_READ)?"read":"write",
> > + host->pio_count, host->pio_words);
> > +
> > + host->mrq->data->error = MMC_ERR_DMA;
> > + }
> > +
> > + disable_irq(host->irq);
> > + finalize_request(host);
> > + }
> > +}
>
> I see no tasklet_disable() in the driver. Is there anything to prevent
> the tasklet from running after device close, rmmod, etc?
fixed.
> The disable_irq()/enable_irq() on every interrupt will be quite
> inefficient and hostile to any device which shares the interrupt. WHy
> is it necessary?
do you mean "on every request", no the driver won't be sharing
interrupts with another block, and I would expect that it is to
stop any interrupts when there is no request present, although
I have not checked for this.
> > +/*
> > + * ISR for SDI Interface IRQ
> > + * Communication between driver and ISR works as follows:
> > + * host->mrq points to current request
> > + * host->complete_what tells the ISR when the request is considered done
> > + * COMPLETION_CMDSENT when the command was sent
> > + * COMPLETION_RSPFIN when a response was received
> > + * COMPLETION_XFERFINISH when the data transfer is finished
> > + * COMPLETION_XFERFINISH_RSPFIN both of the above.
> > + * host->complete_request is the completion-object the driver waits for
> > + *
> > + * 1) Driver sets up host->mrq and host->complete_what
> > + * 2) Driver prepares the transfer
> > + * 3) Driver enables interrupts
> > + * 4) Driver starts transfer
> > + * 5) Driver waits for host->complete_rquest
> > + * 6) ISR checks for request status (errors and success)
> > + * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
> > + * 7) ISR completes host->complete_request
> > + * 8) ISR disables interrupts
> > + * 9) Driver wakes up and takes care of the request
> > + *
> > + * Note: "->error"-fields are expected to be set to 0 before the request
> > + * was issued by mmc.c - therefore they are only set, when an error
> > + * contition comes up
> > + */
> > +
> > +static irqreturn_t s3cmci_irq(int irq, void *dev_id)
> > +{
> > + struct s3cmci_host *host;
> > + struct mmc_command *cmd;
> > + u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
> > + u32 mci_cclear, mci_dclear;
> > + unsigned long iflags;
> > +
> > + host = (struct s3cmci_host *)dev_id;
>
> Unneeded and undesirable cast of void*.
fuxed
> > + spin_lock_irqsave(&host->complete_lock, iflags);
> > +
> > + mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
> > + mci_dsta = readl(host->base + S3C2410_SDIDSTA);
> > + mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
> > + mci_fsta = readl(host->base + S3C2410_SDIFSTA);
> > + mci_imsk = readl(host->base + host->sdiimsk);
> >
> > ...
> >
> > +void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, void *buf_id,
> > + int size, enum s3c2410_dma_buffresult result)
> > +{
> > + unsigned long iflags;
> > + u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt;
> > + struct s3cmci_host *host = (struct s3cmci_host *)buf_id;
> > +
> > + mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
> > + mci_dsta = readl(host->base + S3C2410_SDIDSTA);
> > + mci_fsta = readl(host->base + S3C2410_SDIFSTA);
> > + mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
> > +
> > + if ((!host->mrq) || (!host->mrq) || (!host->mrq->data))
> > + return;
> > +
> > + if (!host->dmatogo)
> > + return;
>
> Can these things all really happen?
I've not tried finding out.
> > + spin_lock_irqsave(&host->complete_lock, iflags);
> > +
> > + if (result != S3C2410_RES_OK) {
> > + dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x "
> > + "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n",
> > + mci_csta, mci_dsta, mci_fsta,
> > + mci_dcnt, result, host->dmatogo);
> > +
> > + goto fail_request;
> > + }
> > +
> > + host->dmatogo--;
> > + if (host->dmatogo) {
> > + dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] "
> > + "DCNT:[%08x] toGo:%u\n",
> > + size, mci_dsta, mci_dcnt, host->dmatogo);
> > +
> > + goto out;
> > + }
> > +
> > + dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n",
> > + size, mci_dsta, mci_dcnt);
> > +
> > + host->complete_what = COMPLETION_FINALIZE;
> > +
> > +out:
> > + tasklet_schedule(&host->pio_tasklet);
> > + spin_unlock_irqrestore(&host->complete_lock, iflags);
> > + return;
> > +
> > +
> > +fail_request:
> > + host->mrq->data->error = MMC_ERR_DMA;
> > + host->complete_what = COMPLETION_FINALIZE;
> > + writel(0, host->base + host->sdiimsk);
> > + goto out;
> > +
> > +}
> > +
> >
> > ...
> >
> > +static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
> > +{
> > + u32 dcon, imsk, stoptries=3;
> > +
> > + /* write DCON register */
> > +
> > + if (!data) {
> > + writel(0, host->base + S3C2410_SDIDCON);
> > + return 0;
> > + }
> > +
> > + while(readl(host->base + S3C2410_SDIDSTA) &
> > + (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
> > +
> > + dbg(host, dbg_err,
> > + "mci_setup_data() transfer stillin progress.\n");
> > +
> > + writel(0, host->base + S3C2410_SDIDCON);
> > + s3cmci_reset(host);
> > +
> > + if (0 == (stoptries--)) {
>
> Swap 'em, please. It's not necessary.
fixed
> > +#ifdef CONFIG_MMC_DEBUG
> > + dbg_dumpregs(host, "DRF");
> > +#endif
> > +
> > + return -EINVAL;
> > + }
> > + }
> > +
> > + dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
> > +
> > + if (host->dodma) {
> > + dcon |= S3C2410_SDIDCON_DMAEN;
> > + }
> > +
> > + if (host->bus_width == MMC_BUS_WIDTH_4) {
> > + dcon |= S3C2410_SDIDCON_WIDEBUS;
> > + }
> > +
> > + if (!(data->flags & MMC_DATA_STREAM)) {
> > + dcon |= S3C2410_SDIDCON_BLOCKMODE;
> > + }
> > +
> > + if (data->flags & MMC_DATA_WRITE) {
> > + dcon |= S3C2410_SDIDCON_TXAFTERRESP;
> > + dcon |= S3C2410_SDIDCON_XFER_TXSTART;
> > + }
> > +
> > + if (data->flags & MMC_DATA_READ) {
> > + dcon |= S3C2410_SDIDCON_RXAFTERCMD;
> > + dcon |= S3C2410_SDIDCON_XFER_RXSTART;
> > + }
> > +
> >
> > ...
> >
> > +static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
> > +{
> > + int dma_len, i;
> > +
> > + int rw = (data->flags & MMC_DATA_WRITE)?1:0;
> > +
> > + if (rw != ((data->flags & MMC_DATA_READ)?0:1))
> > + return -EINVAL;
>
> That's pretty clumsy looking.
>
> Setting both MMC_DATA_WRITE and MMC_DATA_READ would be a programming
> bug, wouldn't it? If so, BUG would be a more appropriate response.
ok, changed.
> > + s3cmci_dma_setup(host, rw?S3C2410_DMASRC_MEM:S3C2410_DMASRC_HW);
> > + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> > +
> > + dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
> > + (rw)?DMA_TO_DEVICE:DMA_FROM_DEVICE);
> > +
> > +
> > + if (dma_len == 0)
> > + return -ENOMEM;
> > +
> > + host->dma_complete = 0;
> > + host->dmatogo = dma_len;
> > +
> > + for (i = 0; i < dma_len; i++) {
> > + int res;
> > +
> > + dbg(host, dbg_dma, "enqueue %i:%u@%u\n", i,
> > + sg_dma_address(&data->sg[i]),
> > + sg_dma_len(&data->sg[i]));
> > +
> > + res = s3c2410_dma_enqueue(host->dma, (void *) host,
> > + sg_dma_address(&data->sg[i]),
> > + sg_dma_len(&data->sg[i]));
> > +
> > + if (res) {
> > + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
> > + return -EBUSY;
> > + }
> > + }
> > +
> > + s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START);
> > +
> > + return 0;
> > +}
> > +
> >
> > ...
> >
> > +
> > + host->irq = platform_get_irq(pdev, 0);
> > + if (host->irq == 0) {
> > + dev_err(&pdev->dev, "failed to get interrupt resouce.\n");
> > + ret = -EINVAL;
> > + goto probe_iounmap;
> > + }
> > +
> > + if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
>
> So these interrupts cannot be shared? The driver will never be used in
> an environment where interrupt sharing is possible?
All current SoCs in the family have one interrupt for each controller,
so this is unlikely.
> > + dev_err(&pdev->dev, "failed to request mci interrupt.\n");
> > + ret = -ENOENT;
> > + goto probe_iounmap;
> > + }
> > +
> > + disable_irq(host->irq);
> > +
> > + s3c2410_gpio_cfgpin(S3C2410_GPF2, S3C2410_GPF2_EINT2);
> > + set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
> > +
> > + if (request_irq(host->irq_cd, s3cmci_irq_cd, 0, DRIVER_NAME, host)) {
> > + dev_err(&pdev->dev,
> > + "failed to request card detect interrupt.\n");
> > +
> > + ret = -ENOENT;
> > + goto probe_free_irq;
> > + }
> > +
> > + if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL)) {
> > + dev_err(&pdev->dev, "unable to get DMA channel.\n");
> > + ret = -EBUSY;
> > + goto probe_free_irq_cd;
> > + }
> > +
> > + host->clk = clk_get(&pdev->dev, "sdi");
> > + if (IS_ERR(host->clk)) {
> > + dev_err(&pdev->dev, "failed to find clock source.\n");
> > + ret = PTR_ERR(host->clk);
> > + host->clk = NULL;
> > + goto probe_free_host;
> > + }
> > +
> > + if ((ret = clk_enable(host->clk))) {
> > + dev_err(&pdev->dev, "failed to enable clock source.\n");
> > + goto clk_free;
> > + }
> > +
> > + host->clk_rate = clk_get_rate(host->clk);
> > +
> > + mmc->ops = &s3cmci_ops;
> > + mmc->ocr_avail = MMC_VDD_32_33;
> > + mmc->caps = MMC_CAP_4_BIT_DATA;
> > + mmc->f_min = host->clk_rate / (host->clk_div * 256);
> > + mmc->f_max = host->clk_rate / host->clk_div;
> > +
> > + mmc->max_blk_count = 4095;
> > + mmc->max_blk_size = 4095;
> > + mmc->max_req_size = 4095 * 512;
> > + mmc->max_seg_size = mmc->max_req_size;
> > +
> > + mmc->max_phys_segs = 128;
> > + mmc->max_hw_segs = 128;
> > +
> > + dbg(host, dbg_debug, "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
> > + (host->is2440?"2440":""),
> > + host->base, host->irq, host->irq_cd, host->dma);
> > +
> > + if ((ret = mmc_add_host(mmc))) {
> > + dev_err(&pdev->dev, "failed to add mmc host.\n");
> > + goto free_dmabuf;
> > + }
> > +
> > + platform_set_drvdata(pdev, mmc);
> > +
> > + dev_info(&pdev->dev,"initialisation done.\n");
> > + return 0;
> > +
> > + free_dmabuf:
> > + clk_disable(host->clk);
> > +
> > + clk_free:
> > + clk_put(host->clk);
> > +
> > + probe_free_irq_cd:
> > + free_irq(host->irq_cd, host);
> > +
> > + probe_free_irq:
> > + free_irq(host->irq, host);
> > +
> > + probe_iounmap:
> > + iounmap(host->base);
> > +
> > + probe_free_mem_region:
> > + release_mem_region(host->mem->start, RESSIZE(host->mem));
> > +
> > + probe_free_host:
> > + mmc_free_host(mmc);
> > + probe_out:
> > + return ret;
> > +}
> > +
> >
> > ...
> >
> > +//FIXME: DMA Resource management ?!
>
> ?
>
> > +#define S3CMCI_DMA 0
> > +
> > +enum s3cmci_waitfor {
> > + COMPLETION_NONE,
> > + COMPLETION_FINALIZE,
> > + COMPLETION_CMDSENT,
> > + COMPLETION_RSPFIN,
> > + COMPLETION_XFERFINISH,
> > + COMPLETION_XFERFINISH_RSPFIN,
> > +};
> > +
> > +struct s3cmci_host {
> > + struct platform_device *pdev;
> > + struct mmc_host *mmc;
> > + struct resource *mem;
> > + struct clk *clk;
> > + void __iomem *base;
> > + int irq;
> > + int irq_cd;
> > + int dma;
> > +
> > + unsigned long clk_rate;
> > + unsigned long clk_div;
> > + unsigned long real_rate;
> > + u8 prescaler;
> > +
> > + int is2440;
> > + unsigned sdiimsk;
> > + unsigned sdidata;
> > + int dodma;
> > +
> > + volatile int dmatogo;
> > +
> > + struct mmc_request *mrq;
> > + int cmd_is_stop;
> > +
> > + spinlock_t complete_lock;
> > + volatile enum s3cmci_waitfor
> > + complete_what;
> > +
> > + volatile int dma_complete;
> > +
> > + volatile u32 pio_sgptr;
> > + volatile u32 pio_words;
> > + volatile u32 pio_count;
> > + volatile u32 *pio_ptr;
>
> volatiles. checkpatch..
>
> > +#define XFER_NONE 0
> > +#define XFER_READ 1
> > +#define XFER_WRITE 2
> > + volatile u32 pio_active;
> > +
> > + int bus_width;
> > +
> > + char dbgmsg_cmd[301];
> > + char dbgmsg_dat[301];
> > + volatile char *status;
> > +
> > + unsigned int ccnt, dcnt;
> > + struct tasklet_struct pio_tasklet;
> > +};
>
> Those problems were really really basic stuff. kernel drivers 101.
Yeah, however I don't want to go about writing a new driver
for this from scratch, and having this interface supported
would add more support for an SoC series used in an number
of PDAs and other devices.
Unfortunatley the original authour isn't interested in getting
it submitted (IE, it works for the kernel he needs to use), and
other people haven't stepped up to try and get this integrated.
--
Ben (ben@fluff.org, http://www.fluff.org/)
'a smiley only costs 4 bytes'
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-07 12:22 ` Ben Dooks
@ 2008-06-07 19:13 ` Andrew Morton
2008-06-07 19:50 ` Ben Dooks
0 siblings, 1 reply; 28+ messages in thread
From: Andrew Morton @ 2008-06-07 19:13 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Pierre Ossman, Harald Welte, Thomas Kleffel,
Roman Moravcik
(cc's restored. Please don't remove people)
On Sat, 7 Jun 2008 13:22:27 +0100 Ben Dooks <ben@fluff.org> wrote:
> > right at the top of the changelog so that the commit is correctly
> > attributed.
> >
> > > Signed-off-by: Ben Dooks <ben-linux@fluff.org>
> >
> > Please pass these patches through chechpatch? This one alone generates
> >
> > total: 40 errors, 29 warnings, 1488 lines checked
> >
> > many of which you would have fixed had you been aware of them. That's
> > what checkpatch is for.
>
> Is it ok to fit another patch after this one to fixup these problems?
OK by me, as would be a new patch.
Pierre would be merging this (might have already done so?) - I assume
he'd prefer a whole new patch.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-07 19:13 ` Andrew Morton
@ 2008-06-07 19:50 ` Ben Dooks
0 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-07 19:50 UTC (permalink / raw)
To: Andrew Morton
Cc: Ben Dooks, linux-kernel, Pierre Ossman, Harald Welte,
Thomas Kleffel, Roman Moravcik
On Sat, Jun 07, 2008 at 12:13:59PM -0700, Andrew Morton wrote:
>
> (cc's restored. Please don't remove people)
Sorry, thought I had replied to all.
> On Sat, 7 Jun 2008 13:22:27 +0100 Ben Dooks <ben@fluff.org> wrote:
>
> > > right at the top of the changelog so that the commit is correctly
> > > attributed.
> > >
> > > > Signed-off-by: Ben Dooks <ben-linux@fluff.org>
> > >
> > > Please pass these patches through chechpatch? This one alone generates
> > >
> > > total: 40 errors, 29 warnings, 1488 lines checked
> > >
> > > many of which you would have fixed had you been aware of them. That's
> > > what checkpatch is for.
> >
> > Is it ok to fit another patch after this one to fixup these problems?
>
> OK by me, as would be a new patch.
>
> Pierre would be merging this (might have already done so?) - I assume
> he'd prefer a whole new patch.
I'll have to post a new series, the changes to cleanup make the rest of
the series either fuzz, offset or just fail to apply. I'll run the new
series through a test to check that it still works.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
2008-06-07 5:49 ` Andrew Morton
@ 2008-06-14 16:18 ` Pierre Ossman
2008-06-17 23:02 ` Ben Dooks
1 sibling, 1 reply; 28+ messages in thread
From: Pierre Ossman @ 2008-06-14 16:18 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: Type: text/plain, Size: 575 bytes --]
On Fri, 06 Jun 2008 16:51:18 +0100
Ben Dooks <ben-linux@fluff.org> wrote:
> +#include <linux/mmc/mmc.h>
You don't need this anymore.
> + //if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
Please remove all the dead code.
> + if (mrq->data->error == MMC_ERR_NONE) {
And rebase against HEAD. MMC_ERR_* are long gone.
Also, please test using the mmc_test driver available in HEAD. A proper
driver should pass all those tests (or possibly mark a few as
unsupported if the hardware is utterly incapable of handling those
requests).
Rgds
Pierre
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com>
2008-06-06 15:51 ` [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com> Ben Dooks
@ 2008-06-14 16:20 ` Pierre Ossman
0 siblings, 0 replies; 28+ messages in thread
From: Pierre Ossman @ 2008-06-14 16:20 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: Type: text/plain, Size: 773 bytes --]
On Fri, 06 Jun 2008 16:51:22 +0100
Ben Dooks <ben-linux@fluff.org> wrote:
> Fix Bug #677 - I/O errors on heavy microSD writes for 2.6.22.x.
>
> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
>
> Index: linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c
> ===================================================================
> --- linux-2.6.26-rc5-q.orig/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:01.000000000 +0100
> +++ linux-2.6.26-rc5-q/drivers/mmc/host/s3cmci.c 2008-06-06 15:46:03.000000000 +0100
> @@ -313,6 +313,7 @@ static void pio_tasklet(unsigned long da
> {
> struct s3cmci_host *host = (struct s3cmci_host *) data;
>
> + disable_irq(host->irq);
>
This seems harsh. Can't you just fiddle with the interrupt mask of this
specific device?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver
2008-06-06 15:51 ` [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver Ben Dooks
@ 2008-06-14 16:22 ` Pierre Ossman
2008-06-17 23:20 ` Ben Dooks
0 siblings, 1 reply; 28+ messages in thread
From: Pierre Ossman @ 2008-06-14 16:22 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: Type: text/plain, Size: 649 bytes --]
On Fri, 06 Jun 2008 16:51:23 +0100
Ben Dooks <ben-linux@fluff.org> wrote:
> This patch adds platform data support to the s3mci driver. This allows
> flexible board-specific configuration of set_power, card detect and read only
> pins.
>
> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
>
Please see the changes in my #next branch where you can return -ENOSYS
to indicate that you do not know the state of the read only pin.
> +
> +static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
> + .gpio_detect = 0,
> + .set_power = NULL,
> + .ocr_avail = MMC_VDD_32_33,
> };
You probably want MMC_VDD_32_32 | MMC_VDD_33_34 here.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX
2008-06-06 15:51 ` [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX Ben Dooks
@ 2008-06-14 16:24 ` Pierre Ossman
0 siblings, 0 replies; 28+ messages in thread
From: Pierre Ossman @ 2008-06-14 16:24 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: Type: text/plain, Size: 348 bytes --]
On Fri, 06 Jun 2008 16:51:26 +0100
Ben Dooks <ben-linux@fluff.org> wrote:
> Add support to the S3C24XX MMC driver to have the card detect be on
> a pin that is not IRQ capable.
>
> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
>
How is detection handled in this case? Have a lock in my #next tree for
automatic polling by the core.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver
2008-06-06 15:51 ` [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver Ben Dooks
@ 2008-06-14 16:25 ` Pierre Ossman
2008-06-17 23:33 ` Ben Dooks
0 siblings, 1 reply; 28+ messages in thread
From: Pierre Ossman @ 2008-06-14 16:25 UTC (permalink / raw)
To: Ben Dooks
Cc: linux-kernel, Harald Welte, Thomas Kleffel, Roman Moravcik,
Ben Dooks
[-- Attachment #1: Type: text/plain, Size: 195 bytes --]
On Fri, 06 Jun 2008 16:51:28 +0100
Ben Dooks <ben-linux@fluff.org> wrote:
> Fix the error codes
>
> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
>
NAK, for rather obvious reasons.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de>
2008-06-14 16:18 ` Pierre Ossman
@ 2008-06-17 23:02 ` Ben Dooks
0 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-17 23:02 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, linux-kernel, Harald Welte, Thomas Kleffel,
Roman Moravcik
On Sat, Jun 14, 2008 at 06:18:51PM +0200, Pierre Ossman wrote:
> On Fri, 06 Jun 2008 16:51:18 +0100
> Ben Dooks <ben-linux@fluff.org> wrote:
>
> > +#include <linux/mmc/mmc.h>
>
> You don't need this anymore.
right, done in patch #2
> > + //if(cmd->flags & MMC_RSP_MAYFAIL) debug_as_failure = 0;
>
> Please remove all the dead code.
removed in patch #2
> > + if (mrq->data->error == MMC_ERR_NONE) {
ok, i've done:
MMC_ERR_NONE => 0
MMC_ERR_DMA => EINVAL
MMC_ERR_TIMEOUT => ETIMEDOUT
MMC_ERR_BADCRC => EILSEQ
MMC_ERR_FIFO => EILSEQ
> And rebase against HEAD. MMC_ERR_* are long gone.
right, will try and build and test as soon as possible.
> Also, please test using the mmc_test driver available in HEAD. A proper
> driver should pass all those tests (or possibly mark a few as
> unsupported if the hardware is utterly incapable of handling those
> requests).
Thanks, I'll repost v3 when this is done.
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver
2008-06-14 16:22 ` Pierre Ossman
@ 2008-06-17 23:20 ` Ben Dooks
0 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-17 23:20 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, linux-kernel, Harald Welte, Thomas Kleffel,
Roman Moravcik
On Sat, Jun 14, 2008 at 06:22:20PM +0200, Pierre Ossman wrote:
> On Fri, 06 Jun 2008 16:51:23 +0100
> Ben Dooks <ben-linux@fluff.org> wrote:
>
> > This patch adds platform data support to the s3mci driver. This allows
> > flexible board-specific configuration of set_power, card detect and read only
> > pins.
> >
> > Signed-off-by: Ben Dooks <ben-linux@fluff.org>
> >
>
> Please see the changes in my #next branch where you can return -ENOSYS
> to indicate that you do not know the state of the read only pin.
thanks, fixed.
> > +
> > +static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
> > + .gpio_detect = 0,
> > + .set_power = NULL,
> > + .ocr_avail = MMC_VDD_32_33,
> > };
>
> You probably want MMC_VDD_32_32 | MMC_VDD_33_34 here.
Ok, updated.
thanks,
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver
2008-06-14 16:25 ` Pierre Ossman
@ 2008-06-17 23:33 ` Ben Dooks
0 siblings, 0 replies; 28+ messages in thread
From: Ben Dooks @ 2008-06-17 23:33 UTC (permalink / raw)
To: Pierre Ossman
Cc: Ben Dooks, linux-kernel, Harald Welte, Thomas Kleffel,
Roman Moravcik
On Sat, Jun 14, 2008 at 06:25:05PM +0200, Pierre Ossman wrote:
> On Fri, 06 Jun 2008 16:51:28 +0100
> Ben Dooks <ben-linux@fluff.org> wrote:
>
> > Fix the error codes
> >
> > Signed-off-by: Ben Dooks <ben-linux@fluff.org>
> >
>
> NAK, for rather obvious reasons.
Fixed and folded back into patch #2.
thanks,
--
Ben
Q: What's a light-year?
A: One-third less calories than a regular year.
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2008-06-17 23:33 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-06 15:51 [patch 00/15] S3C24XX SD/MMC driver series Ben Dooks
2008-06-06 15:51 ` [patch 01/15] MMC: S3C24XX MMC/SD driver. From: Thomas Kleffel <tk@maintech.de> Ben Dooks
2008-06-07 5:49 ` Andrew Morton
2008-06-07 12:22 ` Ben Dooks
2008-06-07 19:13 ` Andrew Morton
2008-06-07 19:50 ` Ben Dooks
2008-06-14 16:18 ` Pierre Ossman
2008-06-17 23:02 ` Ben Dooks
2008-06-06 15:51 ` [patch 02/15] MMC: S3C24XX MMC/SD driver write fixes From: Harald Welte <laforge@openmoko.org> Ben Dooks
2008-06-06 15:51 ` [patch 03/15] MMC: DMA free fix for S3C24XX SD/MMC driver From: OpenMoko SVN " Ben Dooks
2008-06-06 15:51 ` [patch 04/15] MMC: S3C24XX MMC/SD stop fix From: Thomas Kleffel <tk@maintech.de> Ben Dooks
2008-06-06 15:51 ` [patch 05/15] MMC: Fix S3C24XX IRQ enable during PIO transfers From: Roman Moracik <roman.moravcik@gmail.com> Ben Dooks
2008-06-14 16:20 ` Pierre Ossman
2008-06-06 15:51 ` [patch 06/15] MMC: Add platform data for S3C24XX MMC/SD driver Ben Dooks
2008-06-14 16:22 ` Pierre Ossman
2008-06-17 23:20 ` Ben Dooks
2008-06-06 15:51 ` [patch 07/15] MMC: Add support to invert S3C24XX write protect line Ben Dooks
2008-06-06 15:51 ` [patch 08/15] MMC: Ensure host->mrq->data is valid in S3C24XX driver Ben Dooks
2008-06-06 15:51 ` [patch 09/15] MMC: Allow card-detect to be on an non-IRQ capable pin on the S3C24XX Ben Dooks
2008-06-14 16:24 ` Pierre Ossman
2008-06-06 15:51 ` [patch 10/15] MMC: Fix s3c24xx driver s3c2410_dma_request() return code check Ben Dooks
2008-06-06 15:51 ` [patch 11/15] MMC: Fix the retcode in the S3C24XX MMC/SD driver Ben Dooks
2008-06-14 16:25 ` Pierre Ossman
2008-06-17 23:33 ` Ben Dooks
2008-06-06 15:51 ` [patch 12/15] MMC: S3C24XX MMC/SD driver fixes for sg_ access Ben Dooks
2008-06-06 15:51 ` [patch 13/15] MMC: s3cmci: Add MODULE_ALIAS() entries for the platform devices Ben Dooks
2008-06-06 15:51 ` [patch 14/15] MMC: Fix use of msecs where jiffies are needed Ben Dooks
2008-06-06 15:51 ` [patch 15/15] MMC: Add media presence detection to request handling Ben Dooks
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox