提交 6abd2c86 编写于 作者: L Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc: (67 commits)
  mmc: don't use weight32()
  pxamci: support arbitrary block size
  sdio: make the IRQ thread more resilient in the presence of bad states
  sdio: fix IRQ diagnostic message
  sdhci: remove old dma module params
  sdhci: add SDHCI_QUIRK_BROKEN_DMA quirk
  sdhci: remove DMA capability check from controller's PCI Class reg
  sdhci: fix a typo
  mmc: Disabler for Ricoh MMC controller
  sdio: adaptive interrupt polling
  mmc: pxamci: add SDIO card interrupt reporting capability
  mmc: pxamci: set proper buswidth capabilities according to PXA flavor
  mmc: pxamci: set proper block capabilities according to PXA flavor
  mmc: pxamci: better pending IRQ determination
  arm: i.MX/MX1 SDHC implements SD cards read-only switch read-back
  mmc: add led trigger
  mmc_spi host driver
  MMC core learns about SPI
  MMC/SD card driver learns SPI
  MMC headers learn about SPI
  ...
...@@ -2561,12 +2561,18 @@ L: linux-kernel@vger.kernel.org ...@@ -2561,12 +2561,18 @@ L: linux-kernel@vger.kernel.org
W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
S: Maintained S: Maintained
MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
P: Pierre Ossman P: Pierre Ossman
M: drzeus-mmc@drzeus.cx M: drzeus-mmc@drzeus.cx
L: linux-kernel@vger.kernel.org L: linux-kernel@vger.kernel.org
S: Maintained S: Maintained
MULTIMEDIA CARD (MMC) ETC. OVER SPI
P: David Brownell
M: dbrownell@users.sourceforge.net
L: linux-kernel@vger.kernel.org
S: Odd fixes
MULTISOUND SOUND DRIVER MULTISOUND SOUND DRIVER
P: Andrew Veliath P: Andrew Veliath
M: andrewtv@usa.net M: andrewtv@usa.net
......
...@@ -116,7 +116,7 @@ static struct platform_device *devices[] __initdata = { ...@@ -116,7 +116,7 @@ static struct platform_device *devices[] __initdata = {
}; };
#ifdef CONFIG_MMC_IMX #ifdef CONFIG_MMC_IMX
static int mx1ads_mmc_card_present(void) static int mx1ads_mmc_card_present(struct device *dev)
{ {
/* MMC/SD Card Detect is PB 20 on MX1ADS V1.0.7 */ /* MMC/SD Card Detect is PB 20 on MX1ADS V1.0.7 */
return (SSR(1) & (1 << 20) ? 0 : 1); return (SSR(1) & (1 << 20) ? 0 : 1);
......
...@@ -32,3 +32,10 @@ config MMC_BLOCK_BOUNCE ...@@ -32,3 +32,10 @@ config MMC_BLOCK_BOUNCE
If unsure, say Y here. If unsure, say Y here.
config SDIO_UART
tristate "SDIO UART/GPS class support"
depends on MMC
help
SDIO function driver for SDIO cards that implements the UART
class, as well as the GPS class which appears like a UART.
...@@ -9,3 +9,5 @@ endif ...@@ -9,3 +9,5 @@ endif
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
mmc_block-objs := block.o queue.o mmc_block-objs := block.o queue.o
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
...@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) ...@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
cmd.opcode = MMC_APP_CMD; cmd.opcode = MMC_APP_CMD;
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0); err = mmc_wait_for_cmd(card->host, &cmd, 0);
if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) if (err)
return (u32)-1;
if (!mmc_host_is_spi(card->host) && !(cmd.resp[0] & R1_APP_CMD))
return (u32)-1; return (u32)-1;
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
memset(&data, 0, sizeof(struct mmc_data)); memset(&data, 0, sizeof(struct mmc_data));
...@@ -192,7 +194,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) ...@@ -192,7 +194,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
mmc_wait_for_req(card->host, &mrq); mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) if (cmd.error || data.error)
return (u32)-1; return (u32)-1;
blocks = ntohl(blocks); blocks = ntohl(blocks);
...@@ -220,17 +222,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -220,17 +222,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.cmd.arg = req->sector; brq.cmd.arg = req->sector;
if (!mmc_card_blockaddr(card)) if (!mmc_card_blockaddr(card))
brq.cmd.arg <<= 9; brq.cmd.arg <<= 9;
brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; brq.cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
brq.data.blksz = 1 << md->block_bits; brq.data.blksz = 1 << md->block_bits;
brq.stop.opcode = MMC_STOP_TRANSMISSION; brq.stop.opcode = MMC_STOP_TRANSMISSION;
brq.stop.arg = 0; brq.stop.arg = 0;
brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; brq.stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
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; brq.data.blocks = card->host->max_blk_count;
mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
/* /*
* If the host doesn't support multiple block writes, force * If the host doesn't support multiple block writes, force
* block writes to single block. SD cards are excepted from * block writes to single block. SD cards are excepted from
...@@ -243,7 +243,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -243,7 +243,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.data.blocks = 1; brq.data.blocks = 1;
if (brq.data.blocks > 1) { if (brq.data.blocks > 1) {
brq.data.flags |= MMC_DATA_MULTI; /* SPI multiblock writes terminate using a special
* token, not a STOP_TRANSMISSION request.
*/
if (!mmc_host_is_spi(card->host)
|| rq_data_dir(req) == READ)
brq.mrq.stop = &brq.stop; brq.mrq.stop = &brq.stop;
readcmd = MMC_READ_MULTIPLE_BLOCK; readcmd = MMC_READ_MULTIPLE_BLOCK;
writecmd = MMC_WRITE_MULTIPLE_BLOCK; writecmd = MMC_WRITE_MULTIPLE_BLOCK;
...@@ -261,6 +265,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -261,6 +265,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.data.flags |= MMC_DATA_WRITE; brq.data.flags |= MMC_DATA_WRITE;
} }
mmc_set_data_timeout(&brq.data, card);
brq.data.sg = mq->sg; brq.data.sg = mq->sg;
brq.data.sg_len = mmc_queue_map_sg(mq); brq.data.sg_len = mmc_queue_map_sg(mq);
...@@ -302,7 +308,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) ...@@ -302,7 +308,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
goto cmd_err; goto cmd_err;
} }
if (rq_data_dir(req) != READ) { if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
do { do {
int err; int err;
...@@ -510,7 +516,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) ...@@ -510,7 +516,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
mmc_claim_host(card->host); mmc_claim_host(card->host);
cmd.opcode = MMC_SET_BLOCKLEN; cmd.opcode = MMC_SET_BLOCKLEN;
cmd.arg = 1 << md->block_bits; cmd.arg = 1 << md->block_bits;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 5); err = mmc_wait_for_cmd(card->host, &cmd, 5);
mmc_release_host(card->host); mmc_release_host(card->host);
......
此差异已折叠。
...@@ -8,5 +8,7 @@ endif ...@@ -8,5 +8,7 @@ endif
obj-$(CONFIG_MMC) += mmc_core.o obj-$(CONFIG_MMC) += mmc_core.o
mmc_core-y := core.o sysfs.o bus.o host.o \ mmc_core-y := core.o sysfs.o bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
sdio_cis.o sdio_io.o sdio_irq.o
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "sysfs.h" #include "sysfs.h"
#include "core.h" #include "core.h"
#include "sdio_cis.h"
#include "bus.h" #include "bus.h"
#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
...@@ -34,6 +35,8 @@ static ssize_t mmc_type_show(struct device *dev, ...@@ -34,6 +35,8 @@ static ssize_t mmc_type_show(struct device *dev,
return sprintf(buf, "MMC\n"); return sprintf(buf, "MMC\n");
case MMC_TYPE_SD: case MMC_TYPE_SD:
return sprintf(buf, "SD\n"); return sprintf(buf, "SD\n");
case MMC_TYPE_SDIO:
return sprintf(buf, "SDIO\n");
default: default:
return -EFAULT; return -EFAULT;
} }
...@@ -59,28 +62,34 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, ...@@ -59,28 +62,34 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
int buf_size) int buf_size)
{ {
struct mmc_card *card = dev_to_mmc_card(dev); struct mmc_card *card = dev_to_mmc_card(dev);
int retval = 0, i = 0, length = 0; const char *type;
int i = 0, length = 0;
#define add_env(fmt,val) do { \
retval = add_uevent_var(envp, num_envp, &i, \
buf, buf_size, &length, \
fmt, val); \
if (retval) \
return retval; \
} while (0);
switch (card->type) { switch (card->type) {
case MMC_TYPE_MMC: case MMC_TYPE_MMC:
add_env("MMC_TYPE=%s", "MMC"); type = "MMC";
break; break;
case MMC_TYPE_SD: case MMC_TYPE_SD:
add_env("MMC_TYPE=%s", "SD"); type = "SD";
break; break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
default:
type = NULL;
} }
add_env("MMC_NAME=%s", mmc_card_name(card)); if (type) {
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"MMC_TYPE=%s", type))
return -ENOMEM;
}
#undef add_env if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"MMC_NAME=%s", mmc_card_name(card)))
return -ENOMEM;
envp[i] = NULL; envp[i] = NULL;
...@@ -176,6 +185,11 @@ static void mmc_release_card(struct device *dev) ...@@ -176,6 +185,11 @@ static void mmc_release_card(struct device *dev)
{ {
struct mmc_card *card = dev_to_mmc_card(dev); struct mmc_card *card = dev_to_mmc_card(dev);
sdio_free_common_cis(card);
if (card->info)
kfree(card->info);
kfree(card); kfree(card);
} }
...@@ -221,15 +235,25 @@ int mmc_add_card(struct mmc_card *card) ...@@ -221,15 +235,25 @@ int mmc_add_card(struct mmc_card *card)
if (mmc_card_blockaddr(card)) if (mmc_card_blockaddr(card))
type = "SDHC"; type = "SDHC";
break; break;
case MMC_TYPE_SDIO:
type = "SDIO";
break;
default: default:
type = "?"; type = "?";
break; break;
} }
if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: new %s%s card on SPI\n",
mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "",
type);
} else {
printk(KERN_INFO "%s: new %s%s card at address %04x\n", printk(KERN_INFO "%s: new %s%s card at address %04x\n",
mmc_hostname(card->host), mmc_hostname(card->host),
mmc_card_highspeed(card) ? "high speed " : "", mmc_card_highspeed(card) ? "high speed " : "",
type, card->rca); type, card->rca);
}
card->dev.uevent_suppress = 1; card->dev.uevent_suppress = 1;
...@@ -261,8 +285,13 @@ int mmc_add_card(struct mmc_card *card) ...@@ -261,8 +285,13 @@ int mmc_add_card(struct mmc_card *card)
void mmc_remove_card(struct mmc_card *card) void mmc_remove_card(struct mmc_card *card)
{ {
if (mmc_card_present(card)) { if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
printk(KERN_INFO "%s: SPI card removed\n",
mmc_hostname(card->host));
} else {
printk(KERN_INFO "%s: card %04x removed\n", printk(KERN_INFO "%s: card %04x removed\n",
mmc_hostname(card->host), card->rca); mmc_hostname(card->host), card->rca);
}
if (card->host->bus_ops->sysfs_remove) if (card->host->bus_ops->sysfs_remove)
card->host->bus_ops->sysfs_remove(card->host, card); card->host->bus_ops->sysfs_remove(card->host, card);
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/leds.h>
#include <asm/scatterlist.h> #include <asm/scatterlist.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
...@@ -29,15 +30,26 @@ ...@@ -29,15 +30,26 @@
#include "core.h" #include "core.h"
#include "bus.h" #include "bus.h"
#include "host.h" #include "host.h"
#include "sdio_bus.h"
#include "mmc_ops.h" #include "mmc_ops.h"
#include "sd_ops.h" #include "sd_ops.h"
#include "sdio_ops.h"
extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); extern int mmc_attach_sd(struct mmc_host *host, u32 ocr);
extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
static struct workqueue_struct *workqueue; static struct workqueue_struct *workqueue;
/*
* Enabling software CRCs on the data blocks can be a significant (30%)
* performance cost, and for other reasons may not always be desired.
* So we allow it it to be disabled.
*/
int use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
/* /*
* Internal function. Schedule delayed work in the MMC work queue. * Internal function. Schedule delayed work in the MMC work queue.
*/ */
...@@ -68,6 +80,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) ...@@ -68,6 +80,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd; struct mmc_command *cmd = mrq->cmd;
int err = cmd->error; int err = cmd->error;
if (err && cmd->retries && mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
cmd->retries = 0;
}
if (err && cmd->retries) { if (err && cmd->retries) {
pr_debug("%s: req failed (CMD%u): %d, retrying...\n", pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
mmc_hostname(host), cmd->opcode, err); mmc_hostname(host), cmd->opcode, err);
...@@ -76,6 +93,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) ...@@ -76,6 +93,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
cmd->error = 0; cmd->error = 0;
host->ops->request(host, mrq); host->ops->request(host, mrq);
} else { } else {
led_trigger_event(host->led, LED_OFF);
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n", pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
mmc_hostname(host), cmd->opcode, err, mmc_hostname(host), cmd->opcode, err,
cmd->resp[0], cmd->resp[1], cmd->resp[0], cmd->resp[1],
...@@ -118,7 +137,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -118,7 +137,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
"tsac %d ms nsac %d\n", "tsac %d ms nsac %d\n",
mmc_hostname(host), mrq->data->blksz, mmc_hostname(host), mrq->data->blksz,
mrq->data->blocks, mrq->data->flags, mrq->data->blocks, mrq->data->flags,
mrq->data->timeout_ns / 10000000, mrq->data->timeout_ns / 1000000,
mrq->data->timeout_clks); mrq->data->timeout_clks);
} }
...@@ -130,6 +149,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) ...@@ -130,6 +149,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
led_trigger_event(host->led, LED_FULL);
mrq->cmd->error = 0; mrq->cmd->error = 0;
mrq->cmd->mrq = mrq; mrq->cmd->mrq = mrq;
if (mrq->data) { if (mrq->data) {
...@@ -199,7 +220,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries ...@@ -199,7 +220,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
{ {
struct mmc_request mrq; struct mmc_request mrq;
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
...@@ -220,16 +241,23 @@ EXPORT_SYMBOL(mmc_wait_for_cmd); ...@@ -220,16 +241,23 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);
* mmc_set_data_timeout - set the timeout for a data command * mmc_set_data_timeout - set the timeout for a data command
* @data: data phase for command * @data: data phase for command
* @card: the MMC card associated with the data transfer * @card: the MMC card associated with the data transfer
* @write: flag to differentiate reads from writes
* *
* Computes the data timeout parameters according to the * Computes the data timeout parameters according to the
* correct algorithm given the card type. * correct algorithm given the card type.
*/ */
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
int write)
{ {
unsigned int mult; unsigned int mult;
/*
* SDIO cards only define an upper 1 s limit on access.
*/
if (mmc_card_sdio(card)) {
data->timeout_ns = 1000000000;
data->timeout_clks = 0;
return;
}
/* /*
* SD cards use a 100 multiplier rather than 10 * SD cards use a 100 multiplier rather than 10
*/ */
...@@ -239,7 +267,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, ...@@ -239,7 +267,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
* Scale up the multiplier (and therefore the timeout) by * Scale up the multiplier (and therefore the timeout) by
* the r2w factor for writes. * the r2w factor for writes.
*/ */
if (write) if (data->flags & MMC_DATA_WRITE)
mult <<= card->csd.r2w_factor; mult <<= card->csd.r2w_factor;
data->timeout_ns = card->csd.tacc_ns * mult; data->timeout_ns = card->csd.tacc_ns * mult;
...@@ -255,7 +283,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, ...@@ -255,7 +283,7 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
timeout_us += data->timeout_clks * 1000 / timeout_us += data->timeout_clks * 1000 /
(card->host->ios.clock / 1000); (card->host->ios.clock / 1000);
if (write) if (data->flags & MMC_DATA_WRITE)
limit_us = 250000; limit_us = 250000;
else else
limit_us = 100000; limit_us = 100000;
...@@ -272,15 +300,20 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, ...@@ -272,15 +300,20 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
EXPORT_SYMBOL(mmc_set_data_timeout); EXPORT_SYMBOL(mmc_set_data_timeout);
/** /**
* mmc_claim_host - exclusively claim a host * __mmc_claim_host - exclusively claim a host
* @host: mmc host to claim * @host: mmc host to claim
* @abort: whether or not the operation should be aborted
* *
* Claim a host for a set of operations. * Claim a host for a set of operations. If @abort is non null and
* dereference a non-zero value then this will return prematurely with
* that non-zero value without acquiring the lock. Returns zero
* with the lock held otherwise.
*/ */
void mmc_claim_host(struct mmc_host *host) int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
unsigned long flags; unsigned long flags;
int stop;
might_sleep(); might_sleep();
...@@ -288,19 +321,24 @@ void mmc_claim_host(struct mmc_host *host) ...@@ -288,19 +321,24 @@ void mmc_claim_host(struct mmc_host *host)
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
while (1) { while (1) {
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
if (!host->claimed) stop = abort ? atomic_read(abort) : 0;
if (stop || !host->claimed)
break; break;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
schedule(); schedule();
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
} }
set_current_state(TASK_RUNNING); set_current_state(TASK_RUNNING);
if (!stop)
host->claimed = 1; host->claimed = 1;
else
wake_up(&host->wq);
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
remove_wait_queue(&host->wq, &wait); remove_wait_queue(&host->wq, &wait);
return stop;
} }
EXPORT_SYMBOL(mmc_claim_host); EXPORT_SYMBOL(__mmc_claim_host);
/** /**
* mmc_release_host - release a host * mmc_release_host - release a host
...@@ -313,7 +351,7 @@ void mmc_release_host(struct mmc_host *host) ...@@ -313,7 +351,7 @@ void mmc_release_host(struct mmc_host *host)
{ {
unsigned long flags; unsigned long flags;
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
host->claimed = 0; host->claimed = 0;
...@@ -433,19 +471,32 @@ static void mmc_power_up(struct mmc_host *host) ...@@ -433,19 +471,32 @@ static void mmc_power_up(struct mmc_host *host)
int bit = fls(host->ocr_avail) - 1; int bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit; host->ios.vdd = bit;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; if (mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_HIGH;
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
} else {
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.chip_select = MMC_CS_DONTCARE;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
}
host->ios.power_mode = MMC_POWER_UP; host->ios.power_mode = MMC_POWER_UP;
host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
mmc_set_ios(host); mmc_set_ios(host);
mmc_delay(1); /*
* This delay should be sufficient to allow the power supply
* to reach the minimum voltage.
*/
mmc_delay(2);
host->ios.clock = host->f_min; host->ios.clock = host->f_min;
host->ios.power_mode = MMC_POWER_ON; host->ios.power_mode = MMC_POWER_ON;
mmc_set_ios(host); mmc_set_ios(host);
/*
* This delay must be at least 74 clock sizes, or 1 ms, or the
* time required to reach a stable voltage.
*/
mmc_delay(2); mmc_delay(2);
} }
...@@ -453,8 +504,10 @@ static void mmc_power_off(struct mmc_host *host) ...@@ -453,8 +504,10 @@ static void mmc_power_off(struct mmc_host *host)
{ {
host->ios.clock = 0; host->ios.clock = 0;
host->ios.vdd = 0; host->ios.vdd = 0;
if (!mmc_host_is_spi(host)) {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.chip_select = MMC_CS_DONTCARE;
}
host->ios.power_mode = MMC_POWER_OFF; host->ios.power_mode = MMC_POWER_OFF;
host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.bus_width = MMC_BUS_WIDTH_1;
host->ios.timing = MMC_TIMING_LEGACY; host->ios.timing = MMC_TIMING_LEGACY;
...@@ -511,7 +564,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) ...@@ -511,7 +564,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!ops); BUG_ON(!ops);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
...@@ -535,8 +588,8 @@ void mmc_detach_bus(struct mmc_host *host) ...@@ -535,8 +588,8 @@ void mmc_detach_bus(struct mmc_host *host)
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
BUG_ON(!host->bus_ops); WARN_ON(!host->bus_ops);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
...@@ -564,7 +617,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay) ...@@ -564,7 +617,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
#ifdef CONFIG_MMC_DEBUG #ifdef CONFIG_MMC_DEBUG
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
BUG_ON(host->removed); WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
#endif #endif
...@@ -597,24 +650,38 @@ void mmc_rescan(struct work_struct *work) ...@@ -597,24 +650,38 @@ void mmc_rescan(struct work_struct *work)
mmc_send_if_cond(host, host->ocr_avail); mmc_send_if_cond(host, host->ocr_avail);
/*
* First we search for SDIO...
*/
err = mmc_send_io_op_cond(host, 0, &ocr);
if (!err) {
if (mmc_attach_sdio(host, ocr))
mmc_power_off(host);
return;
}
/*
* ...then normal SD...
*/
err = mmc_send_app_op_cond(host, 0, &ocr); err = mmc_send_app_op_cond(host, 0, &ocr);
if (err == MMC_ERR_NONE) { if (!err) {
if (mmc_attach_sd(host, ocr)) if (mmc_attach_sd(host, ocr))
mmc_power_off(host); mmc_power_off(host);
} else { return;
}
/* /*
* If we fail to detect any SD cards then try * ...and finally MMC.
* searching for MMC cards.
*/ */
err = mmc_send_op_cond(host, 0, &ocr); err = mmc_send_op_cond(host, 0, &ocr);
if (err == MMC_ERR_NONE) { if (!err) {
if (mmc_attach_mmc(host, ocr)) if (mmc_attach_mmc(host, ocr))
mmc_power_off(host); mmc_power_off(host);
} else { return;
mmc_power_off(host);
mmc_release_host(host);
}
} }
mmc_release_host(host);
mmc_power_off(host);
} else { } else {
if (host->bus_ops->detect && !host->bus_dead) if (host->bus_ops->detect && !host->bus_dead)
host->bus_ops->detect(host); host->bus_ops->detect(host);
...@@ -725,22 +792,38 @@ static int __init mmc_init(void) ...@@ -725,22 +792,38 @@ static int __init mmc_init(void)
return -ENOMEM; return -ENOMEM;
ret = mmc_register_bus(); ret = mmc_register_bus();
if (ret == 0) { if (ret)
goto destroy_workqueue;
ret = mmc_register_host_class(); ret = mmc_register_host_class();
if (ret) if (ret)
goto unregister_bus;
ret = sdio_register_bus();
if (ret)
goto unregister_host_class;
return 0;
unregister_host_class:
mmc_unregister_host_class();
unregister_bus:
mmc_unregister_bus(); mmc_unregister_bus();
} destroy_workqueue:
destroy_workqueue(workqueue);
return ret; return ret;
} }
static void __exit mmc_exit(void) static void __exit mmc_exit(void)
{ {
sdio_unregister_bus();
mmc_unregister_host_class(); mmc_unregister_host_class();
mmc_unregister_bus(); mmc_unregister_bus();
destroy_workqueue(workqueue); destroy_workqueue(workqueue);
} }
module_init(mmc_init); subsys_initcall(mmc_init);
module_exit(mmc_exit); module_exit(mmc_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work); ...@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
void mmc_start_host(struct mmc_host *host); void mmc_start_host(struct mmc_host *host);
void mmc_stop_host(struct mmc_host *host); void mmc_stop_host(struct mmc_host *host);
extern int use_spi_crc;
#endif #endif
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/idr.h> #include <linux/idr.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/leds.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
...@@ -100,6 +101,9 @@ int mmc_add_host(struct mmc_host *host) ...@@ -100,6 +101,9 @@ int mmc_add_host(struct mmc_host *host)
{ {
int err; int err;
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
!host->ops->enable_sdio_irq);
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
...@@ -112,6 +116,8 @@ int mmc_add_host(struct mmc_host *host) ...@@ -112,6 +116,8 @@ int mmc_add_host(struct mmc_host *host)
snprintf(host->class_dev.bus_id, BUS_ID_SIZE, snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
"mmc%d", host->index); "mmc%d", host->index);
led_trigger_register_simple(host->class_dev.bus_id, &host->led);
err = device_add(&host->class_dev); err = device_add(&host->class_dev);
if (err) if (err)
return err; return err;
...@@ -137,6 +143,8 @@ void mmc_remove_host(struct mmc_host *host) ...@@ -137,6 +143,8 @@ void mmc_remove_host(struct mmc_host *host)
device_del(&host->class_dev); device_del(&host->class_dev);
led_trigger_unregister(host->led);
spin_lock(&mmc_host_lock); spin_lock(&mmc_host_lock);
idr_remove(&mmc_host_idr, host->index); idr_remove(&mmc_host_idr, host->index);
spin_unlock(&mmc_host_lock); spin_unlock(&mmc_host_lock);
......
...@@ -161,13 +161,12 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -161,13 +161,12 @@ static int mmc_read_ext_csd(struct mmc_card *card)
{ {
int err; int err;
u8 *ext_csd; u8 *ext_csd;
unsigned int ext_csd_struct;
BUG_ON(!card); BUG_ON(!card);
err = MMC_ERR_FAILED;
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return MMC_ERR_NONE; return 0;
/* /*
* As the ext_csd is so large and mostly unused, we don't store the * As the ext_csd is so large and mostly unused, we don't store the
...@@ -176,13 +175,19 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -176,13 +175,19 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd = kmalloc(512, GFP_KERNEL); ext_csd = kmalloc(512, GFP_KERNEL);
if (!ext_csd) { if (!ext_csd) {
printk(KERN_ERR "%s: could not allocate a buffer to " printk(KERN_ERR "%s: could not allocate a buffer to "
"receive the ext_csd. mmc v4 cards will be " "receive the ext_csd.\n", mmc_hostname(card->host));
"treated as v3.\n", mmc_hostname(card->host)); return -ENOMEM;
return MMC_ERR_FAILED;
} }
err = mmc_send_ext_csd(card, ext_csd); err = mmc_send_ext_csd(card, ext_csd);
if (err != MMC_ERR_NONE) { if (err) {
/*
* We all hosts that cannot perform the command
* to fail more gracefully
*/
if (err != -EINVAL)
goto out;
/* /*
* High capacity cards should have this "magic" size * High capacity cards should have this "magic" size
* stored in their CSD. * stored in their CSD.
...@@ -197,11 +202,21 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -197,11 +202,21 @@ static int mmc_read_ext_csd(struct mmc_card *card)
"EXT_CSD, performance might " "EXT_CSD, performance might "
"suffer.\n", "suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = MMC_ERR_NONE; err = 0;
} }
goto out; goto out;
} }
ext_csd_struct = ext_csd[EXT_CSD_REV];
if (ext_csd_struct > 2) {
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
"version %d\n", mmc_hostname(card->host),
ext_csd_struct);
return -EINVAL;
}
if (ext_csd_struct >= 2) {
card->ext_csd.sectors = card->ext_csd.sectors =
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
...@@ -209,6 +224,7 @@ static int mmc_read_ext_csd(struct mmc_card *card) ...@@ -209,6 +224,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
ext_csd[EXT_CSD_SEC_CNT + 3] << 24; ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
if (card->ext_csd.sectors) if (card->ext_csd.sectors)
mmc_card_set_blockaddr(card); mmc_card_set_blockaddr(card);
}
switch (ext_csd[EXT_CSD_CARD_TYPE]) { switch (ext_csd[EXT_CSD_CARD_TYPE]) {
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
...@@ -246,7 +262,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -246,7 +262,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
unsigned int max_dtr; unsigned int max_dtr;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
/* /*
* Since we're changing the OCR value, we seem to * Since we're changing the OCR value, we seem to
...@@ -258,19 +274,33 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -258,19 +274,33 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
/* The extra bit indicates that we support high capacity */ /* The extra bit indicates that we support high capacity */
err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); err = mmc_send_op_cond(host, ocr | (1 << 30), NULL);
if (err != MMC_ERR_NONE) 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; goto err;
}
/* /*
* Fetch CID from card. * Fetch CID from card.
*/ */
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid); err = mmc_all_send_cid(host, cid);
if (err != MMC_ERR_NONE) if (err)
goto err; goto err;
if (oldcard) { if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
goto err; goto err;
}
card = oldcard; card = oldcard;
} else { } else {
...@@ -278,8 +308,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -278,8 +308,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Allocate card structure. * Allocate card structure.
*/ */
card = mmc_alloc_card(host); card = mmc_alloc_card(host);
if (IS_ERR(card)) if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err; goto err;
}
card->type = MMC_TYPE_MMC; card->type = MMC_TYPE_MMC;
card->rca = 1; card->rca = 1;
...@@ -287,43 +319,47 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -287,43 +319,47 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
/* /*
* Set card RCA. * For native busses: set card RCA and quit open drain mode.
*/ */
if (!mmc_host_is_spi(host)) {
err = mmc_set_relative_addr(card); err = mmc_set_relative_addr(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) { if (!oldcard) {
/* /*
* Fetch CSD from card. * Fetch CSD from card.
*/ */
err = mmc_send_csd(card, card->raw_csd); err = mmc_send_csd(card, card->raw_csd);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
err = mmc_decode_csd(card); err = mmc_decode_csd(card);
if (err < 0) if (err)
goto free_card; goto free_card;
err = mmc_decode_cid(card); err = mmc_decode_cid(card);
if (err < 0) if (err)
goto free_card; goto free_card;
} }
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card); err = mmc_select_card(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
}
if (!oldcard) { if (!oldcard) {
/* /*
* Fetch and process extened CSD. * Fetch and process extended CSD.
*/ */
err = mmc_read_ext_csd(card); err = mmc_read_ext_csd(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
} }
...@@ -334,7 +370,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -334,7 +370,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
(host->caps & MMC_CAP_MMC_HIGHSPEED)) { (host->caps & MMC_CAP_MMC_HIGHSPEED)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, 1); EXT_CSD_HS_TIMING, 1);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
mmc_card_set_highspeed(card); mmc_card_set_highspeed(card);
...@@ -363,7 +399,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -363,7 +399,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
(host->caps & MMC_CAP_4_BIT_DATA)) { (host->caps & MMC_CAP_4_BIT_DATA)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
...@@ -372,14 +408,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, ...@@ -372,14 +408,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
return MMC_ERR_NONE; return 0;
free_card: free_card:
if (!oldcard) if (!oldcard)
mmc_remove_card(card); mmc_remove_card(card);
err: err:
return MMC_ERR_FAILED; return err;
} }
/* /*
...@@ -413,7 +449,7 @@ static void mmc_detect(struct mmc_host *host) ...@@ -413,7 +449,7 @@ static void mmc_detect(struct mmc_host *host)
mmc_release_host(host); mmc_release_host(host);
if (err != MMC_ERR_NONE) { if (err) {
mmc_remove(host); mmc_remove(host);
mmc_claim_host(host); mmc_claim_host(host);
...@@ -480,6 +516,7 @@ static void mmc_suspend(struct mmc_host *host) ...@@ -480,6 +516,7 @@ static void mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (!mmc_host_is_spi(host))
mmc_deselect_cards(host); mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
...@@ -502,7 +539,7 @@ static void mmc_resume(struct mmc_host *host) ...@@ -502,7 +539,7 @@ static void mmc_resume(struct mmc_host *host)
err = mmc_init_card(host, host->ocr, host->card); err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
if (err != MMC_ERR_NONE) { if (err) {
mmc_remove(host); mmc_remove(host);
mmc_claim_host(host); mmc_claim_host(host);
...@@ -536,10 +573,19 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) ...@@ -536,10 +573,19 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
int err; int err;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
mmc_attach_bus(host, &mmc_ops); mmc_attach_bus(host, &mmc_ops);
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
err = mmc_spi_read_ocr(host, 1, &ocr);
if (err)
goto err;
}
/* /*
* Sanity check the voltages that the card claims to * Sanity check the voltages that the card claims to
* support. * support.
...@@ -565,7 +611,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) ...@@ -565,7 +611,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
* Detect and init the card. * Detect and init the card.
*/ */
err = mmc_init_card(host, host->ocr, NULL); err = mmc_init_card(host, host->ocr, NULL);
if (err != MMC_ERR_NONE) if (err)
goto err; goto err;
mmc_release_host(host); mmc_release_host(host);
...@@ -587,6 +633,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) ...@@ -587,6 +633,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n", printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
return 0; return err;
} }
...@@ -40,10 +40,10 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) ...@@ -40,10 +40,10 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
} }
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
return MMC_ERR_NONE; return 0;
} }
int mmc_select_card(struct mmc_card *card) int mmc_select_card(struct mmc_card *card)
...@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host) ...@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host)
int err; int err;
struct mmc_command cmd; struct mmc_command cmd;
/*
* Non-SPI hosts need to prevent chipselect going active during
* GO_IDLE; that would put chips into SPI mode. Remind them of
* that in case of hardware that won't pull up DAT3/nCS otherwise.
*
* SPI hosts ignore ios.chip_select; it's managed according to
* rules that must accomodate non-MMC slaves which this layer
* won't even know about.
*/
if (!mmc_host_is_spi(host)) {
mmc_set_chip_select(host, MMC_CS_HIGH); mmc_set_chip_select(host, MMC_CS_HIGH);
mmc_delay(1); mmc_delay(1);
}
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_GO_IDLE_STATE; cmd.opcode = MMC_GO_IDLE_STATE;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
mmc_delay(1); mmc_delay(1);
if (!mmc_host_is_spi(host)) {
mmc_set_chip_select(host, MMC_CS_DONTCARE); mmc_set_chip_select(host, MMC_CS_DONTCARE);
mmc_delay(1); mmc_delay(1);
}
host->use_spi_crc = 0;
return err; return err;
} }
...@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_OP_COND; cmd.opcode = MMC_SEND_OP_COND;
cmd.arg = ocr; cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) { for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE) if (err)
break; break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) /* if we're just probing, do a single pass */
if (ocr == 0)
break; break;
err = MMC_ERR_TIMEOUT; /* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
} }
if (rocr) if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0]; *rocr = cmd.resp[0];
return err; return err;
...@@ -131,12 +154,12 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid) ...@@ -131,12 +154,12 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
memcpy(cid, cmd.resp, sizeof(u32) * 4); memcpy(cid, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE; return 0;
} }
int mmc_set_relative_addr(struct mmc_card *card) int mmc_set_relative_addr(struct mmc_card *card)
...@@ -154,46 +177,52 @@ int mmc_set_relative_addr(struct mmc_card *card) ...@@ -154,46 +177,52 @@ int mmc_set_relative_addr(struct mmc_card *card)
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
return MMC_ERR_NONE; return 0;
} }
int mmc_send_csd(struct mmc_card *card, u32 *csd) static int
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
{ {
int err; int err;
struct mmc_command cmd; struct mmc_command cmd;
BUG_ON(!card); BUG_ON(!host);
BUG_ON(!card->host); BUG_ON(!cxd);
BUG_ON(!csd);
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_CSD; cmd.opcode = opcode;
cmd.arg = card->rca << 16; cmd.arg = arg;
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
memcpy(csd, cmd.resp, sizeof(u32) * 4); memcpy(cxd, cmd.resp, sizeof(u32) * 4);
return MMC_ERR_NONE; return 0;
} }
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) static int
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
u32 opcode, void *buf, unsigned len)
{ {
struct mmc_request mrq; struct mmc_request mrq;
struct mmc_command cmd; struct mmc_command cmd;
struct mmc_data data; struct mmc_data data;
struct scatterlist sg; struct scatterlist sg;
void *data_buf;
BUG_ON(!card); /* dma onto stack is unsafe/nonportable, but callers to this
BUG_ON(!card->host); * routine normally provide temporary on-stack buffers ...
BUG_ON(!ext_csd); */
data_buf = kmalloc(len, GFP_KERNEL);
if (data_buf == NULL)
return -ENOMEM;
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
...@@ -202,28 +231,99 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) ...@@ -202,28 +231,99 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
mrq.cmd = &cmd; mrq.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
cmd.opcode = MMC_SEND_EXT_CSD; cmd.opcode = opcode;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 512; /* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we
* rely on callers to never use this with "native" calls for reading
* CSD or CID. Native versions of those commands use the R2 type,
* not R1 plus a data block.
*/
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = len;
data.blocks = 1; data.blocks = 1;
data.flags = MMC_DATA_READ; data.flags = MMC_DATA_READ;
data.sg = &sg; data.sg = &sg;
data.sg_len = 1; data.sg_len = 1;
sg_init_one(&sg, ext_csd, 512); sg_init_one(&sg, data_buf, len);
mmc_set_data_timeout(&data, card, 0); if (card)
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq); mmc_wait_for_req(host, &mrq);
if (cmd.error != MMC_ERR_NONE) memcpy(buf, data_buf, len);
kfree(data_buf);
if (cmd.error)
return cmd.error; return cmd.error;
if (data.error != MMC_ERR_NONE) if (data.error)
return data.error; return data.error;
return MMC_ERR_NONE; return 0;
}
int mmc_send_csd(struct mmc_card *card, u32 *csd)
{
if (!mmc_host_is_spi(card->host))
return mmc_send_cxd_native(card->host, card->rca << 16,
csd, MMC_SEND_CSD);
return mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd, 16);
}
int mmc_send_cid(struct mmc_host *host, u32 *cid)
{
if (!mmc_host_is_spi(host)) {
if (!host->card)
return -EINVAL;
return mmc_send_cxd_native(host, host->card->rca << 16,
cid, MMC_SEND_CID);
}
return mmc_send_cxd_data(NULL, host, MMC_SEND_CID, cid, 16);
}
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
{
return mmc_send_cxd_data(card, card->host, MMC_SEND_EXT_CSD,
ext_csd, 512);
}
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
{
struct mmc_command cmd;
int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_READ_OCR;
cmd.arg = highcap ? (1 << 30) : 0;
cmd.flags = MMC_RSP_SPI_R3;
err = mmc_wait_for_cmd(host, &cmd, 0);
*ocrp = cmd.resp[1];
return err;
}
int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
{
struct mmc_command cmd;
int err;
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SPI_CRC_ON_OFF;
cmd.flags = MMC_RSP_SPI_R1;
cmd.arg = use_crc;
err = mmc_wait_for_cmd(host, &cmd, 0);
if (!err)
host->use_spi_crc = use_crc;
return err;
} }
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
...@@ -241,13 +341,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) ...@@ -241,13 +341,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
(index << 16) | (index << 16) |
(value << 8) | (value << 8) |
set; set;
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
return MMC_ERR_NONE; return 0;
} }
int mmc_send_status(struct mmc_card *card, u32 *status) int mmc_send_status(struct mmc_card *card, u32 *status)
...@@ -261,16 +361,20 @@ int mmc_send_status(struct mmc_card *card, u32 *status) ...@@ -261,16 +361,20 @@ int mmc_send_status(struct mmc_card *card, u32 *status)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS; cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status) if (status)
*status = cmd.resp[0]; *status = cmd.resp[0];
return MMC_ERR_NONE; return 0;
} }
...@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd); ...@@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_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_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, 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);
#endif #endif
...@@ -166,8 +166,6 @@ static int mmc_decode_scr(struct mmc_card *card) ...@@ -166,8 +166,6 @@ static int mmc_decode_scr(struct mmc_card *card)
unsigned int scr_struct; unsigned int scr_struct;
u32 resp[4]; u32 resp[4];
BUG_ON(!mmc_card_sd(card));
resp[3] = card->raw_scr[1]; resp[3] = card->raw_scr[1];
resp[2] = card->raw_scr[0]; resp[2] = card->raw_scr[0];
...@@ -193,30 +191,38 @@ static int mmc_read_switch(struct mmc_card *card) ...@@ -193,30 +191,38 @@ static int mmc_read_switch(struct mmc_card *card)
u8 *status; u8 *status;
if (card->scr.sda_vsn < SCR_SPEC_VER_1) if (card->scr.sda_vsn < SCR_SPEC_VER_1)
return MMC_ERR_NONE; return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) { if (!(card->csd.cmdclass & CCC_SWITCH)) {
printk(KERN_WARNING "%s: card lacks mandatory switch " printk(KERN_WARNING "%s: card lacks mandatory switch "
"function, performance might suffer.\n", "function, performance might suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return MMC_ERR_NONE; return 0;
} }
err = MMC_ERR_FAILED; err = -EIO;
status = kmalloc(64, GFP_KERNEL); status = kmalloc(64, GFP_KERNEL);
if (!status) { if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for " printk(KERN_ERR "%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host)); "switch capabilities.\n", mmc_hostname(card->host));
return err; return -ENOMEM;
} }
err = mmc_sd_switch(card, 0, 0, 1, status); err = mmc_sd_switch(card, 0, 0, 1, status);
if (err != MMC_ERR_NONE) { if (err) {
/*
* We all hosts that cannot perform the command
* to fail more gracefully
*/
if (err != -EINVAL)
goto out;
printk(KERN_WARNING "%s: problem reading switch " printk(KERN_WARNING "%s: problem reading switch "
"capabilities, performance might suffer.\n", "capabilities, performance might suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = MMC_ERR_NONE; err = 0;
goto out; goto out;
} }
...@@ -238,28 +244,28 @@ static int mmc_switch_hs(struct mmc_card *card) ...@@ -238,28 +244,28 @@ static int mmc_switch_hs(struct mmc_card *card)
u8 *status; u8 *status;
if (card->scr.sda_vsn < SCR_SPEC_VER_1) if (card->scr.sda_vsn < SCR_SPEC_VER_1)
return MMC_ERR_NONE; return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) if (!(card->csd.cmdclass & CCC_SWITCH))
return MMC_ERR_NONE; return 0;
if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
return MMC_ERR_NONE; return 0;
if (card->sw_caps.hs_max_dtr == 0) if (card->sw_caps.hs_max_dtr == 0)
return MMC_ERR_NONE; return 0;
err = MMC_ERR_FAILED; err = -EIO;
status = kmalloc(64, GFP_KERNEL); status = kmalloc(64, GFP_KERNEL);
if (!status) { if (!status) {
printk(KERN_ERR "%s: could not allocate a buffer for " printk(KERN_ERR "%s: could not allocate a buffer for "
"switch capabilities.\n", mmc_hostname(card->host)); "switch capabilities.\n", mmc_hostname(card->host));
return err; return -ENOMEM;
} }
err = mmc_sd_switch(card, 1, 0, 1, status); err = mmc_sd_switch(card, 1, 0, 1, status);
if (err != MMC_ERR_NONE) if (err)
goto out; goto out;
if ((status[16] & 0xF) != 1) { if ((status[16] & 0xF) != 1) {
...@@ -292,7 +298,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -292,7 +298,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
unsigned int max_dtr; unsigned int max_dtr;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
/* /*
* Since we're changing the OCR value, we seem to * Since we're changing the OCR value, we seem to
...@@ -309,23 +315,37 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -309,23 +315,37 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* block-addressed SDHC cards. * block-addressed SDHC cards.
*/ */
err = mmc_send_if_cond(host, ocr); err = mmc_send_if_cond(host, ocr);
if (err == MMC_ERR_NONE) if (!err)
ocr |= 1 << 30; ocr |= 1 << 30;
err = mmc_send_app_op_cond(host, ocr, NULL); err = mmc_send_app_op_cond(host, ocr, NULL);
if (err != MMC_ERR_NONE) 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; goto err;
}
/* /*
* Fetch CID from card. * Fetch CID from card.
*/ */
if (mmc_host_is_spi(host))
err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid); err = mmc_all_send_cid(host, cid);
if (err != MMC_ERR_NONE) if (err)
goto err; goto err;
if (oldcard) { if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
err = -ENOENT;
goto err; goto err;
}
card = oldcard; card = oldcard;
} else { } else {
...@@ -333,32 +353,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -333,32 +353,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Allocate card structure. * Allocate card structure.
*/ */
card = mmc_alloc_card(host); card = mmc_alloc_card(host);
if (IS_ERR(card)) if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err; goto err;
}
card->type = MMC_TYPE_SD; card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
} }
/* /*
* Set card RCA. * For native busses: get card RCA and quit open drain mode.
*/ */
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca); err = mmc_send_relative_addr(host, &card->rca);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
if (!oldcard) { if (!oldcard) {
/* /*
* Fetch CSD from card. * Fetch CSD from card.
*/ */
err = mmc_send_csd(card, card->raw_csd); err = mmc_send_csd(card, card->raw_csd);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
err = mmc_decode_csd(card); err = mmc_decode_csd(card);
if (err < 0) if (err)
goto free_card; goto free_card;
mmc_decode_cid(card); mmc_decode_cid(card);
...@@ -367,16 +391,18 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -367,16 +391,18 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card); err = mmc_select_card(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
}
if (!oldcard) { if (!oldcard) {
/* /*
* Fetch SCR from card. * Fetch SCR from card.
*/ */
err = mmc_app_send_scr(card, card->raw_scr); err = mmc_app_send_scr(card, card->raw_scr);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
err = mmc_decode_scr(card); err = mmc_decode_scr(card);
...@@ -387,7 +413,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -387,7 +413,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Fetch switch information from card. * Fetch switch information from card.
*/ */
err = mmc_read_switch(card); err = mmc_read_switch(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
} }
...@@ -395,7 +421,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -395,7 +421,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
* Attempt to change to high-speed (if supported) * Attempt to change to high-speed (if supported)
*/ */
err = mmc_switch_hs(card); err = mmc_switch_hs(card);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
/* /*
...@@ -418,7 +444,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -418,7 +444,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if ((host->caps & MMC_CAP_4_BIT_DATA) && if ((host->caps & MMC_CAP_4_BIT_DATA) &&
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
if (err != MMC_ERR_NONE) if (err)
goto free_card; goto free_card;
mmc_set_bus_width(host, MMC_BUS_WIDTH_4); mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
...@@ -442,14 +468,14 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, ...@@ -442,14 +468,14 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (!oldcard) if (!oldcard)
host->card = card; host->card = card;
return MMC_ERR_NONE; return 0;
free_card: free_card:
if (!oldcard) if (!oldcard)
mmc_remove_card(card); mmc_remove_card(card);
err: err:
return MMC_ERR_FAILED; return err;
} }
/* /*
...@@ -483,7 +509,7 @@ static void mmc_sd_detect(struct mmc_host *host) ...@@ -483,7 +509,7 @@ static void mmc_sd_detect(struct mmc_host *host)
mmc_release_host(host); mmc_release_host(host);
if (err != MMC_ERR_NONE) { if (err) {
mmc_sd_remove(host); mmc_sd_remove(host);
mmc_claim_host(host); mmc_claim_host(host);
...@@ -552,6 +578,7 @@ static void mmc_sd_suspend(struct mmc_host *host) ...@@ -552,6 +578,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (!mmc_host_is_spi(host))
mmc_deselect_cards(host); mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_release_host(host); mmc_release_host(host);
...@@ -574,7 +601,7 @@ static void mmc_sd_resume(struct mmc_host *host) ...@@ -574,7 +601,7 @@ static void mmc_sd_resume(struct mmc_host *host)
err = mmc_sd_init_card(host, host->ocr, host->card); err = mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
if (err != MMC_ERR_NONE) { if (err) {
mmc_sd_remove(host); mmc_sd_remove(host);
mmc_claim_host(host); mmc_claim_host(host);
...@@ -608,10 +635,21 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -608,10 +635,21 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
int err; int err;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->claimed); WARN_ON(!host->claimed);
mmc_attach_bus(host, &mmc_sd_ops); mmc_attach_bus(host, &mmc_sd_ops);
/*
* We need to get OCR a different way for SPI.
*/
if (mmc_host_is_spi(host)) {
mmc_go_idle(host);
err = mmc_spi_read_ocr(host, 0, &ocr);
if (err)
goto err;
}
/* /*
* Sanity check the voltages that the card claims to * Sanity check the voltages that the card claims to
* support. * support.
...@@ -644,7 +682,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -644,7 +682,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
* Detect and init the card. * Detect and init the card.
*/ */
err = mmc_sd_init_card(host, host->ocr, NULL); err = mmc_sd_init_card(host, host->ocr, NULL);
if (err != MMC_ERR_NONE) if (err)
goto err; goto err;
mmc_release_host(host); mmc_release_host(host);
...@@ -666,6 +704,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) ...@@ -666,6 +704,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
printk(KERN_ERR "%s: error %d whilst initialising SD card\n", printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
mmc_hostname(host), err); mmc_hostname(host), err);
return 0; return err;
} }
...@@ -33,21 +33,21 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) ...@@ -33,21 +33,21 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
if (card) { if (card) {
cmd.arg = card->rca << 16; cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
} else { } else {
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_BCR;
} }
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
/* Check that card supported application commands */ /* Check that card supported application commands */
if (!(cmd.resp[0] & R1_APP_CMD)) if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
return MMC_ERR_FAILED; return -EOPNOTSUPP;
return MMC_ERR_NONE; return 0;
} }
/** /**
...@@ -73,7 +73,7 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, ...@@ -73,7 +73,7 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
BUG_ON(!cmd); BUG_ON(!cmd);
BUG_ON(retries < 0); BUG_ON(retries < 0);
err = MMC_ERR_INVALID; err = -EIO;
/* /*
* We have to resend MMC_APP_CMD for each attempt so * We have to resend MMC_APP_CMD for each attempt so
...@@ -83,8 +83,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, ...@@ -83,8 +83,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
err = mmc_app_cmd(host, card); err = mmc_app_cmd(host, card);
if (err != MMC_ERR_NONE) if (err) {
/* no point in retrying; no APP commands allowed */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break;
}
continue; continue;
}
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
...@@ -97,9 +103,15 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, ...@@ -97,9 +103,15 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
mmc_wait_for_req(host, &mrq); mmc_wait_for_req(host, &mrq);
err = cmd->error; err = cmd->error;
if (cmd->error == MMC_ERR_NONE) if (!cmd->error)
break;
/* no point in retrying illegal APP commands */
if (mmc_host_is_spi(host)) {
if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND)
break; break;
} }
}
return err; return err;
} }
...@@ -127,14 +139,14 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width) ...@@ -127,14 +139,14 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
cmd.arg = SD_BUS_WIDTH_4; cmd.arg = SD_BUS_WIDTH_4;
break; break;
default: default:
return MMC_ERR_INVALID; return -EINVAL;
} }
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
return MMC_ERR_NONE; return 0;
} }
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
...@@ -147,23 +159,36 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) ...@@ -147,23 +159,36 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_APP_OP_COND; cmd.opcode = SD_APP_OP_COND;
if (mmc_host_is_spi(host))
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
else
cmd.arg = ocr; cmd.arg = ocr;
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
for (i = 100; i; i--) { for (i = 100; i; i--) {
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
break; break;
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) /* if we're just probing, do a single pass */
if (ocr == 0)
break; break;
err = MMC_ERR_TIMEOUT; /* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
if (!(cmd.resp[0] & R1_SPI_IDLE))
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10); mmc_delay(10);
} }
if (rocr) if (rocr && !mmc_host_is_spi(host))
*rocr = cmd.resp[0]; *rocr = cmd.resp[0];
return err; return err;
...@@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) ...@@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
struct mmc_command cmd; struct mmc_command cmd;
int err; int err;
static const u8 test_pattern = 0xAA; static const u8 test_pattern = 0xAA;
u8 result_pattern;
/* /*
* To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND
...@@ -182,16 +208,21 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr) ...@@ -182,16 +208,21 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
*/ */
cmd.opcode = SD_SEND_IF_COND; cmd.opcode = SD_SEND_IF_COND;
cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern;
cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; cmd.flags = MMC_RSP_SPI_R7 | MMC_RSP_R7 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, 0); err = mmc_wait_for_cmd(host, &cmd, 0);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
if ((cmd.resp[0] & 0xFF) != test_pattern) if (mmc_host_is_spi(host))
return MMC_ERR_FAILED; result_pattern = cmd.resp[1] & 0xFF;
else
result_pattern = cmd.resp[0] & 0xFF;
return MMC_ERR_NONE; if (result_pattern != test_pattern)
return -EIO;
return 0;
} }
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
...@@ -209,12 +240,12 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) ...@@ -209,12 +240,12 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
*rca = cmd.resp[0] >> 16; *rca = cmd.resp[0] >> 16;
return MMC_ERR_NONE; return 0;
} }
int mmc_app_send_scr(struct mmc_card *card, u32 *scr) int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
...@@ -229,8 +260,10 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -229,8 +260,10 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
BUG_ON(!card->host); BUG_ON(!card->host);
BUG_ON(!scr); BUG_ON(!scr);
/* NOTE: caller guarantees scr is heap-allocated */
err = mmc_app_cmd(card->host, card); err = mmc_app_cmd(card->host, card);
if (err != MMC_ERR_NONE) if (err)
return err; return err;
memset(&mrq, 0, sizeof(struct mmc_request)); memset(&mrq, 0, sizeof(struct mmc_request));
...@@ -242,7 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -242,7 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
cmd.opcode = SD_APP_SEND_SCR; cmd.opcode = SD_APP_SEND_SCR;
cmd.arg = 0; cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 8; data.blksz = 8;
data.blocks = 1; data.blocks = 1;
...@@ -252,19 +285,19 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr) ...@@ -252,19 +285,19 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
sg_init_one(&sg, scr, 8); sg_init_one(&sg, scr, 8);
mmc_set_data_timeout(&data, card, 0); mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq); mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE) if (cmd.error)
return cmd.error; return cmd.error;
if (data.error != MMC_ERR_NONE) if (data.error)
return data.error; return data.error;
scr[0] = ntohl(scr[0]); scr[0] = ntohl(scr[0]);
scr[1] = ntohl(scr[1]); scr[1] = ntohl(scr[1]);
return MMC_ERR_NONE; return 0;
} }
int mmc_sd_switch(struct mmc_card *card, int mode, int group, int mmc_sd_switch(struct mmc_card *card, int mode, int group,
...@@ -278,6 +311,8 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -278,6 +311,8 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); BUG_ON(!card->host);
/* NOTE: caller guarantees resp is heap-allocated */
mode = !!mode; mode = !!mode;
value &= 0xF; value &= 0xF;
...@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
cmd.arg = mode << 31 | 0x00FFFFFF; cmd.arg = mode << 31 | 0x00FFFFFF;
cmd.arg &= ~(0xF << (group * 4)); cmd.arg &= ~(0xF << (group * 4));
cmd.arg |= value << (group * 4); cmd.arg |= value << (group * 4);
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = 64; data.blksz = 64;
data.blocks = 1; data.blocks = 1;
...@@ -302,15 +337,15 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group, ...@@ -302,15 +337,15 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
sg_init_one(&sg, resp, 64); sg_init_one(&sg, resp, 64);
mmc_set_data_timeout(&data, card, 0); mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq); mmc_wait_for_req(card->host, &mrq);
if (cmd.error != MMC_ERR_NONE) if (cmd.error)
return cmd.error; return cmd.error;
if (data.error != MMC_ERR_NONE) if (data.error)
return data.error; return data.error;
return MMC_ERR_NONE; return 0;
} }
/*
* linux/drivers/mmc/sdio.c
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "core.h"
#include "bus.h"
#include "sdio_bus.h"
#include "mmc_ops.h"
#include "sd_ops.h"
#include "sdio_ops.h"
#include "sdio_cis.h"
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
unsigned char data;
ret = mmc_io_rw_direct(func->card, 0, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
if (ret)
goto out;
data &= 0x0f;
if (data == 0x0f) {
ret = mmc_io_rw_direct(func->card, 0, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
if (ret)
goto out;
}
func->class = data;
out:
return ret;
}
static int sdio_init_func(struct mmc_card *card, unsigned int fn)
{
int ret;
struct sdio_func *func;
BUG_ON(fn > SDIO_MAX_FUNCS);
func = sdio_alloc_func(card);
if (IS_ERR(func))
return PTR_ERR(func);
func->num = fn;
ret = sdio_read_fbr(func);
if (ret)
goto fail;
ret = sdio_read_func_cis(func);
if (ret)
goto fail;
card->sdio_func[fn - 1] = func;
return 0;
fail:
/*
* It is okay to remove the function here even though we hold
* the host lock as we haven't registered the device yet.
*/
sdio_remove_func(func);
return ret;
}
static int sdio_read_cccr(struct mmc_card *card)
{
int ret;
int cccr_vsn;
unsigned char data;
memset(&card->cccr, 0, sizeof(struct sdio_cccr));
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
if (ret)
goto out;
cccr_vsn = data & 0x0f;
if (cccr_vsn > SDIO_CCCR_REV_1_20) {
printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
mmc_hostname(card->host), cccr_vsn);
return -EINVAL;
}
card->cccr.sdio_vsn = (data & 0xf0) >> 4;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
if (ret)
goto out;
if (data & SDIO_CCCR_CAP_SMB)
card->cccr.multi_block = 1;
if (data & SDIO_CCCR_CAP_LSC)
card->cccr.low_speed = 1;
if (data & SDIO_CCCR_CAP_4BLS)
card->cccr.wide_bus = 1;
if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
if (ret)
goto out;
if (data & SDIO_POWER_SMPC)
card->cccr.high_power = 1;
}
if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
if (ret)
goto out;
if (data & SDIO_SPEED_SHS)
card->cccr.high_speed = 1;
}
out:
return ret;
}
static int sdio_enable_wide(struct mmc_card *card)
{
int ret;
u8 ctrl;
if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
return 0;
if (card->cccr.low_speed && !card->cccr.wide_bus)
return 0;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
if (ret)
return ret;
ctrl |= SDIO_BUS_WIDTH_4BIT;
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_IF, ctrl, NULL);
if (ret)
return ret;
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
return 0;
}
/*
* Host is being removed. Free up the current card.
*/
static void mmc_sdio_remove(struct mmc_host *host)
{
int i;
BUG_ON(!host);
BUG_ON(!host->card);
for (i = 0;i < host->card->sdio_funcs;i++) {
if (host->card->sdio_func[i]) {
sdio_remove_func(host->card->sdio_func[i]);
host->card->sdio_func[i] = NULL;
}
}
mmc_remove_card(host->card);
host->card = NULL;
}
/*
* Card detection callback from host.
*/
static void mmc_sdio_detect(struct mmc_host *host)
{
int err;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
/*
* Just check if our card has been removed.
*/
err = mmc_select_card(host->card);
mmc_release_host(host);
if (err) {
mmc_sdio_remove(host);
mmc_claim_host(host);
mmc_detach_bus(host);
mmc_release_host(host);
}
}
static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove,
.detect = mmc_sdio_detect,
};
/*
* Starting point for SDIO card init.
*/
int mmc_attach_sdio(struct mmc_host *host, u32 ocr)
{
int err;
int i, funcs;
struct mmc_card *card;
BUG_ON(!host);
WARN_ON(!host->claimed);
mmc_attach_bus(host, &mmc_sdio_ops);
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
printk(KERN_WARNING "%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
if (ocr & MMC_VDD_165_195) {
printk(KERN_WARNING "%s: SDIO card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}
host->ocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
err = -EINVAL;
goto err;
}
/*
* Inform the card of the voltage
*/
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
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;
}
/*
* The number of functions on the card is encoded inside
* the ocr.
*/
funcs = (ocr & 0x70000000) >> 28;
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card->type = MMC_TYPE_SDIO;
card->sdio_funcs = funcs;
host->card = card;
/*
* For native busses: set card RCA and quit open drain mode.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_send_relative_addr(host, &card->rca);
if (err)
goto remove;
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
}
/*
* Select card, as all following commands rely on that.
*/
if (!mmc_host_is_spi(host)) {
err = mmc_select_card(card);
if (err)
goto remove;
}
/*
* Read the common registers.
*/
err = sdio_read_cccr(card);
if (err)
goto remove;
/*
* Read the common CIS tuples.
*/
err = sdio_read_common_cis(card);
if (err)
goto remove;
/*
* No support for high-speed yet, so just set
* the card's maximum speed.
*/
mmc_set_clock(host, card->cis.max_dtr);
/*
* Switch to wider bus (if supported).
*/
err = sdio_enable_wide(card);
if (err)
goto remove;
/*
* Initialize (but don't add) all present functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_init_func(host->card, i + 1);
if (err)
goto remove;
}
mmc_release_host(host);
/*
* First add the card to the driver model...
*/
err = mmc_add_card(host->card);
if (err)
goto remove_added;
/*
* ...then the SDIO functions.
*/
for (i = 0;i < funcs;i++) {
err = sdio_add_func(host->card->sdio_func[i]);
if (err)
goto remove_added;
}
return 0;
remove_added:
/* Remove without lock if the device has been added. */
mmc_sdio_remove(host);
mmc_claim_host(host);
remove:
/* And with lock if it hasn't been added. */
if (host->card)
mmc_sdio_remove(host);
err:
mmc_detach_bus(host);
mmc_release_host(host);
printk(KERN_ERR "%s: error %d whilst initialising SDIO card\n",
mmc_hostname(host), err);
return err;
}
/*
* linux/drivers/mmc/core/sdio_bus.c
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* SDIO function driver model
*/
#include <linux/device.h>
#include <linux/err.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_cis.h"
#include "sdio_bus.h"
#define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
/* show configuration fields */
#define sdio_config_attr(field, format_string) \
static ssize_t \
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct sdio_func *func; \
\
func = dev_to_sdio_func (dev); \
return sprintf (buf, format_string, func->field); \
}
sdio_config_attr(class, "0x%02x\n");
sdio_config_attr(vendor, "0x%04x\n");
sdio_config_attr(device, "0x%04x\n");
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct sdio_func *func = dev_to_sdio_func (dev);
return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
func->class, func->vendor, func->device);
}
static struct device_attribute sdio_dev_attrs[] = {
__ATTR_RO(class),
__ATTR_RO(vendor),
__ATTR_RO(device),
__ATTR_RO(modalias),
__ATTR_NULL,
};
static const struct sdio_device_id *sdio_match_one(struct sdio_func *func,
const struct sdio_device_id *id)
{
if (id->class != (__u8)SDIO_ANY_ID && id->class != func->class)
return NULL;
if (id->vendor != (__u16)SDIO_ANY_ID && id->vendor != func->vendor)
return NULL;
if (id->device != (__u16)SDIO_ANY_ID && id->device != func->device)
return NULL;
return id;
}
static const struct sdio_device_id *sdio_match_device(struct sdio_func *func,
struct sdio_driver *sdrv)
{
const struct sdio_device_id *ids;
ids = sdrv->id_table;
if (ids) {
while (ids->class || ids->vendor || ids->device) {
if (sdio_match_one(func, ids))
return ids;
ids++;
}
}
return NULL;
}
static int sdio_bus_match(struct device *dev, struct device_driver *drv)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct sdio_driver *sdrv = to_sdio_driver(drv);
if (sdio_match_device(func, sdrv))
return 1;
return 0;
}
static int
sdio_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
int buf_size)
{
struct sdio_func *func = dev_to_sdio_func(dev);
int i = 0, length = 0;
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"SDIO_CLASS=%02X", func->class))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"SDIO_ID=%04X:%04X", func->vendor, func->device))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buf, buf_size, &length,
"MODALIAS=sdio:c%02Xv%04Xd%04X",
func->class, func->vendor, func->device))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
static int sdio_bus_probe(struct device *dev)
{
struct sdio_driver *drv = to_sdio_driver(dev->driver);
struct sdio_func *func = dev_to_sdio_func(dev);
const struct sdio_device_id *id;
int ret;
id = sdio_match_device(func, drv);
if (!id)
return -ENODEV;
/* Set the default block size so the driver is sure it's something
* sensible. */
sdio_claim_host(func);
ret = sdio_set_block_size(func, 0);
sdio_release_host(func);
if (ret)
return ret;
return drv->probe(func, id);
}
static int sdio_bus_remove(struct device *dev)
{
struct sdio_driver *drv = to_sdio_driver(dev->driver);
struct sdio_func *func = dev_to_sdio_func(dev);
drv->remove(func);
if (func->irq_handler) {
printk(KERN_WARNING "WARNING: driver %s did not remove "
"its interrupt handler!\n", drv->name);
sdio_claim_host(func);
sdio_release_irq(func);
sdio_release_host(func);
}
return 0;
}
static struct bus_type sdio_bus_type = {
.name = "sdio",
.dev_attrs = sdio_dev_attrs,
.match = sdio_bus_match,
.uevent = sdio_bus_uevent,
.probe = sdio_bus_probe,
.remove = sdio_bus_remove,
};
int sdio_register_bus(void)
{
return bus_register(&sdio_bus_type);
}
void sdio_unregister_bus(void)
{
bus_unregister(&sdio_bus_type);
}
/**
* sdio_register_driver - register a function driver
* @drv: SDIO function driver
*/
int sdio_register_driver(struct sdio_driver *drv)
{
drv->drv.name = drv->name;
drv->drv.bus = &sdio_bus_type;
return driver_register(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_register_driver);
/**
* sdio_unregister_driver - unregister a function driver
* @drv: SDIO function driver
*/
void sdio_unregister_driver(struct sdio_driver *drv)
{
drv->drv.bus = &sdio_bus_type;
driver_unregister(&drv->drv);
}
EXPORT_SYMBOL_GPL(sdio_unregister_driver);
static void sdio_release_func(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
sdio_free_func_cis(func);
if (func->info)
kfree(func->info);
kfree(func);
}
/*
* Allocate and initialise a new SDIO function structure.
*/
struct sdio_func *sdio_alloc_func(struct mmc_card *card)
{
struct sdio_func *func;
func = kzalloc(sizeof(struct sdio_func), GFP_KERNEL);
if (!func)
return ERR_PTR(-ENOMEM);
func->card = card;
device_initialize(&func->dev);
func->dev.parent = &card->dev;
func->dev.bus = &sdio_bus_type;
func->dev.release = sdio_release_func;
return func;
}
/*
* Register a new SDIO function with the driver model.
*/
int sdio_add_func(struct sdio_func *func)
{
int ret;
snprintf(func->dev.bus_id, sizeof(func->dev.bus_id),
"%s:%d", mmc_card_id(func->card), func->num);
ret = device_add(&func->dev);
if (ret == 0)
sdio_func_set_present(func);
return ret;
}
/*
* Unregister a SDIO function with the driver model, and
* (eventually) free it.
*/
void sdio_remove_func(struct sdio_func *func)
{
if (sdio_func_present(func))
device_del(&func->dev);
put_device(&func->dev);
}
/*
* linux/drivers/mmc/core/sdio_bus.h
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_CORE_SDIO_BUS_H
#define _MMC_CORE_SDIO_BUS_H
struct sdio_func *sdio_alloc_func(struct mmc_card *card);
int sdio_add_func(struct sdio_func *func);
void sdio_remove_func(struct sdio_func *func);
int sdio_register_bus(void);
void sdio_unregister_bus(void);
#endif
/*
* linux/drivers/mmc/core/sdio_cis.c
*
* Author: Nicolas Pitre
* Created: June 11, 2007
* Copyright: MontaVista Software Inc.
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_cis.h"
#include "sdio_ops.h"
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned i, nr_strings;
char **buffer, *string;
buf += 2;
size -= 2;
nr_strings = 0;
for (i = 0; i < size; i++) {
if (buf[i] == 0xff)
break;
if (buf[i] == 0)
nr_strings++;
}
if (buf[i-1] != '\0') {
printk(KERN_WARNING "SDIO: ignoring broken CISTPL_VERS_1\n");
return 0;
}
size = i;
buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);
if (!buffer)
return -ENOMEM;
string = (char*)(buffer + nr_strings);
for (i = 0; i < nr_strings; i++) {
buffer[i] = string;
strcpy(string, buf);
string += strlen(string) + 1;
buf += strlen(buf) + 1;
}
if (func) {
func->num_info = nr_strings;
func->info = (const char**)buffer;
} else {
card->num_info = nr_strings;
card->info = (const char**)buffer;
}
return 0;
}
static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned int vendor, device;
/* TPLMID_MANF */
vendor = buf[0] | (buf[1] << 8);
/* TPLMID_CARD */
device = buf[2] | (buf[3] << 8);
if (func) {
func->vendor = vendor;
func->device = device;
} else {
card->cis.vendor = vendor;
card->cis.device = device;
}
return 0;
}
static const unsigned char speed_val[16] =
{ 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
static const unsigned int speed_unit[8] =
{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
static int cistpl_funce_common(struct mmc_card *card,
const unsigned char *buf, unsigned size)
{
if (size < 0x04 || buf[0] != 0)
return -EINVAL;
/* TPLFE_FN0_BLK_SIZE */
card->cis.blksize = buf[1] | (buf[2] << 8);
/* TPLFE_MAX_TRAN_SPEED */
card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] *
speed_unit[buf[3] & 7];
return 0;
}
static int cistpl_funce_func(struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
unsigned vsn;
unsigned min_size;
vsn = func->card->cccr.sdio_vsn;
min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
if (size < min_size || buf[0] != 1)
return -EINVAL;
/* TPLFE_MAX_BLK_SIZE */
func->max_blksize = buf[12] | (buf[13] << 8);
return 0;
}
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
const unsigned char *buf, unsigned size)
{
int ret;
/*
* There should be two versions of the CISTPL_FUNCE tuple,
* one for the common CIS (function 0) and a version used by
* the individual function's CIS (1-7). Yet, the later has a
* different length depending on the SDIO spec version.
*/
if (func)
ret = cistpl_funce_func(func, buf, size);
else
ret = cistpl_funce_common(card, buf, size);
if (ret) {
printk(KERN_ERR "%s: bad CISTPL_FUNCE size %u "
"type %u\n", mmc_hostname(card->host), size, buf[0]);
return ret;
}
return 0;
}
typedef int (tpl_parse_t)(struct mmc_card *, struct sdio_func *,
const unsigned char *, unsigned);
struct cis_tpl {
unsigned char code;
unsigned char min_size;
tpl_parse_t *parse;
};
static const struct cis_tpl cis_tpl_list[] = {
{ 0x15, 3, cistpl_vers_1 },
{ 0x20, 4, cistpl_manfid },
{ 0x21, 2, /* cistpl_funcid */ },
{ 0x22, 0, cistpl_funce },
};
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
{
int ret;
struct sdio_func_tuple *this, **prev;
unsigned i, ptr = 0;
/*
* Note that this works for the common CIS (function number 0) as
* well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS
* have the same offset.
*/
for (i = 0; i < 3; i++) {
unsigned char x, fn;
if (func)
fn = func->num;
else
fn = 0;
ret = mmc_io_rw_direct(card, 0, 0,
SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
if (ret)
return ret;
ptr |= x << (i * 8);
}
if (func)
prev = &func->tuples;
else
prev = &card->tuples;
BUG_ON(*prev);
do {
unsigned char tpl_code, tpl_link;
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
if (ret)
break;
/* 0xff means we're done */
if (tpl_code == 0xff)
break;
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link);
if (ret)
break;
this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL);
if (!this)
return -ENOMEM;
for (i = 0; i < tpl_link; i++) {
ret = mmc_io_rw_direct(card, 0, 0,
ptr + i, 0, &this->data[i]);
if (ret)
break;
}
if (ret) {
kfree(this);
break;
}
for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
if (cis_tpl_list[i].code == tpl_code)
break;
if (i >= ARRAY_SIZE(cis_tpl_list)) {
/* this tuple is unknown to the core */
this->next = NULL;
this->code = tpl_code;
this->size = tpl_link;
*prev = this;
prev = &this->next;
printk(KERN_DEBUG
"%s: queuing CIS tuple 0x%02x length %u\n",
mmc_hostname(card->host), tpl_code, tpl_link);
} else {
const struct cis_tpl *tpl = cis_tpl_list + i;
if (tpl_link < tpl->min_size) {
printk(KERN_ERR
"%s: bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",
mmc_hostname(card->host),
tpl_code, tpl_link, tpl->min_size);
ret = -EINVAL;
} else if (tpl->parse) {
ret = tpl->parse(card, func,
this->data, tpl_link);
}
kfree(this);
}
ptr += tpl_link;
} while (!ret);
/*
* Link in all unknown tuples found in the common CIS so that
* drivers don't have to go digging in two places.
*/
if (func)
*prev = card->tuples;
return ret;
}
int sdio_read_common_cis(struct mmc_card *card)
{
return sdio_read_cis(card, NULL);
}
void sdio_free_common_cis(struct mmc_card *card)
{
struct sdio_func_tuple *tuple, *victim;
tuple = card->tuples;
while (tuple) {
victim = tuple;
tuple = tuple->next;
kfree(victim);
}
card->tuples = NULL;
}
int sdio_read_func_cis(struct sdio_func *func)
{
int ret;
ret = sdio_read_cis(func->card, func);
if (ret)
return ret;
/*
* Since we've linked to tuples in the card structure,
* we must make sure we have a reference to it.
*/
get_device(&func->card->dev);
/*
* Vendor/device id is optional for function CIS, so
* copy it from the card structure as needed.
*/
if (func->vendor == 0) {
func->vendor = func->card->cis.vendor;
func->device = func->card->cis.device;
}
return 0;
}
void sdio_free_func_cis(struct sdio_func *func)
{
struct sdio_func_tuple *tuple, *victim;
tuple = func->tuples;
while (tuple && tuple != func->card->tuples) {
victim = tuple;
tuple = tuple->next;
kfree(victim);
}
func->tuples = NULL;
/*
* We have now removed the link to the tuples in the
* card structure, so remove the reference.
*/
put_device(&func->card->dev);
}
/*
* linux/drivers/mmc/core/sdio_cis.h
*
* Author: Nicolas Pitre
* Created: June 11, 2007
* Copyright: MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_SDIO_CIS_H
#define _MMC_SDIO_CIS_H
int sdio_read_common_cis(struct mmc_card *card);
void sdio_free_common_cis(struct mmc_card *card);
int sdio_read_func_cis(struct sdio_func *func);
void sdio_free_func_cis(struct sdio_func *func);
#endif
/*
* linux/drivers/mmc/core/sdio_io.c
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
/**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
* @func: SDIO function that will be accessed
*
* Claim a bus for a set of operations. The SDIO function given
* is used to figure out which bus is relevant.
*/
void sdio_claim_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_claim_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_claim_host);
/**
* sdio_release_host - release a bus for a certain SDIO function
* @func: SDIO function that was accessed
*
* Release a bus, allowing others to claim the bus for their
* operations.
*/
void sdio_release_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_release_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_release_host);
/**
* sdio_enable_func - enables a SDIO function for usage
* @func: SDIO function to enable
*
* Powers up and activates a SDIO function so that register
* access is possible.
*/
int sdio_enable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
unsigned long timeout;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg |= 1 << func->num;
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
/*
* FIXME: This should timeout based on information in the CIS,
* but we don't have card to parse that yet.
*/
timeout = jiffies + HZ;
while (1) {
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
if (ret)
goto err;
if (reg & (1 << func->num))
break;
ret = -ETIME;
if (time_after(jiffies, timeout))
goto err;
}
pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
return ret;
}
EXPORT_SYMBOL_GPL(sdio_enable_func);
/**
* sdio_disable_func - disable a SDIO function
* @func: SDIO function to disable
*
* Powers down and deactivates a SDIO function. Register access
* to this function will fail until the function is reenabled.
*/
int sdio_disable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg &= ~(1 << func->num);
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
return -EIO;
}
EXPORT_SYMBOL_GPL(sdio_disable_func);
/**
* sdio_set_block_size - set the block size of an SDIO function
* @func: SDIO function to change
* @blksz: new block size or 0 to use the default.
*
* The default block size is the largest supported by both the function
* and the host, with a maximum of 512 to ensure that arbitrarily sized
* data transfer use the optimal (least) number of commands.
*
* A driver may call this to override the default block size set by the
* core. This can be used to set a block size greater than the maximum
* that reported by the card; it is the driver's responsibility to ensure
* it uses a value that the card supports.
*
* Returns 0 on success, -EINVAL if the host does not support the
* requested block size, or -EIO (etc.) if one of the resultant FBR block
* size register writes failed.
*
*/
int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
{
int ret;
if (blksz > func->card->host->max_blk_size)
return -EINVAL;
if (blksz == 0) {
blksz = min(min(
func->max_blksize,
func->card->host->max_blk_size),
512u);
}
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
blksz & 0xff, NULL);
if (ret)
return ret;
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
(blksz >> 8) & 0xff, NULL);
if (ret)
return ret;
func->cur_blksize = blksz;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_block_size);
/* Split an arbitrarily sized data transfer into several
* IO_RW_EXTENDED commands. */
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
unsigned addr, int incr_addr, u8 *buf, unsigned size)
{
unsigned remainder = size;
unsigned max_blocks;
int ret;
/* Do the bulk of the transfer using block mode (if supported). */
if (func->card->cccr.multi_block) {
/* Blocks per command is limited by host count, host transfer
* size (we only use a single sg entry) and the maximum for
* IO_RW_EXTENDED of 511 blocks. */
max_blocks = min(min(
func->card->host->max_blk_count,
func->card->host->max_seg_size / func->cur_blksize),
511u);
while (remainder > func->cur_blksize) {
unsigned blocks;
blocks = remainder / func->cur_blksize;
if (blocks > max_blocks)
blocks = max_blocks;
size = blocks * func->cur_blksize;
ret = mmc_io_rw_extended(func->card, write,
func->num, addr, incr_addr, buf,
blocks, func->cur_blksize);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
}
/* Write the remainder using byte mode. */
while (remainder > 0) {
size = remainder;
if (size > func->cur_blksize)
size = func->cur_blksize;
if (size > 512)
size = 512; /* maximum size for byte mode */
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
incr_addr, buf, 1, size);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
return 0;
}
/**
* sdio_readb - read a single byte from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of a given SDIO
* function. If there is a problem reading the address, 0xff
* is returned and @err_ret will contain the error code.
*/
unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_readb);
/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
{
int ret;
BUG_ON(!func);
ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writeb);
/**
* sdio_memcpy_fromio - read a chunk of memory from a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address to begin reading from
* @count: number of bytes to read
*
* Reads from the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
unsigned int addr, int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
/**
* sdio_memcpy_toio - write a chunk of memory to a SDIO function
* @func: SDIO function to access
* @addr: address to start writing to
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
void *src, int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
/**
* sdio_readsb - read from a FIFO on a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address of (single byte) FIFO
* @count: number of bytes to read
*
* Reads from the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_readsb);
/**
* sdio_writesb - write to a FIFO of a SDIO function
* @func: SDIO function to access
* @addr: address of (single byte) FIFO
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
}
EXPORT_SYMBOL_GPL(sdio_writesb);
/**
* sdio_readw - read a 16 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 16 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address, 0xffff
* is returned and @err_ret will contain the error code.
*/
unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFF;
}
return le16_to_cpu(*(u16*)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readw);
/**
* sdio_writew - write a 16 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 16 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
int *err_ret)
{
int ret;
*(u16*)func->tmpbuf = cpu_to_le16(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writew);
/**
* sdio_readl - read a 32 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 32 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address,
* 0xffffffff is returned and @err_ret will contain the error
* code.
*/
unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFFFFFF;
}
return le32_to_cpu(*(u32*)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readl);
/**
* sdio_writel - write a 32 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 32 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
int *err_ret)
{
int ret;
*(u32*)func->tmpbuf = cpu_to_le32(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writel);
/**
* sdio_f0_readb - read a single byte from SDIO function 0
* @func: an SDIO function of the card
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of SDIO function 0.
* If there is a problem reading the address, 0xff is returned
* and @err_ret will contain the error code.
*/
unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_f0_readb);
/**
* sdio_f0_writeb - write a single byte to SDIO function 0
* @func: an SDIO function of the card
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of SDIO function 0.
* @err_ret will contain the status of the actual transfer.
*
* Only writes to the vendor specific CCCR registers (0xF0 -
* 0xFF) are permiited; @err_ret will be set to -EINVAL for *
* writes outside this range.
*/
void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
{
int ret;
BUG_ON(!func);
if (addr < 0xF0 || addr > 0xFF) {
if (err_ret)
*err_ret = -EINVAL;
return;
}
ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_f0_writeb);
/*
* linux/drivers/mmc/core/sdio_irq.c
*
* Author: Nicolas Pitre
* Created: June 18, 2007
* Copyright: MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
static int process_sdio_pending_irqs(struct mmc_card *card)
{
int i, ret, count;
unsigned char pending;
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
if (ret) {
printk(KERN_DEBUG "%s: error %d reading SDIO_CCCR_INTx\n",
mmc_card_id(card), ret);
return ret;
}
count = 0;
for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) {
struct sdio_func *func = card->sdio_func[i - 1];
if (!func) {
printk(KERN_WARNING "%s: pending IRQ for "
"non-existant function\n",
mmc_card_id(card));
ret = -EINVAL;
} else if (func->irq_handler) {
func->irq_handler(func);
count++;
} else {
printk(KERN_WARNING "%s: pending IRQ with no handler\n",
sdio_func_id(func));
ret = -EINVAL;
}
}
}
if (count)
return count;
return ret;
}
static int sdio_irq_thread(void *_host)
{
struct mmc_host *host = _host;
struct sched_param param = { .sched_priority = 1 };
unsigned long period, idle_period;
int ret;
sched_setscheduler(current, SCHED_FIFO, &param);
/*
* We want to allow for SDIO cards to work even on non SDIO
* aware hosts. One thing that non SDIO host cannot do is
* asynchronous notification of pending SDIO card interrupts
* hence we poll for them in that case.
*/
idle_period = msecs_to_jiffies(10);
period = (host->caps & MMC_CAP_SDIO_IRQ) ?
MAX_SCHEDULE_TIMEOUT : idle_period;
pr_debug("%s: IRQ thread started (poll period = %lu jiffies)\n",
mmc_hostname(host), period);
do {
/*
* We claim the host here on drivers behalf for a couple
* reasons:
*
* 1) it is already needed to retrieve the CCCR_INTx;
* 2) we want the driver(s) to clear the IRQ condition ASAP;
* 3) we need to control the abort condition locally.
*
* Just like traditional hard IRQ handlers, we expect SDIO
* IRQ handlers to be quick and to the point, so that the
* holding of the host lock does not cover too much work
* that doesn't require that lock to be held.
*/
ret = __mmc_claim_host(host, &host->sdio_irq_thread_abort);
if (ret)
break;
ret = process_sdio_pending_irqs(host->card);
mmc_release_host(host);
/*
* Give other threads a chance to run in the presence of
* errors. FIXME: determine if due to card removal and
* possibly exit this thread if so.
*/
if (ret < 0)
ssleep(1);
/*
* Adaptive polling frequency based on the assumption
* that an interrupt will be closely followed by more.
* This has a substantial benefit for network devices.
*/
if (!(host->caps & MMC_CAP_SDIO_IRQ)) {
if (ret > 0)
period /= 2;
else {
period++;
if (period > idle_period)
period = idle_period;
}
}
set_task_state(current, TASK_INTERRUPTIBLE);
if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 1);
if (!kthread_should_stop())
schedule_timeout(period);
set_task_state(current, TASK_RUNNING);
} while (!kthread_should_stop());
if (host->caps & MMC_CAP_SDIO_IRQ)
host->ops->enable_sdio_irq(host, 0);
pr_debug("%s: IRQ thread exiting with code %d\n",
mmc_hostname(host), ret);
return ret;
}
static int sdio_card_irq_get(struct mmc_card *card)
{
struct mmc_host *host = card->host;
WARN_ON(!host->claimed);
if (!host->sdio_irqs++) {
atomic_set(&host->sdio_irq_thread_abort, 0);
host->sdio_irq_thread =
kthread_run(sdio_irq_thread, host, "ksdiorqd");
if (IS_ERR(host->sdio_irq_thread)) {
int err = PTR_ERR(host->sdio_irq_thread);
host->sdio_irqs--;
return err;
}
}
return 0;
}
static int sdio_card_irq_put(struct mmc_card *card)
{
struct mmc_host *host = card->host;
WARN_ON(!host->claimed);
BUG_ON(host->sdio_irqs < 1);
if (!--host->sdio_irqs) {
atomic_set(&host->sdio_irq_thread_abort, 1);
kthread_stop(host->sdio_irq_thread);
}
return 0;
}
/**
* sdio_claim_irq - claim the IRQ for a SDIO function
* @func: SDIO function
* @handler: IRQ handler callback
*
* Claim and activate the IRQ for the given SDIO function. The provided
* handler will be called when that IRQ is asserted. The host is always
* claimed already when the handler is called so the handler must not
* call sdio_claim_host() nor sdio_release_host().
*/
int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
if (func->irq_handler) {
pr_debug("SDIO: IRQ for %s already in use.\n", sdio_func_id(func));
return -EBUSY;
}
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
if (ret)
return ret;
reg |= 1 << func->num;
reg |= 1; /* Master interrupt enable */
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
if (ret)
return ret;
func->irq_handler = handler;
ret = sdio_card_irq_get(func->card);
if (ret)
func->irq_handler = NULL;
return ret;
}
EXPORT_SYMBOL_GPL(sdio_claim_irq);
/**
* sdio_release_irq - release the IRQ for a SDIO function
* @func: SDIO function
*
* Disable and release the IRQ for the given SDIO function.
*/
int sdio_release_irq(struct sdio_func *func)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
if (func->irq_handler) {
func->irq_handler = NULL;
sdio_card_irq_put(func->card);
}
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, &reg);
if (ret)
return ret;
reg &= ~(1 << func->num);
/* Disable master interrupt with the last function interrupt */
if (!(reg & 0xFE))
reg = 0;
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
if (ret)
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_release_irq);
/*
* linux/drivers/mmc/sdio_ops.c
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <asm/scatterlist.h>
#include <linux/scatterlist.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include "core.h"
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
{
struct mmc_command cmd;
int i, err = 0;
BUG_ON(!host);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_SEND_OP_COND;
cmd.arg = ocr;
cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR;
for (i = 100; i; i--) {
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
break;
/* if we're just probing, do a single pass */
if (ocr == 0)
break;
/* otherwise wait until reset completes */
if (mmc_host_is_spi(host)) {
/*
* Both R1_SPI_IDLE and MMC_CARD_BUSY indicate
* an initialized card under SPI, but some cards
* (Marvell's) only behave when looking at this
* one.
*/
if (cmd.resp[1] & MMC_CARD_BUSY)
break;
} else {
if (cmd.resp[0] & MMC_CARD_BUSY)
break;
}
err = -ETIMEDOUT;
mmc_delay(10);
}
if (rocr)
*rocr = cmd.resp[mmc_host_is_spi(host) ? 1 : 0];
return err;
}
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out)
{
struct mmc_command cmd;
int err;
BUG_ON(!card);
BUG_ON(fn > 7);
memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = SD_IO_RW_DIRECT;
cmd.arg = write ? 0x80000000 : 0x00000000;
cmd.arg |= fn << 28;
cmd.arg |= (write && out) ? 0x08000000 : 0x00000000;
cmd.arg |= addr << 9;
cmd.arg |= in;
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err)
return err;
if (mmc_host_is_spi(card->host)) {
/* host driver already reported errors */
} else {
if (cmd.resp[0] & R5_ERROR)
return -EIO;
if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
if (out) {
if (mmc_host_is_spi(card->host))
*out = (cmd.resp[0] >> 8) & 0xFF;
else
*out = cmd.resp[0] & 0xFF;
}
return 0;
}
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
{
struct mmc_request mrq;
struct mmc_command cmd;
struct mmc_data data;
struct scatterlist sg;
BUG_ON(!card);
BUG_ON(fn > 7);
BUG_ON(blocks == 1 && blksz > 512);
WARN_ON(blocks == 0);
WARN_ON(blksz == 0);
memset(&mrq, 0, sizeof(struct mmc_request));
memset(&cmd, 0, sizeof(struct mmc_command));
memset(&data, 0, sizeof(struct mmc_data));
mrq.cmd = &cmd;
mrq.data = &data;
cmd.opcode = SD_IO_RW_EXTENDED;
cmd.arg = write ? 0x80000000 : 0x00000000;
cmd.arg |= fn << 28;
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
cmd.arg |= addr << 9;
if (blocks == 1 && blksz <= 512)
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
else
cmd.arg |= 0x08000000 | blocks; /* block mode */
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
data.blksz = blksz;
data.blocks = blocks;
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, buf, blksz * blocks);
mmc_set_data_timeout(&data, card);
mmc_wait_for_req(card->host, &mrq);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
if (mmc_host_is_spi(card->host)) {
/* host driver already reported errors */
} else {
if (cmd.resp[0] & R5_ERROR)
return -EIO;
if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
}
return 0;
}
/*
* linux/drivers/mmc/sdio_ops.c
*
* Copyright 2006-2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef _MMC_SDIO_OPS_H
#define _MMC_SDIO_OPS_H
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out);
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
#endif
此差异已折叠。
...@@ -10,9 +10,11 @@ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o ...@@ -10,9 +10,11 @@ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_IMX) += imxmmc.o obj-$(CONFIG_MMC_IMX) += imxmmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_AT91) += at91_mci.o
obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册