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:
Linus Torvalds 2007-10-11 18:57:31 -07:00
Родитель d2c75f2f4b 019a5f56ec
Коммит 6abd2c860e
52 изменённых файлов: 6260 добавлений и 431 удалений

Просмотреть файл

@ -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 = {
}; };
#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
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
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)
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)
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)
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,8 +243,12 @@ 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
brq.mrq.stop = &brq.stop; * 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; readcmd = MMC_READ_MULTIPLE_BLOCK;
writecmd = MMC_WRITE_MULTIPLE_BLOCK; writecmd = MMC_WRITE_MULTIPLE_BLOCK;
} else { } else {
@ -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)
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)
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);

1158
drivers/mmc/card/sdio_uart.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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 @@
#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,
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,
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)
{ {
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)
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;
} }
printk(KERN_INFO "%s: new %s%s card at address %04x\n", if (mmc_host_is_spi(card->host)) {
mmc_hostname(card->host), printk(KERN_INFO "%s: new %s%s card on SPI\n",
mmc_card_highspeed(card) ? "high speed " : "", mmc_hostname(card->host),
type, card->rca); 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; 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) void mmc_remove_card(struct mmc_card *card)
{ {
if (mmc_card_present(card)) { if (mmc_card_present(card)) {
printk(KERN_INFO "%s: card %04x removed\n", if (mmc_host_is_spi(card->host)) {
mmc_hostname(card->host), card->rca); 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) 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 @@
#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 @@
#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)
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)
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)
"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)
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
{ {
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);
* 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,
* 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,
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,
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)
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);
host->claimed = 1; if (!stop)
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)
{ {
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)
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_DONTCARE; 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.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)
{ {
host->ios.clock = 0; host->ios.clock = 0;
host->ios.vdd = 0; host->ios.vdd = 0;
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; if (!mmc_host_is_spi(host)) {
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
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)
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)
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)
#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)
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
* 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);
}
} }
/*
* ...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 { } 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)
return -ENOMEM; return -ENOMEM;
ret = mmc_register_bus(); ret = mmc_register_bus();
if (ret == 0) { if (ret)
ret = mmc_register_host_class(); goto destroy_workqueue;
if (ret)
mmc_unregister_bus(); 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; 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);
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 @@
#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)
{ {
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)
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)
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)
{ {
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)
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,18 +202,29 @@ 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;
} }
card->ext_csd.sectors = ext_csd_struct = ext_csd[EXT_CSD_REV];
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | if (ext_csd_struct > 2) {
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | "version %d\n", mmc_hostname(card->host),
ext_csd[EXT_CSD_SEC_CNT + 3] << 24; ext_csd_struct);
if (card->ext_csd.sectors) return -EINVAL;
mmc_card_set_blockaddr(card); }
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]) { 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,
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,
/* 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; 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. * Fetch CID from card.
*/ */
err = mmc_all_send_cid(host, cid); if (mmc_host_is_spi(host))
if (err != MMC_ERR_NONE) err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
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,
* 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,
} }
/* /*
* Set card RCA. * For native busses: set card RCA and quit open drain mode.
*/ */
err = mmc_set_relative_addr(card); if (!mmc_host_is_spi(host)) {
if (err != MMC_ERR_NONE) err = mmc_set_relative_addr(card);
goto free_card; if (err)
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.
*/ */
err = mmc_select_card(card); if (!mmc_host_is_spi(host)) {
if (err != MMC_ERR_NONE) err = mmc_select_card(card);
goto free_card; if (err)
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,
(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,
(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,
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)
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,7 +516,8 @@ static void mmc_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_deselect_cards(host); if (!mmc_host_is_spi(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)
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)
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)
* 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 @@ err:
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)
} }
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)
int err; int err;
struct mmc_command cmd; struct mmc_command cmd;
mmc_set_chip_select(host, MMC_CS_HIGH); /*
* Non-SPI hosts need to prevent chipselect going active during
mmc_delay(1); * 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)); 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);
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; 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)); 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)
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)
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)
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)
(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)
memset(&cmd, 0, sizeof(struct mmc_command)); memset(&cmd, 0, sizeof(struct mmc_command));
cmd.opcode = MMC_SEND_STATUS; cmd.opcode = MMC_SEND_STATUS;
cmd.arg = card->rca << 16; if (!mmc_host_is_spi(card->host))
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; 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); 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);
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)
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)
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)
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,
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,
* 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; 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. * Fetch CID from card.
*/ */
err = mmc_all_send_cid(host, cid); if (mmc_host_is_spi(host))
if (err != MMC_ERR_NONE) err = mmc_send_cid(host, cid);
else
err = mmc_all_send_cid(host, cid);
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,
* 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.
*/ */
err = mmc_send_relative_addr(host, &card->rca); if (!mmc_host_is_spi(host)) {
if (err != MMC_ERR_NONE) err = mmc_send_relative_addr(host, &card->rca);
goto free_card; if (err)
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,
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
err = mmc_select_card(card); if (!mmc_host_is_spi(host)) {
if (err != MMC_ERR_NONE) err = mmc_select_card(card);
goto free_card; if (err)
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,
* 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,
* 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,
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,
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)
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,7 +578,8 @@ 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);
mmc_deselect_cards(host); if (!mmc_host_is_spi(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)
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)
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)
* 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 @@ err:
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)
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,
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,
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,8 +103,14 @@ 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; 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; return err;
@ -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)
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;
cmd.arg = ocr; if (mmc_host_is_spi(host))
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; 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--) { 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)
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)
*/ */
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)
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)
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)
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)
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,
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,
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,
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;
} }

395
drivers/mmc/core/sdio.c Normal file
Просмотреть файл

@ -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;
}

270
drivers/mmc/core/sdio_bus.c Normal file
Просмотреть файл

@ -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

346
drivers/mmc/core/sdio_cis.c Normal file
Просмотреть файл

@ -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);
}

Просмотреть файл

@ -0,0 +1,23 @@
/*
* 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

548
drivers/mmc/core/sdio_io.c Normal file
Просмотреть файл

@ -0,0 +1,548 @@
/*
* linux/drivers/mmc/core/sdio_io.c
*
* Copyright 2007 Pierre Ossman
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "sdio_ops.h"
/**
* sdio_claim_host - exclusively claim a bus for a certain SDIO function
* @func: SDIO function that will be accessed
*
* Claim a bus for a set of operations. The SDIO function given
* is used to figure out which bus is relevant.
*/
void sdio_claim_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_claim_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_claim_host);
/**
* sdio_release_host - release a bus for a certain SDIO function
* @func: SDIO function that was accessed
*
* Release a bus, allowing others to claim the bus for their
* operations.
*/
void sdio_release_host(struct sdio_func *func)
{
BUG_ON(!func);
BUG_ON(!func->card);
mmc_release_host(func->card->host);
}
EXPORT_SYMBOL_GPL(sdio_release_host);
/**
* sdio_enable_func - enables a SDIO function for usage
* @func: SDIO function to enable
*
* Powers up and activates a SDIO function so that register
* access is possible.
*/
int sdio_enable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
unsigned long timeout;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Enabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg |= 1 << func->num;
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
/*
* FIXME: This should timeout based on information in the CIS,
* but we don't have card to parse that yet.
*/
timeout = jiffies + HZ;
while (1) {
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, &reg);
if (ret)
goto err;
if (reg & (1 << func->num))
break;
ret = -ETIME;
if (time_after(jiffies, timeout))
goto err;
}
pr_debug("SDIO: Enabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to enable device %s\n", sdio_func_id(func));
return ret;
}
EXPORT_SYMBOL_GPL(sdio_enable_func);
/**
* sdio_disable_func - disable a SDIO function
* @func: SDIO function to disable
*
* Powers down and deactivates a SDIO function. Register access
* to this function will fail until the function is reenabled.
*/
int sdio_disable_func(struct sdio_func *func)
{
int ret;
unsigned char reg;
BUG_ON(!func);
BUG_ON(!func->card);
pr_debug("SDIO: Disabling device %s...\n", sdio_func_id(func));
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, &reg);
if (ret)
goto err;
reg &= ~(1 << func->num);
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
if (ret)
goto err;
pr_debug("SDIO: Disabled device %s\n", sdio_func_id(func));
return 0;
err:
pr_debug("SDIO: Failed to disable device %s\n", sdio_func_id(func));
return -EIO;
}
EXPORT_SYMBOL_GPL(sdio_disable_func);
/**
* sdio_set_block_size - set the block size of an SDIO function
* @func: SDIO function to change
* @blksz: new block size or 0 to use the default.
*
* The default block size is the largest supported by both the function
* and the host, with a maximum of 512 to ensure that arbitrarily sized
* data transfer use the optimal (least) number of commands.
*
* A driver may call this to override the default block size set by the
* core. This can be used to set a block size greater than the maximum
* that reported by the card; it is the driver's responsibility to ensure
* it uses a value that the card supports.
*
* Returns 0 on success, -EINVAL if the host does not support the
* requested block size, or -EIO (etc.) if one of the resultant FBR block
* size register writes failed.
*
*/
int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
{
int ret;
if (blksz > func->card->host->max_blk_size)
return -EINVAL;
if (blksz == 0) {
blksz = min(min(
func->max_blksize,
func->card->host->max_blk_size),
512u);
}
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
blksz & 0xff, NULL);
if (ret)
return ret;
ret = mmc_io_rw_direct(func->card, 1, 0,
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
(blksz >> 8) & 0xff, NULL);
if (ret)
return ret;
func->cur_blksize = blksz;
return 0;
}
EXPORT_SYMBOL_GPL(sdio_set_block_size);
/* Split an arbitrarily sized data transfer into several
* IO_RW_EXTENDED commands. */
static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
unsigned addr, int incr_addr, u8 *buf, unsigned size)
{
unsigned remainder = size;
unsigned max_blocks;
int ret;
/* Do the bulk of the transfer using block mode (if supported). */
if (func->card->cccr.multi_block) {
/* Blocks per command is limited by host count, host transfer
* size (we only use a single sg entry) and the maximum for
* IO_RW_EXTENDED of 511 blocks. */
max_blocks = min(min(
func->card->host->max_blk_count,
func->card->host->max_seg_size / func->cur_blksize),
511u);
while (remainder > func->cur_blksize) {
unsigned blocks;
blocks = remainder / func->cur_blksize;
if (blocks > max_blocks)
blocks = max_blocks;
size = blocks * func->cur_blksize;
ret = mmc_io_rw_extended(func->card, write,
func->num, addr, incr_addr, buf,
blocks, func->cur_blksize);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
}
/* Write the remainder using byte mode. */
while (remainder > 0) {
size = remainder;
if (size > func->cur_blksize)
size = func->cur_blksize;
if (size > 512)
size = 512; /* maximum size for byte mode */
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
incr_addr, buf, 1, size);
if (ret)
return ret;
remainder -= size;
buf += size;
if (incr_addr)
addr += size;
}
return 0;
}
/**
* sdio_readb - read a single byte from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of a given SDIO
* function. If there is a problem reading the address, 0xff
* is returned and @err_ret will contain the error code.
*/
unsigned char sdio_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_readb);
/**
* sdio_writeb - write a single byte to a SDIO function
* @func: SDIO function to access
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
{
int ret;
BUG_ON(!func);
ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writeb);
/**
* sdio_memcpy_fromio - read a chunk of memory from a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address to begin reading from
* @count: number of bytes to read
*
* Reads from the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
unsigned int addr, int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 1, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_fromio);
/**
* sdio_memcpy_toio - write a chunk of memory to a SDIO function
* @func: SDIO function to access
* @addr: address to start writing to
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the address space of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
void *src, int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 1, src, count);
}
EXPORT_SYMBOL_GPL(sdio_memcpy_toio);
/**
* sdio_readsb - read from a FIFO on a SDIO function
* @func: SDIO function to access
* @dst: buffer to store the data
* @addr: address of (single byte) FIFO
* @count: number of bytes to read
*
* Reads from the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_readsb(struct sdio_func *func, void *dst, unsigned int addr,
int count)
{
return sdio_io_rw_ext_helper(func, 0, addr, 0, dst, count);
}
EXPORT_SYMBOL_GPL(sdio_readsb);
/**
* sdio_writesb - write to a FIFO of a SDIO function
* @func: SDIO function to access
* @addr: address of (single byte) FIFO
* @src: buffer that contains the data to write
* @count: number of bytes to write
*
* Writes to the specified FIFO of a given SDIO function. Return
* value indicates if the transfer succeeded or not.
*/
int sdio_writesb(struct sdio_func *func, unsigned int addr, void *src,
int count)
{
return sdio_io_rw_ext_helper(func, 1, addr, 0, src, count);
}
EXPORT_SYMBOL_GPL(sdio_writesb);
/**
* sdio_readw - read a 16 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 16 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address, 0xffff
* is returned and @err_ret will contain the error code.
*/
unsigned short sdio_readw(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 2);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFF;
}
return le16_to_cpu(*(u16*)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readw);
/**
* sdio_writew - write a 16 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 16 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writew(struct sdio_func *func, unsigned short b, unsigned int addr,
int *err_ret)
{
int ret;
*(u16*)func->tmpbuf = cpu_to_le16(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writew);
/**
* sdio_readl - read a 32 bit integer from a SDIO function
* @func: SDIO function to access
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a 32 bit integer from the address space of a given SDIO
* function. If there is a problem reading the address,
* 0xffffffff is returned and @err_ret will contain the error
* code.
*/
unsigned long sdio_readl(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
if (err_ret)
*err_ret = 0;
ret = sdio_memcpy_fromio(func, func->tmpbuf, addr, 4);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFFFFFFFF;
}
return le32_to_cpu(*(u32*)func->tmpbuf);
}
EXPORT_SYMBOL_GPL(sdio_readl);
/**
* sdio_writel - write a 32 bit integer to a SDIO function
* @func: SDIO function to access
* @b: integer to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a 32 bit integer to the address space of a given SDIO
* function. @err_ret will contain the status of the actual
* transfer.
*/
void sdio_writel(struct sdio_func *func, unsigned long b, unsigned int addr,
int *err_ret)
{
int ret;
*(u32*)func->tmpbuf = cpu_to_le32(b);
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_writel);
/**
* sdio_f0_readb - read a single byte from SDIO function 0
* @func: an SDIO function of the card
* @addr: address to read
* @err_ret: optional status value from transfer
*
* Reads a single byte from the address space of SDIO function 0.
* If there is a problem reading the address, 0xff is returned
* and @err_ret will contain the error code.
*/
unsigned char sdio_f0_readb(struct sdio_func *func, unsigned int addr,
int *err_ret)
{
int ret;
unsigned char val;
BUG_ON(!func);
if (err_ret)
*err_ret = 0;
ret = mmc_io_rw_direct(func->card, 0, 0, addr, 0, &val);
if (ret) {
if (err_ret)
*err_ret = ret;
return 0xFF;
}
return val;
}
EXPORT_SYMBOL_GPL(sdio_f0_readb);
/**
* sdio_f0_writeb - write a single byte to SDIO function 0
* @func: an SDIO function of the card
* @b: byte to write
* @addr: address to write to
* @err_ret: optional status value from transfer
*
* Writes a single byte to the address space of SDIO function 0.
* @err_ret will contain the status of the actual transfer.
*
* Only writes to the vendor specific CCCR registers (0xF0 -
* 0xFF) are permiited; @err_ret will be set to -EINVAL for *
* writes outside this range.
*/
void sdio_f0_writeb(struct sdio_func *func, unsigned char b, unsigned int addr,
int *err_ret)
{
int ret;
BUG_ON(!func);
if (addr < 0xF0 || addr > 0xFF) {
if (err_ret)
*err_ret = -EINVAL;
return;
}
ret = mmc_io_rw_direct(func->card, 1, 0, addr, b, NULL);
if (err_ret)
*err_ret = ret;
}
EXPORT_SYMBOL_GPL(sdio_f0_writeb);

267
drivers/mmc/core/sdio_irq.c Normal file
Просмотреть файл

@ -0,0 +1,267 @@
/*
* 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);

176
drivers/mmc/core/sdio_ops.c Normal file
Просмотреть файл

@ -0,0 +1,176 @@
/*
* 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;
}

Просмотреть файл

@ -0,0 +1,22 @@
/*
* 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

Просмотреть файл

@ -35,6 +35,23 @@ config MMC_SDHCI
If unsure, say N. If unsure, say N.
config MMC_RICOH_MMC
tristate "Ricoh MMC Controller Disabler (EXPERIMENTAL)"
depends on PCI && EXPERIMENTAL && MMC_SDHCI
help
This selects the disabler for the Ricoh MMC Controller. This
proprietary controller is unnecessary because the SDHCI driver
supports MMC cards on the SD controller, but if it is not
disabled, it will steal the MMC cards away - rendering them
useless. It is safe to select this driver even if you don't
have a Ricoh based card reader.
To compile this driver as a module, choose M here:
the module will be called ricoh_mmc.
If unsure, say Y.
config MMC_OMAP config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support" tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP depends on ARCH_OMAP
@ -100,3 +117,16 @@ config MMC_TIFM_SD
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called tifm_sd. module will be called tifm_sd.
config MMC_SPI
tristate "MMC/SD over SPI (EXPERIMENTAL)"
depends on MMC && SPI_MASTER && !HIGHMEM && EXPERIMENTAL
select CRC7
select CRC_ITU_T
help
Some systems accss MMC/SD cards using a SPI controller instead of
using a "native" MMC/SD controller. This has a disadvantage of
being relatively high overhead, but a compensating advantage of
working on many systems without dedicated MMC/SD controllers.
If unsure, or if your system has no SPI master driver, say N.

Просмотреть файл

@ -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

Просмотреть файл

@ -328,7 +328,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
data = cmd->data; data = cmd->data;
if (!data) return; if (!data) return;
if (cmd->data->flags & MMC_DATA_MULTI) { if (cmd->data->blocks > 1) {
pr_debug("multiple write : wait for BLKE...\n"); pr_debug("multiple write : wait for BLKE...\n");
at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE);
} else } else
@ -428,6 +428,14 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
} }
if (data) { if (data) {
if ( data->blksz & 0x3 ) {
pr_debug("Unsupported block size\n");
cmd->error = -EINVAL;
mmc_request_done(host->mmc, host->request);
return;
}
block_length = data->blksz; block_length = data->blksz;
blocks = data->blocks; blocks = data->blocks;
@ -439,7 +447,7 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command
if (data->flags & MMC_DATA_STREAM) if (data->flags & MMC_DATA_STREAM)
cmdr |= AT91_MCI_TRTYP_STREAM; cmdr |= AT91_MCI_TRTYP_STREAM;
if (data->flags & MMC_DATA_MULTI) if (data->blocks > 1)
cmdr |= AT91_MCI_TRTYP_MULTIPLE; cmdr |= AT91_MCI_TRTYP_MULTIPLE;
} }
else { else {
@ -577,24 +585,22 @@ static void at91_mci_completed_command(struct at91mci_host *host)
AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE |
AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) {
if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) {
cmd->error = MMC_ERR_NONE; cmd->error = 0;
} }
else { else {
if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE))
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ETIMEDOUT;
else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE))
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE))
cmd->error = MMC_ERR_FIFO;
else else
cmd->error = MMC_ERR_FAILED; cmd->error = -EIO;
pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n",
cmd->error, cmd->opcode, cmd->retries); cmd->error, cmd->opcode, cmd->retries);
} }
} }
else else
cmd->error = MMC_ERR_NONE; cmd->error = 0;
at91_mci_process_next(host); at91_mci_process_next(host);
} }
@ -836,7 +842,6 @@ static int __init at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000; mmc->f_min = 375000;
mmc->f_max = 25000000; mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_BYTEBLOCK;
mmc->max_blk_size = 4095; mmc->max_blk_size = 4095;
mmc->max_blk_count = mmc->max_req_size; mmc->max_blk_count = mmc->max_req_size;

Просмотреть файл

@ -186,7 +186,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
} }
static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
struct mmc_command *cmd, unsigned int flags) struct mmc_command *cmd, struct mmc_data *data)
{ {
u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
@ -208,19 +208,21 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
default: default:
printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", printk(KERN_INFO "au1xmmc: unhandled response type %02x\n",
mmc_resp_type(cmd)); mmc_resp_type(cmd));
return MMC_ERR_INVALID; return -EINVAL;
} }
if (flags & MMC_DATA_READ) { if (data) {
if (flags & MMC_DATA_MULTI) if (flags & MMC_DATA_READ) {
mmccmd |= SD_CMD_CT_4; if (data->blocks > 1)
else mmccmd |= SD_CMD_CT_4;
mmccmd |= SD_CMD_CT_2; else
} else if (flags & MMC_DATA_WRITE) { mmccmd |= SD_CMD_CT_2;
if (flags & MMC_DATA_MULTI) } else if (flags & MMC_DATA_WRITE) {
mmccmd |= SD_CMD_CT_3; if (data->blocks > 1)
else mmccmd |= SD_CMD_CT_3;
mmccmd |= SD_CMD_CT_1; else
mmccmd |= SD_CMD_CT_1;
}
} }
au_writel(cmd->arg, HOST_CMDARG(host)); au_writel(cmd->arg, HOST_CMDARG(host));
@ -253,7 +255,7 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
IRQ_ON(host, SD_CONFIG_CR); IRQ_ON(host, SD_CONFIG_CR);
} }
return MMC_ERR_NONE; return 0;
} }
static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
@ -278,7 +280,7 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
status = au_readl(HOST_STATUS(host)); status = au_readl(HOST_STATUS(host));
data->error = MMC_ERR_NONE; data->error = 0;
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir); dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
/* Process any errors */ /* Process any errors */
@ -288,14 +290,14 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
crc |= ((status & 0x07) == 0x02) ? 0 : 1; crc |= ((status & 0x07) == 0x02) ? 0 : 1;
if (crc) if (crc)
data->error = MMC_ERR_BADCRC; data->error = -EILSEQ;
/* Clear the CRC bits */ /* Clear the CRC bits */
au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host)); au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
data->bytes_xfered = 0; data->bytes_xfered = 0;
if (data->error == MMC_ERR_NONE) { if (!data->error) {
if (host->flags & HOST_F_DMA) { if (host->flags & HOST_F_DMA) {
u32 chan = DMA_CHANNEL(host); u32 chan = DMA_CHANNEL(host);
@ -475,7 +477,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
return; return;
cmd = mrq->cmd; cmd = mrq->cmd;
cmd->error = MMC_ERR_NONE; cmd->error = 0;
if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) { if (cmd->flags & MMC_RSP_136) {
@ -512,11 +514,11 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
/* Figure out errors */ /* Figure out errors */
if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC)) if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
trans = host->flags & (HOST_F_XMIT | HOST_F_RECV); trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
if (!trans || cmd->error != MMC_ERR_NONE) { if (!trans || cmd->error) {
IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF);
tasklet_schedule(&host->finish_task); tasklet_schedule(&host->finish_task);
@ -589,7 +591,7 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
data->sg_len, host->dma.dir); data->sg_len, host->dma.dir);
if (host->dma.len == 0) if (host->dma.len == 0)
return MMC_ERR_TIMEOUT; return -ETIMEDOUT;
au_writel(data->blksz - 1, HOST_BLKSIZE(host)); au_writel(data->blksz - 1, HOST_BLKSIZE(host));
@ -640,11 +642,11 @@ au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data)
//IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF);
} }
return MMC_ERR_NONE; return 0;
dataerr: dataerr:
dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir);
return MMC_ERR_TIMEOUT; return -ETIMEDOUT;
} }
/* static void au1xmmc_request /* static void au1xmmc_request
@ -656,7 +658,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
struct au1xmmc_host *host = mmc_priv(mmc); struct au1xmmc_host *host = mmc_priv(mmc);
unsigned int flags = 0; unsigned int flags = 0;
int ret = MMC_ERR_NONE; int ret = 0;
WARN_ON(irqs_disabled()); WARN_ON(irqs_disabled());
WARN_ON(host->status != HOST_S_IDLE); WARN_ON(host->status != HOST_S_IDLE);
@ -672,10 +674,10 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
ret = au1xmmc_prepare_data(host, mrq->data); ret = au1xmmc_prepare_data(host, mrq->data);
} }
if (ret == MMC_ERR_NONE) if (!ret)
ret = au1xmmc_send_command(host, 0, mrq->cmd, flags); ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
if (ret != MMC_ERR_NONE) { if (ret) {
mrq->cmd->error = ret; mrq->cmd->error = ret;
au1xmmc_finish_request(host); au1xmmc_finish_request(host);
} }
@ -764,10 +766,10 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id)
if (host->mrq && (status & STATUS_TIMEOUT)) { if (host->mrq && (status & STATUS_TIMEOUT)) {
if (status & SD_STATUS_RAT) if (status & SD_STATUS_RAT)
host->mrq->cmd->error = MMC_ERR_TIMEOUT; host->mrq->cmd->error = -ETIMEDOUT;
else if (status & SD_STATUS_DT) else if (status & SD_STATUS_DT)
host->mrq->data->error = MMC_ERR_TIMEOUT; host->mrq->data->error = -ETIMEDOUT;
/* In PIO mode, interrupts might still be enabled */ /* In PIO mode, interrupts might still be enabled */
IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);

