diff -NurbBpP linux-2.6.11.11-h1940-2/arch/arm/mach-s3c2410/mach-h1940.c linux-2.6.11.11-h1940-3.3/arch/arm/mach-s3c2410/mach-h1940.c
--- linux-2.6.11.11-h1940-2/arch/arm/mach-s3c2410/mach-h1940.c 2005-06-03 12:13:42.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/arch/arm/mach-s3c2410/mach-h1940.c 2005-06-03 19:11:47.000000000 +0200
@@ -167,6 +167,7 @@ static struct platform_device *h1940_dev
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
+ &s3c_device_sdi,
&s3c_device_usbgadget,
};
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/Kconfig linux-2.6.11.11-h1940-3.3/drivers/mmc/Kconfig
--- linux-2.6.11.11-h1940-2/drivers/mmc/Kconfig 2005-03-29 06:05:33.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/Kconfig 2005-06-03 19:11:47.000000000 +0200
@@ -39,6 +39,15 @@ config MMC_ARMMMCI
If unsure, say N.
+config MMC_S3C2410
+ tristate "Samsung S3C2410 Multimedia Card Interface support"
+ depends on ARCH_S3C2410 && MMC
+ help
+ This selects the Samsung S3C2410 Multimedia Card Interface
+ support.
+
+ If unsure, say N.
+
config MMC_PXA
tristate "Intel PXA255 Multimedia Card Interface support"
depends on ARCH_PXA && MMC
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/Makefile linux-2.6.11.11-h1940-3.3/drivers/mmc/Makefile
--- linux-2.6.11.11-h1940-2/drivers/mmc/Makefile 2005-03-29 06:04:22.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/Makefile 2005-06-03 19:11:47.000000000 +0200
@@ -18,5 +18,6 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
+obj-$(CONFIG_MMC_S3C2410) += s3c2410mci.o
mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/mmc_block.c linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc_block.c
--- linux-2.6.11.11-h1940-2/drivers/mmc/mmc_block.c 2005-03-29 06:05:33.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc_block.c 2005-06-03 19:45:15.000000000 +0200
@@ -3,6 +3,9 @@
*
* Copyright 2002 Hewlett-Packard Company
*
+ * SD-Card support based on the work of:
+ * Thomas Kleffel, Ian Molton, and Pierre Ossman
+ *
* Use consistent with the GNU GPL is permitted,
* provided that this copyright notice is
* preserved in its entirety in all copies and derived works.
@@ -30,11 +33,13 @@
#include <linux/devfs_fs_kernel.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/system.h>
#include <asm/uaccess.h>
+#include "mmc.h"
#include "mmc_queue.h"
/*
@@ -165,11 +170,19 @@ static int mmc_blk_issue_rq(struct mmc_q
{
struct mmc_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card;
+ struct mmc_host *host = md->queue.card->host;
int ret;
if (mmc_card_claim_host(card))
goto cmd_err;
+ if ((card->sd) && (host->flags & MMC_HOST_WIDEMODE)
+ && (card->bus_width != 4)) {
+ if (mmc_sd_set_bus_width(host, card->rca, 4))
+ goto cmd_err;
+ card->bus_width=4;
+ }
+
do {
struct mmc_blk_request brq;
struct mmc_command cmd;
@@ -180,6 +193,7 @@ static int mmc_blk_issue_rq(struct mmc_q
brq.cmd.arg = req->sector << 9;
brq.cmd.flags = MMC_RSP_R1;
+ brq.data.req = req;
brq.data.timeout_ns = card->csd.tacc_ns * 10;
brq.data.timeout_clks = card->csd.tacc_clks * 10;
brq.data.blksz_bits = md->block_bits;
@@ -188,6 +202,9 @@ static int mmc_blk_issue_rq(struct mmc_q
brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_R1B;
+ if (card->bus_width==4)
+ brq.data.flags |= MMC_DATA_WIDE;
+
if (rq_data_dir(req) == READ) {
brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
brq.data.flags |= MMC_DATA_READ;
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/mmc.c linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc.c
--- linux-2.6.11.11-h1940-2/drivers/mmc/mmc.c 2005-03-29 06:05:33.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc.c 2005-06-03 21:47:26.000000000 +0200
@@ -3,6 +3,9 @@
*
* Copyright (C) 2003-2004 Russell King, All Rights Reserved.
*
+ * SD-Card support based on the work of:
+ * Thomas Kleffel, Ian Molton, and Pierre Ossman
+ *
* 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.
@@ -173,6 +176,48 @@ int mmc_wait_for_cmd(struct mmc_host *ho
EXPORT_SYMBOL(mmc_wait_for_cmd);
+static int mmc_send_app_cmd(struct mmc_host *host, u16 rca)
+ {
+ struct mmc_command cmd;
+
+ cmd.opcode = MMC_APP_CMD;
+ cmd.arg =((u32)rca)<<16;
+ cmd.flags = MMC_RSP_R1;
+
+ return mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
+}
+
+/**
+ * mmc_wait_for_app_cmd - start an application command and wait
+ * for completion
+ * @host: MMC host to start command
+ * @rca: RCA to send MMC_APP_CMD to
+ * @cmd: MMC command to start
+ * @retries: maximum number of retries
+ *
+ * Sends a MMC_APP_CMD, checks the card response, sends the command
+ * in the parameter and waits for it to complete. Return any error
+ * that occurred while the command was executing.
+ * Do not attempt to parse the response.
+ */
+int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca,
+ struct mmc_command *cmd, int retries)
+{
+ int err;
+
+ BUG_ON(host->card_busy == NULL);
+ BUG_ON(retries < 0);
+
+ err = mmc_send_app_cmd(host, rca);
+ if (err != MMC_ERR_NONE)
+ goto fail;
+
+ err = mmc_wait_for_cmd(host, cmd, retries);
+fail:
+ return err;
+}
+
+EXPORT_SYMBOL(mmc_wait_for_app_cmd);
/**
* __mmc_claim_host - exclusively claim a host
@@ -322,13 +367,30 @@ static void mmc_decode_cid(struct mmc_ca
memset(&card->cid, 0, sizeof(struct mmc_cid));
- /*
- * The selection of the format here is guesswork based upon
- * information people have sent to date.
- */
- switch (card->csd.mmca_vsn) {
- case 0: /* MMC v1.? */
- case 1: /* MMC v1.4 */
+ if (card->sd) {
+ if(card->csd.csd_vers != SD_CSDV_1) {
+ DBG("%s: card has unknown SD-MMCA version %d\n", card->host->host_name, card->csd.spec_vers);
+ mmc_card_set_bad(card);
+ } else {
+ card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
+ card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
+ card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
+ card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
+ card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8);
+ card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8);
+ card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8);
+ card->cid.prod_name[5] = 0;
+ card->cid.prod_name[6] = 0;
+ card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4);
+ card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4);
+ card->cid.serial = UNSTUFF_BITS(resp, 24, 32);
+ card->cid.month = UNSTUFF_BITS(resp, 12, 4);
+ card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 2000;
+ }
+ } else {
+ switch (card->csd.csd_vers) {
+ case MMC_CSDV_1: /* MMC v1.? */
+ case MMC_CSDV_14: /* MMC v1.4 */
card->cid.manfid = UNSTUFF_BITS(resp, 104, 24);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8);
@@ -344,8 +406,8 @@ static void mmc_decode_cid(struct mmc_ca
card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997;
break;
- case 2: /* MMC v2.x ? */
- case 3: /* MMC v3.x ? */
+ case MMC_CSDV_2: /* MMC v2.x ? */
+ case MMC_CSDV_3: /* MMC v3.x ? */
card->cid.manfid = UNSTUFF_BITS(resp, 120, 8);
card->cid.oemid = UNSTUFF_BITS(resp, 104, 16);
card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8);
@@ -360,11 +422,11 @@ static void mmc_decode_cid(struct mmc_ca
break;
default:
- printk("%s: card has unknown MMCA version %d\n",
- card->host->host_name, card->csd.mmca_vsn);
+ printk(KERN_WARNING "%s: card has unknown MMC-MMCA version %d\n", card->host->host_name, card->csd.spec_vers);
mmc_card_set_bad(card);
break;
}
+ }
}
/*
@@ -373,22 +435,79 @@ static void mmc_decode_cid(struct mmc_ca
static void mmc_decode_csd(struct mmc_card *card)
{
struct mmc_csd *csd = &card->csd;
- unsigned int e, m, csd_struct;
+ unsigned int e, m;
u32 *resp = card->raw_csd;
+ if (card->sd) {
/*
- * We only understand CSD structure v1.1 and v2.
+ * We only understand SD-CSD structure v1.0.
+ */
+ csd->sd=1;
+ csd->csd_vers = UNSTUFF_BITS(resp, 126, 2);
+ if(csd->csd_vers != SD_CSDV_1) {
+ printk("%s: unrecognised SD-CSD structure version %d\n",
+ card->host->host_name, csd->csd_vers);
+ mmc_card_set_bad(card);
+ return;
+ }
+
+ csd->spec_vers = 0;
+ csd->taac = UNSTUFF_BITS(resp, 112, 8);
+ csd->nsac = UNSTUFF_BITS(resp, 104, 8);
+ csd->tran_speed = UNSTUFF_BITS(resp, 96, 8);
+ csd->cmdclass = UNSTUFF_BITS(resp, 84,12);
+ csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
+ csd->read_bl_partial = UNSTUFF_BITS(resp, 79, 1);
+ csd->write_blk_misalign = UNSTUFF_BITS(resp, 78, 1);
+ csd->read_blk_misalign = UNSTUFF_BITS(resp, 77, 1);
+ csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
+ csd->c_size = UNSTUFF_BITS(resp, 62,12);
+ csd->vdd_r_curr_min = UNSTUFF_BITS(resp, 69, 3);
+ csd->vdd_r_curr_max = UNSTUFF_BITS(resp, 56, 3);
+ csd->vdd_w_curr_min = UNSTUFF_BITS(resp, 53, 3);
+ csd->vdd_w_curr_max = UNSTUFF_BITS(resp, 50, 3);
+ csd->c_size_mult = UNSTUFF_BITS(resp, 47, 3);
+ csd->erase.sd10.erase_blk_en = UNSTUFF_BITS(resp, 46, 1);
+ csd->erase.sd10.erase_blk_size= UNSTUFF_BITS(resp, 39, 7);
+ csd->wp_grp_size = UNSTUFF_BITS(resp, 32, 7);
+ csd->wp_grp_enable = UNSTUFF_BITS(resp, 31, 1);
+ csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
+ csd->write_bl_len = UNSTUFF_BITS(resp, 22, 4);
+ csd->write_bl_partial = UNSTUFF_BITS(resp, 21, 1);
+ csd->file_format_grp = UNSTUFF_BITS(resp, 15, 1);
+ csd->copy = UNSTUFF_BITS(resp, 14, 1);
+ csd->perm_write_protect = UNSTUFF_BITS(resp, 13, 1);
+ csd->tmp_write_protect = UNSTUFF_BITS(resp, 12, 1);
+ csd->file_format = UNSTUFF_BITS(resp, 10, 1);
+ csd->ecc = UNSTUFF_BITS(resp, 1, 7);
+
+ m = UNSTUFF_BITS(resp, 115, 4);
+ e = UNSTUFF_BITS(resp, 112, 3);
+ csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
+ csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100;
+
+ m = UNSTUFF_BITS(resp, 99, 4);
+ e = UNSTUFF_BITS(resp, 96, 3);
+ csd->max_dtr = tran_exp[e] * tran_mant[m];
+
+ e = UNSTUFF_BITS(resp, 47, 3);
+ m = UNSTUFF_BITS(resp, 62, 12);
+ csd->capacity = (1 + m) << (e + 2);
+ } else {
+ /*
+ * We only understand MMC-CSD structure v1.1 and v2.
* v2 has extra information in bits 15, 11 and 10.
*/
- csd_struct = UNSTUFF_BITS(resp, 126, 2);
- if (csd_struct != 1 && csd_struct != 2) {
- printk("%s: unrecognised CSD structure version %d\n",
- card->host->host_name, csd_struct);
+ csd->sd=0;
+ csd->csd_vers = UNSTUFF_BITS(resp, 126, 2);
+ if (csd->csd_vers != MMC_CSDV_14 && csd->csd_vers != MMC_CSDV_2) {
+ printk("%s: unrecognised MMC-CSD structure version %d\n",
+ card->host->host_name, csd->csd_vers);
mmc_card_set_bad(card);
return;
}
- csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4);
+ csd->spec_vers = UNSTUFF_BITS(resp, 122, 4);
m = UNSTUFF_BITS(resp, 115, 4);
e = UNSTUFF_BITS(resp, 112, 3);
csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10;
@@ -404,6 +523,7 @@ static void mmc_decode_csd(struct mmc_ca
csd->capacity = (1 + m) << (e + 2);
csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4);
+ }
}
/*
@@ -456,6 +576,7 @@ mmc_alloc_card(struct mmc_host *host, u3
static void mmc_idle_cards(struct mmc_host *host)
{
struct mmc_command cmd;
+ struct mmc_card *card;
cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0;
@@ -463,9 +584,95 @@ static void mmc_idle_cards(struct mmc_ho
mmc_wait_for_cmd(host, &cmd, 0);
- mmc_delay(1);
+ //MMC_GO_IDLE_STATE resets all cards to bus width 1
+ list_for_each_entry(card, &host->cards, node) {
+ card->bus_width=1;
+ }
+
+
+ mmc_delay(100);
+}
+
+int mmc_sd_set_bus_width(struct mmc_host *host, u16 rca, u32 bus_width)
+{
+ struct mmc_command cmd;
+ int err;
+
+ cmd.opcode = MMC_SD_SET_BUS_WIDTH;
+ cmd.flags = MMC_RSP_R1;
+
+ switch(bus_width) {
+ case 1: cmd.arg = 00; break;
+ case 4: cmd.arg = 10; break;
+ default: return -EINVAL;
+ }
+
+ err = mmc_wait_for_app_cmd(host, rca, &cmd, CMD_RETRIES);
+ if (err != MMC_ERR_NONE)
+ goto fail;
+
+ DBG("MMC: sd_set_bus_width done.\n");
+ return err;
+
+fail:
+ switch (err) {
+ case MMC_ERR_TIMEOUT:
+ printk(KERN_ERR "MMC: sd_set_bus_width timed out.\n");
+ break;
+ case MMC_ERR_BADCRC:
+ printk(KERN_ERR "MMC: sd_set_bus_width yielded crc error.\n");
+ break;
+ default:
+ printk(KERN_ERR "MMC: sd_set_bus_width error %d.\n", err);
+ }
+ return err;
+}
+
+EXPORT_SYMBOL(mmc_sd_set_bus_width);
+
+
+static int mmc_send_app_op_cond(struct mmc_host *host, u16 rca, u32 parameter, u32 *response)
+{
+ struct mmc_command cmd;
+ int err;
+ int retries = 100;
+
+ cmd.opcode = MMC_SD_APP_OP_COND;
+ cmd.arg = parameter;
+ cmd.flags = MMC_RSP_R3;
+
+ DBG("MMC: send_app_op_cond to %08x\n",parameter);
+
+ while (retries--) {
+ err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES);
+ if (err != MMC_ERR_NONE)
+ goto retry; // was break;
+
+ if (cmd.resp[0] & MMC_CARD_BUSY || parameter == 0)
+ break;
+
+ retry:
+ err = MMC_ERR_TIMEOUT;
+ mmc_delay(10);
+ }
+
+ if (response)
+ *response = cmd.resp[0];
+
+ switch (err) {
+ case MMC_ERR_TIMEOUT:
+ printk(KERN_WARNING "MMC: send_app_op_cond timed out. Probably no SD-Card here.\n");
+ break;
+ case MMC_ERR_BUSY:
+ printk(KERN_ERR "MMC: send_app_op_cond locked busy. Probably have broken SD-Card.\n");
+ break;
+ default:
+ DBG("MMC: send_app_op_cond done. Results are: 0x%08x.\n",cmd.resp[0]);
+ }
+ return err;
}
+
/*
* Apply power to the MMC stack.
*/
@@ -532,7 +739,7 @@ static int mmc_send_op_cond(struct mmc_h
* Create a mmc_card entry for each discovered card, assigning
* it an RCA, and save the raw CID for decoding later.
*/
-static void mmc_discover_cards(struct mmc_host *host)
+static void mmc_discover_cards(struct mmc_host *host, u8 sd)
{
struct mmc_card *card;
unsigned int first_rca = 1, err;
@@ -542,7 +749,7 @@ static void mmc_discover_cards(struct mm
cmd.opcode = MMC_ALL_SEND_CID;
cmd.arg = 0;
- cmd.flags = MMC_RSP_R2;
+ cmd.flags = MMC_RSP_LONG; //HACK! CRC currently not implemented
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err == MMC_ERR_TIMEOUT) {
@@ -550,8 +757,7 @@ static void mmc_discover_cards(struct mm
break;
}
if (err != MMC_ERR_NONE) {
- printk(KERN_ERR "%s: error requesting CID: %d\n",
- host->host_name, err);
+ printk(KERN_ERR "%s: error requesting CID: %d\n", host->host_name, err);
break;
}
@@ -562,6 +768,7 @@ static void mmc_discover_cards(struct mm
err = PTR_ERR(card);
break;
}
+ card->sd = sd;
list_add(&card->node, &host->cards);
}
@@ -574,6 +781,15 @@ static void mmc_discover_cards(struct mm
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE)
mmc_card_set_dead(card);
+
+ //SD-Cards choose their adresses themselfes (yuck!)
+ if (card->sd) {
+ card->rca = (cmd.resp[0] >> 16);
+ if (!card->rca) {
+ mmc_card_set_dead(card);
+ break;
+ }
+ }
}
}
@@ -590,7 +806,7 @@ static void mmc_read_csds(struct mmc_hos
cmd.opcode = MMC_SEND_CSD;
cmd.arg = card->rca << 16;
- cmd.flags = MMC_RSP_R2;
+ cmd.flags = MMC_RSP_LONG; //HACK: No CRC check as s3c-Core is broken
err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);
if (err != MMC_ERR_NONE) {
@@ -651,63 +867,23 @@ static void mmc_check_cards(struct mmc_h
}
}
+
static void mmc_setup(struct mmc_host *host)
{
- if (host->ios.power_mode != MMC_POWER_ON) {
- int err;
- u32 ocr;
mmc_power_up(host);
mmc_idle_cards(host);
- err = mmc_send_op_cond(host, 0, &ocr);
- if (err != MMC_ERR_NONE)
- return;
-
- host->ocr = mmc_select_voltage(host, ocr);
-
- /*
- * Since we're changing the OCR value, we seem to
- * need to tell some cards to go back to the idle
- * state. We wait 1ms to give cards time to
- * respond.
- */
- if (host->ocr)
- mmc_idle_cards(host);
- } else {
- host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
- host->ios.clock = host->f_min;
- host->ops->set_ios(host, &host->ios);
-
- /*
- * We should remember the OCR mask from the existing
- * cards, and detect the new cards OCR mask, combine
- * the two and re-select the VDD. However, if we do
- * change VDD, we should do an idle, and then do a
- * full re-initialisation. We would need to notify
- * drivers so that they can re-setup the cards as
- * well, while keeping their queues at bay.
- *
- * For the moment, we take the easy way out - if the
- * new cards don't like our currently selected VDD,
- * they drop off the bus.
- */
- }
-
- if (host->ocr == 0)
- return;
-
- /*
- * Send the selected OCR multiple times... until the cards
- * all get the idea that they should be ready for CMD2.
- * (My SanDisk card seems to need this.)
- */
- mmc_send_op_cond(host, host->ocr, NULL);
-
- mmc_discover_cards(host);
+ // Wake and discover SD-Cards
+ mmc_send_app_op_cond(host, 0, host->ocr_avail, NULL);
+ mmc_discover_cards(host, 1);
+
+ // Wake and discover MMC-Cards
+ mmc_send_op_cond(host, host->ocr_avail, NULL);
+ mmc_discover_cards(host, 0);
/*
- * Ok, now switch to push-pull mode.
+ * switch to push-pull mode.
*/
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
host->ops->set_ios(host, &host->ios);
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/mmc.h linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc.h
--- linux-2.6.11.11-h1940-2/drivers/mmc/mmc.h 2004-11-21 14:46:06.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc.h 2005-06-03 19:46:38.000000000 +0200
@@ -13,4 +13,5 @@
void mmc_init_card(struct mmc_card *card, struct mmc_host *host);
int mmc_register_card(struct mmc_card *card);
void mmc_remove_card(struct mmc_card *card);
+int mmc_sd_set_bus_width(struct mmc_host *host, u16 rca, u32 bus_width);
#endif
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/mmc_sysfs.c linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc_sysfs.c
--- linux-2.6.11.11-h1940-2/drivers/mmc/mmc_sysfs.c 2004-11-21 14:46:06.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/mmc_sysfs.c 2005-06-03 19:13:27.000000000 +0200
@@ -3,6 +3,9 @@
*
* Copyright (C) 2003 Russell King, All Rights Reserved.
*
+ * SD-Card support based on the work of:
+ * Thomas Kleffel, Ian Molton, and Pierre Ossman
+ *
* 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.
@@ -201,17 +204,10 @@ void mmc_init_card(struct mmc_card *card
*/
int mmc_register_card(struct mmc_card *card)
{
- int ret, i;
-
snprintf(card->dev.bus_id, sizeof(card->dev.bus_id),
"%s:%04x", card->host->host_name, card->rca);
- ret = device_add(&card->dev);
- if (ret == 0)
- for (i = 0; i < ARRAY_SIZE(mmc_dev_attributes); i++)
- device_create_file(&card->dev, mmc_dev_attributes[i]);
-
- return ret;
+ return device_add(&card->dev);
}
/*
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/s3c2410mci.c linux-2.6.11.11-h1940-3.3/drivers/mmc/s3c2410mci.c
--- linux-2.6.11.11-h1940-2/drivers/mmc/s3c2410mci.c 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/s3c2410mci.c 2005-06-03 21:59:13.000000000 +0200
@@ -0,0 +1,748 @@
+/*
+ * linux/drivers/mmc/s3c2410mci.c - Samsung S3C2410 SDI Interface driver
+ *
+ * Copyright (C) 2004 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.
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+
+#include <asm/dma.h>
+#include <asm/dma-mapping.h>
+#include <asm/arch/dma.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/amba.h>
+#include <asm/hardware/clock.h>
+#include <asm/mach/mmc.h>
+
+#include <asm/arch/regs-sdi.h>
+#include <asm/arch/regs-gpio.h>
+
+//#define S3C2410SDI_DMA_BACKBUF
+
+#ifdef CONFIG_MMC_DEBUG
+#define DBG(x...) printk(KERN_DEBUG x)
+#else
+#define DBG(x...) do { } while (0)
+#endif
+
+#include "s3c2410mci.h"
+
+#define DRIVER_NAME "mmci-s3c2410"
+#define PFX DRIVER_NAME ": "
+
+#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
+
+// #define KERN_DEBUG KERN_INFO
+
+typedef enum {
+ DMAP_READ,
+ DMAP_WRITE,
+} eDMAPurpose_t;
+
+static struct s3c2410_dma_client s3c2410sdi_dma_client = {
+ .name = "s3c2410-sdi",
+};
+
+
+
+/*
+ * 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
+*/
+
+static irqreturn_t s3c2410sdi_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct s3c2410sdi_host *host;
+ u32 sdi_csta, sdi_dsta, sdi_dcnt;
+ u32 sdi_cclear, sdi_dclear;
+ unsigned long iflags;
+
+ host = (struct s3c2410sdi_host *)dev_id;
+
+ //Check for things not supposed to happen
+ if (!host)
+ return IRQ_HANDLED;
+
+ sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ sdi_dsta = readl(host->base + S3C2410_SDIDSTA);
+ sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);
+
+ DBG(PFX "IRQ csta=0x%08x dsta=0x%08x dcnt:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt);
+
+ spin_lock_irqsave( &host->complete_lock, iflags);
+
+ if (host->complete_what==COMPLETION_NONE)
+ goto clear_imask;
+
+ if (!host->mrq)
+ goto clear_imask;
+
+ sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ sdi_dsta = readl(host->base + S3C2410_SDIDSTA);
+ sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);
+ sdi_cclear = 0;
+ sdi_dclear = 0;
+
+
+ if (sdi_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
+ host->mrq->cmd->error = MMC_ERR_TIMEOUT;
+ goto transfer_closed;
+ }
+
+ if (sdi_csta & S3C2410_SDICMDSTAT_CMDSENT) {
+ if (host->complete_what == COMPLETION_CMDSENT) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ goto transfer_closed;
+ }
+ sdi_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
+ }
+
+ if (sdi_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
+ if (host->mrq->cmd->flags & MMC_RSP_CRC) {
+ host->mrq->cmd->error = MMC_ERR_BADCRC;
+ goto transfer_closed;
+ }
+ sdi_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
+ }
+
+ if (sdi_csta & S3C2410_SDICMDSTAT_RSPFIN) {
+ if (host->complete_what == COMPLETION_RSPFIN) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ goto transfer_closed;
+ }
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->complete_what = COMPLETION_XFERFINISH;
+ }
+ sdi_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
+ }
+
+ if (sdi_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->mrq->data->error = MMC_ERR_FIFO;
+ goto transfer_closed;
+ }
+
+ if (sdi_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->mrq->data->error = MMC_ERR_BADCRC;
+ goto transfer_closed;
+ }
+
+ if (sdi_dsta & S3C2410_SDIDSTA_CRCFAIL) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->mrq->data->error = MMC_ERR_BADCRC;
+ goto transfer_closed;
+ }
+
+ if (sdi_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->mrq->data->error = MMC_ERR_TIMEOUT;
+ goto transfer_closed;
+ }
+
+ if (sdi_dsta & S3C2410_SDIDSTA_XFERFINISH) {
+ if (host->complete_what == COMPLETION_XFERFINISH) {
+ host->mrq->cmd->error = MMC_ERR_NONE;
+ host->mrq->data->error = MMC_ERR_NONE;
+ goto transfer_closed;
+ }
+ if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN) {
+ host->mrq->data->error = MMC_ERR_NONE;
+ host->complete_what = COMPLETION_RSPFIN;
+ }
+ sdi_dclear |= S3C2410_SDIDSTA_XFERFINISH;
+ }
+
+ writel(sdi_cclear, host->base + S3C2410_SDICMDSTAT);
+ writel(sdi_dclear, host->base + S3C2410_SDIDSTA);
+
+ spin_unlock_irqrestore( &host->complete_lock, iflags);
+ DBG(PFX "IRQ still waiting.\n");
+ return IRQ_HANDLED;
+
+
+transfer_closed:
+ host->complete_what = COMPLETION_NONE;
+ complete(&host->complete_request);
+ writel(0, host->base + S3C2410_SDIIMSK);
+ spin_unlock_irqrestore( &host->complete_lock, iflags);
+ DBG(PFX "IRQ transfer closed.\n");
+ return IRQ_HANDLED;
+
+clear_imask:
+ writel(0, host->base + S3C2410_SDIIMSK);
+ spin_unlock_irqrestore( &host->complete_lock, iflags);
+ DBG(PFX "IRQ clear imask.\n");
+ return IRQ_HANDLED;
+
+}
+
+
+/*
+ * ISR for the CardDetect Pin
+*/
+
+static irqreturn_t s3c2410sdi_irq_cd(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)dev_id;
+
+ printk("s3c2410sdi_irq_cd: irq=%d, host=%d\n", irq, host);
+ mmc_detect_change(host->mmc);
+
+ return IRQ_HANDLED;
+}
+
+
+
+void s3c2410sdi_dma_done_callback(s3c2410_dma_chan_t *dma_ch, void *buf_id,
+ int size, s3c2410_dma_buffresult_t result)
+{ unsigned long iflags;
+ u32 sdi_csta, sdi_dsta,sdi_dcnt;
+ struct s3c2410sdi_host *host = (struct s3c2410sdi_host *)buf_id;
+
+ sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ sdi_dsta = readl(host->base + S3C2410_SDIDSTA);
+ sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);
+
+ DBG(PFX "DMAD csta=0x%08x dsta=0x%08x dcnt:0x%08x result:0x%08x\n", sdi_csta, sdi_dsta, sdi_dcnt, result);
+
+ spin_lock_irqsave( &host->complete_lock, iflags);
+
+ if(!host->mrq) goto out;
+ if(!host->mrq->data) goto out;
+
+
+ sdi_csta = readl(host->base + S3C2410_SDICMDSTAT);
+ sdi_dsta = readl(host->base + S3C2410_SDIDSTA);
+ sdi_dcnt = readl(host->base + S3C2410_SDIDCNT);
+
+ if (result!=S3C2410_RES_OK)
+ goto fail_request;
+
+ if (host->mrq->data->flags & MMC_DATA_READ) {
+ if (sdi_dcnt>0)
+ goto fail_request;
+ }
+
+out:
+ complete(&host->complete_dma);
+ spin_unlock_irqrestore(&host->complete_lock, iflags);
+ return;
+
+
+fail_request:
+ host->mrq->data->error = MMC_ERR_DMA;
+ host->complete_what = COMPLETION_NONE;
+ complete(&host->complete_dma);
+ complete(&host->complete_request);
+ writel(0, host->base + S3C2410_SDIIMSK);
+ goto out;
+
+}
+
+
+void s3c2410sdi_dma_setup(struct s3c2410sdi_host *host, eDMAPurpose_t purpose) {
+ s3c2410_dmasrc_t source = 0;
+
+ switch (purpose) {
+ case DMAP_READ:
+ source = S3C2410_DMASRC_HW;
+ break;
+
+ case DMAP_WRITE:
+ source = S3C2410_DMASRC_MEM;
+ break;
+
+ default:
+ BUG();
+ }
+
+ s3c2410_dma_devconfig(host->dma, source, 3, host->mem->start + S3C2410_SDIDATA);
+ s3c2410_dma_config(host->dma, 4, (1<<23) | (2<<24));
+ s3c2410_dma_set_buffdone_fn(host->dma, s3c2410sdi_dma_done_callback);
+ s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART);
+}
+
+static void s3c2410sdi_request(struct mmc_host *mmc, struct mmc_request *mrq) {
+ struct s3c2410sdi_host *host = mmc_priv(mmc);
+ u32 sdi_carg, sdi_ccon, sdi_timer;
+ u32 sdi_bsize, sdi_dcon, sdi_imsk;
+
+ DBG(KERN_DEBUG PFX "request: [CMD] opcode:0x%02x arg:0x%08x flags:%x retries:%u\n",
+ mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
+
+ sdi_ccon = mrq->cmd->opcode & S3C2410_SDICMDCON_INDEX;
+ sdi_ccon|= S3C2410_SDICMDCON_SENDERHOST;
+ sdi_ccon|= S3C2410_SDICMDCON_CMDSTART;
+
+ sdi_carg = mrq->cmd->arg;
+
+ //FIXME: Timer value ?!
+ sdi_timer= 0xF000;
+
+ sdi_bsize= 0;
+ sdi_dcon = 0;
+ sdi_imsk = 0;
+
+ //enable interrupts for transmission errors
+ sdi_imsk |= S3C2410_SDIIMSK_RESPONSEND;
+ sdi_imsk |= S3C2410_SDIIMSK_CRCSTATUS;
+
+
+ host->complete_what = COMPLETION_CMDSENT;
+
+ if (mrq->cmd->flags & MMC_RSP_MASK) {
+ host->complete_what = COMPLETION_RSPFIN;
+
+ sdi_ccon |= S3C2410_SDICMDCON_WAITRSP;
+ sdi_imsk |= S3C2410_SDIIMSK_CMDTIMEOUT;
+
+ } else {
+ //We need the CMDSENT-Interrupt only if we want are not waiting
+ //for a response
+ sdi_imsk |= S3C2410_SDIIMSK_CMDSENT;
+ }
+
+ if (mrq->cmd->flags & MMC_RSP_LONG)
+ sdi_ccon |= S3C2410_SDICMDCON_LONGRSP;
+
+ if (mrq->cmd->flags & MMC_RSP_CRC)
+ sdi_imsk |= S3C2410_SDIIMSK_RESPONSECRC;
+
+
+ if (mrq->data) {
+ host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
+
+ sdi_bsize = (1 << mrq->data->blksz_bits);
+
+ sdi_dcon = (mrq->data->blocks & S3C2410_SDIDCON_BLKNUM_MASK);
+ sdi_dcon |= S3C2410_SDIDCON_DMAEN;
+
+ sdi_imsk |= S3C2410_SDIIMSK_FIFOFAIL;
+ sdi_imsk |= S3C2410_SDIIMSK_DATACRC;
+ sdi_imsk |= S3C2410_SDIIMSK_DATATIMEOUT;
+ sdi_imsk |= S3C2410_SDIIMSK_DATAFINISH;
+ sdi_imsk |= 0xFFFFFFE0;
+
+ DBG(PFX "request: [DAT] bsize:%u blocks:%u bytes:%u\n",
+ sdi_bsize, mrq->data->blocks, mrq->data->blocks * sdi_bsize);
+
+ if (mrq->data->flags & MMC_DATA_WIDE)
+ sdi_dcon |= S3C2410_SDIDCON_WIDEBUS;
+
+ if (!(mrq->data->flags & MMC_DATA_STREAM))
+ sdi_dcon |= S3C2410_SDIDCON_BLOCKMODE;
+
+ if (mrq->data->flags & MMC_DATA_WRITE) {
+ sdi_dcon |= S3C2410_SDIDCON_TXAFTERRESP;
+ sdi_dcon |= S3C2410_SDIDCON_XFER_TXSTART;
+ s3c2410sdi_dma_setup(host, DMAP_WRITE);
+#ifdef S3C2410SDI_DMA_BACKBUF
+ memcpy(host->dmabuf_log, mrq->data->req->buffer, mrq->data->blocks * sdi_bsize);
+#endif
+ }
+
+ if (mrq->data->flags & MMC_DATA_READ) {
+ sdi_dcon |= S3C2410_SDIDCON_RXAFTERCMD;
+ sdi_dcon |= S3C2410_SDIDCON_XFER_RXSTART;
+ s3c2410sdi_dma_setup(host, DMAP_READ);
+ }
+
+
+ //start DMA
+#ifdef S3C2410SDI_DMA_BACKBUF
+ s3c2410_dma_enqueue(host->dma, (void *) host,
+ host->dmabuf_phys,
+ (mrq->data->blocks << mrq->data->blksz_bits) );
+#else
+ s3c2410_dma_enqueue(host->dma, (void *) host,
+ virt_to_phys(mrq->data->req->buffer),
+ (mrq->data->blocks << mrq->data->blksz_bits) );
+#endif
+ }
+
+ host->mrq = mrq;
+
+ init_completion(&host->complete_request);
+ init_completion(&host->complete_dma);
+
+ //Clear command and data status registers
+ writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
+ writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
+
+ // Setup SDI controller
+ writel(sdi_bsize,host->base + S3C2410_SDIBSIZE);
+ writel(sdi_timer,host->base + S3C2410_SDITIMER);
+ writel(sdi_imsk,host->base + S3C2410_SDIIMSK);
+
+ // Setup SDI command argument and data control
+ writel(sdi_carg, host->base + S3C2410_SDICMDARG);
+ writel(sdi_dcon, host->base + S3C2410_SDIDCON);
+
+ // This initiates transfer
+ writel(sdi_ccon, host->base + S3C2410_SDICMDCON);
+
+ // Wait for transfer to complete
+ wait_for_completion(&host->complete_request);
+ DBG("[CMD] request complete.\n");
+ if(mrq->data) {
+ wait_for_completion(&host->complete_dma);
+ DBG("[DAT] DMA complete.\n");
+ }
+
+ // Wait for DMA to complete
+// if (mrq->data)
+// wait_for_completion(&host->complete_dma);
+
+ //Cleanup controller
+ writel(0, host->base + S3C2410_SDICMDARG);
+ writel(0, host->base + S3C2410_SDIDCON);
+ writel(0, host->base + S3C2410_SDICMDCON);
+ writel(0, host->base + S3C2410_SDIIMSK);
+
+ // Read response
+ mrq->cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
+ mrq->cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
+ mrq->cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
+ mrq->cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
+
+ host->mrq = NULL;
+
+ DBG(PFX "request done.\n");
+
+ // 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_bits);
+ // if (mrq->data->flags & MMC_DATA_READ);
+#ifdef S3C2410SDI_DMA_BACKBUF
+ memcpy(mrq->data->req->buffer, host->dmabuf_log, mrq->data->bytes_xfered);
+#endif
+ } else {
+ mrq->data->bytes_xfered = 0;
+ }
+
+ // If we had an error while transfering data we flush the
+ // DMA channel to clear out any garbage
+ if (mrq->data->error != MMC_ERR_NONE) {
+ s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH);
+ DBG(PFX "flushing DMA.\n");
+ }
+ // Issue stop command
+ if (mrq->data->stop)
+ mmc_wait_for_cmd(mmc, mrq->data->stop, 3);
+
+
+request_done:
+
+ mrq->done(mrq);
+}
+
+static void s3c2410sdi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) {
+ struct s3c2410sdi_host *host = mmc_priv(mmc);
+ u32 sdi_psc, sdi_con;
+
+ //Set power
+ sdi_con = readl(host->base + S3C2410_SDICON);
+ switch (ios->power_mode) {
+ case MMC_POWER_ON:
+ case MMC_POWER_UP:
+ s3c2410_gpio_setpin(S3C2410_GPA17, 1); // card power on
+
+ 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);
+
+ sdi_con |= S3C2410_SDICON_FIFORESET;
+ break;
+
+ case MMC_POWER_OFF:
+ default:
+ s3c2410_gpio_setpin(S3C2410_GPA17, 0); // card power off
+
+ s3c2410_gpio_setpin(S3C2410_GPE5, 0);
+ s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_OUTP);
+ break;
+ }
+
+ //Set clock
+ for (sdi_psc=0;sdi_psc<255;sdi_psc++) {
+ if ((clk_get_rate(host->clk) / (2*(sdi_psc+1))) <= ios->clock)
+ break;
+ }
+
+ if (sdi_psc > 255)
+ sdi_psc = 255;
+ writel(sdi_psc, host->base + S3C2410_SDIPRE);
+
+ //Set CLOCK_ENABLE
+ if (ios->clock) sdi_con |= S3C2410_SDICON_CLOCKTYPE;
+ else sdi_con &=~S3C2410_SDICON_CLOCKTYPE;
+
+ writel(sdi_con, host->base + S3C2410_SDICON);
+}
+
+static struct mmc_host_ops s3c2410sdi_ops = {
+ .request = s3c2410sdi_request,
+ .set_ios = s3c2410sdi_set_ios,
+};
+
+static int s3c2410sdi_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mmc_host *mmc;
+ struct s3c2410sdi_host *host;
+
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct s3c2410sdi_host), dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto probe_out;
+ }
+
+ host = mmc_priv(mmc);
+
+ spin_lock_init( &host->complete_lock );
+ host->complete_what = COMPLETION_NONE;
+ host->mmc = mmc;
+ host->dma = S3C2410SDI_DMA;
+ host->irq_cd = IRQ_EINT5;
+
+ host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!host->mem) {
+ printk(KERN_INFO PFX "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) {
+ printk(KERN_INFO PFX "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) {
+ printk(KERN_INFO PFX "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) {
+ printk(KERN_INFO PFX "failed to get interrupt resouce.\n");
+ ret = -EINVAL;
+ goto probe_iounmap;
+ }
+
+ if (request_irq(host->irq, s3c2410sdi_irq, 0, DRIVER_NAME, host)) {
+ printk(KERN_INFO PFX "failed to request sdi interrupt.\n");
+ ret = -ENOENT;
+ goto probe_iounmap;
+ }
+
+ s3c2410_gpio_cfgpin(S3C2410_GPF5, S3C2410_GPF5_EINT5);
+ set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
+
+ if (request_irq(host->irq_cd, s3c2410sdi_irq_cd, 0, DRIVER_NAME, host)) {
+ printk(KERN_WARNING PFX "failed to request card detect interrupt.\n" );
+ ret = -ENOENT;
+ goto probe_free_irq;
+ }
+
+ if (s3c2410_dma_request(S3C2410SDI_DMA, &s3c2410sdi_dma_client, NULL)) {
+ printk(KERN_WARNING PFX "unable to get DMA channel.\n" );
+ ret = -EBUSY;
+ goto probe_free_irq_cd;
+ }
+
+ host->clk = clk_get(dev, "sdi");
+ if (IS_ERR(host->clk)) {
+ printk(KERN_INFO PFX "failed to find clock source.\n");
+ ret = PTR_ERR(host->clk);
+ host->clk = NULL;
+ goto probe_free_host;
+ }
+
+ if ((ret = clk_use(host->clk))) {
+ printk(KERN_INFO PFX "failed to use clock source.\n");
+ goto clk_free;
+ }
+
+ if ((ret = clk_enable(host->clk))) {
+ printk(KERN_INFO PFX "failed to enable clock source.\n");
+ goto clk_unuse;
+ }
+
+ mmc->ops = &s3c2410sdi_ops;
+ mmc->ocr_avail = MMC_VDD_32_33;
+ mmc->flags = MMC_HOST_WIDEMODE;
+ mmc->f_min = clk_get_rate(host->clk) / 512;
+ mmc->f_max = clk_get_rate(host->clk) / 2;
+
+
+ //HACK: There seems to be a hardware bug in TomTom GO.
+ if (mmc->f_max>3000000)
+ mmc->f_max=3000000;
+
+ /*
+ * Since we only have a 16-bit data length register, we must
+ * ensure that we don't exceed 2^16-1 bytes in a single request.
+ * Choose 64 (512-byte) sectors as the limit.
+ */
+ mmc->max_sectors = 64;
+
+ /*
+ * Set the maximum segment size. Since we aren't doing DMA
+ * (yet) we are only limited by the data length register.
+ */
+
+ mmc->max_seg_size = mmc->max_sectors << 9;
+#ifdef S3C2410SDI_DMA_BACKBUF
+ host->dmabuf_log = dma_alloc_coherent(NULL, mmc->max_seg_size ,&host->dmabuf_phys, GFP_KERNEL|GFP_DMA);
+
+ if (!host->dmabuf_log) {
+ printk(KERN_INFO PFX "failed to allocate DMA buffer.\n");
+ goto clk_disable;
+ }
+
+ host->dmabuf_size = mmc->max_seg_size;
+
+ printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u dma=%u dmabuf_l=%p dmabuf_p=%p.\n",
+ host->base, host->irq, host->irq_cd, host->dma, host->dmabuf_log, host->dmabuf_phys);
+#else
+ printk(KERN_INFO PFX "probe: mapped sdi_base=%p irq=%u irq_cd=%u dma=%u.\n",
+ host->base, host->irq, host->irq_cd, host->dma);
+#endif
+ if ((ret = mmc_add_host(mmc))) {
+ printk(KERN_INFO PFX "failed to add mmc host.\n");
+ goto free_dmabuf;
+ }
+
+ dev_set_drvdata(dev, mmc);
+
+ printk(KERN_INFO PFX "initialisation done.\n");
+ return 0;
+
+ free_dmabuf:
+#ifdef S3C2410SDI_DMA_BACKBUF
+ dma_free_coherent(NULL, host->dmabuf_size, host->dmabuf_log, host->dmabuf_phys);
+#endif
+
+ clk_disable(host->clk);
+
+ clk_unuse:
+ clk_unuse(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 s3c2410sdi_remove(struct device *dev)
+{
+ struct mmc_host *mmc = dev_get_drvdata(dev);
+ struct s3c2410sdi_host *host = mmc_priv(mmc);
+
+ mmc_remove_host(mmc);
+#ifdef S3C2410SDI_DMA_BACKBUF
+ dma_free_coherent(NULL, host->dmabuf_size, host->dmabuf_log, host->dmabuf_phys);
+#endif
+ clk_disable(host->clk);
+ clk_unuse(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 struct device_driver s3c2410sdi_driver =
+{
+ .name = "s3c2410-sdi",
+ .bus = &platform_bus_type,
+ .probe = s3c2410sdi_probe,
+ .remove = s3c2410sdi_remove,
+};
+
+static int __init s3c2410sdi_init(void)
+{
+ return driver_register(&s3c2410sdi_driver);
+}
+
+static void __exit s3c2410sdi_exit(void)
+{
+ driver_unregister(&s3c2410sdi_driver);
+}
+
+module_init(s3c2410sdi_init);
+module_exit(s3c2410sdi_exit);
+
+MODULE_DESCRIPTION("Samsung S3C2410 Multimedia Card Interface driver");
+MODULE_LICENSE("GPL");
diff -NurbBpP linux-2.6.11.11-h1940-2/drivers/mmc/s3c2410mci.h linux-2.6.11.11-h1940-3.3/drivers/mmc/s3c2410mci.h
--- linux-2.6.11.11-h1940-2/drivers/mmc/s3c2410mci.h 1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/drivers/mmc/s3c2410mci.h 2005-06-03 18:53:54.000000000 +0200
@@ -0,0 +1,45 @@
+/*
+ * linux/drivers/mmc/s3c2410mci.h - Samsung S3C2410 SDI Interface driver
+ *
+ * Copyright (C) 2004 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.
+ */
+
+struct clk;
+
+//FIXME: DMA Resource management ?!
+#define S3C2410SDI_DMA 0
+
+enum s3c2410sdi_waitfor {
+ COMPLETION_NONE,
+ COMPLETION_CMDSENT,
+ COMPLETION_RSPFIN,
+ COMPLETION_XFERFINISH,
+ COMPLETION_XFERFINISH_RSPFIN,
+};
+
+struct s3c2410sdi_host {
+ struct mmc_host *mmc;
+
+ struct resource *mem;
+ struct clk *clk;
+ void __iomem *base;
+ int irq;
+ int irq_cd;
+ int dma;
+#ifdef S3C2410SDI_DMA_BACKBUF
+ dma_addr_t dmabuf_phys;
+ void *dmabuf_log;
+ unsigned int dmabuf_size;
+#endif
+
+ struct mmc_request *mrq;
+
+ spinlock_t complete_lock;
+ struct completion complete_request;
+ struct completion complete_dma;
+ enum s3c2410sdi_waitfor complete_what;
+};
diff -NurbBpP linux-2.6.11.11-h1940-2/include/asm-arm/arch-s3c2410/regs-sdi.h linux-2.6.11.11-h1940-3.3/include/asm-arm/arch-s3c2410/regs-sdi.h
--- linux-2.6.11.11-h1940-2/include/asm-arm/arch-s3c2410/regs-sdi.h 2005-03-29 06:04:40.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/include/asm-arm/arch-s3c2410/regs-sdi.h 2005-06-03 19:11:47.000000000 +0200
@@ -47,7 +47,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)
@@ -73,6 +74,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)
diff -NurbBpP linux-2.6.11.11-h1940-2/include/linux/mmc/card.h linux-2.6.11.11-h1940-3.3/include/linux/mmc/card.h
--- linux-2.6.11.11-h1940-2/include/linux/mmc/card.h 2004-11-21 14:46:21.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/include/linux/mmc/card.h 2005-06-03 19:11:47.000000000 +0200
@@ -24,12 +24,55 @@ struct mmc_cid {
};
struct mmc_csd {
- unsigned char mmca_vsn;
- unsigned short cmdclass;
+ u8 sd;
+ u8 csd_vers;
+ u8 spec_vers;
+ u8 taac;
+ u8 nsac;
+ u16 cmdclass;
+ u8 tran_speed;
+ u8 read_blkbits;
+ u8 read_bl_partial;
+ u8 write_blk_misalign;
+ u8 read_blk_misalign;
+ u8 dsr_imp;
+ u16 c_size;
+ u8 vdd_r_curr_min;
+ u8 vdd_r_curr_max;
+ u8 vdd_w_curr_min;
+ u8 vdd_w_curr_max;
+ u8 c_size_mult;
+ union {
+ struct { /* MMC system specification version 3.1 */
+ u8 erase_grp_size;
+ u8 erase_grp_mult;
+ } mmc31;
+ struct { /* MMC system specification version 2.2 */
+ u8 sector_size;
+ u8 erase_grp_size;
+ } mmc22;
+ struct { /* SD Version 1.0 */
+ u8 erase_blk_en;
+ u8 erase_blk_size;
+ } sd10;
+ } erase;
+
+ u8 wp_grp_size;
+ u8 wp_grp_enable;
+ u8 default_ecc;
+ u8 r2w_factor;
+ u8 write_bl_len;
+ u8 write_bl_partial;
+ u8 file_format_grp;
+ u8 copy;
+ u8 perm_write_protect;
+ u8 tmp_write_protect;
+ u8 file_format;
+ u8 ecc;
+
unsigned short tacc_clks;
unsigned int tacc_ns;
unsigned int max_dtr;
- unsigned int read_blkbits;
unsigned int capacity;
};
@@ -42,8 +85,10 @@ struct mmc_card {
struct list_head node; /* node in hosts devices list */
struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the device */
+ unsigned char sd;
unsigned int rca; /* relative card address of device */
unsigned int state; /* (our) card state */
+ u8 bus_width; /* bus_width the card is set to */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */
#define MMC_STATE_BAD (1<<2) /* unrecognised device */
diff -NurbBpP linux-2.6.11.11-h1940-2/include/linux/mmc/host.h linux-2.6.11.11-h1940-3.3/include/linux/mmc/host.h
--- linux-2.6.11.11-h1940-2/include/linux/mmc/host.h 2004-11-21 14:46:21.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/include/linux/mmc/host.h 2005-06-03 19:11:47.000000000 +0200
@@ -61,12 +61,15 @@ struct mmc_host_ops {
struct mmc_card;
struct device;
+#define MMC_HOST_WIDEMODE 1
+
struct mmc_host {
struct device *dev;
struct mmc_host_ops *ops;
unsigned int f_min;
unsigned int f_max;
u32 ocr_avail;
+ u32 flags;
char host_name[8];
/* host specific block data */
diff -NurbBpP linux-2.6.11.11-h1940-2/include/linux/mmc/mmc.h linux-2.6.11.11-h1940-3.3/include/linux/mmc/mmc.h
--- linux-2.6.11.11-h1940-2/include/linux/mmc/mmc.h 2005-03-29 06:05:49.000000000 +0200
+++ linux-2.6.11.11-h1940-3.3/include/linux/mmc/mmc.h 2005-06-03 19:11:47.000000000 +0200
@@ -47,6 +47,8 @@ struct mmc_command {
#define MMC_ERR_FIFO 3
#define MMC_ERR_FAILED 4
#define MMC_ERR_INVALID 5
+#define MMC_ERR_BUSY 6
+#define MMC_ERR_DMA 7
struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* assoicated request */
@@ -63,6 +65,7 @@ struct mmc_data {
#define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10)
+#define MMC_DATA_WIDE (1 << 11)
unsigned int bytes_xfered;
@@ -71,6 +74,8 @@ struct mmc_data {
unsigned int sg_len; /* size of scatter list */
struct scatterlist *sg; /* I/O scatter list */
+
+ struct request *req;
};
struct mmc_request {
diff -NurbBpP linux-2.6.11.11-h1940-2/include/linux/mmc/protocol.h linux-2.6.11.11-h1940-3.3/include/linux/mmc/protocol.h
--- linux-2.6.11.11-h1940-2/include/linux/mmc/protocol.h 2004-11-21 14:46:21.000000000 +0100
+++ linux-2.6.11.11-h1940-3.3/include/linux/mmc/protocol.h 2005-06-03 19:39:23.000000000 +0200
@@ -76,6 +76,9 @@
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1b */
+#define MMC_SD_APP_OP_COND 41 /* ???? op conds ??? */
+#define MMC_SD_SET_BUS_WIDTH 6 /* ???? 00=1 10=4 ??? */
+
/*
MMC status in R1
Type
@@ -118,13 +121,14 @@
/* These are unpacked versions of the actual responses */
struct _mmc_csd {
- u8 csd_structure;
+ u8 sd;
+ u8 csd_vers;
u8 spec_vers;
u8 taac;
u8 nsac;
u8 tran_speed;
- u16 ccc;
- u8 read_bl_len;
+ u16 cmdclass;
+ u8 read_blkbits;
u8 read_bl_partial;
u8 write_blk_misalign;
u8 read_blk_misalign;
@@ -139,11 +143,15 @@ struct _mmc_csd {
struct { /* MMC system specification version 3.1 */
u8 erase_grp_size;
u8 erase_grp_mult;
- } v31;
+ } mmc31;
struct { /* MMC system specification version 2.2 */
u8 sector_size;
u8 erase_grp_size;
- } v22;
+ } mmc22;
+ struct { /* SD Version 1.0 */
+ u8 erase_blk_en;
+ u8 erase_blk_size;
+ } sd10;
} erase;
u8 wp_grp_size;
u8 wp_grp_enable;
@@ -159,6 +167,13 @@ struct _mmc_csd {
u8 ecc;
};
+#define SD_CSDV_1 0
+
+#define MMC_CSDV_1 0
+#define MMC_CSDV_14 1
+#define MMC_CSDV_2 2
+#define MMC_CSDV_3 3
+
#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */
#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */
#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */