mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball:
"MMC highlights for 3.11:
Core:
- Add support for eMMC 5.1 devices
- Add MMC_CAP_AGGRESSIVE_PM capability for aggressive power
management of eMMC/SD between requests, using runtime PM
- Add an ioctl to perform the eMMC 4.5 Sanitize command. Sample code
at:
git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc-utils.git
Drivers:
- dw_mmc: Add support for Rockchip's Cortex-A9 SoCs
- dw_mmc: Add support for Altera SoCFPGAs
- sdhci-esdhc-imx: Add support for 8-bit bus width, non-removable
cards
- sdhci-bcm-kona: New driver for Broadcom Kona (281xx) SoCs
- sdhi/tmio: Add DT DMA support"
* tag 'mmc-updates-for-3.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (87 commits)
mmc: bcm281xx SDHCI driver
mmc: sdhci: add card_event callback to sdhci
mmc: core: Fixup Oops for SDIO shutdown
mmc: sdhci-pci: add another device id
mmc: esdhc: Fix bug when writing to SDHCI_HOST_CONTROL register
mmc: esdhc: Add support for 8-bit bus width and non-removable card
mmc: core: production year for eMMC 4.41 and later
mmc: omap: remove unnecessary #if 0's
mmc: sdhci: fix ctrl_2 on super-speed selection
mmc: dw_mmc-pltfm: add Rockchip variant
mmc: dw_mmc-pltfm: move probe and remove below dt match table
mmc: dw_mmc-pltfm: remove static from dw_mci_pltfm_remove
mmc: sdhci-acpi: add support for eMMC hardware reset for HID 80860F14
mmc: sdhci-pci: add support for eMMC hardware reset for BYT eMMC.
mmc: dw_mmc: Add support DW SD/MMC driver on SOCFPGA
mmc: sdhci: fix caps2 for HS200
sdhci-pxav3: Fix runtime PM initialization
mmc: core: Add DT-bindings for MMC_CAP2_FULL_PWR_CYCLE
mmc: core: Invent MMC_CAP2_FULL_PWR_CYCLE
mmc: core: Enable power_off_notify for eMMC shutdown sequence
...
This commit is contained in:
@@ -28,6 +28,7 @@ Optional properties:
|
||||
- cap-mmc-highspeed: MMC high-speed timing is supported
|
||||
- cap-power-off-card: powering off the card is safe
|
||||
- cap-sdio-irq: enable SDIO IRQ signalling on this interface
|
||||
- full-pwr-cycle: full power cycle of the card is supported
|
||||
|
||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||
|
||||
23
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
Normal file
23
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
* Rockchip specific extensions to the Synopsis Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
The Synopsis designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsis dw mshc controller properties described
|
||||
by synopsis-dw-mshc.txt and the properties used by the Rockchip specific
|
||||
extensions to the Synopsis Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following
|
||||
|
||||
Example:
|
||||
|
||||
rkdwmmc0@12200000 {
|
||||
compatible = "rockchip,rk2928-dw-mshc";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
@@ -39,6 +39,19 @@ Required Properties:
|
||||
|
||||
Optional properties:
|
||||
|
||||
* clocks: from common clock binding: handle to biu and ciu clocks for the
|
||||
bus interface unit clock and the card interface unit clock.
|
||||
|
||||
* clock-names: from common clock binding: Shall be "biu" and "ciu".
|
||||
If the biu clock is missing we'll simply skip enabling it. If the
|
||||
ciu clock is missing we'll just assume that the clock is running at
|
||||
clock-frequency. It is an error to omit both the ciu clock and the
|
||||
clock-frequency.
|
||||
|
||||
* clock-frequency: should be the frequency (in Hz) of the ciu clock. If this
|
||||
is specified and the ciu clock is specified then we'll try to set the ciu
|
||||
clock to this at probe time.
|
||||
|
||||
* num-slots: specifies the number of slots supported by the controller.
|
||||
The number of physical slots actually used could be equal or less than the
|
||||
value specified by num-slots. If this property is not specified, the value
|
||||
@@ -55,6 +68,9 @@ Optional properties:
|
||||
|
||||
* broken-cd: as documented in mmc core bindings.
|
||||
|
||||
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
|
||||
specified we'll defer probe until we can find this regulator.
|
||||
|
||||
Aliases:
|
||||
|
||||
- All the MSHC controller nodes should be represented in the aliases node using
|
||||
@@ -67,6 +83,8 @@ board specific portions as listed below.
|
||||
|
||||
dwmmc0@12200000 {
|
||||
compatible = "snps,dw-mshc";
|
||||
clocks = <&clock 351>, <&clock 132>;
|
||||
clock-names = "biu", "ciu";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
@@ -74,11 +92,13 @@ board specific portions as listed below.
|
||||
};
|
||||
|
||||
dwmmc0@12200000 {
|
||||
clock-frequency = <400000000>;
|
||||
num-slots = <1>;
|
||||
supports-highspeed;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
vmmc-supply = <&buck8>;
|
||||
|
||||
slot@0 {
|
||||
reg = <0>;
|
||||
|
||||
@@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||
CONFIG_LCD_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_UNSAFE_RESUME=y
|
||||
CONFIG_MMC_BLOCK_MINORS=32
|
||||
CONFIG_MMC_TEST=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MMC_SDHCI_BCM_KONA=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
CONFIG_LEDS_TRIGGERS=y
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/ioctl.h>
|
||||
#include <linux/mmc/card.h>
|
||||
@@ -58,6 +59,8 @@ MODULE_ALIAS("mmc:block");
|
||||
#define INAND_CMD38_ARG_SECTRIM1 0x81
|
||||
#define INAND_CMD38_ARG_SECTRIM2 0x88
|
||||
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
#define MMC_SANITIZE_REQ_TIMEOUT 240000
|
||||
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
|
||||
|
||||
#define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \
|
||||
(req->cmd_flags & REQ_META)) && \
|
||||
@@ -222,7 +225,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
|
||||
md = mmc_blk_get(dev_to_disk(dev));
|
||||
card = md->queue.card;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
mmc_get_card(card);
|
||||
|
||||
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
|
||||
card->ext_csd.boot_ro_lock |
|
||||
@@ -233,7 +236,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
|
||||
else
|
||||
card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN;
|
||||
|
||||
mmc_release_host(card->host);
|
||||
mmc_put_card(card);
|
||||
|
||||
if (!ret) {
|
||||
pr_info("%s: Locking boot partition ro until next power on\n",
|
||||
@@ -408,6 +411,35 @@ static int ioctl_rpmb_card_status_poll(struct mmc_card *card, u32 *status,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ioctl_do_sanitize(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(mmc_can_sanitize(card) &&
|
||||
(card->host->caps2 & MMC_CAP2_SANITIZE))) {
|
||||
pr_warn("%s: %s - SANITIZE is not supported\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_debug("%s: %s - SANITIZE IN PROGRESS...\n",
|
||||
mmc_hostname(card->host), __func__);
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_SANITIZE_START, 1,
|
||||
MMC_SANITIZE_REQ_TIMEOUT);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: %s - EXT_CSD_SANITIZE_START failed. err=%d\n",
|
||||
mmc_hostname(card->host), __func__, err);
|
||||
|
||||
pr_debug("%s: %s - SANITIZE COMPLETED\n", mmc_hostname(card->host),
|
||||
__func__);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
struct mmc_ioc_cmd __user *ic_ptr)
|
||||
{
|
||||
@@ -491,7 +523,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
mmc_get_card(card);
|
||||
|
||||
err = mmc_blk_part_switch(card, md);
|
||||
if (err)
|
||||
@@ -510,6 +542,17 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
if ((MMC_EXTRACT_INDEX_FROM_ARG(cmd.arg) == EXT_CSD_SANITIZE_START) &&
|
||||
(cmd.opcode == MMC_SWITCH)) {
|
||||
err = ioctl_do_sanitize(card);
|
||||
|
||||
if (err)
|
||||
pr_err("%s: ioctl_do_sanitize() failed. err = %d",
|
||||
__func__, err);
|
||||
|
||||
goto cmd_rel_host;
|
||||
}
|
||||
|
||||
mmc_wait_for_req(card->host, &mrq);
|
||||
|
||||
if (cmd.error) {
|
||||
@@ -558,7 +601,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
|
||||
}
|
||||
|
||||
cmd_rel_host:
|
||||
mmc_release_host(card->host);
|
||||
mmc_put_card(card);
|
||||
|
||||
cmd_done:
|
||||
mmc_blk_put(md);
|
||||
@@ -939,10 +982,10 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
{
|
||||
struct mmc_blk_data *md = mq->data;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
unsigned int from, nr, arg, trim_arg, erase_arg;
|
||||
unsigned int from, nr, arg;
|
||||
int err = 0, type = MMC_BLK_SECDISCARD;
|
||||
|
||||
if (!(mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))) {
|
||||
if (!(mmc_can_secure_erase_trim(card))) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
@@ -950,23 +993,11 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
||||
from = blk_rq_pos(req);
|
||||
nr = blk_rq_sectors(req);
|
||||
|
||||
/* The sanitize operation is supported at v4.5 only */
|
||||
if (mmc_can_sanitize(card)) {
|
||||
erase_arg = MMC_ERASE_ARG;
|
||||
trim_arg = MMC_TRIM_ARG;
|
||||
} else {
|
||||
erase_arg = MMC_SECURE_ERASE_ARG;
|
||||
trim_arg = MMC_SECURE_TRIM1_ARG;
|
||||
}
|
||||
if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
|
||||
arg = MMC_SECURE_TRIM1_ARG;
|
||||
else
|
||||
arg = MMC_SECURE_ERASE_ARG;
|
||||
|
||||
if (mmc_erase_group_aligned(card, from, nr))
|
||||
arg = erase_arg;
|
||||
else if (mmc_can_trim(card))
|
||||
arg = trim_arg;
|
||||
else {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
retry:
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
@@ -1002,9 +1033,6 @@ retry:
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mmc_can_sanitize(card))
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_SANITIZE_START, 1, 0);
|
||||
out_retry:
|
||||
if (err && !mmc_blk_reset(md, card->host, type))
|
||||
goto retry;
|
||||
@@ -1895,7 +1923,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
|
||||
|
||||
if (req && !mq->mqrq_prev->req)
|
||||
/* claim host only for the first request */
|
||||
mmc_claim_host(card->host);
|
||||
mmc_get_card(card);
|
||||
|
||||
ret = mmc_blk_part_switch(card, md);
|
||||
if (ret) {
|
||||
@@ -1939,7 +1967,7 @@ out:
|
||||
* In case sepecial request, there is no reentry to
|
||||
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
|
||||
*/
|
||||
mmc_release_host(card->host);
|
||||
mmc_put_card(card);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2158,6 +2186,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
|
||||
struct mmc_card *card;
|
||||
|
||||
if (md) {
|
||||
/*
|
||||
* Flush remaining requests and free queues. It
|
||||
* is freeing the queue that stops new requests
|
||||
* from being accepted.
|
||||
*/
|
||||
mmc_cleanup_queue(&md->queue);
|
||||
if (md->flags & MMC_BLK_PACKED_CMD)
|
||||
mmc_packed_clean(&md->queue);
|
||||
card = md->queue.card;
|
||||
if (md->disk->flags & GENHD_FL_UP) {
|
||||
device_remove_file(disk_to_dev(md->disk), &md->force_ro);
|
||||
@@ -2166,14 +2202,8 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
|
||||
device_remove_file(disk_to_dev(md->disk),
|
||||
&md->power_ro_lock);
|
||||
|
||||
/* Stop new requests from getting into the queue */
|
||||
del_gendisk(md->disk);
|
||||
}
|
||||
|
||||
/* Then flush out any already in there */
|
||||
mmc_cleanup_queue(&md->queue);
|
||||
if (md->flags & MMC_BLK_PACKED_CMD)
|
||||
mmc_packed_clean(&md->queue);
|
||||
mmc_blk_put(md);
|
||||
}
|
||||
}
|
||||
@@ -2336,6 +2366,19 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
if (mmc_add_disk(part_md))
|
||||
goto out;
|
||||
}
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&card->dev, 3000);
|
||||
pm_runtime_use_autosuspend(&card->dev);
|
||||
|
||||
/*
|
||||
* Don't enable runtime PM for SD-combo cards here. Leave that
|
||||
* decision to be taken during the SDIO init sequence instead.
|
||||
*/
|
||||
if (card->type != MMC_TYPE_SD_COMBO) {
|
||||
pm_runtime_set_active(&card->dev);
|
||||
pm_runtime_enable(&card->dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@@ -2349,20 +2392,24 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
mmc_blk_remove_parts(card, md);
|
||||
pm_runtime_get_sync(&card->dev);
|
||||
mmc_claim_host(card->host);
|
||||
mmc_blk_part_switch(card, md);
|
||||
mmc_release_host(card->host);
|
||||
if (card->type != MMC_TYPE_SD_COMBO)
|
||||
pm_runtime_disable(&card->dev);
|
||||
pm_runtime_put_noidle(&card->dev);
|
||||
mmc_blk_remove_req(md);
|
||||
mmc_set_drvdata(card, NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmc_blk_suspend(struct mmc_card *card)
|
||||
static int _mmc_blk_suspend(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
pm_runtime_get_sync(&card->dev);
|
||||
mmc_queue_suspend(&md->queue);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_suspend(&part_md->queue);
|
||||
@@ -2371,6 +2418,17 @@ static int mmc_blk_suspend(struct mmc_card *card)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_blk_shutdown(struct mmc_card *card)
|
||||
{
|
||||
_mmc_blk_suspend(card);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mmc_blk_suspend(struct mmc_card *card)
|
||||
{
|
||||
return _mmc_blk_suspend(card);
|
||||
}
|
||||
|
||||
static int mmc_blk_resume(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_blk_data *part_md;
|
||||
@@ -2386,6 +2444,7 @@ static int mmc_blk_resume(struct mmc_card *card)
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_resume(&part_md->queue);
|
||||
}
|
||||
pm_runtime_put(&card->dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -2402,6 +2461,7 @@ static struct mmc_driver mmc_driver = {
|
||||
.remove = mmc_blk_remove,
|
||||
.suspend = mmc_blk_suspend,
|
||||
.resume = mmc_blk_resume,
|
||||
.shutdown = mmc_blk_shutdown,
|
||||
};
|
||||
|
||||
static int __init mmc_blk_init(void)
|
||||
|
||||
@@ -3025,12 +3025,17 @@ static void mmc_test_remove(struct mmc_card *card)
|
||||
mmc_test_free_dbgfs_file(card);
|
||||
}
|
||||
|
||||
static void mmc_test_shutdown(struct mmc_card *card)
|
||||
{
|
||||
}
|
||||
|
||||
static struct mmc_driver mmc_driver = {
|
||||
.drv = {
|
||||
.name = "mmc_test",
|
||||
},
|
||||
.probe = mmc_test_probe,
|
||||
.remove = mmc_test_remove,
|
||||
.shutdown = mmc_test_shutdown,
|
||||
};
|
||||
|
||||
static int __init mmc_test_init(void)
|
||||
|
||||
@@ -173,7 +173,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
|
||||
/* granularity must not be greater than max. discard */
|
||||
if (card->pref_erase > max_discard)
|
||||
q->limits.discard_granularity = 0;
|
||||
if (mmc_can_secure_erase_trim(card) || mmc_can_sanitize(card))
|
||||
if (mmc_can_secure_erase_trim(card))
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,15 +122,39 @@ static int mmc_bus_remove(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mmc_bus_shutdown(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret;
|
||||
|
||||
if (dev->driver && drv->shutdown)
|
||||
drv->shutdown(card);
|
||||
|
||||
if (host->bus_ops->shutdown) {
|
||||
ret = host->bus_ops->shutdown(host);
|
||||
if (ret)
|
||||
pr_warn("%s: error %d during shutdown\n",
|
||||
mmc_hostname(host), ret);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mmc_bus_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
struct mmc_host *host = card->host;
|
||||
int ret;
|
||||
|
||||
if (dev->driver && drv->suspend)
|
||||
if (dev->driver && drv->suspend) {
|
||||
ret = drv->suspend(card);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = host->bus_ops->suspend(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -138,10 +162,17 @@ static int mmc_bus_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_driver *drv = to_mmc_driver(dev->driver);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
int ret = 0;
|
||||
struct mmc_host *host = card->host;
|
||||
int ret;
|
||||
|
||||
ret = host->bus_ops->resume(host);
|
||||
if (ret)
|
||||
pr_warn("%s: error %d during resume (card was removed?)\n",
|
||||
mmc_hostname(host), ret);
|
||||
|
||||
if (dev->driver && drv->resume)
|
||||
ret = drv->resume(card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
@@ -151,15 +182,25 @@ static int mmc_bus_resume(struct device *dev)
|
||||
static int mmc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret = 0;
|
||||
|
||||
return mmc_power_save_host(card->host);
|
||||
if (host->bus_ops->runtime_suspend)
|
||||
ret = host->bus_ops->runtime_suspend(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
struct mmc_host *host = card->host;
|
||||
int ret = 0;
|
||||
|
||||
return mmc_power_restore_host(card->host);
|
||||
if (host->bus_ops->runtime_resume)
|
||||
ret = host->bus_ops->runtime_resume(host);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_runtime_idle(struct device *dev)
|
||||
@@ -182,6 +223,7 @@ static struct bus_type mmc_bus_type = {
|
||||
.uevent = mmc_bus_uevent,
|
||||
.probe = mmc_bus_probe,
|
||||
.remove = mmc_bus_remove,
|
||||
.shutdown = mmc_bus_shutdown,
|
||||
.pm = &mmc_bus_pm_ops,
|
||||
};
|
||||
|
||||
|
||||
@@ -402,6 +402,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
|
||||
context_info->is_done_rcv = false;
|
||||
context_info->is_new_req = false;
|
||||
cmd = mrq->cmd;
|
||||
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card)) {
|
||||
err = host->areq->err_check(host->card,
|
||||
@@ -436,6 +437,24 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
|
||||
wait_for_completion(&mrq->completion);
|
||||
|
||||
cmd = mrq->cmd;
|
||||
|
||||
/*
|
||||
* If host has timed out waiting for the sanitize
|
||||
* to complete, card might be still in programming state
|
||||
* so let's try to bring the card out of programming
|
||||
* state.
|
||||
*/
|
||||
if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
|
||||
if (!mmc_interrupt_hpi(host->card)) {
|
||||
pr_warning("%s: %s: Interrupted sanitize\n",
|
||||
mmc_hostname(host), __func__);
|
||||
cmd->error = 0;
|
||||
break;
|
||||
} else {
|
||||
pr_err("%s: %s: Failed to interrupt sanitize\n",
|
||||
mmc_hostname(host), __func__);
|
||||
}
|
||||
}
|
||||
if (!cmd->error || !cmd->retries ||
|
||||
mmc_card_removed(host->card))
|
||||
break;
|
||||
@@ -951,6 +970,29 @@ void mmc_release_host(struct mmc_host *host)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_release_host);
|
||||
|
||||
/*
|
||||
* This is a helper function, which fetches a runtime pm reference for the
|
||||
* card device and also claims the host.
|
||||
*/
|
||||
void mmc_get_card(struct mmc_card *card)
|
||||
{
|
||||
pm_runtime_get_sync(&card->dev);
|
||||
mmc_claim_host(card->host);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_get_card);
|
||||
|
||||
/*
|
||||
* This is a helper function, which releases the host and drops the runtime
|
||||
* pm reference for the card device.
|
||||
*/
|
||||
void mmc_put_card(struct mmc_card *card)
|
||||
{
|
||||
mmc_release_host(card->host);
|
||||
pm_runtime_mark_last_busy(&card->dev);
|
||||
pm_runtime_put_autosuspend(&card->dev);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_put_card);
|
||||
|
||||
/*
|
||||
* Internal function that does the actual ios call to the host driver,
|
||||
* optionally printing some debug output.
|
||||
@@ -1459,7 +1501,7 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
|
||||
* If a host does all the power sequencing itself, ignore the
|
||||
* initial MMC_POWER_UP stage.
|
||||
*/
|
||||
static void mmc_power_up(struct mmc_host *host)
|
||||
void mmc_power_up(struct mmc_host *host)
|
||||
{
|
||||
int bit;
|
||||
|
||||
@@ -2325,14 +2367,13 @@ int mmc_detect_card_removed(struct mmc_host *host)
|
||||
* The card will be considered unchanged unless we have been asked to
|
||||
* detect a change or host requires polling to provide card detection.
|
||||
*/
|
||||
if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) &&
|
||||
!(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
|
||||
if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
|
||||
return ret;
|
||||
|
||||
host->detect_change = 0;
|
||||
if (!ret) {
|
||||
ret = _mmc_detect_card_removed(host);
|
||||
if (ret && (host->caps2 & MMC_CAP2_DETECT_ON_ERR)) {
|
||||
if (ret && (host->caps & MMC_CAP_NEEDS_POLL)) {
|
||||
/*
|
||||
* Schedule a detect work as soon as possible to let a
|
||||
* rescan handle the card removal.
|
||||
@@ -2442,9 +2483,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);
|
||||
|
||||
host->bus_ops->remove(host);
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
@@ -2509,52 +2548,6 @@ int mmc_power_restore_host(struct mmc_host *host)
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_power_restore_host);
|
||||
|
||||
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)
|
||||
err = host->bus_ops->awake(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_card_awake);
|
||||
|
||||
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)
|
||||
err = host->bus_ops->sleep(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_card_sleep);
|
||||
|
||||
int mmc_card_can_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
|
||||
if (card && mmc_card_mmc(card) && card->ext_csd.rev >= 3)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_card_can_sleep);
|
||||
|
||||
/*
|
||||
* Flush the cache to the non-volatile storage.
|
||||
*/
|
||||
@@ -2626,48 +2619,9 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
|
||||
*/
|
||||
int mmc_suspend_host(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_flush_scheduled_work();
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
if (host->bus_ops->suspend) {
|
||||
if (mmc_card_doing_bkops(host->card)) {
|
||||
err = mmc_stop_bkops(host->card);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = host->bus_ops->suspend(host);
|
||||
}
|
||||
|
||||
if (err == -ENOSYS || !host->bus_ops->resume) {
|
||||
/*
|
||||
* We simply "remove" the card in this case.
|
||||
* 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);
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
host->pm_flags = 0;
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
mmc_bus_put(host);
|
||||
|
||||
if (!err && !mmc_card_keep_power(host))
|
||||
mmc_power_off(host);
|
||||
|
||||
out:
|
||||
return err;
|
||||
/* This function is deprecated */
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_suspend_host);
|
||||
|
||||
/**
|
||||
@@ -2676,39 +2630,8 @@ EXPORT_SYMBOL(mmc_suspend_host);
|
||||
*/
|
||||
int mmc_resume_host(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
mmc_bus_get(host);
|
||||
if (host->bus_ops && !host->bus_dead) {
|
||||
if (!mmc_card_keep_power(host)) {
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
/*
|
||||
* Tell runtime PM core we just powered up the card,
|
||||
* since it still believes the card is powered off.
|
||||
* Note that currently runtime PM is only enabled
|
||||
* for SDIO cards that are MMC_CAP_POWER_OFF_CARD
|
||||
*/
|
||||
if (mmc_card_sdio(host->card) &&
|
||||
(host->caps & MMC_CAP_POWER_OFF_CARD)) {
|
||||
pm_runtime_disable(&host->card->dev);
|
||||
pm_runtime_set_active(&host->card->dev);
|
||||
pm_runtime_enable(&host->card->dev);
|
||||
}
|
||||
}
|
||||
BUG_ON(!host->bus_ops->resume);
|
||||
err = host->bus_ops->resume(host);
|
||||
if (err) {
|
||||
pr_warning("%s: error %d during resume "
|
||||
"(card was removed?)\n",
|
||||
mmc_hostname(host), err);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
host->pm_flags &= ~MMC_PM_KEEP_POWER;
|
||||
mmc_bus_put(host);
|
||||
|
||||
return err;
|
||||
/* This function is deprecated */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_resume_host);
|
||||
|
||||
@@ -2727,29 +2650,22 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||
switch (mode) {
|
||||
case PM_HIBERNATION_PREPARE:
|
||||
case PM_SUSPEND_PREPARE:
|
||||
if (host->card && mmc_card_mmc(host->card) &&
|
||||
mmc_card_doing_bkops(host->card)) {
|
||||
err = mmc_stop_bkops(host->card);
|
||||
if (err) {
|
||||
pr_err("%s: didn't stop bkops\n",
|
||||
mmc_hostname(host));
|
||||
return err;
|
||||
}
|
||||
mmc_card_clr_doing_bkops(host->card);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->rescan_disable = 1;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
cancel_delayed_work_sync(&host->detect);
|
||||
|
||||
if (!host->bus_ops || host->bus_ops->suspend)
|
||||
if (!host->bus_ops)
|
||||
break;
|
||||
|
||||
/* Validate prerequisites for suspend */
|
||||
if (host->bus_ops->pre_suspend)
|
||||
err = host->bus_ops->pre_suspend(host);
|
||||
if (!err && host->bus_ops->suspend)
|
||||
break;
|
||||
|
||||
/* Calling bus_ops->remove() with a claimed host can deadlock */
|
||||
if (host->bus_ops->remove)
|
||||
host->bus_ops->remove(host);
|
||||
|
||||
host->bus_ops->remove(host);
|
||||
mmc_claim_host(host);
|
||||
mmc_detach_bus(host);
|
||||
mmc_power_off(host);
|
||||
|
||||
@@ -16,15 +16,17 @@
|
||||
#define MMC_CMD_RETRIES 3
|
||||
|
||||
struct mmc_bus_ops {
|
||||
int (*awake)(struct mmc_host *);
|
||||
int (*sleep)(struct mmc_host *);
|
||||
void (*remove)(struct mmc_host *);
|
||||
void (*detect)(struct mmc_host *);
|
||||
int (*pre_suspend)(struct mmc_host *);
|
||||
int (*suspend)(struct mmc_host *);
|
||||
int (*resume)(struct mmc_host *);
|
||||
int (*runtime_suspend)(struct mmc_host *);
|
||||
int (*runtime_resume)(struct mmc_host *);
|
||||
int (*power_save)(struct mmc_host *);
|
||||
int (*power_restore)(struct mmc_host *);
|
||||
int (*alive)(struct mmc_host *);
|
||||
int (*shutdown)(struct mmc_host *);
|
||||
};
|
||||
|
||||
void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
|
||||
@@ -44,6 +46,7 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||
void mmc_power_up(struct mmc_host *host);
|
||||
void mmc_power_off(struct mmc_host *host);
|
||||
void mmc_power_cycle(struct mmc_host *host);
|
||||
|
||||
|
||||
@@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
|
||||
u32 status;
|
||||
int ret;
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
mmc_get_card(card);
|
||||
|
||||
ret = mmc_send_status(data, &status);
|
||||
if (!ret)
|
||||
*val = status;
|
||||
|
||||
mmc_release_host(card->host);
|
||||
mmc_put_card(card);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
mmc_claim_host(card->host);
|
||||
mmc_get_card(card);
|
||||
err = mmc_send_ext_csd(card, ext_csd);
|
||||
mmc_release_host(card->host);
|
||||
mmc_put_card(card);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host)
|
||||
* parse the properties and set respective generic mmc-host flags and
|
||||
* parameters.
|
||||
*/
|
||||
void mmc_of_parse(struct mmc_host *host)
|
||||
int mmc_of_parse(struct mmc_host *host)
|
||||
{
|
||||
struct device_node *np;
|
||||
u32 bus_width;
|
||||
@@ -315,7 +315,7 @@ void mmc_of_parse(struct mmc_host *host)
|
||||
int len, ret, gpio;
|
||||
|
||||
if (!host->parent || !host->parent->of_node)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
np = host->parent->of_node;
|
||||
|
||||
@@ -338,6 +338,7 @@ void mmc_of_parse(struct mmc_host *host)
|
||||
default:
|
||||
dev_err(host->parent,
|
||||
"Invalid \"bus-width\" value %ud!\n", bus_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* f_max is obtained from the optional "max-frequency" property */
|
||||
@@ -367,18 +368,22 @@ void mmc_of_parse(struct mmc_host *host)
|
||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
|
||||
if (gpio == -EPROBE_DEFER)
|
||||
return gpio;
|
||||
if (gpio_is_valid(gpio)) {
|
||||
if (!(flags & OF_GPIO_ACTIVE_LOW))
|
||||
gpio_inv_cd = true;
|
||||
|
||||
ret = mmc_gpio_request_cd(host, gpio);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(host->parent,
|
||||
"Failed to request CD GPIO #%d: %d!\n",
|
||||
gpio, ret);
|
||||
else
|
||||
return ret;
|
||||
} else {
|
||||
dev_info(host->parent, "Got CD GPIO #%d.\n",
|
||||
gpio);
|
||||
}
|
||||
}
|
||||
|
||||
if (explicit_inv_cd ^ gpio_inv_cd)
|
||||
@@ -389,14 +394,23 @@ void mmc_of_parse(struct mmc_host *host)
|
||||
explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
|
||||
|
||||
gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
|
||||
if (gpio == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out;
|
||||
}
|
||||
if (gpio_is_valid(gpio)) {
|
||||
if (!(flags & OF_GPIO_ACTIVE_LOW))
|
||||
gpio_inv_wp = true;
|
||||
|
||||
ret = mmc_gpio_request_ro(host, gpio);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
dev_err(host->parent,
|
||||
"Failed to request WP GPIO: %d!\n", ret);
|
||||
goto out;
|
||||
} else {
|
||||
dev_info(host->parent, "Got WP GPIO #%d.\n",
|
||||
gpio);
|
||||
}
|
||||
}
|
||||
if (explicit_inv_wp ^ gpio_inv_wp)
|
||||
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
@@ -409,10 +423,18 @@ void mmc_of_parse(struct mmc_host *host)
|
||||
host->caps |= MMC_CAP_POWER_OFF_CARD;
|
||||
if (of_find_property(np, "cap-sdio-irq", &len))
|
||||
host->caps |= MMC_CAP_SDIO_IRQ;
|
||||
if (of_find_property(np, "full-pwr-cycle", &len))
|
||||
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
|
||||
if (of_find_property(np, "keep-power-in-suspend", &len))
|
||||
host->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
if (of_find_property(np, "enable-sdio-wakeup", &len))
|
||||
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mmc_gpio_free_cd(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_of_parse);
|
||||
|
||||
@@ -293,7 +293,7 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
}
|
||||
|
||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||
if (card->ext_csd.rev > 6) {
|
||||
if (card->ext_csd.rev > 7) {
|
||||
pr_err("%s: unrecognised EXT_CSD revision %d\n",
|
||||
mmc_hostname(card->host), card->ext_csd.rev);
|
||||
err = -EINVAL;
|
||||
@@ -461,9 +461,31 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
||||
*/
|
||||
card->ext_csd.boot_ro_lock = ext_csd[EXT_CSD_BOOT_WP];
|
||||
card->ext_csd.boot_ro_lockable = true;
|
||||
|
||||
/* Save power class values */
|
||||
card->ext_csd.raw_pwr_cl_52_195 =
|
||||
ext_csd[EXT_CSD_PWR_CL_52_195];
|
||||
card->ext_csd.raw_pwr_cl_26_195 =
|
||||
ext_csd[EXT_CSD_PWR_CL_26_195];
|
||||
card->ext_csd.raw_pwr_cl_52_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_52_360];
|
||||
card->ext_csd.raw_pwr_cl_26_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_26_360];
|
||||
card->ext_csd.raw_pwr_cl_200_195 =
|
||||
ext_csd[EXT_CSD_PWR_CL_200_195];
|
||||
card->ext_csd.raw_pwr_cl_200_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_200_360];
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_195 =
|
||||
ext_csd[EXT_CSD_PWR_CL_DDR_52_195];
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_DDR_52_360];
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 5) {
|
||||
/* Adjust production date as per JEDEC JESD84-B451 */
|
||||
if (card->cid.year < 2010)
|
||||
card->cid.year += 16;
|
||||
|
||||
/* check whether the eMMC card supports BKOPS */
|
||||
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
|
||||
card->ext_csd.bkops = 1;
|
||||
@@ -607,7 +629,23 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
||||
(card->ext_csd.raw_sectors[2] ==
|
||||
bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
|
||||
(card->ext_csd.raw_sectors[3] ==
|
||||
bw_ext_csd[EXT_CSD_SEC_CNT + 3]));
|
||||
bw_ext_csd[EXT_CSD_SEC_CNT + 3]) &&
|
||||
(card->ext_csd.raw_pwr_cl_52_195 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_52_195]) &&
|
||||
(card->ext_csd.raw_pwr_cl_26_195 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_26_195]) &&
|
||||
(card->ext_csd.raw_pwr_cl_52_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_52_360]) &&
|
||||
(card->ext_csd.raw_pwr_cl_26_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_26_360]) &&
|
||||
(card->ext_csd.raw_pwr_cl_200_195 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_200_195]) &&
|
||||
(card->ext_csd.raw_pwr_cl_200_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_200_360]) &&
|
||||
(card->ext_csd.raw_pwr_cl_ddr_52_195 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
|
||||
(card->ext_csd.raw_pwr_cl_ddr_52_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]));
|
||||
if (err)
|
||||
err = -EINVAL;
|
||||
|
||||
@@ -676,11 +714,10 @@ static struct device_type mmc_type = {
|
||||
* mmc_switch command.
|
||||
*/
|
||||
static int mmc_select_powerclass(struct mmc_card *card,
|
||||
unsigned int bus_width, u8 *ext_csd)
|
||||
unsigned int bus_width)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned int pwrclass_val;
|
||||
unsigned int index = 0;
|
||||
unsigned int pwrclass_val = 0;
|
||||
struct mmc_host *host;
|
||||
|
||||
BUG_ON(!card);
|
||||
@@ -688,9 +725,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
host = card->host;
|
||||
BUG_ON(!host);
|
||||
|
||||
if (ext_csd == NULL)
|
||||
return 0;
|
||||
|
||||
/* Power class selection is supported for versions >= 4.0 */
|
||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
||||
return 0;
|
||||
@@ -702,13 +736,13 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
switch (1 << host->ios.vdd) {
|
||||
case MMC_VDD_165_195:
|
||||
if (host->ios.clock <= 26000000)
|
||||
index = EXT_CSD_PWR_CL_26_195;
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_26_195;
|
||||
else if (host->ios.clock <= 52000000)
|
||||
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
EXT_CSD_PWR_CL_52_195 :
|
||||
EXT_CSD_PWR_CL_DDR_52_195;
|
||||
pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
card->ext_csd.raw_pwr_cl_52_195 :
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_195;
|
||||
else if (host->ios.clock <= 200000000)
|
||||
index = EXT_CSD_PWR_CL_200_195;
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_200_195;
|
||||
break;
|
||||
case MMC_VDD_27_28:
|
||||
case MMC_VDD_28_29:
|
||||
@@ -720,13 +754,13 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
case MMC_VDD_34_35:
|
||||
case MMC_VDD_35_36:
|
||||
if (host->ios.clock <= 26000000)
|
||||
index = EXT_CSD_PWR_CL_26_360;
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_26_360;
|
||||
else if (host->ios.clock <= 52000000)
|
||||
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
EXT_CSD_PWR_CL_52_360 :
|
||||
EXT_CSD_PWR_CL_DDR_52_360;
|
||||
pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
card->ext_csd.raw_pwr_cl_52_360 :
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_360;
|
||||
else if (host->ios.clock <= 200000000)
|
||||
index = EXT_CSD_PWR_CL_200_360;
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_200_360;
|
||||
break;
|
||||
default:
|
||||
pr_warning("%s: Voltage range not supported "
|
||||
@@ -734,8 +768,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pwrclass_val = ext_csd[index];
|
||||
|
||||
if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
|
||||
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
|
||||
EXT_CSD_PWR_CL_8BIT_SHIFT;
|
||||
@@ -1013,11 +1045,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
|
||||
/*
|
||||
* If the host supports the power_off_notify capability then
|
||||
* set the notification byte in the ext_csd register of device
|
||||
* Enable power_off_notification byte in the ext_csd register
|
||||
*/
|
||||
if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) &&
|
||||
(card->ext_csd.rev >= 6)) {
|
||||
if (card->ext_csd.rev >= 6) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||
EXT_CSD_POWER_ON,
|
||||
@@ -1131,7 +1161,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
|
||||
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);
|
||||
err = mmc_select_powerclass(card, ext_csd_bits);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to bus width %d"
|
||||
" failed\n", mmc_hostname(card->host),
|
||||
@@ -1164,8 +1194,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
bus_width = bus_widths[idx];
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
ddr = 0; /* no DDR for 1-bit width */
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][0],
|
||||
ext_csd);
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][0]);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d failed\n",
|
||||
@@ -1195,8 +1224,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
}
|
||||
|
||||
if (!err && ddr) {
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][1],
|
||||
ext_csd);
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][1]);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d ddr %d failed\n",
|
||||
@@ -1321,6 +1349,45 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_can_sleep(struct mmc_card *card)
|
||||
{
|
||||
return (card && card->ext_csd.rev >= 3);
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_card *card = host->card;
|
||||
int err;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
|
||||
return 0;
|
||||
|
||||
err = mmc_deselect_cards(host);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cmd.opcode = MMC_SLEEP_AWAKE;
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.arg |= 1 << 15;
|
||||
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* If the host does not wait while the card signals busy, then we will
|
||||
* will have to wait the sleep/awake timeout. Note, we cannot use the
|
||||
* SEND_STATUS command to poll the status because that command (and most
|
||||
* others) is invalid while the card sleeps.
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_can_poweroff_notify(const struct mmc_card *card)
|
||||
{
|
||||
return card &&
|
||||
@@ -1380,14 +1447,14 @@ static void mmc_detect(struct mmc_host *host)
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_get_card(host->card);
|
||||
|
||||
/*
|
||||
* Just check if our card has been removed.
|
||||
*/
|
||||
err = _mmc_detect_card_removed(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
mmc_put_card(host->card);
|
||||
|
||||
if (err) {
|
||||
mmc_remove(host);
|
||||
@@ -1399,35 +1466,59 @@ static void mmc_detect(struct mmc_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback from host.
|
||||
*/
|
||||
static int mmc_suspend(struct mmc_host *host)
|
||||
static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
|
||||
EXT_CSD_POWER_OFF_LONG;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
if (mmc_card_doing_bkops(host->card)) {
|
||||
err = mmc_stop_bkops(host->card);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = mmc_cache_ctrl(host, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (mmc_can_poweroff_notify(host->card))
|
||||
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT);
|
||||
else if (mmc_card_can_sleep(host))
|
||||
err = mmc_card_sleep(host);
|
||||
if (mmc_can_poweroff_notify(host->card) &&
|
||||
((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
|
||||
err = mmc_poweroff_notify(host->card, notify_type);
|
||||
else if (mmc_can_sleep(host->card))
|
||||
err = mmc_sleep(host);
|
||||
else if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
|
||||
if (!err)
|
||||
mmc_power_off(host);
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback from host.
|
||||
*/
|
||||
static int mmc_suspend(struct mmc_host *host)
|
||||
{
|
||||
return _mmc_suspend(host, true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Shutdown callback
|
||||
*/
|
||||
static int mmc_shutdown(struct mmc_host *host)
|
||||
{
|
||||
return _mmc_suspend(host, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resume callback from host.
|
||||
*
|
||||
@@ -1442,12 +1533,62 @@ static int mmc_resume(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
err = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback for runtime_suspend.
|
||||
*/
|
||||
static int mmc_runtime_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
err = mmc_suspend(host);
|
||||
if (err) {
|
||||
pr_err("%s: error %d doing aggessive suspend\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
mmc_power_off(host);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for runtime_resume.
|
||||
*/
|
||||
static int mmc_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
err = mmc_resume(host);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive resume\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
mmc_release_host(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
@@ -1460,56 +1601,26 @@ static int mmc_power_restore(struct mmc_host *host)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_sleep(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (card && card->ext_csd.rev >= 3) {
|
||||
err = mmc_card_sleepawake(host, 1);
|
||||
if (err < 0)
|
||||
pr_debug("%s: Error %d while putting card into sleep",
|
||||
mmc_hostname(host), err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_awake(struct mmc_host *host)
|
||||
{
|
||||
struct mmc_card *card = host->card;
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (card && card->ext_csd.rev >= 3) {
|
||||
err = mmc_card_sleepawake(host, 0);
|
||||
if (err < 0)
|
||||
pr_debug("%s: Error %d while awaking sleeping card",
|
||||
mmc_hostname(host), err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops = {
|
||||
.awake = mmc_awake,
|
||||
.sleep = mmc_sleep,
|
||||
.remove = mmc_remove,
|
||||
.detect = mmc_detect,
|
||||
.suspend = NULL,
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
.shutdown = mmc_shutdown,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_ops_unsafe = {
|
||||
.awake = mmc_awake,
|
||||
.sleep = mmc_sleep,
|
||||
.remove = mmc_remove,
|
||||
.detect = mmc_detect,
|
||||
.suspend = mmc_suspend,
|
||||
.resume = mmc_resume,
|
||||
.runtime_suspend = mmc_runtime_suspend,
|
||||
.runtime_resume = mmc_runtime_resume,
|
||||
.power_restore = mmc_power_restore,
|
||||
.alive = mmc_alive,
|
||||
.shutdown = mmc_shutdown,
|
||||
};
|
||||
|
||||
static void mmc_attach_bus_ops(struct mmc_host *host)
|
||||
|
||||
@@ -59,40 +59,6 @@ int mmc_deselect_cards(struct mmc_host *host)
|
||||
return _mmc_select_card(host, NULL);
|
||||
}
|
||||
|
||||
int mmc_card_sleepawake(struct mmc_host *host, int sleep)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_card *card = host->card;
|
||||
int err;
|
||||
|
||||
if (sleep)
|
||||
mmc_deselect_cards(host);
|
||||
|
||||
cmd.opcode = MMC_SLEEP_AWAKE;
|
||||
cmd.arg = card->rca << 16;
|
||||
if (sleep)
|
||||
cmd.arg |= 1 << 15;
|
||||
|
||||
cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* If the host does not wait while the card signals busy, then we will
|
||||
* will have to wait the sleep/awake timeout. Note, we cannot use the
|
||||
* SEND_STATUS command to poll the status because that command (and most
|
||||
* others) is invalid while the card sleeps.
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
|
||||
|
||||
if (!sleep)
|
||||
err = mmc_select_card(card);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int mmc_go_idle(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
@@ -431,6 +397,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
|
||||
|
||||
cmd.cmd_timeout_ms = timeout_ms;
|
||||
if (index == EXT_CSD_SANITIZE_START)
|
||||
cmd.sanitize_busy = true;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
|
||||
@@ -24,7 +24,6 @@ int mmc_send_status(struct mmc_card *card, u32 *status);
|
||||
int mmc_send_cid(struct mmc_host *host, u32 *cid);
|
||||
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
|
||||
int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
|
||||
int mmc_card_sleepawake(struct mmc_host *host, int sleep);
|
||||
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
|
||||
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
|
||||
|
||||
|
||||
@@ -646,8 +646,13 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* SPI mode doesn't define CMD19 */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning) {
|
||||
/*
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
|
||||
(card->sd_bus_speed == UHS_SDR50_BUS_SPEED ||
|
||||
card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
@@ -1037,14 +1042,14 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_get_card(host->card);
|
||||
|
||||
/*
|
||||
* Just check if our card has been removed.
|
||||
*/
|
||||
err = _mmc_detect_card_removed(host);
|
||||
|
||||
mmc_release_host(host);
|
||||
mmc_put_card(host->card);
|
||||
|
||||
if (err) {
|
||||
mmc_sd_remove(host);
|
||||
@@ -1070,6 +1075,8 @@ static int mmc_sd_suspend(struct mmc_host *host)
|
||||
if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
if (!err)
|
||||
mmc_power_off(host);
|
||||
mmc_release_host(host);
|
||||
|
||||
return err;
|
||||
@@ -1089,12 +1096,61 @@ static int mmc_sd_resume(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
err = mmc_sd_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for runtime_suspend.
|
||||
*/
|
||||
static int mmc_sd_runtime_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
err = mmc_sd_suspend(host);
|
||||
if (err) {
|
||||
pr_err("%s: error %d doing aggessive suspend\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
mmc_power_off(host);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for runtime_resume.
|
||||
*/
|
||||
static int mmc_sd_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
err = mmc_sd_resume(host);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive resume\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
mmc_release_host(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
@@ -1114,15 +1170,19 @@ static const struct mmc_bus_ops mmc_sd_ops = {
|
||||
.resume = NULL,
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
};
|
||||
|
||||
static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
|
||||
.remove = mmc_sd_remove,
|
||||
.detect = mmc_sd_detect,
|
||||
.runtime_suspend = mmc_sd_runtime_suspend,
|
||||
.runtime_resume = mmc_sd_runtime_resume,
|
||||
.suspend = mmc_sd_suspend,
|
||||
.resume = mmc_sd_resume,
|
||||
.power_restore = mmc_sd_power_restore,
|
||||
.alive = mmc_sd_alive,
|
||||
.shutdown = mmc_sd_suspend,
|
||||
};
|
||||
|
||||
static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
||||
|
||||
@@ -563,10 +563,18 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Initialize and start re-tuning timer */
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning)
|
||||
/*
|
||||
* SPI mode doesn't define CMD19 and tuning is only valid for SDR50 and
|
||||
* SDR104 mode SD-cards. Note that tuning is mandatory for SDR104.
|
||||
*/
|
||||
if (!mmc_host_is_spi(card->host) && card->host->ops->execute_tuning &&
|
||||
((card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR50) ||
|
||||
(card->sw_caps.sd3_bus_mode & SD_MODE_UHS_SDR104))) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@@ -902,11 +910,11 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* SDIO suspend. We need to suspend all functions separately.
|
||||
* SDIO pre_suspend. We need to suspend all functions separately.
|
||||
* Therefore all registered functions must have drivers with suspend
|
||||
* and resume methods. Failing that we simply remove the whole card.
|
||||
*/
|
||||
static int mmc_sdio_suspend(struct mmc_host *host)
|
||||
static int mmc_sdio_pre_suspend(struct mmc_host *host)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
@@ -917,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host)
|
||||
if (!pmops || !pmops->suspend || !pmops->resume) {
|
||||
/* force removal of entire card in that case */
|
||||
err = -ENOSYS;
|
||||
} else
|
||||
err = pmops->suspend(&func->dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* SDIO suspend. Suspend all functions separately.
|
||||
*/
|
||||
static int mmc_sdio_suspend(struct mmc_host *host)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < host->card->sdio_funcs; i++) {
|
||||
struct sdio_func *func = host->card->sdio_func[i];
|
||||
if (func && sdio_func_present(func) && func->dev.driver) {
|
||||
const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
||||
err = pmops->suspend(&func->dev);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
@@ -937,6 +963,9 @@ static int mmc_sdio_suspend(struct mmc_host *host)
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
if (!err && !mmc_card_keep_power(host))
|
||||
mmc_power_off(host);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -950,6 +979,23 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
/* Basic card reinitialization. */
|
||||
mmc_claim_host(host);
|
||||
|
||||
/* Restore power if needed */
|
||||
if (!mmc_card_keep_power(host)) {
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
/*
|
||||
* Tell runtime PM core we just powered up the card,
|
||||
* since it still believes the card is powered off.
|
||||
* Note that currently runtime PM is only enabled
|
||||
* for SDIO cards that are MMC_CAP_POWER_OFF_CARD
|
||||
*/
|
||||
if (host->caps & MMC_CAP_POWER_OFF_CARD) {
|
||||
pm_runtime_disable(&host->card->dev);
|
||||
pm_runtime_set_active(&host->card->dev);
|
||||
pm_runtime_enable(&host->card->dev);
|
||||
}
|
||||
}
|
||||
|
||||
/* No need to reinitialize powered-resumed nonremovable cards */
|
||||
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
||||
sdio_reset(host);
|
||||
@@ -987,6 +1033,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
host->pm_flags &= ~MMC_PM_KEEP_POWER;
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1051,11 +1098,28 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mmc_sdio_runtime_suspend(struct mmc_host *host)
|
||||
{
|
||||
/* No references to the card, cut the power to it. */
|
||||
mmc_power_off(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_sdio_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
/* Restore power and re-initialize. */
|
||||
mmc_power_up(host);
|
||||
return mmc_sdio_power_restore(host);
|
||||
}
|
||||
|
||||
static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||
.remove = mmc_sdio_remove,
|
||||
.detect = mmc_sdio_detect,
|
||||
.pre_suspend = mmc_sdio_pre_suspend,
|
||||
.suspend = mmc_sdio_suspend,
|
||||
.resume = mmc_sdio_resume,
|
||||
.runtime_suspend = mmc_sdio_runtime_suspend,
|
||||
.runtime_resume = mmc_sdio_runtime_resume,
|
||||
.power_restore = mmc_sdio_power_restore,
|
||||
.alive = mmc_sdio_alive,
|
||||
};
|
||||
|
||||
@@ -249,6 +249,17 @@ config MMC_SDHCI_S3C_DMA
|
||||
|
||||
YMMV.
|
||||
|
||||
config MMC_SDHCI_BCM_KONA
|
||||
tristate "SDHCI support on Broadcom KONA platform"
|
||||
depends on ARCH_BCM
|
||||
select MMC_SDHCI_PLTFM
|
||||
help
|
||||
This selects the Broadcom Kona Secure Digital Host Controller
|
||||
Interface(SDHCI) support.
|
||||
This is used in Broadcom mobile SoCs.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
config MMC_SDHCI_BCM2835
|
||||
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
|
||||
depends on ARCH_BCM2835
|
||||
@@ -556,6 +567,14 @@ config MMC_DW_EXYNOS
|
||||
Synopsys DesignWare Memory Card Interface driver. Select this option
|
||||
for platforms based on Exynos4 and Exynos5 SoC's.
|
||||
|
||||
config MMC_DW_SOCFPGA
|
||||
tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
|
||||
depends on MMC_DW
|
||||
select MMC_DW_PLTFM
|
||||
help
|
||||
This selects support for Altera SoCFPGA specific extensions to the
|
||||
Synopsys DesignWare Memory Card Interface driver.
|
||||
|
||||
config MMC_DW_PCI
|
||||
tristate "Synopsys Designware MCI support on PCI bus"
|
||||
depends on MMC_DW && PCI
|
||||
|
||||
@@ -42,6 +42,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
|
||||
obj-$(CONFIG_MMC_DW) += dw_mmc.o
|
||||
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
|
||||
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
|
||||
obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
|
||||
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
|
||||
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
|
||||
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
||||
@@ -60,6 +61,7 @@ obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
|
||||
|
||||
ifeq ($(CONFIG_CB710_DEBUG),y)
|
||||
|
||||
@@ -546,8 +546,6 @@ static int goldfish_mmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
BUG_ON(host == NULL);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user