Просмотреть файл

@ -428,11 +428,11 @@ static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat)
if ( stat & STATUS_ERR_MASK ) { if ( stat & STATUS_ERR_MASK ) {
dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat); dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat);
if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR)) if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR))
data->error = MMC_ERR_BADCRC; data->error = -EILSEQ;
else if(stat & STATUS_TIME_OUT_READ) else if(stat & STATUS_TIME_OUT_READ)
data->error = MMC_ERR_TIMEOUT; data->error = -ETIMEDOUT;
else else
data->error = MMC_ERR_FAILED; data->error = -EIO;
} else { } else {
data->bytes_xfered = host->dma_size; data->bytes_xfered = host->dma_size;
} }
@ -458,10 +458,10 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
if (stat & STATUS_TIME_OUT_RESP) { if (stat & STATUS_TIME_OUT_RESP) {
dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n");
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ETIMEDOUT;
} else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); dev_dbg(mmc_dev(host->mmc), "cmd crc error\n");
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
} }
if(cmd->flags & MMC_RSP_PRESENT) { if(cmd->flags & MMC_RSP_PRESENT) {
@ -482,7 +482,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n", dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n",
cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error);
if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) { if (data && !cmd->error && !(stat & STATUS_ERR_MASK)) {
if (host->req->data->flags & MMC_DATA_WRITE) { if (host->req->data->flags & MMC_DATA_WRITE) {
/* Wait for FIFO to be empty before starting DMA write */ /* Wait for FIFO to be empty before starting DMA write */
@ -491,7 +491,7 @@ static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat)
if(imxmci_busy_wait_for_status(host, &stat, if(imxmci_busy_wait_for_status(host, &stat,
STATUS_APPL_BUFF_FE, STATUS_APPL_BUFF_FE,
40, "imxmci_cmd_done DMA WR") < 0) { 40, "imxmci_cmd_done DMA WR") < 0) {
cmd->error = MMC_ERR_FIFO; cmd->error = -EIO;
imxmci_finish_data(host, stat); imxmci_finish_data(host, stat);
if(host->req) if(host->req)
imxmci_finish_request(host, host->req); imxmci_finish_request(host, host->req);
@ -884,9 +884,21 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} }
} }
static int imxmci_get_ro(struct mmc_host *mmc)
{
struct imxmci_host *host = mmc_priv(mmc);
if (host->pdata && host->pdata->get_ro)
return host->pdata->get_ro(mmc_dev(mmc));
/* Host doesn't support read only detection so assume writeable */
return 0;
}
static const struct mmc_host_ops imxmci_ops = { static const struct mmc_host_ops imxmci_ops = {
.request = imxmci_request, .request = imxmci_request,
.set_ios = imxmci_set_ios, .set_ios = imxmci_set_ios,
.get_ro = imxmci_get_ro,
}; };
static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr)
@ -913,7 +925,7 @@ static void imxmci_check_status(unsigned long data)
{ {
struct imxmci_host *host = (struct imxmci_host *)data; struct imxmci_host *host = (struct imxmci_host *)data;
if( host->pdata->card_present() != host->present ) { if( host->pdata->card_present(mmc_dev(host->mmc)) != host->present ) {
host->present ^= 1; host->present ^= 1;
dev_info(mmc_dev(host->mmc), "card %s\n", dev_info(mmc_dev(host->mmc), "card %s\n",
host->present ? "inserted" : "removed"); host->present ? "inserted" : "removed");
@ -963,7 +975,7 @@ static int imxmci_probe(struct platform_device *pdev)
mmc->f_min = 150000; mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2; mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33; mmc->ocr_avail = MMC_VDD_32_33;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; mmc->caps = MMC_CAP_4_BIT_DATA;
/* MMC core transfer sizes tunable parameters */ /* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64; mmc->max_hw_segs = 64;
@ -1022,7 +1034,7 @@ static int imxmci_probe(struct platform_device *pdev)
if (ret) if (ret)
goto out; goto out;
host->present = host->pdata->card_present(); host->present = host->pdata->card_present(mmc_dev(mmc));
init_timer(&host->timer); init_timer(&host->timer);
host->timer.data = (unsigned long)host; host->timer.data = (unsigned long)host;
host->timer.function = imxmci_check_status; host->timer.function = imxmci_check_status;

1408
drivers/mmc/host/mmc_spi.c Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -16,6 +16,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/log2.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/amba/bus.h> #include <linux/amba/bus.h>
#include <linux/clk.h> #include <linux/clk.h>
@ -154,11 +155,11 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
} }
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
if (status & MCI_DATACRCFAIL) if (status & MCI_DATACRCFAIL)
data->error = MMC_ERR_BADCRC; data->error = -EILSEQ;
else if (status & MCI_DATATIMEOUT) else if (status & MCI_DATATIMEOUT)
data->error = MMC_ERR_TIMEOUT; data->error = -ETIMEDOUT;
else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN))
data->error = MMC_ERR_FIFO; data->error = -EIO;
status |= MCI_DATAEND; status |= MCI_DATAEND;
/* /*
@ -193,12 +194,12 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
cmd->resp[3] = readl(base + MMCIRESPONSE3); cmd->resp[3] = readl(base + MMCIRESPONSE3);
if (status & MCI_CMDTIMEOUT) { if (status & MCI_CMDTIMEOUT) {
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ETIMEDOUT;
} else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) {
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
} }
if (!cmd->data || cmd->error != MMC_ERR_NONE) { if (!cmd->data || cmd->error) {
if (host->data) if (host->data)
mmci_stop_data(host); mmci_stop_data(host);
mmci_request_end(host, cmd->mrq); mmci_request_end(host, cmd->mrq);
@ -391,6 +392,14 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
WARN_ON(host->mrq != NULL); WARN_ON(host->mrq != NULL);
if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
mmc_hostname(mmc), mrq->data->blksz);
mrq->cmd->error = -EINVAL;
mmc_request_done(mmc, mrq);
return;
}
spin_lock_irq(&host->lock); spin_lock_irq(&host->lock);
host->mrq = mrq; host->mrq = mrq;

Просмотреть файл

@ -263,7 +263,7 @@ mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
enum dma_data_direction dma_data_dir; enum dma_data_direction dma_data_dir;
BUG_ON(host->dma_ch < 0); BUG_ON(host->dma_ch < 0);
if (data->error != MMC_ERR_NONE) if (data->error)
omap_stop_dma(host->dma_ch); omap_stop_dma(host->dma_ch);
/* Release DMA channel lazily */ /* Release DMA channel lazily */
mod_timer(&host->dma_timer, jiffies + HZ); mod_timer(&host->dma_timer, jiffies + HZ);
@ -368,7 +368,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
} }
} }
if (host->data == NULL || cmd->error != MMC_ERR_NONE) { if (host->data == NULL || cmd->error) {
host->mrq = NULL; host->mrq = NULL;
clk_disable(host->fclk); clk_disable(host->fclk);
mmc_request_done(host->mmc, cmd->mrq); mmc_request_done(host->mmc, cmd->mrq);
@ -475,14 +475,14 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
if (status & OMAP_MMC_STAT_DATA_TOUT) { if (status & OMAP_MMC_STAT_DATA_TOUT) {
dev_dbg(mmc_dev(host->mmc), "data timeout\n"); dev_dbg(mmc_dev(host->mmc), "data timeout\n");
if (host->data) { if (host->data) {
host->data->error |= MMC_ERR_TIMEOUT; host->data->error = -ETIMEDOUT;
transfer_error = 1; transfer_error = 1;
} }
} }
if (status & OMAP_MMC_STAT_DATA_CRC) { if (status & OMAP_MMC_STAT_DATA_CRC) {
if (host->data) { if (host->data) {
host->data->error |= MMC_ERR_BADCRC; host->data->error = -EILSEQ;
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
"data CRC error, bytes left %d\n", "data CRC error, bytes left %d\n",
host->total_bytes_left); host->total_bytes_left);
@ -504,7 +504,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"command timeout, CMD %d\n", "command timeout, CMD %d\n",
host->cmd->opcode); host->cmd->opcode);
host->cmd->error = MMC_ERR_TIMEOUT; host->cmd->error = -ETIMEDOUT;
end_command = 1; end_command = 1;
} }
} }
@ -514,7 +514,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"command CRC error (CMD%d, arg 0x%08x)\n", "command CRC error (CMD%d, arg 0x%08x)\n",
host->cmd->opcode, host->cmd->arg); host->cmd->opcode, host->cmd->arg);
host->cmd->error = MMC_ERR_BADCRC; host->cmd->error = -EILSEQ;
end_command = 1; end_command = 1;
} else } else
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),

Просмотреть файл

@ -142,6 +142,10 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->dma_dir); host->dma_dir);
for (i = 0; i < host->dma_len; i++) { for (i = 0; i < host->dma_len; i++) {
unsigned int length = sg_dma_len(&data->sg[i]);
host->sg_cpu[i].dcmd = dcmd | length;
if (length & 31 && !(data->flags & MMC_DATA_READ))
host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
@ -149,7 +153,6 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
} }
host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
sizeof(struct pxa_dma_desc); sizeof(struct pxa_dma_desc);
} }
@ -226,7 +229,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
} }
if (stat & STAT_TIME_OUT_RESPONSE) { if (stat & STAT_TIME_OUT_RESPONSE) {
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ETIMEDOUT;
} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
#ifdef CONFIG_PXA27x #ifdef CONFIG_PXA27x
/* /*
@ -239,11 +242,11 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode); pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode);
} else } else
#endif #endif
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
} }
pxamci_disable_irq(host, END_CMD_RES); pxamci_disable_irq(host, END_CMD_RES);
if (host->data && cmd->error == MMC_ERR_NONE) { if (host->data && !cmd->error) {
pxamci_enable_irq(host, DATA_TRAN_DONE); pxamci_enable_irq(host, DATA_TRAN_DONE);
} else { } else {
pxamci_finish_request(host, host->mrq); pxamci_finish_request(host, host->mrq);
@ -264,9 +267,9 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
host->dma_dir); host->dma_dir);
if (stat & STAT_READ_TIME_OUT) if (stat & STAT_READ_TIME_OUT)
data->error = MMC_ERR_TIMEOUT; data->error = -ETIMEDOUT;
else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR))
data->error = MMC_ERR_BADCRC; data->error = -EILSEQ;
/* /*
* There appears to be a hardware design bug here. There seems to * There appears to be a hardware design bug here. There seems to
@ -274,7 +277,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
* This means that if there was an error on any block, we mark all * This means that if there was an error on any block, we mark all
* data blocks as being in error. * data blocks as being in error.
*/ */
if (data->error == MMC_ERR_NONE) if (!data->error)
data->bytes_xfered = data->blocks * data->blksz; data->bytes_xfered = data->blocks * data->blksz;
else else
data->bytes_xfered = 0; data->bytes_xfered = 0;
@ -284,7 +287,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
host->data = NULL; host->data = NULL;
if (host->mrq->stop) { if (host->mrq->stop) {
pxamci_stop_clock(host); pxamci_stop_clock(host);
pxamci_start_cmd(host, host->mrq->stop, 0); pxamci_start_cmd(host, host->mrq->stop, host->cmdat);
} else { } else {
pxamci_finish_request(host, host->mrq); pxamci_finish_request(host, host->mrq);
} }
@ -298,7 +301,7 @@ static irqreturn_t pxamci_irq(int irq, void *devid)
unsigned int ireg; unsigned int ireg;
int handled = 0; int handled = 0;
ireg = readl(host->base + MMC_I_REG); ireg = readl(host->base + MMC_I_REG) & ~readl(host->base + MMC_I_MASK);
if (ireg) { if (ireg) {
unsigned stat = readl(host->base + MMC_STAT); unsigned stat = readl(host->base + MMC_STAT);
@ -309,6 +312,10 @@ static irqreturn_t pxamci_irq(int irq, void *devid)
handled |= pxamci_cmd_done(host, stat); handled |= pxamci_cmd_done(host, stat);
if (ireg & DATA_TRAN_DONE) if (ireg & DATA_TRAN_DONE)
handled |= pxamci_data_done(host, stat); handled |= pxamci_data_done(host, stat);
if (ireg & SDIO_INT) {
mmc_signal_sdio_irq(host->mmc);
handled = 1;
}
} }
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
@ -382,20 +389,46 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->cmdat |= CMDAT_INIT; host->cmdat |= CMDAT_INIT;
} }
if (ios->bus_width == MMC_BUS_WIDTH_4)
host->cmdat |= CMDAT_SD_4DAT;
else
host->cmdat &= ~CMDAT_SD_4DAT;
pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", pr_debug("PXAMCI: clkrt = %x cmdat = %x\n",
host->clkrt, host->cmdat); host->clkrt, host->cmdat);
} }
static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable)
{
struct pxamci_host *pxa_host = mmc_priv(host);
if (enable)
pxamci_enable_irq(pxa_host, SDIO_INT);
else
pxamci_disable_irq(pxa_host, SDIO_INT);
}
static const struct mmc_host_ops pxamci_ops = { static const struct mmc_host_ops pxamci_ops = {
.request = pxamci_request, .request = pxamci_request,
.get_ro = pxamci_get_ro, .get_ro = pxamci_get_ro,
.set_ios = pxamci_set_ios, .set_ios = pxamci_set_ios,
.enable_sdio_irq = pxamci_enable_sdio_irq,
}; };
static void pxamci_dma_irq(int dma, void *devid) static void pxamci_dma_irq(int dma, void *devid)
{ {
printk(KERN_ERR "DMA%d: IRQ???\n", dma); struct pxamci_host *host = devid;
DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; int dcsr = DCSR(dma);
DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
if (dcsr & DCSR_ENDINTR) {
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
} else {
printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
mmc_hostname(host->mmc), dma, dcsr);
host->data->error = -EIO;
pxamci_data_done(host, 0);
}
} }
static irqreturn_t pxamci_detect_irq(int irq, void *devid) static irqreturn_t pxamci_detect_irq(int irq, void *devid)
@ -444,9 +477,9 @@ static int pxamci_probe(struct platform_device *pdev)
mmc->max_seg_size = PAGE_SIZE; mmc->max_seg_size = PAGE_SIZE;
/* /*
* Block length register is 10 bits. * Block length register is only 10 bits before PXA27x.
*/ */
mmc->max_blk_size = 1023; mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048;
/* /*
* Block count register is 16 bits. * Block count register is 16 bits.
@ -460,6 +493,12 @@ static int pxamci_probe(struct platform_device *pdev)
mmc->ocr_avail = host->pdata ? mmc->ocr_avail = host->pdata ?
host->pdata->ocr_mask : host->pdata->ocr_mask :
MMC_VDD_32_33|MMC_VDD_33_34; MMC_VDD_32_33|MMC_VDD_33_34;
mmc->caps = 0;
host->cmdat = 0;
if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) {
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
host->cmdat |= CMDAT_SDIO_INT_EN;
}
host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
if (!host->sg_cpu) { if (!host->sg_cpu) {

Просмотреть файл

@ -25,6 +25,8 @@
#define SPI_EN (1 << 0) #define SPI_EN (1 << 0)
#define MMC_CMDAT 0x0010 #define MMC_CMDAT 0x0010
#define CMDAT_SDIO_INT_EN (1 << 11)
#define CMDAT_SD_4DAT (1 << 8)
#define CMDAT_DMAEN (1 << 7) #define CMDAT_DMAEN (1 << 7)
#define CMDAT_INIT (1 << 6) #define CMDAT_INIT (1 << 6)
#define CMDAT_BUSY (1 << 5) #define CMDAT_BUSY (1 << 5)

Просмотреть файл

@ -0,0 +1,151 @@
/*
* ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
*
* Copyright (C) 2007 Philip Langdale, All Rights Reserved.
*
* 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.
*/
/*
* This is a conceptually ridiculous driver, but it is required by the way
* the Ricoh multi-function R5C832 works. This chip implements firewire
* and four different memory card controllers. Two of those controllers are
* an SDHCI controller and a proprietary MMC controller. The linux SDHCI
* driver supports MMC cards but the chip detects MMC cards in hardware
* and directs them to the MMC controller - so the SDHCI driver never sees
* them. To get around this, we must disable the useless MMC controller.
* At that point, the SDHCI controller will start seeing them. As a bonus,
* a detection event occurs immediately, even if the MMC card is already
* in the reader.
*
* The relevant registers live on the firewire function, so this is unavoidably
* ugly. Such is life.
*/
#include <linux/pci.h>
#define DRIVER_NAME "ricoh-mmc"
static const struct pci_device_id pci_ids[] __devinitdata = {
{
.vendor = PCI_VENDOR_ID_RICOH,
.device = PCI_DEVICE_ID_RICOH_R5C843,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(pci, pci_ids);
static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
u8 rev;
struct pci_dev *fw_dev = NULL;
BUG_ON(pdev == NULL);
BUG_ON(ent == NULL);
pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
printk(KERN_INFO DRIVER_NAME
": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
(int)rev);
while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
pdev->bus == fw_dev->bus) {
u8 write_enable;
u8 disable;
pci_read_config_byte(fw_dev, 0xCB, &disable);
if (disable & 0x02) {
printk(KERN_INFO DRIVER_NAME
": Controller already disabled. Nothing to do.\n");
return -ENODEV;
}
pci_read_config_byte(fw_dev, 0xCA, &write_enable);
pci_write_config_byte(fw_dev, 0xCA, 0x57);
pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
pci_write_config_byte(fw_dev, 0xCA, write_enable);
pci_set_drvdata(pdev, fw_dev);
printk(KERN_INFO DRIVER_NAME
": Controller is now disabled.\n");
break;
}
}
if (pci_get_drvdata(pdev) == NULL) {
printk(KERN_WARNING DRIVER_NAME
": Main firewire function not found. Cannot disable controller.\n");
return -ENODEV;
}
return 0;
}
static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
{
u8 write_enable;
u8 disable;
struct pci_dev *fw_dev = NULL;
fw_dev = pci_get_drvdata(pdev);
BUG_ON(fw_dev == NULL);
pci_read_config_byte(fw_dev, 0xCA, &write_enable);
pci_read_config_byte(fw_dev, 0xCB, &disable);
pci_write_config_byte(fw_dev, 0xCA, 0x57);
pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
pci_write_config_byte(fw_dev, 0xCA, write_enable);
printk(KERN_INFO DRIVER_NAME
": Controller is now re-enabled.\n");
pci_set_drvdata(pdev, NULL);
}
static struct pci_driver ricoh_mmc_driver = {
.name = DRIVER_NAME,
.id_table = pci_ids,
.probe = ricoh_mmc_probe,
.remove = __devexit_p(ricoh_mmc_remove),
};
/*****************************************************************************\
* *
* Driver init/exit *
* *
\*****************************************************************************/
static int __init ricoh_mmc_drv_init(void)
{
printk(KERN_INFO DRIVER_NAME
": Ricoh MMC Controller disabling driver\n");
printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
return pci_register_driver(&ricoh_mmc_driver);
}
static void __exit ricoh_mmc_drv_exit(void)
{
pci_unregister_driver(&ricoh_mmc_driver);
}
module_init(ricoh_mmc_drv_init);
module_exit(ricoh_mmc_drv_exit);
MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
MODULE_LICENSE("GPL");

Просмотреть файл

@ -25,8 +25,6 @@
#define DBG(f, x...) \ #define DBG(f, x...) \
pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
static unsigned int debug_nodma = 0;
static unsigned int debug_forcedma = 0;
static unsigned int debug_quirks = 0; static unsigned int debug_quirks = 0;
#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0)
@ -35,6 +33,7 @@ static unsigned int debug_quirks = 0;
#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) #define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2)
#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) #define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3)
#define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4) #define SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS (1<<4)
#define SDHCI_QUIRK_BROKEN_DMA (1<<5)
static const struct pci_device_id pci_ids[] __devinitdata = { static const struct pci_device_id pci_ids[] __devinitdata = {
{ {
@ -68,7 +67,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_ENE_CB712_SD, .device = PCI_DEVICE_ID_ENE_CB712_SD,
.subvendor = PCI_ANY_ID, .subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
}, },
{ {
@ -76,7 +76,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = {
.device = PCI_DEVICE_ID_ENE_CB712_SD_2, .device = PCI_DEVICE_ID_ENE_CB712_SD_2,
.subvendor = PCI_ANY_ID, .subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID, .subdevice = PCI_ANY_ID,
.driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_BROKEN_DMA,
}, },
{ {
@ -132,7 +133,7 @@ static void sdhci_dumpregs(struct sdhci_host *host)
readb(host->ioaddr + SDHCI_POWER_CONTROL), readb(host->ioaddr + SDHCI_POWER_CONTROL),
readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL)); readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n",
readb(host->ioaddr + SDHCI_WALK_UP_CONTROL), readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL),
readw(host->ioaddr + SDHCI_CLOCK_CONTROL)); readw(host->ioaddr + SDHCI_CLOCK_CONTROL));
printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n",
readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL), readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL),
@ -481,16 +482,16 @@ static void sdhci_finish_data(struct sdhci_host *host)
* Controller doesn't count down when in single block mode. * Controller doesn't count down when in single block mode.
*/ */
if (data->blocks == 1) if (data->blocks == 1)
blocks = (data->error == MMC_ERR_NONE) ? 0 : 1; blocks = (data->error == 0) ? 0 : 1;
else else
blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT);
data->bytes_xfered = data->blksz * (data->blocks - blocks); data->bytes_xfered = data->blksz * (data->blocks - blocks);
if ((data->error == MMC_ERR_NONE) && blocks) { if (!data->error && blocks) {
printk(KERN_ERR "%s: Controller signalled completion even " printk(KERN_ERR "%s: Controller signalled completion even "
"though there were blocks left.\n", "though there were blocks left.\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
data->error = MMC_ERR_FAILED; data->error = -EIO;
} }
if (data->stop) { if (data->stop) {
@ -498,7 +499,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
* The controller needs a reset of internal state machines * The controller needs a reset of internal state machines
* upon error conditions. * upon error conditions.
*/ */
if (data->error != MMC_ERR_NONE) { if (data->error) {
sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA); sdhci_reset(host, SDHCI_RESET_DATA);
} }
@ -533,7 +534,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
printk(KERN_ERR "%s: Controller never released " printk(KERN_ERR "%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc)); "inhibit bit(s).\n", mmc_hostname(host->mmc));
sdhci_dumpregs(host); sdhci_dumpregs(host);
cmd->error = MMC_ERR_FAILED; cmd->error = -EIO;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
return; return;
} }
@ -554,7 +555,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
printk(KERN_ERR "%s: Unsupported response type!\n", printk(KERN_ERR "%s: Unsupported response type!\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
cmd->error = MMC_ERR_INVALID; cmd->error = -EINVAL;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
return; return;
} }
@ -601,7 +602,7 @@ static void sdhci_finish_command(struct sdhci_host *host)
} }
} }
host->cmd->error = MMC_ERR_NONE; host->cmd->error = 0;
if (host->data && host->data_early) if (host->data && host->data_early)
sdhci_finish_data(host); sdhci_finish_data(host);
@ -722,7 +723,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq; host->mrq = mrq;
if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
host->mrq->cmd->error = MMC_ERR_TIMEOUT; host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} else } else
sdhci_send_command(host, mrq->cmd); sdhci_send_command(host, mrq->cmd);
@ -800,10 +801,35 @@ static int sdhci_get_ro(struct mmc_host *mmc)
return !(present & SDHCI_WRITE_PROTECT); return !(present & SDHCI_WRITE_PROTECT);
} }
static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
{
struct sdhci_host *host;
unsigned long flags;
u32 ier;
host = mmc_priv(mmc);
spin_lock_irqsave(&host->lock, flags);
ier = readl(host->ioaddr + SDHCI_INT_ENABLE);
ier &= ~SDHCI_INT_CARD_INT;
if (enable)
ier |= SDHCI_INT_CARD_INT;
writel(ier, host->ioaddr + SDHCI_INT_ENABLE);
writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
static const struct mmc_host_ops sdhci_ops = { static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request, .request = sdhci_request,
.set_ios = sdhci_set_ios, .set_ios = sdhci_set_ios,
.get_ro = sdhci_get_ro, .get_ro = sdhci_get_ro,
.enable_sdio_irq = sdhci_enable_sdio_irq,
}; };
/*****************************************************************************\ /*****************************************************************************\
@ -831,7 +857,7 @@ static void sdhci_tasklet_card(unsigned long param)
sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_CMD);
sdhci_reset(host, SDHCI_RESET_DATA); sdhci_reset(host, SDHCI_RESET_DATA);
host->mrq->cmd->error = MMC_ERR_FAILED; host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} }
} }
@ -859,9 +885,9 @@ static void sdhci_tasklet_finish(unsigned long param)
* The controller needs a reset of internal state machines * The controller needs a reset of internal state machines
* upon error conditions. * upon error conditions.
*/ */
if ((mrq->cmd->error != MMC_ERR_NONE) || if (mrq->cmd->error ||
(mrq->data && ((mrq->data->error != MMC_ERR_NONE) || (mrq->data && (mrq->data->error ||
(mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) { (mrq->data->stop && mrq->data->stop->error)))) {
/* Some controllers need this kick or reset won't work here */ /* Some controllers need this kick or reset won't work here */
if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) {
@ -906,13 +932,13 @@ static void sdhci_timeout_timer(unsigned long data)
sdhci_dumpregs(host); sdhci_dumpregs(host);
if (host->data) { if (host->data) {
host->data->error = MMC_ERR_TIMEOUT; host->data->error = -ETIMEDOUT;
sdhci_finish_data(host); sdhci_finish_data(host);
} else { } else {
if (host->cmd) if (host->cmd)
host->cmd->error = MMC_ERR_TIMEOUT; host->cmd->error = -ETIMEDOUT;
else else
host->mrq->cmd->error = MMC_ERR_TIMEOUT; host->mrq->cmd->error = -ETIMEDOUT;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} }
@ -941,13 +967,12 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
} }
if (intmask & SDHCI_INT_TIMEOUT) if (intmask & SDHCI_INT_TIMEOUT)
host->cmd->error = MMC_ERR_TIMEOUT; host->cmd->error = -ETIMEDOUT;
else if (intmask & SDHCI_INT_CRC) else if (intmask & (SDHCI_INT_CRC | SDHCI_INT_END_BIT |
host->cmd->error = MMC_ERR_BADCRC; SDHCI_INT_INDEX))
else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) host->cmd->error = -EILSEQ;
host->cmd->error = MMC_ERR_FAILED;
if (host->cmd->error != MMC_ERR_NONE) if (host->cmd->error)
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
else if (intmask & SDHCI_INT_RESPONSE) else if (intmask & SDHCI_INT_RESPONSE)
sdhci_finish_command(host); sdhci_finish_command(host);
@ -974,13 +999,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
} }
if (intmask & SDHCI_INT_DATA_TIMEOUT) if (intmask & SDHCI_INT_DATA_TIMEOUT)
host->data->error = MMC_ERR_TIMEOUT; host->data->error = -ETIMEDOUT;
else if (intmask & SDHCI_INT_DATA_CRC) else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT))
host->data->error = MMC_ERR_BADCRC; host->data->error = -EILSEQ;
else if (intmask & SDHCI_INT_DATA_END_BIT)
host->data->error = MMC_ERR_FAILED;
if (host->data->error != MMC_ERR_NONE) if (host->data->error)
sdhci_finish_data(host); sdhci_finish_data(host);
else { else {
if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL))
@ -1015,6 +1038,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
irqreturn_t result; irqreturn_t result;
struct sdhci_host* host = dev_id; struct sdhci_host* host = dev_id;
u32 intmask; u32 intmask;
int cardint = 0;
spin_lock(&host->lock); spin_lock(&host->lock);
@ -1059,6 +1083,11 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
intmask &= ~SDHCI_INT_BUS_POWER; intmask &= ~SDHCI_INT_BUS_POWER;
if (intmask & SDHCI_INT_CARD_INT)
cardint = 1;
intmask &= ~SDHCI_INT_CARD_INT;
if (intmask) { if (intmask) {
printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), intmask); mmc_hostname(host->mmc), intmask);
@ -1073,6 +1102,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
out: out:
spin_unlock(&host->lock); spin_unlock(&host->lock);
/*
* We have to delay this as it calls back into the driver.
*/
if (cardint)
mmc_signal_sdio_irq(host->mmc);
return result; return result;
} }
@ -1258,20 +1293,26 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
caps = readl(host->ioaddr + SDHCI_CAPABILITIES); caps = readl(host->ioaddr + SDHCI_CAPABILITIES);
if (debug_nodma) if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
DBG("DMA forced off\n");
else if (debug_forcedma) {
DBG("DMA forced on\n");
host->flags |= SDHCI_USE_DMA; host->flags |= SDHCI_USE_DMA;
} else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA)
host->flags |= SDHCI_USE_DMA;
else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA)
DBG("Controller doesn't have DMA interface\n");
else if (!(caps & SDHCI_CAN_DO_DMA)) else if (!(caps & SDHCI_CAN_DO_DMA))
DBG("Controller doesn't have DMA capability\n"); DBG("Controller doesn't have DMA capability\n");
else else
host->flags |= SDHCI_USE_DMA; host->flags |= SDHCI_USE_DMA;
if ((chip->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
(host->flags & SDHCI_USE_DMA)) {
DBG("Disabling DMA as it is marked broken");
host->flags &= ~SDHCI_USE_DMA;
}
if (((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) &&
(host->flags & SDHCI_USE_DMA)) {
printk(KERN_WARNING "%s: Will use DMA "
"mode even though HW doesn't fully "
"claim to support it.\n", host->slot_descr);
}
if (host->flags & SDHCI_USE_DMA) { if (host->flags & SDHCI_USE_DMA) {
if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) {
printk(KERN_WARNING "%s: No suitable DMA available. " printk(KERN_WARNING "%s: No suitable DMA available. "
@ -1312,7 +1353,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
mmc->ops = &sdhci_ops; mmc->ops = &sdhci_ops;
mmc->f_min = host->max_clk / 256; mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk; mmc->f_max = host->max_clk;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_SDIO_IRQ;
if (caps & SDHCI_CAN_DO_HISPD) if (caps & SDHCI_CAN_DO_HISPD)
mmc->caps |= MMC_CAP_SD_HIGHSPEED; mmc->caps |= MMC_CAP_SD_HIGHSPEED;
@ -1565,14 +1606,10 @@ static void __exit sdhci_drv_exit(void)
module_init(sdhci_drv_init); module_init(sdhci_drv_init);
module_exit(sdhci_drv_exit); module_exit(sdhci_drv_exit);
module_param(debug_nodma, uint, 0444);
module_param(debug_forcedma, uint, 0444);
module_param(debug_quirks, uint, 0444); module_param(debug_quirks, uint, 0444);
MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>"); MODULE_AUTHOR("Pierre Ossman <drzeus@drzeus.cx>");
MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)");
MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)");
MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); MODULE_PARM_DESC(debug_quirks, "Force certain quirks.");

Просмотреть файл

@ -81,7 +81,7 @@
#define SDHCI_BLOCK_GAP_CONTROL 0x2A #define SDHCI_BLOCK_GAP_CONTROL 0x2A
#define SDHCI_WALK_UP_CONTROL 0x2B #define SDHCI_WAKE_UP_CONTROL 0x2B
#define SDHCI_CLOCK_CONTROL 0x2C #define SDHCI_CLOCK_CONTROL 0x2C
#define SDHCI_DIVIDER_SHIFT 8 #define SDHCI_DIVIDER_SHIFT 8

Просмотреть файл

@ -16,6 +16,7 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/log2.h>
#include <asm/io.h> #include <asm/io.h>
#define DRIVER_NAME "tifm_sd" #define DRIVER_NAME "tifm_sd"
@ -404,14 +405,14 @@ static void tifm_sd_check_status(struct tifm_sd *host)
struct tifm_dev *sock = host->dev; struct tifm_dev *sock = host->dev;
struct mmc_command *cmd = host->req->cmd; struct mmc_command *cmd = host->req->cmd;
if (cmd->error != MMC_ERR_NONE) if (cmd->error)
goto finish_request; goto finish_request;
if (!(host->cmd_flags & CMD_READY)) if (!(host->cmd_flags & CMD_READY))
return; return;
if (cmd->data) { if (cmd->data) {
if (cmd->data->error != MMC_ERR_NONE) { if (cmd->data->error) {
if ((host->cmd_flags & SCMD_ACTIVE) if ((host->cmd_flags & SCMD_ACTIVE)
&& !(host->cmd_flags & SCMD_READY)) && !(host->cmd_flags & SCMD_READY))
return; return;
@ -504,7 +505,7 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
{ {
struct tifm_sd *host; struct tifm_sd *host;
unsigned int host_status = 0; unsigned int host_status = 0;
int cmd_error = MMC_ERR_NONE; int cmd_error = 0;
struct mmc_command *cmd = NULL; struct mmc_command *cmd = NULL;
unsigned long flags; unsigned long flags;
@ -521,15 +522,15 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
writel(host_status & TIFM_MMCSD_ERRMASK, writel(host_status & TIFM_MMCSD_ERRMASK,
sock->addr + SOCK_MMCSD_STATUS); sock->addr + SOCK_MMCSD_STATUS);
if (host_status & TIFM_MMCSD_CTO) if (host_status & TIFM_MMCSD_CTO)
cmd_error = MMC_ERR_TIMEOUT; cmd_error = -ETIMEDOUT;
else if (host_status & TIFM_MMCSD_CCRC) else if (host_status & TIFM_MMCSD_CCRC)
cmd_error = MMC_ERR_BADCRC; cmd_error = -EILSEQ;
if (cmd->data) { if (cmd->data) {
if (host_status & TIFM_MMCSD_DTO) if (host_status & TIFM_MMCSD_DTO)
cmd->data->error = MMC_ERR_TIMEOUT; cmd->data->error = -ETIMEDOUT;
else if (host_status & TIFM_MMCSD_DCRC) else if (host_status & TIFM_MMCSD_DCRC)
cmd->data->error = MMC_ERR_BADCRC; cmd->data->error = -EILSEQ;
} }
writel(TIFM_FIFO_INT_SETALL, writel(TIFM_FIFO_INT_SETALL,
@ -626,14 +627,21 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_lock_irqsave(&sock->lock, flags); spin_lock_irqsave(&sock->lock, flags);
if (host->eject) { if (host->eject) {
spin_unlock_irqrestore(&sock->lock, flags); mrq->cmd->error = -ENOMEDIUM;
goto err_out; goto err_out;
} }
if (host->req) { if (host->req) {
printk(KERN_ERR "%s : unfinished request detected\n", printk(KERN_ERR "%s : unfinished request detected\n",
sock->dev.bus_id); sock->dev.bus_id);
spin_unlock_irqrestore(&sock->lock, flags); mrq->cmd->error = -ETIMEDOUT;
goto err_out;
}
if (mrq->data && !is_power_of_2(mrq->data->blksz)) {
printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n",
sock->dev.bus_id, mrq->data->blksz);
mrq->cmd->error = -EINVAL;
goto err_out; goto err_out;
} }
@ -722,7 +730,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
return; return;
err_out: err_out:
mrq->cmd->error = MMC_ERR_TIMEOUT; spin_unlock_irqrestore(&sock->lock, flags);
mmc_request_done(mmc, mrq); mmc_request_done(mmc, mrq);
} }
@ -1012,9 +1020,9 @@ static void tifm_sd_remove(struct tifm_dev *sock)
writel(TIFM_FIFO_INT_SETALL, writel(TIFM_FIFO_INT_SETALL,
sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET);
host->req->cmd->error = MMC_ERR_TIMEOUT; host->req->cmd->error = -ENOMEDIUM;
if (host->req->stop) if (host->req->stop)
host->req->stop->error = MMC_ERR_TIMEOUT; host->req->stop->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} }
spin_unlock_irqrestore(&sock->lock, flags); spin_unlock_irqrestore(&sock->lock, flags);

Просмотреть файл

@ -317,7 +317,7 @@ static inline void wbsd_get_short_reply(struct wbsd_host *host,
* Correct response type? * Correct response type?
*/ */
if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) {
cmd->error = MMC_ERR_INVALID; cmd->error = -EILSEQ;
return; return;
} }
@ -337,7 +337,7 @@ static inline void wbsd_get_long_reply(struct wbsd_host *host,
* Correct response type? * Correct response type?
*/ */
if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) {
cmd->error = MMC_ERR_INVALID; cmd->error = -EILSEQ;
return; return;
} }
@ -372,7 +372,7 @@ static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
for (i = 3; i >= 0; i--) for (i = 3; i >= 0; i--)
outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR);
cmd->error = MMC_ERR_NONE; cmd->error = 0;
/* /*
* Wait for the request to complete. * Wait for the request to complete.
@ -392,13 +392,13 @@ static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd)
/* Card removed? */ /* Card removed? */
if (isr & WBSD_INT_CARD) if (isr & WBSD_INT_CARD)
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ENOMEDIUM;
/* Timeout? */ /* Timeout? */
else if (isr & WBSD_INT_TIMEOUT) else if (isr & WBSD_INT_TIMEOUT)
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ETIMEDOUT;
/* CRC? */ /* CRC? */
else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC))
cmd->error = MMC_ERR_BADCRC; cmd->error = -EILSEQ;
/* All ok */ /* All ok */
else { else {
if (cmd->flags & MMC_RSP_136) if (cmd->flags & MMC_RSP_136)
@ -585,7 +585,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH);
wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF);
} else { } else {
data->error = MMC_ERR_INVALID; data->error = -EINVAL;
return; return;
} }
@ -607,7 +607,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
*/ */
BUG_ON(size > 0x10000); BUG_ON(size > 0x10000);
if (size > 0x10000) { if (size > 0x10000) {
data->error = MMC_ERR_INVALID; data->error = -EINVAL;
return; return;
} }
@ -669,7 +669,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data)
} }
} }
data->error = MMC_ERR_NONE; data->error = 0;
} }
static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
@ -724,8 +724,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
"%d bytes left.\n", "%d bytes left.\n",
mmc_hostname(host->mmc), count); mmc_hostname(host->mmc), count);
if (data->error == MMC_ERR_NONE) if (!data->error)
data->error = MMC_ERR_FAILED; data->error = -EIO;
} else { } else {
/* /*
* Transfer data from DMA buffer to * Transfer data from DMA buffer to
@ -735,7 +735,7 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data)
wbsd_dma_to_sg(host, data); wbsd_dma_to_sg(host, data);
} }
if (data->error != MMC_ERR_NONE) { if (data->error) {
if (data->bytes_xfered) if (data->bytes_xfered)
data->bytes_xfered -= data->blksz; data->bytes_xfered -= data->blksz;
} }
@ -767,11 +767,10 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq; host->mrq = mrq;
/* /*
* If there is no card in the slot then * Check that there is actually a card in the slot.
* timeout immediatly.
*/ */
if (!(host->flags & WBSD_FCARD_PRESENT)) { if (!(host->flags & WBSD_FCARD_PRESENT)) {
cmd->error = MMC_ERR_TIMEOUT; cmd->error = -ENOMEDIUM;
goto done; goto done;
} }
@ -807,7 +806,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
"supported by this controller.\n", "supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), cmd->opcode);
#endif #endif
cmd->error = MMC_ERR_INVALID; cmd->error = -EINVAL;
goto done; goto done;
}; };
@ -819,7 +818,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
if (cmd->data) { if (cmd->data) {
wbsd_prepare_data(host, cmd->data); wbsd_prepare_data(host, cmd->data);
if (cmd->data->error != MMC_ERR_NONE) if (cmd->data->error)
goto done; goto done;
} }
@ -830,7 +829,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
* will be finished after the data has * will be finished after the data has
* transfered. * transfered.
*/ */
if (cmd->data && (cmd->error == MMC_ERR_NONE)) { if (cmd->data && !cmd->error) {
/* /*
* Dirty fix for hardware bug. * Dirty fix for hardware bug.
*/ */
@ -1033,7 +1032,7 @@ static void wbsd_tasklet_card(unsigned long param)
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
wbsd_reset(host); wbsd_reset(host);
host->mrq->cmd->error = MMC_ERR_FAILED; host->mrq->cmd->error = -ENOMEDIUM;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
} }
@ -1097,7 +1096,7 @@ static void wbsd_tasklet_crc(unsigned long param)
DBGF("CRC error\n"); DBGF("CRC error\n");
data->error = MMC_ERR_BADCRC; data->error = -EILSEQ;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
@ -1121,7 +1120,7 @@ static void wbsd_tasklet_timeout(unsigned long param)
DBGF("Timeout\n"); DBGF("Timeout\n");
data->error = MMC_ERR_TIMEOUT; data->error = -ETIMEDOUT;
tasklet_schedule(&host->finish_tasklet); tasklet_schedule(&host->finish_tasklet);
@ -1220,7 +1219,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
mmc->f_min = 375000; mmc->f_min = 375000;
mmc->f_max = 24000000; mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);

