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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.13 (GNU/Linux) iQIcBAABAgAGBQJR2v+uAAoJEHNBYZ7TNxYMIT8P/0W2g0efrm1+c679L6x/ToaJ rsZF4J+gv/yMPPNNxV03BL8NSuU7EyniMBeRAutykCTfhr/NoToYnPRR4trAsN9G J/GAhXbMFQj8YTvOUhMVjzH4B9z2FNfZ2L4mx/9kBLcBphWvafKXmfoD4WqI8b17 WS+vGbEdYmiQKCjlZnWvkiNiGWZmhmp49iAL0QiZsFVizGQaWeTZdzt95JV62cUw WDA/TGh1L1Zezf68i5iwJsW3mWuZS7iemeL4jFN8owyBU5wXCNae/4TjbIR0zpYr HVGgkQLX+FLFg+EJe1s/A1feD3ZwPWymoImuL44d1VsQYO6cTEKHd78qSNPuDFBI oATvJR/TgrdvHO17iQgVwa104Q+zz9r3/3eC1qZhUIV+oq8Xv6aBckGlhNIc42ef YuFTxOnY3VF6DmOwXFgzyrHnQemLn+AzHqM5zVBjF7oRDyD5MnNaCb/AQ7xinlfd V/VY8N8pJLSV2IC5OLjVyqovBHb4nwiGIwrawWJA83uw7QyLbAMafXyuNAVEsaiX olVgNH8wuEye5f5Y6ZKXEGzKrKvIH+3pFXL3Q5AA8Kv8+851oMFgjdEg+WDOKc6j 7DoQPIrmwRNi789dyzbwrKfCOPIW6ClALc4H3QIvu/GDEbeexfjDcfYj8VPM8Wuk hOHYcxEDD9k3BSqhnaI+ =c/CM -----END PGP SIGNATURE----- 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:
commit
bfffbea1aa
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
#include <asm/io.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <mach/cpu.h>
|
||||
|
||||
#include "atmel-mci-regs.h"
|
||||
|
||||
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
||||
|
@ -2475,8 +2473,6 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
|||
struct atmel_mci *host = platform_get_drvdata(pdev);
|
||||
unsigned int i;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (host->buffer)
|
||||
dma_free_coherent(&pdev->dev, host->buf_size,
|
||||
host->buffer, host->buf_phys_addr);
|
||||
|
@ -2504,7 +2500,7 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int atmci_suspend(struct device *dev)
|
||||
{
|
||||
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||
|
@ -2559,17 +2555,15 @@ static int atmci_resume(struct device *dev)
|
|||
|
||||
return ret;
|
||||
}
|
||||
static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
|
||||
#define ATMCI_PM_OPS (&atmci_pm)
|
||||
#else
|
||||
#define ATMCI_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
|
||||
|
||||
static struct platform_driver atmci_driver = {
|
||||
.remove = __exit_p(atmci_remove),
|
||||
.driver = {
|
||||
.name = "atmel_mci",
|
||||
.pm = ATMCI_PM_OPS,
|
||||
.pm = &atmci_pm,
|
||||
.of_match_table = of_match_ptr(atmci_dt_ids),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1149,7 +1149,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
|
|||
kfree(host->ioarea);
|
||||
|
||||
mmc_free_host(host->mmc);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -621,8 +621,6 @@ static int sdh_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
struct sdh_host *host = mmc_priv(mmc);
|
||||
|
||||
|
|
|
@ -703,7 +703,7 @@ static int cb710_mmc_init(struct platform_device *pdev)
|
|||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, mmc);
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
/* harmless (maybe) magic */
|
||||
pci_read_config_dword(chip->pdev, 0x48, &val);
|
||||
|
|
|
@ -24,7 +24,7 @@ struct cb710_mmc_reader {
|
|||
|
||||
static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot)
|
||||
{
|
||||
return dev_get_drvdata(&slot->pdev.dev);
|
||||
return platform_get_drvdata(&slot->pdev);
|
||||
}
|
||||
|
||||
static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc)
|
||||
|
|
|
@ -1407,7 +1407,6 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
if (host) {
|
||||
mmc_davinci_cpufreq_deregister(host);
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
|
||||
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
|
||||
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "dw_mmc.h"
|
||||
|
||||
#define PCI_BAR_NO 2
|
||||
#define COMPLETE_BAR 0
|
||||
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
|
||||
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
|
||||
/* Defining the Capabilities */
|
||||
|
@ -38,51 +37,37 @@ static struct dw_mci_board pci_board_data = {
|
|||
};
|
||||
|
||||
static int dw_mci_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *entries)
|
||||
const struct pci_device_id *entries)
|
||||
{
|
||||
struct dw_mci *host;
|
||||
int ret;
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (pci_request_regions(pdev, "dw_mmc_pci")) {
|
||||
ret = -ENODEV;
|
||||
goto err_disable_dev;
|
||||
}
|
||||
|
||||
host = kzalloc(sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host) {
|
||||
ret = -ENOMEM;
|
||||
goto err_release;
|
||||
}
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
host->irq = pdev->irq;
|
||||
host->irq_flags = IRQF_SHARED;
|
||||
host->dev = &pdev->dev;
|
||||
host->pdata = &pci_board_data;
|
||||
|
||||
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR);
|
||||
if (!host->regs) {
|
||||
ret = -EIO;
|
||||
goto err_unmap;
|
||||
}
|
||||
ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->regs = pcim_iomap_table(pdev)[0];
|
||||
|
||||
pci_set_drvdata(pdev, host);
|
||||
ret = dw_mci_probe(host);
|
||||
if (ret)
|
||||
goto err_probe_failed;
|
||||
return ret;
|
||||
return ret;
|
||||
|
||||
err_probe_failed:
|
||||
pci_iounmap(pdev, host->regs);
|
||||
err_unmap:
|
||||
kfree(host);
|
||||
err_release:
|
||||
pci_release_regions(pdev);
|
||||
err_disable_dev:
|
||||
pci_disable_device(pdev);
|
||||
return ret;
|
||||
pci_set_drvdata(pdev, host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_mci_pci_remove(struct pci_dev *pdev)
|
||||
|
@ -90,32 +75,23 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
|
|||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
dw_mci_remove(host);
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
pci_release_regions(pdev);
|
||||
pci_iounmap(pdev, host->regs);
|
||||
kfree(host);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_mci_pci_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
ret = dw_mci_suspend(host);
|
||||
return ret;
|
||||
return dw_mci_suspend(host);
|
||||
}
|
||||
|
||||
static int dw_mci_pci_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
struct dw_mci *host = pci_get_drvdata(pdev);
|
||||
|
||||
ret = dw_mci_resume(host);
|
||||
return ret;
|
||||
return dw_mci_resume(host);
|
||||
}
|
||||
#else
|
||||
#define dw_mci_pci_suspend NULL
|
||||
|
|
|
@ -24,8 +24,17 @@
|
|||
|
||||
#include "dw_mmc.h"
|
||||
|
||||
static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data rockchip_drv_data = {
|
||||
.prepare_command = dw_mci_rockchip_prepare_command,
|
||||
};
|
||||
|
||||
int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
const struct dw_mci_drv_data *drv_data)
|
||||
const struct dw_mci_drv_data *drv_data)
|
||||
{
|
||||
struct dw_mci *host;
|
||||
struct resource *regs;
|
||||
|
@ -35,10 +44,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
|||
if (!host)
|
||||
return -ENOMEM;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
return -ENXIO;
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0)
|
||||
return host->irq;
|
||||
|
@ -47,6 +52,8 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
|||
host->dev = &pdev->dev;
|
||||
host->irq_flags = 0;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
if (IS_ERR(host->regs))
|
||||
return PTR_ERR(host->regs);
|
||||
|
@ -58,52 +65,26 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
|||
}
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
ret = dw_mci_probe(host);
|
||||
return ret;
|
||||
return dw_mci_probe(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
|
||||
|
||||
static int dw_mci_pltfm_probe(struct platform_device *pdev)
|
||||
{
|
||||
return dw_mci_pltfm_register(pdev, NULL);
|
||||
}
|
||||
|
||||
static int dw_mci_pltfm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
dw_mci_remove(host);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* TODO: we should probably disable the clock to the card in the suspend path.
|
||||
*/
|
||||
static int dw_mci_pltfm_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_mci_suspend(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return dw_mci_suspend(host);
|
||||
}
|
||||
|
||||
static int dw_mci_pltfm_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
ret = dw_mci_resume(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
return dw_mci_resume(host);
|
||||
}
|
||||
#else
|
||||
#define dw_mci_pltfm_suspend NULL
|
||||
|
@ -115,10 +96,34 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
|
|||
|
||||
static const struct of_device_id dw_mci_pltfm_match[] = {
|
||||
{ .compatible = "snps,dw-mshc", },
|
||||
{ .compatible = "rockchip,rk2928-dw-mshc",
|
||||
.data = &rockchip_drv_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
|
||||
|
||||
static int dw_mci_pltfm_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data = NULL;
|
||||
const struct of_device_id *match;
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
match = of_match_node(dw_mci_pltfm_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
}
|
||||
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
int dw_mci_pltfm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_mci *host = platform_get_drvdata(pdev);
|
||||
|
||||
dw_mci_remove(host);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dw_mci_pltfm_remove);
|
||||
|
||||
static struct platform_driver dw_mci_pltfm_driver = {
|
||||
.probe = dw_mci_pltfm_probe,
|
||||
.remove = dw_mci_pltfm_remove,
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
|
||||
* driver
|
||||
*
|
||||
* Copyright (C) 2012, Samsung Electronics Co., Ltd.
|
||||
* Copyright (C) 2013 Altera Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Taken from dw_mmc-exynos.c
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
|
||||
#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
|
||||
#define DRV_CLK_PHASE_SHIFT_SEL_MASK 0x7
|
||||
#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
|
||||
((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
|
||||
|
||||
/* SOCFPGA implementation specific driver private data */
|
||||
struct dw_mci_socfpga_priv_data {
|
||||
u8 ciu_div; /* card interface unit divisor */
|
||||
u32 hs_timing; /* bitmask for CIU clock phase shift */
|
||||
struct regmap *sysreg; /* regmap for system manager register */
|
||||
};
|
||||
|
||||
static int dw_mci_socfpga_priv_init(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
|
||||
if (IS_ERR(priv->sysreg)) {
|
||||
dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
|
||||
return PTR_ERR(priv->sysreg);
|
||||
}
|
||||
host->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
|
||||
clk_disable_unprepare(host->ciu_clk);
|
||||
regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
|
||||
priv->hs_timing);
|
||||
clk_prepare_enable(host->ciu_clk);
|
||||
|
||||
host->bus_hz /= (priv->ciu_div + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
|
||||
if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
|
||||
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
|
||||
}
|
||||
|
||||
static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
struct device_node *np = host->dev->of_node;
|
||||
u32 timing[2];
|
||||
u32 div = 0;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
|
||||
if (ret)
|
||||
dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
|
||||
priv->ciu_div = div;
|
||||
|
||||
ret = of_property_read_u32_array(np,
|
||||
"altr,dw-mshc-sdr-timing", timing, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dw_mci_drv_data socfpga_drv_data = {
|
||||
.init = dw_mci_socfpga_priv_init,
|
||||
.setup_clock = dw_mci_socfpga_setup_clock,
|
||||
.prepare_command = dw_mci_socfpga_prepare_command,
|
||||
.parse_dt = dw_mci_socfpga_parse_dt,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_socfpga_match[] = {
|
||||
{ .compatible = "altr,socfpga-dw-mshc",
|
||||
.data = &socfpga_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
|
||||
|
||||
int dw_mci_socfpga_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
|
||||
drv_data = match->data;
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
static struct platform_driver dw_mci_socfpga_pltfm_driver = {
|
||||
.probe = dw_mci_socfpga_probe,
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dwmmc_socfpga",
|
||||
.of_match_table = of_match_ptr(dw_mci_socfpga_match),
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_mci_socfpga_pltfm_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:dwmmc-socfpga");
|
|
@ -39,7 +39,7 @@
|
|||
#include "dw_mmc.h"
|
||||
|
||||
/* Common flag combinations */
|
||||
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DTO | SDMMC_INT_DCRC | \
|
||||
#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \
|
||||
SDMMC_INT_HTO | SDMMC_INT_SBE | \
|
||||
SDMMC_INT_EBE)
|
||||
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
|
||||
|
@ -51,6 +51,11 @@
|
|||
#define DW_MCI_DMA_THRESHOLD 16
|
||||
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
|
||||
SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
|
||||
SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
|
||||
SDMMC_IDMAC_INT_TI)
|
||||
|
||||
struct idmac_desc {
|
||||
u32 des0; /* Control Descriptor */
|
||||
#define IDMAC_DES0_DIC BIT(1)
|
||||
|
@ -433,6 +438,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
|
|||
mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
|
||||
|
||||
/* Mask out interrupts - get Tx & Rx complete only */
|
||||
mci_writel(host, IDSTS, IDMAC_INT_CLR);
|
||||
mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
|
||||
SDMMC_IDMAC_INT_TI);
|
||||
|
||||
|
@ -1087,7 +1093,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
status = host->data_status;
|
||||
|
||||
if (status & DW_MCI_DATA_ERROR_FLAGS) {
|
||||
if (status & SDMMC_INT_DTO) {
|
||||
if (status & SDMMC_INT_DRTO) {
|
||||
data->error = -ETIMEDOUT;
|
||||
} else if (status & SDMMC_INT_DCRC) {
|
||||
data->error = -EILSEQ;
|
||||
|
@ -1985,19 +1991,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
}
|
||||
|
||||
host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc");
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc));
|
||||
host->vmmc = NULL;
|
||||
} else {
|
||||
ret = regulator_enable(host->vmmc);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"failed to enable regulator: %d\n", ret);
|
||||
goto err_setup_bus;
|
||||
}
|
||||
}
|
||||
|
||||
if (dw_mci_get_cd(mmc))
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
else
|
||||
|
@ -2124,6 +2117,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
struct device_node *np = dev->of_node;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int idx, ret;
|
||||
u32 clock_frequency;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
|
@ -2150,6 +2144,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
|
||||
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
|
||||
|
||||
if (!of_property_read_u32(np, "clock-frequency", &clock_frequency))
|
||||
pdata->bus_hz = clock_frequency;
|
||||
|
||||
if (drv_data && drv_data->parse_dt) {
|
||||
ret = drv_data->parse_dt(host);
|
||||
if (ret)
|
||||
|
@ -2207,18 +2204,23 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
host->ciu_clk = devm_clk_get(host->dev, "ciu");
|
||||
if (IS_ERR(host->ciu_clk)) {
|
||||
dev_dbg(host->dev, "ciu clock not available\n");
|
||||
host->bus_hz = host->pdata->bus_hz;
|
||||
} else {
|
||||
ret = clk_prepare_enable(host->ciu_clk);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "failed to enable ciu clock\n");
|
||||
goto err_clk_biu;
|
||||
}
|
||||
}
|
||||
|
||||
if (IS_ERR(host->ciu_clk))
|
||||
host->bus_hz = host->pdata->bus_hz;
|
||||
else
|
||||
if (host->pdata->bus_hz) {
|
||||
ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
|
||||
if (ret)
|
||||
dev_warn(host->dev,
|
||||
"Unable to set bus rate to %ul\n",
|
||||
host->pdata->bus_hz);
|
||||
}
|
||||
host->bus_hz = clk_get_rate(host->ciu_clk);
|
||||
}
|
||||
|
||||
if (drv_data && drv_data->setup_clock) {
|
||||
ret = drv_data->setup_clock(host);
|
||||
|
@ -2229,11 +2231,29 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
}
|
||||
}
|
||||
|
||||
host->vmmc = devm_regulator_get(host->dev, "vmmc");
|
||||
if (IS_ERR(host->vmmc)) {
|
||||
ret = PTR_ERR(host->vmmc);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_clk_ciu;
|
||||
|
||||
dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
|
||||
host->vmmc = NULL;
|
||||
} else {
|
||||
ret = regulator_enable(host->vmmc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(host->dev,
|
||||
"regulator_enable fail: %d\n", ret);
|
||||
goto err_clk_ciu;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host->bus_hz) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply bus speed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_clk_ciu;
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
@ -2321,8 +2341,10 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
|
||||
host->card_workqueue = alloc_workqueue("dw-mci-card",
|
||||
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
|
||||
if (!host->card_workqueue)
|
||||
if (!host->card_workqueue) {
|
||||
ret = -ENOMEM;
|
||||
goto err_dmaunmap;
|
||||
}
|
||||
INIT_WORK(&host->card_work, dw_mci_work_routine_card);
|
||||
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
|
||||
host->irq_flags, "dw-mci", host);
|
||||
|
@ -2378,6 +2400,7 @@ err_dmaunmap:
|
|||
if (host->use_dma && host->dma_ops->exit)
|
||||
host->dma_ops->exit(host);
|
||||
|
||||
err_regulator:
|
||||
if (host->vmmc)
|
||||
regulator_disable(host->vmmc);
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
#define SDMMC_INT_HLE BIT(12)
|
||||
#define SDMMC_INT_FRUN BIT(11)
|
||||
#define SDMMC_INT_HTO BIT(10)
|
||||
#define SDMMC_INT_DTO BIT(9)
|
||||
#define SDMMC_INT_DRTO BIT(9)
|
||||
#define SDMMC_INT_RTO BIT(8)
|
||||
#define SDMMC_INT_DCRC BIT(7)
|
||||
#define SDMMC_INT_RCRC BIT(6)
|
||||
|
@ -111,6 +111,7 @@
|
|||
#define SDMMC_INT_ERROR 0xbfc2
|
||||
/* Command register defines */
|
||||
#define SDMMC_CMD_START BIT(31)
|
||||
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
|
||||
#define SDMMC_CMD_CCS_EXP BIT(23)
|
||||
#define SDMMC_CMD_CEATA_RD BIT(22)
|
||||
#define SDMMC_CMD_UPD_CLK BIT(21)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
@ -120,7 +121,6 @@ struct jz4740_mmc_host {
|
|||
int irq;
|
||||
int card_detect_irq;
|
||||
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
struct mmc_request *req;
|
||||
struct mmc_command *cmd;
|
||||
|
@ -231,6 +231,14 @@ static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host,
|
|||
host->req->cmd->error = -EIO;
|
||||
data->error = -EIO;
|
||||
}
|
||||
} else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) {
|
||||
if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) {
|
||||
host->req->cmd->error = -ETIMEDOUT;
|
||||
data->error = -ETIMEDOUT;
|
||||
} else {
|
||||
host->req->cmd->error = -EIO;
|
||||
data->error = -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,11 +568,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid)
|
|||
if (cmd->data)
|
||||
cmd->data->error = -EIO;
|
||||
cmd->error = -EIO;
|
||||
} else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
|
||||
JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
|
||||
if (cmd->data)
|
||||
cmd->data->error = -EIO;
|
||||
cmd->error = -EIO;
|
||||
}
|
||||
|
||||
jz4740_mmc_set_irq_enabled(host, irq_reg, false);
|
||||
|
@ -626,7 +629,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
gpio_set_value(host->pdata->gpio_power,
|
||||
!host->pdata->power_active_low);
|
||||
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
||||
clk_enable(host->clk);
|
||||
clk_prepare_enable(host->clk);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
break;
|
||||
|
@ -634,7 +637,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
if (gpio_is_valid(host->pdata->gpio_power))
|
||||
gpio_set_value(host->pdata->gpio_power,
|
||||
host->pdata->power_active_low);
|
||||
clk_disable(host->clk);
|
||||
clk_disable_unprepare(host->clk);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -650,35 +653,6 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
}
|
||||
}
|
||||
|
||||
static int jz4740_mmc_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct jz4740_mmc_host *host = mmc_priv(mmc);
|
||||
if (!gpio_is_valid(host->pdata->gpio_read_only))
|
||||
return -ENOSYS;
|
||||
|
||||
return gpio_get_value(host->pdata->gpio_read_only) ^
|
||||
host->pdata->read_only_active_low;
|
||||
}
|
||||
|
||||
static int jz4740_mmc_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct jz4740_mmc_host *host = mmc_priv(mmc);
|
||||
if (!gpio_is_valid(host->pdata->gpio_card_detect))
|
||||
return -ENOSYS;
|
||||
|
||||
return gpio_get_value(host->pdata->gpio_card_detect) ^
|
||||
host->pdata->card_detect_active_low;
|
||||
}
|
||||
|
||||
static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid)
|
||||
{
|
||||
struct jz4740_mmc_host *host = devid;
|
||||
|
||||
mmc_detect_change(host->mmc, HZ / 2);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
||||
{
|
||||
struct jz4740_mmc_host *host = mmc_priv(mmc);
|
||||
|
@ -688,8 +662,8 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
|
|||
static const struct mmc_host_ops jz4740_mmc_ops = {
|
||||
.request = jz4740_mmc_request,
|
||||
.set_ios = jz4740_mmc_set_ios,
|
||||
.get_ro = jz4740_mmc_get_ro,
|
||||
.get_cd = jz4740_mmc_get_cd,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
.get_cd = mmc_gpio_get_cd,
|
||||
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
|
@ -724,58 +698,34 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int jz4740_mmc_request_gpios(struct platform_device *pdev)
|
||||
static int jz4740_mmc_request_gpios(struct mmc_host *mmc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
||||
int ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return 0;
|
||||
|
||||
ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_card_detect,
|
||||
"MMC detect change", false, 0);
|
||||
if (ret)
|
||||
goto err;
|
||||
if (!pdata->card_detect_active_low)
|
||||
mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
if (!pdata->read_only_active_low)
|
||||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_read_only,
|
||||
"MMC read only", false, 0);
|
||||
if (ret)
|
||||
goto err_free_gpio_card_detect;
|
||||
|
||||
ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power,
|
||||
"MMC read only", true, pdata->power_active_low);
|
||||
if (ret)
|
||||
goto err_free_gpio_read_only;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_gpio_read_only:
|
||||
if (gpio_is_valid(pdata->gpio_read_only))
|
||||
gpio_free(pdata->gpio_read_only);
|
||||
err_free_gpio_card_detect:
|
||||
if (gpio_is_valid(pdata->gpio_card_detect))
|
||||
gpio_free(pdata->gpio_card_detect);
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int jz4740_mmc_request_cd_irq(struct platform_device *pdev,
|
||||
struct jz4740_mmc_host *host)
|
||||
{
|
||||
struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
if (!gpio_is_valid(pdata->gpio_card_detect))
|
||||
return 0;
|
||||
|
||||
host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect);
|
||||
if (host->card_detect_irq < 0) {
|
||||
dev_warn(&pdev->dev, "Failed to get card detect irq\n");
|
||||
return 0;
|
||||
if (gpio_is_valid(pdata->gpio_card_detect)) {
|
||||
ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return request_irq(host->card_detect_irq, jz4740_mmc_card_detect_irq,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"MMC card detect", host);
|
||||
if (gpio_is_valid(pdata->gpio_read_only)) {
|
||||
ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power,
|
||||
"MMC read only", true, pdata->power_active_low);
|
||||
}
|
||||
|
||||
static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
||||
|
@ -787,10 +737,6 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev)
|
|||
|
||||
if (gpio_is_valid(pdata->gpio_power))
|
||||
gpio_free(pdata->gpio_power);
|
||||
if (gpio_is_valid(pdata->gpio_read_only))
|
||||
gpio_free(pdata->gpio_read_only);
|
||||
if (gpio_is_valid(pdata->gpio_card_detect))
|
||||
gpio_free(pdata->gpio_card_detect);
|
||||
}
|
||||
|
||||
static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host)
|
||||
|
@ -808,6 +754,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
struct mmc_host *mmc;
|
||||
struct jz4740_mmc_host *host;
|
||||
struct jz4740_mmc_platform_data *pdata;
|
||||
struct resource *res;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
|
||||
|
@ -827,42 +774,28 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
goto err_free_host;
|
||||
}
|
||||
|
||||
host->clk = clk_get(&pdev->dev, "mmc");
|
||||
host->clk = devm_clk_get(&pdev->dev, "mmc");
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
dev_err(&pdev->dev, "Failed to get mmc clock\n");
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!host->mem) {
|
||||
ret = -ENOENT;
|
||||
dev_err(&pdev->dev, "Failed to get base platform memory\n");
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
host->mem = request_mem_region(host->mem->start,
|
||||
resource_size(host->mem), pdev->name);
|
||||
if (!host->mem) {
|
||||
ret = -EBUSY;
|
||||
dev_err(&pdev->dev, "Failed to request base memory region\n");
|
||||
goto err_clk_put;
|
||||
}
|
||||
|
||||
host->base = ioremap_nocache(host->mem->start, resource_size(host->mem));
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (!host->base) {
|
||||
ret = -EBUSY;
|
||||
dev_err(&pdev->dev, "Failed to ioremap base memory\n");
|
||||
goto err_release_mem_region;
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", ret);
|
||||
goto err_iounmap;
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
ret = jz4740_mmc_request_gpios(pdev);
|
||||
ret = jz4740_mmc_request_gpios(mmc, pdev);
|
||||
if (ret)
|
||||
goto err_gpio_bulk_free;
|
||||
|
||||
|
@ -885,17 +818,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
spin_lock_init(&host->lock);
|
||||
host->irq_mask = 0xffff;
|
||||
|
||||
ret = jz4740_mmc_request_cd_irq(pdev, host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request card detect irq\n");
|
||||
goto err_free_gpios;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
|
||||
goto err_free_card_detect_irq;
|
||||
goto err_free_gpios;
|
||||
}
|
||||
|
||||
jz4740_mmc_reset(host);
|
||||
|
@ -918,21 +845,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
|
||||
err_free_irq:
|
||||
free_irq(host->irq, host);
|
||||
err_free_card_detect_irq:
|
||||
if (host->card_detect_irq >= 0)
|
||||
free_irq(host->card_detect_irq, host);
|
||||
err_free_gpios:
|
||||
jz4740_mmc_free_gpios(pdev);
|
||||
err_gpio_bulk_free:
|
||||
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
|
||||
err_iounmap:
|
||||
iounmap(host->base);
|
||||
err_release_mem_region:
|
||||
release_mem_region(host->mem->start, resource_size(host->mem));
|
||||
err_clk_put:
|
||||
clk_put(host->clk);
|
||||
err_free_host:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return ret;
|
||||
|
@ -949,24 +866,16 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
|
|||
mmc_remove_host(host->mmc);
|
||||
|
||||
free_irq(host->irq, host);
|
||||
if (host->card_detect_irq >= 0)
|
||||
free_irq(host->card_detect_irq, host);
|
||||
|
||||
jz4740_mmc_free_gpios(pdev);
|
||||
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
|
||||
|
||||
iounmap(host->base);
|
||||
release_mem_region(host->mem->start, resource_size(host->mem));
|
||||
|
||||
clk_put(host->clk);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
mmc_free_host(host->mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int jz4740_mmc_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -990,13 +899,8 @@ static int jz4740_mmc_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
const struct dev_pm_ops jz4740_mmc_pm_ops = {
|
||||
.suspend = jz4740_mmc_suspend,
|
||||
.resume = jz4740_mmc_resume,
|
||||
.poweroff = jz4740_mmc_suspend,
|
||||
.restore = jz4740_mmc_resume,
|
||||
};
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
|
||||
jz4740_mmc_resume);
|
||||
#define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops)
|
||||
#else
|
||||
#define JZ4740_MMC_PM_OPS NULL
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
#define DRIVER_NAME "mvsdio"
|
||||
|
||||
static int maxfreq = MVSD_CLOCKRATE_MAX;
|
||||
static int maxfreq;
|
||||
static int nodma;
|
||||
|
||||
struct mvsd_host {
|
||||
|
@ -685,7 +685,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
const struct mbus_dram_target_info *dram;
|
||||
struct resource *r;
|
||||
int ret, irq;
|
||||
int gpio_card_detect, gpio_write_protect;
|
||||
struct pinctrl *pinctrl;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -718,6 +717,20 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
if (!IS_ERR(host->clk))
|
||||
clk_prepare_enable(host->clk);
|
||||
|
||||
mmc->ops = &mvsd_ops;
|
||||
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
|
||||
mmc->f_max = MVSD_CLOCKRATE_MAX;
|
||||
|
||||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 65535;
|
||||
|
||||
mmc->max_segs = 1;
|
||||
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
||||
if (np) {
|
||||
if (IS_ERR(host->clk)) {
|
||||
dev_err(&pdev->dev, "DT platforms must have a clock associated\n");
|
||||
|
@ -726,35 +739,38 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
host->base_clock = clk_get_rate(host->clk) / 2;
|
||||
gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
} else {
|
||||
const struct mvsdio_platform_data *mvsd_data;
|
||||
|
||||
mvsd_data = pdev->dev.platform_data;
|
||||
if (!mvsd_data) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
host->base_clock = mvsd_data->clock / 2;
|
||||
gpio_card_detect = mvsd_data->gpio_card_detect ? : -EINVAL;
|
||||
gpio_write_protect = mvsd_data->gpio_write_protect ? : -EINVAL;
|
||||
/* GPIO 0 regarded as invalid for backward compatibility */
|
||||
if (mvsd_data->gpio_card_detect &&
|
||||
gpio_is_valid(mvsd_data->gpio_card_detect)) {
|
||||
ret = mmc_gpio_request_cd(mmc,
|
||||
mvsd_data->gpio_card_detect);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
}
|
||||
|
||||
if (mvsd_data->gpio_write_protect &&
|
||||
gpio_is_valid(mvsd_data->gpio_write_protect))
|
||||
mmc_gpio_request_ro(mmc, mvsd_data->gpio_write_protect);
|
||||
}
|
||||
|
||||
mmc->ops = &mvsd_ops;
|
||||
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ |
|
||||
MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
|
||||
mmc->f_max = maxfreq;
|
||||
|
||||
mmc->max_blk_size = 2048;
|
||||
mmc->max_blk_count = 65535;
|
||||
|
||||
mmc->max_segs = 1;
|
||||
mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
if (maxfreq)
|
||||
mmc->f_max = maxfreq;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
|
@ -777,15 +793,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(gpio_card_detect)) {
|
||||
ret = mmc_gpio_request_cd(mmc, gpio_card_detect);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
mmc_gpio_request_ro(mmc, gpio_write_protect);
|
||||
|
||||
setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
ret = mmc_add_host(mmc);
|
||||
|
@ -793,10 +800,10 @@ static int __init mvsd_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
|
||||
if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
|
||||
dev_notice(&pdev->dev, "using GPIO %d for card detection\n",
|
||||
gpio_card_detect);
|
||||
dev_notice(&pdev->dev, "using GPIO for card detection\n");
|
||||
else
|
||||
dev_notice(&pdev->dev, "lacking card detect (fall back to polling)\n");
|
||||
dev_notice(&pdev->dev,
|
||||
"lacking card detect (fall back to polling)\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -827,7 +834,6 @@ static int __exit mvsd_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(host->clk);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -1067,7 +1067,9 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
goto out_release_mem;
|
||||
}
|
||||
|
||||
mmc_of_parse(mmc);
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
mmc->ops = &mxcmci_ops;
|
||||
|
||||
/* For devicetree parsing, the bus width is read from devicetree */
|
||||
|
@ -1219,8 +1221,6 @@ static int mxcmci_remove(struct platform_device *pdev)
|
|||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
if (host->vcc)
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/stmp_device.h>
|
||||
#include <linux/spi/mxs-spi.h>
|
||||
|
||||
|
@ -580,7 +579,6 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
struct mxs_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *iores;
|
||||
struct pinctrl *pinctrl;
|
||||
int ret = 0, irq_err;
|
||||
struct regulator *reg_vmmc;
|
||||
enum of_gpio_flags flags;
|
||||
|
@ -620,12 +618,6 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl)) {
|
||||
ret = PTR_ERR(pinctrl);
|
||||
goto out_mmc_free;
|
||||
}
|
||||
|
||||
ssp->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(ssp->clk)) {
|
||||
ret = PTR_ERR(ssp->clk);
|
||||
|
@ -639,6 +631,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
if (!ssp->dmach) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"%s: failed to request dma\n", __func__);
|
||||
ret = -ENODEV;
|
||||
goto out_clk_put;
|
||||
}
|
||||
|
||||
|
@ -708,8 +701,6 @@ static int mxs_mmc_remove(struct platform_device *pdev)
|
|||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (ssp->dmach)
|
||||
dma_release_channel(ssp->dmach);
|
||||
|
||||
|
|
|
@ -1413,33 +1413,17 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
|||
else
|
||||
sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
|
||||
host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
|
||||
#if 0
|
||||
if (!host->dma_tx) {
|
||||
dev_err(host->dev, "unable to obtain TX DMA engine channel %u\n",
|
||||
sig);
|
||||
goto err_dma;
|
||||
}
|
||||
#else
|
||||
if (!host->dma_tx)
|
||||
dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
|
||||
sig);
|
||||
#endif
|
||||
if (mmc_omap2())
|
||||
sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
|
||||
else
|
||||
sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
|
||||
host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig);
|
||||
#if 0
|
||||
if (!host->dma_rx) {
|
||||
dev_err(host->dev, "unable to obtain RX DMA engine channel %u\n",
|
||||
sig);
|
||||
goto err_dma;
|
||||
}
|
||||
#else
|
||||
if (!host->dma_rx)
|
||||
dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
|
||||
sig);
|
||||
#endif
|
||||
|
||||
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
|
||||
if (ret)
|
||||
|
@ -1500,8 +1484,6 @@ static int mmc_omap_remove(struct platform_device *pdev)
|
|||
struct mmc_omap_host *host = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
BUG_ON(host == NULL);
|
||||
|
||||
for (i = 0; i < host->nr_slots; i++)
|
||||
|
|
|
@ -2047,7 +2047,6 @@ err_irq:
|
|||
}
|
||||
err1:
|
||||
iounmap(host->base);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
mmc_free_host(mmc);
|
||||
err_alloc:
|
||||
omap_hsmmc_gpio_free(pdata);
|
||||
|
@ -2093,7 +2092,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -834,8 +834,6 @@ static int pxamci_remove(struct platform_device *pdev)
|
|||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
struct pxamci_host *host = mmc_priv(mmc);
|
||||
|
||||
|
|
|
@ -1316,8 +1316,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
|
|||
mmc_remove_host(mmc);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
dev_dbg(&(pdev->dev),
|
||||
": Realtek PCI-E SDMMC controller has been removed\n");
|
||||
|
||||
|
|
|
@ -31,10 +31,13 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_gpio.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
|
@ -83,12 +86,37 @@ static int sdhci_acpi_enable_dma(struct sdhci_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
|
||||
reg |= 0x10;
|
||||
sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
|
||||
/* For eMMC, minimum is 1us but give it 9us for good measure */
|
||||
udelay(9);
|
||||
reg &= ~0x10;
|
||||
sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
|
||||
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
||||
usleep_range(300, 1000);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_int = {
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
.hw_reset = sdhci_acpi_int_hw_reset,
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
|
||||
.ops = &sdhci_acpi_ops_int,
|
||||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
|
||||
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE,
|
||||
.chip = &sdhci_acpi_chip_int,
|
||||
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET,
|
||||
.caps2 = MMC_CAP2_HC_ERASE_SZ,
|
||||
.flags = SDHCI_ACPI_RUNTIME_PM,
|
||||
};
|
||||
|
@ -101,6 +129,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
|
|||
};
|
||||
|
||||
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
|
||||
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM,
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
||||
};
|
||||
|
||||
struct sdhci_acpi_uid_slot {
|
||||
|
@ -161,6 +191,59 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
|
|||
return slot;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id)
|
||||
{
|
||||
mmc_detect_change(dev_id, msecs_to_jiffies(200));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sdhci_acpi_add_own_cd(struct device *dev, int gpio,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err, irq;
|
||||
|
||||
if (gpio < 0) {
|
||||
err = gpio;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = devm_gpio_request_one(dev, gpio, GPIOF_DIR_IN, "sd_cd");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
irq = gpio_to_irq(gpio);
|
||||
if (irq < 0) {
|
||||
err = irq;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
|
||||
err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc);
|
||||
if (err)
|
||||
goto out_free;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
devm_gpio_free(dev, gpio);
|
||||
out:
|
||||
dev_warn(dev, "failed to setup card detect wake up\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int sdhci_acpi_add_own_cd(struct device *dev, int gpio,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int sdhci_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -171,7 +254,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
struct resource *iomem;
|
||||
resource_size_t len;
|
||||
const char *hid;
|
||||
int err;
|
||||
int err, gpio;
|
||||
|
||||
if (acpi_bus_get_device(handle, &device))
|
||||
return -ENODEV;
|
||||
|
@ -196,6 +279,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
gpio = acpi_get_gpio_by_index(dev, 0, NULL);
|
||||
|
||||
c = sdhci_priv(host);
|
||||
c->host = host;
|
||||
c->slot = sdhci_acpi_get_slot(handle, hid);
|
||||
|
@ -251,6 +336,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto err_free;
|
||||
|
||||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
if (sdhci_acpi_add_own_cd(dev, gpio, host->mmc))
|
||||
c->use_runtime_pm = false;
|
||||
}
|
||||
|
||||
if (c->use_runtime_pm) {
|
||||
pm_runtime_set_active(dev);
|
||||
pm_suspend_ignore_children(dev, 1);
|
||||
|
@ -262,7 +352,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err_free:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_free_host(c->host);
|
||||
return err;
|
||||
}
|
||||
|
@ -281,7 +370,6 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
|
|||
|
||||
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
|
||||
sdhci_remove_host(c->host, dead);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_free_host(c->host);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci.h"
|
||||
|
||||
#define SDHCI_SOFT_RESET 0x01000000
|
||||
#define KONA_SDHOST_CORECTRL 0x8000
|
||||
#define KONA_SDHOST_CD_PINCTRL 0x00000008
|
||||
#define KONA_SDHOST_STOP_HCLK 0x00000004
|
||||
#define KONA_SDHOST_RESET 0x00000002
|
||||
#define KONA_SDHOST_EN 0x00000001
|
||||
|
||||
#define KONA_SDHOST_CORESTAT 0x8004
|
||||
#define KONA_SDHOST_WP 0x00000002
|
||||
#define KONA_SDHOST_CD_SW 0x00000001
|
||||
|
||||
#define KONA_SDHOST_COREIMR 0x8008
|
||||
#define KONA_SDHOST_IP 0x00000001
|
||||
|
||||
#define KONA_SDHOST_COREISR 0x800C
|
||||
#define KONA_SDHOST_COREIMSR 0x8010
|
||||
#define KONA_SDHOST_COREDBG1 0x8014
|
||||
#define KONA_SDHOST_COREGPO_MASK 0x8018
|
||||
|
||||
#define SD_DETECT_GPIO_DEBOUNCE_128MS 128
|
||||
|
||||
#define KONA_MMC_AUTOSUSPEND_DELAY (50)
|
||||
|
||||
struct sdhci_bcm_kona_dev {
|
||||
struct mutex write_lock; /* protect back to back writes */
|
||||
};
|
||||
|
||||
|
||||
static int sdhci_bcm_kona_sd_reset(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int val;
|
||||
unsigned long timeout;
|
||||
|
||||
/* This timeout should be sufficent for core to reset */
|
||||
timeout = jiffies + msecs_to_jiffies(100);
|
||||
|
||||
/* reset the host using the top level reset */
|
||||
val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
|
||||
val |= KONA_SDHOST_RESET;
|
||||
sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
|
||||
|
||||
while (!(sdhci_readl(host, KONA_SDHOST_CORECTRL) & KONA_SDHOST_RESET)) {
|
||||
if (time_is_before_jiffies(timeout)) {
|
||||
pr_err("Error: sd host is stuck in reset!!!\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/* bring the host out of reset */
|
||||
val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
|
||||
val &= ~KONA_SDHOST_RESET;
|
||||
|
||||
/*
|
||||
* Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
|
||||
* Back-to-Back writes to same register needs delay when SD bus clock
|
||||
* is very low w.r.t AHB clock, mainly during boot-time and during card
|
||||
* insert-removal.
|
||||
*/
|
||||
usleep_range(1000, 5000);
|
||||
sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_bcm_kona_sd_init(struct sdhci_host *host)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
/* enable the interrupt from the IP core */
|
||||
val = sdhci_readl(host, KONA_SDHOST_COREIMR);
|
||||
val |= KONA_SDHOST_IP;
|
||||
sdhci_writel(host, val, KONA_SDHOST_COREIMR);
|
||||
|
||||
/* Enable the AHB clock gating module to the host */
|
||||
val = sdhci_readl(host, KONA_SDHOST_CORECTRL);
|
||||
val |= KONA_SDHOST_EN;
|
||||
|
||||
/*
|
||||
* Back-to-Back register write needs a delay of 1ms at bootup (min 10uS)
|
||||
* Back-to-Back writes to same register needs delay when SD bus clock
|
||||
* is very low w.r.t AHB clock, mainly during boot-time and during card
|
||||
* insert-removal.
|
||||
*/
|
||||
usleep_range(1000, 5000);
|
||||
sdhci_writel(host, val, KONA_SDHOST_CORECTRL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Software emulation of the SD card insertion/removal. Set insert=1 for insert
|
||||
* and insert=0 for removal. The card detection is done by GPIO. For Broadcom
|
||||
* IP to function properly the bit 0 of CORESTAT register needs to be set/reset
|
||||
* to generate the CD IRQ handled in sdhci.c which schedules card_tasklet.
|
||||
*/
|
||||
static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
|
||||
struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* Back-to-Back register write needs a delay of min 10uS.
|
||||
* Back-to-Back writes to same register needs delay when SD bus clock
|
||||
* is very low w.r.t AHB clock, mainly during boot-time and during card
|
||||
* insert-removal.
|
||||
* We keep 20uS
|
||||
*/
|
||||
mutex_lock(&kona_dev->write_lock);
|
||||
udelay(20);
|
||||
val = sdhci_readl(host, KONA_SDHOST_CORESTAT);
|
||||
|
||||
if (insert) {
|
||||
int ret;
|
||||
|
||||
ret = mmc_gpio_get_ro(host->mmc);
|
||||
if (ret >= 0)
|
||||
val = (val & ~KONA_SDHOST_WP) |
|
||||
((ret) ? KONA_SDHOST_WP : 0);
|
||||
|
||||
val |= KONA_SDHOST_CD_SW;
|
||||
sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
|
||||
} else {
|
||||
val &= ~KONA_SDHOST_CD_SW;
|
||||
sdhci_writel(host, val, KONA_SDHOST_CORESTAT);
|
||||
}
|
||||
mutex_unlock(&kona_dev->write_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SD card interrupt event callback
|
||||
*/
|
||||
void sdhci_bcm_kona_card_event(struct sdhci_host *host)
|
||||
{
|
||||
if (mmc_gpio_get_cd(host->mmc) > 0) {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"card inserted\n");
|
||||
sdhci_bcm_kona_sd_card_emulate(host, 1);
|
||||
} else {
|
||||
dev_dbg(mmc_dev(host->mmc),
|
||||
"card removed\n");
|
||||
sdhci_bcm_kona_sd_card_emulate(host, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the base clock. Use central clock source for now. Not sure if different
|
||||
* clock speed to each dev is allowed
|
||||
*/
|
||||
static unsigned int sdhci_bcm_kona_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_bcm_kona_dev *kona_dev;
|
||||
struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
|
||||
kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
|
||||
return host->mmc->f_max;
|
||||
}
|
||||
|
||||
static unsigned int sdhci_bcm_kona_get_timeout_clock(struct sdhci_host *host)
|
||||
{
|
||||
return sdhci_bcm_kona_get_max_clk(host);
|
||||
}
|
||||
|
||||
static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
|
||||
u8 power_mode)
|
||||
{
|
||||
/*
|
||||
* JEDEC and SD spec specify supplying 74 continuous clocks to
|
||||
* device after power up. With minimum bus (100KHz) that
|
||||
* that translates to 740us
|
||||
*/
|
||||
if (power_mode != MMC_POWER_OFF)
|
||||
udelay(740);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_bcm_kona_ops = {
|
||||
.get_max_clock = sdhci_bcm_kona_get_max_clk,
|
||||
.get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
|
||||
.platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
|
||||
.card_event = sdhci_bcm_kona_card_event,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
|
||||
.ops = &sdhci_bcm_kona_ops,
|
||||
.quirks = SDHCI_QUIRK_NO_CARD_NO_RESET |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE | SDHCI_QUIRK_32BIT_ADMA_SIZE |
|
||||
SDHCI_QUIRK_FORCE_BLK_SZ_2048 |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = {
|
||||
{ .compatible = "bcm,kona-sdhci"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match);
|
||||
|
||||
static int __init sdhci_bcm_kona_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_bcm_kona_dev *kona_dev = NULL;
|
||||
struct sdhci_pltfm_host *pltfm_priv;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host;
|
||||
int ret;
|
||||
|
||||
ret = 0;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pltfm_data_kona,
|
||||
sizeof(*kona_dev));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
dev_dbg(dev, "%s: inited. IOADDR=%p\n", __func__, host->ioaddr);
|
||||
|
||||
pltfm_priv = sdhci_priv(host);
|
||||
|
||||
kona_dev = sdhci_pltfm_priv(pltfm_priv);
|
||||
mutex_init(&kona_dev->write_lock);
|
||||
|
||||
mmc_of_parse(host->mmc);
|
||||
|
||||
if (!host->mmc->f_max) {
|
||||
dev_err(&pdev->dev, "Missing max-freq for SDHCI cfg\n");
|
||||
ret = -ENXIO;
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "non-removable=%c\n",
|
||||
(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
|
||||
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
|
||||
(mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N',
|
||||
(mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N');
|
||||
|
||||
if (host->mmc->caps | MMC_CAP_NONREMOVABLE)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
|
||||
dev_dbg(dev, "is_8bit=%c\n",
|
||||
(host->mmc->caps | MMC_CAP_8_BIT_DATA) ? 'Y' : 'N');
|
||||
|
||||
ret = sdhci_bcm_kona_sd_reset(host);
|
||||
if (ret)
|
||||
goto err_pltfm_free;
|
||||
|
||||
sdhci_bcm_kona_sd_init(host);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed sdhci_add_host\n");
|
||||
goto err_reset;
|
||||
}
|
||||
|
||||
/* if device is eMMC, emulate card insert right here */
|
||||
if (host->mmc->caps | MMC_CAP_NONREMOVABLE) {
|
||||
ret = sdhci_bcm_kona_sd_card_emulate(host, 1);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"unable to emulate card insertion\n");
|
||||
goto err_remove_host;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Since the card detection GPIO interrupt is configured to be
|
||||
* edge sensitive, check the initial GPIO value here, emulate
|
||||
* only if the card is present
|
||||
*/
|
||||
if (mmc_gpio_get_cd(host->mmc) > 0)
|
||||
sdhci_bcm_kona_sd_card_emulate(host, 1);
|
||||
|
||||
dev_dbg(dev, "initialized properly\n");
|
||||
return 0;
|
||||
|
||||
err_remove_host:
|
||||
sdhci_remove_host(host, 0);
|
||||
|
||||
err_reset:
|
||||
sdhci_bcm_kona_sd_reset(host);
|
||||
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
int dead;
|
||||
u32 scratch;
|
||||
|
||||
dead = 0;
|
||||
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
|
||||
if (scratch == (u32)-1)
|
||||
dead = 1;
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
sdhci_free_host(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-kona",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.of_match_table = of_match_ptr(sdhci_bcm_kona_of_match),
|
||||
},
|
||||
.probe = sdhci_bcm_kona_probe,
|
||||
.remove = __exit_p(sdhci_bcm_kona_remove),
|
||||
};
|
||||
module_platform_driver(sdhci_bcm_kona_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for Broadcom Kona platform");
|
||||
MODULE_AUTHOR("Broadcom");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -148,7 +148,7 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
|
|||
struct sdhci_pltfm_host *pltfm_host;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata);
|
||||
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
|
|||
|
||||
static int sdhci_cns3xxx_probe(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata);
|
||||
return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0);
|
||||
}
|
||||
|
||||
static int sdhci_cns3xxx_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -130,7 +130,7 @@ static int sdhci_dove_probe(struct platform_device *pdev)
|
|||
gpio_direction_input(priv->gpio_cd);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err_sdhci_pltfm_init;
|
||||
|
|
|
@ -384,6 +384,20 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
|
||||
u32 f_host = clk_get_rate(pltfm_host->clk);
|
||||
|
||||
if (boarddata->f_max && (boarddata->f_max < f_host))
|
||||
return boarddata->f_max;
|
||||
else
|
||||
return f_host;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -391,6 +405,14 @@ static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
|
|||
return clk_get_rate(pltfm_host->clk) / 256 / 16;
|
||||
}
|
||||
|
||||
static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk));
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -438,8 +460,8 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.write_l = esdhc_writel_le,
|
||||
.write_w = esdhc_writew_le,
|
||||
.write_b = esdhc_writeb_le,
|
||||
.set_clock = esdhc_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_clock = esdhc_pltfm_set_clock,
|
||||
.get_max_clock = esdhc_pltfm_get_max_clock,
|
||||
.get_min_clock = esdhc_pltfm_get_min_clock,
|
||||
.get_ro = esdhc_pltfm_get_ro,
|
||||
.platform_bus_width = esdhc_pltfm_bus_width,
|
||||
|
@ -482,6 +504,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||
|
||||
of_property_read_u32(np, "bus-width", &boarddata->max_bus_width);
|
||||
|
||||
of_property_read_u32(np, "max-frequency", &boarddata->f_max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
|
@ -503,7 +527,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
int err;
|
||||
struct pltfm_imx_data *imx_data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
|
|
|
@ -36,13 +36,21 @@
|
|||
/* pltfm-specific */
|
||||
#define ESDHC_HOST_CONTROL_LE 0x20
|
||||
|
||||
/*
|
||||
* P2020 interpretation of the SDHCI_HOST_CONTROL register
|
||||
*/
|
||||
#define ESDHC_CTRL_4BITBUS (0x1 << 1)
|
||||
#define ESDHC_CTRL_8BITBUS (0x2 << 1)
|
||||
#define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
|
||||
|
||||
/* OF-specific */
|
||||
#define ESDHC_DMA_SYSCTL 0x40c
|
||||
#define ESDHC_DMA_SNOOP 0x00000040
|
||||
|
||||
#define ESDHC_HOST_CONTROL_RES 0x05
|
||||
|
||||
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
|
||||
unsigned int host_clock)
|
||||
{
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
|
@ -56,14 +64,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
| ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
while (host_clock / pre_div / 16 > clock && pre_div < 256)
|
||||
pre_div *= 2;
|
||||
|
||||
while (host->max_clk / pre_div / div > clock && div < 16)
|
||||
while (host_clock / pre_div / div > clock && div < 16)
|
||||
div++;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||
clock, host->max_clk / pre_div / div);
|
||||
clock, host_clock / pre_div / div);
|
||||
|
||||
pre_div >>= 1;
|
||||
div--;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -120,6 +121,13 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg)
|
|||
if (reg == SDHCI_HOST_CONTROL) {
|
||||
u32 dma_bits;
|
||||
|
||||
/*
|
||||
* If host control register is not standard, exit
|
||||
* this function
|
||||
*/
|
||||
if (host->quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL)
|
||||
return;
|
||||
|
||||
/* DMA select is 22,23 bits in Protocol Control Register */
|
||||
dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5;
|
||||
clrsetbits_be32(host->ioaddr + reg , SDHCI_CTRL_DMA_MASK << 5,
|
||||
|
@ -200,7 +208,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
|
||||
/* Set the clock */
|
||||
esdhc_set_clock(host, clock);
|
||||
esdhc_set_clock(host, clock, host->max_clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -230,6 +238,30 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
|
|||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
}
|
||||
|
||||
static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
switch (width) {
|
||||
case MMC_BUS_WIDTH_8:
|
||||
ctrl = ESDHC_CTRL_8BITBUS;
|
||||
break;
|
||||
|
||||
case MMC_BUS_WIDTH_4:
|
||||
ctrl = ESDHC_CTRL_4BITBUS;
|
||||
break;
|
||||
|
||||
default:
|
||||
ctrl = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
|
||||
ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl,
|
||||
.read_w = esdhc_readw,
|
||||
|
@ -247,6 +279,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.platform_resume = esdhc_of_resume,
|
||||
#endif
|
||||
.adma_workaround = esdhci_of_adma_workaround,
|
||||
.platform_bus_width = esdhc_pltfm_bus_width,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
||||
|
@ -262,7 +295,33 @@ static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
|||
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata);
|
||||
struct sdhci_host *host;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
if (of_device_is_compatible(np, "fsl,p2020-esdhc")) {
|
||||
/*
|
||||
* Freescale messed up with P2020 as it has a non-standard
|
||||
* host control register
|
||||
*/
|
||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HOST_CONTROL;
|
||||
}
|
||||
|
||||
/* call to generic mmc_of_parse to support additional capabilities */
|
||||
mmc_of_parse(host->mmc);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -68,7 +68,7 @@ static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
|
|||
|
||||
static int sdhci_hlwd_probe(struct platform_device *pdev)
|
||||
{
|
||||
return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata);
|
||||
return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata, 0);
|
||||
}
|
||||
|
||||
static int sdhci_hlwd_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
|
||||
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
|
||||
|
||||
/*
|
||||
* PCI registers
|
||||
|
@ -77,6 +78,8 @@ struct sdhci_pci_slot {
|
|||
int rst_n_gpio;
|
||||
int cd_gpio;
|
||||
int cd_irq;
|
||||
|
||||
void (*hw_reset)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
struct sdhci_pci_chip {
|
||||
|
@ -307,10 +310,27 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
|
|||
.probe_slot = pch_hc_probe_slot,
|
||||
};
|
||||
|
||||
static void sdhci_pci_int_hw_reset(struct sdhci_host *host)
|
||||
{
|
||||
u8 reg;
|
||||
|
||||
reg = sdhci_readb(host, SDHCI_POWER_CONTROL);
|
||||
reg |= 0x10;
|
||||
sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
|
||||
/* For eMMC, minimum is 1us but give it 9us for good measure */
|
||||
udelay(9);
|
||||
reg &= ~0x10;
|
||||
sdhci_writeb(host, reg, SDHCI_POWER_CONTROL);
|
||||
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
||||
usleep_range(300, 1000);
|
||||
}
|
||||
|
||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE;
|
||||
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
|
||||
MMC_CAP_HW_RESET;
|
||||
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
|
||||
slot->hw_reset = sdhci_pci_int_hw_reset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -332,6 +352,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
|||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
|
||||
.allow_runtime_pm = true,
|
||||
};
|
||||
|
||||
/* O2Micro extra registers */
|
||||
|
@ -909,6 +931,14 @@ static const struct pci_device_id pci_ids[] = {
|
|||
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_INTEL,
|
||||
.device = PCI_DEVICE_ID_INTEL_BYT_EMMC2,
|
||||
.subvendor = PCI_ANY_ID,
|
||||
.subdevice = PCI_ANY_ID,
|
||||
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
|
||||
},
|
||||
|
||||
{
|
||||
.vendor = PCI_VENDOR_ID_O2,
|
||||
.device = PCI_DEVICE_ID_O2_8120,
|
||||
|
@ -1014,7 +1044,7 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||
static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||
int rst_n_gpio = slot->rst_n_gpio;
|
||||
|
@ -1029,6 +1059,14 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
|||
usleep_range(300, 1000);
|
||||
}
|
||||
|
||||
static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = sdhci_priv(host);
|
||||
|
||||
if (slot->hw_reset)
|
||||
slot->hw_reset(host);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_pci_ops = {
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.platform_bus_width = sdhci_pci_bus_width,
|
||||
|
@ -1326,6 +1364,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||
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;
|
||||
slot->hw_reset = sdhci_pci_gpio_hw_reset;
|
||||
} else {
|
||||
dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
|
||||
slot->rst_n_gpio = -EINVAL;
|
||||
|
|
|
@ -115,10 +115,10 @@ void sdhci_get_of_property(struct platform_device *pdev) {}
|
|||
EXPORT_SYMBOL_GPL(sdhci_get_of_property);
|
||||
|
||||
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
|
||||
const struct sdhci_pltfm_data *pdata)
|
||||
const struct sdhci_pltfm_data *pdata,
|
||||
size_t priv_size)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *iomem;
|
||||
int ret;
|
||||
|
@ -134,24 +134,27 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
|
|||
|
||||
/* Some PCI-based MFD need the parent here */
|
||||
if (pdev->dev.parent != &platform_bus && !np)
|
||||
host = sdhci_alloc_host(pdev->dev.parent, sizeof(*pltfm_host));
|
||||
host = sdhci_alloc_host(pdev->dev.parent,
|
||||
sizeof(struct sdhci_pltfm_host) + priv_size);
|
||||
else
|
||||
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
|
||||
host = sdhci_alloc_host(&pdev->dev,
|
||||
sizeof(struct sdhci_pltfm_host) + priv_size);
|
||||
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
||||
host->hw_name = dev_name(&pdev->dev);
|
||||
if (pdata && pdata->ops)
|
||||
host->ops = pdata->ops;
|
||||
else
|
||||
host->ops = &sdhci_pltfm_ops;
|
||||
if (pdata)
|
||||
if (pdata) {
|
||||
host->quirks = pdata->quirks;
|
||||
host->quirks2 = pdata->quirks2;
|
||||
}
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!request_mem_region(iomem->start, resource_size(iomem),
|
||||
|
@ -197,17 +200,17 @@ void sdhci_pltfm_free(struct platform_device *pdev)
|
|||
iounmap(host->ioaddr);
|
||||
release_mem_region(iomem->start, resource_size(iomem));
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
|
||||
|
||||
int sdhci_pltfm_register(struct platform_device *pdev,
|
||||
const struct sdhci_pltfm_data *pdata)
|
||||
const struct sdhci_pltfm_data *pdata,
|
||||
size_t priv_size)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
int ret = 0;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, pdata);
|
||||
host = sdhci_pltfm_init(pdev, pdata, priv_size);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
struct sdhci_pltfm_data {
|
||||
const struct sdhci_ops *ops;
|
||||
unsigned int quirks;
|
||||
unsigned int quirks2;
|
||||
};
|
||||
|
||||
struct sdhci_pltfm_host {
|
||||
|
@ -27,6 +28,8 @@ struct sdhci_pltfm_host {
|
|||
/* migrate from sdhci_of_host */
|
||||
unsigned int clock;
|
||||
u16 xfer_mode_shadow;
|
||||
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
|
||||
|
@ -91,15 +94,22 @@ static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
|
|||
extern void sdhci_get_of_property(struct platform_device *pdev);
|
||||
|
||||
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
|
||||
const struct sdhci_pltfm_data *pdata);
|
||||
const struct sdhci_pltfm_data *pdata,
|
||||
size_t priv_size);
|
||||
extern void sdhci_pltfm_free(struct platform_device *pdev);
|
||||
|
||||
extern int sdhci_pltfm_register(struct platform_device *pdev,
|
||||
const struct sdhci_pltfm_data *pdata);
|
||||
const struct sdhci_pltfm_data *pdata,
|
||||
size_t priv_size);
|
||||
extern int sdhci_pltfm_unregister(struct platform_device *pdev);
|
||||
|
||||
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
|
||||
|
||||
static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
|
||||
{
|
||||
return (void *)host->private;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern const struct dev_pm_ops sdhci_pltfm_pmops;
|
||||
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
|
||||
|
|
|
@ -175,7 +175,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
|
|||
if (!pxa)
|
||||
return -ENOMEM;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, NULL);
|
||||
host = sdhci_pltfm_init(pdev, NULL, 0);
|
||||
if (IS_ERR(host)) {
|
||||
kfree(pxa);
|
||||
return PTR_ERR(host);
|
||||
|
@ -253,8 +253,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
|
|||
sdhci_pltfm_free(pdev);
|
||||
kfree(pxa);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
if (!pxa)
|
||||
return -ENOMEM;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata);
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
kfree(pxa);
|
||||
return PTR_ERR(host);
|
||||
|
@ -252,7 +252,9 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
|
||||
match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
mmc_of_parse(host->mmc);
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_of_parse;
|
||||
sdhci_get_of_property(pdev);
|
||||
pdata = pxav3_get_mmc_pdata(dev);
|
||||
} else if (pdata) {
|
||||
|
@ -285,18 +287,15 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add host\n");
|
||||
pm_runtime_forbid(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
goto err_add_host;
|
||||
}
|
||||
|
||||
|
@ -313,10 +312,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
err_of_parse:
|
||||
err_cd_req:
|
||||
err_add_host:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
clk_disable_unprepare(clk);
|
||||
clk_put(clk);
|
||||
err_cd_req:
|
||||
err_clk_get:
|
||||
sdhci_pltfm_free(pdev);
|
||||
kfree(pxa);
|
||||
|
@ -339,8 +341,6 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
|
|||
sdhci_pltfm_free(pdev);
|
||||
kfree(pxa);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -745,7 +745,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
|
|||
clk_disable_unprepare(sc->clk_io);
|
||||
|
||||
sdhci_free_host(host);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_sirf_priv {
|
||||
|
@ -24,7 +23,7 @@ struct sdhci_sirf_priv {
|
|||
static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
return clk_get_rate(priv->clk);
|
||||
}
|
||||
|
||||
|
@ -46,47 +45,35 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
|
|||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_sirf_priv *priv;
|
||||
struct pinctrl *pinctrl;
|
||||
struct clk *clk;
|
||||
int gpio_cd;
|
||||
int ret;
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
|
||||
if (IS_ERR(pinctrl)) {
|
||||
dev_err(&pdev->dev, "unable to get pinmux");
|
||||
return PTR_ERR(pinctrl);
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_sirf_priv),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "unable to get clock");
|
||||
return PTR_ERR(priv->clk);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
|
||||
"cd-gpios", 0);
|
||||
} else {
|
||||
priv->gpio_cd = -EINVAL;
|
||||
}
|
||||
if (pdev->dev.of_node)
|
||||
gpio_cd = of_get_named_gpio(pdev->dev.of_node, "cd-gpios", 0);
|
||||
else
|
||||
gpio_cd = -EINVAL;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err_sdhci_pltfm_init;
|
||||
}
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, sizeof(struct sdhci_sirf_priv));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->clk = clk;
|
||||
priv->gpio_cd = gpio_cd;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
clk_prepare_enable(priv->clk);
|
||||
ret = clk_prepare_enable(priv->clk);
|
||||
if (ret)
|
||||
goto err_clk_prepare;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
|
@ -111,8 +98,8 @@ err_request_cd:
|
|||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
clk_disable_unprepare(priv->clk);
|
||||
err_clk_prepare:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_sdhci_pltfm_init:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -120,7 +107,7 @@ static int sdhci_sirf_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
|
@ -136,7 +123,7 @@ static int sdhci_sirf_suspend(struct device *dev)
|
|||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
|
@ -152,7 +139,7 @@ static int sdhci_sirf_resume(struct device *dev)
|
|||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_sirf_priv *priv = pltfm_host->priv;
|
||||
struct sdhci_sirf_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(priv->clk);
|
||||
|
|
|
@ -258,7 +258,6 @@ static int sdhci_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
set_drvdata:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
sdhci_remove_host(host, 1);
|
||||
free_host:
|
||||
sdhci_free_host(host);
|
||||
|
@ -278,7 +277,6 @@ static int sdhci_remove(struct platform_device *pdev)
|
|||
int dead = 0;
|
||||
u32 scratch;
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
|
||||
if (scratch == (u32)-1)
|
||||
dead = 1;
|
||||
|
|
|
@ -205,7 +205,7 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
|
||||
|
||||
static void sdhci_tegra_parse_dt(struct device *dev)
|
||||
static int sdhci_tegra_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
@ -213,7 +213,7 @@ static void sdhci_tegra_parse_dt(struct device *dev)
|
|||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
|
||||
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0);
|
||||
mmc_of_parse(host->mmc);
|
||||
return mmc_of_parse(host->mmc);
|
||||
}
|
||||
|
||||
static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
|
@ -231,7 +231,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, soc_data->pdata);
|
||||
host = sdhci_pltfm_init(pdev, soc_data->pdata, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
pltfm_host = sdhci_priv(host);
|
||||
|
@ -245,7 +245,9 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
tegra_host->soc_data = soc_data;
|
||||
pltfm_host->priv = tegra_host;
|
||||
|
||||
sdhci_tegra_parse_dt(&pdev->dev);
|
||||
rc = sdhci_tegra_parse_dt(&pdev->dev);
|
||||
if (rc)
|
||||
goto err_parse_dt;
|
||||
|
||||
if (gpio_is_valid(tegra_host->power_gpio)) {
|
||||
rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
|
||||
|
@ -279,6 +281,7 @@ err_clk_get:
|
|||
if (gpio_is_valid(tegra_host->power_gpio))
|
||||
gpio_free(tegra_host->power_gpio);
|
||||
err_power_req:
|
||||
err_parse_dt:
|
||||
err_alloc_tegra_host:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return rc;
|
||||
|
|
|
@ -58,6 +58,8 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
|||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int sdhci_runtime_pm_get(struct sdhci_host *host);
|
||||
static int sdhci_runtime_pm_put(struct sdhci_host *host);
|
||||
static void sdhci_runtime_pm_bus_on(struct sdhci_host *host);
|
||||
static void sdhci_runtime_pm_bus_off(struct sdhci_host *host);
|
||||
#else
|
||||
static inline int sdhci_runtime_pm_get(struct sdhci_host *host)
|
||||
{
|
||||
|
@ -67,6 +69,12 @@ static inline int sdhci_runtime_pm_put(struct sdhci_host *host)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
|
||||
{
|
||||
}
|
||||
static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void sdhci_dumpregs(struct sdhci_host *host)
|
||||
|
@ -192,8 +200,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
|
|||
|
||||
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
|
||||
|
||||
if (mask & SDHCI_RESET_ALL)
|
||||
if (mask & SDHCI_RESET_ALL) {
|
||||
host->clock = 0;
|
||||
/* Reset-all turns off SD Bus Power */
|
||||
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
||||
sdhci_runtime_pm_bus_off(host);
|
||||
}
|
||||
|
||||
/* Wait max 100 ms */
|
||||
timeout = 100;
|
||||
|
@ -1268,6 +1280,8 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|||
|
||||
if (pwr == 0) {
|
||||
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL);
|
||||
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
||||
sdhci_runtime_pm_bus_off(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1289,6 +1303,9 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
|
|||
|
||||
sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL);
|
||||
|
||||
if (host->quirks2 & SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON)
|
||||
sdhci_runtime_pm_bus_on(host);
|
||||
|
||||
/*
|
||||
* Some controllers need an extra 10ms delay of 10ms before they
|
||||
* can apply clock after applying power
|
||||
|
@ -1526,16 +1543,15 @@ 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_MMC_HS200)
|
||||
ctrl_2 |= SDHCI_CTRL_HS_SDR200;
|
||||
if ((ios->timing == MMC_TIMING_MMC_HS200) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR104))
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
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;
|
||||
else if (ios->timing == MMC_TIMING_UHS_SDR50)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
||||
else if (ios->timing == MMC_TIMING_UHS_SDR104)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
else if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
|
@ -1846,7 +1862,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
*/
|
||||
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
|
||||
(host->flags & SDHCI_SDR50_NEEDS_TUNING ||
|
||||
host->flags & SDHCI_HS200_NEEDS_TUNING))
|
||||
host->flags & SDHCI_SDR104_NEEDS_TUNING))
|
||||
requires_tuning_nonuhs = true;
|
||||
|
||||
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) ||
|
||||
|
@ -2046,11 +2062,14 @@ static void sdhci_card_event(struct mmc_host *mmc)
|
|||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
|
||||
/* First check if client has provided their own card event */
|
||||
if (host->ops->card_event)
|
||||
host->ops->card_event(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* Check host->mrq first in case we are runtime suspended */
|
||||
if (host->mrq &&
|
||||
!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
|
||||
if (host->mrq && !sdhci_do_get_cd(host)) {
|
||||
pr_err("%s: Card removed during transfer!\n",
|
||||
mmc_hostname(host->mmc));
|
||||
pr_err("%s: Resetting controller.\n",
|
||||
|
@ -2625,6 +2644,22 @@ static int sdhci_runtime_pm_put(struct sdhci_host *host)
|
|||
return pm_runtime_put_autosuspend(host->mmc->parent);
|
||||
}
|
||||
|
||||
static void sdhci_runtime_pm_bus_on(struct sdhci_host *host)
|
||||
{
|
||||
if (host->runtime_suspended || host->bus_on)
|
||||
return;
|
||||
host->bus_on = true;
|
||||
pm_runtime_get_noresume(host->mmc->parent);
|
||||
}
|
||||
|
||||
static void sdhci_runtime_pm_bus_off(struct sdhci_host *host)
|
||||
{
|
||||
if (host->runtime_suspended || !host->bus_on)
|
||||
return;
|
||||
host->bus_on = false;
|
||||
pm_runtime_put_noidle(host->mmc->parent);
|
||||
}
|
||||
|
||||
int sdhci_runtime_suspend_host(struct sdhci_host *host)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -2962,9 +2997,13 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
||||
|
||||
/* SDR104 supports also implies SDR50 support */
|
||||
if (caps[1] & SDHCI_SUPPORT_SDR104)
|
||||
if (caps[1] & SDHCI_SUPPORT_SDR104) {
|
||||
mmc->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50;
|
||||
else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
|
||||
* field can be promoted to support HS200.
|
||||
*/
|
||||
mmc->caps2 |= MMC_CAP2_HS200;
|
||||
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
|
||||
mmc->caps |= MMC_CAP_UHS_SDR50;
|
||||
|
||||
if (caps[1] & SDHCI_SUPPORT_DDR50)
|
||||
|
@ -2974,9 +3013,9 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
if (caps[1] & SDHCI_USE_SDR50_TUNING)
|
||||
host->flags |= SDHCI_SDR50_NEEDS_TUNING;
|
||||
|
||||
/* Does the host need tuning for HS200? */
|
||||
/* Does the host need tuning for SDR104 / HS200? */
|
||||
if (mmc->caps2 & MMC_CAP2_HS200)
|
||||
host->flags |= SDHCI_HS200_NEEDS_TUNING;
|
||||
host->flags |= SDHCI_SDR104_NEEDS_TUNING;
|
||||
|
||||
/* Driver Type(s) (A, C, D) supported by the host */
|
||||
if (caps[1] & SDHCI_DRIVER_TYPE_A)
|
||||
|
|
|
@ -294,6 +294,7 @@ struct sdhci_ops {
|
|||
void (*platform_resume)(struct sdhci_host *host);
|
||||
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
||||
void (*platform_init)(struct sdhci_host *host);
|
||||
void (*card_event)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
|
|
@ -1244,7 +1244,8 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
|
|||
u32 state;
|
||||
|
||||
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT,
|
||||
~(state & sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK)));
|
||||
sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
|
||||
|
||||
if (state & ~MASK_CLEAN)
|
||||
|
@ -1369,7 +1370,11 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
ret = -ENOMEM;
|
||||
goto ealloch;
|
||||
}
|
||||
mmc_of_parse(mmc);
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto eofparse;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->addr = reg;
|
||||
|
@ -1464,6 +1469,7 @@ eclkupdate:
|
|||
clk_put(host->hclk);
|
||||
eclkget:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
eofparse:
|
||||
mmc_free_host(mmc);
|
||||
ealloch:
|
||||
iounmap(reg);
|
||||
|
@ -1501,8 +1507,6 @@ static int sh_mmcif_remove(struct platform_device *pdev)
|
|||
if (irq[1] >= 0)
|
||||
free_irq(irq[1], host);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
clk_disable(host->hclk);
|
||||
mmc_free_host(host->mmc);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
|
|
@ -46,14 +46,12 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
|
|||
struct sh_mobile_sdhi {
|
||||
struct clk *clk;
|
||||
struct tmio_mmc_data mmc_data;
|
||||
struct sh_dmae_slave param_tx;
|
||||
struct sh_dmae_slave param_rx;
|
||||
struct tmio_mmc_dma dma_priv;
|
||||
};
|
||||
|
||||
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
int ret = clk_enable(priv->clk);
|
||||
|
@ -66,7 +64,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int
|
|||
|
||||
static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
|
||||
clk_disable(priv->clk);
|
||||
|
@ -121,7 +119,7 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
|
|||
|
||||
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
|
||||
{
|
||||
mmc_detect_change(dev_get_drvdata(&pdev->dev), msecs_to_jiffies(100));
|
||||
mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
|
||||
}
|
||||
|
||||
static const struct sh_mobile_sdhi_ops sdhi_ops = {
|
||||
|
@ -146,6 +144,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
struct tmio_mmc_host *host;
|
||||
int irq, ret, i = 0;
|
||||
bool multiplexed_isr = true;
|
||||
struct tmio_mmc_dma *dma_priv;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
|
||||
if (priv == NULL) {
|
||||
|
@ -154,6 +153,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
mmc_data = &priv->mmc_data;
|
||||
dma_priv = &priv->dma_priv;
|
||||
|
||||
if (p) {
|
||||
if (p->init) {
|
||||
|
@ -186,15 +186,23 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
mmc_data->get_cd = sh_mobile_sdhi_get_cd;
|
||||
|
||||
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
|
||||
priv->param_tx.shdma_slave.slave_id = p->dma_slave_tx;
|
||||
priv->param_rx.shdma_slave.slave_id = p->dma_slave_rx;
|
||||
priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave;
|
||||
priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave;
|
||||
priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
|
||||
mmc_data->dma = &priv->dma_priv;
|
||||
/*
|
||||
* Yes, we have to provide slave IDs twice to TMIO:
|
||||
* once as a filter parameter and once for channel
|
||||
* configuration as an explicit slave ID
|
||||
*/
|
||||
dma_priv->chan_priv_tx = (void *)p->dma_slave_tx;
|
||||
dma_priv->chan_priv_rx = (void *)p->dma_slave_rx;
|
||||
dma_priv->slave_id_tx = p->dma_slave_tx;
|
||||
dma_priv->slave_id_rx = p->dma_slave_rx;
|
||||
}
|
||||
}
|
||||
|
||||
dma_priv->alignment_shift = 1; /* 2-byte alignment */
|
||||
dma_priv->filter = shdma_chan_filter;
|
||||
|
||||
mmc_data->dma = dma_priv;
|
||||
|
||||
/*
|
||||
* All SDHI blocks support 2-byte and larger block sizes in 4-bit
|
||||
* bus width mode.
|
||||
|
@ -265,8 +273,10 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* There must be at least one IRQ source */
|
||||
if (!i)
|
||||
if (!i) {
|
||||
ret = irq;
|
||||
goto eirq;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",
|
||||
|
|
|
@ -112,8 +112,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
|
|||
const struct mfd_cell *cell = mfd_get_cell(pdev);
|
||||
struct mmc_host *mmc = platform_get_drvdata(pdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
free_irq(platform_get_irq(pdev, 0), host);
|
||||
|
|
|
@ -40,6 +40,22 @@
|
|||
|
||||
struct tmio_mmc_data;
|
||||
|
||||
/*
|
||||
* We differentiate between the following 3 power states:
|
||||
* 1. card slot powered off, controller stopped. This is used, when either there
|
||||
* is no card in the slot, or the card really has to be powered down.
|
||||
* 2. card slot powered on, controller stopped. This is used, when a card is in
|
||||
* the slot, but no activity is currently taking place. This is a power-
|
||||
* saving mode with card-state preserved. This state can be entered, e.g.
|
||||
* when MMC clock-gating is used.
|
||||
* 3. card slot powered on, controller running. This is the actual active state.
|
||||
*/
|
||||
enum tmio_mmc_power {
|
||||
TMIO_MMC_OFF_STOP, /* card power off, controller stopped */
|
||||
TMIO_MMC_ON_STOP, /* card power on, controller stopped */
|
||||
TMIO_MMC_ON_RUN, /* card power on, controller running */
|
||||
};
|
||||
|
||||
struct tmio_mmc_host {
|
||||
void __iomem *ctl;
|
||||
unsigned long bus_shift;
|
||||
|
@ -48,8 +64,8 @@ struct tmio_mmc_host {
|
|||
struct mmc_data *data;
|
||||
struct mmc_host *mmc;
|
||||
|
||||
/* Controller power state */
|
||||
bool power;
|
||||
/* Controller and card power state */
|
||||
enum tmio_mmc_power power;
|
||||
|
||||
/* Callbacks for clock / power control */
|
||||
void (*set_pwr)(struct platform_device *host, int state);
|
||||
|
@ -85,6 +101,7 @@ struct tmio_mmc_host {
|
|||
unsigned long last_req_ts;
|
||||
struct mutex ios_lock; /* protect set_ios() context */
|
||||
bool native_hotplug;
|
||||
bool resuming;
|
||||
};
|
||||
|
||||
int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
||||
|
|
|
@ -261,42 +261,62 @@ out:
|
|||
spin_unlock_irq(&host->lock);
|
||||
}
|
||||
|
||||
/* It might be necessary to make filter MFD specific */
|
||||
static bool tmio_mmc_filter(struct dma_chan *chan, void *arg)
|
||||
{
|
||||
dev_dbg(chan->device->dev, "%s: slave data %p\n", __func__, arg);
|
||||
chan->private = arg;
|
||||
return true;
|
||||
}
|
||||
|
||||
void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata)
|
||||
{
|
||||
/* We can only either use DMA for both Tx and Rx or not use it at all */
|
||||
if (!pdata->dma)
|
||||
if (!pdata->dma || (!host->pdev->dev.of_node &&
|
||||
(!pdata->dma->chan_priv_tx || !pdata->dma->chan_priv_rx)))
|
||||
return;
|
||||
|
||||
if (!host->chan_tx && !host->chan_rx) {
|
||||
struct resource *res = platform_get_resource(host->pdev,
|
||||
IORESOURCE_MEM, 0);
|
||||
struct dma_slave_config cfg = {};
|
||||
dma_cap_mask_t mask;
|
||||
int ret;
|
||||
|
||||
if (!res)
|
||||
return;
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->chan_tx = dma_request_channel(mask, tmio_mmc_filter,
|
||||
pdata->dma->chan_priv_tx);
|
||||
host->chan_tx = dma_request_slave_channel_compat(mask,
|
||||
pdata->dma->filter, pdata->dma->chan_priv_tx,
|
||||
&host->pdev->dev, "tx");
|
||||
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
|
||||
host->chan_tx);
|
||||
|
||||
if (!host->chan_tx)
|
||||
return;
|
||||
|
||||
host->chan_rx = dma_request_channel(mask, tmio_mmc_filter,
|
||||
pdata->dma->chan_priv_rx);
|
||||
if (pdata->dma->chan_priv_tx)
|
||||
cfg.slave_id = pdata->dma->slave_id_tx;
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
|
||||
cfg.src_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_tx, &cfg);
|
||||
if (ret < 0)
|
||||
goto ecfgtx;
|
||||
|
||||
host->chan_rx = dma_request_slave_channel_compat(mask,
|
||||
pdata->dma->filter, pdata->dma->chan_priv_rx,
|
||||
&host->pdev->dev, "rx");
|
||||
dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
|
||||
host->chan_rx);
|
||||
|
||||
if (!host->chan_rx)
|
||||
goto ereqrx;
|
||||
|
||||
if (pdata->dma->chan_priv_rx)
|
||||
cfg.slave_id = pdata->dma->slave_id_rx;
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = cfg.dst_addr;
|
||||
cfg.dst_addr = 0;
|
||||
ret = dmaengine_slave_config(host->chan_rx, &cfg);
|
||||
if (ret < 0)
|
||||
goto ecfgrx;
|
||||
|
||||
host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||
if (!host->bounce_buf)
|
||||
goto ebouncebuf;
|
||||
|
@ -310,9 +330,11 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
|
|||
return;
|
||||
|
||||
ebouncebuf:
|
||||
ecfgrx:
|
||||
dma_release_channel(host->chan_rx);
|
||||
host->chan_rx = NULL;
|
||||
ereqrx:
|
||||
ecfgtx:
|
||||
dma_release_channel(host->chan_tx);
|
||||
host->chan_tx = NULL;
|
||||
}
|
||||
|
|
|
@ -859,32 +859,45 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
* is kept positive, so no suspending actually takes place.
|
||||
*/
|
||||
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
|
||||
if (!host->power) {
|
||||
if (host->power != TMIO_MMC_ON_RUN) {
|
||||
tmio_mmc_clk_update(mmc);
|
||||
pm_runtime_get_sync(dev);
|
||||
if (host->resuming) {
|
||||
tmio_mmc_reset(host);
|
||||
host->resuming = false;
|
||||
}
|
||||
}
|
||||
if (host->power == TMIO_MMC_OFF_STOP)
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_set_clock(host, ios->clock);
|
||||
if (!host->power) {
|
||||
if (host->power == TMIO_MMC_OFF_STOP)
|
||||
/* power up SD card and the bus */
|
||||
tmio_mmc_power_on(host, ios->vdd);
|
||||
host->power = true;
|
||||
}
|
||||
host->power = TMIO_MMC_ON_RUN;
|
||||
/* start bus clock */
|
||||
tmio_mmc_clk_start(host);
|
||||
} else if (ios->power_mode != MMC_POWER_UP) {
|
||||
if (host->power) {
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
if (ios->power_mode == MMC_POWER_OFF)
|
||||
struct tmio_mmc_data *pdata = host->pdata;
|
||||
unsigned int old_power = host->power;
|
||||
|
||||
if (old_power != TMIO_MMC_OFF_STOP) {
|
||||
if (ios->power_mode == MMC_POWER_OFF) {
|
||||
tmio_mmc_power_off(host);
|
||||
host->power = TMIO_MMC_OFF_STOP;
|
||||
} else {
|
||||
host->power = TMIO_MMC_ON_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
if (old_power == TMIO_MMC_ON_RUN) {
|
||||
tmio_mmc_clk_stop(host);
|
||||
host->power = false;
|
||||
pm_runtime_put(dev);
|
||||
if (pdata->clk_disable)
|
||||
pdata->clk_disable(host->pdev);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->power) {
|
||||
if (host->power != TMIO_MMC_OFF_STOP) {
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
|
||||
|
@ -988,7 +1001,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
mmc_of_parse(mmc);
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto host_free;
|
||||
|
||||
pdata->dev = &pdev->dev;
|
||||
_host = mmc_priv(mmc);
|
||||
|
@ -1025,7 +1040,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
|
|||
mmc->caps & MMC_CAP_NONREMOVABLE ||
|
||||
mmc->slot.cd_irq >= 0);
|
||||
|
||||
_host->power = false;
|
||||
_host->power = TMIO_MMC_OFF_STOP;
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_resume(&pdev->dev);
|
||||
if (ret < 0)
|
||||
|
@ -1154,10 +1169,10 @@ int tmio_mmc_host_resume(struct device *dev)
|
|||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
|
||||
/* The MMC core will perform the complete set up */
|
||||
host->resuming = true;
|
||||
return mmc_resume_host(mmc);
|
||||
}
|
||||
EXPORT_SYMBOL(tmio_mmc_host_resume);
|
||||
|
@ -1175,7 +1190,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
|||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_enable_dma(host, true);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -927,8 +927,6 @@ static int wmt_mci_remove(struct platform_device *pdev)
|
|||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
dev_info(&pdev->dev, "WMT MCI device removed\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -81,10 +81,15 @@ int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
|
|||
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
|
||||
void tmio_core_mmc_clk_div(void __iomem *cnf, int shift, int state);
|
||||
|
||||
struct dma_chan;
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
void *chan_priv_tx;
|
||||
void *chan_priv_rx;
|
||||
int slave_id_tx;
|
||||
int slave_id_rx;
|
||||
int alignment_shift;
|
||||
bool (*filter)(struct dma_chan *chan, void *arg);
|
||||
};
|
||||
|
||||
struct tmio_mmc_host;
|
||||
|
|
|
@ -94,7 +94,11 @@ struct mmc_ext_csd {
|
|||
u8 raw_ext_csd_structure; /* 194 */
|
||||
u8 raw_card_type; /* 196 */
|
||||
u8 out_of_int_time; /* 198 */
|
||||
u8 raw_s_a_timeout; /* 217 */
|
||||
u8 raw_pwr_cl_52_195; /* 200 */
|
||||
u8 raw_pwr_cl_26_195; /* 201 */
|
||||
u8 raw_pwr_cl_52_360; /* 202 */
|
||||
u8 raw_pwr_cl_26_360; /* 203 */
|
||||
u8 raw_s_a_timeout; /* 217 */
|
||||
u8 raw_hc_erase_gap_size; /* 221 */
|
||||
u8 raw_erase_timeout_mult; /* 223 */
|
||||
u8 raw_hc_erase_grp_size; /* 224 */
|
||||
|
@ -102,6 +106,10 @@ struct mmc_ext_csd {
|
|||
u8 raw_sec_erase_mult; /* 230 */
|
||||
u8 raw_sec_feature_support;/* 231 */
|
||||
u8 raw_trim_mult; /* 232 */
|
||||
u8 raw_pwr_cl_200_195; /* 236 */
|
||||
u8 raw_pwr_cl_200_360; /* 237 */
|
||||
u8 raw_pwr_cl_ddr_52_195; /* 238 */
|
||||
u8 raw_pwr_cl_ddr_52_360; /* 239 */
|
||||
u8 raw_bkops_status; /* 246 */
|
||||
u8 raw_sectors[4]; /* 212 - 4 bytes */
|
||||
|
||||
|
@ -512,6 +520,7 @@ struct mmc_driver {
|
|||
void (*remove)(struct mmc_card *);
|
||||
int (*suspend)(struct mmc_card *);
|
||||
int (*resume)(struct mmc_card *);
|
||||
void (*shutdown)(struct mmc_card *);
|
||||
};
|
||||
|
||||
extern int mmc_register_driver(struct mmc_driver *);
|
||||
|
|
|
@ -96,6 +96,8 @@ struct mmc_command {
|
|||
*/
|
||||
|
||||
unsigned int cmd_timeout_ms; /* in milliseconds */
|
||||
/* Set this flag only for blocking sanitize request */
|
||||
bool sanitize_busy;
|
||||
|
||||
struct mmc_data *data; /* data segment associated with cmd */
|
||||
struct mmc_request *mrq; /* associated request */
|
||||
|
@ -188,6 +190,9 @@ extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
|
|||
extern void mmc_release_host(struct mmc_host *host);
|
||||
extern int mmc_try_claim_host(struct mmc_host *host);
|
||||
|
||||
extern void mmc_get_card(struct mmc_card *card);
|
||||
extern void mmc_put_card(struct mmc_card *card);
|
||||
|
||||
extern int mmc_flush_cache(struct mmc_card *);
|
||||
|
||||
extern int mmc_detect_card_removed(struct mmc_host *host);
|
||||
|
|
|
@ -239,7 +239,7 @@ struct mmc_host {
|
|||
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
|
||||
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
|
||||
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
|
||||
|
||||
#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */
|
||||
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
|
||||
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
|
||||
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
|
||||
|
@ -264,7 +264,7 @@ struct mmc_host {
|
|||
|
||||
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
|
||||
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */
|
||||
#define MMC_CAP2_POWEROFF_NOTIFY (1 << 2) /* Notify poweroff supported */
|
||||
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
|
||||
#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 */
|
||||
|
@ -272,7 +272,6 @@ struct mmc_host {
|
|||
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
|
||||
MMC_CAP2_HS200_1_2V_SDR)
|
||||
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */
|
||||
#define MMC_CAP2_DETECT_ON_ERR (1 << 8) /* On I/O err check card removal */
|
||||
#define MMC_CAP2_HC_ERASE_SZ (1 << 9) /* High-capacity erase size */
|
||||
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
|
||||
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect signal active high */
|
||||
|
@ -281,6 +280,7 @@ struct mmc_host {
|
|||
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
|
||||
MMC_CAP2_PACKED_WR)
|
||||
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
|
||||
#define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
|
@ -369,7 +369,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *);
|
|||
int mmc_add_host(struct mmc_host *);
|
||||
void mmc_remove_host(struct mmc_host *);
|
||||
void mmc_free_host(struct mmc_host *);
|
||||
void mmc_of_parse(struct mmc_host *host);
|
||||
int mmc_of_parse(struct mmc_host *host);
|
||||
|
||||
static inline void *mmc_priv(struct mmc_host *host)
|
||||
{
|
||||
|
@ -425,10 +425,6 @@ static inline int mmc_regulator_get_supply(struct mmc_host *mmc)
|
|||
}
|
||||
#endif
|
||||
|
||||
int mmc_card_awake(struct mmc_host *host);
|
||||
int mmc_card_sleep(struct mmc_host *host);
|
||||
int mmc_card_can_sleep(struct mmc_host *host);
|
||||
|
||||
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
|
||||
|
||||
/* Module parameter */
|
||||
|
|
|
@ -95,6 +95,9 @@ struct sdhci_host {
|
|||
/* The system physically doesn't support 1.8v, even if the host does */
|
||||
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
|
||||
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
|
||||
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
|
||||
/* Controller has a non-standard host control register */
|
||||
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
@ -126,7 +129,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 */
|
||||
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
|
||||
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
|
||||
|
||||
unsigned int version; /* SDHCI spec. version */
|
||||
|
@ -139,6 +142,7 @@ struct sdhci_host {
|
|||
u8 pwr; /* Current voltage */
|
||||
|
||||
bool runtime_suspended; /* Host is runtime suspended */
|
||||
bool bus_on; /* Bus power prevents runtime suspend */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
|
|
|
@ -40,5 +40,6 @@ struct esdhc_platform_data {
|
|||
enum wp_types wp_type;
|
||||
enum cd_types cd_type;
|
||||
int max_bus_width;
|
||||
unsigned int f_max;
|
||||
};
|
||||
#endif /* __ASM_ARCH_IMX_ESDHC_H */
|
||||
|
|
Loading…
Reference in New Issue