mirror of
https://github.com/armbian/linux.git
synced 2026-01-06 10:13:00 -08:00
Merge tag 'mmc-merge-for-3.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
MMC highlights for 3.3: Core: * Support for the HS200 high-speed eMMC mode. * Support SDIO 3.0 Ultra High Speed cards. * Kill pending block requests immediately if card is removed. * Enable the eMMC feature for locking boot partitions read-only until next power on, exposed via sysfs. Drivers: * Runtime PM support for Intel Medfield SDIO. * Suspend/resume support for sdhci-spear. * sh-mmcif now processes requests asynchronously. * tag 'mmc-merge-for-3.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (58 commits) mmc: fix a deadlock between system suspend and MMC block IO mmc: sdhci: restore the enabled dma when do reset all mmc: dw_mmc: miscaculated the fifo-depth with wrong bit operation mmc: host: Adds support for eMMC 4.5 HS200 mode mmc: core: HS200 mode support for eMMC 4.5 mmc: dw_mmc: fixed wrong bit operation for SDMMC_GET_FCNT() mmc: core: Separate the timeout value for cache-ctrl mmc: sdhci-spear: Fix compilation error mmc: sdhci: Deal with failure case in sdhci_suspend_host mmc: dw_mmc: Clear the DDR mode for non-DDR mmc: sd: Fix SDR12 timing regression mmc: sdhci: Fix tuning timer incorrect setting when suspending host mmc: core: Add option to prevent eMMC sleep command mmc: omap_hsmmc: use threaded irq handler for card-detect. mmc: sdhci-pci: enable runtime PM for Medfield SDIO mmc: sdhci: Always pass clock request value zero to set_clock host op mmc: sdhci-pci: remove SDHCI_QUIRK2_OWN_CARD_DETECTION mmc: sdhci-pci: get gpio numbers from platform data mmc: sdhci-pci: add platform data mmc: sdhci: prevent card detection activity for non-removable cards ...
This commit is contained in:
@@ -64,3 +64,13 @@ Note on Erase Size and Preferred Erase Size:
|
||||
size specified by the card.
|
||||
|
||||
"preferred_erase_size" is in bytes.
|
||||
|
||||
SD/MMC/SDIO Clock Gating Attribute
|
||||
==================================
|
||||
|
||||
Read and write access is provided to following attribute.
|
||||
This attribute appears only if CONFIG_MMC_CLKGATE is enabled.
|
||||
|
||||
clkgate_delay Tune the clock gating delay with desired value in milliseconds.
|
||||
|
||||
echo <desired delay> > /sys/class/mmc_host/mmcX/clkgate_delay
|
||||
|
||||
@@ -25,3 +25,16 @@ echo 0 > /sys/block/mmcblkXbootY/force_ro
|
||||
To re-enable read-only access:
|
||||
|
||||
echo 1 > /sys/block/mmcblkXbootY/force_ro
|
||||
|
||||
The boot partitions can also be locked read only until the next power on,
|
||||
with:
|
||||
|
||||
echo 1 > /sys/block/mmcblkXbootY/ro_lock_until_next_power_on
|
||||
|
||||
This is a feature of the card and not of the kernel. If the card does
|
||||
not support boot partition locking, the file will not exist. If the
|
||||
feature has been disabled on the card, the file will be read-only.
|
||||
|
||||
The boot partitions can also be locked permanently, but this feature is
|
||||
not accessible through sysfs in order to avoid accidental or malicious
|
||||
bricking.
|
||||
|
||||
@@ -63,6 +63,7 @@ enum clk_types {
|
||||
struct s3c_sdhci_platdata {
|
||||
unsigned int max_width;
|
||||
unsigned int host_caps;
|
||||
unsigned int pm_caps;
|
||||
enum cd_types cd_type;
|
||||
enum clk_types clk_type;
|
||||
|
||||
|
||||
@@ -53,6 +53,8 @@ void s3c_sdhci_set_platdata(struct s3c_sdhci_platdata *pd,
|
||||
set->cfg_gpio = pd->cfg_gpio;
|
||||
if (pd->host_caps)
|
||||
set->host_caps |= pd->host_caps;
|
||||
if (pd->pm_caps)
|
||||
set->pm_caps |= pd->pm_caps;
|
||||
if (pd->clk_type)
|
||||
set->clk_type = pd->clk_type;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ obj-$(CONFIG_EISA) += eisa/
|
||||
obj-y += lguest/
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
obj-$(CONFIG_CPU_IDLE) += cpuidle/
|
||||
obj-$(CONFIG_MMC) += mmc/
|
||||
obj-y += mmc/
|
||||
obj-$(CONFIG_MEMSTICK) += memstick/
|
||||
obj-y += leds/
|
||||
obj-$(CONFIG_INFINIBAND) += infiniband/
|
||||
|
||||
@@ -6,5 +6,4 @@ subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG
|
||||
|
||||
obj-$(CONFIG_MMC) += core/
|
||||
obj-$(CONFIG_MMC) += card/
|
||||
obj-$(CONFIG_MMC) += host/
|
||||
|
||||
obj-$(subst m,y,$(CONFIG_MMC)) += host/
|
||||
|
||||
@@ -107,6 +107,8 @@ struct mmc_blk_data {
|
||||
*/
|
||||
unsigned int part_curr;
|
||||
struct device_attribute force_ro;
|
||||
struct device_attribute power_ro_lock;
|
||||
int area_type;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(open_lock);
|
||||
@@ -119,6 +121,7 @@ enum mmc_blk_status {
|
||||
MMC_BLK_ABORT,
|
||||
MMC_BLK_DATA_ERR,
|
||||
MMC_BLK_ECC_ERR,
|
||||
MMC_BLK_NOMEDIUM,
|
||||
};
|
||||
|
||||
module_param(perdev_minors, int, 0444);
|
||||
@@ -165,6 +168,70 @@ static void mmc_blk_put(struct mmc_blk_data *md)
|
||||
mutex_unlock(&open_lock);
|
||||
}
|
||||
|
||||
static ssize_t power_ro_lock_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev));
|
||||
struct mmc_card *card = md->queue.card;
|
||||
int locked = 0;
|
||||
|
||||
if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PERM_WP_EN)
|
||||
locked = 2;
|
||||
else if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_EN)
|
||||
locked = 1;
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "%d\n", locked);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t power_ro_lock_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_blk_data *md, *part_md;
|
||||
struct mmc_card *card;
|
||||
unsigned long set;
|
||||
|
||||
if (kstrtoul(buf, 0, &set))
|
||||
return -EINVAL;
|
||||
|
||||
if (set != 1)
|
||||
return count;
|
||||
|
||||
md = mmc_blk_get(dev_to_disk(dev));
|
||||
card = md->queue.card;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
|
||||
card->ext_csd.boot_ro_lock |
|
||||
EXT_CSD_BOOT_WP_B_PWR_WP_EN,
|
||||
card->ext_csd.part_time);
|
||||
if (ret)
|
||||
pr_err("%s: Locking boot partition ro until next power on failed: %d\n", md->disk->disk_name, ret);
|
||||
else
|
||||
card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
|
||||
|
||||
mmc_release_host(card->host);
|
||||
|
||||
if (!ret) {
|
||||
pr_info("%s: Locking boot partition ro until next power on\n",
|
||||
md->disk->disk_name);
|
||||
set_disk_ro(md->disk, 1);
|
||||
|
||||
list_for_each_entry(part_md, &md->part, part)
|
||||
if (part_md->area_type == MMC_BLK_DATA_AREA_BOOT) {
|
||||
pr_info("%s: Locking boot partition ro until next power on\n", part_md->disk->disk_name);
|
||||
set_disk_ro(part_md->disk, 1);
|
||||
}
|
||||
}
|
||||
|
||||
mmc_blk_put(md);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t force_ro_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@@ -266,6 +333,9 @@ static struct mmc_blk_ioc_data *mmc_blk_ioctl_copy_from_user(
|
||||
goto idata_err;
|
||||
}
|
||||
|
||||
if (!idata->buf_bytes)
|
||||
return idata;
|
||||
|
||||
idata->buf = kzalloc(idata->buf_bytes, GFP_KERNEL);
|
||||
if (!idata->buf) {
|
||||
err = -ENOMEM;
|
||||
@@ -312,25 +382,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
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;
|
||||
@@ -343,6 +394,48 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
cmd.opcode = idata->ic.opcode;
|
||||
cmd.arg = idata->ic.arg;
|
||||
cmd.flags = idata->ic.flags;
|
||||
|
||||
if (idata->buf_bytes) {
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
mrq.data = &data;
|
||||
}
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
|
||||
if (idata->ic.is_acmd) {
|
||||
@@ -351,24 +444,6 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
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) {
|
||||
@@ -565,6 +640,7 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
|
||||
return err;
|
||||
}
|
||||
|
||||
#define ERR_NOMEDIUM 3
|
||||
#define ERR_RETRY 2
|
||||
#define ERR_ABORT 1
|
||||
#define ERR_CONTINUE 0
|
||||
@@ -632,6 +708,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
u32 status, stop_status = 0;
|
||||
int err, retry;
|
||||
|
||||
if (mmc_card_removed(card))
|
||||
return ERR_NOMEDIUM;
|
||||
|
||||
/*
|
||||
* Try to get card status which indicates both the card state
|
||||
* and why there was no response. If the first attempt fails,
|
||||
@@ -648,8 +727,12 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
|
||||
}
|
||||
|
||||
/* We couldn't get a response from the card. Give up. */
|
||||
if (err)
|
||||
if (err) {
|
||||
/* Check if the card is removed */
|
||||
if (mmc_detect_card_removed(card->host))
|
||||
return ERR_NOMEDIUM;
|
||||
return ERR_ABORT;
|
||||
}
|
||||
|
||||
/* Flag ECC errors */
|
||||
if ((status & R1_CARD_ECC_FAILED) ||
|
||||
@@ -922,6 +1005,8 @@ static int mmc_blk_err_check(struct mmc_card *card,
|
||||
return MMC_BLK_RETRY;
|
||||
case ERR_ABORT:
|
||||
return MMC_BLK_ABORT;
|
||||
case ERR_NOMEDIUM:
|
||||
return MMC_BLK_NOMEDIUM;
|
||||
case ERR_CONTINUE:
|
||||
break;
|
||||
}
|
||||
@@ -1255,6 +1340,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||
if (!ret)
|
||||
goto start_new_req;
|
||||
break;
|
||||
case MMC_BLK_NOMEDIUM:
|
||||
goto cmd_abort;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
@@ -1271,6 +1358,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
|
||||
|
||||
cmd_abort:
|
||||
spin_lock_irq(&md->lock);
|
||||
if (mmc_card_removed(card))
|
||||
req->cmd_flags |= REQ_QUIET;
|
||||
while (ret)
|
||||
ret = __blk_end_request(req, -EIO, blk_rq_cur_bytes(req));
|
||||
spin_unlock_irq(&md->lock);
|
||||
@@ -1339,7 +1428,8 @@ 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)
|
||||
const char *subname,
|
||||
int area_type)
|
||||
{
|
||||
struct mmc_blk_data *md;
|
||||
int devidx, ret;
|
||||
@@ -1364,11 +1454,12 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
|
||||
if (!subname) {
|
||||
md->name_idx = find_first_zero_bit(name_use, max_devices);
|
||||
__set_bit(md->name_idx, name_use);
|
||||
}
|
||||
else
|
||||
} else
|
||||
md->name_idx = ((struct mmc_blk_data *)
|
||||
dev_to_disk(parent)->private_data)->name_idx;
|
||||
|
||||
md->area_type = area_type;
|
||||
|
||||
/*
|
||||
* Set the read-only status based on the supported commands
|
||||
* and the write protect switch.
|
||||
@@ -1462,7 +1553,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
|
||||
size = card->csd.capacity << (card->csd.read_blkbits - 9);
|
||||
}
|
||||
|
||||
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL);
|
||||
md = mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
|
||||
MMC_BLK_DATA_AREA_MAIN);
|
||||
return md;
|
||||
}
|
||||
|
||||
@@ -1471,13 +1563,14 @@ static int mmc_blk_alloc_part(struct mmc_card *card,
|
||||
unsigned int part_type,
|
||||
sector_t size,
|
||||
bool default_ro,
|
||||
const char *subname)
|
||||
const char *subname,
|
||||
int area_type)
|
||||
{
|
||||
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);
|
||||
subname, area_type);
|
||||
if (IS_ERR(part_md))
|
||||
return PTR_ERR(part_md);
|
||||
part_md->part_type = part_type;
|
||||
@@ -1510,7 +1603,8 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md)
|
||||
card->part[idx].part_cfg,
|
||||
card->part[idx].size >> 9,
|
||||
card->part[idx].force_ro,
|
||||
card->part[idx].name);
|
||||
card->part[idx].name,
|
||||
card->part[idx].area_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -1539,9 +1633,16 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
|
||||
|
||||
static void mmc_blk_remove_req(struct mmc_blk_data *md)
|
||||
{
|
||||
struct mmc_card *card;
|
||||
|
||||
if (md) {
|
||||
card = md->queue.card;
|
||||
if (md->disk->flags & GENHD_FL_UP) {
|
||||
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
|
||||
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
||||
card->ext_csd.boot_ro_lockable)
|
||||
device_remove_file(disk_to_dev(md->disk),
|
||||
&md->power_ro_lock);
|
||||
|
||||
/* Stop new requests from getting into the queue */
|
||||
del_gendisk(md->disk);
|
||||
@@ -1570,6 +1671,7 @@ static void mmc_blk_remove_parts(struct mmc_card *card,
|
||||
static int mmc_add_disk(struct mmc_blk_data *md)
|
||||
{
|
||||
int ret;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
|
||||
add_disk(md->disk);
|
||||
md->force_ro.show = force_ro_show;
|
||||
@@ -1579,18 +1681,53 @@ static int mmc_add_disk(struct mmc_blk_data *md)
|
||||
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);
|
||||
goto force_ro_fail;
|
||||
|
||||
if ((md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
||||
card->ext_csd.boot_ro_lockable) {
|
||||
mode_t mode;
|
||||
|
||||
if (card->ext_csd.boot_ro_lock & EXT_CSD_BOOT_WP_B_PWR_WP_DIS)
|
||||
mode = S_IRUGO;
|
||||
else
|
||||
mode = S_IRUGO | S_IWUSR;
|
||||
|
||||
md->power_ro_lock.show = power_ro_lock_show;
|
||||
md->power_ro_lock.store = power_ro_lock_store;
|
||||
md->power_ro_lock.attr.mode = mode;
|
||||
md->power_ro_lock.attr.name =
|
||||
"ro_lock_until_next_power_on";
|
||||
ret = device_create_file(disk_to_dev(md->disk),
|
||||
&md->power_ro_lock);
|
||||
if (ret)
|
||||
goto power_ro_lock_fail;
|
||||
}
|
||||
return ret;
|
||||
|
||||
power_ro_lock_fail:
|
||||
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
|
||||
force_ro_fail:
|
||||
del_gendisk(md->disk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define CID_MANFID_SANDISK 0x2
|
||||
#define CID_MANFID_TOSHIBA 0x11
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
|
||||
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),
|
||||
MMC_FIXUP("SEM02G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM04G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM08G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM16G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
MMC_FIXUP("SEM32G", CID_MANFID_SANDISK, 0x100, add_quirk,
|
||||
MMC_QUIRK_INAND_CMD38),
|
||||
|
||||
/*
|
||||
* Some MMC cards experience performance degradation with CMD23
|
||||
@@ -1600,18 +1737,18 @@ static const struct mmc_fixup blk_fixups[] =
|
||||
*
|
||||
* N.B. This doesn't affect SD cards.
|
||||
*/
|
||||
MMC_FIXUP("MMC08G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC16G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC32G", 0x11, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_FIXUP("MMC32G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
|
||||
/*
|
||||
* Some Micron MMC cards needs longer data read timeout than
|
||||
* indicated in CSD.
|
||||
*/
|
||||
MMC_FIXUP(CID_NAME_ANY, 0x13, 0x200, add_quirk_mmc,
|
||||
MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc,
|
||||
MMC_QUIRK_LONG_READ_TIME),
|
||||
|
||||
END_FIXUP
|
||||
|
||||
@@ -1581,6 +1581,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill)
|
||||
|
||||
t->max_segs = test->card->host->max_segs;
|
||||
t->max_seg_sz = test->card->host->max_seg_size;
|
||||
t->max_seg_sz -= t->max_seg_sz % 512;
|
||||
|
||||
t->max_tfr = t->max_sz;
|
||||
if (t->max_tfr >> 9 > test->card->host->max_blk_count)
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
*/
|
||||
static int mmc_prep_request(struct request_queue *q, struct request *req)
|
||||
{
|
||||
struct mmc_queue *mq = q->queuedata;
|
||||
|
||||
/*
|
||||
* We only like normal block requests and discards.
|
||||
*/
|
||||
@@ -37,6 +39,9 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
|
||||
return BLKPREP_KILL;
|
||||
}
|
||||
|
||||
if (mq && mmc_card_removed(mq->card))
|
||||
return BLKPREP_KILL;
|
||||
|
||||
req->cmd_flags |= REQ_DONTPREP;
|
||||
|
||||
return BLKPREP_OK;
|
||||
|
||||
@@ -7,6 +7,6 @@ mmc_core-y := core.o bus.o host.o \
|
||||
mmc.o mmc_ops.o sd.o sd_ops.o \
|
||||
sdio.o sdio_ops.o sdio_bus.o \
|
||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||
quirks.o
|
||||
quirks.o cd-gpio.o
|
||||
|
||||
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
|
||||
@@ -303,10 +303,11 @@ int mmc_add_card(struct mmc_card *card)
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type);
|
||||
} else {
|
||||
printk(KERN_INFO "%s: new %s%s%s card at address %04x\n",
|
||||
pr_info("%s: new %s%s%s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_sd_card_uhs(card) ? "ultra high speed " :
|
||||
mmc_card_uhs(card) ? "ultra high speed " :
|
||||
(mmc_card_highspeed(card) ? "high speed " : ""),
|
||||
(mmc_card_hs200(card) ? "HS200 " : ""),
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
type, card->rca);
|
||||
}
|
||||
|
||||
74
drivers/mmc/core/cd-gpio.c
Normal file
74
drivers/mmc/core/cd-gpio.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Generic GPIO card-detect helper
|
||||
*
|
||||
* Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct mmc_cd_gpio {
|
||||
unsigned int gpio;
|
||||
char label[0];
|
||||
};
|
||||
|
||||
static irqreturn_t mmc_cd_gpio_irqt(int irq, void *dev_id)
|
||||
{
|
||||
/* Schedule a card detection after a debounce timeout */
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(100));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int irq, unsigned long flags)
|
||||
{
|
||||
size_t len = strlen(dev_name(host->parent)) + 4;
|
||||
struct mmc_cd_gpio *cd = kmalloc(sizeof(*cd) + len, GFP_KERNEL);
|
||||
int ret;
|
||||
|
||||
if (!cd)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(cd->label, len, "%s cd", dev_name(host->parent));
|
||||
|
||||
ret = gpio_request_one(gpio, GPIOF_DIR_IN, cd->label);
|
||||
if (ret < 0)
|
||||
goto egpioreq;
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, mmc_cd_gpio_irqt,
|
||||
flags, cd->label, host);
|
||||
if (ret < 0)
|
||||
goto eirqreq;
|
||||
|
||||
cd->gpio = gpio;
|
||||
host->hotplug.irq = irq;
|
||||
host->hotplug.handler_priv = cd;
|
||||
|
||||
return 0;
|
||||
|
||||
eirqreq:
|
||||
gpio_free(gpio);
|
||||
egpioreq:
|
||||
kfree(cd);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cd_gpio_request);
|
||||
|
||||
void mmc_cd_gpio_free(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_cd_gpio *cd = host->hotplug.handler_priv;
|
||||
|
||||
free_irq(host->hotplug.irq, host);
|
||||
gpio_free(cd->gpio);
|
||||
kfree(cd);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_cd_gpio_free);
|
||||
@@ -140,7 +140,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
|
||||
cmd->retries = 0;
|
||||
}
|
||||
|
||||
if (err && cmd->retries) {
|
||||
if (err && cmd->retries && !mmc_card_removed(host->card)) {
|
||||
/*
|
||||
* Request starter must handle retries - see
|
||||
* mmc_wait_for_req_done().
|
||||
@@ -247,6 +247,11 @@ static void __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
init_completion(&mrq->completion);
|
||||
mrq->done = mmc_wait_done;
|
||||
if (mmc_card_removed(host->card)) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
complete(&mrq->completion);
|
||||
return;
|
||||
}
|
||||
mmc_start_request(host, mrq);
|
||||
}
|
||||
|
||||
@@ -259,7 +264,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||
wait_for_completion(&mrq->completion);
|
||||
|
||||
cmd = mrq->cmd;
|
||||
if (!cmd->error || !cmd->retries)
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card))
|
||||
break;
|
||||
|
||||
pr_debug("%s: req failed (CMD%u): %d, retrying...\n",
|
||||
@@ -1456,7 +1462,7 @@ void mmc_detect_change(struct mmc_host *host, unsigned long delay)
|
||||
WARN_ON(host->removed);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
host->detect_change = 1;
|
||||
mmc_schedule_delayed_work(&host->detect, delay);
|
||||
}
|
||||
|
||||
@@ -2049,6 +2055,43 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int _mmc_detect_card_removed(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
|
||||
return 0;
|
||||
|
||||
if (!host->card || mmc_card_removed(host->card))
|
||||
return 1;
|
||||
|
||||
ret = host->bus_ops->alive(host);
|
||||
if (ret) {
|
||||
mmc_card_set_removed(host->card);
|
||||
pr_debug("%s: card remove detected\n", mmc_hostname(host));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mmc_detect_card_removed(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
WARN_ON(!host->claimed);
|
||||
/*
|
||||
* The card will be considered unchanged unless we have been asked to
|
||||
* detect a change or host requires polling to provide card detection.
|
||||
*/
|
||||
if (card && !host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
|
||||
return mmc_card_removed(card);
|
||||
|
||||
host->detect_change = 0;
|
||||
|
||||
return _mmc_detect_card_removed(host);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_detect_card_removed);
|
||||
|
||||
void mmc_rescan(struct work_struct *work)
|
||||
{
|
||||
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
@@ -2069,6 +2112,8 @@ void mmc_rescan(struct work_struct *work)
|
||||
&& !(host->caps & MMC_CAP_NONREMOVABLE))
|
||||
host->bus_ops->detect(host);
|
||||
|
||||
host->detect_change = 0;
|
||||
|
||||
/*
|
||||
* Let mmc_bus_put() free the bus/bus_ops if we've found that
|
||||
* the card is no longer present.
|
||||
@@ -2130,6 +2175,7 @@ void mmc_stop_host(struct mmc_host *host)
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
if (host->bus_ops->remove)
|
||||
host->bus_ops->remove(host);
|
||||
|
||||
@@ -2201,6 +2247,9 @@ int mmc_card_awake(struct mmc_host *host)
|
||||
{
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
|
||||
return 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (host->bus_ops && !host->bus_dead && host->bus_ops->awake)
|
||||
@@ -2216,6 +2265,9 @@ int mmc_card_sleep(struct mmc_host *host)
|
||||
{
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
|
||||
return 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
|
||||
if (host->bus_ops && !host->bus_dead && host->bus_ops->sleep)
|
||||
@@ -2270,6 +2322,7 @@ EXPORT_SYMBOL(mmc_flush_cache);
|
||||
int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
unsigned int timeout;
|
||||
int err = 0;
|
||||
|
||||
if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
|
||||
@@ -2280,16 +2333,18 @@ int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
|
||||
(card->ext_csd.cache_size > 0)) {
|
||||
enable = !!enable;
|
||||
|
||||
if (card->ext_csd.cache_ctrl ^ enable)
|
||||
if (card->ext_csd.cache_ctrl ^ enable) {
|
||||
timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_CACHE_CTRL, enable, 0);
|
||||
if (err)
|
||||
pr_err("%s: cache %s error %d\n",
|
||||
mmc_hostname(card->host),
|
||||
enable ? "on" : "off",
|
||||
err);
|
||||
else
|
||||
card->ext_csd.cache_ctrl = enable;
|
||||
EXT_CSD_CACHE_CTRL, enable, timeout);
|
||||
if (err)
|
||||
pr_err("%s: cache %s error %d\n",
|
||||
mmc_hostname(card->host),
|
||||
enable ? "on" : "off",
|
||||
err);
|
||||
else
|
||||
card->ext_csd.cache_ctrl = enable;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
@@ -2310,7 +2365,13 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||
cancel_delayed_work(&host->disable);
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
err = mmc_cache_ctrl(host, 0);
|
||||
if (mmc_try_claim_host(host)) {
|
||||
err = mmc_cache_ctrl(host, 0);
|
||||
mmc_do_release_host(host);
|
||||
} else {
|
||||
err = -EBUSY;
|
||||
}
|
||||
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
@@ -2338,7 +2399,9 @@ int mmc_suspend_host(struct mmc_host *host)
|
||||
if (err == -ENOSYS || !host->bus_ops->resume) {
|
||||
/*
|
||||
* We simply "remove" the card in this case.
|
||||
* It will be redetected on resume.
|
||||
* It will be redetected on resume. (Calling
|
||||
* bus_ops->remove() with a claimed host can
|
||||
* deadlock.)
|
||||
*/
|
||||
if (host->bus_ops->remove)
|
||||
host->bus_ops->remove(host);
|
||||
@@ -2431,11 +2494,11 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||
if (!host->bus_ops || host->bus_ops->suspend)
|
||||
break;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
if (host->bus_ops->remove)
|
||||
host->bus_ops->remove(host);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
|
||||
@@ -24,6 +24,7 @@ struct mmc_bus_ops {
|
||||
int (*resume)(struct mmc_host *);
|
||||
int (*power_save)(struct mmc_host *);
|
||||
int (*power_restore)(struct mmc_host *);
|
||||
int (*alive)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
@@ -59,6 +60,8 @@ void mmc_rescan(struct work_struct *work);
|
||||
void mmc_start_host(struct mmc_host *host);
|
||||
void mmc_stop_host(struct mmc_host *host);
|
||||
|
||||
int _mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
||||
int mmc_attach_mmc(struct mmc_host *host);
|
||||
int mmc_attach_sd(struct mmc_host *host);
|
||||
int mmc_attach_sdio(struct mmc_host *host);
|
||||
|
||||
@@ -57,6 +57,8 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
||||
const char *str;
|
||||
|
||||
seq_printf(s, "clock:\t\t%u Hz\n", ios->clock);
|
||||
if (host->actual_clock)
|
||||
seq_printf(s, "actual clock:\t%u Hz\n", host->actual_clock);
|
||||
seq_printf(s, "vdd:\t\t%u ", ios->vdd);
|
||||
if ((1 << ios->vdd) & MMC_VDD_165_195)
|
||||
seq_printf(s, "(1.65 - 1.95 V)\n");
|
||||
@@ -133,6 +135,9 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
str = "sd uhs DDR50";
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
str = "mmc high-speed SDR200";
|
||||
break;
|
||||
default:
|
||||
str = "invalid";
|
||||
break;
|
||||
|
||||
@@ -54,6 +54,27 @@ static DEFINE_IDR(mmc_host_idr);
|
||||
static DEFINE_SPINLOCK(mmc_host_lock);
|
||||
|
||||
#ifdef CONFIG_MMC_CLKGATE
|
||||
static ssize_t clkgate_delay_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
return snprintf(buf, PAGE_SIZE, "%lu\n", host->clkgate_delay);
|
||||
}
|
||||
|
||||
static ssize_t clkgate_delay_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct mmc_host *host = cls_dev_to_mmc_host(dev);
|
||||
unsigned long flags, value;
|
||||
|
||||
if (kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&host->clk_lock, flags);
|
||||
host->clkgate_delay = value;
|
||||
spin_unlock_irqrestore(&host->clk_lock, flags);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enabling clock gating will make the core call out to the host
|
||||
@@ -114,7 +135,7 @@ static void mmc_host_clk_gate_delayed(struct mmc_host *host)
|
||||
static void mmc_host_clk_gate_work(struct work_struct *work)
|
||||
{
|
||||
struct mmc_host *host = container_of(work, struct mmc_host,
|
||||
clk_gate_work);
|
||||
clk_gate_work.work);
|
||||
|
||||
mmc_host_clk_gate_delayed(host);
|
||||
}
|
||||
@@ -131,6 +152,8 @@ void mmc_host_clk_hold(struct mmc_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
/* cancel any clock gating work scheduled by mmc_host_clk_release() */
|
||||
cancel_delayed_work_sync(&host->clk_gate_work);
|
||||
mutex_lock(&host->clk_gate_mutex);
|
||||
spin_lock_irqsave(&host->clk_lock, flags);
|
||||
if (host->clk_gated) {
|
||||
@@ -180,7 +203,8 @@ void mmc_host_clk_release(struct mmc_host *host)
|
||||
host->clk_requests--;
|
||||
if (mmc_host_may_gate_card(host->card) &&
|
||||
!host->clk_requests)
|
||||
queue_work(system_nrt_wq, &host->clk_gate_work);
|
||||
queue_delayed_work(system_nrt_wq, &host->clk_gate_work,
|
||||
msecs_to_jiffies(host->clkgate_delay));
|
||||
spin_unlock_irqrestore(&host->clk_lock, flags);
|
||||
}
|
||||
|
||||
@@ -213,8 +237,13 @@ static inline void mmc_host_clk_init(struct mmc_host *host)
|
||||
host->clk_requests = 0;
|
||||
/* Hold MCI clock for 8 cycles by default */
|
||||
host->clk_delay = 8;
|
||||
/*
|
||||
* Default clock gating delay is 200ms.
|
||||
* This value can be tuned by writing into sysfs entry.
|
||||
*/
|
||||
host->clkgate_delay = 200;
|
||||
host->clk_gated = false;
|
||||
INIT_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
|
||||
INIT_DELAYED_WORK(&host->clk_gate_work, mmc_host_clk_gate_work);
|
||||
spin_lock_init(&host->clk_lock);
|
||||
mutex_init(&host->clk_gate_mutex);
|
||||
}
|
||||
@@ -229,7 +258,7 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
|
||||
* Wait for any outstanding gate and then make sure we're
|
||||
* ungated before exiting.
|
||||
*/
|
||||
if (cancel_work_sync(&host->clk_gate_work))
|
||||
if (cancel_delayed_work_sync(&host->clk_gate_work))
|
||||
mmc_host_clk_gate_delayed(host);
|
||||
if (host->clk_gated)
|
||||
mmc_host_clk_hold(host);
|
||||
@@ -237,6 +266,17 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
|
||||
WARN_ON(host->clk_requests > 1);
|
||||
}
|
||||
|
||||
static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
|
||||
{
|
||||
host->clkgate_delay_attr.show = clkgate_delay_show;
|
||||
host->clkgate_delay_attr.store = clkgate_delay_store;
|
||||
sysfs_attr_init(&host->clkgate_delay_attr.attr);
|
||||
host->clkgate_delay_attr.attr.name = "clkgate_delay";
|
||||
host->clkgate_delay_attr.attr.mode = S_IRUGO | S_IWUSR;
|
||||
if (device_create_file(&host->class_dev, &host->clkgate_delay_attr))
|
||||
pr_err("%s: Failed to create clkgate_delay sysfs entry\n",
|
||||
mmc_hostname(host));
|
||||
}
|
||||
#else
|
||||
|
||||
static inline void mmc_host_clk_init(struct mmc_host *host)
|
||||
@@ -247,6 +287,10 @@ static inline void mmc_host_clk_exit(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -335,6 +379,7 @@ int mmc_add_host(struct mmc_host *host)
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
mmc_add_host_debugfs(host);
|
||||
#endif
|
||||
mmc_host_clk_sysfs_init(host);
|
||||
|
||||
mmc_start_host(host);
|
||||
register_pm_notifier(&host->pm_notify);
|
||||
|
||||
@@ -286,6 +286,27 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
}
|
||||
card->ext_csd.raw_card_type = ext_csd[EXT_CSD_CARD_TYPE];
|
||||
switch (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_MASK) {
|
||||
case EXT_CSD_CARD_TYPE_SDR_ALL:
|
||||
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52:
|
||||
card->ext_csd.hs_max_dtr = 200000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_200;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_2V_ALL:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52:
|
||||
card->ext_csd.hs_max_dtr = 200000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_2V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_8V_ALL:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V:
|
||||
case EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52:
|
||||
card->ext_csd.hs_max_dtr = 200000000;
|
||||
card->ext_csd.card_type = EXT_CSD_CARD_TYPE_SDR_1_8V;
|
||||
break;
|
||||
case EXT_CSD_CARD_TYPE_DDR_52 | EXT_CSD_CARD_TYPE_52 |
|
||||
EXT_CSD_CARD_TYPE_26:
|
||||
card->ext_csd.hs_max_dtr = 52000000;
|
||||
@@ -348,7 +369,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
part_size = ext_csd[EXT_CSD_BOOT_MULT] << 17;
|
||||
mmc_part_add(card, part_size,
|
||||
EXT_CSD_PART_CONFIG_ACC_BOOT0 + idx,
|
||||
"boot%d", idx, true);
|
||||
"boot%d", idx, true,
|
||||
MMC_BLK_DATA_AREA_BOOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +457,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
hc_wp_grp_sz);
|
||||
mmc_part_add(card, part_size << 19,
|
||||
EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
|
||||
"gp%d", idx, false);
|
||||
"gp%d", idx, false,
|
||||
MMC_BLK_DATA_AREA_GP);
|
||||
}
|
||||
}
|
||||
card->ext_csd.sec_trim_mult =
|
||||
@@ -446,6 +469,14 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
|
||||
card->ext_csd.trim_timeout = 300 *
|
||||
ext_csd[EXT_CSD_TRIM_MULT];
|
||||
|
||||
/*
|
||||
* Note that the call to mmc_part_add above defaults to read
|
||||
* only. If this default assumption is changed, the call must
|
||||
* take into account the value of boot_locked below.
|
||||
*/
|
||||
card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP];
|
||||
card->ext_csd.boot_ro_lockable = true;
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 5) {
|
||||
@@ -689,6 +720,79 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selects the desired buswidth and switch to the HS200 mode
|
||||
* if bus width set without error
|
||||
*/
|
||||
static int mmc_select_hs200(struct mmc_card *card)
|
||||
{
|
||||
int idx, err = 0;
|
||||
struct mmc_host *host;
|
||||
static unsigned ext_csd_bits[] = {
|
||||
EXT_CSD_BUS_WIDTH_4,
|
||||
EXT_CSD_BUS_WIDTH_8,
|
||||
};
|
||||
static unsigned bus_widths[] = {
|
||||
MMC_BUS_WIDTH_4,
|
||||
MMC_BUS_WIDTH_8,
|
||||
};
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
host = card->host;
|
||||
|
||||
if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
|
||||
host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
|
||||
if (mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120, 0))
|
||||
err = mmc_set_signal_voltage(host,
|
||||
MMC_SIGNAL_VOLTAGE_180, 0);
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
|
||||
|
||||
/*
|
||||
* Unlike SD, MMC cards dont have a configuration register to notify
|
||||
* supported bus width. So bus test command should be run to identify
|
||||
* the supported bus width or compare the ext csd values of current
|
||||
* bus width and ext csd values of 1 bit mode read earlier.
|
||||
*/
|
||||
for (; idx >= 0; idx--) {
|
||||
|
||||
/*
|
||||
* Host is capable of 8bit transfer, then switch
|
||||
* the device to work in 8bit transfer mode. If the
|
||||
* mmc switch command returns error then switch to
|
||||
* 4bit transfer mode. On success set the corresponding
|
||||
* bus width on the host.
|
||||
*/
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx],
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err)
|
||||
continue;
|
||||
|
||||
mmc_set_bus_width(card->host, bus_widths[idx]);
|
||||
|
||||
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
|
||||
err = mmc_compare_ext_csds(card, bus_widths[idx]);
|
||||
else
|
||||
err = mmc_bus_test(card, bus_widths[idx]);
|
||||
if (!err)
|
||||
break;
|
||||
}
|
||||
|
||||
/* switch to HS200 mode if bus width set successfully */
|
||||
if (!err)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 2, 0);
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
@@ -895,11 +999,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
/*
|
||||
* 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,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (card->ext_csd.hs_max_dtr != 0) {
|
||||
err = 0;
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
host->caps2 & MMC_CAP2_HS200)
|
||||
err = mmc_select_hs200(card);
|
||||
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1, 0);
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
|
||||
@@ -908,8 +1016,15 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
} else {
|
||||
mmc_card_set_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
host->caps2 & MMC_CAP2_HS200) {
|
||||
mmc_card_set_hs200(card);
|
||||
mmc_set_timing(card->host,
|
||||
MMC_TIMING_MMC_HS200);
|
||||
} else {
|
||||
mmc_card_set_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -934,7 +1049,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
*/
|
||||
max_dtr = (unsigned int)-1;
|
||||
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
|
||||
if (max_dtr > card->ext_csd.hs_max_dtr)
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
@@ -959,10 +1074,49 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate HS200 SDR mode (if supported).
|
||||
*/
|
||||
if (mmc_card_hs200(card)) {
|
||||
u32 ext_csd_bits;
|
||||
u32 bus_width = card->host->ios.bus_width;
|
||||
|
||||
/*
|
||||
* For devices supporting HS200 mode, the bus width has
|
||||
* to be set before executing the tuning function. If
|
||||
* set before tuning, then device will respond with CRC
|
||||
* errors for responses on CMD line. So for HS200 the
|
||||
* sequence will be
|
||||
* 1. set bus width 4bit / 8 bit (1 bit not supported)
|
||||
* 2. switch to HS200 mode
|
||||
* 3. set the clock to > 52Mhz <=200MHz and
|
||||
* 4. execute tuning for HS200
|
||||
*/
|
||||
if ((host->caps2 & MMC_CAP2_HS200) &&
|
||||
card->host->ops->execute_tuning)
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
if (err) {
|
||||
pr_warning("%s: tuning execution failed\n",
|
||||
mmc_hostname(card->host));
|
||||
goto err;
|
||||
}
|
||||
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
|
||||
err = mmc_select_powerclass(card, ext_csd_bits, ext_csd);
|
||||
if (err) {
|
||||
pr_err("%s: power class selection to bus width %d failed\n",
|
||||
mmc_hostname(card->host), 1 << bus_width);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate wide bus and DDR (if supported).
|
||||
*/
|
||||
if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
||||
if (!mmc_card_hs200(card) &&
|
||||
(card->csd.mmca_vsn >= CSD_SPEC_VER_3) &&
|
||||
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
||||
static unsigned ext_csd_bits[][2] = {
|
||||
{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
|
||||
@@ -1048,7 +1202,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
*
|
||||
* WARNING: eMMC rules are NOT the same as SD DDR
|
||||
*/
|
||||
if (ddr == EXT_CSD_CARD_TYPE_DDR_1_2V) {
|
||||
if (ddr == MMC_1_2V_DDR_MODE) {
|
||||
err = mmc_set_signal_voltage(host,
|
||||
MMC_SIGNAL_VOLTAGE_120, 0);
|
||||
if (err)
|
||||
@@ -1067,14 +1221,23 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
|
||||
card->ext_csd.cache_size > 0) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_CACHE_CTRL, 1, 0);
|
||||
EXT_CSD_CACHE_CTRL, 1,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
|
||||
/*
|
||||
* Only if no error, cache is turned on successfully.
|
||||
*/
|
||||
card->ext_csd.cache_ctrl = err ? 0 : 1;
|
||||
if (err) {
|
||||
pr_warning("%s: Cache is supported, "
|
||||
"but failed to turn on (%d)\n",
|
||||
mmc_hostname(card->host), err);
|
||||
card->ext_csd.cache_ctrl = 0;
|
||||
err = 0;
|
||||
} else {
|
||||
card->ext_csd.cache_ctrl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldcard)
|
||||
@@ -1104,6 +1267,14 @@ static void mmc_remove(struct mmc_host *host)
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection - card is alive.
|
||||
*/
|
||||
static int mmc_alive(struct mmc_host *host)
|
||||
{
|
||||
return mmc_send_status(host->card, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection callback from host.
|
||||
*/
|
||||
@@ -1119,7 +1290,7 @@ static void mmc_detect(struct mmc_host *host)
|
||||
/*
|
||||
* Just check if our card has been removed.
|
||||
*/
|
||||
err = mmc_send_status(host->card, NULL);
|
||||
err = _mmc_detect_card_removed(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
@@ -1224,6 +1395,7 @@ static const struct mmc_bus_ops mmc_ops = {
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops_unsafe = {
|
||||
@@ -1234,6 +1406,7 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
|
||||
.suspend = mmc_suspend,
|
||||
.resume = mmc_resume,
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
};
|
||||
|
||||
static void mmc_attach_bus_ops(struct mmc_host *host)
|
||||
|
||||
@@ -307,8 +307,8 @@ static int mmc_read_switch(struct mmc_card *card)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status[13] & UHS_SDR50_BUS_SPEED)
|
||||
card->sw_caps.hs_max_dtr = 50000000;
|
||||
if (status[13] & SD_MODE_HIGH_SPEED)
|
||||
card->sw_caps.hs_max_dtr = HIGH_SPEED_MAX_DTR;
|
||||
|
||||
if (card->scr.sda_spec3) {
|
||||
card->sw_caps.sd3_bus_mode = status[13];
|
||||
@@ -661,7 +661,8 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
|
||||
/* 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);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
|
||||
out:
|
||||
kfree(status);
|
||||
@@ -960,7 +961,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
goto free_card;
|
||||
|
||||
/* Card is an ultra-high-speed card */
|
||||
mmc_sd_card_set_uhs(card);
|
||||
mmc_card_set_uhs(card);
|
||||
|
||||
/*
|
||||
* Since initialization is now complete, enable preset
|
||||
@@ -1018,6 +1019,14 @@ static void mmc_sd_remove(struct mmc_host *host)
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection - card is alive.
|
||||
*/
|
||||
static int mmc_sd_alive(struct mmc_host *host)
|
||||
{
|
||||
return mmc_send_status(host->card, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection callback from host.
|
||||
*/
|
||||
@@ -1033,7 +1042,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
/*
|
||||
* Just check if our card has been removed.
|
||||
*/
|
||||
err = mmc_send_status(host->card, NULL);
|
||||
err = _mmc_detect_card_removed(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
@@ -1102,6 +1111,7 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
|
||||
@@ -1110,6 +1120,7 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
|
||||
.suspend = mmc_sd_suspend,
|
||||
.resume = mmc_sd_resume,
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
};
|
||||
|
||||
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
@@ -102,6 +103,7 @@ static int sdio_read_cccr(struct mmc_card *card)
|
||||
int ret;
|
||||
int cccr_vsn;
|
||||
unsigned char data;
|
||||
unsigned char speed;
|
||||
|
||||
memset(&card->cccr, 0, sizeof(struct sdio_cccr));
|
||||
|
||||
@@ -140,12 +142,60 @@ static int sdio_read_cccr(struct mmc_card *card)
|
||||
}
|
||||
|
||||
if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (data & SDIO_SPEED_SHS)
|
||||
card->cccr.high_speed = 1;
|
||||
card->scr.sda_spec3 = 0;
|
||||
card->sw_caps.sd3_bus_mode = 0;
|
||||
card->sw_caps.sd3_drv_type = 0;
|
||||
if (cccr_vsn >= SDIO_CCCR_REV_3_00) {
|
||||
card->scr.sda_spec3 = 1;
|
||||
ret = mmc_io_rw_direct(card, 0, 0,
|
||||
SDIO_CCCR_UHS, 0, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (card->host->caps &
|
||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_DDR50)) {
|
||||
if (data & SDIO_UHS_DDR50)
|
||||
card->sw_caps.sd3_bus_mode
|
||||
|= SD_MODE_UHS_DDR50;
|
||||
|
||||
if (data & SDIO_UHS_SDR50)
|
||||
card->sw_caps.sd3_bus_mode
|
||||
|= SD_MODE_UHS_SDR50;
|
||||
|
||||
if (data & SDIO_UHS_SDR104)
|
||||
card->sw_caps.sd3_bus_mode
|
||||
|= SD_MODE_UHS_SDR104;
|
||||
}
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0,
|
||||
SDIO_CCCR_DRIVE_STRENGTH, 0, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (data & SDIO_DRIVE_SDTA)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A;
|
||||
if (data & SDIO_DRIVE_SDTC)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
|
||||
if (data & SDIO_DRIVE_SDTD)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
|
||||
}
|
||||
|
||||
/* if no uhs mode ensure we check for high speed */
|
||||
if (!card->sw_caps.sd3_bus_mode) {
|
||||
if (speed & SDIO_SPEED_SHS) {
|
||||
card->cccr.high_speed = 1;
|
||||
card->sw_caps.hs_max_dtr = 50000000;
|
||||
} else {
|
||||
card->cccr.high_speed = 0;
|
||||
card->sw_caps.hs_max_dtr = 25000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -327,6 +377,194 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
||||
return max_dtr;
|
||||
}
|
||||
|
||||
static unsigned char host_drive_to_sdio_drive(int host_strength)
|
||||
{
|
||||
switch (host_strength) {
|
||||
case MMC_SET_DRIVER_TYPE_A:
|
||||
return SDIO_DTSx_SET_TYPE_A;
|
||||
case MMC_SET_DRIVER_TYPE_B:
|
||||
return SDIO_DTSx_SET_TYPE_B;
|
||||
case MMC_SET_DRIVER_TYPE_C:
|
||||
return SDIO_DTSx_SET_TYPE_C;
|
||||
case MMC_SET_DRIVER_TYPE_D:
|
||||
return SDIO_DTSx_SET_TYPE_D;
|
||||
default:
|
||||
return SDIO_DTSx_SET_TYPE_B;
|
||||
}
|
||||
}
|
||||
|
||||
static void sdio_select_driver_type(struct mmc_card *card)
|
||||
{
|
||||
int host_drv_type = SD_DRIVER_TYPE_B;
|
||||
int card_drv_type = SD_DRIVER_TYPE_B;
|
||||
int drive_strength;
|
||||
unsigned char card_strength;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* If the host doesn't support any of the Driver Types A,C or D,
|
||||
* or there is no board specific handler then 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;
|
||||
|
||||
if (!card->host->ops->select_drive_strength)
|
||||
return;
|
||||
|
||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_A)
|
||||
host_drv_type |= SD_DRIVER_TYPE_A;
|
||||
|
||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_C)
|
||||
host_drv_type |= SD_DRIVER_TYPE_C;
|
||||
|
||||
if (card->host->caps & MMC_CAP_DRIVER_TYPE_D)
|
||||
host_drv_type |= SD_DRIVER_TYPE_D;
|
||||
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A)
|
||||
card_drv_type |= SD_DRIVER_TYPE_A;
|
||||
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C)
|
||||
card_drv_type |= SD_DRIVER_TYPE_C;
|
||||
|
||||
if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D)
|
||||
card_drv_type |= SD_DRIVER_TYPE_D;
|
||||
|
||||
/*
|
||||
* The drive strength that the hardware can support
|
||||
* depends on the board design. Pass the appropriate
|
||||
* information and let the hardware specific code
|
||||
* return what is possible given the options
|
||||
*/
|
||||
drive_strength = card->host->ops->select_drive_strength(
|
||||
card->sw_caps.uhs_max_dtr,
|
||||
host_drv_type, card_drv_type);
|
||||
|
||||
/* if error just use default for drive strength B */
|
||||
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0,
|
||||
&card_strength);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
card_strength &= ~(SDIO_DRIVE_DTSx_MASK<<SDIO_DRIVE_DTSx_SHIFT);
|
||||
card_strength |= host_drive_to_sdio_drive(drive_strength);
|
||||
|
||||
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_DRIVE_STRENGTH,
|
||||
card_strength, NULL);
|
||||
|
||||
/* if error default to drive strength B */
|
||||
if (!err)
|
||||
mmc_set_driver_type(card->host, drive_strength);
|
||||
}
|
||||
|
||||
|
||||
static int sdio_set_bus_speed_mode(struct mmc_card *card)
|
||||
{
|
||||
unsigned int bus_speed, timing;
|
||||
int err;
|
||||
unsigned char speed;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
bus_speed = SDIO_SPEED_SDR12;
|
||||
timing = MMC_TIMING_UHS_SDR12;
|
||||
if ((card->host->caps & MMC_CAP_UHS_SDR104) &&
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104)) {
|
||||
bus_speed = SDIO_SPEED_SDR104;
|
||||
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 = SDIO_SPEED_DDR50;
|
||||
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 = SDIO_SPEED_SDR50;
|
||||
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 = SDIO_SPEED_SDR25;
|
||||
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 = SDIO_SPEED_SDR12;
|
||||
timing = MMC_TIMING_UHS_SDR12;
|
||||
card->sw_caps.uhs_max_dtr = UHS_SDR12_MAX_DTR;
|
||||
}
|
||||
|
||||
err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &speed);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
speed &= ~SDIO_SPEED_BSS_MASK;
|
||||
speed |= bus_speed;
|
||||
err = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_SPEED, speed, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (bus_speed) {
|
||||
mmc_set_timing(card->host, timing);
|
||||
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* UHS-I specific initialization procedure
|
||||
*/
|
||||
static int mmc_sdio_init_uhs_card(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!card->scr.sda_spec3)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
if (card->host->caps & MMC_CAP_4_BIT_DATA) {
|
||||
err = sdio_enable_4bit_bus(card);
|
||||
if (err > 0) {
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the driver strength for the card */
|
||||
sdio_select_driver_type(card);
|
||||
|
||||
/* Set bus speed mode of the card */
|
||||
err = sdio_set_bus_speed_mode(card);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Initialize and start re-tuning timer */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
|
||||
out:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
@@ -393,6 +631,30 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
if (host->ops->init_card)
|
||||
host->ops->init_card(host, card);
|
||||
|
||||
/*
|
||||
* If the host and card support UHS-I mode request the card
|
||||
* to switch to 1.8V signaling level. No 1.8v signalling if
|
||||
* UHS mode is not enabled to maintain compatibilty and some
|
||||
* systems that claim 1.8v signalling in fact do not support
|
||||
* it.
|
||||
*/
|
||||
if ((ocr & R4_18V_PRESENT) &&
|
||||
(host->caps &
|
||||
(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
|
||||
MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 |
|
||||
MMC_CAP_UHS_DDR50))) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
|
||||
true);
|
||||
if (err) {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
err = 0;
|
||||
} else {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* For native busses: set card RCA and quit open drain mode.
|
||||
*/
|
||||
@@ -492,29 +754,39 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Switch to high-speed (if supported).
|
||||
*/
|
||||
err = sdio_enable_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
else if (err)
|
||||
goto remove;
|
||||
/* Initialization sequence for UHS-I cards */
|
||||
/* Only if card supports 1.8v and UHS signaling */
|
||||
if ((ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) {
|
||||
err = mmc_sdio_init_uhs_card(card);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Change to the card's maximum speed.
|
||||
*/
|
||||
mmc_set_clock(host, mmc_sdio_get_max_clock(card));
|
||||
/* Card is an ultra-high-speed card */
|
||||
mmc_card_set_uhs(card);
|
||||
} else {
|
||||
/*
|
||||
* Switch to high-speed (if supported).
|
||||
*/
|
||||
err = sdio_enable_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
else if (err)
|
||||
goto remove;
|
||||
|
||||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
err = sdio_enable_4bit_bus(card);
|
||||
if (err > 0)
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
else if (err)
|
||||
goto remove;
|
||||
/*
|
||||
* Change to the card's maximum speed.
|
||||
*/
|
||||
mmc_set_clock(host, mmc_sdio_get_max_clock(card));
|
||||
|
||||
/*
|
||||
* Switch to wider bus (if supported).
|
||||
*/
|
||||
err = sdio_enable_4bit_bus(card);
|
||||
if (err > 0)
|
||||
mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4);
|
||||
else if (err)
|
||||
goto remove;
|
||||
}
|
||||
finish:
|
||||
if (!oldcard)
|
||||
host->card = card;
|
||||
@@ -549,6 +821,14 @@ static void mmc_sdio_remove(struct mmc_host *host)
|
||||
host->card = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection - card is alive.
|
||||
*/
|
||||
static int mmc_sdio_alive(struct mmc_host *host)
|
||||
{
|
||||
return mmc_select_card(host->card);
|
||||
}
|
||||
|
||||
/*
|
||||
* Card detection callback from host.
|
||||
*/
|
||||
@@ -571,7 +851,7 @@ static void mmc_sdio_detect(struct mmc_host *host)
|
||||
/*
|
||||
* Just check if our card has been removed.
|
||||
*/
|
||||
err = mmc_select_card(host->card);
|
||||
err = _mmc_detect_card_removed(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
|
||||
@@ -749,6 +1029,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||
.suspend = mmc_sdio_suspend,
|
||||
.resume = mmc_sdio_resume,
|
||||
.power_restore = mmc_sdio_power_restore,
|
||||
.alive = mmc_sdio_alive,
|
||||
};
|
||||
|
||||
|
||||
@@ -797,8 +1078,17 @@ int mmc_attach_sdio(struct mmc_host *host)
|
||||
* Detect and init the card.
|
||||
*/
|
||||
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
|
||||
if (err)
|
||||
goto err;
|
||||
if (err) {
|
||||
if (err == -EAGAIN) {
|
||||
/*
|
||||
* Retry initialization with S18R set to 0.
|
||||
*/
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
|
||||
}
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
card = host->card;
|
||||
|
||||
/*
|
||||
|
||||
@@ -196,6 +196,9 @@ static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
|
||||
else
|
||||
mval = min(mval, func->max_blksize);
|
||||
|
||||
if (mmc_card_broken_byte_mode_512(func->card))
|
||||
return min(mval, 511u);
|
||||
|
||||
return min(mval, 512u); /* maximum size for byte mode */
|
||||
}
|
||||
|
||||
@@ -314,7 +317,7 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
||||
func->card->host->max_seg_size / func->cur_blksize);
|
||||
max_blocks = min(max_blocks, 511u);
|
||||
|
||||
while (remainder > func->cur_blksize) {
|
||||
while (remainder >= func->cur_blksize) {
|
||||
unsigned blocks;
|
||||
|
||||
blocks = remainder / func->cur_blksize;
|
||||
@@ -339,8 +342,9 @@ static int sdio_io_rw_ext_helper(struct sdio_func *func, int write,
|
||||
while (remainder > 0) {
|
||||
size = min(remainder, sdio_max_byte_size(func));
|
||||
|
||||
/* Indicate byte mode by setting "blocks" = 0 */
|
||||
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
|
||||
incr_addr, buf, 1, size);
|
||||
incr_addr, buf, 0, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user