Просмотреть файл

@ -3,8 +3,11 @@
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
struct device;
struct imxmmc_platform_data { struct imxmmc_platform_data {
int (*card_present)(void); int (*card_present)(struct device *);
int (*get_ro)(struct device *);
}; };
extern void imx_set_mmc_info(struct imxmmc_platform_data *info); extern void imx_set_mmc_info(struct imxmmc_platform_data *info);

Просмотреть файл

@ -55,7 +55,28 @@ struct sd_switch_caps {
unsigned int hs_max_dtr; unsigned int hs_max_dtr;
}; };
struct sdio_cccr {
unsigned int sdio_vsn;
unsigned int sd_vsn;
unsigned int multi_block:1,
low_speed:1,
wide_bus:1,
high_power:1,
high_speed:1;
};
struct sdio_cis {
unsigned short vendor;
unsigned short device;
unsigned short blksize;
unsigned int max_dtr;
};
struct mmc_host; struct mmc_host;
struct sdio_func;
struct sdio_func_tuple;
#define SDIO_MAX_FUNCS 7
/* /*
* MMC device * MMC device
@ -67,11 +88,13 @@ struct mmc_card {
unsigned int type; /* card type */ unsigned int type; /* card type */
#define MMC_TYPE_MMC 0 /* MMC card */ #define MMC_TYPE_MMC 0 /* MMC card */
#define MMC_TYPE_SD 1 /* SD card */ #define MMC_TYPE_SD 1 /* SD card */
#define MMC_TYPE_SDIO 2 /* SDIO card */
unsigned int state; /* (our) card state */ unsigned int state; /* (our) card state */
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
#define MMC_STATE_READONLY (1<<1) /* card is read-only */ #define MMC_STATE_READONLY (1<<1) /* card is read-only */
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ #define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ #define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
u32 raw_cid[4]; /* raw card CID */ u32 raw_cid[4]; /* raw card CID */
u32 raw_csd[4]; /* raw card CSD */ u32 raw_csd[4]; /* raw card CSD */
u32 raw_scr[2]; /* raw card SCR */ u32 raw_scr[2]; /* raw card SCR */
@ -80,10 +103,19 @@ struct mmc_card {
struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */ struct mmc_ext_csd ext_csd; /* mmc v4 extended card specific */
struct sd_scr scr; /* extra SD information */ struct sd_scr scr; /* extra SD information */
struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ struct sd_switch_caps sw_caps; /* switch (CMD6) caps */
unsigned int sdio_funcs; /* number of SDIO functions */
struct sdio_cccr cccr; /* common card info */
struct sdio_cis cis; /* common tuple info */
struct sdio_func *sdio_func[SDIO_MAX_FUNCS]; /* SDIO functions (devices) */
unsigned num_info; /* number of info strings */
const char **info; /* info strings */
struct sdio_func_tuple *tuples; /* unknown common tuples */
}; };
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)

Просмотреть файл

@ -25,14 +25,20 @@ struct mmc_command {
#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ #define MMC_RSP_CRC (1 << 2) /* expect valid crc */
#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ #define MMC_RSP_BUSY (1 << 3) /* card may send busy */
#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ #define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */
#define MMC_CMD_MASK (3 << 5) /* command type */
#define MMC_CMD_MASK (3 << 5) /* non-SPI command type */
#define MMC_CMD_AC (0 << 5) #define MMC_CMD_AC (0 << 5)
#define MMC_CMD_ADTC (1 << 5) #define MMC_CMD_ADTC (1 << 5)
#define MMC_CMD_BC (2 << 5) #define MMC_CMD_BC (2 << 5)
#define MMC_CMD_BCR (3 << 5) #define MMC_CMD_BCR (3 << 5)
#define MMC_RSP_SPI_S1 (1 << 7) /* one status byte */
#define MMC_RSP_SPI_S2 (1 << 8) /* second byte */
#define MMC_RSP_SPI_B4 (1 << 9) /* four data bytes */
#define MMC_RSP_SPI_BUSY (1 << 10) /* card may send busy */
/* /*
* These are the response types, and correspond to valid bit * These are the native response types, and correspond to valid bit
* patterns of the above flags. One additional valid pattern * patterns of the above flags. One additional valid pattern
* is all zeros, which means we don't expect a response. * is all zeros, which means we don't expect a response.
*/ */
@ -41,11 +47,29 @@ struct mmc_command {
#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) #define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY)
#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) #define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3 (MMC_RSP_PRESENT) #define MMC_RSP_R3 (MMC_RSP_PRESENT)
#define MMC_RSP_R4 (MMC_RSP_PRESENT)
#define MMC_RSP_R5 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) #define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) #define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
/*
* These are the SPI response types for MMC, SD, and SDIO cards.
* Commands return R1, with maybe more info. Zero is an error type;
* callers must always provide the appropriate MMC_RSP_SPI_Rx flags.
*/
#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)
#define MMC_RSP_SPI_R1B (MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY)
#define MMC_RSP_SPI_R2 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
#define MMC_RSP_SPI_R3 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
#define MMC_RSP_SPI_R4 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
#define MMC_RSP_SPI_R5 (MMC_RSP_SPI_S1|MMC_RSP_SPI_S2)
#define MMC_RSP_SPI_R7 (MMC_RSP_SPI_S1|MMC_RSP_SPI_B4)
#define mmc_spi_resp_type(cmd) ((cmd)->flags & \
(MMC_RSP_SPI_S1|MMC_RSP_SPI_BUSY|MMC_RSP_SPI_S2|MMC_RSP_SPI_B4))
/* /*
* These are the command types. * These are the command types.
*/ */
@ -54,12 +78,19 @@ struct mmc_command {
unsigned int retries; /* max number of retries */ unsigned int retries; /* max number of retries */
unsigned int error; /* command error */ unsigned int error; /* command error */
#define MMC_ERR_NONE 0 /*
#define MMC_ERR_TIMEOUT 1 * Standard errno values are used for errors, but some have specific
#define MMC_ERR_BADCRC 2 * meaning in the MMC layer:
#define MMC_ERR_FIFO 3 *
#define MMC_ERR_FAILED 4 * ETIMEDOUT Card took too long to respond
#define MMC_ERR_INVALID 5 * EILSEQ Basic format problem with the received or sent data
* (e.g. CRC check failed, incorrect opcode in response
* or bad end bit)
* EINVAL Request cannot be performed because of restrictions
* in hardware and/or the driver
* ENOMEDIUM Host can determine that the slot is empty and is
* actively failing requests
*/
struct mmc_data *data; /* data segment associated with cmd */ struct mmc_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */ struct mmc_request *mrq; /* associated request */
@ -76,7 +107,6 @@ struct mmc_data {
#define MMC_DATA_WRITE (1 << 8) #define MMC_DATA_WRITE (1 << 8)
#define MMC_DATA_READ (1 << 9) #define MMC_DATA_READ (1 << 9)
#define MMC_DATA_STREAM (1 << 10) #define MMC_DATA_STREAM (1 << 10)
#define MMC_DATA_MULTI (1 << 11)
unsigned int bytes_xfered; unsigned int bytes_xfered;
@ -104,9 +134,20 @@ extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int); struct mmc_command *, int);
extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *);
extern void mmc_claim_host(struct mmc_host *host); extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host); extern void mmc_release_host(struct mmc_host *host);
/**
* mmc_claim_host - exclusively claim a host
* @host: mmc host to claim
*
* Claim a host for a set of operations.
*/
static inline void mmc_claim_host(struct mmc_host *host)
{
__mmc_claim_host(host, NULL);
}
#endif #endif

Просмотреть файл

@ -10,6 +10,8 @@
#ifndef LINUX_MMC_HOST_H #ifndef LINUX_MMC_HOST_H
#define LINUX_MMC_HOST_H #define LINUX_MMC_HOST_H
#include <linux/leds.h>
#include <linux/mmc/core.h> #include <linux/mmc/core.h>
struct mmc_ios { struct mmc_ios {
@ -51,6 +53,7 @@ struct mmc_host_ops {
void (*request)(struct mmc_host *host, struct mmc_request *req); void (*request)(struct mmc_host *host, struct mmc_request *req);
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios); void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
int (*get_ro)(struct mmc_host *host); int (*get_ro)(struct mmc_host *host);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
}; };
struct mmc_card; struct mmc_card;
@ -87,9 +90,10 @@ struct mmc_host {
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
#define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */ #define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */
#define MMC_CAP_BYTEBLOCK (1 << 2) /* Can do non-log2 block sizes */ #define MMC_CAP_MMC_HIGHSPEED (1 << 2) /* Can do MMC high-speed timing */
#define MMC_CAP_MMC_HIGHSPEED (1 << 3) /* Can do MMC high-speed timing */ #define MMC_CAP_SD_HIGHSPEED (1 << 3) /* Can do SD high-speed timing */
#define MMC_CAP_SD_HIGHSPEED (1 << 4) /* Can do SD high-speed timing */ #define MMC_CAP_SDIO_IRQ (1 << 4) /* Can signal pending SDIO IRQs */
#define MMC_CAP_SPI (1 << 5) /* Talks only SPI protocols */
/* host specific block data */ /* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */
@ -106,6 +110,14 @@ struct mmc_host {
struct mmc_ios ios; /* current io bus settings */ struct mmc_ios ios; /* current io bus settings */
u32 ocr; /* the current OCR setting */ u32 ocr; /* the current OCR setting */
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;
unsigned int claimed:1; /* host exclusively claimed */
unsigned int bus_dead:1; /* bus has been released */
#ifdef CONFIG_MMC_DEBUG
unsigned int removed:1; /* host is being removed */
#endif
unsigned int mode; /* current card mode of host */ unsigned int mode; /* current card mode of host */
#define MMC_MODE_MMC 0 #define MMC_MODE_MMC 0
#define MMC_MODE_SD 1 #define MMC_MODE_SD 1
@ -113,16 +125,19 @@ struct mmc_host {
struct mmc_card *card; /* device attached to this host */ struct mmc_card *card; /* device attached to this host */
wait_queue_head_t wq; wait_queue_head_t wq;
unsigned int claimed:1; /* host exclusively claimed */
struct delayed_work detect; struct delayed_work detect;
#ifdef CONFIG_MMC_DEBUG
unsigned int removed:1; /* host is being removed */
#endif
const struct mmc_bus_ops *bus_ops; /* current bus driver */ const struct mmc_bus_ops *bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */ unsigned int bus_refs; /* reference counter */
unsigned int bus_dead:1; /* bus has been released */
unsigned int sdio_irqs;
struct task_struct *sdio_irq_thread;
atomic_t sdio_irq_thread_abort;
#ifdef CONFIG_LEDS_TRIGGERS
struct led_trigger *led; /* activity led */
#endif
unsigned long private[0] ____cacheline_aligned; unsigned long private[0] ____cacheline_aligned;
}; };
@ -137,6 +152,8 @@ static inline void *mmc_priv(struct mmc_host *host)
return (void *)host->private; return (void *)host->private;
} }
#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
#define mmc_dev(x) ((x)->parent) #define mmc_dev(x) ((x)->parent)
#define mmc_classdev(x) (&(x)->class_dev) #define mmc_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) ((x)->class_dev.bus_id) #define mmc_hostname(x) ((x)->class_dev.bus_id)
@ -147,5 +164,11 @@ extern int mmc_resume_host(struct mmc_host *);
extern void mmc_detect_change(struct mmc_host *, unsigned long delay); extern void mmc_detect_change(struct mmc_host *, unsigned long delay);
extern void mmc_request_done(struct mmc_host *, struct mmc_request *); extern void mmc_request_done(struct mmc_host *, struct mmc_request *);
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
{
host->ops->enable_sdio_irq(host, 0);
wake_up_process(host->sdio_irq_thread);
}
#endif #endif

Просмотреть файл

