Commit 6abd2c86 authored by Linus Torvalds's avatar 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
  ...
parents d2c75f2f 019a5f56
...@@ -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);
......
This diff is collapsed.
...@@ -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
This diff is collapsed.
/*
* 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
This diff is collapsed.
...@@ -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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment