You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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 ...
This commit is contained in:
+7
-1
@@ -2561,12 +2561,18 @@ L: linux-kernel@vger.kernel.org
|
||||
W: http://www.atnf.csiro.au/~rgooch/linux/kernel-patches.html
|
||||
S: Maintained
|
||||
|
||||
MULTIMEDIA CARD (MMC) AND SECURE DIGITAL (SD) SUBSYSTEM
|
||||
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
|
||||
P: Pierre Ossman
|
||||
M: drzeus-mmc@drzeus.cx
|
||||
L: linux-kernel@vger.kernel.org
|
||||
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
|
||||
P: Andrew Veliath
|
||||
M: andrewtv@usa.net
|
||||
|
||||
@@ -116,7 +116,7 @@ static struct platform_device *devices[] __initdata = {
|
||||
};
|
||||
|
||||
#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 */
|
||||
return (SSR(1) & (1 << 20) ? 0 : 1);
|
||||
|
||||
@@ -32,3 +32,10 @@ config MMC_BLOCK_BOUNCE
|
||||
|
||||
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
|
||||
obj-$(CONFIG_MMC_BLOCK) += mmc_block.o
|
||||
mmc_block-objs := block.o queue.o
|
||||
|
||||
obj-$(CONFIG_SDIO_UART) += sdio_uart.o
|
||||
|
||||
|
||||
+18
-12
@@ -151,17 +151,19 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||
|
||||
cmd.opcode = MMC_APP_CMD;
|
||||
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);
|
||||
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;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = SD_APP_SEND_NUM_WR_BLKS;
|
||||
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));
|
||||
|
||||
@@ -192,7 +194,7 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||
|
||||
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;
|
||||
|
||||
blocks = ntohl(blocks);
|
||||
@@ -220,17 +222,15 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
brq.cmd.arg = req->sector;
|
||||
if (!mmc_card_blockaddr(card))
|
||||
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.stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
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);
|
||||
if (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
|
||||
* block writes to single block. SD cards are excepted from
|
||||
@@ -243,8 +243,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
brq.data.blocks = 1;
|
||||
|
||||
if (brq.data.blocks > 1) {
|
||||
brq.data.flags |= MMC_DATA_MULTI;
|
||||
brq.mrq.stop = &brq.stop;
|
||||
/* 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;
|
||||
readcmd = MMC_READ_MULTIPLE_BLOCK;
|
||||
writecmd = MMC_WRITE_MULTIPLE_BLOCK;
|
||||
} else {
|
||||
@@ -261,6 +265,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
brq.data.flags |= MMC_DATA_WRITE;
|
||||
}
|
||||
|
||||
mmc_set_data_timeout(&brq.data, card);
|
||||
|
||||
brq.data.sg = mq->sg;
|
||||
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)
|
||||
goto cmd_err;
|
||||
}
|
||||
|
||||
if (rq_data_dir(req) != READ) {
|
||||
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
|
||||
do {
|
||||
int err;
|
||||
|
||||
@@ -510,7 +516,7 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||
mmc_claim_host(card->host);
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
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);
|
||||
mmc_release_host(card->host);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,5 +8,7 @@ endif
|
||||
|
||||
obj-$(CONFIG_MMC) += mmc_core.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
|
||||
|
||||
|
||||
+48
-19
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "sysfs.h"
|
||||
#include "core.h"
|
||||
#include "sdio_cis.h"
|
||||
#include "bus.h"
|
||||
|
||||
#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,
|
||||
return sprintf(buf, "MMC\n");
|
||||
case MMC_TYPE_SD:
|
||||
return sprintf(buf, "SD\n");
|
||||
case MMC_TYPE_SDIO:
|
||||
return sprintf(buf, "SDIO\n");
|
||||
default:
|
||||
return -EFAULT;
|
||||
}
|
||||
@@ -59,28 +62,34 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf,
|
||||
int buf_size)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
int retval = 0, 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);
|
||||
const char *type;
|
||||
int i = 0, length = 0;
|
||||
|
||||
switch (card->type) {
|
||||
case MMC_TYPE_MMC:
|
||||
add_env("MMC_TYPE=%s", "MMC");
|
||||
type = "MMC";
|
||||
break;
|
||||
case MMC_TYPE_SD:
|
||||
add_env("MMC_TYPE=%s", "SD");
|
||||
type = "SD";
|
||||
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;
|
||||
|
||||
@@ -176,6 +185,11 @@ static void mmc_release_card(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = dev_to_mmc_card(dev);
|
||||
|
||||
sdio_free_common_cis(card);
|
||||
|
||||
if (card->info)
|
||||
kfree(card->info);
|
||||
|
||||
kfree(card);
|
||||
}
|
||||
|
||||
@@ -221,15 +235,25 @@ int mmc_add_card(struct mmc_card *card)
|
||||
if (mmc_card_blockaddr(card))
|
||||
type = "SDHC";
|
||||
break;
|
||||
case MMC_TYPE_SDIO:
|
||||
type = "SDIO";
|
||||
break;
|
||||
default:
|
||||
type = "?";
|
||||
break;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
type, card->rca);
|
||||
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",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
type, card->rca);
|
||||
}
|
||||
|
||||
card->dev.uevent_suppress = 1;
|
||||
|
||||
@@ -261,8 +285,13 @@ int mmc_add_card(struct mmc_card *card)
|
||||
void mmc_remove_card(struct mmc_card *card)
|
||||
{
|
||||
if (mmc_card_present(card)) {
|
||||
printk(KERN_INFO "%s: card %04x removed\n",
|
||||
mmc_hostname(card->host), card->rca);
|
||||
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",
|
||||
mmc_hostname(card->host), card->rca);
|
||||
}
|
||||
|
||||
if (card->host->bus_ops->sysfs_remove)
|
||||
card->host->bus_ops->sysfs_remove(card->host, card);
|
||||
|
||||
+126
-43
@@ -18,6 +18,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/leds.h>
|
||||
#include <asm/scatterlist.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
@@ -29,15 +30,26 @@
|
||||
#include "core.h"
|
||||
#include "bus.h"
|
||||
#include "host.h"
|
||||
#include "sdio_bus.h"
|
||||
|
||||
#include "mmc_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_sd(struct mmc_host *host, u32 ocr);
|
||||
extern int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -68,6 +80,11 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
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) {
|
||||
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
||||
mmc_hostname(host), cmd->opcode, err);
|
||||
@@ -76,6 +93,8 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
cmd->error = 0;
|
||||
host->ops->request(host, mrq);
|
||||
} else {
|
||||
led_trigger_event(host->led, LED_OFF);
|
||||
|
||||
pr_debug("%s: req done (CMD%u): %d: %08x %08x %08x %08x\n",
|
||||
mmc_hostname(host), cmd->opcode, err,
|
||||
cmd->resp[0], cmd->resp[1],
|
||||
@@ -118,7 +137,7 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
"tsac %d ms nsac %d\n",
|
||||
mmc_hostname(host), mrq->data->blksz,
|
||||
mrq->data->blocks, mrq->data->flags,
|
||||
mrq->data->timeout_ns / 10000000,
|
||||
mrq->data->timeout_ns / 1000000,
|
||||
mrq->data->timeout_clks);
|
||||
}
|
||||
|
||||
@@ -130,6 +149,8 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
led_trigger_event(host->led, LED_FULL);
|
||||
|
||||
mrq->cmd->error = 0;
|
||||
mrq->cmd->mrq = mrq;
|
||||
if (mrq->data) {
|
||||
@@ -199,7 +220,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
|
||||
@@ -220,16 +241,23 @@ EXPORT_SYMBOL(mmc_wait_for_cmd);
|
||||
* mmc_set_data_timeout - set the timeout for a data command
|
||||
* @data: data phase for command
|
||||
* @card: the MMC card associated with the data transfer
|
||||
* @write: flag to differentiate reads from writes
|
||||
*
|
||||
* Computes the data timeout parameters according to the
|
||||
* correct algorithm given the card type.
|
||||
*/
|
||||
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
|
||||
int write)
|
||||
void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
||||
{
|
||||
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
|
||||
*/
|
||||
@@ -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
|
||||
* the r2w factor for writes.
|
||||
*/
|
||||
if (write)
|
||||
if (data->flags & MMC_DATA_WRITE)
|
||||
mult <<= card->csd.r2w_factor;
|
||||
|
||||
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,
|
||||
timeout_us += data->timeout_clks * 1000 /
|
||||
(card->host->ios.clock / 1000);
|
||||
|
||||
if (write)
|
||||
if (data->flags & MMC_DATA_WRITE)
|
||||
limit_us = 250000;
|
||||
else
|
||||
limit_us = 100000;
|
||||
@@ -272,15 +300,20 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card,
|
||||
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
|
||||
* @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);
|
||||
unsigned long flags;
|
||||
int stop;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@@ -288,19 +321,24 @@ void mmc_claim_host(struct mmc_host *host)
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
while (1) {
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
if (!host->claimed)
|
||||
stop = abort ? atomic_read(abort) : 0;
|
||||
if (stop || !host->claimed)
|
||||
break;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
schedule();
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
}
|
||||
set_current_state(TASK_RUNNING);
|
||||
host->claimed = 1;
|
||||
if (!stop)
|
||||
host->claimed = 1;
|
||||
else
|
||||
wake_up(&host->wq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
remove_wait_queue(&host->wq, &wait);
|
||||
return stop;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_claim_host);
|
||||
EXPORT_SYMBOL(__mmc_claim_host);
|
||||
|
||||
/**
|
||||
* mmc_release_host - release a host
|
||||
@@ -313,7 +351,7 @@ void mmc_release_host(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->claimed = 0;
|
||||
@@ -433,19 +471,32 @@ static void mmc_power_up(struct mmc_host *host)
|
||||
int bit = fls(host->ocr_avail) - 1;
|
||||
|
||||
host->ios.vdd = bit;
|
||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||
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.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||
}
|
||||
host->ios.power_mode = MMC_POWER_UP;
|
||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||
host->ios.timing = MMC_TIMING_LEGACY;
|
||||
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.power_mode = MMC_POWER_ON;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -453,8 +504,10 @@ static void mmc_power_off(struct mmc_host *host)
|
||||
{
|
||||
host->ios.clock = 0;
|
||||
host->ios.vdd = 0;
|
||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||
}
|
||||
host->ios.power_mode = MMC_POWER_OFF;
|
||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||
host->ios.timing = MMC_TIMING_LEGACY;
|
||||
@@ -511,7 +564,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!ops);
|
||||
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
@@ -535,8 +588,8 @@ void mmc_detach_bus(struct mmc_host *host)
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
BUG_ON(!host->claimed);
|
||||
BUG_ON(!host->bus_ops);
|
||||
WARN_ON(!host->claimed);
|
||||
WARN_ON(!host->bus_ops);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
@@ -564,7 +617,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
BUG_ON(host->removed);
|
||||
WARN_ON(host->removed);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
@@ -597,24 +650,38 @@ void mmc_rescan(struct work_struct *work)
|
||||
|
||||
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);
|
||||
if (err == MMC_ERR_NONE) {
|
||||
if (!err) {
|
||||
if (mmc_attach_sd(host, ocr))
|
||||
mmc_power_off(host);
|
||||
} else {
|
||||
/*
|
||||
* If we fail to detect any SD cards then try
|
||||
* searching for MMC cards.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (err == MMC_ERR_NONE) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
} else {
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ...and finally MMC.
|
||||
*/
|
||||
err = mmc_send_op_cond(host, 0, &ocr);
|
||||
if (!err) {
|
||||
if (mmc_attach_mmc(host, ocr))
|
||||
mmc_power_off(host);
|
||||
return;
|
||||
}
|
||||
|
||||
mmc_release_host(host);
|
||||
mmc_power_off(host);
|
||||
} else {
|
||||
if (host->bus_ops->detect && !host->bus_dead)
|
||||
host->bus_ops->detect(host);
|
||||
@@ -725,22 +792,38 @@ static int __init mmc_init(void)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_register_bus();
|
||||
if (ret == 0) {
|
||||
ret = mmc_register_host_class();
|
||||
if (ret)
|
||||
mmc_unregister_bus();
|
||||
}
|
||||
if (ret)
|
||||
goto destroy_workqueue;
|
||||
|
||||
ret = mmc_register_host_class();
|
||||
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();
|
||||
destroy_workqueue:
|
||||
destroy_workqueue(workqueue);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit mmc_exit(void)
|
||||
{
|
||||
sdio_unregister_bus();
|
||||
mmc_unregister_host_class();
|
||||
mmc_unregister_bus();
|
||||
destroy_workqueue(workqueue);
|
||||
}
|
||||
|
||||
module_init(mmc_init);
|
||||
subsys_initcall(mmc_init);
|
||||
module_exit(mmc_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
|
||||
void mmc_start_host(struct mmc_host *host);
|
||||
void mmc_stop_host(struct mmc_host *host);
|
||||
|
||||
extern int use_spi_crc;
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
@@ -100,6 +101,9 @@ int mmc_add_host(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
!host->ops->enable_sdio_irq);
|
||||
|
||||
if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -112,6 +116,8 @@ int mmc_add_host(struct mmc_host *host)
|
||||
snprintf(host->class_dev.bus_id, BUS_ID_SIZE,
|
||||
"mmc%d", host->index);
|
||||
|
||||
led_trigger_register_simple(host->class_dev.bus_id, &host->led);
|
||||
|
||||
err = device_add(&host->class_dev);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -137,6 +143,8 @@ void mmc_remove_host(struct mmc_host *host)
|
||||
|
||||
device_del(&host->class_dev);
|
||||
|
||||
led_trigger_unregister(host->led);
|
||||
|
||||
spin_lock(&mmc_host_lock);
|
||||
idr_remove(&mmc_host_idr, host->index);
|
||||
spin_unlock(&mmc_host_lock);
|
||||
|
||||
+90
-44
@@ -161,13 +161,12 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
u8 *ext_csd;
|
||||
unsigned int ext_csd_struct;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
err = MMC_ERR_FAILED;
|
||||
|
||||
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
|
||||
@@ -176,13 +175,19 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
ext_csd = kmalloc(512, GFP_KERNEL);
|
||||
if (!ext_csd) {
|
||||
printk(KERN_ERR "%s: could not allocate a buffer to "
|
||||
"receive the ext_csd. mmc v4 cards will be "
|
||||
"treated as v3.\n", mmc_hostname(card->host));
|
||||
return MMC_ERR_FAILED;
|
||||
"receive the ext_csd.\n", mmc_hostname(card->host));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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
|
||||
* stored in their CSD.
|
||||
@@ -197,18 +202,29 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
"EXT_CSD, performance might "
|
||||
"suffer.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = MMC_ERR_NONE;
|
||||
err = 0;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
card->ext_csd.sectors =
|
||||
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
|
||||
if (card->ext_csd.sectors)
|
||||
mmc_card_set_blockaddr(card);
|
||||
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 =
|
||||
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 |
|
||||
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
|
||||
if (card->ext_csd.sectors)
|
||||
mmc_card_set_blockaddr(card);
|
||||
}
|
||||
|
||||
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
|
||||
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,
|
||||
unsigned int max_dtr;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
|
||||
/* The extra bit indicates that we support high capacity */
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch CID from card.
|
||||
*/
|
||||
err = mmc_all_send_cid(host, cid);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (mmc_host_is_spi(host))
|
||||
err = mmc_send_cid(host, cid);
|
||||
else
|
||||
err = mmc_all_send_cid(host, cid);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (oldcard) {
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||
err = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
card = oldcard;
|
||||
} else {
|
||||
@@ -278,8 +308,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Allocate card structure.
|
||||
*/
|
||||
card = mmc_alloc_card(host);
|
||||
if (IS_ERR(card))
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto err;
|
||||
}
|
||||
|
||||
card->type = MMC_TYPE_MMC;
|
||||
card->rca = 1;
|
||||
@@ -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.
|
||||
*/
|
||||
err = mmc_set_relative_addr(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
goto free_card;
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
err = mmc_set_relative_addr(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
/*
|
||||
* Fetch CSD from card.
|
||||
*/
|
||||
err = mmc_send_csd(card, card->raw_csd);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
err = mmc_decode_csd(card);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
goto free_card;
|
||||
err = mmc_decode_cid(card);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select card, as all following commands rely on that.
|
||||
*/
|
||||
err = mmc_select_card(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
goto free_card;
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
err = mmc_select_card(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
/*
|
||||
* Fetch and process extened CSD.
|
||||
* Fetch and process extended CSD.
|
||||
*/
|
||||
err = mmc_read_ext_csd(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
@@ -334,7 +370,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_card_set_highspeed(card);
|
||||
@@ -363,7 +399,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
(host->caps & MMC_CAP_4_BIT_DATA)) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
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,
|
||||
if (!oldcard)
|
||||
host->card = card;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
|
||||
free_card:
|
||||
if (!oldcard)
|
||||
mmc_remove_card(card);
|
||||
err:
|
||||
|
||||
return MMC_ERR_FAILED;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -413,7 +449,7 @@ static void mmc_detect(struct mmc_host *host)
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
if (err != MMC_ERR_NONE) {
|
||||
if (err) {
|
||||
mmc_remove(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
@@ -480,7 +516,8 @@ static void mmc_suspend(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_deselect_cards(host);
|
||||
if (!mmc_host_is_spi(host))
|
||||
mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_release_host(host);
|
||||
}
|
||||
@@ -502,7 +539,7 @@ static void mmc_resume(struct mmc_host *host)
|
||||
err = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
if (err != MMC_ERR_NONE) {
|
||||
if (err) {
|
||||
mmc_remove(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
@@ -536,10 +573,19 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
|
||||
int err;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
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
|
||||
* support.
|
||||
@@ -565,7 +611,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr)
|
||||
* Detect and init the card.
|
||||
*/
|
||||
err = mmc_init_card(host, host->ocr, NULL);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
mmc_release_host(host);
|
||||
@@ -587,6 +633,6 @@ err:
|
||||
printk(KERN_ERR "%s: error %d whilst initialising MMC card\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+152
-48
@@ -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);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmc_select_card(struct mmc_card *card)
|
||||
@@ -63,23 +63,36 @@ int mmc_go_idle(struct mmc_host *host)
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
|
||||
mmc_set_chip_select(host, MMC_CS_HIGH);
|
||||
|
||||
mmc_delay(1);
|
||||
/*
|
||||
* 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_delay(1);
|
||||
}
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_GO_IDLE_STATE;
|
||||
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);
|
||||
|
||||
mmc_delay(1);
|
||||
|
||||
mmc_set_chip_select(host, MMC_CS_DONTCARE);
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
mmc_set_chip_select(host, MMC_CS_DONTCARE);
|
||||
mmc_delay(1);
|
||||
}
|
||||
|
||||
mmc_delay(1);
|
||||
host->use_spi_crc = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SEND_OP_COND;
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
|
||||
for (i = 100; i; i--) {
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
|
||||
/* if we're just probing, do a single pass */
|
||||
if (ocr == 0)
|
||||
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);
|
||||
}
|
||||
|
||||
if (rocr)
|
||||
if (rocr && !mmc_host_is_spi(host))
|
||||
*rocr = cmd.resp[0];
|
||||
|
||||
return err;
|
||||
@@ -131,12 +154,12 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
||||
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memcpy(cid, cmd.resp, sizeof(u32) * 4);
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (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;
|
||||
struct mmc_command cmd;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
BUG_ON(!csd);
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!cxd);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SEND_CSD;
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = arg;
|
||||
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
if (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_command cmd;
|
||||
struct mmc_data data;
|
||||
struct scatterlist sg;
|
||||
void *data_buf;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
BUG_ON(!ext_csd);
|
||||
/* dma onto stack is unsafe/nonportable, but callers to this
|
||||
* routine normally provide temporary on-stack buffers ...
|
||||
*/
|
||||
data_buf = kmalloc(len, GFP_KERNEL);
|
||||
if (data_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
@@ -202,28 +231,99 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
|
||||
cmd.opcode = MMC_SEND_EXT_CSD;
|
||||
cmd.opcode = opcode;
|
||||
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.flags = MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
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;
|
||||
if (data.error != MMC_ERR_NONE)
|
||||
if (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)
|
||||
@@ -241,13 +341,13 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
|
||||
(index << 16) |
|
||||
(value << 8) |
|
||||
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);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* NOTE: callers are required to understand the difference
|
||||
* between "native" and SPI format status words!
|
||||
*/
|
||||
if (status)
|
||||
*status = cmd.resp[0];
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status);
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+82
-44
@@ -166,8 +166,6 @@ static int mmc_decode_scr(struct mmc_card *card)
|
||||
unsigned int scr_struct;
|
||||
u32 resp[4];
|
||||
|
||||
BUG_ON(!mmc_card_sd(card));
|
||||
|
||||
resp[3] = card->raw_scr[1];
|
||||
resp[2] = card->raw_scr[0];
|
||||
|
||||
@@ -193,30 +191,38 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||
u8 *status;
|
||||
|
||||
if (card->scr.sda_vsn < SCR_SPEC_VER_1)
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
|
||||
if (!(card->csd.cmdclass & CCC_SWITCH)) {
|
||||
printk(KERN_WARNING "%s: card lacks mandatory switch "
|
||||
"function, performance might suffer.\n",
|
||||
mmc_hostname(card->host));
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = MMC_ERR_FAILED;
|
||||
err = -EIO;
|
||||
|
||||
status = kmalloc(64, GFP_KERNEL);
|
||||
if (!status) {
|
||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
||||
"switch capabilities.\n", mmc_hostname(card->host));
|
||||
return err;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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 "
|
||||
"capabilities, performance might suffer.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = MMC_ERR_NONE;
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -238,28 +244,28 @@ static int mmc_switch_hs(struct mmc_card *card)
|
||||
u8 *status;
|
||||
|
||||
if (card->scr.sda_vsn < SCR_SPEC_VER_1)
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
|
||||
if (!(card->csd.cmdclass & CCC_SWITCH))
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
|
||||
if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED))
|
||||
return MMC_ERR_NONE;
|
||||
return 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);
|
||||
if (!status) {
|
||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
||||
"switch capabilities.\n", mmc_hostname(card->host));
|
||||
return err;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = mmc_sd_switch(card, 1, 0, 1, status);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if ((status[16] & 0xF) != 1) {
|
||||
@@ -292,7 +298,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
unsigned int max_dtr;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
* block-addressed SDHC cards.
|
||||
*/
|
||||
err = mmc_send_if_cond(host, ocr);
|
||||
if (err == MMC_ERR_NONE)
|
||||
if (!err)
|
||||
ocr |= 1 << 30;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch CID from card.
|
||||
*/
|
||||
err = mmc_all_send_cid(host, cid);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (mmc_host_is_spi(host))
|
||||
err = mmc_send_cid(host, cid);
|
||||
else
|
||||
err = mmc_all_send_cid(host, cid);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
if (oldcard) {
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||
err = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
||||
card = oldcard;
|
||||
} else {
|
||||
@@ -333,32 +353,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Allocate card structure.
|
||||
*/
|
||||
card = mmc_alloc_card(host);
|
||||
if (IS_ERR(card))
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto err;
|
||||
}
|
||||
|
||||
card->type = MMC_TYPE_SD;
|
||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Set card RCA.
|
||||
* For native busses: get card RCA and quit open drain mode.
|
||||
*/
|
||||
err = mmc_send_relative_addr(host, &card->rca);
|
||||
if (err != MMC_ERR_NONE)
|
||||
goto free_card;
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
err = mmc_send_relative_addr(host, &card->rca);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
/*
|
||||
* Fetch CSD from card.
|
||||
*/
|
||||
err = mmc_send_csd(card, card->raw_csd);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
err = mmc_decode_csd(card);
|
||||
if (err < 0)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_decode_cid(card);
|
||||
@@ -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.
|
||||
*/
|
||||
err = mmc_select_card(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
goto free_card;
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
err = mmc_select_card(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
if (!oldcard) {
|
||||
/*
|
||||
* Fetch SCR from card.
|
||||
*/
|
||||
err = mmc_app_send_scr(card, card->raw_scr);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
err = mmc_decode_scr(card);
|
||||
@@ -387,7 +413,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Fetch switch information from card.
|
||||
*/
|
||||
err = mmc_read_switch(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
@@ -395,7 +421,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
* Attempt to change to high-speed (if supported)
|
||||
*/
|
||||
err = mmc_switch_hs(card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
/*
|
||||
@@ -418,7 +444,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
if ((host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
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,
|
||||
if (!oldcard)
|
||||
host->card = card;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
|
||||
free_card:
|
||||
if (!oldcard)
|
||||
mmc_remove_card(card);
|
||||
err:
|
||||
|
||||
return MMC_ERR_FAILED;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -483,7 +509,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
if (err != MMC_ERR_NONE) {
|
||||
if (err) {
|
||||
mmc_sd_remove(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
@@ -552,7 +578,8 @@ static void mmc_sd_suspend(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_deselect_cards(host);
|
||||
if (!mmc_host_is_spi(host))
|
||||
mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_release_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);
|
||||
mmc_release_host(host);
|
||||
|
||||
if (err != MMC_ERR_NONE) {
|
||||
if (err) {
|
||||
mmc_sd_remove(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
@@ -608,10 +635,21 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
||||
int err;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->claimed);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
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
|
||||
* support.
|
||||
@@ -644,7 +682,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr)
|
||||
* Detect and init the card.
|
||||
*/
|
||||
err = mmc_sd_init_card(host, host->ocr, NULL);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
mmc_release_host(host);
|
||||
@@ -666,6 +704,6 @@ err:
|
||||
printk(KERN_ERR "%s: error %d whilst initialising SD card\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+71
-36
@@ -33,21 +33,21 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
||||
|
||||
if (card) {
|
||||
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 {
|
||||
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);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Check that card supported application commands */
|
||||
if (!(cmd.resp[0] & R1_APP_CMD))
|
||||
return MMC_ERR_FAILED;
|
||||
if (!mmc_host_is_spi(host) && !(cmd.resp[0] & R1_APP_CMD))
|
||||
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,
|
||||
BUG_ON(!cmd);
|
||||
BUG_ON(retries < 0);
|
||||
|
||||
err = MMC_ERR_INVALID;
|
||||
err = -EIO;
|
||||
|
||||
/*
|
||||
* 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,
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
|
||||
@@ -97,8 +103,14 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
mmc_wait_for_req(host, &mrq);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -127,14 +139,14 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
cmd.arg = SD_BUS_WIDTH_4;
|
||||
break;
|
||||
default:
|
||||
return MMC_ERR_INVALID;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
if (mmc_host_is_spi(host))
|
||||
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
|
||||
else
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
|
||||
for (i = 100; i; i--) {
|
||||
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
|
||||
/* if we're just probing, do a single pass */
|
||||
if (ocr == 0)
|
||||
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);
|
||||
}
|
||||
|
||||
if (rocr)
|
||||
if (rocr && !mmc_host_is_spi(host))
|
||||
*rocr = cmd.resp[0];
|
||||
|
||||
return err;
|
||||
@@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
||||
struct mmc_command cmd;
|
||||
int err;
|
||||
static const u8 test_pattern = 0xAA;
|
||||
u8 result_pattern;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
cmd.opcode = SD_SEND_IF_COND;
|
||||
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);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((cmd.resp[0] & 0xFF) != test_pattern)
|
||||
return MMC_ERR_FAILED;
|
||||
if (mmc_host_is_spi(host))
|
||||
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)
|
||||
@@ -209,12 +240,12 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
||||
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*rca = cmd.resp[0] >> 16;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(!scr);
|
||||
|
||||
/* NOTE: caller guarantees scr is heap-allocated */
|
||||
|
||||
err = mmc_app_cmd(card->host, card);
|
||||
if (err != MMC_ERR_NONE)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
@@ -242,7 +275,7 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||
|
||||
cmd.opcode = SD_APP_SEND_SCR;
|
||||
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.blocks = 1;
|
||||
@@ -252,19 +285,19 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||
|
||||
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);
|
||||
|
||||
if (cmd.error != MMC_ERR_NONE)
|
||||
if (cmd.error)
|
||||
return cmd.error;
|
||||
if (data.error != MMC_ERR_NONE)
|
||||
if (data.error)
|
||||
return data.error;
|
||||
|
||||
scr[0] = ntohl(scr[0]);
|
||||
scr[1] = ntohl(scr[1]);
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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->host);
|
||||
|
||||
/* NOTE: caller guarantees resp is heap-allocated */
|
||||
|
||||
mode = !!mode;
|
||||
value &= 0xF;
|
||||
|
||||
@@ -292,7 +327,7 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||
cmd.arg = mode << 31 | 0x00FFFFFF;
|
||||
cmd.arg &= ~(0xF << (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.blocks = 1;
|
||||
@@ -302,15 +337,15 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||
|
||||
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);
|
||||
|
||||
if (cmd.error != MMC_ERR_NONE)
|
||||
if (cmd.error)
|
||||
return cmd.error;
|
||||
if (data.error != MMC_ERR_NONE)
|
||||
if (data.error)
|
||||
return data.error;
|
||||
|
||||
return MMC_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user