@ -27,7 +27,7 @@
/* Standard MMC commands (4.1) type argument response */ /* Standard MMC commands (4.1) type argument response */
/* class 1 */ /* class 1 */
#define MMC_GO_IDLE_STATE 0 /* bc */ #define MMC_GO_IDLE_STATE 0 /* bc */
#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ #define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */
#define MMC_ALL_SEND_CID 2 /* bcr R2 */ #define MMC_ALL_SEND_CID 2 /* bcr R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ #define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */
@ -39,8 +39,10 @@
#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ #define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */
#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ #define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */
#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ #define MMC_STOP_TRANSMISSION 12 /* ac R1b */
#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ #define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */
#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ #define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */
#define MMC_SPI_READ_OCR 58 /* spi spi_R3 */
#define MMC_SPI_CRC_ON_OFF 59 /* spi [0:0] flag spi_R1 */
/* class 2 */ /* class 2 */
#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ #define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
@ -90,15 +92,15 @@
*/ */
/* /*
MMC status in R1 MMC status in R1, for native mode (SPI bits are different)
Type Type
e : error bit e : error bit
s : status bit s : status bit
r : detected and set for the actual command response r : detected and set for the actual command response
x : detected and set during command execution. the host must poll x : detected and set during command execution. the host must poll
the card by sending status command in order to read these bits. the card by sending status command in order to read these bits.
Clear condition Clear condition
a : according to the card state a : according to the card state
b : always related to the previous command. Reception of b : always related to the previous command. Reception of
a valid command will clear it (with a delay of one command) a valid command will clear it (with a delay of one command)
c : clear by read c : clear by read
@ -124,10 +126,33 @@
#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ #define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */
#define R1_ERASE_RESET (1 << 13) /* sr, c */ #define R1_ERASE_RESET (1 << 13) /* sr, c */
#define R1_STATUS(x) (x & 0xFFFFE000) #define R1_STATUS(x) (x & 0xFFFFE000)
#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ #define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */
#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ #define R1_READY_FOR_DATA (1 << 8) /* sx, a */
#define R1_APP_CMD (1 << 5) /* sr, c */ #define R1_APP_CMD (1 << 5) /* sr, c */
/*
* MMC/SD in SPI mode reports R1 status always, and R2 for SEND_STATUS
* R1 is the low order byte; R2 is the next highest byte, when present.
*/
#define R1_SPI_IDLE (1 << 0)
#define R1_SPI_ERASE_RESET (1 << 1)
#define R1_SPI_ILLEGAL_COMMAND (1 << 2)
#define R1_SPI_COM_CRC (1 << 3)
#define R1_SPI_ERASE_SEQ (1 << 4)
#define R1_SPI_ADDRESS (1 << 5)
#define R1_SPI_PARAMETER (1 << 6)
/* R1 bit 7 is always zero */
#define R2_SPI_CARD_LOCKED (1 << 8)
#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */
#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP
#define R2_SPI_ERROR (1 << 10)
#define R2_SPI_CC_ERROR (1 << 11)
#define R2_SPI_CARD_ECC_ERROR (1 << 12)
#define R2_SPI_WP_VIOLATION (1 << 13)
#define R2_SPI_ERASE_PARAM (1 << 14)
#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */
#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE
/* These are unpacked versions of the actual responses */ /* These are unpacked versions of the actual responses */
struct _mmc_csd { struct _mmc_csd {
@ -182,6 +207,7 @@ struct _mmc_csd {
*/ */
#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ #define CCC_BASIC (1<<0) /* (0) Basic protocol functions */
/* (CMD0,1,2,3,4,7,9,10,12,13,15) */ /* (CMD0,1,2,3,4,7,9,10,12,13,15) */
/* (and for SPI, CMD58,59) */
#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ #define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */
/* (CMD11) */ /* (CMD11) */
#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ #define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */
@ -227,6 +253,7 @@ struct _mmc_csd {
#define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_BUS_WIDTH 183 /* R/W */
#define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */
#define EXT_CSD_CARD_TYPE 196 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_REV 192 /* RO */
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ #define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
/* /*

159
include/linux/mmc/sdio.h Normal file
Просмотреть файл

@ -0,0 +1,159 @@
/*
* include/linux/mmc/sdio.h
*
* 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_H
#define MMC_SDIO_H
/* SDIO commands type argument response */
#define SD_IO_SEND_OP_COND 5 /* bcr [23:0] OCR R4 */
#define SD_IO_RW_DIRECT 52 /* ac [31:0] See below R5 */
#define SD_IO_RW_EXTENDED 53 /* adtc [31:0] See below R5 */
/*
* SD_IO_RW_DIRECT argument format:
*
* [31] R/W flag
* [30:28] Function number
* [27] RAW flag
* [25:9] Register address
* [7:0] Data
*/
/*
* SD_IO_RW_EXTENDED argument format:
*
* [31] R/W flag
* [30:28] Function number
* [27] Block mode
* [26] Increment address
* [25:9] Register address
* [8:0] Byte/block count
*/
/*
SDIO status in R5
Type
e : error bit
s : status bit
r : detected and set for the actual command response
x : detected and set during command execution. the host must poll
the card by sending status command in order to read these bits.
Clear condition
a : according to the card state
b : always related to the previous command. Reception of
a valid command will clear it (with a delay of one command)
c : clear by read
*/
#define R5_COM_CRC_ERROR (1 << 15) /* er, b */
#define R5_ILLEGAL_COMMAND (1 << 14) /* er, b */
#define R5_ERROR (1 << 11) /* erx, c */
#define R5_FUNCTION_NUMBER (1 << 9) /* er, c */
#define R5_OUT_OF_RANGE (1 << 8) /* er, c */
#define R5_STATUS(x) (x & 0xCB00)
#define R5_IO_CURRENT_STATE(x) ((x & 0x3000) >> 12) /* s, b */
/*
* Card Common Control Registers (CCCR)
*/
#define SDIO_CCCR_CCCR 0x00
#define SDIO_CCCR_REV_1_00 0 /* CCCR/FBR Version 1.00 */
#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
#define SDIO_SDIO_REV_1_10 1 /* SDIO Spec Version 1.10 */
#define SDIO_SDIO_REV_1_20 2 /* SDIO Spec Version 1.20 */
#define SDIO_SDIO_REV_2_00 3 /* SDIO Spec Version 2.00 */
#define SDIO_CCCR_SD 0x01
#define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */
#define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */
#define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */
#define SDIO_CCCR_IOEx 0x02
#define SDIO_CCCR_IORx 0x03
#define SDIO_CCCR_IENx 0x04 /* Function/Master Interrupt Enable */
#define SDIO_CCCR_INTx 0x05 /* Function Interrupt Pending */
#define SDIO_CCCR_ABORT 0x06 /* function abort/card reset */
#define SDIO_CCCR_IF 0x07 /* bus interface controls */
#define SDIO_BUS_WIDTH_1BIT 0x00
#define SDIO_BUS_WIDTH_4BIT 0x02
#define SDIO_BUS_CD_DISABLE 0x80 /* disable pull-up on DAT3 (pin 1) */
#define SDIO_CCCR_CAPS 0x08
#define SDIO_CCCR_CAP_SDC 0x01 /* can do CMD52 while data transfer */
#define SDIO_CCCR_CAP_SMB 0x02 /* can do multi-block xfers (CMD53) */
#define SDIO_CCCR_CAP_SRW 0x04 /* supports read-wait protocol */
#define SDIO_CCCR_CAP_SBS 0x08 /* supports suspend/resume */
#define SDIO_CCCR_CAP_S4MI 0x10 /* interrupt during 4-bit CMD53 */
#define SDIO_CCCR_CAP_E4MI 0x20 /* enable ints during 4-bit CMD53 */
#define SDIO_CCCR_CAP_LSC 0x40 /* low speed card */
#define SDIO_CCCR_CAP_4BLS 0x80 /* 4 bit low speed card */
#define SDIO_CCCR_CIS 0x09 /* common CIS pointer (3 bytes) */
/* Following 4 regs are valid only if SBS is set */
#define SDIO_CCCR_SUSPEND 0x0c
#define SDIO_CCCR_SELx 0x0d
#define SDIO_CCCR_EXECx 0x0e
#define SDIO_CCCR_READYx 0x0f
#define SDIO_CCCR_BLKSIZE 0x10
#define SDIO_CCCR_POWER 0x12
#define SDIO_POWER_SMPC 0x01 /* Supports Master Power Control */
#define SDIO_POWER_EMPC 0x02 /* Enable Master Power Control */
#define SDIO_CCCR_SPEED 0x13
#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */
#define SDIO_SPEED_EHS 0x02 /* Enable High-Speed mode */
/*
* Function Basic Registers (FBR)
*/
#define SDIO_FBR_BASE(f) ((f) * 0x100) /* base of function f's FBRs */
#define SDIO_FBR_STD_IF 0x00
#define SDIO_FBR_SUPPORTS_CSA 0x40 /* supports Code Storage Area */
#define SDIO_FBR_ENABLE_CSA 0x80 /* enable Code Storage Area */
#define SDIO_FBR_STD_IF_EXT 0x01
#define SDIO_FBR_POWER 0x02
#define SDIO_FBR_POWER_SPS 0x01 /* Supports Power Selection */
#define SDIO_FBR_POWER_EPS 0x02 /* Enable (low) Power Selection */
#define SDIO_FBR_CIS 0x09 /* CIS pointer (3 bytes) */
#define SDIO_FBR_CSA 0x0C /* CSA pointer (3 bytes) */
#define SDIO_FBR_CSA_DATA 0x0F
#define SDIO_FBR_BLKSIZE 0x10 /* block size (2 bytes) */
#endif

Просмотреть файл

@ -0,0 +1,153 @@
/*
* include/linux/mmc/sdio_func.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_SDIO_FUNC_H
#define MMC_SDIO_FUNC_H
#include <linux/device.h>
#include <linux/mod_devicetable.h>
struct mmc_card;
struct sdio_func;
typedef void (sdio_irq_handler_t)(struct sdio_func *);
/*
* SDIO function CIS tuple (unknown to the core)
*/
struct sdio_func_tuple {
struct sdio_func_tuple *next;
unsigned char code;
unsigned char size;
unsigned char data[0];
};
/*
* SDIO function devices
*/
struct sdio_func {
struct mmc_card *card; /* the card this device belongs to */
struct device dev; /* the device */
sdio_irq_handler_t *irq_handler; /* IRQ callback */
unsigned int num; /* function number */
unsigned char class; /* standard interface class */
unsigned short vendor; /* vendor id */
unsigned short device; /* device id */
unsigned max_blksize; /* maximum block size */
unsigned cur_blksize; /* current block size */
unsigned int state; /* function state */
#define SDIO_STATE_PRESENT (1<<0) /* present in sysfs */
u8 tmpbuf[4]; /* DMA:able scratch buffer */
unsigned num_info; /* number of info strings */
const char **info; /* info strings */
struct sdio_func_tuple *tuples;
};
#define sdio_func_present(f) ((f)->state & SDIO_STATE_PRESENT)
#define sdio_func_set_present(f) ((f)->state |= SDIO_STATE_PRESENT)
#define sdio_func_id(f) ((f)->dev.bus_id)
#define sdio_get_drvdata(f) dev_get_drvdata(&(f)->dev)
#define sdio_set_drvdata(f,d) dev_set_drvdata(&(f)->dev, d)
/*
* SDIO function device driver
*/
struct sdio_driver {
char *name;
const struct sdio_device_id *id_table;
int (*probe)(struct sdio_func *, const struct sdio_device_id *);
void (*remove)(struct sdio_func *);
struct device_driver drv;
};
/**
* SDIO_DEVICE - macro used to describe a specific SDIO device
* @vend: the 16 bit manufacturer code
* @dev: the 16 bit function id
*
* This macro is used to create a struct sdio_device_id that matches a
* specific device. The class field will be set to SDIO_ANY_ID.
*/
#define SDIO_DEVICE(vend,dev) \
.class = SDIO_ANY_ID, \
.vendor = (vend), .device = (dev)
/**
* SDIO_DEVICE_CLASS - macro used to describe a specific SDIO device class
* @dev_class: the 8 bit standard interface code
*
* This macro is used to create a struct sdio_device_id that matches a
* specific standard SDIO function type. The vendor and device fields will
* be set to SDIO_ANY_ID.
*/
#define SDIO_DEVICE_CLASS(dev_class) \
.class = (dev_class), \
.vendor = SDIO_ANY_ID, .device = SDIO_ANY_ID
extern int sdio_register_driver(struct sdio_driver *);
extern void sdio_unregister_driver(struct sdio_driver *);
/*
* SDIO I/O operations
*/
extern void sdio_claim_host(struct sdio_func *func);
extern void sdio_release_host(struct sdio_func *func);
extern int sdio_enable_func(struct sdio_func *func);
extern int sdio_disable_func(struct sdio_func *func);
extern int sdio_set_block_size(struct sdio_func *func, unsigned blksz);
extern int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler);
extern int sdio_release_irq(struct sdio_func *func);
extern unsigned char sdio_readb(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern unsigned short sdio_readw(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern unsigned long sdio_readl(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern int sdio_memcpy_fromio(struct sdio_func *func, void *dst,
unsigned int addr, int count);
extern int sdio_readsb(struct sdio_func *func, void *dst,
unsigned int addr, int count);
extern void sdio_writeb(struct sdio_func *func, unsigned char b,
unsigned int addr, int *err_ret);
extern void sdio_writew(struct sdio_func *func, unsigned short b,
unsigned int addr, int *err_ret);
extern void sdio_writel(struct sdio_func *func, unsigned long b,
unsigned int addr, int *err_ret);
extern int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,
void *src, int count);
extern int sdio_writesb(struct sdio_func *func, unsigned int addr,
void *src, int count);
extern unsigned char sdio_f0_readb(struct sdio_func *func,
unsigned int addr, int *err_ret);
extern void sdio_f0_writeb(struct sdio_func *func, unsigned char b,
unsigned int addr, int *err_ret);
#endif

Просмотреть файл

@ -0,0 +1,23 @@
/*
* SDIO Classes, Interface Types, Manufacturer IDs, etc.
*/
#ifndef MMC_SDIO_IDS_H
#define MMC_SDIO_IDS_H
/*
* Standard SDIO Function Interfaces
*/
#define SDIO_CLASS_NONE 0x00 /* Not a SDIO standard interface */
#define SDIO_CLASS_UART 0x01 /* standard UART interface */
#define SDIO_CLASS_BT_A 0x02 /* Type-A BlueTooth std interface */
#define SDIO_CLASS_BT_B 0x03 /* Type-B BlueTooth std interface */
#define SDIO_CLASS_GPS 0x04 /* GPS standard interface */
#define SDIO_CLASS_CAMERA 0x05 /* Camera standard interface */
#define SDIO_CLASS_PHS 0x06 /* PHS standard interface */
#define SDIO_CLASS_WLAN 0x07 /* WLAN interface */
#define SDIO_CLASS_ATA 0x08 /* Embedded SDIO-ATA std interface */
#endif

Просмотреть файл

@ -340,4 +340,15 @@ struct parisc_device_id {
#define PA_HVERSION_ANY_ID 0xffff #define PA_HVERSION_ANY_ID 0xffff
#define PA_SVERSION_ANY_ID 0xffffffff #define PA_SVERSION_ANY_ID 0xffffffff
/* SDIO */
#define SDIO_ANY_ID (~0)
struct sdio_device_id {
__u8 class; /* Standard interface or SDIO_ANY_ID */
__u16 vendor; /* Vendor or SDIO_ANY_ID */
__u16 device; /* Device ID or SDIO_ANY_ID */
kernel_ulong_t driver_data; /* Data private to the driver */
};
#endif /* LINUX_MOD_DEVICETABLE_H */ #endif /* LINUX_MOD_DEVICETABLE_H */

Просмотреть файл

@ -1471,6 +1471,8 @@
#define PCI_DEVICE_ID_RICOH_RL5C476 0x0476 #define PCI_DEVICE_ID_RICOH_RL5C476 0x0476
#define PCI_DEVICE_ID_RICOH_RL5C478 0x0478 #define PCI_DEVICE_ID_RICOH_RL5C478 0x0478
#define PCI_DEVICE_ID_RICOH_R5C822 0x0822 #define PCI_DEVICE_ID_RICOH_R5C822 0x0822
#define PCI_DEVICE_ID_RICOH_R5C832 0x0832
#define PCI_DEVICE_ID_RICOH_R5C843 0x0843
#define PCI_VENDOR_ID_DLINK 0x1186 #define PCI_VENDOR_ID_DLINK 0x1186
#define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00 #define PCI_DEVICE_ID_DLINK_DGE510T 0x4c00

Просмотреть файл

@ -0,0 +1,33 @@
#ifndef __LINUX_SPI_MMC_SPI_H
#define __LINUX_SPI_MMC_SPI_H
struct device;
struct mmc_host;
/* Put this in platform_data of a device being used to manage an MMC/SD
* card slot. (Modeled after PXA mmc glue; see that for usage examples.)
*
* REVISIT This is not a spi-specific notion. Any card slot should be
* able to handle it. If the MMC core doesn't adopt this kind of notion,
* switch the "struct device *" parameters over to "struct spi_device *".
*/
struct mmc_spi_platform_data {
/* driver activation and (optional) card detect irq hookup */
int (*init)(struct device *,
irqreturn_t (*)(int, void *),
void *);
void (*exit)(struct device *, void *);
/* sense switch on sd cards */
int (*get_ro)(struct device *);
/* how long to debounce card detect, in msecs */
u16 detect_delay;
/* power management */
u16 powerup_msecs; /* delay of up to 250 msec */
u32 ocr_mask; /* available voltages */
void (*setpower)(struct device *, unsigned int maskval);
};
#endif /* __LINUX_SPI_MMC_SPI_H */

Просмотреть файл

@ -484,6 +484,22 @@ static int do_parisc_entry(const char *filename, struct parisc_device_id *id,
return 1; return 1;
} }
/* Looks like: sdio:cNvNdN. */
static int do_sdio_entry(const char *filename,
struct sdio_device_id *id, char *alias)
{
id->class = TO_NATIVE(id->class);
id->vendor = TO_NATIVE(id->vendor);
id->device = TO_NATIVE(id->device);
strcpy(alias, "sdio:");
ADD(alias, "c", id->class != (__u8)SDIO_ANY_ID, id->class);
ADD(alias, "v", id->vendor != (__u16)SDIO_ANY_ID, id->vendor);
ADD(alias, "d", id->device != (__u16)SDIO_ANY_ID, id->device);
return 1;
}
/* Ignore any prefix, eg. v850 prepends _ */ /* Ignore any prefix, eg. v850 prepends _ */
static inline int sym_is(const char *symbol, const char *name) static inline int sym_is(const char *symbol, const char *name)
{ {
@ -599,6 +615,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
do_table(symval, sym->st_size, do_table(symval, sym->st_size,
sizeof(struct parisc_device_id), "parisc", sizeof(struct parisc_device_id), "parisc",
do_parisc_entry, mod); do_parisc_entry, mod);
else if (sym_is(symname, "__mod_sdio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct sdio_device_id), "sdio",
do_sdio_entry, mod);
} }
/* Now add out buffered information to the generated C source */ /* Now add out buffered information to the generated C source */