mirror of https://gitee.com/openkylin/linux.git
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (75 commits) mmc: core: eMMC bus width may not work on all platforms mmc: sdhci: Auto-CMD23 fixes. mmc: sdhci: Auto-CMD23 support. mmc: core: Block CMD23 support for UHS104/SDXC cards. mmc: sdhci: Implement MMC_CAP_CMD23 for SDHCI. mmc: core: Use CMD23 for multiblock transfers when we can. mmc: quirks: Add/remove quirks conditional support. mmc: Add new VUB300 USB-to-SD/SDIO/MMC driver mmc: sdhci-pxa: Add quirks for DMA/ADMA to match h/w mmc: core: duplicated trial with same freq in mmc_rescan_try_freq() mmc: core: add support for eMMC Dual Data Rate mmc: core: eMMC signal voltage does not use CMD11 mmc: sdhci-pxa: add platform code for UHS signaling mmc: sdhci: add hooks for setting UHS in platform specific code mmc: core: clear MMC_PM_KEEP_POWER flag on resume mmc: dw_mmc: fixed wrong regulator_enable in suspend/resume mmc: sdhi: allow powering down controller with no card inserted mmc: tmio: runtime suspend the controller, where possible mmc: sdhi: support up to 3 interrupt sources mmc: sdhi: print physical base address and clock rate ...
This commit is contained in:
commit
8c1c77ff9b
|
@ -304,6 +304,7 @@ Code Seq#(hex) Include File Comments
|
|||
0xB0 all RATIO devices in development:
|
||||
<mailto:vgo@ratio.de>
|
||||
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
|
||||
0xB3 00 linux/mmc/ioctl.h
|
||||
0xC0 00-0F linux/usb/iowarrior.h
|
||||
0xCB 00-1F CBM serial IEC bus in development:
|
||||
<mailto:michael.klein@puffin.lb.shuttle.de>
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
- this file
|
||||
mmc-dev-attrs.txt
|
||||
- info on SD and MMC device attributes
|
||||
mmc-dev-parts.txt
|
||||
- info on SD and MMC device partitions
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
SD and MMC Block Device Attributes
|
||||
==================================
|
||||
|
||||
These attributes are defined for the block devices associated with the
|
||||
SD or MMC device.
|
||||
|
||||
The following attributes are read/write.
|
||||
|
||||
force_ro Enforce read-only access even if write protect switch is off.
|
||||
|
||||
SD and MMC Device Attributes
|
||||
============================
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
SD and MMC Device Partitions
|
||||
============================
|
||||
|
||||
Device partitions are additional logical block devices present on the
|
||||
SD/MMC device.
|
||||
|
||||
As of this writing, MMC boot partitions as supported and exposed as
|
||||
/dev/mmcblkXboot0 and /dev/mmcblkXboot1, where X is the index of the
|
||||
parent /dev/mmcblkX.
|
||||
|
||||
MMC Boot Partitions
|
||||
===================
|
||||
|
||||
Read and write access is provided to the two MMC boot partitions. Due to
|
||||
the sensitive nature of the boot partition contents, which often store
|
||||
a bootloader or bootloader configuration tables crucial to booting the
|
||||
platform, write access is disabled by default to reduce the chance of
|
||||
accidental bricking.
|
||||
|
||||
To enable write access to /dev/mmcblkXbootY, disable the forced read-only
|
||||
access with:
|
||||
|
||||
echo 0 > /sys/block/mmcblkXbootY/force_ro
|
||||
|
||||
To re-enable read-only access:
|
||||
|
||||
echo 1 > /sys/block/mmcblkXbootY/force_ro
|
|
@ -6800,6 +6800,13 @@ L: lm-sensors@lm-sensors.org
|
|||
S: Maintained
|
||||
F: drivers/hwmon/vt8231.c
|
||||
|
||||
VUB300 USB to SDIO/SD/MMC bridge chip
|
||||
M: Tony Olech <tony.olech@elandigitalsystems.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
L: linux-usb@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/mmc/host/vub300.c
|
||||
|
||||
W1 DALLAS'S 1-WIRE BUS
|
||||
M: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
|
||||
S: Maintained
|
||||
|
|
|
@ -24,6 +24,7 @@ struct tegra_sdhci_platform_data {
|
|||
int wp_gpio;
|
||||
int power_gpio;
|
||||
int is_8bit;
|
||||
int pm_flags;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,7 +31,11 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/string_helpers.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <linux/mmc/ioctl.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
|
@ -48,6 +52,13 @@ MODULE_ALIAS("mmc:block");
|
|||
#endif
|
||||
#define MODULE_PARAM_PREFIX "mmcblk."
|
||||
|
||||
#define INAND_CMD38_ARG_EXT_CSD 113
|
||||
#define INAND_CMD38_ARG_ERASE 0x00
|
||||
#define INAND_CMD38_ARG_TRIM 0x01
|
||||
#define INAND_CMD38_ARG_SECERASE 0x80
|
||||
#define INAND_CMD38_ARG_SECTRIM1 0x81
|
||||
#define INAND_CMD38_ARG_SECTRIM2 0x88
|
||||
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
|
||||
/*
|
||||
|
@ -64,6 +75,7 @@ static int max_devices;
|
|||
|
||||
/* 256 minors, so at most 256 separate devices */
|
||||
static DECLARE_BITMAP(dev_use, 256);
|
||||
static DECLARE_BITMAP(name_use, 256);
|
||||
|
||||
/*
|
||||
* There is one mmc_blk_data per slot.
|
||||
|
@ -72,9 +84,24 @@ struct mmc_blk_data {
|
|||
spinlock_t lock;
|
||||
struct gendisk *disk;
|
||||
struct mmc_queue queue;
|
||||
struct list_head part;
|
||||
|
||||
unsigned int flags;
|
||||
#define MMC_BLK_CMD23 (1 << 0) /* Can do SET_BLOCK_COUNT for multiblock */
|
||||
#define MMC_BLK_REL_WR (1 << 1) /* MMC Reliable write support */
|
||||
|
||||
unsigned int usage;
|
||||
unsigned int read_only;
|
||||
unsigned int part_type;
|
||||
unsigned int name_idx;
|
||||
|
||||
/*
|
||||
* Only set in main mmc_blk_data associated
|
||||
* with mmc_card with mmc_set_drvdata, and keeps
|
||||
* track of the current selected device partition.
|
||||
*/
|
||||
unsigned int part_curr;
|
||||
struct device_attribute force_ro;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(open_lock);
|
||||
|
@ -97,17 +124,22 @@ static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk)
|
|||
return md;
|
||||
}
|
||||
|
||||
static inline int mmc_get_devidx(struct gendisk *disk)
|
||||
{
|
||||
int devmaj = MAJOR(disk_devt(disk));
|
||||
int devidx = MINOR(disk_devt(disk)) / perdev_minors;
|
||||
|
||||
if (!devmaj)
|
||||
devidx = disk->first_minor / perdev_minors;
|
||||
return devidx;
|
||||
}
|
||||
|
||||
static void mmc_blk_put(struct mmc_blk_data *md)
|
||||
{
|
||||
mutex_lock(&open_lock);
|
||||
md->usage--;
|
||||
if (md->usage == 0) {
|
||||
int devmaj = MAJOR(disk_devt(md->disk));
|
||||
int devidx = MINOR(disk_devt(md->disk)) / perdev_minors;
|
||||
|
||||
if (!devmaj)
|
||||
devidx = md->disk->first_minor / perdev_minors;
|
||||
|
||||
int devidx = mmc_get_devidx(md->disk);
|
||||
blk_cleanup_queue(md->queue.queue);
|
||||
|
||||
__clear_bit(devidx, dev_use);
|
||||
|
@ -118,6 +150,38 @@ static void mmc_blk_put(struct mmc_blk_data *md)
|
|||
mutex_unlock(&open_lock);
|
||||
}
|
||||
|
||||
static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "%d",
|
||||
get_disk_ro(dev_to_disk(dev)) ^
|
||||
md->read_only);
|
||||
mmc_blk_put(md);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
char *end;
|
||||
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
||||
unsigned long set = simple_strtoul(buf, &end, 0);
|
||||
if (end == buf) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_disk_ro(dev_to_disk(dev), set || md->read_only);
|
||||
ret = count;
|
||||
out:
|
||||
mmc_blk_put(md);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_blk_open(struct block_device *bdev, fmode_t mode)
|
||||
{
|
||||
struct mmc_blk_data *md = mmc_blk_get(bdev->bd_disk);
|
||||
|
@ -158,35 +222,255 @@ mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
|||
return 0;
|
||||
}
|
||||
|
||||
struct mmc_blk_ioc_data {
|
||||
struct mmc_ioc_cmd ic;
|
||||
unsigned char *buf;
|
||||
u64 buf_bytes;
|
||||
};
|
||||
|
||||
static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
|
||||
struct mmc_ioc_cmd __user *user)
|
||||
{
|
||||
struct mmc_blk_ioc_data *idata;
|
||||
int err;
|
||||
|
||||
idata = kzalloc(sizeof(*idata), GFP_KERNEL);
|
||||
if (!idata) {
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_from_user(&idata->ic, user, sizeof(idata->ic))) {
|
||||
err = -EFAULT;
|
||||
goto idata_err;
|
||||
}
|
||||
|
||||
idata->buf_bytes = (u64) idata->ic.blksz * idata->ic.blocks;
|
||||
if (idata->buf_bytes > MMC_IOC_MAX_BYTES) {
|
||||
err = -EOVERFLOW;
|
||||
goto idata_err;
|
||||
}
|
||||
|
||||
idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
|
||||
if (!idata->buf) {
|
||||
err = -ENOMEM;
|
||||
goto idata_err;
|
||||
}
|
||||
|
||||
if (copy_from_user(idata->buf, (void __user *)(unsigned long)
|
||||
idata->ic.data_ptr, idata->buf_bytes)) {
|
||||
err = -EFAULT;
|
||||
goto copy_err;
|
||||
}
|
||||
|
||||
return idata;
|
||||
|
||||
copy_err:
|
||||
kfree(idata->buf);
|
||||
idata_err:
|
||||
kfree(idata);
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_ioc_cmd __user *ic_ptr)
|
||||
{
|
||||
struct mmc_blk_ioc_data *idata;
|
||||
struct mmc_blk_data *md;
|
||||
struct mmc_card *card;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct mmc_request mrq = {0};
|
||||
struct scatterlist sg;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* The caller must have CAP_SYS_RAWIO, and must be calling this on the
|
||||
* whole block device, not on a partition. This prevents overspray
|
||||
* between sibling partitions.
|
||||
*/
|
||||
if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
|
||||
return -EPERM;
|
||||
|
||||
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
|
||||
if (IS_ERR(idata))
|
||||
return PTR_ERR(idata);
|
||||
|
||||
cmd.opcode = idata->ic.opcode;
|
||||
cmd.arg = idata->ic.arg;
|
||||
cmd.flags = idata->ic.flags;
|
||||
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
data.blksz = idata->ic.blksz;
|
||||
data.blocks = idata->ic.blocks;
|
||||
|
||||
sg_init_one(data.sg, idata->buf, idata->buf_bytes);
|
||||
|
||||
if (idata->ic.write_flag)
|
||||
data.flags = MMC_DATA_WRITE;
|
||||
else
|
||||
data.flags = MMC_DATA_READ;
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
|
||||
md = mmc_blk_get(bdev->bd_disk);
|
||||
if (!md) {
|
||||
err = -EINVAL;
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
card = md->queue.card;
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
if (idata->ic.is_acmd) {
|
||||
err = mmc_app_cmd(card->host, card);
|
||||
if (err)
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
/* data.flags must already be set before doing this. */
|
||||
mmc_set_data_timeout(&data, card);
|
||||
/* Allow overriding the timeout_ns for empirical tuning. */
|
||||
if (idata->ic.data_timeout_ns)
|
||||
data.timeout_ns = idata->ic.data_timeout_ns;
|
||||
|
||||
if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
|
||||
/*
|
||||
* Pretend this is a data transfer and rely on the host driver
|
||||
* to compute timeout. When all host drivers support
|
||||
* cmd.cmd_timeout for R1B, this can be changed to:
|
||||
*
|
||||
* mrq.data = NULL;
|
||||
* cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
|
||||
*/
|
||||
data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
|
||||
}
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
if (cmd.error) {
|
||||
dev_err(mmc_dev(card->host), "%s: cmd error %d\n",
|
||||
__func__, cmd.error);
|
||||
err = cmd.error;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
if (data.error) {
|
||||
dev_err(mmc_dev(card->host), "%s: data error %d\n",
|
||||
__func__, data.error);
|
||||
err = data.error;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to the SD specs, some commands require a delay after
|
||||
* issuing the command.
|
||||
*/
|
||||
if (idata->ic.postsleep_min_us)
|
||||
usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
|
||||
|
||||
if (copy_to_user(&(ic_ptr->response), cmd.resp, sizeof(cmd.resp))) {
|
||||
err = -EFAULT;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
if (!idata->ic.write_flag) {
|
||||
if (copy_to_user((void __user *)(unsigned long) idata->ic.data_ptr,
|
||||
idata->buf, idata->buf_bytes)) {
|
||||
err = -EFAULT;
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
}
|
||||
|
||||
cmd_rel_host:
|
||||
mmc_release_host(card->host);
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
kfree(idata->buf);
|
||||
kfree(idata);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
if (cmd == MMC_IOC_CMD)
|
||||
ret = mmc_blk_ioctl_cmd(bdev, (struct mmc_ioc_cmd __user *)arg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static int mmc_blk_compat_ioctl(struct block_device *bdev, fmode_t mode,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return mmc_blk_ioctl(bdev, mode, cmd, (unsigned long) compat_ptr(arg));
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct block_device_operations mmc_bdops = {
|
||||
.open = mmc_blk_open,
|
||||
.release = mmc_blk_release,
|
||||
.getgeo = mmc_blk_getgeo,
|
||||
.owner = THIS_MODULE,
|
||||
.ioctl = mmc_blk_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = mmc_blk_compat_ioctl,
|
||||
#endif
|
||||
};
|
||||
|
||||
struct mmc_blk_request {
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command sbc;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command stop;
|
||||
struct mmc_data data;
|
||||
};
|
||||
|
||||
static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
struct mmc_blk_data *md)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *main_md = mmc_get_drvdata(card);
|
||||
if (main_md->part_curr == md->part_type)
|
||||
return 0;
|
||||
|
||||
if (mmc_card_mmc(card)) {
|
||||
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
||||
card->ext_csd.part_config |= md->part_type;
|
||||
|
||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_PART_CONFIG, card->ext_csd.part_config,
|
||||
card->ext_csd.part_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
main_md->part_curr = md->part_type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
u32 result;
|
||||
__be32 *blocks;
|
||||
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
unsigned int timeout_us;
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_APP_CMD;
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
@ -203,8 +487,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
|
||||
memset(&data, 0, sizeof(struct mmc_data));
|
||||
|
||||
data.timeout_ns = card->csd.tacc_ns * 100;
|
||||
data.timeout_clks = card->csd.tacc_clks * 100;
|
||||
|
||||
|
@ -223,8 +505,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
|
||||
|
@ -247,10 +527,9 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
|
|||
|
||||
static u32 get_card_status(struct mmc_card *card, struct request *req)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
int err;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
|
@ -269,8 +548,6 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
unsigned int from, nr, arg;
|
||||
int err = 0;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
if (!mmc_can_erase(card)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
|
@ -284,14 +561,22 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
else
|
||||
arg = MMC_ERASE_ARG;
|
||||
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
arg == MMC_TRIM_ARG ?
|
||||
INAND_CMD38_ARG_TRIM :
|
||||
INAND_CMD38_ARG_ERASE,
|
||||
0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = mmc_erase(card, from, nr, arg);
|
||||
out:
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
|
||||
mmc_release_host(card->host);
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
||||
|
@ -303,8 +588,6 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
|||
unsigned int from, nr, arg;
|
||||
int err = 0;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
if (!mmc_can_secure_erase_trim(card)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
|
@ -318,19 +601,74 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
|||
else
|
||||
arg = MMC_SECURE_ERASE_ARG;
|
||||
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
arg == MMC_SECURE_TRIM1_ARG ?
|
||||
INAND_CMD38_ARG_SECTRIM1 :
|
||||
INAND_CMD38_ARG_SECERASE,
|
||||
0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = mmc_erase(card, from, nr, arg);
|
||||
if (!err && arg == MMC_SECURE_TRIM1_ARG)
|
||||
if (!err && arg == MMC_SECURE_TRIM1_ARG) {
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
INAND_CMD38_ARG_SECTRIM2,
|
||||
0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
|
||||
}
|
||||
out:
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request(req, err, blk_rq_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
|
||||
mmc_release_host(card->host);
|
||||
|
||||
return err ? 0 : 1;
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->data;
|
||||
|
||||
/*
|
||||
* No-op, only service this because we need REQ_FUA for reliable
|
||||
* writes.
|
||||
*/
|
||||
spin_lock_irq(&md->lock);
|
||||
__blk_end_request_all(req, 0);
|
||||
spin_unlock_irq(&md->lock);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reformat current write as a reliable write, supporting
|
||||
* both legacy and the enhanced reliable write MMC cards.
|
||||
* In each transfer we'll handle only as much as a single
|
||||
* reliable write can handle, thus finish the request in
|
||||
* partial completions.
|
||||
*/
|
||||
static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
|
||||
struct mmc_card *card,
|
||||
struct request *req)
|
||||
{
|
||||
if (!(card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN)) {
|
||||
/* Legacy mode imposes restrictions on transfers. */
|
||||
if (!IS_ALIGNED(brq->cmd.arg, card->ext_csd.rel_sectors))
|
||||
brq->data.blocks = 1;
|
||||
|
||||
if (brq->data.blocks > card->ext_csd.rel_sectors)
|
||||
brq->data.blocks = card->ext_csd.rel_sectors;
|
||||
else if (brq->data.blocks < card->ext_csd.rel_sectors)
|
||||
brq->data.blocks = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->data;
|
||||
|
@ -338,10 +676,17 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
struct mmc_blk_request brq;
|
||||
int ret = 1, disable_multi = 0;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
/*
|
||||
* Reliable writes are used to implement Forced Unit Access and
|
||||
* REQ_META accesses, and are supported only on MMCs.
|
||||
*/
|
||||
bool do_rel_wr = ((req->cmd_flags & REQ_FUA) ||
|
||||
(req->cmd_flags & REQ_META)) &&
|
||||
(rq_data_dir(req) == WRITE) &&
|
||||
(md->flags & MMC_BLK_REL_WR);
|
||||
|
||||
do {
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
u32 readcmd, writecmd, status = 0;
|
||||
|
||||
memset(&brq, 0, sizeof(struct mmc_blk_request));
|
||||
|
@ -374,12 +719,12 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
if (disable_multi && brq.data.blocks > 1)
|
||||
brq.data.blocks = 1;
|
||||
|
||||
if (brq.data.blocks > 1) {
|
||||
if (brq.data.blocks > 1 || do_rel_wr) {
|
||||
/* 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)
|
||||
if (!mmc_host_is_spi(card->host) ||
|
||||
rq_data_dir(req) == READ)
|
||||
brq.mrq.stop = &brq.stop;
|
||||
readcmd = MMC_READ_MULTIPLE_BLOCK;
|
||||
writecmd = MMC_WRITE_MULTIPLE_BLOCK;
|
||||
|
@ -396,6 +741,38 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
brq.data.flags |= MMC_DATA_WRITE;
|
||||
}
|
||||
|
||||
if (do_rel_wr)
|
||||
mmc_apply_rel_rw(&brq, card, req);
|
||||
|
||||
/*
|
||||
* Pre-defined multi-block transfers are preferable to
|
||||
* open ended-ones (and necessary for reliable writes).
|
||||
* However, it is not sufficient to just send CMD23,
|
||||
* and avoid the final CMD12, as on an error condition
|
||||
* CMD12 (stop) needs to be sent anyway. This, coupled
|
||||
* with Auto-CMD23 enhancements provided by some
|
||||
* hosts, means that the complexity of dealing
|
||||
* with this is best left to the host. If CMD23 is
|
||||
* supported by card and host, we'll fill sbc in and let
|
||||
* the host deal with handling it correctly. This means
|
||||
* that for hosts that don't expose MMC_CAP_CMD23, no
|
||||
* change of behavior will be observed.
|
||||
*
|
||||
* N.B: Some MMC cards experience perf degradation.
|
||||
* We'll avoid using CMD23-bounded multiblock writes for
|
||||
* these, while retaining features like reliable writes.
|
||||
*/
|
||||
|
||||
if ((md->flags & MMC_BLK_CMD23) &&
|
||||
mmc_op_multi(brq.cmd.opcode) &&
|
||||
(do_rel_wr || !(card->quirks & MMC_QUIRK_BLK_NO_CMD23))) {
|
||||
brq.sbc.opcode = MMC_SET_BLOCK_COUNT;
|
||||
brq.sbc.arg = brq.data.blocks |
|
||||
(do_rel_wr ? (1 << 31) : 0);
|
||||
brq.sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
brq.mrq.sbc = &brq.sbc;
|
||||
}
|
||||
|
||||
mmc_set_data_timeout(&brq.data, card);
|
||||
|
||||
brq.data.sg = mq->sg;
|
||||
|
@ -431,7 +808,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
* until later as we need to wait for the card to leave
|
||||
* programming mode even when things go wrong.
|
||||
*/
|
||||
if (brq.cmd.error || brq.data.error || brq.stop.error) {
|
||||
if (brq.sbc.error || brq.cmd.error ||
|
||||
brq.data.error || brq.stop.error) {
|
||||
if (brq.data.blocks > 1 && rq_data_dir(req) == READ) {
|
||||
/* Redo read one sector at a time */
|
||||
printk(KERN_WARNING "%s: retrying using single "
|
||||
|
@ -442,6 +820,13 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
status = get_card_status(card, req);
|
||||
}
|
||||
|
||||
if (brq.sbc.error) {
|
||||
printk(KERN_ERR "%s: error %d sending SET_BLOCK_COUNT "
|
||||
"command, response %#x, card status %#x\n",
|
||||
req->rq_disk->disk_name, brq.sbc.error,
|
||||
brq.sbc.resp[0], status);
|
||||
}
|
||||
|
||||
if (brq.cmd.error) {
|
||||
printk(KERN_ERR "%s: error %d sending read/write "
|
||||
"command, response %#x, card status %#x\n",
|
||||
|
@ -520,8 +905,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
spin_unlock_irq(&md->lock);
|
||||
} while (ret);
|
||||
|
||||
mmc_release_host(card->host);
|
||||
|
||||
return 1;
|
||||
|
||||
cmd_err:
|
||||
|
@ -548,8 +931,6 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
spin_unlock_irq(&md->lock);
|
||||
}
|
||||
|
||||
mmc_release_host(card->host);
|
||||
|
||||
spin_lock_irq(&md->lock);
|
||||
while (ret)
|
||||
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
|
||||
|
@ -560,14 +941,31 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
|||
|
||||
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md = mq->data;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
ret = mmc_blk_part_switch(card, md);
|
||||
if (ret) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (req->cmd_flags & REQ_DISCARD) {
|
||||
if (req->cmd_flags & REQ_SECURE)
|
||||
return mmc_blk_issue_secdiscard_rq(mq, req);
|
||||
ret = mmc_blk_issue_secdiscard_rq(mq, req);
|
||||
else
|
||||
return mmc_blk_issue_discard_rq(mq, req);
|
||||
ret = mmc_blk_issue_discard_rq(mq, req);
|
||||
} else if (req->cmd_flags & REQ_FLUSH) {
|
||||
ret = mmc_blk_issue_flush(mq, req);
|
||||
} else {
|
||||
return mmc_blk_issue_rw_rq(mq, req);
|
||||
ret = mmc_blk_issue_rw_rq(mq, req);
|
||||
}
|
||||
|
||||
out:
|
||||
mmc_release_host(card->host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int mmc_blk_readonly(struct mmc_card *card)
|
||||
|
@ -576,7 +974,11 @@ static inline int mmc_blk_readonly(struct mmc_card *card)
|
|||
!(card->csd.cmdclass & CCC_BLOCK_WRITE);
|
||||
}
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
struct device *parent,
|
||||
sector_t size,
|
||||
bool default_ro,
|
||||
const char *subname)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
|
@ -592,6 +994,19 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* !subname implies we are creating main mmc_blk_data that will be
|
||||
* associated with mmc_card with mmc_set_drvdata. Due to device
|
||||
* partitions, devidx will not coincide with a per-physical card
|
||||
* index anymore so we keep track of a name index.
|
||||
*/
|
||||
if (!subname) {
|
||||
md->name_idx = find_first_zero_bit(name_use, max_devices);
|
||||
__set_bit(md->name_idx, name_use);
|
||||
}
|
||||
else
|
||||
md->name_idx = ((struct mmc_blk_data *)
|
||||
dev_to_disk(parent)->private_data)->name_idx;
|
||||
|
||||
/*
|
||||
* Set the read-only status based on the supported commands
|
||||
|
@ -606,6 +1021,7 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
}
|
||||
|
||||
spin_lock_init(&md->lock);
|
||||
INIT_LIST_HEAD(&md->part);
|
||||
md->usage = 1;
|
||||
|
||||
ret = mmc_init_queue(&md->queue, card, &md->lock);
|
||||
|
@ -620,8 +1036,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
md->disk->fops = &mmc_bdops;
|
||||
md->disk->private_data = md;
|
||||
md->disk->queue = md->queue.queue;
|
||||
md->disk->driverfs_dev = &card->dev;
|
||||
set_disk_ro(md->disk, md->read_only);
|
||||
md->disk->driverfs_dev = parent;
|
||||
set_disk_ro(md->disk, md->read_only || default_ro);
|
||||
|
||||
/*
|
||||
* As discussed on lkml, GENHD_FL_REMOVABLE should:
|
||||
|
@ -636,24 +1052,26 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
*/
|
||||
|
||||
snprintf(md->disk->disk_name, sizeof(md->disk->disk_name),
|
||||
"mmcblk%d", devidx);
|
||||
"mmcblk%d%s", md->name_idx, subname ? subname : "");
|
||||
|
||||
blk_queue_logical_block_size(md->queue.queue, 512);
|
||||
set_capacity(md->disk, size);
|
||||
|
||||
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
|
||||
/*
|
||||
* The EXT_CSD sector count is in number or 512 byte
|
||||
* sectors.
|
||||
*/
|
||||
set_capacity(md->disk, card->ext_csd.sectors);
|
||||
} else {
|
||||
/*
|
||||
* The CSD capacity field is in units of read_blkbits.
|
||||
* set_capacity takes units of 512 bytes.
|
||||
*/
|
||||
set_capacity(md->disk,
|
||||
card->csd.capacity << (card->csd.read_blkbits - 9));
|
||||
if (mmc_host_cmd23(card->host)) {
|
||||
if (mmc_card_mmc(card) ||
|
||||
(mmc_card_sd(card) &&
|
||||
card->scr.cmds & SD_SCR_CMD23_SUPPORT))
|
||||
md->flags |= MMC_BLK_CMD23;
|
||||
}
|
||||
|
||||
if (mmc_card_mmc(card) &&
|
||||
md->flags & MMC_BLK_CMD23 &&
|
||||
((card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN) ||
|
||||
card->ext_csd.rel_sectors)) {
|
||||
md->flags |= MMC_BLK_REL_WR;
|
||||
blk_queue_flush(md->queue.queue, REQ_FLUSH | REQ_FUA);
|
||||
}
|
||||
|
||||
return md;
|
||||
|
||||
err_putdisk:
|
||||
|
@ -664,6 +1082,79 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
{
|
||||
sector_t size;
|
||||
struct mmc_blk_data *md;
|
||||
|
||||
if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
|
||||
/*
|
||||
* The EXT_CSD sector count is in number or 512 byte
|
||||
* sectors.
|
||||
*/
|
||||
size = card->ext_csd.sectors;
|
||||
} else {
|
||||
/*
|
||||
* The CSD capacity field is in units of read_blkbits.
|
||||
* set_capacity takes units of 512 bytes.
|
||||
*/
|
||||
size = card->csd.capacity << (card->csd.read_blkbits - 9);
|
||||
}
|
||||
|
||||
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);
|
||||
return md;
|
||||
}
|
||||
|
||||
static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||
struct mmc_blk_data *md,
|
||||
unsigned int part_type,
|
||||
sector_t size,
|
||||
bool default_ro,
|
||||
const char *subname)
|
||||
{
|
||||
char cap_str[10];
|
||||
struct mmc_blk_data *part_md;
|
||||
|
||||
part_md = mmc_blk_alloc_req(card, disk_to_dev(md->disk), size, default_ro,
|
||||
subname);
|
||||
if (IS_ERR(part_md))
|
||||
return PTR_ERR(part_md);
|
||||
part_md->part_type = part_type;
|
||||
list_add(&part_md->part, &md->part);
|
||||
|
||||
string_get_size((u64)get_capacity(part_md->disk) << 9, STRING_UNITS_2,
|
||||
cap_str, sizeof(cap_str));
|
||||
printk(KERN_INFO "%s: %s %s partition %u %s\n",
|
||||
part_md->disk->disk_name, mmc_card_id(card),
|
||||
mmc_card_name(card), part_md->part_type, cap_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!mmc_card_mmc(card))
|
||||
return 0;
|
||||
|
||||
if (card->ext_csd.boot_size) {
|
||||
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT0,
|
||||
card->ext_csd.boot_size >> 9,
|
||||
true,
|
||||
"boot0");
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = mmc_blk_alloc_part(card, md, EXT_CSD_PART_CONFIG_ACC_BOOT1,
|
||||
card->ext_csd.boot_size >> 9,
|
||||
true,
|
||||
"boot1");
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||
{
|
||||
|
@ -682,9 +1173,81 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_blk_remove_req(struct mmc_blk_data *md)
|
||||
{
|
||||
if (md) {
|
||||
if (md->disk->flags & GENHD_FL_UP) {
|
||||
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
|
||||
|
||||
/* Stop new requests from getting into the queue */
|
||||
del_gendisk(md->disk);
|
||||
}
|
||||
|
||||
/* Then flush out any already in there */
|
||||
mmc_cleanup_queue(&md->queue);
|
||||
mmc_blk_put(md);
|
||||
}
|
||||
}
|
||||
|
||||
static void mmc_blk_remove_parts(struct mmc_card *card,
|
||||
struct mmc_blk_data *md)
|
||||
{
|
||||
struct list_head *pos, *q;
|
||||
struct mmc_blk_data *part_md;
|
||||
|
||||
__clear_bit(md->name_idx, name_use);
|
||||
list_for_each_safe(pos, q, &md->part) {
|
||||
part_md = list_entry(pos, struct mmc_blk_data, part);
|
||||
list_del(pos);
|
||||
mmc_blk_remove_req(part_md);
|
||||
}
|
||||
}
|
||||
|
||||
static int mmc_add_disk(struct mmc_blk_data *md)
|
||||
{
|
||||
int ret;
|
||||
|
||||
add_disk(md->disk);
|
||||
md->force_ro.show = force_ro_show;
|
||||
md->force_ro.store = force_ro_store;
|
||||
sysfs_attr_init(&md->force_ro.attr);
|
||||
md->force_ro.attr.name = "force_ro";
|
||||
md->force_ro.attr.mode = S_IRUGO | S_IWUSR;
|
||||
ret = device_create_file(disk_to_dev(md->disk), &md->force_ro);
|
||||
if (ret)
|
||||
del_gendisk(md->disk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct mmc_fixup blk_fixups[] =
|
||||
{
|
||||
MMC_FIXUP("SEM02G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM04G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM08G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM32G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
|
||||
|
||||
/*
|
||||
* Some MMC cards experience performance degradation with CMD23
|
||||
* instead of CMD12-bounded multiblock transfers. For now we'll
|
||||
* black list what's bad...
|
||||
* - Certain Toshiba cards.
|
||||
*
|
||||
* N.B. This doesn't affect SD cards.
|
||||
*/
|
||||
MMC_FIXUP("MMC08G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC16G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC32G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
static int mmc_blk_probe(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
int err;
|
||||
char cap_str[10];
|
||||
|
||||
|
@ -708,14 +1271,24 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||
md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),
|
||||
cap_str, md->read_only ? "(ro)" : "");
|
||||
|
||||
if (mmc_blk_alloc_parts(card, md))
|
||||
goto out;
|
||||
|
||||
mmc_set_drvdata(card, md);
|
||||
add_disk(md->disk);
|
||||
mmc_fixup_device(card, blk_fixups);
|
||||
|
||||
if (mmc_add_disk(md))
|
||||
goto out;
|
||||
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
if (mmc_add_disk(part_md))
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mmc_cleanup_queue(&md->queue);
|
||||
mmc_blk_put(md);
|
||||
|
||||
mmc_blk_remove_parts(card, md);
|
||||
mmc_blk_remove_req(md);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -723,36 +1296,43 @@ static void mmc_blk_remove(struct mmc_card *card)
|
|||
{
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
/* Stop new requests from getting into the queue */
|
||||
del_gendisk(md->disk);
|
||||
|
||||
/* Then flush out any already in there */
|
||||
mmc_cleanup_queue(&md->queue);
|
||||
|
||||
mmc_blk_put(md);
|
||||
}
|
||||
mmc_blk_remove_parts(card, md);
|
||||
mmc_blk_remove_req(md);
|
||||
mmc_set_drvdata(card, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
mmc_queue_suspend(&md->queue);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_suspend(&part_md->queue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_blk_resume(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
mmc_blk_set_blksize(md, card);
|
||||
|
||||
/*
|
||||
* Resume involves the card going into idle state,
|
||||
* so current partition is always the main one.
|
||||
*/
|
||||
md->part_curr = md->part_type;
|
||||
mmc_queue_resume(&md->queue);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_resume(&part_md->queue);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -212,7 +212,7 @@ static int mmc_test_busy(struct mmc_command *cmd)
|
|||
static int mmc_test_wait_busy(struct mmc_test_card *test)
|
||||
{
|
||||
int ret, busy;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
busy = 0;
|
||||
do {
|
||||
|
@ -246,18 +246,13 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test,
|
|||
{
|
||||
int ret;
|
||||
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command stop;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
memset(&data, 0, sizeof(struct mmc_data));
|
||||
memset(&stop, 0, sizeof(struct mmc_command));
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
mrq.stop = &stop;
|
||||
|
@ -731,15 +726,10 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
|
|||
struct scatterlist *sg, unsigned sg_len, unsigned dev_addr,
|
||||
unsigned blocks, unsigned blksz, int write)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command stop;
|
||||
struct mmc_data data;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
memset(&data, 0, sizeof(struct mmc_data));
|
||||
memset(&stop, 0, sizeof(struct mmc_command));
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
|
@ -761,18 +751,13 @@ static int mmc_test_simple_transfer(struct mmc_test_card *test,
|
|||
static int mmc_test_broken_transfer(struct mmc_test_card *test,
|
||||
unsigned blocks, unsigned blksz, int write)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command stop;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
|
||||
struct scatterlist sg;
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
memset(&data, 0, sizeof(struct mmc_data));
|
||||
memset(&stop, 0, sizeof(struct mmc_command));
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.data = &data;
|
||||
mrq.stop = &stop;
|
||||
|
@ -1401,8 +1386,9 @@ static int mmc_test_area_io(struct mmc_test_card *test, unsigned long sz,
|
|||
*/
|
||||
static int mmc_test_area_fill(struct mmc_test_card *test)
|
||||
{
|
||||
return mmc_test_area_io(test, test->area.max_tfr, test->area.dev_addr,
|
||||
1, 0, 0);
|
||||
struct mmc_test_area *t = &test->area;
|
||||
|
||||
return mmc_test_area_io(test, t->max_tfr, t->dev_addr, 1, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1415,7 +1401,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test)
|
|||
if (!mmc_can_erase(test->card))
|
||||
return 0;
|
||||
|
||||
return mmc_erase(test->card, t->dev_addr, test->area.max_sz >> 9,
|
||||
return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9,
|
||||
MMC_ERASE_ARG);
|
||||
}
|
||||
|
||||
|
@ -1542,8 +1528,10 @@ static int mmc_test_area_prepare_fill(struct mmc_test_card *test)
|
|||
static int mmc_test_best_performance(struct mmc_test_card *test, int write,
|
||||
int max_scatter)
|
||||
{
|
||||
return mmc_test_area_io(test, test->area.max_tfr, test->area.dev_addr,
|
||||
write, max_scatter, 1);
|
||||
struct mmc_test_area *t = &test->area;
|
||||
|
||||
return mmc_test_area_io(test, t->max_tfr, t->dev_addr, write,
|
||||
max_scatter, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1583,18 +1571,19 @@ static int mmc_test_best_write_perf_max_scatter(struct mmc_test_card *test)
|
|||
*/
|
||||
static int mmc_test_profile_read_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
unsigned int dev_addr;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
dev_addr = test->area.dev_addr + (sz >> 9);
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
dev_addr = test->area.dev_addr;
|
||||
sz = t->max_tfr;
|
||||
dev_addr = t->dev_addr;
|
||||
return mmc_test_area_io(test, sz, dev_addr, 0, 0, 1);
|
||||
}
|
||||
|
||||
|
@ -1603,6 +1592,7 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test)
|
|||
*/
|
||||
static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
unsigned int dev_addr;
|
||||
int ret;
|
||||
|
@ -1610,8 +1600,8 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
|||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
dev_addr = test->area.dev_addr + (sz >> 9);
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1619,8 +1609,8 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
|||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
sz = test->area.max_tfr;
|
||||
dev_addr = test->area.dev_addr;
|
||||
sz = t->max_tfr;
|
||||
dev_addr = t->dev_addr;
|
||||
return mmc_test_area_io(test, sz, dev_addr, 1, 0, 1);
|
||||
}
|
||||
|
||||
|
@ -1629,6 +1619,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test)
|
|||
*/
|
||||
static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
unsigned int dev_addr;
|
||||
struct timespec ts1, ts2;
|
||||
|
@ -1640,8 +1631,8 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
|||
if (!mmc_can_erase(test->card))
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
for (sz = 512; sz < test->area.max_sz; sz <<= 1) {
|
||||
dev_addr = test->area.dev_addr + (sz >> 9);
|
||||
for (sz = 512; sz < t->max_sz; sz <<= 1) {
|
||||
dev_addr = t->dev_addr + (sz >> 9);
|
||||
getnstimeofday(&ts1);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
|
||||
if (ret)
|
||||
|
@ -1649,7 +1640,7 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
|||
getnstimeofday(&ts2);
|
||||
mmc_test_print_rate(test, sz, &ts1, &ts2);
|
||||
}
|
||||
dev_addr = test->area.dev_addr;
|
||||
dev_addr = t->dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG);
|
||||
if (ret)
|
||||
|
@ -1661,12 +1652,13 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test)
|
|||
|
||||
static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
cnt = t->max_sz / sz;
|
||||
dev_addr = t->dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0);
|
||||
|
@ -1684,20 +1676,22 @@ static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz)
|
|||
*/
|
||||
static int mmc_test_profile_seq_read_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
ret = mmc_test_seq_read_perf(test, sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
sz = t->max_tfr;
|
||||
return mmc_test_seq_read_perf(test, sz);
|
||||
}
|
||||
|
||||
static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
@ -1705,8 +1699,8 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
|
|||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
cnt = t->max_sz / sz;
|
||||
dev_addr = t->dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0);
|
||||
|
@ -1724,15 +1718,16 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz)
|
|||
*/
|
||||
static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
ret = mmc_test_seq_write_perf(test, sz);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
sz = t->max_tfr;
|
||||
return mmc_test_seq_write_perf(test, sz);
|
||||
}
|
||||
|
||||
|
@ -1741,6 +1736,7 @@ static int mmc_test_profile_seq_write_perf(struct mmc_test_card *test)
|
|||
*/
|
||||
static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long sz;
|
||||
unsigned int dev_addr, i, cnt;
|
||||
struct timespec ts1, ts2;
|
||||
|
@ -1752,15 +1748,15 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test)
|
|||
if (!mmc_can_erase(test->card))
|
||||
return RESULT_UNSUP_HOST;
|
||||
|
||||
for (sz = 512; sz <= test->area.max_sz; sz <<= 1) {
|
||||
for (sz = 512; sz <= t->max_sz; sz <<= 1) {
|
||||
ret = mmc_test_area_erase(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = mmc_test_area_fill(test);
|
||||
if (ret)
|
||||
return ret;
|
||||
cnt = test->area.max_sz / sz;
|
||||
dev_addr = test->area.dev_addr;
|
||||
cnt = t->max_sz / sz;
|
||||
dev_addr = t->dev_addr;
|
||||
getnstimeofday(&ts1);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
ret = mmc_erase(test->card, dev_addr, sz >> 9,
|
||||
|
@ -1823,11 +1819,12 @@ static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print,
|
|||
|
||||
static int mmc_test_random_perf(struct mmc_test_card *test, int write)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned int next;
|
||||
unsigned long sz;
|
||||
int ret;
|
||||
|
||||
for (sz = 512; sz < test->area.max_tfr; sz <<= 1) {
|
||||
for (sz = 512; sz < t->max_tfr; sz <<= 1) {
|
||||
/*
|
||||
* When writing, try to get more consistent results by running
|
||||
* the test twice with exactly the same I/O but outputting the
|
||||
|
@ -1844,7 +1841,7 @@ static int mmc_test_random_perf(struct mmc_test_card *test, int write)
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
sz = test->area.max_tfr;
|
||||
sz = t->max_tfr;
|
||||
if (write) {
|
||||
next = rnd_next;
|
||||
ret = mmc_test_rnd_perf(test, write, 0, sz);
|
||||
|
@ -1874,17 +1871,18 @@ static int mmc_test_random_write_perf(struct mmc_test_card *test)
|
|||
static int mmc_test_seq_perf(struct mmc_test_card *test, int write,
|
||||
unsigned int tot_sz, int max_scatter)
|
||||
{
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned int dev_addr, i, cnt, sz, ssz;
|
||||
struct timespec ts1, ts2;
|
||||
int ret;
|
||||
|
||||
sz = test->area.max_tfr;
|
||||
sz = t->max_tfr;
|
||||
|
||||
/*
|
||||
* In the case of a maximally scattered transfer, the maximum transfer
|
||||
* size is further limited by using PAGE_SIZE segments.
|
||||
*/
|
||||
if (max_scatter) {
|
||||
struct mmc_test_area *t = &test->area;
|
||||
unsigned long max_tfr;
|
||||
|
||||
if (t->max_seg_sz >= PAGE_SIZE)
|
||||
|
|
|
@ -343,18 +343,14 @@ unsigned int mmc_queue_map_sg(struct mmc_queue *mq)
|
|||
*/
|
||||
void mmc_queue_bounce_pre(struct mmc_queue *mq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!mq->bounce_buf)
|
||||
return;
|
||||
|
||||
if (rq_data_dir(mq->req) != WRITE)
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
sg_copy_to_buffer(mq->bounce_sg, mq->bounce_sg_len,
|
||||
mq->bounce_buf, mq->sg[0].length);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -363,17 +359,13 @@ void mmc_queue_bounce_pre(struct mmc_queue *mq)
|
|||
*/
|
||||
void mmc_queue_bounce_post(struct mmc_queue *mq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (!mq->bounce_buf)
|
||||
return;
|
||||
|
||||
if (rq_data_dir(mq->req) != READ)
|
||||
return;
|
||||
|
||||
local_irq_save(flags);
|
||||
sg_copy_from_buffer(mq->bounce_sg, mq->bounce_sg_len,
|
||||
mq->bounce_buf, mq->sg[0].length);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
|
|
|
@ -274,8 +274,12 @@ int mmc_add_card(struct mmc_card *card)
|
|||
break;
|
||||
case MMC_TYPE_SD:
|
||||
type = "SD";
|
||||
if (mmc_card_blockaddr(card))
|
||||
type = "SDHC";
|
||||
if (mmc_card_blockaddr(card)) {
|
||||
if (mmc_card_ext_capacity(card))
|
||||
type = "SDXC";
|
||||
else
|
||||
type = "SDHC";
|
||||
}
|
||||
break;
|
||||
case MMC_TYPE_SDIO:
|
||||
type = "SDIO";
|
||||
|
@ -299,7 +303,8 @@ int mmc_add_card(struct mmc_card *card)
|
|||
} else {
|
||||
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
mmc_sd_card_uhs(card) ? "ultra high speed " :
|
||||
(mmc_card_highspeed(card) ? "high speed " : ""),
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type, card->rca);
|
||||
}
|
||||
|
|
|
@ -236,12 +236,10 @@ EXPORT_SYMBOL(mmc_wait_for_req);
|
|||
*/
|
||||
int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_request mrq = {0};
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
memset(&mrq, 0, sizeof(struct mmc_request));
|
||||
|
||||
memset(cmd->resp, 0, sizeof(cmd->resp));
|
||||
cmd->retries = retries;
|
||||
|
||||
|
@ -719,23 +717,13 @@ void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode)
|
|||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width and DDR mode of a host.
|
||||
*/
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr)
|
||||
{
|
||||
host->ios.bus_width = width;
|
||||
host->ios.ddr = ddr;
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change data bus width of a host.
|
||||
*/
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width)
|
||||
{
|
||||
mmc_set_bus_width_ddr(host, width, MMC_SDR_MODE);
|
||||
host->ios.bus_width = width;
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -944,6 +932,38 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
|
|||
return ocr;
|
||||
}
|
||||
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, bool cmd11)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
/*
|
||||
* Send CMD11 only if the request is to switch the card to
|
||||
* 1.8V signalling.
|
||||
*/
|
||||
if ((signal_voltage != MMC_SIGNAL_VOLTAGE_330) && cmd11) {
|
||||
cmd.opcode = SD_SWITCH_VOLTAGE;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!mmc_host_is_spi(host) && (cmd.resp[0] & R1_ERROR))
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
host->ios.signal_voltage = signal_voltage;
|
||||
|
||||
if (host->ops->start_signal_voltage_switch)
|
||||
err = host->ops->start_signal_voltage_switch(host, &host->ios);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select timing parameters for host.
|
||||
*/
|
||||
|
@ -953,6 +973,15 @@ void mmc_set_timing(struct mmc_host *host, unsigned int timing)
|
|||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select appropriate driver type for host.
|
||||
*/
|
||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
|
||||
{
|
||||
host->ios.drv_type = drv_type;
|
||||
mmc_set_ios(host);
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply power to the MMC stack. This is a two-stage process.
|
||||
* First, we enable power to the card without the clock running.
|
||||
|
@ -1187,9 +1216,8 @@ void mmc_init_erase(struct mmc_card *card)
|
|||
}
|
||||
}
|
||||
|
||||
static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
|
||||
struct mmc_command *cmd,
|
||||
unsigned int arg, unsigned int qty)
|
||||
static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
|
||||
unsigned int arg, unsigned int qty)
|
||||
{
|
||||
unsigned int erase_timeout;
|
||||
|
||||
|
@ -1246,44 +1274,48 @@ static void mmc_set_mmc_erase_timeout(struct mmc_card *card,
|
|||
if (mmc_host_is_spi(card->host) && erase_timeout < 1000)
|
||||
erase_timeout = 1000;
|
||||
|
||||
cmd->erase_timeout = erase_timeout;
|
||||
return erase_timeout;
|
||||
}
|
||||
|
||||
static void mmc_set_sd_erase_timeout(struct mmc_card *card,
|
||||
struct mmc_command *cmd, unsigned int arg,
|
||||
unsigned int qty)
|
||||
static unsigned int mmc_sd_erase_timeout(struct mmc_card *card,
|
||||
unsigned int arg,
|
||||
unsigned int qty)
|
||||
{
|
||||
unsigned int erase_timeout;
|
||||
|
||||
if (card->ssr.erase_timeout) {
|
||||
/* Erase timeout specified in SD Status Register (SSR) */
|
||||
cmd->erase_timeout = card->ssr.erase_timeout * qty +
|
||||
card->ssr.erase_offset;
|
||||
erase_timeout = card->ssr.erase_timeout * qty +
|
||||
card->ssr.erase_offset;
|
||||
} else {
|
||||
/*
|
||||
* Erase timeout not specified in SD Status Register (SSR) so
|
||||
* use 250ms per write block.
|
||||
*/
|
||||
cmd->erase_timeout = 250 * qty;
|
||||
erase_timeout = 250 * qty;
|
||||
}
|
||||
|
||||
/* Must not be less than 1 second */
|
||||
if (cmd->erase_timeout < 1000)
|
||||
cmd->erase_timeout = 1000;
|
||||
if (erase_timeout < 1000)
|
||||
erase_timeout = 1000;
|
||||
|
||||
return erase_timeout;
|
||||
}
|
||||
|
||||
static void mmc_set_erase_timeout(struct mmc_card *card,
|
||||
struct mmc_command *cmd, unsigned int arg,
|
||||
unsigned int qty)
|
||||
static unsigned int mmc_erase_timeout(struct mmc_card *card,
|
||||
unsigned int arg,
|
||||
unsigned int qty)
|
||||
{
|
||||
if (mmc_card_sd(card))
|
||||
mmc_set_sd_erase_timeout(card, cmd, arg, qty);
|
||||
return mmc_sd_erase_timeout(card, arg, qty);
|
||||
else
|
||||
mmc_set_mmc_erase_timeout(card, cmd, arg, qty);
|
||||
return mmc_mmc_erase_timeout(card, arg, qty);
|
||||
}
|
||||
|
||||
static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
||||
unsigned int to, unsigned int arg)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned int qty = 0;
|
||||
int err;
|
||||
|
||||
|
@ -1317,7 +1349,6 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||
to <<= 9;
|
||||
}
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
if (mmc_card_sd(card))
|
||||
cmd.opcode = SD_ERASE_WR_BLK_START;
|
||||
else
|
||||
|
@ -1351,7 +1382,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
|
|||
cmd.opcode = MMC_ERASE;
|
||||
cmd.arg = arg;
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
mmc_set_erase_timeout(card, &cmd, arg, qty);
|
||||
cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, 0);
|
||||
if (err) {
|
||||
printk(KERN_ERR "mmc_erase: erase error %d, status %#x\n",
|
||||
|
@ -1487,12 +1518,11 @@ EXPORT_SYMBOL(mmc_erase_group_aligned);
|
|||
|
||||
int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
|
||||
return 0;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
cmd.arg = blocklen;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
@ -1578,7 +1608,7 @@ void mmc_rescan(struct work_struct *work)
|
|||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
|
||||
break;
|
||||
if (freqs[i] < host->f_min)
|
||||
if (freqs[i] <= host->f_min)
|
||||
break;
|
||||
}
|
||||
mmc_release_host(host);
|
||||
|
@ -1746,7 +1776,7 @@ int mmc_suspend_host(struct mmc_host *host)
|
|||
}
|
||||
mmc_bus_put(host);
|
||||
|
||||
if (!err && !(host->pm_flags & MMC_PM_KEEP_POWER))
|
||||
if (!err && !mmc_card_keep_power(host))
|
||||
mmc_power_off(host);
|
||||
|
||||
return err;
|
||||
|
@ -1764,7 +1794,7 @@ int mmc_resume_host(struct mmc_host *host)
|
|||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
if (!(host->pm_flags & MMC_PM_KEEP_POWER)) {
|
||||
if (!mmc_card_keep_power(host)) {
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
/*
|
||||
|
@ -1789,6 +1819,7 @@ int mmc_resume_host(struct mmc_host *host)
|
|||
err = 0;
|
||||
}
|
||||
}
|
||||
host->pm_flags &= ~MMC_PM_KEEP_POWER;
|
||||
mmc_bus_put(host);
|
||||
|
||||
return err;
|
||||
|
|
|
@ -38,10 +38,11 @@ void mmc_ungate_clock(struct mmc_host *host);
|
|||
void mmc_set_ungated(struct mmc_host *host);
|
||||
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
|
||||
void mmc_set_bus_width_ddr(struct mmc_host *host, unsigned int width,
|
||||
unsigned int ddr);
|
||||
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage,
|
||||
bool cmd11);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||
|
||||
static inline void mmc_delay(unsigned int ms)
|
||||
{
|
||||
|
@ -61,8 +62,6 @@ int mmc_attach_mmc(struct mmc_host *host);
|
|||
int mmc_attach_sd(struct mmc_host *host);
|
||||
int mmc_attach_sdio(struct mmc_host *host);
|
||||
|
||||
void mmc_fixup_device(struct mmc_card *card);
|
||||
|
||||
/* Module parameters */
|
||||
extern int use_spi_crc;
|
||||
|
||||
|
|
|
@ -325,12 +325,12 @@ int mmc_add_host(struct mmc_host *host)
|
|||
WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
!host->ops->enable_sdio_irq);
|
||||
|
||||
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
|
||||
|
||||
err = device_add(&host->class_dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
mmc_add_host_debugfs(host);
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "core.h"
|
||||
#include "bus.h"
|
||||
#include "mmc_ops.h"
|
||||
#include "sd_ops.h"
|
||||
|
||||
static const unsigned int tran_exp[] = {
|
||||
10000, 100000, 1000000, 10000000,
|
||||
|
@ -173,14 +174,17 @@ static int mmc_decode_csd(struct mmc_card *card)
|
|||
}
|
||||
|
||||
/*
|
||||
* Read and decode extended CSD.
|
||||
* Read extended CSD.
|
||||
*/
|
||||
static int mmc_read_ext_csd(struct mmc_card *card)
|
||||
static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
||||
{
|
||||
int err;
|
||||
u8 *ext_csd;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!new_ext_csd);
|
||||
|
||||
*new_ext_csd = NULL;
|
||||
|
||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
||||
return 0;
|
||||
|
@ -198,12 +202,15 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
|
||||
err = mmc_send_ext_csd(card, ext_csd);
|
||||
if (err) {
|
||||
kfree(ext_csd);
|
||||
*new_ext_csd = NULL;
|
||||
|
||||
/* If the host or the card can't do the switch,
|
||||
* fail more gracefully. */
|
||||
if ((err != -EINVAL)
|
||||
&& (err != -ENOSYS)
|
||||
&& (err != -EFAULT))
|
||||
goto out;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* High capacity cards should have this "magic" size
|
||||
|
@ -221,9 +228,23 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
}
|
||||
} else
|
||||
*new_ext_csd = ext_csd;
|
||||
|
||||
goto out;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode extended CSD.
|
||||
*/
|
||||
static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
if (!ext_csd)
|
||||
return 0;
|
||||
|
||||
/* Version is coded in the CSD_STRUCTURE byte in the EXT_CSD register */
|
||||
if (card->csd.structure == 3) {
|
||||
|
@ -288,6 +309,10 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
|
||||
if (card->ext_csd.rev >= 3) {
|
||||
u8 sa_shift = ext_csd[EXT_CSD_S_A_TIMEOUT];
|
||||
card->ext_csd.part_config = ext_csd[EXT_CSD_PART_CONFIG];
|
||||
|
||||
/* EXT_CSD value is in units of 10ms, but we store in ms */
|
||||
card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME];
|
||||
|
||||
/* Sleep / awake timeout in 100ns units */
|
||||
if (sa_shift > 0 && sa_shift <= 0x17)
|
||||
|
@ -299,6 +324,14 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT];
|
||||
card->ext_csd.hc_erase_size =
|
||||
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] << 10;
|
||||
|
||||
card->ext_csd.rel_sectors = ext_csd[EXT_CSD_REL_WR_SEC_C];
|
||||
|
||||
/*
|
||||
* There are two boot regions of equal size, defined in
|
||||
* multiples of 128K.
|
||||
*/
|
||||
card->ext_csd.boot_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 4) {
|
||||
|
@ -350,14 +383,78 @@ static int mmc_read_ext_csd(struct mmc_card *card)
|
|||
ext_csd[EXT_CSD_TRIM_MULT];
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 5)
|
||||
card->ext_csd.rel_param = ext_csd[EXT_CSD_WR_REL_PARAM];
|
||||
|
||||
if (ext_csd[EXT_CSD_ERASED_MEM_CONT])
|
||||
card->erased_byte = 0xFF;
|
||||
else
|
||||
card->erased_byte = 0x0;
|
||||
|
||||
out:
|
||||
kfree(ext_csd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void mmc_free_ext_csd(u8 *ext_csd)
|
||||
{
|
||||
kfree(ext_csd);
|
||||
}
|
||||
|
||||
|
||||
static int mmc_compare_ext_csds(struct mmc_card *card, u8 *ext_csd,
|
||||
unsigned bus_width)
|
||||
{
|
||||
u8 *bw_ext_csd;
|
||||
int err;
|
||||
|
||||
err = mmc_get_ext_csd(card, &bw_ext_csd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((ext_csd == NULL || bw_ext_csd == NULL)) {
|
||||
if (bus_width != MMC_BUS_WIDTH_1)
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
goto out;
|
||||
|
||||
/* only compare read only fields */
|
||||
err = (!(ext_csd[EXT_CSD_PARTITION_SUPPORT] ==
|
||||
bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) &&
|
||||
(ext_csd[EXT_CSD_ERASED_MEM_CONT] ==
|
||||
bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) &&
|
||||
(ext_csd[EXT_CSD_REV] ==
|
||||
bw_ext_csd[EXT_CSD_REV]) &&
|
||||
(ext_csd[EXT_CSD_STRUCTURE] ==
|
||||
bw_ext_csd[EXT_CSD_STRUCTURE]) &&
|
||||
(ext_csd[EXT_CSD_CARD_TYPE] ==
|
||||
bw_ext_csd[EXT_CSD_CARD_TYPE]) &&
|
||||
(ext_csd[EXT_CSD_S_A_TIMEOUT] ==
|
||||
bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) &&
|
||||
(ext_csd[EXT_CSD_HC_WP_GRP_SIZE] ==
|
||||
bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) &&
|
||||
(ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] ==
|
||||
bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) &&
|
||||
(ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] ==
|
||||
bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) &&
|
||||
(ext_csd[EXT_CSD_SEC_TRIM_MULT] ==
|
||||
bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) &&
|
||||
(ext_csd[EXT_CSD_SEC_ERASE_MULT] ==
|
||||
bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) &&
|
||||
(ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] ==
|
||||
bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) &&
|
||||
(ext_csd[EXT_CSD_TRIM_MULT] ==
|
||||
bw_ext_csd[EXT_CSD_TRIM_MULT]) &&
|
||||
memcmp(&ext_csd[EXT_CSD_SEC_CNT],
|
||||
&bw_ext_csd[EXT_CSD_SEC_CNT],
|
||||
4) != 0);
|
||||
if (err)
|
||||
err = -EINVAL;
|
||||
|
||||
out:
|
||||
mmc_free_ext_csd(bw_ext_csd);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -422,6 +519,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
u32 cid[4];
|
||||
unsigned int max_dtr;
|
||||
u32 rocr;
|
||||
u8 *ext_csd = NULL;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
|
@ -520,7 +618,11 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
/*
|
||||
* Fetch and process extended CSD.
|
||||
*/
|
||||
err = mmc_read_ext_csd(card);
|
||||
|
||||
err = mmc_get_ext_csd(card, &ext_csd);
|
||||
if (err)
|
||||
goto free_card;
|
||||
err = mmc_read_ext_csd(card, ext_csd);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
|
@ -542,7 +644,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
*/
|
||||
if (card->ext_csd.enhanced_area_en) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_ERASE_GROUP_DEF, 1);
|
||||
EXT_CSD_ERASE_GROUP_DEF, 1, 0);
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
|
@ -567,13 +669,25 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure eMMC user default partition is enabled
|
||||
*/
|
||||
if (card->ext_csd.part_config & EXT_CSD_PART_CONFIG_ACC_MASK) {
|
||||
card->ext_csd.part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONFIG,
|
||||
card->ext_csd.part_config,
|
||||
card->ext_csd.part_time);
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate high speed (if supported)
|
||||
*/
|
||||
if ((card->ext_csd.hs_max_dtr != 0) &&
|
||||
(host->caps & MMC_CAP_MMC_HIGHSPEED)) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1);
|
||||
EXT_CSD_HS_TIMING, 1, 0);
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
|
||||
|
@ -606,10 +720,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
*/
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
||||
&& (host->caps & (MMC_CAP_1_8V_DDR)))
|
||||
&& ((host->caps & (MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_UHS_DDR50))
|
||||
== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
|
||||
ddr = MMC_1_8V_DDR_MODE;
|
||||
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
&& (host->caps & (MMC_CAP_1_2V_DDR)))
|
||||
&& ((host->caps & (MMC_CAP_1_2V_DDR |
|
||||
MMC_CAP_UHS_DDR50))
|
||||
== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
|
@ -640,18 +758,22 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
ddr = 0; /* no DDR for 1-bit width */
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx][0]);
|
||||
ext_csd_bits[idx][0],
|
||||
0);
|
||||
if (!err) {
|
||||
mmc_set_bus_width_ddr(card->host,
|
||||
bus_width, MMC_SDR_MODE);
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
|
||||
/*
|
||||
* If controller can't handle bus width test,
|
||||
* use the highest bus width to maintain
|
||||
* compatibility with previous MMC behavior.
|
||||
* compare ext_csd previously read in 1 bit mode
|
||||
* against ext_csd at new bus width
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
|
||||
break;
|
||||
err = mmc_bus_test(card, bus_width);
|
||||
err = mmc_compare_ext_csds(card,
|
||||
ext_csd,
|
||||
bus_width);
|
||||
else
|
||||
err = mmc_bus_test(card, bus_width);
|
||||
if (!err)
|
||||
break;
|
||||
}
|
||||
|
@ -659,8 +781,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
|
||||
if (!err && ddr) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx][1]);
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx][1],
|
||||
0);
|
||||
}
|
||||
if (err) {
|
||||
printk(KERN_WARNING "%s: switch to bus width %d ddr %d "
|
||||
|
@ -668,20 +791,43 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
1 << bus_width, ddr);
|
||||
goto free_card;
|
||||
} else if (ddr) {
|
||||
/*
|
||||
* eMMC cards can support 3.3V to 1.2V i/o (vccq)
|
||||
* signaling.
|
||||
*
|
||||
* EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
|
||||
*
|
||||
* 1.8V vccq at 3.3V core voltage (vcc) is not required
|
||||
* in the JEDEC spec for DDR.
|
||||
*
|
||||
* Do not force change in vccq since we are obviously
|
||||
* working and no change to vccq is needed.
|
||||
*
|
||||
* WARNING: eMMC rules are NOT the same as SD DDR
|
||||
*/
|
||||
if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) {
|
||||
err = mmc_set_signal_voltage(host,
|
||||
MMC_SIGNAL_VOLTAGE_120, 0);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
mmc_card_set_ddr_mode(card);
|
||||
mmc_set_bus_width_ddr(card->host, bus_width, ddr);
|
||||
mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldcard)
|
||||
host->card = card;
|
||||
|
||||
mmc_free_ext_csd(ext_csd);
|
||||
return 0;
|
||||
|
||||
free_card:
|
||||
if (!oldcard)
|
||||
mmc_remove_card(card);
|
||||
err:
|
||||
mmc_free_ext_csd(ext_csd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -23,12 +23,10 @@
|
|||
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SELECT_CARD;
|
||||
|
||||
if (card) {
|
||||
|
@ -60,15 +58,13 @@ int mmc_deselect_cards(struct mmc_host *host)
|
|||
|
||||
int mmc_card_sleepawake(struct mmc_host *host, int sleep)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_card *card = host->card;
|
||||
int err;
|
||||
|
||||
if (sleep)
|
||||
mmc_deselect_cards(host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SLEEP_AWAKE;
|
||||
cmd.arg = card->rca << 16;
|
||||
if (sleep)
|
||||
|
@ -97,7 +93,7 @@ int mmc_card_sleepawake(struct mmc_host *host, int sleep)
|
|||
int mmc_go_idle(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
/*
|
||||
* Non-SPI hosts need to prevent chipselect going active during
|
||||
|
@ -113,8 +109,6 @@ int mmc_go_idle(struct mmc_host *host)
|
|||
mmc_delay(1);
|
||||
}
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_GO_IDLE_STATE;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_NONE | MMC_CMD_BC;
|
||||
|
@ -135,13 +129,11 @@ int mmc_go_idle(struct mmc_host *host)
|
|||
|
||||
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
int i, err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SEND_OP_COND;
|
||||
cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
|
||||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
|
@ -178,13 +170,11 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!cid);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_ALL_SEND_CID;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR;
|
||||
|
@ -201,13 +191,11 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
|
|||
int mmc_set_relative_addr(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SET_RELATIVE_ADDR;
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
@ -223,13 +211,11 @@ static int
|
|||
mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!cxd);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = arg;
|
||||
cmd.flags = MMC_RSP_R2 | MMC_CMD_AC;
|
||||
|
@ -247,9 +233,9 @@ static int
|
|||
mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
||||
u32 opcode, void *buf, unsigned len)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
void *data_buf;
|
||||
|
||||
|
@ -260,10 +246,6 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host,
|
|||
if (data_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -355,11 +337,9 @@ int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
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;
|
||||
|
@ -372,11 +352,9 @@ 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)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
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;
|
||||
|
@ -387,23 +365,34 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
|
|||
return err;
|
||||
}
|
||||
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
|
||||
/**
|
||||
* mmc_switch - modify EXT_CSD register
|
||||
* @card: the MMC card associated with the data transfer
|
||||
* @set: cmd set values
|
||||
* @index: EXT_CSD register index
|
||||
* @value: value to program into EXT_CSD register
|
||||
* @timeout_ms: timeout (ms) for operation performed by register write,
|
||||
* timeout of zero implies maximum possible timeout
|
||||
*
|
||||
* Modifies the EXT_CSD register for selected card.
|
||||
*/
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
u32 status;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SWITCH;
|
||||
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
|
||||
(index << 16) |
|
||||
(value << 8) |
|
||||
set;
|
||||
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
|
||||
cmd.cmd_timeout_ms = timeout_ms;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
|
@ -433,17 +422,16 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
|
@ -466,9 +454,9 @@ static int
|
|||
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||
u8 len)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
u8 *data_buf;
|
||||
u8 *test_buf;
|
||||
|
@ -497,10 +485,6 @@ mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
|||
if (opcode == MMC_BUS_TEST_W)
|
||||
memcpy(data_buf, test_buf, len);
|
||||
|
||||
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 = opcode;
|
||||
|
|
|
@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
|
|||
int mmc_set_relative_addr(struct mmc_card *card);
|
||||
int mmc_send_csd(struct mmc_card *card, u32 *csd);
|
||||
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status);
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* This file contains work-arounds for many known sdio hardware
|
||||
* bugs.
|
||||
* This file contains work-arounds for many known SD/MMC
|
||||
* and SDIO hardware bugs.
|
||||
*
|
||||
* Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com>
|
||||
* Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
|
||||
* Inspired from pci fixup code:
|
||||
* Copyright (c) 1999 Martin Mares <mj@ucw.cz>
|
||||
|
@ -11,34 +12,14 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
/*
|
||||
* The world is not perfect and supplies us with broken mmc/sdio devices.
|
||||
* For at least a part of these bugs we need a work-around
|
||||
*/
|
||||
#ifndef SDIO_VENDOR_ID_TI
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
#endif
|
||||
|
||||
struct mmc_fixup {
|
||||
u16 vendor, device; /* You can use SDIO_ANY_ID here of course */
|
||||
void (*vendor_fixup)(struct mmc_card *card, int data);
|
||||
int data;
|
||||
};
|
||||
|
||||
/*
|
||||
* This hook just adds a quirk unconditionnally
|
||||
*/
|
||||
static void __maybe_unused add_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
/*
|
||||
* This hook just removes a quirk unconditionnally
|
||||
*/
|
||||
static void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
#ifndef SDIO_DEVICE_ID_TI_WL1271
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This hook just adds a quirk for all sdio devices
|
||||
|
@ -49,33 +30,47 @@ static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
|
|||
card->quirks |= data;
|
||||
}
|
||||
|
||||
#ifndef SDIO_VENDOR_ID_TI
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
#endif
|
||||
|
||||
#ifndef SDIO_DEVICE_ID_TI_WL1271
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
#endif
|
||||
|
||||
static const struct mmc_fixup mmc_fixup_methods[] = {
|
||||
/* by default sdio devices are considered CLK_GATING broken */
|
||||
/* good cards will be whitelisted as they are tested */
|
||||
{ SDIO_ANY_ID, SDIO_ANY_ID,
|
||||
add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING },
|
||||
{ SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING },
|
||||
{ 0 }
|
||||
SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
|
||||
add_quirk_for_sdio_devices,
|
||||
MMC_QUIRK_BROKEN_CLK_GATING),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_NONSTD_FUNC_IF),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
|
||||
add_quirk, MMC_QUIRK_DISABLE_CD),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
void mmc_fixup_device(struct mmc_card *card)
|
||||
void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table)
|
||||
{
|
||||
const struct mmc_fixup *f;
|
||||
u64 rev = cid_rev_card(card);
|
||||
|
||||
for (f = mmc_fixup_methods; f->vendor_fixup; f++) {
|
||||
if ((f->vendor == card->cis.vendor
|
||||
|| f->vendor == (u16) SDIO_ANY_ID) &&
|
||||
(f->device == card->cis.device
|
||||
|| f->device == (u16) SDIO_ANY_ID)) {
|
||||
/* Non-core specific workarounds. */
|
||||
if (!table)
|
||||
table = mmc_fixup_methods;
|
||||
|
||||
for (f = table; f->vendor_fixup; f++) {
|
||||
if ((f->manfid == CID_MANFID_ANY ||
|
||||
f->manfid == card->cid.manfid) &&
|
||||
(f->oemid == CID_OEMID_ANY ||
|
||||
f->oemid == card->cid.oemid) &&
|
||||
(f->name == CID_NAME_ANY ||
|
||||
!strncmp(f->name, card->cid.prod_name,
|
||||
sizeof(card->cid.prod_name))) &&
|
||||
(f->cis_vendor == card->cis.vendor ||
|
||||
f->cis_vendor == (u16) SDIO_ANY_ID) &&
|
||||
(f->cis_device == card->cis.device ||
|
||||
f->cis_device == (u16) SDIO_ANY_ID) &&
|
||||
rev >= f->rev_start && rev <= f->rev_end) {
|
||||
dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
|
||||
f->vendor_fixup(card, f->data);
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@ static int mmc_decode_csd(struct mmc_card *card)
|
|||
break;
|
||||
case 1:
|
||||
/*
|
||||
* This is a block-addressed SDHC card. Most
|
||||
* This is a block-addressed SDHC or SDXC card. Most
|
||||
* interesting fields are unused and have fixed
|
||||
* values. To avoid getting tripped by buggy cards,
|
||||
* we assume those fixed values ourselves.
|
||||
|
@ -144,6 +144,11 @@ static int mmc_decode_csd(struct mmc_card *card)
|
|||
e = UNSTUFF_BITS(resp, 96, 3);
|
||||
csd->max_dtr = tran_exp[e] * tran_mant[m];
|
||||
csd->cmdclass = UNSTUFF_BITS(resp, 84, 12);
|
||||
csd->c_size = UNSTUFF_BITS(resp, 48, 22);
|
||||
|
||||
/* SDXC cards have a minimum C_SIZE of 0x00FFFF */
|
||||
if (csd->c_size >= 0xFFFF)
|
||||
mmc_card_set_ext_capacity(card);
|
||||
|
||||
m = UNSTUFF_BITS(resp, 48, 22);
|
||||
csd->capacity = (1 + m) << 10;
|
||||
|
@ -189,12 +194,17 @@ static int mmc_decode_scr(struct mmc_card *card)
|
|||
|
||||
scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4);
|
||||
scr->bus_widths = UNSTUFF_BITS(resp, 48, 4);
|
||||
if (scr->sda_vsn == SCR_SPEC_VER_2)
|
||||
/* Check if Physical Layer Spec v3.0 is supported */
|
||||
scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
|
||||
|
||||
if (UNSTUFF_BITS(resp, 55, 1))
|
||||
card->erased_byte = 0xFF;
|
||||
else
|
||||
card->erased_byte = 0x0;
|
||||
|
||||
if (scr->sda_spec3)
|
||||
scr->cmds = UNSTUFF_BITS(resp, 32, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -274,29 +284,74 @@ static int mmc_read_switch(struct mmc_card *card)
|
|||
status = kmalloc(64, GFP_KERNEL);
|
||||
if (!status) {
|
||||
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 -ENOMEM;
|
||||
}
|
||||
|
||||
/* Find out the supported Bus Speed Modes. */
|
||||
err = mmc_sd_switch(card, 0, 0, 1, status);
|
||||
if (err) {
|
||||
/* If the host or the card can't do the switch,
|
||||
* fail more gracefully. */
|
||||
if ((err != -EINVAL)
|
||||
&& (err != -ENOSYS)
|
||||
&& (err != -EFAULT))
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
* fail more gracefully.
|
||||
*/
|
||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||
goto out;
|
||||
|
||||
printk(KERN_WARNING "%s: problem reading switch "
|
||||
"capabilities, performance might suffer.\n",
|
||||
printk(KERN_WARNING "%s: problem reading Bus Speed modes.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status[13] & 0x02)
|
||||
card->sw_caps.hs_max_dtr = 50000000;
|
||||
if (card->scr.sda_spec3) {
|
||||
card->sw_caps.sd3_bus_mode = status[13];
|
||||
|
||||
/* Find out Driver Strengths supported by the card */
|
||||
err = mmc_sd_switch(card, 0, 2, 1, status);
|
||||
if (err) {
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
* fail more gracefully.
|
||||
*/
|
||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||
goto out;
|
||||
|
||||
printk(KERN_WARNING "%s: problem reading "
|
||||
"Driver Strength.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
card->sw_caps.sd3_drv_type = status[9];
|
||||
|
||||
/* Find out Current Limits supported by the card */
|
||||
err = mmc_sd_switch(card, 0, 3, 1, status);
|
||||
if (err) {
|
||||
/*
|
||||
* If the host or the card can't do the switch,
|
||||
* fail more gracefully.
|
||||
*/
|
||||
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
|
||||
goto out;
|
||||
|
||||
printk(KERN_WARNING "%s: problem reading "
|
||||
"Current Limit.\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
card->sw_caps.sd3_curr_limit = status[7];
|
||||
} else {
|
||||
if (status[13] & 0x02)
|
||||
card->sw_caps.hs_max_dtr = 50000000;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(status);
|
||||
|
@ -352,6 +407,232 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
|||
return err;
|
||||
}
|
||||
|
||||
static int sd_select_driver_type(struct mmc_card *card, u8 *status)
|
||||
{
|
||||
int host_drv_type = 0, card_drv_type = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If the host doesn't support any of the Driver Types A,C or D,
|
||||
* default Driver Type B is used.
|
||||
*/
|
||||
if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C
|
||||
| MMC_CAP_DRIVER_TYPE_D)))
|
||||
return 0;
|
||||
|
||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) {
|
||||
host_drv_type = MMC_SET_DRIVER_TYPE_A;
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_A;
|
||||
else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_B;
|
||||
else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_C;
|
||||
} else if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) {
|
||||
host_drv_type = MMC_SET_DRIVER_TYPE_C;
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_C;
|
||||
} else if (!(card->host->caps & MMC_CAP_DRIVER_TYPE_D)) {
|
||||
/*
|
||||
* If we are here, that means only the default driver type
|
||||
* B is supported by the host.
|
||||
*/
|
||||
host_drv_type = MMC_SET_DRIVER_TYPE_B;
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_B)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_B;
|
||||
else if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
||||
card_drv_type = MMC_SET_DRIVER_TYPE_C;
|
||||
}
|
||||
|
||||
err = mmc_sd_switch(card, 1, 2, card_drv_type, status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((status[15] & 0xF) != card_drv_type) {
|
||||
printk(KERN_WARNING "%s: Problem setting driver strength!\n",
|
||||
mmc_hostname(card->host));
|
||||
return 0;
|
||||
}
|
||||
|
||||
mmc_set_driver_type(card->host, host_drv_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
|
||||
{
|
||||
unsigned int bus_speed = 0, timing = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If the host doesn't support any of the UHS-I modes, fallback on
|
||||
* default speed.
|
||||
*/
|
||||
if (!(card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)))
|
||||
return 0;
|
||||
|
||||
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
|
||||
bus_speed = UHS_SDR104_BUS_SPEED;
|
||||
timing = MMC_TIMING_UHS_SDR104;
|
||||
card->sw_caps.uhs_max_dtr = UHS_SDR104_MAX_DTR;
|
||||
} else if ((card->host->caps & MMC_CAP_UHS_DDR50) &&
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_DDR50)) {
|
||||
bus_speed = UHS_DDR50_BUS_SPEED;
|
||||
timing = MMC_TIMING_UHS_DDR50;
|
||||
card->sw_caps.uhs_max_dtr = UHS_DDR50_MAX_DTR;
|
||||
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_SDR50)) && (card->sw_caps.sd3_bus_mode &
|
||||
SD_MODE_UHS_SDR50)) {
|
||||
bus_speed = UHS_SDR50_BUS_SPEED;
|
||||
timing = MMC_TIMING_UHS_SDR50;
|
||||
card->sw_caps.uhs_max_dtr = UHS_SDR50_MAX_DTR;
|
||||
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25)) &&
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR25)) {
|
||||
bus_speed = UHS_SDR25_BUS_SPEED;
|
||||
timing = MMC_TIMING_UHS_SDR25;
|
||||
card->sw_caps.uhs_max_dtr = UHS_SDR25_MAX_DTR;
|
||||
} else if ((card->host->caps & (MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR12)) && (card->sw_caps.sd3_bus_mode &
|
||||
SD_MODE_UHS_SDR12)) {
|
||||
bus_speed = UHS_SDR12_BUS_SPEED;
|
||||
timing = MMC_TIMING_UHS_SDR12;
|
||||
card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
|
||||
}
|
||||
|
||||
card->sd_bus_speed = bus_speed;
|
||||
err = mmc_sd_switch(card, 1, 0, bus_speed, status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((status[16] & 0xF) != bus_speed)
|
||||
printk(KERN_WARNING "%s: Problem setting bus speed mode!\n",
|
||||
mmc_hostname(card->host));
|
||||
else {
|
||||
mmc_set_timing(card->host, timing);
|
||||
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||
{
|
||||
int current_limit = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Current limit switch is only defined for SDR50, SDR104, and DDR50
|
||||
* bus speed modes. For other bus speed modes, we set the default
|
||||
* current limit of 200mA.
|
||||
*/
|
||||
if ((card->sd_bus_speed == UHS_SDR50_BUS_SPEED) ||
|
||||
(card->sd_bus_speed == UHS_SDR104_BUS_SPEED) ||
|
||||
(card->sd_bus_speed == UHS_DDR50_BUS_SPEED)) {
|
||||
if (card->host->caps & MMC_CAP_MAX_CURRENT_800) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_800)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_800;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_600)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_600) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_600)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_600;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_400) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||
else if (card->sw_caps.sd3_curr_limit &
|
||||
SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
} else if (card->host->caps & MMC_CAP_MAX_CURRENT_200) {
|
||||
if (card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
}
|
||||
} else
|
||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
|
||||
err = mmc_sd_switch(card, 1, 3, current_limit, status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (((status[15] >> 4) & 0x0F) != current_limit)
|
||||
printk(KERN_WARNING "%s: Problem setting current limit!\n",
|
||||
mmc_hostname(card->host));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* UHS-I specific initialization procedure
|
||||
*/
|
||||
static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
u8 *status;
|
||||
|
||||
if (!card->scr.sda_spec3)
|
||||
return 0;
|
||||
|
||||
if (!(card->csd.cmdclass & CCC_SWITCH))
|
||||
return 0;
|
||||
|
||||
status = kmalloc(64, GFP_KERNEL);
|
||||
if (!status) {
|
||||
printk(KERN_ERR "%s: could not allocate a buffer for "
|
||||
"switch capabilities.\n", mmc_hostname(card->host));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set 4-bit bus width */
|
||||
if ((card->host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
}
|
||||
|
||||
/* Set the driver strength for the card */
|
||||
err = sd_select_driver_type(card, status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Set bus speed mode of the card */
|
||||
err = sd_set_bus_speed_mode(card, status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Set current limit for the card */
|
||||
err = sd_set_current_limit(card, status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* SPI mode doesn't define CMD19 */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
|
||||
err = card->host->ops->execute_tuning(card->host);
|
||||
|
||||
out:
|
||||
kfree(status);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
MMC_DEV_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1],
|
||||
card->raw_cid[2], card->raw_cid[3]);
|
||||
MMC_DEV_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1],
|
||||
|
@ -400,7 +681,7 @@ struct device_type sd_type = {
|
|||
/*
|
||||
* Fetch CID from card.
|
||||
*/
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
@ -420,12 +701,39 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid)
|
|||
*/
|
||||
err = mmc_send_if_cond(host, ocr);
|
||||
if (!err)
|
||||
ocr |= 1 << 30;
|
||||
ocr |= SD_OCR_CCS;
|
||||
|
||||
err = mmc_send_app_op_cond(host, ocr, NULL);
|
||||
/*
|
||||
* If the host supports one of UHS-I modes, request the card
|
||||
* to switch to 1.8V signaling level.
|
||||
*/
|
||||
if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))
|
||||
ocr |= SD_OCR_S18R;
|
||||
|
||||
/* If the host can supply more than 150mA, XPC should be set to 1. */
|
||||
if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 |
|
||||
MMC_CAP_SET_XPC_180))
|
||||
ocr |= SD_OCR_XPC;
|
||||
|
||||
try_again:
|
||||
err = mmc_send_app_op_cond(host, ocr, rocr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* In case CCS and S18A in the response is set, start Signal Voltage
|
||||
* Switch procedure. SPI mode doesn't support CMD11.
|
||||
*/
|
||||
if (!mmc_host_is_spi(host) && rocr &&
|
||||
((*rocr & 0x41000000) == 0x41000000)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true);
|
||||
if (err) {
|
||||
ocr &= ~SD_OCR_S18R;
|
||||
goto try_again;
|
||||
}
|
||||
}
|
||||
|
||||
if (mmc_host_is_spi(host))
|
||||
err = mmc_send_cid(host, cid);
|
||||
else
|
||||
|
@ -553,11 +861,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|||
struct mmc_card *card;
|
||||
int err;
|
||||
u32 cid[4];
|
||||
u32 rocr = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
err = mmc_sd_get_cid(host, ocr, cid);
|
||||
err = mmc_sd_get_cid(host, ocr, cid, &rocr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -610,30 +919,47 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|||
if (err)
|
||||
goto free_card;
|
||||
|
||||
/*
|
||||
* Attempt to change to high-speed (if supported)
|
||||
*/
|
||||
err = mmc_sd_switch_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
else if (err)
|
||||
goto free_card;
|
||||
|
||||
/*
|
||||
* Set bus speed.
|
||||
*/
|
||||
mmc_set_clock(host, mmc_sd_get_max_clock(card));
|
||||
|
||||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
if ((host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||
/* Initialization sequence for UHS-I cards */
|
||||
if (rocr & SD_ROCR_S18A) {
|
||||
err = mmc_sd_init_uhs_card(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||
/* Card is an ultra-high-speed card */
|
||||
mmc_sd_card_set_uhs(card);
|
||||
|
||||
/*
|
||||
* Since initialization is now complete, enable preset
|
||||
* value registers for UHS-I cards.
|
||||
*/
|
||||
if (host->ops->enable_preset_value)
|
||||
host->ops->enable_preset_value(host, true);
|
||||
} else {
|
||||
/*
|
||||
* Attempt to change to high-speed (if supported)
|
||||
*/
|
||||
err = mmc_sd_switch_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
else if (err)
|
||||
goto free_card;
|
||||
|
||||
/*
|
||||
* Set bus speed.
|
||||
*/
|
||||
mmc_set_clock(host, mmc_sd_get_max_clock(card));
|
||||
|
||||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
if ((host->caps & MMC_CAP_4_BIT_DATA) &&
|
||||
(card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) {
|
||||
err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
mmc_set_bus_width(host, MMC_BUS_WIDTH_4);
|
||||
}
|
||||
}
|
||||
|
||||
host->card = card;
|
||||
|
@ -773,6 +1099,15 @@ int mmc_attach_sd(struct mmc_host *host)
|
|||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/* Make sure we are at 3.3V signalling voltage */
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330, false);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Disable preset value enable if already set since last time */
|
||||
if (host->ops->enable_preset_value)
|
||||
host->ops->enable_preset_value(host, false);
|
||||
|
||||
err = mmc_send_app_op_cond(host, 0, &ocr);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
extern struct device_type sd_type;
|
||||
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid);
|
||||
int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr);
|
||||
int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card);
|
||||
void mmc_decode_cid(struct mmc_card *card);
|
||||
int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
||||
|
|
|
@ -21,10 +21,10 @@
|
|||
#include "core.h"
|
||||
#include "sd_ops.h"
|
||||
|
||||
static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
||||
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(card && (card->host != host));
|
||||
|
@ -49,6 +49,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
|||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_app_cmd);
|
||||
|
||||
/**
|
||||
* mmc_wait_for_app_cmd - start an application command and wait for
|
||||
|
@ -66,7 +67,7 @@ static int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
|||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd, int retries)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_request mrq = {0};
|
||||
|
||||
int i, err;
|
||||
|
||||
|
@ -119,13 +120,11 @@ EXPORT_SYMBOL(mmc_wait_for_app_cmd);
|
|||
int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = SD_APP_SET_BUS_WIDTH;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
|
@ -149,13 +148,11 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
|||
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
int i, err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
if (mmc_host_is_spi(host))
|
||||
cmd.arg = ocr & (1 << 30); /* SPI only defines one bit */
|
||||
|
@ -194,7 +191,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
|
||||
int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
int err;
|
||||
static const u8 test_pattern = 0xAA;
|
||||
u8 result_pattern;
|
||||
|
@ -226,13 +223,11 @@ int mmc_send_if_cond(struct mmc_host *host, u32 ocr)
|
|||
int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!rca);
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = SD_SEND_RELATIVE_ADDR;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR;
|
||||
|
@ -249,9 +244,9 @@ int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca)
|
|||
int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
||||
{
|
||||
int err;
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
void *data_buf;
|
||||
|
||||
|
@ -272,10 +267,6 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
|||
if (data_buf == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -312,9 +303,9 @@ int mmc_app_send_scr(struct mmc_card *card, u32 *scr)
|
|||
int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
||||
u8 value, u8 *resp)
|
||||
{
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
@ -325,10 +316,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
|||
mode = !!mode;
|
||||
value &= 0xF;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -361,9 +348,9 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
|||
int mmc_app_sd_status(struct mmc_card *card, void *ssr)
|
||||
{
|
||||
int err;
|
||||
struct mmc_request mrq;
|
||||
struct mmc_command cmd;
|
||||
struct mmc_data data;
|
||||
struct mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
@ -376,10 +363,6 @@ int mmc_app_sd_status(struct mmc_card *card, void *ssr)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "bus.h"
|
||||
|
@ -31,6 +32,11 @@ static int sdio_read_fbr(struct sdio_func *func)
|
|||
int ret;
|
||||
unsigned char data;
|
||||
|
||||
if (mmc_card_nonstd_func_interface(func->card)) {
|
||||
func->class = SDIO_CLASS_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = mmc_io_rw_direct(func->card, 0, 0,
|
||||
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
|
||||
if (ret)
|
||||
|
@ -181,7 +187,7 @@ static int sdio_disable_cd(struct mmc_card *card)
|
|||
int ret;
|
||||
u8 ctrl;
|
||||
|
||||
if (!card->cccr.disable_cd)
|
||||
if (!mmc_card_disable_cd(card))
|
||||
return 0;
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_IF, 0, &ctrl);
|
||||
|
@ -363,8 +369,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
goto err;
|
||||
}
|
||||
|
||||
if (ocr & R4_MEMORY_PRESENT
|
||||
&& mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid) == 0) {
|
||||
if ((ocr & R4_MEMORY_PRESENT) &&
|
||||
mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) {
|
||||
card->type = MMC_TYPE_SD_COMBO;
|
||||
|
||||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
|
@ -466,7 +472,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
|
||||
card = oldcard;
|
||||
}
|
||||
mmc_fixup_device(card);
|
||||
mmc_fixup_device(card, NULL);
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
err = mmc_sd_setup_card(host, card, oldcard != NULL);
|
||||
|
@ -625,7 +631,7 @@ static int mmc_sdio_suspend(struct mmc_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (!err && host->pm_flags & MMC_PM_KEEP_POWER) {
|
||||
if (!err && mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
mmc_claim_host(host);
|
||||
sdio_disable_wide(host->card);
|
||||
mmc_release_host(host);
|
||||
|
@ -645,10 +651,10 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
mmc_claim_host(host);
|
||||
|
||||
/* No need to reinitialize powered-resumed nonremovable cards */
|
||||
if (mmc_card_is_removable(host) || !mmc_card_is_powered_resumed(host))
|
||||
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host))
|
||||
err = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
(host->pm_flags & MMC_PM_KEEP_POWER));
|
||||
else if (mmc_card_is_powered_resumed(host)) {
|
||||
mmc_card_keep_power(host));
|
||||
else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
/* We may have switched to 1-bit mode during suspend */
|
||||
err = sdio_enable_4bit_bus(host->card);
|
||||
if (err > 0) {
|
||||
|
@ -691,7 +697,7 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
|||
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
(host->pm_flags & MMC_PM_KEEP_POWER));
|
||||
mmc_card_keep_power(host));
|
||||
if (!ret && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -31,6 +31,17 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
|
|||
{
|
||||
int i, ret, count;
|
||||
unsigned char pending;
|
||||
struct sdio_func *func;
|
||||
|
||||
/*
|
||||
* Optimization, if there is only 1 function interrupt registered
|
||||
* call irq handler directly
|
||||
*/
|
||||
func = card->sdio_single_irq;
|
||||
if (func) {
|
||||
func->irq_handler(func);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTx, 0, &pending);
|
||||
if (ret) {
|
||||
|
@ -42,7 +53,7 @@ static int process_sdio_pending_irqs(struct mmc_card *card)
|
|||
count = 0;
|
||||
for (i = 1; i <= 7; i++) {
|
||||
if (pending & (1 << i)) {
|
||||
struct sdio_func *func = card->sdio_func[i - 1];
|
||||
func = card->sdio_func[i - 1];
|
||||
if (!func) {
|
||||
printk(KERN_WARNING "%s: pending IRQ for "
|
||||
"non-existent function\n",
|
||||
|
@ -186,6 +197,24 @@ static int sdio_card_irq_put(struct mmc_card *card)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* If there is only 1 function registered set sdio_single_irq */
|
||||
static void sdio_single_irq_set(struct mmc_card *card)
|
||||
{
|
||||
struct sdio_func *func;
|
||||
int i;
|
||||
|
||||
card->sdio_single_irq = NULL;
|
||||
if ((card->host->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
card->host->sdio_irqs == 1)
|
||||
for (i = 0; i < card->sdio_funcs; i++) {
|
||||
func = card->sdio_func[i];
|
||||
if (func && func->irq_handler) {
|
||||
card->sdio_single_irq = func;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sdio_claim_irq - claim the IRQ for a SDIO function
|
||||
* @func: SDIO function
|
||||
|
@ -227,6 +256,7 @@ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
|
|||
ret = sdio_card_irq_get(func->card);
|
||||
if (ret)
|
||||
func->irq_handler = NULL;
|
||||
sdio_single_irq_set(func->card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -251,6 +281,7 @@ int sdio_release_irq(struct sdio_func *func)
|
|||
if (func->irq_handler) {
|
||||
func->irq_handler = NULL;
|
||||
sdio_card_irq_put(func->card);
|
||||
sdio_single_irq_set(func->card);
|
||||
}
|
||||
|
||||
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
|
||||
|
|
|
@ -21,13 +21,11 @@
|
|||
|
||||
int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
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;
|
||||
|
@ -70,7 +68,7 @@ int mmc_send_io_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
|
||||
unsigned addr, u8 in, u8 *out)
|
||||
{
|
||||
struct mmc_command cmd;
|
||||
struct mmc_command cmd = {0};
|
||||
int err;
|
||||
|
||||
BUG_ON(!host);
|
||||
|
@ -80,8 +78,6 @@ static int mmc_io_rw_direct_host(struct mmc_host *host, int write, unsigned fn,
|
|||
if (addr & ~0x1FFFF)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&cmd, 0, sizeof(struct mmc_command));
|
||||
|
||||
cmd.opcode = SD_IO_RW_DIRECT;
|
||||
cmd.arg = write ? 0x80000000 : 0x00000000;
|
||||
cmd.arg |= fn << 28;
|
||||
|
@ -125,9 +121,9 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
|||
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 mmc_request mrq = {0};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
@ -140,10 +136,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
|||
if (addr & ~0x1FFFF)
|
||||
return -EINVAL;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ config MMC_SDHCI_DOVE
|
|||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_TEGRA
|
||||
tristate "SDHCI platform support for the Tegra SD/MMC Controller"
|
||||
bool "SDHCI platform support for the Tegra SD/MMC Controller"
|
||||
depends on MMC_SDHCI_PLTFM && ARCH_TEGRA
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
|
@ -535,6 +535,37 @@ config MMC_JZ4740
|
|||
If you have a board based on such a SoC and with a SD/MMC slot,
|
||||
say Y or M here.
|
||||
|
||||
config MMC_VUB300
|
||||
tristate "VUB300 USB to SDIO/SD/MMC Host Controller support"
|
||||
depends on USB
|
||||
help
|
||||
This selects support for Elan Digital Systems' VUB300 chip.
|
||||
|
||||
The VUB300 is a USB-SDIO Host Controller Interface chip
|
||||
that enables the host computer to use SDIO/SD/MMC cards
|
||||
via a USB 2.0 or USB 1.1 host.
|
||||
|
||||
The VUB300 chip will be found in both physically separate
|
||||
USB to SDIO/SD/MMC adapters and embedded on some motherboards.
|
||||
|
||||
The VUB300 chip supports SD and MMC memory cards in addition
|
||||
to single and multifunction SDIO cards.
|
||||
|
||||
Some SDIO cards will need a firmware file to be loaded and
|
||||
sent to VUB300 chip in order to achieve better data throughput.
|
||||
Download these "Offload Pseudocode" from Elan Digital Systems'
|
||||
web-site http://www.elandigitalsystems.com/support/downloads.php
|
||||
and put them in /lib/firmware. Note that without these additional
|
||||
firmware files the VUB300 chip will still function, but not at
|
||||
the best obtainable data rate.
|
||||
|
||||
To compile this mmc host controller driver as a module,
|
||||
choose M here: the module will be called vub300.
|
||||
|
||||
If you have a computer with an embedded VUB300 chip
|
||||
or if you intend connecting a USB adapter based on a
|
||||
VUB300 chip say Y or M here.
|
||||
|
||||
config MMC_USHC
|
||||
tristate "USB SD Host Controller (USHC) support"
|
||||
depends on USB
|
||||
|
|
|
@ -41,6 +41,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
|
|||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
|
||||
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-platform.o
|
||||
|
|
|
@ -1769,9 +1769,6 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|||
int i, ret;
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
|
||||
if (host->vmmc)
|
||||
regulator_enable(host->vmmc);
|
||||
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
if (!slot)
|
||||
|
@ -1798,6 +1795,9 @@ static int dw_mci_resume(struct platform_device *pdev)
|
|||
int i, ret;
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
|
||||
if (host->vmmc)
|
||||
regulator_enable(host->vmmc);
|
||||
|
||||
if (host->dma_ops->init)
|
||||
host->dma_ops->init(host);
|
||||
|
||||
|
|
|
@ -18,11 +18,9 @@
|
|||
#include <linux/dma-mapping.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include <asm/scatterlist.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
|
@ -46,14 +44,14 @@ struct sdhci_pci_slot;
|
|||
struct sdhci_pci_fixes {
|
||||
unsigned int quirks;
|
||||
|
||||
int (*probe)(struct sdhci_pci_chip*);
|
||||
int (*probe) (struct sdhci_pci_chip *);
|
||||
|
||||
int (*probe_slot)(struct sdhci_pci_slot*);
|
||||
void (*remove_slot)(struct sdhci_pci_slot*, int);
|
||||
int (*probe_slot) (struct sdhci_pci_slot *);
|
||||
void (*remove_slot) (struct sdhci_pci_slot *, int);
|
||||
|
||||
int (*suspend)(struct sdhci_pci_chip*,
|
||||
int (*suspend) (struct sdhci_pci_chip *,
|
||||
pm_message_t);
|
||||
int (*resume)(struct sdhci_pci_chip*);
|
||||
int (*resume) (struct sdhci_pci_chip *);
|
||||
};
|
||||
|
||||
struct sdhci_pci_slot {
|
||||
|
@ -329,6 +327,11 @@ static int jmicron_probe(struct sdhci_pci_chip *chip)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* quirk for unsable RO-detection on JM388 chips */
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD ||
|
||||
chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD)
|
||||
chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -402,7 +405,7 @@ static int jmicron_suspend(struct sdhci_pci_chip *chip, pm_message_t state)
|
|||
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC ||
|
||||
chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) {
|
||||
for (i = 0;i < chip->num_slots;i++)
|
||||
for (i = 0; i < chip->num_slots; i++)
|
||||
jmicron_enable_mmc(chip->slots[i]->host, 0);
|
||||
}
|
||||
|
||||
|
@ -415,7 +418,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
|
|||
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB38X_MMC ||
|
||||
chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) {
|
||||
for (i = 0;i < chip->num_slots;i++)
|
||||
for (i = 0; i < chip->num_slots; i++)
|
||||
jmicron_enable_mmc(chip->slots[i]->host, 1);
|
||||
}
|
||||
|
||||
|
@ -798,7 +801,7 @@ static struct sdhci_ops sdhci_pci_ops = {
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
|
||||
static int sdhci_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
{
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_pci_slot *slot;
|
||||
|
@ -810,7 +813,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
|
|||
if (!chip)
|
||||
return 0;
|
||||
|
||||
for (i = 0;i < chip->num_slots;i++) {
|
||||
for (i = 0; i < chip->num_slots; i++) {
|
||||
slot = chip->slots[i];
|
||||
if (!slot)
|
||||
continue;
|
||||
|
@ -818,7 +821,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
|
|||
ret = sdhci_suspend_host(slot->host, state);
|
||||
|
||||
if (ret) {
|
||||
for (i--;i >= 0;i--)
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
|
@ -833,7 +836,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
|
|||
if (chip->fixes && chip->fixes->suspend) {
|
||||
ret = chip->fixes->suspend(chip, state);
|
||||
if (ret) {
|
||||
for (i = chip->num_slots - 1;i >= 0;i--)
|
||||
for (i = chip->num_slots - 1; i >= 0; i--)
|
||||
sdhci_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
|
@ -855,7 +858,7 @@ static int sdhci_pci_suspend (struct pci_dev *pdev, pm_message_t state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_pci_resume (struct pci_dev *pdev)
|
||||
static int sdhci_pci_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_pci_slot *slot;
|
||||
|
@ -877,7 +880,7 @@ static int sdhci_pci_resume (struct pci_dev *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0;i < chip->num_slots;i++) {
|
||||
for (i = 0; i < chip->num_slots; i++) {
|
||||
slot = chip->slots[i];
|
||||
if (!slot)
|
||||
continue;
|
||||
|
@ -1059,7 +1062,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
|||
}
|
||||
|
||||
chip->pdev = pdev;
|
||||
chip->fixes = (const struct sdhci_pci_fixes*)ent->driver_data;
|
||||
chip->fixes = (const struct sdhci_pci_fixes *)ent->driver_data;
|
||||
if (chip->fixes)
|
||||
chip->quirks = chip->fixes->quirks;
|
||||
chip->num_slots = slots;
|
||||
|
@ -1074,10 +1077,10 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev,
|
|||
|
||||
slots = chip->num_slots; /* Quirk may have changed this */
|
||||
|
||||
for (i = 0;i < slots;i++) {
|
||||
for (i = 0; i < slots; i++) {
|
||||
slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
|
||||
if (IS_ERR(slot)) {
|
||||
for (i--;i >= 0;i--)
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
ret = PTR_ERR(slot);
|
||||
goto free;
|
||||
|
@ -1105,7 +1108,7 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
|
|||
chip = pci_get_drvdata(pdev);
|
||||
|
||||
if (chip) {
|
||||
for (i = 0;i < chip->num_slots; i++)
|
||||
for (i = 0; i < chip->num_slots; i++)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
|
@ -1116,9 +1119,9 @@ static void __devexit sdhci_pci_remove(struct pci_dev *pdev)
|
|||
}
|
||||
|
||||
static struct pci_driver sdhci_driver = {
|
||||
.name = "sdhci-pci",
|
||||
.name = "sdhci-pci",
|
||||
.id_table = pci_ids,
|
||||
.probe = sdhci_pci_probe,
|
||||
.probe = sdhci_pci_probe,
|
||||
.remove = __devexit_p(sdhci_pci_remove),
|
||||
.suspend = sdhci_pci_suspend,
|
||||
.resume = sdhci_pci_resume,
|
||||
|
|
|
@ -69,7 +69,45 @@ static void set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
}
|
||||
|
||||
static int set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
|
||||
/*
|
||||
* Set V18_EN -- UHS modes do not work without this.
|
||||
* does not change signaling voltage
|
||||
*/
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
|
||||
/* Select Bus Speed Mode for host */
|
||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||
switch (uhs) {
|
||||
case MMC_TIMING_UHS_SDR12:
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR50 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50 | SDHCI_CTRL_VDD_180;
|
||||
break;
|
||||
}
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
pr_debug("%s:%s uhs = %d, ctrl_2 = %04X\n",
|
||||
__func__, mmc_hostname(host->mmc), uhs, ctrl_2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_pxa_ops = {
|
||||
.set_uhs_signaling = set_uhs_signaling,
|
||||
.set_clock = set_clock,
|
||||
};
|
||||
|
||||
|
@ -136,11 +174,19 @@ static int __devinit sdhci_pxa_probe(struct platform_device *pdev)
|
|||
host->hw_name = "MMC";
|
||||
host->ops = &sdhci_pxa_ops;
|
||||
host->irq = irq;
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
host->quirks = SDHCI_QUIRK_BROKEN_ADMA
|
||||
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
|
||||
| SDHCI_QUIRK_32BIT_DMA_ADDR
|
||||
| SDHCI_QUIRK_32BIT_DMA_SIZE
|
||||
| SDHCI_QUIRK_32BIT_ADMA_SIZE
|
||||
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
|
||||
|
||||
if (pdata->quirks)
|
||||
host->quirks |= pdata->quirks;
|
||||
|
||||
/* enable 1/8V DDR capable */
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
/* If slot design supports 8 bit data, indicate this to MMC. */
|
||||
if (pdata->flags & PXA_FLAG_SD_8_BIT_CAPABLE_SLOT)
|
||||
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
|
|
|
@ -184,6 +184,8 @@ static int tegra_sdhci_pltfm_init(struct sdhci_host *host,
|
|||
clk_enable(clk);
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
host->mmc->pm_caps = plat->pm_flags;
|
||||
|
||||
if (plat->is_8bit)
|
||||
host->mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
#define SDHCI_DMA_ADDRESS 0x00
|
||||
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
|
||||
|
||||
#define SDHCI_BLOCK_SIZE 0x04
|
||||
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
|
||||
|
@ -36,7 +37,8 @@
|
|||
#define SDHCI_TRANSFER_MODE 0x0C
|
||||
#define SDHCI_TRNS_DMA 0x01
|
||||
#define SDHCI_TRNS_BLK_CNT_EN 0x02
|
||||
#define SDHCI_TRNS_ACMD12 0x04
|
||||
#define SDHCI_TRNS_AUTO_CMD12 0x04
|
||||
#define SDHCI_TRNS_AUTO_CMD23 0x08
|
||||
#define SDHCI_TRNS_READ 0x10
|
||||
#define SDHCI_TRNS_MULTI 0x20
|
||||
|
||||
|
@ -68,8 +70,10 @@
|
|||
#define SDHCI_DATA_AVAILABLE 0x00000800
|
||||
#define SDHCI_CARD_PRESENT 0x00010000
|
||||
#define SDHCI_WRITE_PROTECT 0x00080000
|
||||
#define SDHCI_DATA_LVL_MASK 0x00F00000
|
||||
#define SDHCI_DATA_LVL_SHIFT 20
|
||||
|
||||
#define SDHCI_HOST_CONTROL 0x28
|
||||
#define SDHCI_HOST_CONTROL 0x28
|
||||
#define SDHCI_CTRL_LED 0x01
|
||||
#define SDHCI_CTRL_4BITBUS 0x02
|
||||
#define SDHCI_CTRL_HISPD 0x04
|
||||
|
@ -99,6 +103,7 @@
|
|||
#define SDHCI_DIV_MASK 0xFF
|
||||
#define SDHCI_DIV_MASK_LEN 8
|
||||
#define SDHCI_DIV_HI_MASK 0x300
|
||||
#define SDHCI_PROG_CLOCK_MODE 0x0020
|
||||
#define SDHCI_CLOCK_CARD_EN 0x0004
|
||||
#define SDHCI_CLOCK_INT_STABLE 0x0002
|
||||
#define SDHCI_CLOCK_INT_EN 0x0001
|
||||
|
@ -146,7 +151,22 @@
|
|||
|
||||
#define SDHCI_ACMD12_ERR 0x3C
|
||||
|
||||
/* 3E-3F reserved */
|
||||
#define SDHCI_HOST_CONTROL2 0x3E
|
||||
#define SDHCI_CTRL_UHS_MASK 0x0007
|
||||
#define SDHCI_CTRL_UHS_SDR12 0x0000
|
||||
#define SDHCI_CTRL_UHS_SDR25 0x0001
|
||||
#define SDHCI_CTRL_UHS_SDR50 0x0002
|
||||
#define SDHCI_CTRL_UHS_SDR104 0x0003
|
||||
#define SDHCI_CTRL_UHS_DDR50 0x0004
|
||||
#define SDHCI_CTRL_VDD_180 0x0008
|
||||
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
|
||||
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
|
||||
#define SDHCI_CTRL_DRV_TYPE_A 0x0010
|
||||
#define SDHCI_CTRL_DRV_TYPE_C 0x0020
|
||||
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
|
||||
#define SDHCI_CTRL_EXEC_TUNING 0x0040
|
||||
#define SDHCI_CTRL_TUNED_CLK 0x0080
|
||||
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
|
||||
|
||||
#define SDHCI_CAPABILITIES 0x40
|
||||
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
|
||||
|
@ -167,9 +187,30 @@
|
|||
#define SDHCI_CAN_VDD_180 0x04000000
|
||||
#define SDHCI_CAN_64BIT 0x10000000
|
||||
|
||||
#define SDHCI_SUPPORT_SDR50 0x00000001
|
||||
#define SDHCI_SUPPORT_SDR104 0x00000002
|
||||
#define SDHCI_SUPPORT_DDR50 0x00000004
|
||||
#define SDHCI_DRIVER_TYPE_A 0x00000010
|
||||
#define SDHCI_DRIVER_TYPE_C 0x00000020
|
||||
#define SDHCI_DRIVER_TYPE_D 0x00000040
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
|
||||
#define SDHCI_USE_SDR50_TUNING 0x00002000
|
||||
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
|
||||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
|
||||
#define SDHCI_MAX_CURRENT_330_SHIFT 0
|
||||
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
|
||||
#define SDHCI_MAX_CURRENT_300_SHIFT 8
|
||||
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
|
||||
#define SDHCI_MAX_CURRENT_180_SHIFT 16
|
||||
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
|
||||
|
||||
/* 4C-4F reserved for more max current */
|
||||
|
||||
|
@ -202,6 +243,12 @@
|
|||
#define SDHCI_MAX_DIV_SPEC_200 256
|
||||
#define SDHCI_MAX_DIV_SPEC_300 2046
|
||||
|
||||
/*
|
||||
* Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
|
||||
*/
|
||||
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
|
||||
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
|
||||
|
||||
struct sdhci_ops {
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
u32 (*read_l)(struct sdhci_host *host, int reg);
|
||||
|
@ -223,6 +270,10 @@ struct sdhci_ops {
|
|||
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
|
||||
u8 power_mode);
|
||||
unsigned int (*get_ro)(struct sdhci_host *host);
|
||||
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
|
||||
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
|
||||
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
|
||||
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <linux/mmc/sh_mmcif.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define DRIVER_NAME "sh_mmcif"
|
||||
#define DRIVER_VERSION "2010-04-28"
|
||||
|
@ -153,6 +155,12 @@
|
|||
#define CLKDEV_MMC_DATA 20000000 /* 20MHz */
|
||||
#define CLKDEV_INIT 400000 /* 400 KHz */
|
||||
|
||||
enum mmcif_state {
|
||||
STATE_IDLE,
|
||||
STATE_REQUEST,
|
||||
STATE_IOS,
|
||||
};
|
||||
|
||||
struct sh_mmcif_host {
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_data *data;
|
||||
|
@ -164,6 +172,9 @@ struct sh_mmcif_host {
|
|||
long timeout;
|
||||
void __iomem *addr;
|
||||
struct completion intr_wait;
|
||||
enum mmcif_state state;
|
||||
spinlock_t lock;
|
||||
bool power;
|
||||
|
||||
/* DMA support */
|
||||
struct dma_chan *chan_rx;
|
||||
|
@ -798,17 +809,31 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host,
|
|||
static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->state != STATE_IDLE) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mrq->cmd->error = -EAGAIN;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
host->state = STATE_REQUEST;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
switch (mrq->cmd->opcode) {
|
||||
/* MMCIF does not support SD/SDIO command */
|
||||
case SD_IO_SEND_OP_COND:
|
||||
case MMC_APP_CMD:
|
||||
host->state = STATE_IDLE;
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
case MMC_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
|
||||
if (!mrq->data) {
|
||||
/* send_if_cond cmd (not support) */
|
||||
host->state = STATE_IDLE;
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
|
@ -830,12 +855,9 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
sh_mmcif_start_cmd(host, mrq, mrq->cmd);
|
||||
host->data = NULL;
|
||||
|
||||
if (mrq->cmd->error != 0) {
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
if (mrq->stop)
|
||||
if (!mrq->cmd->error && mrq->stop)
|
||||
sh_mmcif_stop_cmd(host, mrq, mrq->stop);
|
||||
host->state = STATE_IDLE;
|
||||
mmc_request_done(mmc, mrq);
|
||||
}
|
||||
|
||||
|
@ -843,15 +865,39 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
{
|
||||
struct sh_mmcif_host *host = mmc_priv(mmc);
|
||||
struct sh_mmcif_plat_data *p = host->pd->dev.platform_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->state != STATE_IDLE) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
host->state = STATE_IOS;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (ios->power_mode == MMC_POWER_UP) {
|
||||
if (p->set_pwr)
|
||||
p->set_pwr(host->pd, ios->power_mode);
|
||||
if (!host->power) {
|
||||
/* See if we also get DMA */
|
||||
sh_mmcif_request_dma(host, host->pd->dev.platform_data);
|
||||
pm_runtime_get_sync(&host->pd->dev);
|
||||
host->power = true;
|
||||
}
|
||||
} else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
|
||||
/* clock stop */
|
||||
sh_mmcif_clock_control(host, 0);
|
||||
if (ios->power_mode == MMC_POWER_OFF && p->down_pwr)
|
||||
p->down_pwr(host->pd);
|
||||
if (ios->power_mode == MMC_POWER_OFF) {
|
||||
if (host->power) {
|
||||
pm_runtime_put(&host->pd->dev);
|
||||
sh_mmcif_release_dma(host);
|
||||
host->power = false;
|
||||
}
|
||||
if (p->down_pwr)
|
||||
p->down_pwr(host->pd);
|
||||
}
|
||||
host->state = STATE_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -859,6 +905,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
sh_mmcif_clock_control(host, ios->clock);
|
||||
|
||||
host->bus_width = ios->bus_width;
|
||||
host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
static int sh_mmcif_get_cd(struct mmc_host *mmc)
|
||||
|
@ -925,7 +972,7 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
|||
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
|
||||
err = 1;
|
||||
} else {
|
||||
dev_dbg(&host->pd->dev, "Not support int\n");
|
||||
dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
|
||||
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state);
|
||||
err = 1;
|
||||
|
@ -996,6 +1043,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
host->pd = pdev;
|
||||
|
||||
init_completion(&host->intr_wait);
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
mmc->ops = &sh_mmcif_ops;
|
||||
mmc->f_max = host->clk;
|
||||
|
@ -1020,24 +1068,29 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
sh_mmcif_sync_reset(host);
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
/* See if we also get DMA */
|
||||
sh_mmcif_request_dma(host, pd);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
host->power = false;
|
||||
|
||||
ret = pm_runtime_resume(&pdev->dev);
|
||||
if (ret < 0)
|
||||
goto clean_up2;
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n");
|
||||
goto clean_up2;
|
||||
goto clean_up3;
|
||||
}
|
||||
ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host);
|
||||
if (ret) {
|
||||
free_irq(irq[0], host);
|
||||
dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n");
|
||||
goto clean_up2;
|
||||
goto clean_up3;
|
||||
}
|
||||
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
sh_mmcif_detect(host->mmc);
|
||||
|
||||
dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION);
|
||||
|
@ -1045,7 +1098,11 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev)
|
|||
sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff);
|
||||
return ret;
|
||||
|
||||
clean_up3:
|
||||
mmc_remove_host(mmc);
|
||||
pm_runtime_suspend(&pdev->dev);
|
||||
clean_up2:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable(host->hclk);
|
||||
clean_up1:
|
||||
mmc_free_host(mmc);
|
||||
|
@ -1060,14 +1117,14 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
|
|||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
int irq[2];
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
sh_mmcif_release_dma(host);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
if (host->addr)
|
||||
iounmap(host->addr);
|
||||
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
|
||||
irq[0] = platform_get_irq(pdev, 0);
|
||||
irq[1] = platform_get_irq(pdev, 1);
|
||||
|
||||
|
@ -1078,15 +1135,52 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev)
|
|||
|
||||
clk_disable(host->hclk);
|
||||
mmc_free_host(host->mmc);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sh_mmcif_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
int ret = mmc_suspend_host(host->mmc);
|
||||
|
||||
if (!ret) {
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
clk_disable(host->hclk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sh_mmcif_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct sh_mmcif_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
clk_enable(host->hclk);
|
||||
|
||||
return mmc_resume_host(host->mmc);
|
||||
}
|
||||
#else
|
||||
#define sh_mmcif_suspend NULL
|
||||
#define sh_mmcif_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
|
||||
.suspend = sh_mmcif_suspend,
|
||||
.resume = sh_mmcif_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver sh_mmcif_driver = {
|
||||
.probe = sh_mmcif_probe,
|
||||
.remove = sh_mmcif_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &sh_mmcif_dev_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
struct tmio_mmc_host *host;
|
||||
char clk_name[8];
|
||||
int ret;
|
||||
int i, irq, ret;
|
||||
|
||||
priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
|
@ -71,6 +71,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
mmc_data = &priv->mmc_data;
|
||||
p->pdata = mmc_data;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id);
|
||||
priv->clk = clk_get(&pdev->dev, clk_name);
|
||||
|
@ -116,11 +117,36 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
if (ret < 0)
|
||||
goto eprobe;
|
||||
|
||||
pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
|
||||
(unsigned long)host->ctl, host->irq);
|
||||
for (i = 0; i < 3; i++) {
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq < 0) {
|
||||
if (i) {
|
||||
continue;
|
||||
} else {
|
||||
ret = irq;
|
||||
goto eirq;
|
||||
}
|
||||
}
|
||||
ret = request_irq(irq, tmio_mmc_irq, 0,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
while (i--) {
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq >= 0)
|
||||
free_irq(irq, host);
|
||||
}
|
||||
goto eirq;
|
||||
}
|
||||
}
|
||||
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
|
||||
mmc_hostname(host->mmc), (unsigned long)
|
||||
(platform_get_resource(pdev,IORESOURCE_MEM, 0)->start),
|
||||
mmc_data->hclk / 1000000);
|
||||
|
||||
return ret;
|
||||
|
||||
eirq:
|
||||
tmio_mmc_host_remove(host);
|
||||
eprobe:
|
||||
clk_disable(priv->clk);
|
||||
clk_put(priv->clk);
|
||||
|
@ -134,6 +160,16 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
|||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
|
||||
int i, irq;
|
||||
|
||||
p->pdata = NULL;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
irq = platform_get_irq(pdev, i);
|
||||
if (irq >= 0)
|
||||
free_irq(irq, host);
|
||||
}
|
||||
|
||||
tmio_mmc_host_remove(host);
|
||||
clk_disable(priv->clk);
|
||||
|
@ -143,10 +179,18 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
|
||||
.suspend = tmio_mmc_host_suspend,
|
||||
.resume = tmio_mmc_host_resume,
|
||||
.runtime_suspend = tmio_mmc_host_runtime_suspend,
|
||||
.runtime_resume = tmio_mmc_host_runtime_resume,
|
||||
};
|
||||
|
||||
static struct platform_driver sh_mobile_sdhi_driver = {
|
||||
.driver = {
|
||||
.name = "sh_mobile_sdhi",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tmio_mmc_dev_pm_ops,
|
||||
},
|
||||
.probe = sh_mobile_sdhi_probe,
|
||||
.remove = __devexit_p(sh_mobile_sdhi_remove),
|
||||
|
|
|
@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
|
|||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = mmc_suspend_host(mmc);
|
||||
ret = tmio_mmc_host_suspend(&dev->dev);
|
||||
|
||||
/* Tell MFD core it can disable us now.*/
|
||||
if (!ret && cell->disable)
|
||||
|
@ -46,15 +46,12 @@ static int tmio_mmc_resume(struct platform_device *dev)
|
|||
int ret = 0;
|
||||
|
||||
/* Tell the MFD core we are ready to be enabled */
|
||||
if (cell->resume) {
|
||||
if (cell->resume)
|
||||
ret = cell->resume(dev);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc_resume_host(mmc);
|
||||
if (!ret)
|
||||
ret = tmio_mmc_host_resume(&dev->dev);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
|
@ -67,7 +64,7 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
|
|||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
struct tmio_mmc_data *pdata;
|
||||
struct tmio_mmc_host *host;
|
||||
int ret = -EINVAL;
|
||||
int ret = -EINVAL, irq;
|
||||
|
||||
if (pdev->num_resources != 2)
|
||||
goto out;
|
||||
|
@ -76,6 +73,12 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
|
|||
if (!pdata || !pdata->hclk)
|
||||
goto out;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Tell the MFD core we are ready to be enabled */
|
||||
if (cell->enable) {
|
||||
ret = cell->enable(pdev);
|
||||
|
@ -87,11 +90,18 @@ static int __devinit tmio_mmc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto cell_disable;
|
||||
|
||||
ret = request_irq(irq, tmio_mmc_irq, IRQF_DISABLED |
|
||||
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host);
|
||||
if (ret)
|
||||
goto host_remove;
|
||||
|
||||
pr_info("%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc),
|
||||
(unsigned long)host->ctl, host->irq);
|
||||
(unsigned long)host->ctl, irq);
|
||||
|
||||
return 0;
|
||||
|
||||
host_remove:
|
||||
tmio_mmc_host_remove(host);
|
||||
cell_disable:
|
||||
if (cell->disable)
|
||||
cell->disable(pdev);
|
||||
|
@ -107,7 +117,9 @@ static int __devexit tmio_mmc_remove(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
tmio_mmc_host_remove(mmc_priv(mmc));
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
free_irq(platform_get_irq(pdev, 0), host);
|
||||
tmio_mmc_host_remove(host);
|
||||
if (cell->disable)
|
||||
cell->disable(pdev);
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/highmem.h>
|
||||
#include <linux/mmc/tmio.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* Definitions for values the CTRL_SDIO_STATUS register can take. */
|
||||
#define TMIO_SDIO_STAT_IOIRQ 0x0001
|
||||
|
@ -44,13 +45,14 @@ struct tmio_mmc_host {
|
|||
struct mmc_request *mrq;
|
||||
struct mmc_data *data;
|
||||
struct mmc_host *mmc;
|
||||
int irq;
|
||||
unsigned int sdio_irq_enabled;
|
||||
|
||||
/* Callbacks for clock / power control */
|
||||
void (*set_pwr)(struct platform_device *host, int state);
|
||||
void (*set_clk_div)(struct platform_device *host, int state);
|
||||
|
||||
int pm_error;
|
||||
|
||||
/* pio related stuff */
|
||||
struct scatterlist *sg_ptr;
|
||||
struct scatterlist *sg_orig;
|
||||
|
@ -83,6 +85,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host);
|
|||
|
||||
void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||
irqreturn_t tmio_mmc_irq(int irq, void *devid);
|
||||
|
||||
static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
|
||||
unsigned long *flags)
|
||||
|
@ -120,4 +123,15 @@ static inline void tmio_mmc_release_dma(struct tmio_mmc_host *host)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int tmio_mmc_host_suspend(struct device *dev);
|
||||
int tmio_mmc_host_resume(struct device *dev);
|
||||
#else
|
||||
#define tmio_mmc_host_suspend NULL
|
||||
#define tmio_mmc_host_resume NULL
|
||||
#endif
|
||||
|
||||
int tmio_mmc_host_runtime_suspend(struct device *dev);
|
||||
int tmio_mmc_host_runtime_resume(struct device *dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -256,7 +256,10 @@ static bool tmio_mmc_filter(struct dma_chan *chan, void *arg)
|
|||
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
|
||||
{
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
if (pdata->dma) {
|
||||
if (!pdata->dma)
|
||||
return;
|
||||
|
||||
if (!host->chan_tx && !host->chan_rx) {
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
|
@ -284,18 +287,18 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
|||
|
||||
tasklet_init(&host->dma_complete, tmio_mmc_tasklet_fn, (unsigned long)host);
|
||||
tasklet_init(&host->dma_issue, tmio_mmc_issue_tasklet_fn, (unsigned long)host);
|
||||
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
|
||||
return;
|
||||
ebouncebuf:
|
||||
dma_release_channel(host->chan_rx);
|
||||
host->chan_rx = NULL;
|
||||
ereqrx:
|
||||
dma_release_channel(host->chan_tx);
|
||||
host->chan_tx = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
|
||||
return;
|
||||
|
||||
ebouncebuf:
|
||||
dma_release_channel(host->chan_rx);
|
||||
host->chan_rx = NULL;
|
||||
ereqrx:
|
||||
dma_release_channel(host->chan_tx);
|
||||
host->chan_tx = NULL;
|
||||
}
|
||||
|
||||
void tmio_mmc_release_dma(struct tmio_mmc_host *host)
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
@ -243,8 +244,12 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||
spin_lock_irqsave(&host->lock, flags);
|
||||
mrq = host->mrq;
|
||||
|
||||
/* request already finished */
|
||||
if (!mrq
|
||||
/*
|
||||
* is request already finished? Since we use a non-blocking
|
||||
* cancel_delayed_work(), it can happen, that a .set_ios() call preempts
|
||||
* us, so, have to check for IS_ERR(host->mrq)
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(mrq)
|
||||
|| time_is_after_jiffies(host->last_req_ts +
|
||||
msecs_to_jiffies(2000))) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
@ -264,16 +269,19 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
host->mrq = NULL;
|
||||
host->force_pio = false;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
|
||||
/* Ready for new calls */
|
||||
host->mrq = NULL;
|
||||
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
/* called with host->lock held, interrupts disabled */
|
||||
static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
|
@ -281,13 +289,15 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
|
|||
if (!mrq)
|
||||
return;
|
||||
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
host->data = NULL;
|
||||
host->force_pio = false;
|
||||
|
||||
cancel_delayed_work(&host->delayed_reset_work);
|
||||
|
||||
host->mrq = NULL;
|
||||
|
||||
/* FIXME: mmc_request_done() can schedule! */
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
|
@ -554,7 +564,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
|
|||
spin_unlock(&host->lock);
|
||||
}
|
||||
|
||||
static irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
||||
irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
||||
{
|
||||
struct tmio_mmc_host *host = devid;
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
|
@ -649,6 +659,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid)
|
|||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_irq);
|
||||
|
||||
static int tmio_mmc_start_data(struct tmio_mmc_host *host,
|
||||
struct mmc_data *data)
|
||||
|
@ -685,15 +696,27 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
|
|||
static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (host->mrq)
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (host->mrq) {
|
||||
pr_debug("request not null\n");
|
||||
if (IS_ERR(host->mrq)) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mrq->cmd->error = -EAGAIN;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
host->last_req_ts = jiffies;
|
||||
wmb();
|
||||
host->mrq = mrq;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (mrq->data) {
|
||||
ret = tmio_mmc_start_data(host, mrq->data);
|
||||
if (ret)
|
||||
|
@ -708,8 +731,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
}
|
||||
|
||||
fail:
|
||||
host->mrq = NULL;
|
||||
host->force_pio = false;
|
||||
host->mrq = NULL;
|
||||
mrq->cmd->error = ret;
|
||||
mmc_request_done(mmc, mrq);
|
||||
}
|
||||
|
@ -723,19 +746,54 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (host->mrq) {
|
||||
if (IS_ERR(host->mrq)) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"%s.%d: concurrent .set_ios(), clk %u, mode %u\n",
|
||||
current->comm, task_pid_nr(current),
|
||||
ios->clock, ios->power_mode);
|
||||
host->mrq = ERR_PTR(-EINTR);
|
||||
} else {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"%s.%d: CMD%u active since %lu, now %lu!\n",
|
||||
current->comm, task_pid_nr(current),
|
||||
host->mrq->cmd->opcode, host->last_req_ts, jiffies);
|
||||
}
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
host->mrq = ERR_PTR(-EBUSY);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (ios->clock)
|
||||
tmio_mmc_set_clock(host, ios->clock);
|
||||
|
||||
/* Power sequence - OFF -> UP -> ON */
|
||||
if (ios->power_mode == MMC_POWER_UP) {
|
||||
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && !pdata->power) {
|
||||
pm_runtime_get_sync(&host->pdev->dev);
|
||||
pdata->power = true;
|
||||
}
|
||||
/* power up SD bus */
|
||||
if (host->set_pwr)
|
||||
host->set_pwr(host->pdev, 1);
|
||||
} else if (ios->power_mode == MMC_POWER_OFF || !ios->clock) {
|
||||
/* power down SD bus */
|
||||
if (ios->power_mode == MMC_POWER_OFF && host->set_pwr)
|
||||
host->set_pwr(host->pdev, 0);
|
||||
if (ios->power_mode == MMC_POWER_OFF) {
|
||||
if (host->set_pwr)
|
||||
host->set_pwr(host->pdev, 0);
|
||||
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
|
||||
pdata->power) {
|
||||
pdata->power = false;
|
||||
pm_runtime_put(&host->pdev->dev);
|
||||
}
|
||||
}
|
||||
tmio_mmc_clk_stop(host);
|
||||
} else {
|
||||
/* start bus clock */
|
||||
|
@ -753,6 +811,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
/* Let things settle. delay taken from winCE driver */
|
||||
udelay(140);
|
||||
if (PTR_ERR(host->mrq) == -EINTR)
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"%s.%d: IOS interrupted: clk %u, mode %u",
|
||||
current->comm, task_pid_nr(current),
|
||||
ios->clock, ios->power_mode);
|
||||
host->mrq = NULL;
|
||||
}
|
||||
|
||||
static int tmio_mmc_get_ro(struct mmc_host *mmc)
|
||||
|
@ -801,6 +865,7 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->dev = &pdev->dev;
|
||||
_host = mmc_priv(mmc);
|
||||
_host->pdata = pdata;
|
||||
_host->mmc = mmc;
|
||||
|
@ -834,24 +899,19 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
else
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
pdata->power = false;
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_resume(&pdev->dev);
|
||||
if (ret < 0)
|
||||
goto pm_disable;
|
||||
|
||||
tmio_mmc_clk_stop(_host);
|
||||
tmio_mmc_reset(_host);
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto unmap_ctl;
|
||||
|
||||
_host->irq = ret;
|
||||
|
||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||
if (pdata->flags & TMIO_MMC_SDIO_IRQ)
|
||||
tmio_mmc_enable_sdio_irq(mmc, 0);
|
||||
|
||||
ret = request_irq(_host->irq, tmio_mmc_irq, IRQF_DISABLED |
|
||||
IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), _host);
|
||||
if (ret)
|
||||
goto unmap_ctl;
|
||||
|
||||
spin_lock_init(&_host->lock);
|
||||
|
||||
/* Init delayed work for request timeouts */
|
||||
|
@ -860,6 +920,10 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
/* See if we also get DMA */
|
||||
tmio_mmc_request_dma(_host, pdata);
|
||||
|
||||
/* We have to keep the device powered for its card detection to work */
|
||||
if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD))
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
/* Unmask the IRQs we want to know about */
|
||||
|
@ -874,7 +938,8 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
|
||||
return 0;
|
||||
|
||||
unmap_ctl:
|
||||
pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
iounmap(_host->ctl);
|
||||
host_free:
|
||||
mmc_free_host(mmc);
|
||||
|
@ -885,13 +950,88 @@ EXPORT_SYMBOL(tmio_mmc_host_probe);
|
|||
|
||||
void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct platform_device *pdev = host->pdev;
|
||||
|
||||
/*
|
||||
* We don't have to manipulate pdata->power here: if there is a card in
|
||||
* the slot, the runtime PM is active and our .runtime_resume() will not
|
||||
* be run. If there is no card in the slot and the platform can suspend
|
||||
* the controller, the runtime PM is suspended and pdata->power == false,
|
||||
* so, our .runtime_resume() will not try to detect a card in the slot.
|
||||
*/
|
||||
if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD)
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
cancel_delayed_work_sync(&host->delayed_reset_work);
|
||||
tmio_mmc_release_dma(host);
|
||||
free_irq(host->irq, host);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
iounmap(host->ctl);
|
||||
mmc_free_host(host->mmc);
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_remove);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int tmio_mmc_host_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
int ret = mmc_suspend_host(mmc);
|
||||
|
||||
if (!ret)
|
||||
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
|
||||
|
||||
host->pm_error = pm_runtime_put_sync(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_suspend);
|
||||
|
||||
int tmio_mmc_host_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
/* The MMC core will perform the complete set up */
|
||||
host->pdata->power = false;
|
||||
|
||||
if (!host->pm_error)
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
tmio_mmc_reset(mmc_priv(mmc));
|
||||
tmio_mmc_request_dma(host, host->pdata);
|
||||
|
||||
return mmc_resume_host(mmc);
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_resume);
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
int tmio_mmc_host_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);
|
||||
|
||||
int tmio_mmc_host_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
|
||||
if (pdata->power) {
|
||||
/* Only entered after a card-insert interrupt */
|
||||
tmio_mmc_set_ios(mmc, &mmc->ios);
|
||||
mmc_detect_change(mmc, msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,6 +4,7 @@ header-y += caif/
|
|||
header-y += dvb/
|
||||
header-y += hdlc/
|
||||
header-y += isdn/
|
||||
header-y += mmc/
|
||||
header-y += nfsd/
|
||||
header-y += raid/
|
||||
header-y += spi/
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <linux/fb.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define tmio_ioread8(addr) readb(addr)
|
||||
#define tmio_ioread16(addr) readw(addr)
|
||||
|
@ -61,6 +62,12 @@
|
|||
* Some controllers can support SDIO IRQ signalling.
|
||||
*/
|
||||
#define TMIO_MMC_SDIO_IRQ (1 << 2)
|
||||
/*
|
||||
* Some platforms can detect card insertion events with controller powered
|
||||
* down, in which case they have to call tmio_mmc_cd_wakeup() to power up the
|
||||
* controller and report the event to the driver.
|
||||
*/
|
||||
#define TMIO_MMC_HAS_COLD_CD (1 << 3)
|
||||
|
||||
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
|
||||
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
|
||||
|
@ -82,11 +89,21 @@ struct tmio_mmc_data {
|
|||
unsigned long flags;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
struct tmio_mmc_dma *dma;
|
||||
struct device *dev;
|
||||
bool power;
|
||||
void (*set_pwr)(struct platform_device *host, int state);
|
||||
void (*set_clk_div)(struct platform_device *host, int state);
|
||||
int (*get_cd)(struct platform_device *host);
|
||||
};
|
||||
|
||||
static inline void tmio_mmc_cd_wakeup(struct tmio_mmc_data *pdata)
|
||||
{
|
||||
if (pdata && !pdata->power) {
|
||||
pdata->power = true;
|
||||
pm_runtime_get(pdata->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* data for the NAND controller
|
||||
*/
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
header-y += ioctl.h
|
|
@ -11,6 +11,7 @@
|
|||
#define LINUX_MMC_CARD_H
|
||||
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
|
||||
struct mmc_cid {
|
||||
unsigned int manfid;
|
||||
|
@ -29,6 +30,7 @@ struct mmc_csd {
|
|||
unsigned short cmdclass;
|
||||
unsigned short tacc_clks;
|
||||
unsigned int tacc_ns;
|
||||
unsigned int c_size;
|
||||
unsigned int r2w_factor;
|
||||
unsigned int max_dtr;
|
||||
unsigned int erase_size; /* In sectors */
|
||||
|
@ -45,6 +47,10 @@ struct mmc_ext_csd {
|
|||
u8 rev;
|
||||
u8 erase_group_def;
|
||||
u8 sec_feature_support;
|
||||
u8 rel_sectors;
|
||||
u8 rel_param;
|
||||
u8 part_config;
|
||||
unsigned int part_time; /* Units: ms */
|
||||
unsigned int sa_timeout; /* Units: 100ns */
|
||||
unsigned int hs_max_dtr;
|
||||
unsigned int sectors;
|
||||
|
@ -57,13 +63,18 @@ struct mmc_ext_csd {
|
|||
bool enhanced_area_en; /* enable bit */
|
||||
unsigned long long enhanced_area_offset; /* Units: Byte */
|
||||
unsigned int enhanced_area_size; /* Units: KB */
|
||||
unsigned int boot_size; /* in bytes */
|
||||
};
|
||||
|
||||
struct sd_scr {
|
||||
unsigned char sda_vsn;
|
||||
unsigned char sda_spec3;
|
||||
unsigned char bus_widths;
|
||||
#define SD_SCR_BUS_WIDTH_1 (1<<0)
|
||||
#define SD_SCR_BUS_WIDTH_4 (1<<2)
|
||||
unsigned char cmds;
|
||||
#define SD_SCR_CMD20_SUPPORT (1<<0)
|
||||
#define SD_SCR_CMD23_SUPPORT (1<<1)
|
||||
};
|
||||
|
||||
struct sd_ssr {
|
||||
|
@ -74,6 +85,39 @@ struct sd_ssr {
|
|||
|
||||
struct sd_switch_caps {
|
||||
unsigned int hs_max_dtr;
|
||||
unsigned int uhs_max_dtr;
|
||||
#define UHS_SDR104_MAX_DTR 208000000
|
||||
#define UHS_SDR50_MAX_DTR 100000000
|
||||
#define UHS_DDR50_MAX_DTR 50000000
|
||||
#define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR
|
||||
#define UHS_SDR12_MAX_DTR 25000000
|
||||
unsigned int sd3_bus_mode;
|
||||
#define UHS_SDR12_BUS_SPEED 0
|
||||
#define UHS_SDR25_BUS_SPEED 1
|
||||
#define UHS_SDR50_BUS_SPEED 2
|
||||
#define UHS_SDR104_BUS_SPEED 3
|
||||
#define UHS_DDR50_BUS_SPEED 4
|
||||
|
||||
#define SD_MODE_UHS_SDR12 (1 << UHS_SDR12_BUS_SPEED)
|
||||
#define SD_MODE_UHS_SDR25 (1 << UHS_SDR25_BUS_SPEED)
|
||||
#define SD_MODE_UHS_SDR50 (1 << UHS_SDR50_BUS_SPEED)
|
||||
#define SD_MODE_UHS_SDR104 (1 << UHS_SDR104_BUS_SPEED)
|
||||
#define SD_MODE_UHS_DDR50 (1 << UHS_DDR50_BUS_SPEED)
|
||||
unsigned int sd3_drv_type;
|
||||
#define SD_DRIVER_TYPE_B 0x01
|
||||
#define SD_DRIVER_TYPE_A 0x02
|
||||
#define SD_DRIVER_TYPE_C 0x04
|
||||
#define SD_DRIVER_TYPE_D 0x08
|
||||
unsigned int sd3_curr_limit;
|
||||
#define SD_SET_CURRENT_LIMIT_200 0
|
||||
#define SD_SET_CURRENT_LIMIT_400 1
|
||||
#define SD_SET_CURRENT_LIMIT_600 2
|
||||
#define SD_SET_CURRENT_LIMIT_800 3
|
||||
|
||||
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
|
||||
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
||||
#define SD_MAX_CURRENT_600 (1 << SD_SET_CURRENT_LIMIT_600)
|
||||
#define SD_MAX_CURRENT_800 (1 << SD_SET_CURRENT_LIMIT_800)
|
||||
};
|
||||
|
||||
struct sdio_cccr {
|
||||
|
@ -118,6 +162,8 @@ struct mmc_card {
|
|||
#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_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */
|
||||
#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
|
||||
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
|
||||
unsigned int quirks; /* card quirks */
|
||||
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
||||
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
||||
|
@ -125,6 +171,10 @@ struct mmc_card {
|
|||
#define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */
|
||||
/* (missing CIA registers) */
|
||||
#define MMC_QUIRK_BROKEN_CLK_GATING (1<<3) /* clock gating the sdio bus will make card fail */
|
||||
#define MMC_QUIRK_NONSTD_FUNC_IF (1<<4) /* SDIO card has nonstd function interfaces */
|
||||
#define MMC_QUIRK_DISABLE_CD (1<<5) /* disconnect CD/DAT[3] resistor */
|
||||
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
|
||||
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
|
||||
|
||||
unsigned int erase_size; /* erase size in sectors */
|
||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||
|
@ -145,14 +195,100 @@ struct mmc_card {
|
|||
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) */
|
||||
struct sdio_func *sdio_single_irq; /* SDIO function when only one IRQ active */
|
||||
unsigned num_info; /* number of info strings */
|
||||
const char **info; /* info strings */
|
||||
struct sdio_func_tuple *tuples; /* unknown common tuples */
|
||||
|
||||
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
||||
|
||||
struct dentry *debugfs_root;
|
||||
};
|
||||
|
||||
void mmc_fixup_device(struct mmc_card *dev);
|
||||
/*
|
||||
* The world is not perfect and supplies us with broken mmc/sdio devices.
|
||||
* For at least some of these bugs we need a work-around.
|
||||
*/
|
||||
|
||||
struct mmc_fixup {
|
||||
/* CID-specific fields. */
|
||||
const char *name;
|
||||
|
||||
/* Valid revision range */
|
||||
u64 rev_start, rev_end;
|
||||
|
||||
unsigned int manfid;
|
||||
unsigned short oemid;
|
||||
|
||||
/* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
|
||||
u16 cis_vendor, cis_device;
|
||||
|
||||
void (*vendor_fixup)(struct mmc_card *card, int data);
|
||||
int data;
|
||||
};
|
||||
|
||||
#define CID_MANFID_ANY (-1u)
|
||||
#define CID_OEMID_ANY ((unsigned short) -1)
|
||||
#define CID_NAME_ANY (NULL)
|
||||
|
||||
#define END_FIXUP { 0 }
|
||||
|
||||
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_cis_vendor, _cis_device, \
|
||||
_fixup, _data) \
|
||||
{ \
|
||||
.name = (_name), \
|
||||
.manfid = (_manfid), \
|
||||
.oemid = (_oemid), \
|
||||
.rev_start = (_rev_start), \
|
||||
.rev_end = (_rev_end), \
|
||||
.cis_vendor = (_cis_vendor), \
|
||||
.cis_device = (_cis_device), \
|
||||
.vendor_fixup = (_fixup), \
|
||||
.data = (_data), \
|
||||
}
|
||||
|
||||
#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_fixup, _data) \
|
||||
_FIXUP_EXT(_name, _manfid, \
|
||||
_oemid, _rev_start, _rev_end, \
|
||||
SDIO_ANY_ID, SDIO_ANY_ID, \
|
||||
_fixup, _data) \
|
||||
|
||||
#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
|
||||
MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data)
|
||||
|
||||
#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
|
||||
_FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
|
||||
CID_OEMID_ANY, 0, -1ull, \
|
||||
_vendor, _device, \
|
||||
_fixup, _data) \
|
||||
|
||||
#define cid_rev(hwrev, fwrev, year, month) \
|
||||
(((u64) hwrev) << 40 | \
|
||||
((u64) fwrev) << 32 | \
|
||||
((u64) year) << 16 | \
|
||||
((u64) month))
|
||||
|
||||
#define cid_rev_card(card) \
|
||||
cid_rev(card->cid.hwrev, \
|
||||
card->cid.fwrev, \
|
||||
card->cid.year, \
|
||||
card->cid.month)
|
||||
|
||||
/*
|
||||
* Unconditionally quirk add/remove.
|
||||
*/
|
||||
|
||||
static inline void __maybe_unused add_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
{
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
|
||||
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
|
||||
|
@ -163,12 +299,50 @@ void mmc_fixup_device(struct mmc_card *dev);
|
|||
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
|
||||
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
|
||||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
|
||||
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
|
||||
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
|
||||
|
||||
/*
|
||||
* Quirk add/remove for MMC products.
|
||||
*/
|
||||
|
||||
static inline void __maybe_unused add_quirk_mmc(struct mmc_card *card, int data)
|
||||
{
|
||||
if (mmc_card_mmc(card))
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk_mmc(struct mmc_card *card,
|
||||
int data)
|
||||
{
|
||||
if (mmc_card_mmc(card))
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quirk add/remove for SD products.
|
||||
*/
|
||||
|
||||
static inline void __maybe_unused add_quirk_sd(struct mmc_card *card, int data)
|
||||
{
|
||||
if (mmc_card_sd(card))
|
||||
card->quirks |= data;
|
||||
}
|
||||
|
||||
static inline void __maybe_unused remove_quirk_sd(struct mmc_card *card,
|
||||
int data)
|
||||
{
|
||||
if (mmc_card_sd(card))
|
||||
card->quirks &= ~data;
|
||||
}
|
||||
|
||||
static inline int mmc_card_lenient_fn0(const struct mmc_card *c)
|
||||
{
|
||||
|
@ -180,6 +354,16 @@ static inline int mmc_blksz_for_byte_mode(const struct mmc_card *c)
|
|||
return c->quirks & MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
|
||||
}
|
||||
|
||||
static inline int mmc_card_disable_cd(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_DISABLE_CD;
|
||||
}
|
||||
|
||||
static inline int mmc_card_nonstd_func_interface(const struct mmc_card *c)
|
||||
{
|
||||
return c->quirks & MMC_QUIRK_NONSTD_FUNC_IF;
|
||||
}
|
||||
|
||||
#define mmc_card_name(c) ((c)->cid.prod_name)
|
||||
#define mmc_card_id(c) (dev_name(&(c)->dev))
|
||||
|
||||
|
@ -203,4 +387,7 @@ struct mmc_driver {
|
|||
extern int mmc_register_driver(struct mmc_driver *);
|
||||
extern void mmc_unregister_driver(struct mmc_driver *);
|
||||
|
||||
extern void mmc_fixup_device(struct mmc_card *card,
|
||||
const struct mmc_fixup *table);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -92,7 +92,7 @@ struct mmc_command {
|
|||
* actively failing requests
|
||||
*/
|
||||
|
||||
unsigned int erase_timeout; /* in milliseconds */
|
||||
unsigned int cmd_timeout_ms; /* in milliseconds */
|
||||
|
||||
struct mmc_data *data; /* data segment associated with cmd */
|
||||
struct mmc_request *mrq; /* associated request */
|
||||
|
@ -120,6 +120,7 @@ struct mmc_data {
|
|||
};
|
||||
|
||||
struct mmc_request {
|
||||
struct mmc_command *sbc; /* SET_BLOCK_COUNT for multiblock */
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
struct mmc_command *stop;
|
||||
|
@ -133,8 +134,10 @@ struct mmc_card;
|
|||
|
||||
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
|
||||
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
|
||||
extern int mmc_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);
|
||||
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
|
||||
|
||||
#define MMC_ERASE_ARG 0x00000000
|
||||
#define MMC_SECURE_ERASE_ARG 0x80000000
|
||||
|
|
|
@ -50,12 +50,30 @@ struct mmc_ios {
|
|||
#define MMC_TIMING_LEGACY 0
|
||||
#define MMC_TIMING_MMC_HS 1
|
||||
#define MMC_TIMING_SD_HS 2
|
||||
#define MMC_TIMING_UHS_SDR12 MMC_TIMING_LEGACY
|
||||
#define MMC_TIMING_UHS_SDR25 MMC_TIMING_SD_HS
|
||||
#define MMC_TIMING_UHS_SDR50 3
|
||||
#define MMC_TIMING_UHS_SDR104 4
|
||||
#define MMC_TIMING_UHS_DDR50 5
|
||||
|
||||
unsigned char ddr; /* dual data rate used */
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
#define MMC_1_8V_DDR_MODE 2
|
||||
|
||||
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
|
||||
|
||||
#define MMC_SIGNAL_VOLTAGE_330 0
|
||||
#define MMC_SIGNAL_VOLTAGE_180 1
|
||||
#define MMC_SIGNAL_VOLTAGE_120 2
|
||||
|
||||
unsigned char drv_type; /* driver type (A, B, C, D) */
|
||||
|
||||
#define MMC_SET_DRIVER_TYPE_B 0
|
||||
#define MMC_SET_DRIVER_TYPE_A 1
|
||||
#define MMC_SET_DRIVER_TYPE_C 2
|
||||
#define MMC_SET_DRIVER_TYPE_D 3
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
|
@ -117,6 +135,10 @@ struct mmc_host_ops {
|
|||
|
||||
/* optional callback for HC quirks */
|
||||
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
|
||||
|
||||
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
|
||||
int (*execute_tuning)(struct mmc_host *host);
|
||||
void (*enable_preset_value)(struct mmc_host *host, bool enable);
|
||||
};
|
||||
|
||||
struct mmc_card;
|
||||
|
@ -173,6 +195,22 @@ struct mmc_host {
|
|||
/* DDR mode at 1.2V */
|
||||
#define MMC_CAP_POWER_OFF_CARD (1 << 13) /* Can power off after boot */
|
||||
#define MMC_CAP_BUS_WIDTH_TEST (1 << 14) /* CMD14/CMD19 bus width ok */
|
||||
#define MMC_CAP_UHS_SDR12 (1 << 15) /* Host supports UHS SDR12 mode */
|
||||
#define MMC_CAP_UHS_SDR25 (1 << 16) /* Host supports UHS SDR25 mode */
|
||||
#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
|
||||
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
|
||||
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
|
||||
#define MMC_CAP_SET_XPC_330 (1 << 20) /* Host supports >150mA current at 3.3V */
|
||||
#define MMC_CAP_SET_XPC_300 (1 << 21) /* Host supports >150mA current at 3.0V */
|
||||
#define MMC_CAP_SET_XPC_180 (1 << 22) /* Host supports >150mA current at 1.8V */
|
||||
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
|
||||
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
|
||||
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
|
||||
#define MMC_CAP_MAX_CURRENT_200 (1 << 26) /* Host max current limit is 200mA */
|
||||
#define MMC_CAP_MAX_CURRENT_400 (1 << 27) /* Host max current limit is 400mA */
|
||||
#define MMC_CAP_MAX_CURRENT_600 (1 << 28) /* Host max current limit is 600mA */
|
||||
#define MMC_CAP_MAX_CURRENT_800 (1 << 29) /* Host max current limit is 800mA */
|
||||
#define MMC_CAP_CMD23 (1 << 30) /* CMD23 supported. */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
|
@ -321,10 +359,19 @@ static inline int mmc_card_is_removable(struct mmc_host *host)
|
|||
return !(host->caps & MMC_CAP_NONREMOVABLE) && mmc_assume_removable;
|
||||
}
|
||||
|
||||
static inline int mmc_card_is_powered_resumed(struct mmc_host *host)
|
||||
static inline int mmc_card_keep_power(struct mmc_host *host)
|
||||
{
|
||||
return host->pm_flags & MMC_PM_KEEP_POWER;
|
||||
}
|
||||
|
||||
static inline int mmc_card_wake_sdio_irq(struct mmc_host *host)
|
||||
{
|
||||
return host->pm_flags & MMC_PM_WAKE_SDIO_IRQ;
|
||||
}
|
||||
|
||||
static inline int mmc_host_cmd23(struct mmc_host *host)
|
||||
{
|
||||
return host->caps & MMC_CAP_CMD23;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
#ifndef LINUX_MMC_IOCTL_H
|
||||
#define LINUX_MMC_IOCTL_H
|
||||
struct mmc_ioc_cmd {
|
||||
/* Implies direction of data. true = write, false = read */
|
||||
int write_flag;
|
||||
|
||||
/* Application-specific command. true = precede with CMD55 */
|
||||
int is_acmd;
|
||||
|
||||
__u32 opcode;
|
||||
__u32 arg;
|
||||
__u32 response[4]; /* CMD response */
|
||||
unsigned int flags;
|
||||
unsigned int blksz;
|
||||
unsigned int blocks;
|
||||
|
||||
/*
|
||||
* Sleep at least postsleep_min_us useconds, and at most
|
||||
* postsleep_max_us useconds *after* issuing command. Needed for
|
||||
* some read commands for which cards have no other way of indicating
|
||||
* they're ready for the next command (i.e. there is no equivalent of
|
||||
* a "busy" indicator for read operations).
|
||||
*/
|
||||
unsigned int postsleep_min_us;
|
||||
unsigned int postsleep_max_us;
|
||||
|
||||
/*
|
||||
* Override driver-computed timeouts. Note the difference in units!
|
||||
*/
|
||||
unsigned int data_timeout_ns;
|
||||
unsigned int cmd_timeout_ms;
|
||||
|
||||
/*
|
||||
* For 64-bit machines, the next member, ``__u64 data_ptr``, wants to
|
||||
* be 8-byte aligned. Make sure this struct is the same size when
|
||||
* built for 32-bit.
|
||||
*/
|
||||
__u32 __pad;
|
||||
|
||||
/* DAT buffer */
|
||||
__u64 data_ptr;
|
||||
};
|
||||
#define mmc_ioc_cmd_set_data(ic, ptr) ic.data_ptr = (__u64)(unsigned long) ptr
|
||||
|
||||
#define MMC_IOC_CMD _IOWR(MMC_BLOCK_MAJOR, 0, struct mmc_ioc_cmd)
|
||||
|
||||
/*
|
||||
* Since this ioctl is only meant to enhance (and not replace) normal access
|
||||
* to the mmc bus device, an upper data transfer limit of MMC_IOC_MAX_BYTES
|
||||
* is enforced per ioctl call. For larger data transfers, use the normal
|
||||
* block device operations.
|
||||
*/
|
||||
#define MMC_IOC_MAX_BYTES (512L * 256)
|
||||
#endif /* LINUX_MMC_IOCTL_H */
|
|
@ -50,6 +50,7 @@
|
|||
#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */
|
||||
#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */
|
||||
#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */
|
||||
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
|
||||
|
||||
/* class 3 */
|
||||
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
|
||||
|
@ -82,6 +83,12 @@
|
|||
#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */
|
||||
#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */
|
||||
|
||||
static inline bool mmc_op_multi(u32 opcode)
|
||||
{
|
||||
return opcode == MMC_WRITE_MULTIPLE_BLOCK ||
|
||||
opcode == MMC_READ_MULTIPLE_BLOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMC_SWITCH argument format:
|
||||
*
|
||||
|
@ -255,18 +262,23 @@ struct _mmc_csd {
|
|||
|
||||
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
|
||||
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
|
||||
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
||||
#define EXT_CSD_ERASE_GROUP_DEF 175 /* R/W */
|
||||
#define EXT_CSD_PART_CONFIG 179 /* R/W */
|
||||
#define EXT_CSD_ERASED_MEM_CONT 181 /* RO */
|
||||
#define EXT_CSD_BUS_WIDTH 183 /* R/W */
|
||||
#define EXT_CSD_HS_TIMING 185 /* R/W */
|
||||
#define EXT_CSD_REV 192 /* RO */
|
||||
#define EXT_CSD_STRUCTURE 194 /* RO */
|
||||
#define EXT_CSD_CARD_TYPE 196 /* RO */
|
||||
#define EXT_CSD_PART_SWITCH_TIME 199 /* RO */
|
||||
#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */
|
||||
#define EXT_CSD_S_A_TIMEOUT 217 /* RO */
|
||||
#define EXT_CSD_REL_WR_SEC_C 222 /* RO */
|
||||
#define EXT_CSD_HC_WP_GRP_SIZE 221 /* RO */
|
||||
#define EXT_CSD_ERASE_TIMEOUT_MULT 223 /* RO */
|
||||
#define EXT_CSD_HC_ERASE_GRP_SIZE 224 /* RO */
|
||||
#define EXT_CSD_BOOT_MULT 226 /* RO */
|
||||
#define EXT_CSD_SEC_TRIM_MULT 229 /* RO */
|
||||
#define EXT_CSD_SEC_ERASE_MULT 230 /* RO */
|
||||
#define EXT_CSD_SEC_FEATURE_SUPPORT 231 /* RO */
|
||||
|
@ -276,6 +288,12 @@ struct _mmc_csd {
|
|||
* EXT_CSD field definitions
|
||||
*/
|
||||
|
||||
#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
|
||||
|
||||
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x2)
|
||||
|
||||
#define EXT_CSD_CMD_SET_NORMAL (1<<0)
|
||||
#define EXT_CSD_CMD_SET_SECURE (1<<1)
|
||||
#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
/* This is basically the same command as for MMC with some quirks. */
|
||||
#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */
|
||||
#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */
|
||||
#define SD_SWITCH_VOLTAGE 11 /* ac R1 */
|
||||
|
||||
/* class 10 */
|
||||
#define SD_SWITCH 6 /* adtc [31:0] See below R1 */
|
||||
|
@ -32,6 +33,12 @@
|
|||
#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */
|
||||
#define SD_APP_SEND_SCR 51 /* adtc R1 */
|
||||
|
||||
/* OCR bit definitions */
|
||||
#define SD_OCR_S18R (1 << 24) /* 1.8V switching request */
|
||||
#define SD_ROCR_S18A SD_OCR_S18R /* 1.8V switching accepted by card */
|
||||
#define SD_OCR_XPC (1 << 28) /* SDXC power control */
|
||||
#define SD_OCR_CCS (1 << 30) /* Card Capacity Status */
|
||||
|
||||
/*
|
||||
* SD_SWITCH argument format:
|
||||
*
|
||||
|
@ -59,7 +66,7 @@
|
|||
|
||||
#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */
|
||||
#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */
|
||||
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */
|
||||
#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00-3.0X */
|
||||
|
||||
/*
|
||||
* SD bus widths
|
||||
|
|
|
@ -85,6 +85,8 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK_NO_HISPD_BIT (1<<29)
|
||||
/* Controller treats ADMA descriptors with length 0000h incorrectly */
|
||||
#define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30)
|
||||
/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */
|
||||
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
@ -109,11 +111,16 @@ struct sdhci_host {
|
|||
#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
|
||||
#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
|
||||
#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
|
||||
#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
|
||||
#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */
|
||||
#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
|
||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
unsigned int max_clk; /* Max possible freq (MHz) */
|
||||
unsigned int timeout_clk; /* Timeout freq (KHz) */
|
||||
unsigned int clk_mul; /* Clock Muliplier value */
|
||||
|
||||
unsigned int clock; /* Current clock (MHz) */
|
||||
u8 pwr; /* Current voltage */
|
||||
|
@ -145,6 +152,14 @@ struct sdhci_host {
|
|||
unsigned int ocr_avail_sd;
|
||||
unsigned int ocr_avail_mmc;
|
||||
|
||||
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
|
||||
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
|
||||
|
||||
unsigned int tuning_count; /* Timer count for re-tuning */
|
||||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
struct timer_list tuning_timer; /* Timer for tuning */
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
#endif /* __SDHCI_H */
|
||||
|
|
|
@ -3,12 +3,16 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct platform_device;
|
||||
struct tmio_mmc_data;
|
||||
|
||||
struct sh_mobile_sdhi_info {
|
||||
int dma_slave_tx;
|
||||
int dma_slave_rx;
|
||||
unsigned long tmio_flags;
|
||||
unsigned long tmio_caps;
|
||||
u32 tmio_ocr_mask; /* available MMC voltages */
|
||||
struct tmio_mmc_data *pdata;
|
||||
void (*set_pwr)(struct platform_device *pdev, int state);
|
||||
int (*get_cd)(struct platform_device *pdev);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue