Re: mmc errors (was Re: [PATCH] arm: Fix DEBUG_LL for omap zoom2/3)

Laurent Epinat
2010-03-24T15:52:53Z

Hello all

I've juste finished a modification about a multi eMMC on own platform.
we used 4 eMMC of 32Gib (micron MTFC32GHKD) on same host (MMC1) and we used it like de big device (128Gib)

I had a similar problem (It was the pbias 1 configuration)

I can't test the effect on SD card
any comments...

diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig
index 3f2a912..6054441 100644
--- a/drivers/mmc/card/Kconfig
+++ b/drivers/mmc/card/Kconfig
@@ -32,6 +32,19 @@ config MMC_BLOCK_BOUNCE

If unsure, say Y here.

+config MULTI_MMC_AS_ONE
+ bool "Simulate multi mmc card as one big"
+ depends on MMC_BLOCK && MULTI_MMC_ON_HOST
+ default y
+ help
+ A multiple SD/MMC card can be connected on a single host controller.
+ with this you can see a large device.
+
+ Say N here if you want to see as multiple storage devices
+
+ If unsure, say N here.
+
+
config SDIO_UART
tristate "SDIO UART/GPS class support"
help
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 1f552c6..a368ecb 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -16,6 +16,12 @@
*
* Author: Andrew Christian
* 28 May 2002
+ *
+ * 20100315
+ * Laurent Epinat <laurent.epinat@cioinfoindus.fr>
+ * Add multi "card" on one controller
+ * seen as multi card or as one big card
+ *
*/
#include <linux/moduleparam.h>
#include <linux/module.h>
@@ -41,6 +47,10 @@

#include "queue.h"

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+#include "../core/mmc_ops.h"
+#endif
+
MODULE_ALIAS("mmc:block");

/*
@@ -248,6 +258,13 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_blk_request brq;
int ret = 1, disable_multi = 0;

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+static struct mmc_card *card_cur=NULL;
+int card_size;
+#ifdef CONFIG_MULTI_MMC_AS_ONE
+ int idx;
+#endif
+#endif
mmc_claim_host(card->host);

do {
@@ -261,6 +278,17 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.cmd.arg = blk_rq_pos(req);
if (!mmc_card_blockaddr(card))
brq.cmd.arg <<= 9;
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ else {
+ /* All cards as a seem size */
+ /* TODO: check for card <2Gb (ext_csd is 0 );and
+ * calculation for a size different
+ **/
+ BUG_ON(!card->ext_csd.sectors);
+ card_size = card->ext_csd.sectors;
+ }
+#endif
+
brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
brq.data.blksz = 512;
brq.stop.opcode = MMC_STOP_TRANSMISSION;
@@ -273,9 +301,39 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
* restrictions, so we need to be prepared for too big
* requests.
*/
- if (brq.data.blocks > card->host->max_blk_count)
+ if (brq.data.blocks > card->host->max_blk_count)
brq.data.blocks = card->host->max_blk_count;

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+#ifdef CONFIG_MULTI_MMC_AS_ONE
+ idx = (brq.cmd.arg/card_size);
+ /* Force to select a new card if necessary */
+ if (card != card->host->cards[idx]) {
+ card = card->host->cards[idx];
+ card_size = card->ext_csd.sectors;
+ brq.cmd.arg -= idx * card_size;
+ }
+ /*
+ * Check if the block can go the selected card
+ * if not truc it
+ */
+ if (brq.cmd.arg + brq.data.blocks > card_size) {
+ brq.data.blocks = card_size - brq.cmd.arg;
+ }
+#endif /* CONFIG_MULTI_MMC_AS_ONE */
+
+ /* Change the current mmc on bus if needed */
+ if (card != card_cur){
+ if (!mmc_select_card(card)) {
+ card_cur=card;
+ }
+ else {
+ printk(KERN_ERR "%s: error: Can't select Card %d\n",
+ req->rq_disk->disk_name, card->rca);
+ }
+ }
+#endif /* CONFIG_MULTI_MMC_ON_HOST */
+
/*
* After a read error, we redo the request one sector at a time
* in order to accurately determine which sectors can be read
@@ -481,6 +539,12 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
struct mmc_blk_data *md;
int devidx, ret;

+#ifdef CONFIG_MULTI_MMC_AS_ONE
+ struct mmc_host *host = card->host;
+ int i;
+ int sec_num;
+#endif
+
devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);
if (devidx >= MMC_NUM_MINORS)
return ERR_PTR(-ENOSPC);
@@ -492,7 +556,6 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
goto out;
}

-
/*
* Set the read-only status based on the supported commands
* and the write protect switch.
@@ -543,15 +606,47 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
* The EXT_CSD sector count is in number or 512 byte
* sectors.
*/
+#ifdef CONFIG_MULTI_MMC_AS_ONE
+ for (i=0;i<host->card_detected;++i){
+ sec_num+=host->cards[i]->ext_csd.sectors;
+ }
+ set_capacity(md->disk, sec_num);
+#else
set_capacity(md->disk, card->ext_csd.sectors);
+#endif
} else {
/*
* The CSD capacity field is in units of read_blkbits.
* set_capacity takes units of 512 bytes.
*/
- set_capacity(md->disk,
- card->csd.capacity << (card->csd.read_blkbits - 9));
+#ifdef MMC_MULTI_CARD_AS_ONE
+ for (i=0; i<host->card_detected; ++i){
+ sec_num += host->cards[i]->csd.capacity <<
+ (host->cards[i]->csd.read_blkbits - 9)
+ }
+ set_capacity(md->disk, sec_num);
+#else
+ set_capacity(md->disk,
+ card->csd.capacity << (card->csd.read_blkbits - 9));
+#endif
}
+
+#if defined(CONFIG_MULTI_MMC_ON_HOST)
+{
+ /* deselect cards without waiting for completion */
+ struct mmc_request mrq={0};
+ struct mmc_command cmd={0};
+
+ cmd.opcode = MMC_SELECT_CARD;
+ cmd.arg = 0; /* deselect cards */
+ cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
+ mrq.cmd = &cmd;
+ mrq.cmd->mrq = &mrq;
+
+ card->host->ops->request(card->host, &mrq);
+}
+#endif
+
return md;

err_putdisk:
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index bb22ffd..b3c854e 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -16,3 +16,8 @@ config MMC_UNSAFE_RESUME

This option sets a default which can be overridden by the
module parameter "removable=0" or "removable=1".
+
+config MULTI_MMC_ON_HOST
+ bool "Detect multiple MMC/SD cards one same host controller"
+ help
+ If you say Y here, the MMC layer scan the bus to discover MMC cards
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index bdb165f..badec96 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -221,7 +222,9 @@ struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
*/
int mmc_add_card(struct mmc_card *card)
{
+#ifndef CONFIG_MULTI_MMC_AS_ONE
int ret;
+#endif
const char *type;

dev_set_name(&card->dev, "%s:%04x", mmc_hostname(card->host), card->rca);
@@ -255,9 +258,11 @@ int mmc_add_card(struct mmc_card *card)
type, card->rca);
}

+#ifndef CONFIG_MULTI_MMC_AS_ONE
ret = device_add(&card->dev);
if (ret)
return ret;
+#endif

#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
@@ -286,7 +291,9 @@ void mmc_remove_card(struct mmc_card *card)
printk(KERN_INFO "%s: card %04x removed\n",
mmc_hostname(card->host), card->rca);
}
+#ifndef CONFIG_MULTI_MMC_AS_ONE
device_del(&card->dev);
+#endif
}

put_device(&card->dev);
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 30acd52..b186933 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -910,9 +910,9 @@ static void mmc_power_up(struct mmc_host *host)
if (host->f_min > 400000) {
pr_warning("%s: Minimum clock frequency too high for "
"identification mode\n", mmc_hostname(host));
- host->ios.clock = host->f_min;
- } else
host->ios.clock = 400000;
+ } else
+ host->ios.clock = host->f_min;

host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host);
@@ -1164,8 +1164,6 @@ void mmc_stop_host(struct mmc_host *host)
}
mmc_bus_put(host);

- BUG_ON(host->card);
-
mmc_power_off(host);
}

@@ -1235,10 +1233,21 @@ EXPORT_SYMBOL(mmc_card_sleep);

int mmc_card_can_sleep(struct mmc_host *host)
{
+
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ struct mmc_card *card;
+ int i;
+ for (i=0; i<host->card_detected;++i){
+ card = host->cards[i];
+ if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
+ return 1;
+ }
+#else
struct mmc_card *card = host->card;

if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
return 1;
+#endif
return 0;
}
EXPORT_SYMBOL(mmc_card_can_sleep);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 0eac6c8..e29d1bd 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -5,6 +5,11 @@
* Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved.
* MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved.
*
+ * 20100315
+ * Laurent Epinat <laurent.epinat@cioinfoindus.fr>
+ * Add multi "card" detection on one controller
+ * they can be seen as multi card or as one big card
+ *
* 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.
@@ -224,6 +229,11 @@ static int mmc_read_ext_csd(struct mmc_card *card)
if (card->ext_csd.sectors)
mmc_card_set_blockaddr(card);
}
+ else {
+ /* TODO: In MULTI CARD we need the size to select the crad on the bus */
+ WARN_ON(!card->ext_csd.sectors);
+
+ }

switch (ext_csd[EXT_CSD_CARD_TYPE]) {
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
@@ -293,6 +303,261 @@ static struct device_type mmc_type = {
.groups = mmc_attr_groups,
};

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+/*
+ * Handle the detection and initialisation of a cards.
+ *
+ * In the case of a resume, "rescan" will contain a flag and
+ * we're trying to reinitialise all cards.
+ */
+static int mmc_init_cards(struct mmc_host *host, u32 ocr, int rescan)
+{
+ int i;
+ int high_speed = 1;
+ unsigned int ext_csd_bit=0, bus_width = MMC_BUS_WIDTH_8;
+ struct mmc_card *card=NULL;
+ int err;
+ u32 cid[4];
+ unsigned int max_dtr = (unsigned int)-1;
+
+ BUG_ON(!host);
+ WARN_ON(!host->claimed);
+
+ BUG_ON(host->card_detected);
+
+ /*
+ * 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.
+ */
+ mmc_go_idle(host);
+
+ /* The extra bit indicates that we support high capacity */
+ err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
+ if (err)
+ goto err;
+
+ /*
+ * For SPI, enable CRC as appropriate.
+ */
+ if (mmc_host_is_spi(host)) {
+ err = mmc_spi_set_crc(host, use_spi_crc);
+ if (err)
+ goto err;
+ }
+
+ while(host->card_detected < MMC_MAX_CARD) {
+ /*
+ * Fetch CID from card.
+ */
+ if (mmc_host_is_spi(host))
+ err = mmc_send_cid(host, 0, cid);
+ else
+ err = mmc_all_send_cid(host, cid);
+
+ if (err) {
+ if (!host->card_detected)
+ goto err;
+ else
+ break;
+ }
+ if (rescan) {
+ card = host->cards[host->card_detected];
+ if (memcmp(cid, card->raw_cid, sizeof(cid)) != 0) {
+ err = -ENOENT;
+ goto err;
+ }
+ } else {
+ /*
+ * Allocate card structure.
+ */
+ card = mmc_alloc_card(host, &mmc_type);
+ if (IS_ERR(card)) {
+ err = PTR_ERR(card);
+ goto err;
+ }
+
+ card->type = MMC_TYPE_MMC;
+ card->rca = host->card_detected+1;
+ memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
+
+ host->cards[host->card_detected] = card;
+ }
+
+ /*
+ * For native busses: set card RCA and quit open drain mode.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_set_relative_addr(card);
+ if (err)
+ goto free_card;
+ }
+ host->card_detected++;
+ }
+
+ if (host->card_detected >= MMC_MAX_CARD) {
+ printk(KERN_ERR "%s: Problem card number detected\n",
+ mmc_hostname(host));
+ goto free_card;
+ }
+
+ printk(KERN_INFO "%s: %d cards detected on this controller\n",
+ mmc_hostname(host), host->card_detected);
+
+ /* Set to push pull mode */
+ if (!mmc_host_is_spi(host)) {
+ mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
+ }
+
+ for (i=0; !rescan && i < host->card_detected; ++i) {
+ card = host->cards[i];
+ /*
+ * Fetch CSD from card.
+ */
+ err = mmc_send_csd(card, card->raw_csd);
+ if (err)
+ goto free_card;
+
+ err = mmc_decode_csd(card);
+ if (err)
+ goto free_card;
+ err = mmc_decode_cid(card);
+ if (err)
+ goto free_card;
+ }
+
+ /* Get the EXT CSD for each card */
+ for (i=0; i < host->card_detected; ++i) {
+ card = host->cards[i];
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto free_card;
+ }
+
+ /*
+ * Fetch and process extended CSD.
+ */
+ if (!rescan) {
+ err = mmc_read_ext_csd(card);
+ if (err)
+ goto free_card;
+ }
+
+ /* Check if all cards can be switched to high speed mode */
+ if ((card->ext_csd.hs_max_dtr == 0) ||
+ !(host->caps & MMC_CAP_MMC_HIGHSPEED))
+ {
+ high_speed = 0;
+ }
+
+ /* Check if all cards can be switch to wide bus */
+ if (card->csd.mmca_vsn < CSD_SPEC_VER_4) {
+ bus_width = 0;
+ }
+ }
+
+ if (bus_width && (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
+ if (host->caps & MMC_CAP_8_BIT_DATA) {
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_8;
+ bus_width = MMC_BUS_WIDTH_8;
+ } else {
+ ext_csd_bit = EXT_CSD_BUS_WIDTH_4;
+ bus_width = MMC_BUS_WIDTH_4;
+ }
+ }
+ else {
+ bus_width = 0;
+ }
+
+ /* Set the speed en bus width to all cards */
+ for (i=0; i < host->card_detected; ++i) {
+ card = host->cards[i];
+
+ /*
+ * Select card, as all following commands rely on that.
+ */
+ if (!mmc_host_is_spi(host)) {
+ err = mmc_select_card(card);
+ if (err)
+ goto free_card;
+ }
+
+ if (high_speed) {
+ /*
+ * Activate high speed for all cards
+ */
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1);
+ if (err && err != -EBADMSG)
+ goto free_card;
+
+ if (err) {
+ high_speed = 0;
+ printk(KERN_WARNING "%s: switch to highspeed failed\n",
+ mmc_hostname(card->host));
+ }
+ else {
+ mmc_card_set_highspeed(card);
+ }
+ }
+
+ /*
+ * Activate wide bus (if supported).
+ */
+ if (bus_width) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_BUS_WIDTH, ext_csd_bit);
+
+ if (err) {
+ printk(KERN_WARNING "%s: switch to highspeed failed\n",
+ mmc_hostname(card->host));
+ bus_width = 0;
+ }
+ }
+
+ /*
+ * Compute bus speed.
+ * and chose the fastest of slowest cards
+ */
+ if (mmc_card_highspeed(card)) {
+ if (max_dtr > card->ext_csd.hs_max_dtr)
+ max_dtr = card->ext_csd.hs_max_dtr;
+ } else if (max_dtr > card->csd.max_dtr) {
+ if (max_dtr > card->csd.max_dtr)
+ max_dtr = card->csd.max_dtr;
+ }
+ }
+
+ if (high_speed) {
+ mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
+ }
+ if (bus_width)
+ mmc_set_bus_width(card->host, bus_width);
+
+ if (max_dtr != (unsigned int)-1)
+ mmc_set_clock(host, max_dtr);
+
+ mmc_deselect_cards(host);
+ return 0;
+
+free_card:
+ while(!rescan && host->card_detected) {
+ if (host->cards[host->card_detected])
+ mmc_remove_card(host->cards[host->card_detected]);
+ --host->card_detected;
+ }
+err:
+ return err;
+}
+
+#else /* CONFIG_MULTI_MMC_ON_HOST */
+
/*
* Handle the detection and initialisation of a card.
*
@@ -336,7 +601,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
- err = mmc_send_cid(host, cid);
+ err = mmc_send_cid(host, 0, cid);
else
err = mmc_all_send_cid(host, cid);
if (err)
@@ -486,17 +751,27 @@ err:

return err;
}
-
+#endif
/*
* Host is being removed. Free up the current card.
*/
static void mmc_remove(struct mmc_host *host)
{
BUG_ON(!host);
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ while (host->card_detected--) {
+ BUG_ON(!host->cards[host->card_detected]);
+ mmc_remove_card(host->cards[host->card_detected]);
+ host->cards[host->card_detected] = NULL;
+ }
+
+
+#else
BUG_ON(!host->card);

mmc_remove_card(host->card);
host->card = NULL;
+#endif
}

