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:
commit
6abd2c860e
|
@ -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,7 +243,11 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||||
brq.data.blocks = 1;
|
brq.data.blocks = 1;
|
||||||
|
|
||||||
if (brq.data.blocks > 1) {
|
if (brq.data.blocks > 1) {
|
||||||
brq.data.flags |= MMC_DATA_MULTI;
|
/* SPI multiblock writes terminate using a special
|
||||||
|
* token, not a STOP_TRANSMISSION request.
|
||||||
|
*/
|
||||||
|
if (!mmc_host_is_spi(card->host)
|
||||||
|
|| rq_data_dir(req) == READ)
|
||||||
brq.mrq.stop = &brq.stop;
|
brq.mrq.stop = &brq.stop;
|
||||||
readcmd = MMC_READ_MULTIPLE_BLOCK;
|
readcmd = MMC_READ_MULTIPLE_BLOCK;
|
||||||
writecmd = MMC_WRITE_MULTIPLE_BLOCK;
|
writecmd = MMC_WRITE_MULTIPLE_BLOCK;
|
||||||
|
@ -261,6 +265,8 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||||
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
1158
drivers/mmc/card/sdio_uart.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mmc_host_is_spi(card->host)) {
|
||||||
|
printk(KERN_INFO "%s: new %s%s card on SPI\n",
|
||||||
|
mmc_hostname(card->host),
|
||||||
|
mmc_card_highspeed(card) ? "high speed " : "",
|
||||||
|
type);
|
||||||
|
} else {
|
||||||
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
|
printk(KERN_INFO "%s: new %s%s card at address %04x\n",
|
||||||
mmc_hostname(card->host),
|
mmc_hostname(card->host),
|
||||||
mmc_card_highspeed(card) ? "high speed " : "",
|
mmc_card_highspeed(card) ? "high speed " : "",
|
||||||
type, card->rca);
|
type, card->rca);
|
||||||
|
}
|
||||||
|
|
||||||
card->dev.uevent_suppress = 1;
|
card->dev.uevent_suppress = 1;
|
||||||
|
|
||||||
|
@ -261,8 +285,13 @@ int mmc_add_card(struct mmc_card *card)
|
||||||
void mmc_remove_card(struct mmc_card *card)
|
void mmc_remove_card(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
if (mmc_card_present(card)) {
|
if (mmc_card_present(card)) {
|
||||||
|
if (mmc_host_is_spi(card->host)) {
|
||||||
|
printk(KERN_INFO "%s: SPI card removed\n",
|
||||||
|
mmc_hostname(card->host));
|
||||||
|
} else {
|
||||||
printk(KERN_INFO "%s: card %04x removed\n",
|
printk(KERN_INFO "%s: card %04x removed\n",
|
||||||
mmc_hostname(card->host), card->rca);
|
mmc_hostname(card->host), card->rca);
|
||||||
|
}
|
||||||
|
|
||||||
if (card->host->bus_ops->sysfs_remove)
|
if (card->host->bus_ops->sysfs_remove)
|
||||||
card->host->bus_ops->sysfs_remove(card->host, card);
|
card->host->bus_ops->sysfs_remove(card->host, card);
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#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);
|
||||||
|
if (!stop)
|
||||||
host->claimed = 1;
|
host->claimed = 1;
|
||||||
|
else
|
||||||
|
wake_up(&host->wq);
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
remove_wait_queue(&host->wq, &wait);
|
remove_wait_queue(&host->wq, &wait);
|
||||||
|
return stop;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(mmc_claim_host);
|
EXPORT_SYMBOL(__mmc_claim_host);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_release_host - release a host
|
* mmc_release_host - release a host
|
||||||
|
@ -313,7 +351,7 @@ void mmc_release_host(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
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_HIGH;
|
||||||
|
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||||
|
} else {
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||||
|
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||||
|
}
|
||||||
host->ios.power_mode = MMC_POWER_UP;
|
host->ios.power_mode = MMC_POWER_UP;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
|
|
||||||
mmc_delay(1);
|
/*
|
||||||
|
* This delay should be sufficient to allow the power supply
|
||||||
|
* to reach the minimum voltage.
|
||||||
|
*/
|
||||||
|
mmc_delay(2);
|
||||||
|
|
||||||
host->ios.clock = host->f_min;
|
host->ios.clock = host->f_min;
|
||||||
host->ios.power_mode = MMC_POWER_ON;
|
host->ios.power_mode = MMC_POWER_ON;
|
||||||
mmc_set_ios(host);
|
mmc_set_ios(host);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This delay must be at least 74 clock sizes, or 1 ms, or the
|
||||||
|
* time required to reach a stable voltage.
|
||||||
|
*/
|
||||||
mmc_delay(2);
|
mmc_delay(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,8 +504,10 @@ static void mmc_power_off(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
host->ios.clock = 0;
|
host->ios.clock = 0;
|
||||||
host->ios.vdd = 0;
|
host->ios.vdd = 0;
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||||
|
}
|
||||||
host->ios.power_mode = MMC_POWER_OFF;
|
host->ios.power_mode = MMC_POWER_OFF;
|
||||||
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
host->ios.bus_width = MMC_BUS_WIDTH_1;
|
||||||
host->ios.timing = MMC_TIMING_LEGACY;
|
host->ios.timing = MMC_TIMING_LEGACY;
|
||||||
|
@ -511,7 +564,7 @@ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
|
||||||
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
|
* ...and finally MMC.
|
||||||
* searching for MMC cards.
|
|
||||||
*/
|
*/
|
||||||
err = mmc_send_op_cond(host, 0, &ocr);
|
err = mmc_send_op_cond(host, 0, &ocr);
|
||||||
if (err == MMC_ERR_NONE) {
|
if (!err) {
|
||||||
if (mmc_attach_mmc(host, ocr))
|
if (mmc_attach_mmc(host, ocr))
|
||||||
mmc_power_off(host);
|
mmc_power_off(host);
|
||||||
} else {
|
return;
|
||||||
mmc_power_off(host);
|
}
|
||||||
|
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
}
|
mmc_power_off(host);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (host->bus_ops->detect && !host->bus_dead)
|
if (host->bus_ops->detect && !host->bus_dead)
|
||||||
host->bus_ops->detect(host);
|
host->bus_ops->detect(host);
|
||||||
|
@ -725,22 +792,38 @@ static int __init mmc_init(void)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = mmc_register_bus();
|
ret = mmc_register_bus();
|
||||||
if (ret == 0) {
|
if (ret)
|
||||||
|
goto destroy_workqueue;
|
||||||
|
|
||||||
ret = mmc_register_host_class();
|
ret = mmc_register_host_class();
|
||||||
if (ret)
|
if (ret)
|
||||||
|
goto unregister_bus;
|
||||||
|
|
||||||
|
ret = sdio_register_bus();
|
||||||
|
if (ret)
|
||||||
|
goto unregister_host_class;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unregister_host_class:
|
||||||
|
mmc_unregister_host_class();
|
||||||
|
unregister_bus:
|
||||||
mmc_unregister_bus();
|
mmc_unregister_bus();
|
||||||
}
|
destroy_workqueue:
|
||||||
|
destroy_workqueue(workqueue);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit mmc_exit(void)
|
static void __exit mmc_exit(void)
|
||||||
{
|
{
|
||||||
|
sdio_unregister_bus();
|
||||||
mmc_unregister_host_class();
|
mmc_unregister_host_class();
|
||||||
mmc_unregister_bus();
|
mmc_unregister_bus();
|
||||||
destroy_workqueue(workqueue);
|
destroy_workqueue(workqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(mmc_init);
|
subsys_initcall(mmc_init);
|
||||||
module_exit(mmc_exit);
|
module_exit(mmc_exit);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
|
@ -48,5 +48,7 @@ void mmc_rescan(struct work_struct *work);
|
||||||
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,11 +202,21 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||||
"EXT_CSD, performance might "
|
"EXT_CSD, performance might "
|
||||||
"suffer.\n",
|
"suffer.\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = MMC_ERR_NONE;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext_csd_struct = ext_csd[EXT_CSD_REV];
|
||||||
|
if (ext_csd_struct > 2) {
|
||||||
|
printk(KERN_ERR "%s: unrecognised EXT_CSD structure "
|
||||||
|
"version %d\n", mmc_hostname(card->host),
|
||||||
|
ext_csd_struct);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ext_csd_struct >= 2) {
|
||||||
card->ext_csd.sectors =
|
card->ext_csd.sectors =
|
||||||
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
|
ext_csd[EXT_CSD_SEC_CNT + 0] << 0 |
|
||||||
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
|
ext_csd[EXT_CSD_SEC_CNT + 1] << 8 |
|
||||||
|
@ -209,6 +224,7 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
||||||
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
|
ext_csd[EXT_CSD_SEC_CNT + 3] << 24;
|
||||||
if (card->ext_csd.sectors)
|
if (card->ext_csd.sectors)
|
||||||
mmc_card_set_blockaddr(card);
|
mmc_card_set_blockaddr(card);
|
||||||
|
}
|
||||||
|
|
||||||
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
|
switch (ext_csd[EXT_CSD_CARD_TYPE]) {
|
||||||
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
|
case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26:
|
||||||
|
@ -246,7 +262,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
|
if (mmc_host_is_spi(host))
|
||||||
|
err = mmc_send_cid(host, cid);
|
||||||
|
else
|
||||||
err = mmc_all_send_cid(host, cid);
|
err = mmc_all_send_cid(host, cid);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (oldcard) {
|
if (oldcard) {
|
||||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||||
|
err = -ENOENT;
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
card = oldcard;
|
card = oldcard;
|
||||||
} else {
|
} else {
|
||||||
|
@ -278,8 +308,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
err = mmc_set_relative_addr(card);
|
err = mmc_set_relative_addr(card);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
/*
|
/*
|
||||||
* Fetch CSD from card.
|
* Fetch CSD from card.
|
||||||
*/
|
*/
|
||||||
err = mmc_send_csd(card, card->raw_csd);
|
err = mmc_send_csd(card, card->raw_csd);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
err = mmc_decode_csd(card);
|
err = mmc_decode_csd(card);
|
||||||
if (err < 0)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
err = mmc_decode_cid(card);
|
err = mmc_decode_cid(card);
|
||||||
if (err < 0)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select card, as all following commands rely on that.
|
* Select card, as all following commands rely on that.
|
||||||
*/
|
*/
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
err = mmc_select_card(card);
|
err = mmc_select_card(card);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
/*
|
/*
|
||||||
* Fetch and process extened CSD.
|
* Fetch and process extended CSD.
|
||||||
*/
|
*/
|
||||||
err = mmc_read_ext_csd(card);
|
err = mmc_read_ext_csd(card);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +370,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
(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,6 +516,7 @@ static void mmc_suspend(struct mmc_host *host)
|
||||||
BUG_ON(!host->card);
|
BUG_ON(!host->card);
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
|
if (!mmc_host_is_spi(host))
|
||||||
mmc_deselect_cards(host);
|
mmc_deselect_cards(host);
|
||||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
|
@ -502,7 +539,7 @@ static void mmc_resume(struct mmc_host *host)
|
||||||
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Non-SPI hosts need to prevent chipselect going active during
|
||||||
|
* GO_IDLE; that would put chips into SPI mode. Remind them of
|
||||||
|
* that in case of hardware that won't pull up DAT3/nCS otherwise.
|
||||||
|
*
|
||||||
|
* SPI hosts ignore ios.chip_select; it's managed according to
|
||||||
|
* rules that must accomodate non-MMC slaves which this layer
|
||||||
|
* won't even know about.
|
||||||
|
*/
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
mmc_set_chip_select(host, MMC_CS_HIGH);
|
mmc_set_chip_select(host, MMC_CS_HIGH);
|
||||||
|
|
||||||
mmc_delay(1);
|
mmc_delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||||
|
|
||||||
cmd.opcode = MMC_GO_IDLE_STATE;
|
cmd.opcode = MMC_GO_IDLE_STATE;
|
||||||
cmd.arg = 0;
|
cmd.arg = 0;
|
||||||
cmd.flags = MMC_RSP_NONE | MMC_CMD_BC;
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||||
|
|
||||||
mmc_delay(1);
|
mmc_delay(1);
|
||||||
|
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
mmc_set_chip_select(host, MMC_CS_DONTCARE);
|
mmc_set_chip_select(host, MMC_CS_DONTCARE);
|
||||||
|
|
||||||
mmc_delay(1);
|
mmc_delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
host->use_spi_crc = 0;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -94,23 +107,33 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||||
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;
|
||||||
|
if (!mmc_host_is_spi(card->host))
|
||||||
cmd.arg = card->rca << 16;
|
cmd.arg = card->rca << 16;
|
||||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||||
|
|
||||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
/* NOTE: callers are required to understand the difference
|
||||||
|
* between "native" and SPI format status words!
|
||||||
|
*/
|
||||||
if (status)
|
if (status)
|
||||||
*status = cmd.resp[0];
|
*status = cmd.resp[0];
|
||||||
|
|
||||||
return MMC_ERR_NONE;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@ int mmc_send_csd(struct mmc_card *card, u32 *csd);
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
|
if (mmc_host_is_spi(host))
|
||||||
|
err = mmc_send_cid(host, cid);
|
||||||
|
else
|
||||||
err = mmc_all_send_cid(host, cid);
|
err = mmc_all_send_cid(host, cid);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (oldcard) {
|
if (oldcard) {
|
||||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||||
|
err = -ENOENT;
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
card = oldcard;
|
card = oldcard;
|
||||||
} else {
|
} else {
|
||||||
|
@ -333,32 +353,36 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||||
* Allocate card structure.
|
* Allocate card structure.
|
||||||
*/
|
*/
|
||||||
card = mmc_alloc_card(host);
|
card = mmc_alloc_card(host);
|
||||||
if (IS_ERR(card))
|
if (IS_ERR(card)) {
|
||||||
|
err = PTR_ERR(card);
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
card->type = MMC_TYPE_SD;
|
card->type = MMC_TYPE_SD;
|
||||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set card RCA.
|
* For native busses: get card RCA and quit open drain mode.
|
||||||
*/
|
*/
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
err = mmc_send_relative_addr(host, &card->rca);
|
err = mmc_send_relative_addr(host, &card->rca);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
/*
|
/*
|
||||||
* Fetch CSD from card.
|
* Fetch CSD from card.
|
||||||
*/
|
*/
|
||||||
err = mmc_send_csd(card, card->raw_csd);
|
err = mmc_send_csd(card, card->raw_csd);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
err = mmc_decode_csd(card);
|
err = mmc_decode_csd(card);
|
||||||
if (err < 0)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
mmc_decode_cid(card);
|
mmc_decode_cid(card);
|
||||||
|
@ -367,16 +391,18 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||||
/*
|
/*
|
||||||
* Select card, as all following commands rely on that.
|
* Select card, as all following commands rely on that.
|
||||||
*/
|
*/
|
||||||
|
if (!mmc_host_is_spi(host)) {
|
||||||
err = mmc_select_card(card);
|
err = mmc_select_card(card);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
}
|
||||||
|
|
||||||
if (!oldcard) {
|
if (!oldcard) {
|
||||||
/*
|
/*
|
||||||
* Fetch SCR from card.
|
* Fetch SCR from card.
|
||||||
*/
|
*/
|
||||||
err = mmc_app_send_scr(card, card->raw_scr);
|
err = mmc_app_send_scr(card, card->raw_scr);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
err = mmc_decode_scr(card);
|
err = mmc_decode_scr(card);
|
||||||
|
@ -387,7 +413,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||||
* 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,6 +578,7 @@ static void mmc_sd_suspend(struct mmc_host *host)
|
||||||
BUG_ON(!host->card);
|
BUG_ON(!host->card);
|
||||||
|
|
||||||
mmc_claim_host(host);
|
mmc_claim_host(host);
|
||||||
|
if (!mmc_host_is_spi(host))
|
||||||
mmc_deselect_cards(host);
|
mmc_deselect_cards(host);
|
||||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||||
mmc_release_host(host);
|
mmc_release_host(host);
|
||||||
|
@ -574,7 +601,7 @@ static void mmc_sd_resume(struct mmc_host *host)
|
||||||
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;
|
||||||
|
if (mmc_host_is_spi(host))
|
||||||
|
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
|
||||||
|
else
|
||||||
cmd.arg = ocr;
|
cmd.arg = ocr;
|
||||||
cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR;
|
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||||
|
|
||||||
for (i = 100; i; i--) {
|
for (i = 100; i; i--) {
|
||||||
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
|
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
|
||||||
if (err != MMC_ERR_NONE)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0)
|
/* if we're just probing, do a single pass */
|
||||||
|
if (ocr == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
err = MMC_ERR_TIMEOUT;
|
/* otherwise wait until reset completes */
|
||||||
|
if (mmc_host_is_spi(host)) {
|
||||||
|
if (!(cmd.resp[0] & R1_SPI_IDLE))
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (cmd.resp[0] & MMC_CARD_BUSY)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = -ETIMEDOUT;
|
||||||
|
|
||||||
mmc_delay(10);
|
mmc_delay(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rocr)
|
if (rocr && !mmc_host_is_spi(host))
|
||||||
*rocr = cmd.resp[0];
|
*rocr = cmd.resp[0];
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -174,6 +199,7 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
||||||
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
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
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);
|
||||||
|
}
|
||||||
|
|
22
drivers/mmc/core/sdio_bus.h
Normal file
22
drivers/mmc/core/sdio_bus.h
Normal file
|
@ -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
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);
|
||||||
|
}
|
||||||
|
|
23
drivers/mmc/core/sdio_cis.h
Normal file
23
drivers/mmc/core/sdio_cis.h
Normal file
|
@ -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
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, ®);
|
||||||
|
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, ®);
|
||||||
|
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, ®);
|
||||||
|
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
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, ¶m);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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, ®);
|
||||||
|
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, ®);
|
||||||
|
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
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;
|
||||||
|
}
|
||||||
|
|
22
drivers/mmc/core/sdio_ops.h
Normal file
22
drivers/mmc/core/sdio_ops.h
Normal file
|
@ -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,20 +208,22 @@ 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 (data) {
|
||||||
if (flags & MMC_DATA_READ) {
|
if (flags & MMC_DATA_READ) {
|
||||||
if (flags & MMC_DATA_MULTI)
|
if (data->blocks > 1)
|
||||||
mmccmd |= SD_CMD_CT_4;
|
mmccmd |= SD_CMD_CT_4;
|
||||||
else
|
else
|
||||||
mmccmd |= SD_CMD_CT_2;
|
mmccmd |= SD_CMD_CT_2;
|
||||||
} else if (flags & MMC_DATA_WRITE) {
|
} else if (flags & MMC_DATA_WRITE) {
|
||||||
if (flags & MMC_DATA_MULTI)
|
if (data->blocks > 1)
|
||||||
mmccmd |= SD_CMD_CT_3;
|
mmccmd |= SD_CMD_CT_3;
|
||||||
else
|
else
|
||||||
mmccmd |= SD_CMD_CT_1;
|
mmccmd |= SD_CMD_CT_1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
au_writel(cmd->arg, HOST_CMDARG(host));
|
au_writel(cmd->arg, HOST_CMDARG(host));
|
||||||
au_sync();
|
au_sync();
|
||||||
|
@ -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
1408
drivers/mmc/host/mmc_spi.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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)
|
||||||
|
|
151
drivers/mmc/host/ricoh_mmc.c
Normal file
151
drivers/mmc/host/ricoh_mmc.c
Normal file
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
#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,7 +92,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
||||||
|
@ -128,6 +130,29 @@
|
||||||
#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
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
|
||||||
|
|
153
include/linux/mmc/sdio_func.h
Normal file
153
include/linux/mmc/sdio_func.h
Normal file
|
@ -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
|
||||||
|
|
23
include/linux/mmc/sdio_ids.h
Normal file
23
include/linux/mmc/sdio_ids.h
Normal file
|
@ -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
|
||||||
|
|
33
include/linux/spi/mmc_spi.h
Normal file
33
include/linux/spi/mmc_spi.h
Normal file
|
@ -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 */
|
||||||
|
|
Loading…
Reference in a new issue