mirror of
https://github.com/joel16/android_kernel_sony_msm8994.git
synced 2024-11-27 06:01:12 +00:00
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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPD0P3AAoJEHNBYZ7TNxYMLBQP/1nwYE0Y1prrKsiv6PWQlcDu LZl6kylgkq1Nd7hEX1E+kXjv+ec5Z+7RxqmyMerRJIMsfBtowLL0r9rzAu+BsNjZ t7YSODLCzCp80WGMDSxhgQEOiAXMlpTzD46hXC2hOjNyTXCsGz1smUfryLPCP6v/ QbWZi43cDN+Ok5AEw8sdNIfdJYreIAPnziO6Mttvi9iFgozpxdmSJXco+kjJKd2U lYEG/kcfug2eJRxmsQP9v4W1Y274tFrV9VLP4mgBabFMvKlXN3uU9tqf2S9JDPv8 Y95OzKsyk6pwOTKKL8LLS/FVd3pUoHWkf5CwlH7QPR/NgiWV8DcC7bGCyBKdTAwZ 14yolNVAKdLday7+DoKCk4Eac69exmBNnED9Ky37B+STiob/5qd3iLqspFBITUQU F9bUSyw9HkdWfD4zeUD4qj9iyc6Xgzk5HbxOyJb8Wgg3VlhVESkIBeOQ/ZLT35EC ihYStdu/No/p9r5XLClHAUuo55vT5ypGWFHAeJeUtt/BMKB3ZrHcqoihge2PAjkz pnudMf5TXD4ZUYanIT8SFfmuGisoax0Qg99+xyuYZ1c8foJ7LsMSDqEicwrxi07e 0TT8Z37lrR+FRkusRA+nQ5Ba9/iOdVGq2DdiDUGlaYIaIWYVWNXEgjOPDzS5kfoA PVuJRYhR4fIeFb0aqa/p =LJnN -----END PGP SIGNATURE----- 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:
commit
4b8be38cf7
@ -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;
|
||||
|
||||
|
@ -128,8 +128,6 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(fn > 7);
|
||||
BUG_ON(blocks == 1 && blksz > 512);
|
||||
WARN_ON(blocks == 0);
|
||||
WARN_ON(blksz == 0);
|
||||
|
||||
/* sanity check */
|
||||
@ -144,22 +142,20 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||
cmd.arg |= fn << 28;
|
||||
cmd.arg |= incr_addr ? 0x04000000 : 0x00000000;
|
||||
cmd.arg |= addr << 9;
|
||||
if (blocks == 1 && blksz < 512)
|
||||
cmd.arg |= blksz; /* byte mode */
|
||||
else if (blocks == 1 && blksz == 512 &&
|
||||
!(mmc_card_broken_byte_mode_512(card)))
|
||||
cmd.arg |= 0; /* byte mode, 0==512 */
|
||||
if (blocks == 0)
|
||||
cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */
|
||||
else
|
||||
cmd.arg |= 0x08000000 | blocks; /* block mode */
|
||||
cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
|
||||
|
||||
data.blksz = blksz;
|
||||
data.blocks = blocks;
|
||||
/* Code in host drivers/fwk assumes that "blocks" always is >=1 */
|
||||
data.blocks = blocks ? blocks : 1;
|
||||
data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, buf, blksz * blocks);
|
||||
sg_init_one(&sg, buf, data.blksz * data.blocks);
|
||||
|
||||
mmc_set_data_timeout(&data, card);
|
||||
|
||||
|
@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o
|
||||
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
|
||||
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
|
||||
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
|
||||
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
|
||||
obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o
|
||||
|
@ -236,7 +236,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
|
||||
|
||||
sg = &data->sg[i];
|
||||
|
||||
sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
|
||||
amount = min(size, sg->length);
|
||||
size -= amount;
|
||||
|
||||
@ -252,7 +252,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data
|
||||
dmabuf = (unsigned *)tmpv;
|
||||
}
|
||||
|
||||
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(sgbuffer);
|
||||
|
||||
if (size == 0)
|
||||
break;
|
||||
@ -302,7 +302,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
|
||||
|
||||
sg = &data->sg[i];
|
||||
|
||||
sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset;
|
||||
amount = min(size, sg->length);
|
||||
size -= amount;
|
||||
|
||||
@ -318,7 +318,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host)
|
||||
}
|
||||
|
||||
flush_kernel_dcache_page(sg_page(sg));
|
||||
kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(sgbuffer);
|
||||
data->bytes_xfered += amount;
|
||||
if (size == 0)
|
||||
break;
|
||||
|
@ -627,17 +627,7 @@ static struct platform_driver sdh_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sdh_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdh_driver);
|
||||
}
|
||||
module_init(sdh_init);
|
||||
|
||||
static void __exit sdh_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdh_driver);
|
||||
}
|
||||
module_exit(sdh_exit);
|
||||
module_platform_driver(sdh_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver");
|
||||
MODULE_AUTHOR("Cliff Cai, Roy Huang");
|
||||
|
@ -780,18 +780,7 @@ static struct platform_driver cb710_mmc_driver = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init cb710_mmc_init_module(void)
|
||||
{
|
||||
return platform_driver_register(&cb710_mmc_driver);
|
||||
}
|
||||
|
||||
static void __exit cb710_mmc_cleanup_module(void)
|
||||
{
|
||||
platform_driver_unregister(&cb710_mmc_driver);
|
||||
}
|
||||
|
||||
module_init(cb710_mmc_init_module);
|
||||
module_exit(cb710_mmc_cleanup_module);
|
||||
module_platform_driver(cb710_mmc_driver);
|
||||
|
||||
MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
|
||||
MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part");
|
||||
|
@ -588,11 +588,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot)
|
||||
mci_writel(host, CTYPE, (slot->ctype << slot->id));
|
||||
}
|
||||
|
||||
static void dw_mci_start_request(struct dw_mci *host,
|
||||
struct dw_mci_slot *slot)
|
||||
static void __dw_mci_start_request(struct dw_mci *host,
|
||||
struct dw_mci_slot *slot,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
u32 cmdflags;
|
||||
|
||||
@ -610,14 +610,13 @@ static void dw_mci_start_request(struct dw_mci *host,
|
||||
host->completed_events = 0;
|
||||
host->data_status = 0;
|
||||
|
||||
data = mrq->data;
|
||||
data = cmd->data;
|
||||
if (data) {
|
||||
dw_mci_set_timeout(host);
|
||||
mci_writel(host, BYTCNT, data->blksz*data->blocks);
|
||||
mci_writel(host, BLKSIZ, data->blksz);
|
||||
}
|
||||
|
||||
cmd = mrq->cmd;
|
||||
cmdflags = dw_mci_prepare_command(slot->mmc, cmd);
|
||||
|
||||
/* this is the first command, send the initialization clock */
|
||||
@ -635,6 +634,16 @@ static void dw_mci_start_request(struct dw_mci *host,
|
||||
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
|
||||
}
|
||||
|
||||
static void dw_mci_start_request(struct dw_mci *host,
|
||||
struct dw_mci_slot *slot)
|
||||
{
|
||||
struct mmc_request *mrq = slot->mrq;
|
||||
struct mmc_command *cmd;
|
||||
|
||||
cmd = mrq->sbc ? mrq->sbc : mrq->cmd;
|
||||
__dw_mci_start_request(host, slot, cmd);
|
||||
}
|
||||
|
||||
/* must be called with host->lock held */
|
||||
static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
|
||||
struct mmc_request *mrq)
|
||||
@ -698,12 +707,15 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
break;
|
||||
}
|
||||
|
||||
regs = mci_readl(slot->host, UHS_REG);
|
||||
|
||||
/* DDR mode set */
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||||
regs = mci_readl(slot->host, UHS_REG);
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
regs |= (0x1 << slot->id) << 16;
|
||||
mci_writel(slot->host, UHS_REG, regs);
|
||||
}
|
||||
else
|
||||
regs &= ~(0x1 << slot->id) << 16;
|
||||
|
||||
mci_writel(slot->host, UHS_REG, regs);
|
||||
|
||||
if (ios->clock) {
|
||||
/*
|
||||
@ -889,7 +901,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
||||
cmd = host->cmd;
|
||||
host->cmd = NULL;
|
||||
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
|
||||
dw_mci_command_complete(host, host->mrq->cmd);
|
||||
dw_mci_command_complete(host, cmd);
|
||||
if (cmd == host->mrq->sbc && !cmd->error) {
|
||||
prev_state = state = STATE_SENDING_CMD;
|
||||
__dw_mci_start_request(host, host->cur_slot,
|
||||
host->mrq->cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (!host->mrq->data || cmd->error) {
|
||||
dw_mci_request_end(host, host->mrq);
|
||||
goto unlock;
|
||||
@ -967,6 +986,12 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (host->mrq->sbc && !data->error) {
|
||||
data->stop->error = 0;
|
||||
dw_mci_request_end(host, host->mrq);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
prev_state = state = STATE_SENDING_STOP;
|
||||
if (!data->error)
|
||||
send_stop_cmd(host, data);
|
||||
@ -1678,8 +1703,9 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
|
||||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
else
|
||||
mmc->caps = 0;
|
||||
|
||||
if (host->pdata->caps2)
|
||||
mmc->caps2 = host->pdata->caps2;
|
||||
|
||||
if (host->pdata->get_bus_wd)
|
||||
if (host->pdata->get_bus_wd(slot->id) >= 4)
|
||||
@ -1923,7 +1949,7 @@ static int dw_mci_probe(struct platform_device *pdev)
|
||||
* should put it in the platform data.
|
||||
*/
|
||||
fifo_size = mci_readl(host, FIFOTH);
|
||||
fifo_size = 1 + ((fifo_size >> 16) & 0x7ff);
|
||||
fifo_size = 1 + ((fifo_size >> 16) & 0xfff);
|
||||
} else {
|
||||
fifo_size = host->pdata->fifo_depth;
|
||||
}
|
||||
@ -2062,14 +2088,14 @@ static int __exit dw_mci_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* TODO: we should probably disable the clock to the card in the suspend path.
|
||||
*/
|
||||
static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
static int dw_mci_suspend(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
@ -2092,10 +2118,10 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_resume(struct platform_device *pdev)
|
||||
static int dw_mci_resume(struct device *dev)
|
||||
{
|
||||
int i, ret;
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
if (host->vmmc)
|
||||
regulator_enable(host->vmmc);
|
||||
@ -2103,7 +2129,7 @@ static int dw_mci_resume(struct platform_device *pdev)
|
||||
if (host->dma_ops->init)
|
||||
host->dma_ops->init(host);
|
||||
|
||||
if (!mci_wait_reset(&pdev->dev, host)) {
|
||||
if (!mci_wait_reset(dev, host)) {
|
||||
ret = -ENODEV;
|
||||
return ret;
|
||||
}
|
||||
@ -2131,14 +2157,15 @@ static int dw_mci_resume(struct platform_device *pdev)
|
||||
#else
|
||||
#define dw_mci_suspend NULL
|
||||
#define dw_mci_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume);
|
||||
|
||||
static struct platform_driver dw_mci_driver = {
|
||||
.remove = __exit_p(dw_mci_remove),
|
||||
.suspend = dw_mci_suspend,
|
||||
.resume = dw_mci_resume,
|
||||
.driver = {
|
||||
.name = "dw_mmc",
|
||||
.pm = &dw_mci_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -126,7 +126,7 @@
|
||||
#define SDMMC_CMD_RESP_EXP BIT(6)
|
||||
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
|
||||
/* Status register defines */
|
||||
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FF)
|
||||
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
|
||||
/* Internal DMAC interrupt defines */
|
||||
#define SDMMC_IDMAC_INT_AI BIT(9)
|
||||
#define SDMMC_IDMAC_INT_NI BIT(8)
|
||||
|
@ -1012,17 +1012,7 @@ static struct platform_driver jz4740_mmc_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init jz4740_mmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&jz4740_mmc_driver);
|
||||
}
|
||||
module_init(jz4740_mmc_init);
|
||||
|
||||
static void __exit jz4740_mmc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&jz4740_mmc_driver);
|
||||
}
|
||||
module_exit(jz4740_mmc_exit);
|
||||
module_platform_driver(jz4740_mmc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1525,7 +1525,6 @@ static struct of_device_id mmc_spi_of_match_table[] __devinitdata = {
|
||||
static struct spi_driver mmc_spi_driver = {
|
||||
.driver = {
|
||||
.name = "mmc_spi",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = mmc_spi_of_match_table,
|
||||
},
|
||||
|
@ -1245,6 +1245,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
|
||||
if (host->vcc == NULL)
|
||||
mmc->ocr_avail = plat->ocr_mask;
|
||||
mmc->caps = plat->capabilities;
|
||||
mmc->caps2 = plat->capabilities2;
|
||||
|
||||
/*
|
||||
* We can do SGIO
|
||||
|
@ -689,8 +689,8 @@ msmsdcc_pio_irq(int irq, void *dev_id)
|
||||
|
||||
/* Map the current scatter buffer */
|
||||
local_irq_save(flags);
|
||||
buffer = kmap_atomic(sg_page(host->pio.sg),
|
||||
KM_BIO_SRC_IRQ) + host->pio.sg->offset;
|
||||
buffer = kmap_atomic(sg_page(host->pio.sg))
|
||||
+ host->pio.sg->offset;
|
||||
buffer += host->pio.sg_off;
|
||||
remain = host->pio.sg->length - host->pio.sg_off;
|
||||
len = 0;
|
||||
@ -700,7 +700,7 @@ msmsdcc_pio_irq(int irq, void *dev_id)
|
||||
len = msmsdcc_pio_write(host, buffer, remain, status);
|
||||
|
||||
/* Unmap the buffer */
|
||||
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(buffer);
|
||||
local_irq_restore(flags);
|
||||
|
||||
host->pio.sg_off += len;
|
||||
@ -1480,18 +1480,7 @@ static struct platform_driver msmsdcc_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msmsdcc_init(void)
|
||||
{
|
||||
return platform_driver_register(&msmsdcc_driver);
|
||||
}
|
||||
|
||||
static void __exit msmsdcc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&msmsdcc_driver);
|
||||
}
|
||||
|
||||
module_init(msmsdcc_init);
|
||||
module_exit(msmsdcc_exit);
|
||||
module_platform_driver(msmsdcc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1047,18 +1047,7 @@ static struct platform_driver mxcmci_driver = {
|
||||
}
|
||||
};
|
||||
|
||||
static int __init mxcmci_init(void)
|
||||
{
|
||||
return platform_driver_register(&mxcmci_driver);
|
||||
}
|
||||
|
||||
static void __exit mxcmci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mxcmci_driver);
|
||||
}
|
||||
|
||||
module_init(mxcmci_init);
|
||||
module_exit(mxcmci_exit);
|
||||
module_platform_driver(mxcmci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver");
|
||||
MODULE_AUTHOR("Sascha Hauer, Pengutronix");
|
||||
|
@ -855,18 +855,7 @@ static struct platform_driver mxs_mmc_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init mxs_mmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&mxs_mmc_driver);
|
||||
}
|
||||
|
||||
static void __exit mxs_mmc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mxs_mmc_driver);
|
||||
}
|
||||
|
||||
module_init(mxs_mmc_init);
|
||||
module_exit(mxs_mmc_exit);
|
||||
module_platform_driver(mxs_mmc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral");
|
||||
MODULE_AUTHOR("Freescale Semiconductor");
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
@ -120,7 +119,6 @@
|
||||
|
||||
#define MMC_AUTOSUSPEND_DELAY 100
|
||||
#define MMC_TIMEOUT_MS 20
|
||||
#define OMAP_MMC_MASTER_CLOCK 96000000
|
||||
#define OMAP_MMC_MIN_CLOCK 400000
|
||||
#define OMAP_MMC_MAX_CLOCK 52000000
|
||||
#define DRIVER_NAME "omap_hsmmc"
|
||||
@ -163,7 +161,6 @@ struct omap_hsmmc_host {
|
||||
*/
|
||||
struct regulator *vcc;
|
||||
struct regulator *vcc_aux;
|
||||
struct work_struct mmc_carddetect_work;
|
||||
void __iomem *base;
|
||||
resource_size_t mapbase;
|
||||
spinlock_t irq_lock; /* Prevent races with irq handler */
|
||||
@ -598,12 +595,12 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host)
|
||||
}
|
||||
|
||||
/* Calculate divisor for the given clock frequency */
|
||||
static u16 calc_divisor(struct mmc_ios *ios)
|
||||
static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
|
||||
{
|
||||
u16 dsor = 0;
|
||||
|
||||
if (ios->clock) {
|
||||
dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock);
|
||||
dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock);
|
||||
if (dsor > 250)
|
||||
dsor = 250;
|
||||
}
|
||||
@ -623,7 +620,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
||||
|
||||
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
|
||||
regval = regval & ~(CLKD_MASK | DTO_MASK);
|
||||
regval = regval | (calc_divisor(ios) << 6) | (DTO << 16);
|
||||
regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16);
|
||||
OMAP_HSMMC_WRITE(host->base, SYSCTL, regval);
|
||||
OMAP_HSMMC_WRITE(host->base, SYSCTL,
|
||||
OMAP_HSMMC_READ(host->base, SYSCTL) | ICE);
|
||||
@ -1280,17 +1277,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host)
|
||||
}
|
||||
|
||||
/*
|
||||
* Work Item to notify the core about card insertion/removal
|
||||
* irq handler to notify the core about card insertion/removal
|
||||
*/
|
||||
static void omap_hsmmc_detect(struct work_struct *work)
|
||||
static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
|
||||
{
|
||||
struct omap_hsmmc_host *host =
|
||||
container_of(work, struct omap_hsmmc_host, mmc_carddetect_work);
|
||||
struct omap_hsmmc_host *host = dev_id;
|
||||
struct omap_mmc_slot_data *slot = &mmc_slot(host);
|
||||
int carddetect;
|
||||
|
||||
if (host->suspended)
|
||||
return;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
|
||||
|
||||
@ -1305,19 +1301,6 @@ static void omap_hsmmc_detect(struct work_struct *work)
|
||||
mmc_detect_change(host->mmc, (HZ * 200) / 1000);
|
||||
else
|
||||
mmc_detect_change(host->mmc, (HZ * 50) / 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* ISR for handling card insertion and removal
|
||||
*/
|
||||
static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id;
|
||||
|
||||
if (host->suspended)
|
||||
return IRQ_HANDLED;
|
||||
schedule_work(&host->mmc_carddetect_work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1919,7 +1902,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||
host->next_data.cookie = 1;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect);
|
||||
|
||||
mmc->ops = &omap_hsmmc_ops;
|
||||
|
||||
@ -2049,10 +2031,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev)
|
||||
|
||||
/* Request IRQ for card detect */
|
||||
if ((mmc_slot(host).card_detect_irq)) {
|
||||
ret = request_irq(mmc_slot(host).card_detect_irq,
|
||||
omap_hsmmc_cd_handler,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
mmc_hostname(mmc), host);
|
||||
ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
|
||||
NULL,
|
||||
omap_hsmmc_detect,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"Unable to grab MMC CD IRQ\n");
|
||||
@ -2131,7 +2114,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
||||
free_irq(host->irq, host);
|
||||
if (mmc_slot(host).card_detect_irq)
|
||||
free_irq(mmc_slot(host).card_detect_irq, host);
|
||||
flush_work_sync(&host->mmc_carddetect_work);
|
||||
|
||||
pm_runtime_put_sync(host->dev);
|
||||
pm_runtime_disable(host->dev);
|
||||
@ -2178,7 +2160,6 @@ static int omap_hsmmc_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
cancel_work_sync(&host->mmc_carddetect_work);
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
|
||||
if (ret) {
|
||||
|
@ -872,18 +872,7 @@ static struct platform_driver pxamci_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pxamci_init(void)
|
||||
{
|
||||
return platform_driver_register(&pxamci_driver);
|
||||
}
|
||||
|
||||
static void __exit pxamci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pxamci_driver);
|
||||
}
|
||||
|
||||
module_init(pxamci_init);
|
||||
module_exit(pxamci_exit);
|
||||
module_platform_driver(pxamci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1914,18 +1914,7 @@ static struct platform_driver s3cmci_driver = {
|
||||
.shutdown = s3cmci_shutdown,
|
||||
};
|
||||
|
||||
static int __init s3cmci_init(void)
|
||||
{
|
||||
return platform_driver_register(&s3cmci_driver);
|
||||
}
|
||||
|
||||
static void __exit s3cmci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3cmci_driver);
|
||||
}
|
||||
|
||||
module_init(s3cmci_init);
|
||||
module_exit(s3cmci_exit);
|
||||
module_platform_driver(s3cmci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -115,17 +115,7 @@ static struct platform_driver sdhci_cns3xxx_driver = {
|
||||
.remove = __devexit_p(sdhci_cns3xxx_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_cns3xxx_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_cns3xxx_driver);
|
||||
}
|
||||
module_init(sdhci_cns3xxx_init);
|
||||
|
||||
static void __exit sdhci_cns3xxx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_cns3xxx_driver);
|
||||
}
|
||||
module_exit(sdhci_cns3xxx_exit);
|
||||
module_platform_driver(sdhci_cns3xxx_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for CNS3xxx");
|
||||
MODULE_AUTHOR("Scott Shu, "
|
||||
|
@ -88,17 +88,7 @@ static struct platform_driver sdhci_dove_driver = {
|
||||
.remove = __devexit_p(sdhci_dove_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_dove_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_dove_driver);
|
||||
}
|
||||
module_init(sdhci_dove_init);
|
||||
|
||||
static void __exit sdhci_dove_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_dove_driver);
|
||||
}
|
||||
module_exit(sdhci_dove_exit);
|
||||
module_platform_driver(sdhci_dove_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for Dove");
|
||||
MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, "
|
||||
|
@ -606,17 +606,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = {
|
||||
.remove = __devexit_p(sdhci_esdhc_imx_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_esdhc_imx_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_esdhc_imx_driver);
|
||||
}
|
||||
module_init(sdhci_esdhc_imx_init);
|
||||
|
||||
static void __exit sdhci_esdhc_imx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_esdhc_imx_driver);
|
||||
}
|
||||
module_exit(sdhci_esdhc_imx_exit);
|
||||
module_platform_driver(sdhci_esdhc_imx_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC");
|
||||
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
|
||||
|
@ -73,7 +73,7 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
| (div << ESDHC_DIVIDER_SHIFT)
|
||||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
mdelay(100);
|
||||
mdelay(1);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
@ -131,17 +131,7 @@ static struct platform_driver sdhci_esdhc_driver = {
|
||||
.remove = __devexit_p(sdhci_esdhc_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_esdhc_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_esdhc_driver);
|
||||
}
|
||||
module_init(sdhci_esdhc_init);
|
||||
|
||||
static void __exit sdhci_esdhc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_esdhc_driver);
|
||||
}
|
||||
module_exit(sdhci_esdhc_exit);
|
||||
module_platform_driver(sdhci_esdhc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC");
|
||||
MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, "
|
||||
|
@ -93,17 +93,7 @@ static struct platform_driver sdhci_hlwd_driver = {
|
||||
.remove = __devexit_p(sdhci_hlwd_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_hlwd_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_hlwd_driver);
|
||||
}
|
||||
module_init(sdhci_hlwd_init);
|
||||
|
||||
static void __exit sdhci_hlwd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_hlwd_driver);
|
||||
}
|
||||
module_exit(sdhci_hlwd_exit);
|
||||
module_platform_driver(sdhci_hlwd_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Nintendo Wii SDHCI OF driver");
|
||||
MODULE_AUTHOR("The GameCube Linux Team, Albert Herranz");
|
||||
|
5
drivers/mmc/host/sdhci-pci-data.c
Normal file
5
drivers/mmc/host/sdhci-pci-data.c
Normal file
@ -0,0 +1,5 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mmc/sdhci-pci-data.h>
|
||||
|
||||
struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno);
|
||||
EXPORT_SYMBOL_GPL(sdhci_pci_get_data);
|
@ -23,8 +23,8 @@
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/mmc/sdhci-pci-data.h>
|
||||
|
||||
#include "sdhci.h"
|
||||
|
||||
@ -61,6 +61,7 @@ struct sdhci_pci_fixes {
|
||||
struct sdhci_pci_slot {
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pci_data *data;
|
||||
|
||||
int pci_bar;
|
||||
int rst_n_gpio;
|
||||
@ -171,32 +172,9 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Medfield eMMC hardware reset GPIOs */
|
||||
static int mfd_emmc0_rst_gpio = -EINVAL;
|
||||
static int mfd_emmc1_rst_gpio = -EINVAL;
|
||||
|
||||
static int mfd_emmc_gpio_parse(struct sfi_table_header *table)
|
||||
{
|
||||
struct sfi_table_simple *sb = (struct sfi_table_simple *)table;
|
||||
struct sfi_gpio_table_entry *entry;
|
||||
int i, num;
|
||||
|
||||
num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry);
|
||||
entry = (struct sfi_gpio_table_entry *)sb->pentry;
|
||||
|
||||
for (i = 0; i < num; i++, entry++) {
|
||||
if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN))
|
||||
mfd_emmc0_rst_gpio = entry->pin_no;
|
||||
else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN))
|
||||
mfd_emmc1_rst_gpio = entry->pin_no;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
|
||||
static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = dev_id;
|
||||
struct sdhci_host *host = slot->host;
|
||||
@ -205,15 +183,16 @@ static irqreturn_t mfd_sd_cd(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MFLD_SD_CD_PIN 69
|
||||
|
||||
static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||
static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
int err, irq, gpio = MFLD_SD_CD_PIN;
|
||||
int err, irq, gpio = slot->cd_gpio;
|
||||
|
||||
slot->cd_gpio = -EINVAL;
|
||||
slot->cd_irq = -EINVAL;
|
||||
|
||||
if (!gpio_is_valid(gpio))
|
||||
return;
|
||||
|
||||
err = gpio_request(gpio, "sd_cd");
|
||||
if (err < 0)
|
||||
goto out;
|
||||
@ -226,72 +205,53 @@ static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot)
|
||||
if (irq < 0)
|
||||
goto out_free;
|
||||
|
||||
err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING |
|
||||
err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING, "sd_cd", slot);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
slot->cd_gpio = gpio;
|
||||
slot->cd_irq = irq;
|
||||
slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION;
|
||||
|
||||
return 0;
|
||||
return;
|
||||
|
||||
out_free:
|
||||
gpio_free(gpio);
|
||||
out:
|
||||
dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead)
|
||||
static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
if (slot->cd_irq >= 0)
|
||||
free_irq(slot->cd_irq, slot);
|
||||
gpio_free(slot->cd_gpio);
|
||||
if (gpio_is_valid(slot->cd_gpio))
|
||||
gpio_free(slot->cd_gpio);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define mfd_sd_probe_slot NULL
|
||||
#define mfd_sd_remove_slot NULL
|
||||
static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
const char *name = NULL;
|
||||
int gpio = -EINVAL;
|
||||
|
||||
sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse);
|
||||
|
||||
switch (slot->chip->pdev->device) {
|
||||
case PCI_DEVICE_ID_INTEL_MFD_EMMC0:
|
||||
gpio = mfd_emmc0_rst_gpio;
|
||||
name = "eMMC0_reset";
|
||||
break;
|
||||
case PCI_DEVICE_ID_INTEL_MFD_EMMC1:
|
||||
gpio = mfd_emmc1_rst_gpio;
|
||||
name = "eMMC1_reset";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!gpio_request(gpio, name)) {
|
||||
gpio_direction_output(gpio, 1);
|
||||
slot->rst_n_gpio = gpio;
|
||||
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
}
|
||||
|
||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
|
||||
|
||||
slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
|
||||
static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
gpio_free(slot->rst_n_gpio);
|
||||
slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
|
||||
@ -307,20 +267,18 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.allow_runtime_pm = true,
|
||||
.probe_slot = mfd_sd_probe_slot,
|
||||
.remove_slot = mfd_sd_remove_slot,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.allow_runtime_pm = true,
|
||||
.probe_slot = mfd_sdio_probe_slot,
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.allow_runtime_pm = true,
|
||||
.probe_slot = mfd_emmc_probe_slot,
|
||||
.remove_slot = mfd_emmc_remove_slot,
|
||||
};
|
||||
|
||||
/* O2Micro extra registers */
|
||||
@ -1012,11 +970,8 @@ static int sdhci_pci_suspend(struct device *dev)
|
||||
|
||||
ret = sdhci_suspend_host(slot->host);
|
||||
|
||||
if (ret) {
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_pci_suspend;
|
||||
|
||||
slot_pm_flags = slot->host->mmc->pm_flags;
|
||||
if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ)
|
||||
@ -1027,11 +982,8 @@ static int sdhci_pci_suspend(struct device *dev)
|
||||
|
||||
if (chip->fixes && chip->fixes->suspend) {
|
||||
ret = chip->fixes->suspend(chip);
|
||||
if (ret) {
|
||||
for (i = chip->num_slots - 1; i >= 0; i--)
|
||||
sdhci_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_pci_suspend;
|
||||
}
|
||||
|
||||
pci_save_state(pdev);
|
||||
@ -1048,6 +1000,11 @@ static int sdhci_pci_suspend(struct device *dev)
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pci_suspend:
|
||||
while (--i >= 0)
|
||||
sdhci_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_pci_resume(struct device *dev)
|
||||
@ -1113,23 +1070,22 @@ static int sdhci_pci_runtime_suspend(struct device *dev)
|
||||
|
||||
ret = sdhci_runtime_suspend_host(slot->host);
|
||||
|
||||
if (ret) {
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_runtime_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_pci_runtime_suspend;
|
||||
}
|
||||
|
||||
if (chip->fixes && chip->fixes->suspend) {
|
||||
ret = chip->fixes->suspend(chip);
|
||||
if (ret) {
|
||||
for (i = chip->num_slots - 1; i >= 0; i--)
|
||||
sdhci_runtime_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto err_pci_runtime_suspend;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pci_runtime_suspend:
|
||||
while (--i >= 0)
|
||||
sdhci_runtime_resume_host(chip->slots[i]->host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_pci_runtime_resume(struct device *dev)
|
||||
@ -1190,11 +1146,12 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = {
|
||||
\*****************************************************************************/
|
||||
|
||||
static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
|
||||
struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar)
|
||||
struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar,
|
||||
int slotno)
|
||||
{
|
||||
struct sdhci_pci_slot *slot;
|
||||
struct sdhci_host *host;
|
||||
int ret;
|
||||
int ret, bar = first_bar + slotno;
|
||||
|
||||
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
|
||||
dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
|
||||
@ -1228,6 +1185,23 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
|
||||
slot->host = host;
|
||||
slot->pci_bar = bar;
|
||||
slot->rst_n_gpio = -EINVAL;
|
||||
slot->cd_gpio = -EINVAL;
|
||||
|
||||
/* Retrieve platform data if there is any */
|
||||
if (*sdhci_pci_get_data)
|
||||
slot->data = sdhci_pci_get_data(pdev, slotno);
|
||||
|
||||
if (slot->data) {
|
||||
if (slot->data->setup) {
|
||||
ret = slot->data->setup(slot->data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "platform setup failed\n");
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
slot->rst_n_gpio = slot->data->rst_n_gpio;
|
||||
slot->cd_gpio = slot->data->cd_gpio;
|
||||
}
|
||||
|
||||
host->hw_name = "PCI";
|
||||
host->ops = &sdhci_pci_ops;
|
||||
@ -1238,7 +1212,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
|
||||
ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "cannot request region\n");
|
||||
goto free;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
host->ioaddr = pci_ioremap_bar(pdev, bar);
|
||||
@ -1254,15 +1228,30 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot(
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(slot->rst_n_gpio)) {
|
||||
if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
|
||||
gpio_direction_output(slot->rst_n_gpio, 1);
|
||||
slot->host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
} else {
|
||||
dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
|
||||
slot->rst_n_gpio = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto remove;
|
||||
|
||||
sdhci_pci_add_own_cd(slot);
|
||||
|
||||
return slot;
|
||||
|
||||
remove:
|
||||
if (gpio_is_valid(slot->rst_n_gpio))
|
||||
gpio_free(slot->rst_n_gpio);
|
||||
|
||||
if (chip->fixes && chip->fixes->remove_slot)
|
||||
chip->fixes->remove_slot(slot, 0);
|
||||
|
||||
@ -1272,6 +1261,10 @@ unmap:
|
||||
release:
|
||||
pci_release_region(pdev, bar);
|
||||
|
||||
cleanup:
|
||||
if (slot->data && slot->data->cleanup)
|
||||
slot->data->cleanup(slot->data);
|
||||
|
||||
free:
|
||||
sdhci_free_host(host);
|
||||
|
||||
@ -1283,6 +1276,8 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
||||
int dead;
|
||||
u32 scratch;
|
||||
|
||||
sdhci_pci_remove_own_cd(slot);
|
||||
|
||||
dead = 0;
|
||||
scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS);
|
||||
if (scratch == (u32)-1)
|
||||
@ -1290,9 +1285,15 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot)
|
||||
|
||||
sdhci_remove_host(slot->host, dead);
|
||||
|
||||
if (gpio_is_valid(slot->rst_n_gpio))
|
||||
gpio_free(slot->rst_n_gpio);
|
||||
|
||||
if (slot->chip->fixes && slot->chip->fixes->remove_slot)
|
||||
slot->chip->fixes->remove_slot(slot, dead);
|
||||
|
||||
if (slot->data && slot->data->cleanup)
|
||||
slot->data->cleanup(slot->data);
|
||||
|
||||
pci_release_region(slot->chip->pdev, slot->pci_bar);
|
||||
|
||||
sdhci_free_host(slot->host);
|
||||
@ -1379,7 +1380,7 @@ 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++) {
|
||||
slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i);
|
||||
slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i);
|
||||
if (IS_ERR(slot)) {
|
||||
for (i--; i >= 0; i--)
|
||||
sdhci_pci_remove_slot(chip->slots[i]);
|
||||
|
@ -223,18 +223,8 @@ static struct platform_driver sdhci_pxav2_driver = {
|
||||
.probe = sdhci_pxav2_probe,
|
||||
.remove = __devexit_p(sdhci_pxav2_remove),
|
||||
};
|
||||
static int __init sdhci_pxav2_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_pxav2_driver);
|
||||
}
|
||||
|
||||
static void __exit sdhci_pxav2_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_pxav2_driver);
|
||||
}
|
||||
|
||||
module_init(sdhci_pxav2_init);
|
||||
module_exit(sdhci_pxav2_exit);
|
||||
module_platform_driver(sdhci_pxav2_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for pxav2");
|
||||
MODULE_AUTHOR("Marvell International Ltd.");
|
||||
|
@ -269,18 +269,8 @@ static struct platform_driver sdhci_pxav3_driver = {
|
||||
.probe = sdhci_pxav3_probe,
|
||||
.remove = __devexit_p(sdhci_pxav3_remove),
|
||||
};
|
||||
static int __init sdhci_pxav3_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_pxav3_driver);
|
||||
}
|
||||
|
||||
static void __exit sdhci_pxav3_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_pxav3_driver);
|
||||
}
|
||||
|
||||
module_init(sdhci_pxav3_init);
|
||||
module_exit(sdhci_pxav3_exit);
|
||||
module_platform_driver(sdhci_pxav3_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for pxav3");
|
||||
MODULE_AUTHOR("Marvell International Ltd.");
|
||||
|
@ -80,7 +80,7 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
|
||||
|
||||
tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
|
||||
tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
|
||||
writel(tmp, host->ioaddr + 0x80);
|
||||
writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -521,6 +521,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
|
||||
if (pdata->host_caps)
|
||||
host->mmc->caps |= pdata->host_caps;
|
||||
|
||||
if (pdata->pm_caps)
|
||||
host->mmc->pm_caps |= pdata->pm_caps;
|
||||
|
||||
host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE);
|
||||
|
||||
@ -654,18 +657,7 @@ static struct platform_driver sdhci_s3c_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init sdhci_s3c_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_s3c_driver);
|
||||
}
|
||||
|
||||
static void __exit sdhci_s3c_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_s3c_driver);
|
||||
}
|
||||
|
||||
module_init(sdhci_s3c_init);
|
||||
module_exit(sdhci_s3c_exit);
|
||||
module_platform_driver(sdhci_s3c_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue");
|
||||
MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sdhci-spear.h>
|
||||
@ -271,26 +272,54 @@ static int __devexit sdhci_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (!ret)
|
||||
clk_disable(sdhci->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct spear_sdhci *sdhci = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(sdhci->clk);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Resume: Error enabling clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops sdhci_pm_ops = {
|
||||
.suspend = sdhci_suspend,
|
||||
.resume = sdhci_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct platform_driver sdhci_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &sdhci_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = sdhci_probe,
|
||||
.remove = __devexit_p(sdhci_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_driver);
|
||||
}
|
||||
module_init(sdhci_init);
|
||||
|
||||
static void __exit sdhci_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_driver);
|
||||
}
|
||||
module_exit(sdhci_exit);
|
||||
module_platform_driver(sdhci_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver");
|
||||
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>");
|
||||
|
@ -324,17 +324,7 @@ static struct platform_driver sdhci_tegra_driver = {
|
||||
.remove = __devexit_p(sdhci_tegra_remove),
|
||||
};
|
||||
|
||||
static int __init sdhci_tegra_init(void)
|
||||
{
|
||||
return platform_driver_register(&sdhci_tegra_driver);
|
||||
}
|
||||
module_init(sdhci_tegra_init);
|
||||
|
||||
static void __exit sdhci_tegra_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sdhci_tegra_driver);
|
||||
}
|
||||
module_exit(sdhci_tegra_exit);
|
||||
module_platform_driver(sdhci_tegra_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for Tegra");
|
||||
MODULE_AUTHOR(" Google, Inc.");
|
||||
|
@ -49,7 +49,7 @@ static void sdhci_finish_data(struct sdhci_host *);
|
||||
|
||||
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
|
||||
static void sdhci_finish_command(struct sdhci_host *);
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc);
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
static void sdhci_tuning_timer(unsigned long data);
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
@ -146,10 +146,8 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
u32 present, irqs;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
return;
|
||||
|
||||
if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION)
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
||||
!mmc_card_is_removable(host->mmc))
|
||||
return;
|
||||
|
||||
present = sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
||||
@ -214,6 +212,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
|
||||
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
|
||||
host->ops->enable_dma(host);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
|
||||
@ -423,12 +426,12 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
|
||||
static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
|
||||
{
|
||||
local_irq_save(*flags);
|
||||
return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
return kmap_atomic(sg_page(sg)) + sg->offset;
|
||||
}
|
||||
|
||||
static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
|
||||
{
|
||||
kunmap_atomic(buffer, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(buffer);
|
||||
local_irq_restore(*flags);
|
||||
}
|
||||
|
||||
@ -1016,7 +1019,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
flags |= SDHCI_CMD_INDEX;
|
||||
|
||||
/* CMD19 is special in that the Data Present Select should be set */
|
||||
if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK))
|
||||
if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
|
||||
cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
|
||||
flags |= SDHCI_CMD_DATA;
|
||||
|
||||
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
||||
@ -1066,12 +1070,15 @@ static void sdhci_finish_command(struct sdhci_host *host)
|
||||
static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
int div = 0; /* Initialized for compiler warning */
|
||||
int real_div = div, clk_mul = 1;
|
||||
u16 clk = 0;
|
||||
unsigned long timeout;
|
||||
|
||||
if (clock == host->clock)
|
||||
if (clock && clock == host->clock)
|
||||
return;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
if (host->ops->set_clock) {
|
||||
host->ops->set_clock(host, clock);
|
||||
if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK)
|
||||
@ -1109,6 +1116,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
* Control register.
|
||||
*/
|
||||
clk = SDHCI_PROG_CLOCK_MODE;
|
||||
real_div = div;
|
||||
clk_mul = host->clk_mul;
|
||||
div--;
|
||||
}
|
||||
} else {
|
||||
@ -1122,6 +1131,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
break;
|
||||
}
|
||||
}
|
||||
real_div = div;
|
||||
div >>= 1;
|
||||
}
|
||||
} else {
|
||||
@ -1130,9 +1140,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
if ((host->max_clk / div) <= clock)
|
||||
break;
|
||||
}
|
||||
real_div = div;
|
||||
div >>= 1;
|
||||
}
|
||||
|
||||
if (real_div)
|
||||
host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
|
||||
|
||||
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
|
||||
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
|
||||
<< SDHCI_DIVIDER_HI_SHIFT;
|
||||
@ -1160,7 +1174,7 @@ out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
||||
static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
||||
{
|
||||
u8 pwr = 0;
|
||||
|
||||
@ -1183,13 +1197,13 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
||||
}
|
||||
|
||||
if (host->pwr == pwr)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
host->pwr = pwr;
|
||||
|
||||
if (pwr == 0) {
|
||||
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1216,6 +1230,8 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
||||
*/
|
||||
if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER)
|
||||
mdelay(10);
|
||||
|
||||
return power;
|
||||
}
|
||||
|
||||
/*****************************************************************************\
|
||||
@ -1277,7 +1293,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
if ((host->flags & SDHCI_NEEDS_RETUNING) &&
|
||||
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
sdhci_execute_tuning(mmc);
|
||||
sdhci_execute_tuning(mmc, mrq->cmd->opcode);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* Restore original mmc_request structure */
|
||||
@ -1297,12 +1313,17 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
{
|
||||
unsigned long flags;
|
||||
int vdd_bit = -1;
|
||||
u8 ctrl;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||
goto out;
|
||||
if (host->flags & SDHCI_DEVICE_DEAD) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
if (host->vmmc && ios->power_mode == MMC_POWER_OFF)
|
||||
mmc_regulator_set_ocr(host->mmc, host->vmmc, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the chip on each power off.
|
||||
@ -1316,9 +1337,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
sdhci_set_clock(host, ios->clock);
|
||||
|
||||
if (ios->power_mode == MMC_POWER_OFF)
|
||||
sdhci_set_power(host, -1);
|
||||
vdd_bit = sdhci_set_power(host, -1);
|
||||
else
|
||||
sdhci_set_power(host, ios->vdd);
|
||||
vdd_bit = sdhci_set_power(host, ios->vdd);
|
||||
|
||||
if (host->vmmc && vdd_bit != -1) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
}
|
||||
|
||||
if (host->ops->platform_send_init_74_clocks)
|
||||
host->ops->platform_send_init_74_clocks(host, ios->power_mode);
|
||||
@ -1361,11 +1388,11 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
unsigned int clock;
|
||||
|
||||
/* In case of UHS-I modes, set High Speed Enable */
|
||||
if ((ios->timing == MMC_TIMING_UHS_SDR50) ||
|
||||
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
||||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR25) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR12))
|
||||
(ios->timing == MMC_TIMING_UHS_SDR25))
|
||||
ctrl |= SDHCI_CTRL_HISPD;
|
||||
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
@ -1415,7 +1442,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
/* Select Bus Speed Mode for host */
|
||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||
if (ios->timing == MMC_TIMING_UHS_SDR12)
|
||||
if (ios->timing == MMC_TIMING_MMC_HS200)
|
||||
ctrl_2 |= SDHCI_CTRL_HS_SDR200;
|
||||
else if (ios->timing == MMC_TIMING_UHS_SDR12)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
||||
else if (ios->timing == MMC_TIMING_UHS_SDR25)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
||||
@ -1443,7 +1472,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
||||
if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS)
|
||||
sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
||||
out:
|
||||
mmiowb();
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
@ -1663,7 +1691,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
u16 ctrl;
|
||||
@ -1671,6 +1699,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||
int tuning_loop_counter = MAX_TUNING_LOOP;
|
||||
unsigned long timeout;
|
||||
int err = 0;
|
||||
bool requires_tuning_nonuhs = false;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
@ -1681,13 +1710,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
|
||||
/*
|
||||
* Host Controller needs tuning only in case of SDR104 mode
|
||||
* and for SDR50 mode when Use Tuning for SDR50 is set in
|
||||
* The Host Controller needs tuning only in case of SDR104 mode
|
||||
* and for SDR50 mode when Use Tuning for SDR50 is set in the
|
||||
* Capabilities register.
|
||||
* If the Host Controller supports the HS200 mode then the
|
||||
* tuning function has to be executed.
|
||||
*/
|
||||
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
|
||||
(host->flags & SDHCI_SDR50_NEEDS_TUNING ||
|
||||
host->flags & SDHCI_HS200_NEEDS_TUNING))
|
||||
requires_tuning_nonuhs = true;
|
||||
|
||||
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
|
||||
(((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
|
||||
(host->flags & SDHCI_SDR50_NEEDS_TUNING)))
|
||||
requires_tuning_nonuhs)
|
||||
ctrl |= SDHCI_CTRL_EXEC_TUNING;
|
||||
else {
|
||||
spin_unlock(&host->lock);
|
||||
@ -1723,7 +1758,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||
if (!tuning_loop_counter && !timeout)
|
||||
break;
|
||||
|
||||
cmd.opcode = MMC_SEND_TUNING_BLOCK;
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
cmd.retries = 0;
|
||||
@ -1738,7 +1773,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
|
||||
* block to the Host Controller. So we set the block size
|
||||
* to 64 here.
|
||||
*/
|
||||
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE);
|
||||
if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) {
|
||||
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8)
|
||||
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128),
|
||||
SDHCI_BLOCK_SIZE);
|
||||
else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4)
|
||||
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
|
||||
SDHCI_BLOCK_SIZE);
|
||||
} else {
|
||||
sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64),
|
||||
SDHCI_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/*
|
||||
* The tuning block is sent by the card to the host controller.
|
||||
@ -2121,12 +2166,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { }
|
||||
|
||||
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
u32 command;
|
||||
BUG_ON(intmask == 0);
|
||||
|
||||
/* CMD19 generates _only_ Buffer Read Ready interrupt */
|
||||
if (intmask & SDHCI_INT_DATA_AVAIL) {
|
||||
if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ==
|
||||
MMC_SEND_TUNING_BLOCK) {
|
||||
command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
|
||||
if (command == MMC_SEND_TUNING_BLOCK ||
|
||||
command == MMC_SEND_TUNING_BLOCK_HS200) {
|
||||
host->tuning_done = 1;
|
||||
wake_up(&host->buf_ready_int);
|
||||
return;
|
||||
@ -2330,26 +2377,33 @@ out:
|
||||
int sdhci_suspend_host(struct sdhci_host *host)
|
||||
{
|
||||
int ret;
|
||||
bool has_tuning_timer;
|
||||
|
||||
sdhci_disable_card_detection(host);
|
||||
|
||||
/* Disable tuning since we are suspending */
|
||||
if (host->version >= SDHCI_SPEC_300 && host->tuning_count &&
|
||||
host->tuning_mode == SDHCI_TUNING_MODE_1) {
|
||||
has_tuning_timer = host->version >= SDHCI_SPEC_300 &&
|
||||
host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1;
|
||||
if (has_tuning_timer) {
|
||||
del_timer_sync(&host->tuning_timer);
|
||||
host->flags &= ~SDHCI_NEEDS_RETUNING;
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
}
|
||||
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (has_tuning_timer) {
|
||||
host->flags |= SDHCI_NEEDS_RETUNING;
|
||||
mod_timer(&host->tuning_timer, jiffies +
|
||||
host->tuning_count * HZ);
|
||||
}
|
||||
|
||||
sdhci_enable_card_detection(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
free_irq(host->irq, host);
|
||||
|
||||
if (host->vmmc)
|
||||
ret = regulator_disable(host->vmmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2359,12 +2413,6 @@ int sdhci_resume_host(struct sdhci_host *host)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (host->vmmc) {
|
||||
int ret = regulator_enable(host->vmmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
if (host->ops->enable_dma)
|
||||
host->ops->enable_dma(host);
|
||||
@ -2727,10 +2775,14 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
if (caps[1] & SDHCI_SUPPORT_DDR50)
|
||||
mmc->caps |= MMC_CAP_UHS_DDR50;
|
||||
|
||||
/* Does the host needs tuning for SDR50? */
|
||||
/* Does the host need tuning for SDR50? */
|
||||
if (caps[1] & SDHCI_USE_SDR50_TUNING)
|
||||
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
|
||||
|
||||
/* Does the host need tuning for HS200? */
|
||||
if (mmc->caps2 & MMC_CAP2_HS200)
|
||||
host->flags |= SDHCI_HS200_NEEDS_TUNING;
|
||||
|
||||
/* Driver Type(s) (A, C, D) supported by the host */
|
||||
if (caps[1] & SDHCI_DRIVER_TYPE_A)
|
||||
mmc->caps |= MMC_CAP_DRIVER_TYPE_A;
|
||||
@ -2926,8 +2978,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
} else {
|
||||
regulator_enable(host->vmmc);
|
||||
}
|
||||
|
||||
sdhci_init(host, 0);
|
||||
@ -3016,10 +3066,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
||||
tasklet_kill(&host->card_tasklet);
|
||||
tasklet_kill(&host->finish_tasklet);
|
||||
|
||||
if (host->vmmc) {
|
||||
regulator_disable(host->vmmc);
|
||||
if (host->vmmc)
|
||||
regulator_put(host->vmmc);
|
||||
}
|
||||
|
||||
kfree(host->adma_desc);
|
||||
kfree(host->align_buffer);
|
||||
|
@ -158,6 +158,7 @@
|
||||
#define SDHCI_CTRL_UHS_SDR50 0x0002
|
||||
#define SDHCI_CTRL_UHS_SDR104 0x0003
|
||||
#define SDHCI_CTRL_UHS_DDR50 0x0004
|
||||
#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
|
||||
#define SDHCI_CTRL_VDD_180 0x0008
|
||||
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
|
||||
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -282,18 +282,7 @@ static struct platform_driver sh_mobile_sdhi_driver = {
|
||||
.remove = __devexit_p(sh_mobile_sdhi_remove),
|
||||
};
|
||||
|
||||
static int __init sh_mobile_sdhi_init(void)
|
||||
{
|
||||
return platform_driver_register(&sh_mobile_sdhi_driver);
|
||||
}
|
||||
|
||||
static void __exit sh_mobile_sdhi_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sh_mobile_sdhi_driver);
|
||||
}
|
||||
|
||||
module_init(sh_mobile_sdhi_init);
|
||||
module_exit(sh_mobile_sdhi_exit);
|
||||
module_platform_driver(sh_mobile_sdhi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SuperH Mobile SDHI driver");
|
||||
MODULE_AUTHOR("Magnus Damm");
|
||||
|
@ -118,7 +118,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
|
||||
unsigned char *buf;
|
||||
unsigned int pos = 0, val;
|
||||
|
||||
buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off;
|
||||
buf = kmap_atomic(pg) + off;
|
||||
if (host->cmd_flags & DATA_CARRY) {
|
||||
buf[pos++] = host->bounce_buf_data[0];
|
||||
host->cmd_flags &= ~DATA_CARRY;
|
||||
@ -134,7 +134,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
|
||||
}
|
||||
buf[pos++] = (val >> 8) & 0xff;
|
||||
}
|
||||
kunmap_atomic(buf - off, KM_BIO_DST_IRQ);
|
||||
kunmap_atomic(buf - off);
|
||||
}
|
||||
|
||||
static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
|
||||
@ -144,7 +144,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
|
||||
unsigned char *buf;
|
||||
unsigned int pos = 0, val;
|
||||
|
||||
buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off;
|
||||
buf = kmap_atomic(pg) + off;
|
||||
if (host->cmd_flags & DATA_CARRY) {
|
||||
val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
|
||||
writel(val, sock->addr + SOCK_MMCSD_DATA);
|
||||
@ -161,7 +161,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
|
||||
val |= (buf[pos++] << 8) & 0xff00;
|
||||
writel(val, sock->addr + SOCK_MMCSD_DATA);
|
||||
}
|
||||
kunmap_atomic(buf - off, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(buf - off);
|
||||
}
|
||||
|
||||
static void tifm_sd_transfer_data(struct tifm_sd *host)
|
||||
@ -212,13 +212,13 @@ static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
|
||||
struct page *src, unsigned int src_off,
|
||||
unsigned int count)
|
||||
{
|
||||
unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off;
|
||||
unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off;
|
||||
unsigned char *src_buf = kmap_atomic(src) + src_off;
|
||||
unsigned char *dst_buf = kmap_atomic(dst) + dst_off;
|
||||
|
||||
memcpy(dst_buf, src_buf, count);
|
||||
|
||||
kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ);
|
||||
kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(dst_buf - dst_off);
|
||||
kunmap_atomic(src_buf - src_off);
|
||||
}
|
||||
|
||||
static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
|
||||
|
@ -138,19 +138,7 @@ static struct platform_driver tmio_mmc_driver = {
|
||||
.resume = tmio_mmc_resume,
|
||||
};
|
||||
|
||||
|
||||
static int __init tmio_mmc_init(void)
|
||||
{
|
||||
return platform_driver_register(&tmio_mmc_driver);
|
||||
}
|
||||
|
||||
static void __exit tmio_mmc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&tmio_mmc_driver);
|
||||
}
|
||||
|
||||
module_init(tmio_mmc_init);
|
||||
module_exit(tmio_mmc_exit);
|
||||
module_platform_driver(tmio_mmc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
|
||||
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
|
||||
|
@ -105,13 +105,13 @@ static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
|
||||
unsigned long *flags)
|
||||
{
|
||||
local_irq_save(*flags);
|
||||
return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset;
|
||||
return kmap_atomic(sg_page(sg)) + sg->offset;
|
||||
}
|
||||
|
||||
static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg,
|
||||
unsigned long *flags, void *virt)
|
||||
{
|
||||
kunmap_atomic(virt - sg->offset, KM_BIO_SRC_IRQ);
|
||||
kunmap_atomic(virt - sg->offset);
|
||||
local_irq_restore(*flags);
|
||||
}
|
||||
|
||||
|
@ -800,8 +800,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
} else if (ios->power_mode != MMC_POWER_UP) {
|
||||
if (host->set_pwr && ios->power_mode == MMC_POWER_OFF)
|
||||
host->set_pwr(host->pdev, 0);
|
||||
if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) &&
|
||||
pdata->power) {
|
||||
if (pdata->power) {
|
||||
pdata->power = false;
|
||||
pm_runtime_put(&host->pdev->dev);
|
||||
}
|
||||
@ -915,6 +914,23 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
if (ret < 0)
|
||||
goto pm_disable;
|
||||
|
||||
/*
|
||||
* There are 4 different scenarios for the card detection:
|
||||
* 1) an external gpio irq handles the cd (best for power savings)
|
||||
* 2) internal sdhi irq handles the cd
|
||||
* 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL
|
||||
* 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE
|
||||
*
|
||||
* While we increment the rtpm counter for all scenarios when the mmc
|
||||
* core activates us by calling an appropriate set_ios(), we must
|
||||
* additionally ensure that in case 2) the tmio mmc hardware stays
|
||||
* powered on during runtime for the card detection to work.
|
||||
*/
|
||||
if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD
|
||||
|| mmc->caps & MMC_CAP_NEEDS_POLL
|
||||
|| mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
tmio_mmc_clk_stop(_host);
|
||||
tmio_mmc_reset(_host);
|
||||
|
||||
@ -933,12 +949,6 @@ 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)) {
|
||||
pdata->power = true;
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
}
|
||||
|
||||
mmc_add_host(mmc);
|
||||
|
||||
/* Unmask the IRQs we want to know about */
|
||||
@ -974,7 +984,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
||||
* 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)
|
||||
if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD
|
||||
|| host->mmc->caps & MMC_CAP_NEEDS_POLL
|
||||
|| host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
|
@ -30,6 +30,7 @@ struct dma_chan;
|
||||
* @cd_invert: true if the gpio_cd pin value is active low
|
||||
* @capabilities: the capabilities of the block as implemented in
|
||||
* this platform, signify anything MMC_CAP_* from mmc/host.h
|
||||
* @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h
|
||||
* @dma_filter: function used to select an appropriate RX and TX
|
||||
* DMA channel to be used for DMA, if and only if you're deploying the
|
||||
* generic DMA engine
|
||||
@ -52,6 +53,7 @@ struct mmci_platform_data {
|
||||
int gpio_cd;
|
||||
bool cd_invert;
|
||||
unsigned long capabilities;
|
||||
unsigned long capabilities2;
|
||||
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
|
||||
void *dma_rx_param;
|
||||
void *dma_tx_param;
|
||||
|
@ -71,6 +71,8 @@ struct mmc_ext_csd {
|
||||
bool hpi_en; /* HPI enablebit */
|
||||
bool hpi; /* HPI support bit */
|
||||
unsigned int hpi_cmd; /* cmd used as HPI */
|
||||
unsigned int boot_ro_lock; /* ro lock support */
|
||||
bool boot_ro_lockable;
|
||||
u8 raw_partition_support; /* 160 */
|
||||
u8 raw_erased_mem_count; /* 181 */
|
||||
u8 raw_ext_csd_structure; /* 194 */
|
||||
@ -110,6 +112,7 @@ struct sd_ssr {
|
||||
struct sd_switch_caps {
|
||||
unsigned int hs_max_dtr;
|
||||
unsigned int uhs_max_dtr;
|
||||
#define HIGH_SPEED_MAX_DTR 50000000
|
||||
#define UHS_SDR104_MAX_DTR 208000000
|
||||
#define UHS_SDR50_MAX_DTR 100000000
|
||||
#define UHS_DDR50_MAX_DTR 50000000
|
||||
@ -117,11 +120,13 @@ struct sd_switch_caps {
|
||||
#define UHS_SDR12_MAX_DTR 25000000
|
||||
unsigned int sd3_bus_mode;
|
||||
#define UHS_SDR12_BUS_SPEED 0
|
||||
#define HIGH_SPEED_BUS_SPEED 1
|
||||
#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_HIGH_SPEED (1 << HIGH_SPEED_BUS_SPEED)
|
||||
#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)
|
||||
@ -184,6 +189,10 @@ struct mmc_part {
|
||||
unsigned int part_cfg; /* partition type */
|
||||
char name[MAX_MMC_PART_NAME_LEN];
|
||||
bool force_ro; /* to make boot parts RO by default */
|
||||
unsigned int area_type;
|
||||
#define MMC_BLK_DATA_AREA_MAIN (1<<0)
|
||||
#define MMC_BLK_DATA_AREA_BOOT (1<<1)
|
||||
#define MMC_BLK_DATA_AREA_GP (1<<2)
|
||||
};
|
||||
|
||||
/*
|
||||
@ -206,6 +215,8 @@ struct mmc_card {
|
||||
#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 */
|
||||
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
|
||||
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
|
||||
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 */
|
||||
@ -261,12 +272,14 @@ struct mmc_card {
|
||||
* This function fill contents in mmc_part.
|
||||
*/
|
||||
static inline void mmc_part_add(struct mmc_card *card, unsigned int size,
|
||||
unsigned int part_cfg, char *name, int idx, bool ro)
|
||||
unsigned int part_cfg, char *name, int idx, bool ro,
|
||||
int area_type)
|
||||
{
|
||||
card->part[card->nr_parts].size = size;
|
||||
card->part[card->nr_parts].part_cfg = part_cfg;
|
||||
sprintf(card->part[card->nr_parts].name, name, idx);
|
||||
card->part[card->nr_parts].force_ro = ro;
|
||||
card->part[card->nr_parts].area_type = area_type;
|
||||
card->nr_parts++;
|
||||
}
|
||||
|
||||
@ -362,18 +375,24 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
||||
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
||||
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
|
||||
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
|
||||
#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_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
|
||||
#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_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
|
||||
|
||||
#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_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200)
|
||||
#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_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
|
||||
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
|
||||
|
||||
/*
|
||||
* Quirk add/remove for MMC products.
|
||||
|
19
include/linux/mmc/cd-gpio.h
Normal file
19
include/linux/mmc/cd-gpio.h
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Generic GPIO card-detect helper header
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef MMC_CD_GPIO_H
|
||||
#define MMC_CD_GPIO_H
|
||||
|
||||
struct mmc_host;
|
||||
int mmc_cd_gpio_request(struct mmc_host *host, unsigned int gpio,
|
||||
unsigned int irq, unsigned long flags);
|
||||
void mmc_cd_gpio_free(struct mmc_host *host);
|
||||
|
||||
#endif
|
@ -180,6 +180,8 @@ extern int mmc_try_claim_host(struct mmc_host *host);
|
||||
|
||||
extern int mmc_flush_cache(struct mmc_card *);
|
||||
|
||||
extern int mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
||||
/**
|
||||
* mmc_claim_host - exclusively claim a host
|
||||
* @host: mmc host to claim
|
||||
|
@ -214,6 +214,7 @@ struct dw_mci_board {
|
||||
unsigned int bus_hz; /* Bus speed */
|
||||
|
||||
unsigned int caps; /* Capabilities */
|
||||
unsigned int caps2; /* More capabilities */
|
||||
/*
|
||||
* Override fifo depth. If 0, autodetect it from the FIFOTH register,
|
||||
* but note that this may not be reliable after a bootloader has used
|
||||
|
@ -56,10 +56,13 @@ struct mmc_ios {
|
||||
#define MMC_TIMING_UHS_SDR50 3
|
||||
#define MMC_TIMING_UHS_SDR104 4
|
||||
#define MMC_TIMING_UHS_DDR50 5
|
||||
#define MMC_TIMING_MMC_HS200 6
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
#define MMC_1_8V_DDR_MODE 2
|
||||
#define MMC_1_2V_SDR_MODE 3
|
||||
#define MMC_1_8V_SDR_MODE 4
|
||||
|
||||
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
|
||||
|
||||
@ -148,7 +151,9 @@ struct mmc_host_ops {
|
||||
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);
|
||||
|
||||
/* The tuning command opcode value is different for SD and eMMC cards */
|
||||
int (*execute_tuning)(struct mmc_host *host, u32 opcode);
|
||||
void (*enable_preset_value)(struct mmc_host *host, bool enable);
|
||||
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
||||
void (*hw_reset)(struct mmc_host *host);
|
||||
@ -167,6 +172,11 @@ struct mmc_async_req {
|
||||
int (*err_check) (struct mmc_card *, struct mmc_async_req *);
|
||||
};
|
||||
|
||||
struct mmc_hotplug {
|
||||
unsigned int irq;
|
||||
void *handler_priv;
|
||||
};
|
||||
|
||||
struct mmc_host {
|
||||
struct device *parent;
|
||||
struct device class_dev;
|
||||
@ -242,6 +252,11 @@ struct mmc_host {
|
||||
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
|
||||
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
|
||||
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
|
||||
#define MMC_CAP2_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */
|
||||
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
|
||||
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */
|
||||
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
|
||||
MMC_CAP2_HS200_1_2V_SDR)
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
unsigned int power_notify_type;
|
||||
@ -253,10 +268,12 @@ struct mmc_host {
|
||||
int clk_requests; /* internal reference counter */
|
||||
unsigned int clk_delay; /* number of MCI clk hold cycles */
|
||||
bool clk_gated; /* clock gated */
|
||||
struct work_struct clk_gate_work; /* delayed clock gate */
|
||||
struct delayed_work clk_gate_work; /* delayed clock gate */
|
||||
unsigned int clk_old; /* old clock value cache */
|
||||
spinlock_t clk_lock; /* lock for clk fields */
|
||||
struct mutex clk_gate_mutex; /* mutex for clock gating */
|
||||
struct device_attribute clkgate_delay_attr;
|
||||
unsigned long clkgate_delay;
|
||||
#endif
|
||||
|
||||
/* host specific block data */
|
||||
@ -297,6 +314,8 @@ struct mmc_host {
|
||||
int claim_cnt; /* "claim" nesting count */
|
||||
|
||||
struct delayed_work detect;
|
||||
int detect_change; /* card detect flag */
|
||||
struct mmc_hotplug hotplug;
|
||||
|
||||
const struct mmc_bus_ops *bus_ops; /* current bus driver */
|
||||
unsigned int bus_refs; /* reference counter */
|
||||
@ -323,6 +342,8 @@ struct mmc_host {
|
||||
struct fault_attr fail_mmc_request;
|
||||
#endif
|
||||
|
||||
unsigned int actual_clock; /* Actual HC clock rate */
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
@ -51,6 +51,7 @@
|
||||
#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 */
|
||||
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
|
||||
|
||||
/* class 3 */
|
||||
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
|
||||
@ -280,6 +281,7 @@ struct _mmc_csd {
|
||||
#define EXT_CSD_RST_N_FUNCTION 162 /* R/W */
|
||||
#define EXT_CSD_SANITIZE_START 165 /* W */
|
||||
#define EXT_CSD_WR_REL_PARAM 166 /* RO */
|
||||
#define EXT_CSD_BOOT_WP 173 /* R/W */
|
||||
#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 */
|
||||
@ -321,6 +323,11 @@ struct _mmc_csd {
|
||||
|
||||
#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
|
||||
|
||||
#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40)
|
||||
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)
|
||||
#define EXT_CSD_BOOT_WP_B_PERM_WP_EN (0x04)
|
||||
#define EXT_CSD_BOOT_WP_B_PWR_WP_EN (0x01)
|
||||
|
||||
#define EXT_CSD_PART_CONFIG_ACC_MASK (0x7)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1)
|
||||
#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4)
|
||||
@ -333,13 +340,76 @@ struct _mmc_csd {
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
|
||||
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
|
||||
#define EXT_CSD_CARD_TYPE_MASK 0xF /* Mask out reserved bits */
|
||||
#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.8V or 3V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.2V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
|
||||
| EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
|
||||
/* SDR mode @1.2V I/O */
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_200 (EXT_CSD_CARD_TYPE_SDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_SDR_1_2V)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_ALL (EXT_CSD_CARD_TYPE_SDR_200 | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V_ALL (EXT_CSD_CARD_TYPE_SDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V_ALL (EXT_CSD_CARD_TYPE_SDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_52 | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V_DDR_52 (EXT_CSD_CARD_TYPE_SDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_DDR_52 | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V (EXT_CSD_CARD_TYPE_SDR_200 | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V (EXT_CSD_CARD_TYPE_SDR_200 | \
|
||||
EXT_CSD_CARD_TYPE_DDR_1_2V | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 (EXT_CSD_CARD_TYPE_SDR_200 | \
|
||||
EXT_CSD_CARD_TYPE_DDR_52 | \
|
||||
EXT_CSD_CARD_TYPE_52 | \
|
||||
EXT_CSD_CARD_TYPE_26)
|
||||
|
||||
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
|
||||
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
|
||||
|
18
include/linux/mmc/sdhci-pci-data.h
Normal file
18
include/linux/mmc/sdhci-pci-data.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef LINUX_MMC_SDHCI_PCI_DATA_H
|
||||
#define LINUX_MMC_SDHCI_PCI_DATA_H
|
||||
|
||||
struct pci_dev;
|
||||
|
||||
struct sdhci_pci_data {
|
||||
struct pci_dev *pdev;
|
||||
int slotno;
|
||||
int rst_n_gpio; /* Set to -EINVAL if unused */
|
||||
int cd_gpio; /* Set to -EINVAL if unused */
|
||||
int (*setup)(struct sdhci_pci_data *data);
|
||||
void (*cleanup)(struct sdhci_pci_data *data);
|
||||
};
|
||||
|
||||
extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev,
|
||||
int slotno);
|
||||
|
||||
#endif
|
@ -90,8 +90,6 @@ struct sdhci_host {
|
||||
|
||||
unsigned int quirks2; /* More deviations from spec. */
|
||||
|
||||
#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<0)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
||||
@ -121,6 +119,7 @@ struct sdhci_host {
|
||||
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
|
||||
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
|
||||
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */
|
||||
#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
* [8:0] Byte/block count
|
||||
*/
|
||||
|
||||
#define R4_18V_PRESENT (1<<24)
|
||||
#define R4_MEMORY_PRESENT (1 << 27)
|
||||
|
||||
/*
|
||||
@ -85,6 +86,7 @@
|
||||
#define SDIO_SD_REV_1_01 0 /* SD Physical Spec Version 1.01 */
|
||||
#define SDIO_SD_REV_1_10 1 /* SD Physical Spec Version 1.10 */
|
||||
#define SDIO_SD_REV_2_00 2 /* SD Physical Spec Version 2.00 */
|
||||
#define SDIO_SD_REV_3_00 3 /* SD Physical Spev Version 3.00 */
|
||||
|
||||
#define SDIO_CCCR_IOEx 0x02
|
||||
#define SDIO_CCCR_IORx 0x03
|
||||
@ -134,8 +136,31 @@
|
||||
#define SDIO_CCCR_SPEED 0x13
|
||||
|
||||
#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */
|
||||
#define SDIO_SPEED_EHS 0x02 /* Enable High-Speed mode */
|
||||
#define SDIO_SPEED_BSS_SHIFT 1
|
||||
#define SDIO_SPEED_BSS_MASK (7<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_SDR12 (0<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_SDR25 (1<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_SDR50 (2<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_SDR104 (3<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_DDR50 (4<<SDIO_SPEED_BSS_SHIFT)
|
||||
#define SDIO_SPEED_EHS SDIO_SPEED_SDR25 /* Enable High-Speed */
|
||||
|
||||
#define SDIO_CCCR_UHS 0x14
|
||||
#define SDIO_UHS_SDR50 0x01
|
||||
#define SDIO_UHS_SDR104 0x02
|
||||
#define SDIO_UHS_DDR50 0x04
|
||||
|
||||
#define SDIO_CCCR_DRIVE_STRENGTH 0x15
|
||||
#define SDIO_SDTx_MASK 0x07
|
||||
#define SDIO_DRIVE_SDTA (1<<0)
|
||||
#define SDIO_DRIVE_SDTC (1<<1)
|
||||
#define SDIO_DRIVE_SDTD (1<<2)
|
||||
#define SDIO_DRIVE_DTSx_MASK 0x03
|
||||
#define SDIO_DRIVE_DTSx_SHIFT 4
|
||||
#define SDIO_DTSx_SET_TYPE_B (0 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
/*
|
||||
* Function Basic Registers (FBR)
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user