/*
@@ -504,6 +779,8 @@ static void mmc_remove(struct mmc_host *host)
*/
static void mmc_detect(struct mmc_host *host)
{
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+#else
int err;

BUG_ON(!host);
@@ -525,6 +802,7 @@ static void mmc_detect(struct mmc_host *host)
mmc_detach_bus(host);
mmc_release_host(host);
}
+#endif
}

/*
@@ -532,7 +810,20 @@ static void mmc_detect(struct mmc_host *host)
*/
static int mmc_suspend(struct mmc_host *host)
{
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ int i;
BUG_ON(!host);
+ mmc_claim_host(host);
+ if (!mmc_host_is_spi(host))
+ mmc_deselect_cards(host);
+ for (i=0; i<host->card_detected;++i){
+ host->cards[i]->state &= ~MMC_STATE_HIGHSPEED;
+ }
+ host->card_detected = 0;
+ mmc_release_host(host);
+#else
+ BUG_ON(!host);
+
BUG_ON(!host->card);

mmc_claim_host(host);
@@ -540,7 +831,7 @@ static int mmc_suspend(struct mmc_host *host)
mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host);
-
+#endif
return 0;
}

@@ -554,26 +845,58 @@ static int mmc_resume(struct mmc_host *host)
{
int err;

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ BUG_ON(!host);
+ mmc_claim_host(host);
+ err = mmc_init_cards(host, host->ocr, 1);
+ mmc_release_host(host);
+#else
BUG_ON(!host);
BUG_ON(!host->card);

mmc_claim_host(host);
err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
+#endif

return err;
}

static void mmc_power_restore(struct mmc_host *host)
{
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ int i;
+ mmc_claim_host(host);
+ for (i=0; i<host->card_detected; ++i){
+ host->cards[i]->state &= ~MMC_STATE_HIGHSPEED;
+ }
+ host->card_detected = 0;
+ mmc_init_cards(host, host->ocr, 1);
+ mmc_release_host(host);
+#else
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
+#endif
}

static int mmc_sleep(struct mmc_host *host)
{
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ int i;
+ struct mmc_card *card;
+ int err = -ENOSYS;
+ for (i=0; i<host->card_detected;++i){
+ card = host->cards[i];
+ if (card && card->ext_csd.rev >= 3) {
+ err = mmc_card_sleepawake(card, 1);
+ if (err < 0)
+ pr_debug("%s: Error %d while putting card into sleep",
+ mmc_hostname(host), err);
+ }
+ }
+#else
struct mmc_card *card = host->card;
int err = -ENOSYS;

@@ -583,12 +906,28 @@ static int mmc_sleep(struct mmc_host *host)
pr_debug("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
+#endif

return err;
}

static int mmc_awake(struct mmc_host *host)
{
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ struct mmc_card *card;
+ int err = -ENOSYS;
+ int i;
+ for (i=0; i<host->card_detected;++i){
+ card = host->cards[i];
+
+ if (card && card->ext_csd.rev >= 3) {
+ err = mmc_card_sleepawake(card, 0);
+ if (err < 0)
+ pr_debug("%s: Error %d while awaking sleeping card",
+ mmc_hostname(host), err);
+ }
+ }
+#else
struct mmc_card *card = host->card;
int err = -ENOSYS;

@@ -598,6 +937,7 @@ static int mmc_awake(struct mmc_host *host)
pr_debug("%s: Error %d while awaking sleeping card",
mmc_hostname(host), err);
}
+#endif

return err;
}
@@ -639,7 +979,9 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
{
int err;
-
+#if defined(CONFIG_MULTI_MMC_ON_HOST)
+ int i;
+#endif
BUG_ON(!host);
WARN_ON(!host->claimed);

@@ -671,10 +1013,31 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
* Can we support the voltage of the card?
*/
if (!host->ocr) {
+ printk(KERN_ERR "%s: card claims to unsupported voltages\n",
+ mmc_hostname(host));
err = -EINVAL;
goto err;
}

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+ err = mmc_init_cards(host, host->ocr, 0);
+ if (err) {
+ goto remove_card;
+ }
+ mmc_release_host(host);
+
+ for (i=0; i < host->card_detected; ++i) {
+ err = mmc_add_card(host->cards[i]);
+ if (err)
+ goto remove_card;
+ }
+#ifdef CONFIG_MULTI_MMC_AS_ONE
+ err = device_add(&host->cards[0]->dev);
+ if (err)
+ goto remove_device;
+#endif
+
+#else /* CONFIG_MULTI_MMC_ON_HOST */
/*
* Detect and init the card.
*/
@@ -687,12 +1050,29 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
err = mmc_add_card(host->card);
if (err)
goto remove_card;
+#endif /* CONFIG_MULTI_MMC_ON_HOST */

return 0;
+
+#if defined(CONFIG_MULTI_MMC_AS_ONE)
+remove_device:
+ device_del(&host->cards[0]->dev);
+#endif

+#ifdef CONFIG_MULTI_MMC_ON_HOST
+remove_card:
+ while (host->card_detected) {
+ if (host->cards[host->card_detected])
+ mmc_remove_card(host->cards[host->card_detected]);
+ host->cards[host->card_detected] = NULL;
+ --host->card_detected;
+ }
+ host->card_detected = 0;
+#else
remove_card:
mmc_remove_card(host->card);
host->card = NULL;
+#endif
mmc_claim_host(host);
err:
mmc_detach_bus(host);
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index d2cb5c6..b894481 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -29,7 +29,6 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
memset(&cmd, 0, sizeof(struct mmc_command));

cmd.opcode = MMC_SELECT_CARD;
-
if (card) {
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
@@ -51,20 +50,21 @@ int mmc_select_card(struct mmc_card *card)

return _mmc_select_card(card->host, card);
}
+EXPORT_SYMBOL(mmc_select_card);

int mmc_deselect_cards(struct mmc_host *host)
{
return _mmc_select_card(host, NULL);
}
+EXPORT_SYMBOL(mmc_deselect_cards);

-int mmc_card_sleepawake(struct mmc_host *host, int sleep)
+int mmc_card_sleepawake(struct mmc_card *card, int sleep)
{
struct mmc_command cmd;
- struct mmc_card *card = host->card;
int err;

if (sleep)
- mmc_deselect_cards(host);
+ mmc_deselect_cards(card->host);

memset(&cmd, 0, sizeof(struct mmc_command));

@@ -74,7 +74,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep)
cmd.arg |= 1 << 15;

cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
- err = mmc_wait_for_cmd(host, &cmd, 0);
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
return err;

@@ -84,7 +84,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep)
* SEND_STATUS command to poll the status because that command (and most
* others) is invalid while the card sleeps.
*/
- if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+ if (!(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));

if (!sleep)
@@ -325,14 +325,15 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd)
return 0;
}

-int mmc_send_cid(struct mmc_host *host, u32 *cid)
+int mmc_send_cid(struct mmc_host *host, unsigned int rca, u32 *cid)
{
int ret, i;

+ if (!host)
+ return -EINVAL;
+
if (!mmc_host_is_spi(host)) {
- if (!host->card)
- return -EINVAL;
- return mmc_send_cxd_native(host, host->card->rca << 16,
+ return mmc_send_cxd_native(host, rca << 16,
cid, MMC_SEND_CID);
}

diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 653eb8e..d0aee59 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -22,10 +22,10 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status);
-int mmc_send_cid(struct mmc_host *host, u32 *cid);
+int mmc_send_cid(struct mmc_host *host, unsigned int rca, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
-int mmc_card_sleepawake(struct mmc_host *host, int sleep);
+int mmc_card_sleepawake(struct mmc_card *card, int sleep);

#endif

diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index fdd414e..0889de2 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -366,7 +366,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Fetch CID from card.
*/
if (mmc_host_is_spi(host))
- err = mmc_send_cid(host, cid);
+ err = mmc_send_cid(host, 0, cid);
else
err = mmc_all_send_cid(host, cid);
if (err)
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 83f0aff..e3ad4cf 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -38,6 +38,12 @@
#include <plat/mmc.h>
#include <plat/cpu.h>

+#ifdef CONFIG_MMC_DEBUG
+#define mmc_dbg(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
+#else
+#define mmc_dbg(fmt, arg...)
+#endif
+
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSCONFIG 0x0010
#define OMAP_HSMMC_SYSSTATUS 0x0014
@@ -56,6 +62,7 @@
#define OMAP_HSMMC_IE 0x0134
#define OMAP_HSMMC_ISE 0x0138
#define OMAP_HSMMC_CAPA 0x0140
+#define OMAP_HSMMC_CUR_CAPA 0x0148

#define VS18 (1 << 26)
#define VS30 (1 << 25)
@@ -625,13 +632,13 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
dsor++;

- if (dsor > 250)
- dsor = 250;
+ if (dsor > 1023) /* Maximum of ratio 0x3FF */
+ dsor = 1023;
}

OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
- OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << 16));
+ OMAP_HSMMC_WRITE(host->base, SYSCTL, (dsor << 6) | (DTO << DTO_SHIFT));
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);

@@ -816,6 +823,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,

OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
OMAP_HSMMC_WRITE(host->base, CMD, cmdreg);
+
}

static int
@@ -867,6 +875,52 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
omap_hsmmc_start_command(host, data->stop, NULL);
}

+#ifdef CONFIG_MMC_DEBUG
+static const char *mmc_report_card_status(u32 status)
+{
+ /* It's the status come from the omap mmc controler */
+ /* --- means reserved bit without definition at documentation */
+ static const char *mmc_status_bits[] = {
+ "---", "---", "---", "---", "---", "APP_CMD", "---", "SW_ERROR",
+ "READY_4_DATA",
+ "---", "---", "---", "---", /* mmc_report_card_state */
+
+ "ERASE_RST", "---", "WP_ERASE_SKIP", "CID/CSD_OVR", "---","---",
+ "ERR", "CC_ERR", "CARD_ECC_FAILED", "ILLEGAL_CMD", "COM_CRC_ERR",
+ "(UN)LOCK_FAILED", "CARD_LOCKED", "WP_VIOLATION",
+ "ERASE_PARAM", "ERASE_SEQ_ERR", "BLK_LEN_ERR", "ADDR_MISCALIGN",
+ "ADDR_OUT_OF_RANGE",
+ };
+ static const char *mmc_state_bits[] = {
+ "S_IDLE", "S_READY","S_IDENT","S_STBY",
+ "S_TRAN","S_DATA","S_RCV","S_PRG","S_DIS",
+ "S_BTST","S_SLP","---", "---", "---", "---","---"
+ };
+
+ static char res[256];
+ char *buf = res;
+ int len, i;
+
+ len = sprintf(buf, "CARD status 0x%x :", status);
+ buf += len;
+
+ for (i = 0; i < ARRAY_SIZE(mmc_status_bits); i++) {
+ if (i>=9 && i<=12){
+ if (i==9){
+ len = sprintf(buf, " %s |", mmc_state_bits[status&0x0F]);
+ buf += len;
+ }
+ }
+ else {
+ if (status & (1 << i)) {
+ len = sprintf(buf, " %s |", mmc_status_bits[i]);
+ buf += len;
+ }
+ }
+ }
+ return res;
+}
+#endif /* CONFIG_MMC_DEBUG */
/*
* Notify the core about command completion
*/
@@ -882,9 +936,18 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
cmd->resp[2] = OMAP_HSMMC_READ(host->base, RSP32);
cmd->resp[1] = OMAP_HSMMC_READ(host->base, RSP54);
cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP76);
+ mmc_dbg("%s: "
+ "RSP10=0x%08x RSP32=0x%08x RSP54=0x%08x RSP76=0x%08x\n",
+ mmc_hostname(host->mmc),
+ cmd->resp[3],cmd->resp[2],cmd->resp[1],cmd->resp[0]);
+
} else {
/* response types 1, 1b, 3, 4, 5, 6 */
cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10);
+ mmc_dbg("%s: RSP10=0x%08x %s\n",
+ mmc_hostname(host->mmc),
+ cmd->resp[0],
+ mmc_report_card_status(cmd->resp[0]));
}
}
if ((host->data == NULL && !host->response_busy) || cmd->error) {
@@ -914,6 +977,7 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
* Readable error output
*/
#ifdef CONFIG_MMC_DEBUG
+
static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)
{
/* --- means reserved bit without definition at documentation */
@@ -938,6 +1002,7 @@ static void omap_hsmmc_report_irq(struct omap_hsmmc_host *host, u32 status)

dev_dbg(mmc_dev(host->mmc), "%s\n", res);
}
+
#endif /* CONFIG_MMC_DEBUG */

