From patchwork Wed Mar 24 15:52:53 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Epinat X-Patchwork-Id: 87944 X-Patchwork-Delegate: tony@atomide.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o2OGE5vD032077 for ; Wed, 24 Mar 2010 16:14:05 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932209Ab0CXQNr (ORCPT ); Wed, 24 Mar 2010 12:13:47 -0400 Received: from smtp-out03.msg.oleane.net ([62.161.7.1]:60706 "EHLO smtp-out03.msg.oleane.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932206Ab0CXQNn (ORCPT ); Wed, 24 Mar 2010 12:13:43 -0400 Received: from smtp01.msg.oleane.net (smtp01.mail.priv [172.17.20.110]) by smtp-out03.msg.oleane.net with ESMTP id o2OGDbk7031623 for ; Wed, 24 Mar 2010 17:13:38 +0100 Received: from [192.168.1.108] ([195.6.193.205]) by smtp01.msg.oleane.net (MTA) with ESMTP id o2OFqm3L004002; Wed, 24 Mar 2010 16:52:48 +0100 X-Oleane-Rep: REPA Message-ID: <4BAA3555.80403@cioinfoindus.fr> Date: Wed, 24 Mar 2010 16:52:53 +0100 From: Laurent Epinat User-Agent: Mozilla/5.0 (X11; U; Linux i686; fr; rv:1.9.1.8) Gecko/20100227 Thunderbird/3.0.3 MIME-Version: 1.0 To: Madhusudhan CC: "'Nishanth Menon'" , "'Tony Lindgren'" , "'Pais, Allen'" , linux-arm-kernel@lists.infradead.org, linux-omap@vger.kernel.org, "'Pandita, Vikram'" Subject: Re: mmc errors (was Re: [PATCH] arm: Fix DEBUG_LL for omap zoom2/3) References: <20100322212025.GW11448@atomide.com> <20100323152709.GD11448@atomide.com> <4BA94564.8030803@ti.com> <000201cacb67$9849e4d0$544ff780@am.dhcp.ti.com> In-Reply-To: <000201cacb67$9849e4d0$544ff780@am.dhcp.ti.com> X-Spam-Flag: NO X-PMX-Spam: Probability=8% X-PFSI-Info: PMX 5.5.5.374460, Antispam-Engine: 2.7.1.369594, Antispam-Data: 2010.3.24.153625 (no antivirus check) Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Wed, 24 Mar 2010 16:14:05 +0000 (UTC) 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 + * Add multi "card" on one controller + * seen as multi card or as one big card + * */ #include #include @@ -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;icard_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; icard_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; icard_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 + * 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; icard_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; icard_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; icard_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; icard_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 #include +#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 + * 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 */