/*
@@ -1539,13 +1604,13 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (OMAP_MMC_MASTER_CLOCK / dsor > ios->clock)
dsor++;

- if (dsor > 250)
- dsor = 250;
+ if (dsor > 1023) /* Maximum of ratio 0x3FF */
+ dsor = 1023;
}
omap_hsmmc_stop_clock(host);
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
regval = regval & ~(CLKD_MASK);
- regval = regval | (dsor << 6) | (DTO << 16);
+ regval = regval | (dsor << 6) | (DTO << DTO_SHIFT);
OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
@@ -1921,7 +1986,8 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
OMAP_HSMMC_READ(host->base, ISE));
seq_printf(s, "CAPA:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CAPA));
-
+ seq_printf(s, "CUR_CAPA:\t\t0x%08x\n",
+ OMAP_HSMMC_READ(host->base, CUR_CAPA));
clk_disable(host->fclk);

return 0;
@@ -2132,13 +2198,13 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
ret = request_irq(host->irq, omap_hsmmc_irq, IRQF_DISABLED,
mmc_hostname(mmc), host);
if (ret) {
- dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
+ dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
goto err_irq;
}

if (pdata->init != NULL) {
if (pdata->init(&pdev->dev) != 0) {
- dev_dbg(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"Unable to configure MMC IRQs\n");
goto err_irq_cd_init;
}
@@ -2161,7 +2227,7 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
| IRQF_DISABLED,
mmc_hostname(mmc), host);
if (ret) {
- dev_dbg(mmc_dev(host->mmc),
+ dev_err(mmc_dev(host->mmc),
"Unable to grab MMC CD IRQ\n");
goto err_irq_cd;
}
diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c
index 9ad2295..7996bcc 100644
--- a/arch/arm/mach-omap2/hsmmc.c
+++ b/arch/arm/mach-omap2/hsmmc.c
@@ -5,6 +5,11 @@
* Copyright (C) 2008 Nokia Corporation
* Author: Texas Instruments
*
+ *
+ * 20100315
+ * Laurent Epinat <laurent.epinat@cioinfoindus.fr>
+ * Add cpu_is_omap34xx() for pbias1 (VMMCa1)
+ *
* 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.
@@ -83,14 +88,23 @@ static void hsmmc1_before_set_reg(struct device *dev, int slot,
prog_io = omap_ctrl_readl(OMAP343X_CONTROL_PROG_IO1);
prog_io |= OMAP3630_PRG_SDMMC1_SPEEDCTRL;
omap_ctrl_writel(prog_io, OMAP343X_CONTROL_PROG_IO1);
- } else {
+ }
+ else if (cpu_is_omap34xx()) {
+ reg &= ~(OMAP343X_PBIASSPEEDCTRL1|OMAP2_PBIASSPEEDCTRL0);
+ }
+ else {
reg |= OMAP2_PBIASSPEEDCTRL0;
}
reg &= ~OMAP2_PBIASLITEPWRDNZ0;
+ if (cpu_is_omap34xx())
+ reg &= ~OMAP343X_PBIASLITEPWRDNZ1;
omap_ctrl_writel(reg, control_pbias_offset);
} else {
reg = omap_ctrl_readl(control_pbias_offset);
reg &= ~OMAP2_PBIASLITEPWRDNZ0;
+ if (cpu_is_omap34xx())
+ reg &= ~OMAP343X_PBIASLITEPWRDNZ1;
+
omap_ctrl_writel(reg, control_pbias_offset);
}
}
@@ -106,15 +120,29 @@ static void hsmmc1_after_set_reg(struct device *dev, int slot,
if (power_on) {
reg = omap_ctrl_readl(control_pbias_offset);
reg |= (OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0);
- if ((1 << vdd) <= MMC_VDD_165_195)
+ if (cpu_is_omap34xx()) {
+ reg |= (OMAP343X_PBIASLITEPWRDNZ1 | OMAP343X_PBIASSPEEDCTRL1);
+ }
+ if ((1 << vdd) <= MMC_VDD_165_195) {
reg &= ~OMAP2_PBIASLITEVMODE0;
- else
+ if (cpu_is_omap34xx()) {
+ reg &= ~OMAP343X_PBIASLITEVMODE1;
+ }
+ }
+ else {
reg |= OMAP2_PBIASLITEVMODE0;
+ if (cpu_is_omap34xx()) {
+ reg |= OMAP343X_PBIASLITEVMODE1;
+ }
+ }
omap_ctrl_writel(reg, control_pbias_offset);
} else {
reg = omap_ctrl_readl(control_pbias_offset);
reg |= (OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 |
OMAP2_PBIASLITEVMODE0);
+ if (cpu_is_omap34xx())
+ reg |= (OMAP343X_PBIASSPEEDCTRL1 | OMAP343X_PBIASLITEPWRDNZ1 |
+ OMAP343X_PBIASLITEVMODE1);
omap_ctrl_writel(reg, control_pbias_offset);
}
}
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index eaf3636..b925265 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -182,7 +182,12 @@ struct mmc_host {
unsigned int disable_delay; /* disable delay in msecs */
struct delayed_work disable; /* disabling work */

- struct mmc_card *card; /* device attached to this host */
+#ifdef CONFIG_MULTI_MMC_ON_HOST
+#define MMC_MAX_CARD 64 /* spec ~40 in same host ? */
+ struct mmc_card *cards[MMC_MAX_CARD];/* devices attached to this host */
+ int card_detected;
+#endif
+ struct mmc_card *card; /* Keep for compatibility SD, SDIO, ...*/

wait_queue_head_t wq;
struct task_struct *claimer; /* task that has host claimed */
---

Le 24.03.2010 16:35, Madhusudhan a écrit :
>
>
>> -----Original Message-----
>> From: Nishanth Menon [mailto:nm@ti.com]
>> Sent: Tuesday, March 23, 2010 5:49 PM
>> To: Tony Lindgren
>> Cc: Pais, Allen; linux-arm-kernel@lists.infradead.org; linux-
>> omap@vger.kernel.org; Pandita, Vikram; Madhusudhan Chikkature Rajashekar
>> Subject: mmc errors (was Re: [PATCH] arm: Fix DEBUG_LL for omap zoom2/3)
>>
>> Tony Lindgren had written, on 03/23/2010 10:27 AM, the following:
>> [...]
>>> Then at least one issue remains for zoom3 to be usable..
>>> I'm getting tons of MMC errors trying to mount root on it:
>>>
>>> mmcblk1: error -110 transferring data, sector 2097024, nr 8, card status
>> 0x900
>>> end_request: I/O error, dev mmcblk1, sector 2097024
>>> ...
>>>
>> I have seen this on few platforms and seem to be related to eMMC usage.
>>
>>> Any ideas if that's fixed somewhere also?
>> Madhu, any comments?
>>
> Sorry about multiple replies to this mail. Just wanted to avoid top posting.
>
> The eMMC on Zoom2 has an issue with respect to detection as a high
> capacity card. This was discussed on the list with Pierre sometime back. I
> don't have an acceptable solution to make this device work. Rather I
> prefer to submit a patch which disables MMC2 on these boards.
>
> You can still use MMC1 to mount your file system. This is the card you can
> plug in to the cage.
>
> Regards,
> Madhu
>
>> --
>> Regards,
>> Nishanth Menon
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
>

--

Salutations
Laurent Epinat -> mailto:laurent.epinat@cioinfoindus.fr

CIO Informatique
Le millenium
1, rue de Presse - BP 710
42950 Saint-Etienne Cedex 9

Tel 33 (0) 477 93 34 32
Tcopie 33 (0) 477 79 75 55
WWW : http://www.cioinfoindus.fr/