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:
Linus Torvalds 2013-07-10 11:16:00 -07:00
commit bfffbea1aa
73 changed files with 1811 additions and 794 deletions

View File

@ -28,6 +28,7 @@ Optional properties:
- cap-mmc-highspeed: MMC high-speed timing is supported - cap-mmc-highspeed: MMC high-speed timing is supported
- cap-power-off-card: powering off the card is safe - cap-power-off-card: powering off the card is safe
- cap-sdio-irq: enable SDIO IRQ signalling on this interface - 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 *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" polarity properties, we have to fix the meaning of the "normal" and "inverted"

View File

@ -0,0 +1,23 @@
* Rockchip specific extensions to the Synopsis Designware Mobile
Storage Host Controller
The Synopsis designware mobile storage host controller is used to interface
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
differences between the core Synopsis dw mshc controller properties described
by synopsis-dw-mshc.txt and the properties used by the Rockchip specific
extensions to the Synopsis Designware Mobile Storage Host Controller.
Required Properties:
* compatible: should be
- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following
Example:
rkdwmmc0@12200000 {
compatible = "rockchip,rk2928-dw-mshc";
reg = <0x12200000 0x1000>;
interrupts = <0 75 0>;
#address-cells = <1>;
#size-cells = <0>;
};

View File

@ -39,6 +39,19 @@ Required Properties:
Optional 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. * 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 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 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. * 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: Aliases:
- All the MSHC controller nodes should be represented in the aliases node using - 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 { dwmmc0@12200000 {
compatible = "snps,dw-mshc"; compatible = "snps,dw-mshc";
clocks = <&clock 351>, <&clock 132>;
clock-names = "biu", "ciu";
reg = <0x12200000 0x1000>; reg = <0x12200000 0x1000>;
interrupts = <0 75 0>; interrupts = <0 75 0>;
#address-cells = <1>; #address-cells = <1>;
@ -74,11 +92,13 @@ board specific portions as listed below.
}; };
dwmmc0@12200000 { dwmmc0@12200000 {
clock-frequency = <400000000>;
num-slots = <1>; num-slots = <1>;
supports-highspeed; supports-highspeed;
broken-cd; broken-cd;
fifo-depth = <0x80>; fifo-depth = <0x80>;
card-detect-delay = <200>; card-detect-delay = <200>;
vmmc-supply = <&buck8>;
slot@0 { slot@0 {
reg = <0>; reg = <0>;

View File

@ -78,6 +78,13 @@ CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y CONFIG_LCD_CLASS_DEVICE=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y CONFIG_BACKLIGHT_CLASS_DEVICE=y
# CONFIG_USB_SUPPORT is not set # 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_NEW_LEDS=y
CONFIG_LEDS_CLASS=y CONFIG_LEDS_CLASS=y
CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGERS=y

View File

@ -34,6 +34,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/ioctl.h> #include <linux/mmc/ioctl.h>
#include <linux/mmc/card.h> #include <linux/mmc/card.h>
@ -58,6 +59,8 @@ MODULE_ALIAS("mmc:block");
#define INAND_CMD38_ARG_SECTRIM1 0x81 #define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88 #define INAND_CMD38_ARG_SECTRIM2 0x88
#define MMC_BLK_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ #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) || \ #define mmc_req_rel_wr(req) (((req->cmd_flags & REQ_FUA) || \
(req->cmd_flags & REQ_META)) && \ (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)); md = mmc_blk_get(dev_to_disk(dev));
card = md->queue.card; 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, ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BOOT_WP,
card->ext_csd.boot_ro_lock | card->ext_csd.boot_ro_lock |
@ -233,7 +236,7 @@ static ssize_t power_ro_lock_store(struct device *dev,
else else
card->ext_csd.boot_ro_lock |= EXT_CSD_BOOT_WP_B_PWR_WP_EN; 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) { if (!ret) {
pr_info("%s: Locking boot partition ro until next power on\n", 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; 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, static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_ioc_cmd __user *ic_ptr) struct mmc_ioc_cmd __user *ic_ptr)
{ {
@ -491,7 +523,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
mrq.cmd = &cmd; mrq.cmd = &cmd;
mmc_claim_host(card->host); mmc_get_card(card);
err = mmc_blk_part_switch(card, md); err = mmc_blk_part_switch(card, md);
if (err) if (err)
@ -510,6 +542,17 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
goto cmd_rel_host; 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); mmc_wait_for_req(card->host, &mrq);
if (cmd.error) { if (cmd.error) {
@ -558,7 +601,7 @@ static int mmc_blk_ioctl_cmd(struct block_device *bdev,
} }
cmd_rel_host: cmd_rel_host:
mmc_release_host(card->host); mmc_put_card(card);
cmd_done: cmd_done:
mmc_blk_put(md); 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_blk_data *md = mq->data;
struct mmc_card *card = md->queue.card; 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; 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; err = -EOPNOTSUPP;
goto out; goto out;
} }
@ -950,23 +993,11 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
from = blk_rq_pos(req); from = blk_rq_pos(req);
nr = blk_rq_sectors(req); nr = blk_rq_sectors(req);
/* The sanitize operation is supported at v4.5 only */ if (mmc_can_trim(card) && !mmc_erase_group_aligned(card, from, nr))
if (mmc_can_sanitize(card)) { arg = MMC_SECURE_TRIM1_ARG;
erase_arg = MMC_ERASE_ARG; else
trim_arg = MMC_TRIM_ARG; arg = MMC_SECURE_ERASE_ARG;
} else {
erase_arg = MMC_SECURE_ERASE_ARG;
trim_arg = MMC_SECURE_TRIM1_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: retry:
if (card->quirks & MMC_QUIRK_INAND_CMD38) { if (card->quirks & MMC_QUIRK_INAND_CMD38) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
@ -1002,9 +1033,6 @@ retry:
goto out; goto out;
} }
if (mmc_can_sanitize(card))
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_SANITIZE_START, 1, 0);
out_retry: out_retry:
if (err && !mmc_blk_reset(md, card->host, type)) if (err && !mmc_blk_reset(md, card->host, type))
goto retry; 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) if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */ /* claim host only for the first request */
mmc_claim_host(card->host); mmc_get_card(card);
ret = mmc_blk_part_switch(card, md); ret = mmc_blk_part_switch(card, md);
if (ret) { if (ret) {
@ -1939,7 +1967,7 @@ out:
* In case sepecial request, there is no reentry to * In case sepecial request, there is no reentry to
* the 'mmc_blk_issue_rq' with 'mqrq_prev->req'. * the 'mmc_blk_issue_rq' with 'mqrq_prev->req'.
*/ */
mmc_release_host(card->host); mmc_put_card(card);
return ret; return ret;
} }
@ -2158,6 +2186,14 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md)
struct mmc_card *card; struct mmc_card *card;
if (md) { 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; card = md->queue.card;
if (md->disk->flags & GENHD_FL_UP) { if (md->disk->flags & GENHD_FL_UP) {
device_remove_file(disk_to_dev(md->disk), &md->force_ro); 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), device_remove_file(disk_to_dev(md->disk),
&md->power_ro_lock); &md->power_ro_lock);
/* Stop new requests from getting into the queue */
del_gendisk(md->disk); 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); mmc_blk_put(md);
} }
} }
@ -2336,6 +2366,19 @@ static int mmc_blk_probe(struct mmc_card *card)
if (mmc_add_disk(part_md)) if (mmc_add_disk(part_md))
goto out; 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; return 0;
out: out:
@ -2349,20 +2392,24 @@ static void mmc_blk_remove(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card); struct mmc_blk_data *md = mmc_get_drvdata(card);
mmc_blk_remove_parts(card, md); mmc_blk_remove_parts(card, md);
pm_runtime_get_sync(&card->dev);
mmc_claim_host(card->host); mmc_claim_host(card->host);
mmc_blk_part_switch(card, md); mmc_blk_part_switch(card, md);
mmc_release_host(card->host); 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_blk_remove_req(md);
mmc_set_drvdata(card, NULL); 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 *part_md;
struct mmc_blk_data *md = mmc_get_drvdata(card); struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) { if (md) {
pm_runtime_get_sync(&card->dev);
mmc_queue_suspend(&md->queue); mmc_queue_suspend(&md->queue);
list_for_each_entry(part_md, &md->part, part) { list_for_each_entry(part_md, &md->part, part) {
mmc_queue_suspend(&part_md->queue); mmc_queue_suspend(&part_md->queue);
@ -2371,6 +2418,17 @@ static int mmc_blk_suspend(struct mmc_card *card)
return 0; 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) static int mmc_blk_resume(struct mmc_card *card)
{ {
struct mmc_blk_data *part_md; 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) { list_for_each_entry(part_md, &md->part, part) {
mmc_queue_resume(&part_md->queue); mmc_queue_resume(&part_md->queue);
} }
pm_runtime_put(&card->dev);
} }
return 0; return 0;
} }
@ -2402,6 +2461,7 @@ static struct mmc_driver mmc_driver = {
.remove = mmc_blk_remove, .remove = mmc_blk_remove,
.suspend = mmc_blk_suspend, .suspend = mmc_blk_suspend,
.resume = mmc_blk_resume, .resume = mmc_blk_resume,
.shutdown = mmc_blk_shutdown,
}; };
static int __init mmc_blk_init(void) static int __init mmc_blk_init(void)

View File

@ -3025,12 +3025,17 @@ static void mmc_test_remove(struct mmc_card *card)
mmc_test_free_dbgfs_file(card); mmc_test_free_dbgfs_file(card);
} }
static void mmc_test_shutdown(struct mmc_card *card)
{
}
static struct mmc_driver mmc_driver = { static struct mmc_driver mmc_driver = {
.drv = { .drv = {
.name = "mmc_test", .name = "mmc_test",
}, },
.probe = mmc_test_probe, .probe = mmc_test_probe,
.remove = mmc_test_remove, .remove = mmc_test_remove,
.shutdown = mmc_test_shutdown,
}; };
static int __init mmc_test_init(void) static int __init mmc_test_init(void)

View File

@ -173,7 +173,7 @@ static void mmc_queue_setup_discard(struct request_queue *q,
/* granularity must not be greater than max. discard */ /* granularity must not be greater than max. discard */
if (card->pref_erase > max_discard) if (card->pref_erase > max_discard)
q->limits.discard_granularity = 0; 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); queue_flag_set_unlocked(QUEUE_FLAG_SECDISCARD, q);
} }

View File

@ -122,15 +122,39 @@ static int mmc_bus_remove(struct device *dev)
return 0; 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 #ifdef CONFIG_PM_SLEEP
static int mmc_bus_suspend(struct device *dev) static int mmc_bus_suspend(struct device *dev)
{ {
struct mmc_driver *drv = to_mmc_driver(dev->driver); struct mmc_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev); 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); ret = drv->suspend(card);
if (ret)
return ret;
}
ret = host->bus_ops->suspend(host);
return ret; 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_driver *drv = to_mmc_driver(dev->driver);
struct mmc_card *card = mmc_dev_to_card(dev); 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) if (dev->driver && drv->resume)
ret = drv->resume(card); ret = drv->resume(card);
return ret; return ret;
} }
#endif #endif
@ -151,15 +182,25 @@ static int mmc_bus_resume(struct device *dev)
static int mmc_runtime_suspend(struct device *dev) static int mmc_runtime_suspend(struct device *dev)
{ {
struct mmc_card *card = mmc_dev_to_card(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) static int mmc_runtime_resume(struct device *dev)
{ {
struct mmc_card *card = mmc_dev_to_card(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) static int mmc_runtime_idle(struct device *dev)
@ -182,6 +223,7 @@ static struct bus_type mmc_bus_type = {
.uevent = mmc_bus_uevent, .uevent = mmc_bus_uevent,
.probe = mmc_bus_probe, .probe = mmc_bus_probe,
.remove = mmc_bus_remove, .remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops, .pm = &mmc_bus_pm_ops,
}; };

View File

@ -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_done_rcv = false;
context_info->is_new_req = false; context_info->is_new_req = false;
cmd = mrq->cmd; cmd = mrq->cmd;
if (!cmd->error || !cmd->retries || if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) { mmc_card_removed(host->card)) {
err = host->areq->err_check(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); wait_for_completion(&mrq->completion);
cmd = mrq->cmd; 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 || if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) mmc_card_removed(host->card))
break; break;
@ -951,6 +970,29 @@ void mmc_release_host(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_release_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, * Internal function that does the actual ios call to the host driver,
* optionally printing some debug output. * 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 * If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage. * initial MMC_POWER_UP stage.
*/ */
static void mmc_power_up(struct mmc_host *host) void mmc_power_up(struct mmc_host *host)
{ {
int bit; 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 * The card will be considered unchanged unless we have been asked to
* detect a change or host requires polling to provide card detection. * detect a change or host requires polling to provide card detection.
*/ */
if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL) && if (!host->detect_change && !(host->caps & MMC_CAP_NEEDS_POLL))
!(host->caps2 & MMC_CAP2_DETECT_ON_ERR))
return ret; return ret;
host->detect_change = 0; host->detect_change = 0;
if (!ret) { if (!ret) {
ret = _mmc_detect_card_removed(host); 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 * Schedule a detect work as soon as possible to let a
* rescan handle the card removal. * rescan handle the card removal.
@ -2442,9 +2483,7 @@ void mmc_stop_host(struct mmc_host *host)
mmc_bus_get(host); mmc_bus_get(host);
if (host->bus_ops && !host->bus_dead) { if (host->bus_ops && !host->bus_dead) {
/* Calling bus_ops->remove() with a claimed host can deadlock */ /* 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_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host); mmc_power_off(host);
@ -2509,52 +2548,6 @@ int mmc_power_restore_host(struct mmc_host *host)
} }
EXPORT_SYMBOL(mmc_power_restore_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. * 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 mmc_suspend_host(struct mmc_host *host)
{ {
int err = 0; /* This function is deprecated */
return 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;
} }
EXPORT_SYMBOL(mmc_suspend_host); EXPORT_SYMBOL(mmc_suspend_host);
/** /**
@ -2676,39 +2630,8 @@ EXPORT_SYMBOL(mmc_suspend_host);
*/ */
int mmc_resume_host(struct mmc_host *host) int mmc_resume_host(struct mmc_host *host)
{ {
int err = 0; /* This function is deprecated */
return 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;
} }
EXPORT_SYMBOL(mmc_resume_host); EXPORT_SYMBOL(mmc_resume_host);
@ -2727,29 +2650,22 @@ int mmc_pm_notify(struct notifier_block *notify_block,
switch (mode) { switch (mode) {
case PM_HIBERNATION_PREPARE: case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_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); spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 1; host->rescan_disable = 1;
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
cancel_delayed_work_sync(&host->detect); 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; break;
/* Calling bus_ops->remove() with a claimed host can deadlock */ /* 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_claim_host(host);
mmc_detach_bus(host); mmc_detach_bus(host);
mmc_power_off(host); mmc_power_off(host);

View File

@ -16,15 +16,17 @@
#define MMC_CMD_RETRIES 3 #define MMC_CMD_RETRIES 3
struct mmc_bus_ops { struct mmc_bus_ops {
int (*awake)(struct mmc_host *);
int (*sleep)(struct mmc_host *);
void (*remove)(struct mmc_host *); void (*remove)(struct mmc_host *);
void (*detect)(struct mmc_host *); void (*detect)(struct mmc_host *);
int (*pre_suspend)(struct mmc_host *);
int (*suspend)(struct mmc_host *); int (*suspend)(struct mmc_host *);
int (*resume)(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_save)(struct mmc_host *);
int (*power_restore)(struct mmc_host *); int (*power_restore)(struct mmc_host *);
int (*alive)(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); 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); 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_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); 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_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host);

View File

@ -258,13 +258,13 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
u32 status; u32 status;
int ret; int ret;
mmc_claim_host(card->host); mmc_get_card(card);
ret = mmc_send_status(data, &status); ret = mmc_send_status(data, &status);
if (!ret) if (!ret)
*val = status; *val = status;
mmc_release_host(card->host); mmc_put_card(card);
return ret; return ret;
} }
@ -291,9 +291,9 @@ static int mmc_ext_csd_open(struct inode *inode, struct file *filp)
goto out_free; goto out_free;
} }
mmc_claim_host(card->host); mmc_get_card(card);
err = mmc_send_ext_csd(card, ext_csd); err = mmc_send_ext_csd(card, ext_csd);
mmc_release_host(card->host); mmc_put_card(card);
if (err) if (err)
goto out_free; goto out_free;

View File

@ -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 * parse the properties and set respective generic mmc-host flags and
* parameters. * parameters.
*/ */
void mmc_of_parse(struct mmc_host *host) int mmc_of_parse(struct mmc_host *host)
{ {
struct device_node *np; struct device_node *np;
u32 bus_width; u32 bus_width;
@ -315,7 +315,7 @@ void mmc_of_parse(struct mmc_host *host)
int len, ret, gpio; int len, ret, gpio;
if (!host->parent || !host->parent->of_node) if (!host->parent || !host->parent->of_node)
return; return 0;
np = host->parent->of_node; np = host->parent->of_node;
@ -338,6 +338,7 @@ void mmc_of_parse(struct mmc_host *host)
default: default:
dev_err(host->parent, dev_err(host->parent,
"Invalid \"bus-width\" value %ud!\n", bus_width); "Invalid \"bus-width\" value %ud!\n", bus_width);
return -EINVAL;
} }
/* f_max is obtained from the optional "max-frequency" property */ /* 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; host->caps |= MMC_CAP_NEEDS_POLL;
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
if (gpio == -EPROBE_DEFER)
return gpio;
if (gpio_is_valid(gpio)) { if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW)) if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_cd = true; gpio_inv_cd = true;
ret = mmc_gpio_request_cd(host, gpio); ret = mmc_gpio_request_cd(host, gpio);
if (ret < 0) if (ret < 0) {
dev_err(host->parent, dev_err(host->parent,
"Failed to request CD GPIO #%d: %d!\n", "Failed to request CD GPIO #%d: %d!\n",
gpio, ret); gpio, ret);
else return ret;
} else {
dev_info(host->parent, "Got CD GPIO #%d.\n", dev_info(host->parent, "Got CD GPIO #%d.\n",
gpio); gpio);
}
} }
if (explicit_inv_cd ^ gpio_inv_cd) 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"); explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); 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 (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW)) if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_wp = true; gpio_inv_wp = true;
ret = mmc_gpio_request_ro(host, gpio); ret = mmc_gpio_request_ro(host, gpio);
if (ret < 0) if (ret < 0) {
dev_err(host->parent, dev_err(host->parent,
"Failed to request WP GPIO: %d!\n", ret); "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) if (explicit_inv_wp ^ gpio_inv_wp)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; 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; host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len)) if (of_find_property(np, "cap-sdio-irq", &len))
host->caps |= MMC_CAP_SDIO_IRQ; 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)) if (of_find_property(np, "keep-power-in-suspend", &len))
host->pm_caps |= MMC_PM_KEEP_POWER; host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len)) if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
return 0;
out:
mmc_gpio_free_cd(host);
return ret;
} }
EXPORT_SYMBOL(mmc_of_parse); EXPORT_SYMBOL(mmc_of_parse);

View File

@ -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]; 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", pr_err("%s: unrecognised EXT_CSD revision %d\n",
mmc_hostname(card->host), card->ext_csd.rev); mmc_hostname(card->host), card->ext_csd.rev);
err = -EINVAL; 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_lock = ext_csd[EXT_CSD_BOOT_WP];
card->ext_csd.boot_ro_lockable = true; 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) { 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 */ /* check whether the eMMC card supports BKOPS */
if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) { if (ext_csd[EXT_CSD_BKOPS_SUPPORT] & 0x1) {
card->ext_csd.bkops = 1; 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] == (card->ext_csd.raw_sectors[2] ==
bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && bw_ext_csd[EXT_CSD_SEC_CNT + 2]) &&
(card->ext_csd.raw_sectors[3] == (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) if (err)
err = -EINVAL; err = -EINVAL;
@ -676,11 +714,10 @@ static struct device_type mmc_type = {
* mmc_switch command. * mmc_switch command.
*/ */
static int mmc_select_powerclass(struct mmc_card *card, static int mmc_select_powerclass(struct mmc_card *card,
unsigned int bus_width, u8 *ext_csd) unsigned int bus_width)
{ {
int err = 0; int err = 0;
unsigned int pwrclass_val; unsigned int pwrclass_val = 0;
unsigned int index = 0;
struct mmc_host *host; struct mmc_host *host;
BUG_ON(!card); BUG_ON(!card);
@ -688,9 +725,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
host = card->host; host = card->host;
BUG_ON(!host); BUG_ON(!host);
if (ext_csd == NULL)
return 0;
/* Power class selection is supported for versions >= 4.0 */ /* Power class selection is supported for versions >= 4.0 */
if (card->csd.mmca_vsn < CSD_SPEC_VER_4) if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
return 0; return 0;
@ -702,13 +736,13 @@ static int mmc_select_powerclass(struct mmc_card *card,
switch (1 << host->ios.vdd) { switch (1 << host->ios.vdd) {
case MMC_VDD_165_195: case MMC_VDD_165_195:
if (host->ios.clock <= 26000000) 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) else if (host->ios.clock <= 52000000)
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
EXT_CSD_PWR_CL_52_195 : card->ext_csd.raw_pwr_cl_52_195 :
EXT_CSD_PWR_CL_DDR_52_195; card->ext_csd.raw_pwr_cl_ddr_52_195;
else if (host->ios.clock <= 200000000) else if (host->ios.clock <= 200000000)
index = EXT_CSD_PWR_CL_200_195; pwrclass_val = card->ext_csd.raw_pwr_cl_200_195;
break; break;
case MMC_VDD_27_28: case MMC_VDD_27_28:
case MMC_VDD_28_29: 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_34_35:
case MMC_VDD_35_36: case MMC_VDD_35_36:
if (host->ios.clock <= 26000000) 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) else if (host->ios.clock <= 52000000)
index = (bus_width <= EXT_CSD_BUS_WIDTH_8) ? pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
EXT_CSD_PWR_CL_52_360 : card->ext_csd.raw_pwr_cl_52_360 :
EXT_CSD_PWR_CL_DDR_52_360; card->ext_csd.raw_pwr_cl_ddr_52_360;
else if (host->ios.clock <= 200000000) else if (host->ios.clock <= 200000000)
index = EXT_CSD_PWR_CL_200_360; pwrclass_val = card->ext_csd.raw_pwr_cl_200_360;
break; break;
default: default:
pr_warning("%s: Voltage range not supported " pr_warning("%s: Voltage range not supported "
@ -734,8 +768,6 @@ static int mmc_select_powerclass(struct mmc_card *card,
return -EINVAL; return -EINVAL;
} }
pwrclass_val = ext_csd[index];
if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8)) if (bus_width & (EXT_CSD_BUS_WIDTH_8 | EXT_CSD_DDR_BUS_WIDTH_8))
pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >> pwrclass_val = (pwrclass_val & EXT_CSD_PWR_CL_8BIT_MASK) >>
EXT_CSD_PWR_CL_8BIT_SHIFT; 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 * Enable power_off_notification byte in the ext_csd register
* set the notification byte in the ext_csd register of device
*/ */
if ((host->caps2 & MMC_CAP2_POWEROFF_NOTIFY) && if (card->ext_csd.rev >= 6) {
(card->ext_csd.rev >= 6)) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION, EXT_CSD_POWER_OFF_NOTIFICATION,
EXT_CSD_POWER_ON, 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_bits = (bus_width == MMC_BUS_WIDTH_8) ?
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4; 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) if (err)
pr_warning("%s: power class selection to bus width %d" pr_warning("%s: power class selection to bus width %d"
" failed\n", mmc_hostname(card->host), " 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]; bus_width = bus_widths[idx];
if (bus_width == MMC_BUS_WIDTH_1) if (bus_width == MMC_BUS_WIDTH_1)
ddr = 0; /* no DDR for 1-bit width */ ddr = 0; /* no DDR for 1-bit width */
err = mmc_select_powerclass(card, ext_csd_bits[idx][0], err = mmc_select_powerclass(card, ext_csd_bits[idx][0]);
ext_csd);
if (err) if (err)
pr_warning("%s: power class selection to " pr_warning("%s: power class selection to "
"bus width %d failed\n", "bus width %d failed\n",
@ -1195,8 +1224,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
} }
if (!err && ddr) { if (!err && ddr) {
err = mmc_select_powerclass(card, ext_csd_bits[idx][1], err = mmc_select_powerclass(card, ext_csd_bits[idx][1]);
ext_csd);
if (err) if (err)
pr_warning("%s: power class selection to " pr_warning("%s: power class selection to "
"bus width %d ddr %d failed\n", "bus width %d ddr %d failed\n",
@ -1321,6 +1349,45 @@ err:
return 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) static int mmc_can_poweroff_notify(const struct mmc_card *card)
{ {
return card && return card &&
@ -1380,14 +1447,14 @@ static void mmc_detect(struct mmc_host *host)
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_get_card(host->card);
/* /*
* Just check if our card has been removed. * Just check if our card has been removed.
*/ */
err = _mmc_detect_card_removed(host); err = _mmc_detect_card_removed(host);
mmc_release_host(host); mmc_put_card(host->card);
if (err) { if (err) {
mmc_remove(host); mmc_remove(host);
@ -1399,35 +1466,59 @@ static void mmc_detect(struct mmc_host *host)
} }
} }
/* static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
* Suspend callback from host.
*/
static int mmc_suspend(struct mmc_host *host)
{ {
int err = 0; 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);
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); 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); err = mmc_cache_ctrl(host, 0);
if (err) if (err)
goto out; goto out;
if (mmc_can_poweroff_notify(host->card)) if (mmc_can_poweroff_notify(host->card) &&
err = mmc_poweroff_notify(host->card, EXT_CSD_POWER_OFF_SHORT); ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
else if (mmc_card_can_sleep(host)) err = mmc_poweroff_notify(host->card, notify_type);
err = mmc_card_sleep(host); else if (mmc_can_sleep(host->card))
err = mmc_sleep(host);
else if (!mmc_host_is_spi(host)) else if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host); err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
if (!err)
mmc_power_off(host);
out: out:
mmc_release_host(host); mmc_release_host(host);
return err; 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. * Resume callback from host.
* *
@ -1442,12 +1533,62 @@ static int mmc_resume(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
err = mmc_init_card(host, host->ocr, host->card); err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
return err; 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) static int mmc_power_restore(struct mmc_host *host)
{ {
int ret; int ret;
@ -1460,56 +1601,26 @@ static int mmc_power_restore(struct mmc_host *host)
return ret; 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 = { static const struct mmc_bus_ops mmc_ops = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove, .remove = mmc_remove,
.detect = mmc_detect, .detect = mmc_detect,
.suspend = NULL, .suspend = NULL,
.resume = NULL, .resume = NULL,
.power_restore = mmc_power_restore, .power_restore = mmc_power_restore,
.alive = mmc_alive, .alive = mmc_alive,
.shutdown = mmc_shutdown,
}; };
static const struct mmc_bus_ops mmc_ops_unsafe = { static const struct mmc_bus_ops mmc_ops_unsafe = {
.awake = mmc_awake,
.sleep = mmc_sleep,
.remove = mmc_remove, .remove = mmc_remove,
.detect = mmc_detect, .detect = mmc_detect,
.suspend = mmc_suspend, .suspend = mmc_suspend,
.resume = mmc_resume, .resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
.power_restore = mmc_power_restore, .power_restore = mmc_power_restore,
.alive = mmc_alive, .alive = mmc_alive,
.shutdown = mmc_shutdown,
}; };
static void mmc_attach_bus_ops(struct mmc_host *host) static void mmc_attach_bus_ops(struct mmc_host *host)

View File

@ -59,40 +59,6 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL); 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 mmc_go_idle(struct mmc_host *host)
{ {
int err; 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; 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); err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err) if (err)

View File

@ -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_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); 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_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_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);

View File

@ -646,8 +646,13 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card)
if (err) if (err)
goto out; 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); mmc_host_clk_hold(card->host);
err = card->host->ops->execute_tuning(card->host, err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK); MMC_SEND_TUNING_BLOCK);
@ -1037,14 +1042,14 @@ static void mmc_sd_detect(struct mmc_host *host)
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_get_card(host->card);
/* /*
* Just check if our card has been removed. * Just check if our card has been removed.
*/ */
err = _mmc_detect_card_removed(host); err = _mmc_detect_card_removed(host);
mmc_release_host(host); mmc_put_card(host->card);
if (err) { if (err) {
mmc_sd_remove(host); mmc_sd_remove(host);
@ -1070,6 +1075,8 @@ static int mmc_sd_suspend(struct mmc_host *host)
if (!mmc_host_is_spi(host)) if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host); err = mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
if (!err)
mmc_power_off(host);
mmc_release_host(host); mmc_release_host(host);
return err; return err;
@ -1089,12 +1096,61 @@ static int mmc_sd_resume(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
err = mmc_sd_init_card(host, host->ocr, host->card); err = mmc_sd_init_card(host, host->ocr, host->card);
mmc_release_host(host); mmc_release_host(host);
return err; 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) static int mmc_sd_power_restore(struct mmc_host *host)
{ {
int ret; int ret;
@ -1114,15 +1170,19 @@ static const struct mmc_bus_ops mmc_sd_ops = {
.resume = NULL, .resume = NULL,
.power_restore = mmc_sd_power_restore, .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive, .alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
}; };
static const struct mmc_bus_ops mmc_sd_ops_unsafe = { static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.remove = mmc_sd_remove, .remove = mmc_sd_remove,
.detect = mmc_sd_detect, .detect = mmc_sd_detect,
.runtime_suspend = mmc_sd_runtime_suspend,
.runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend, .suspend = mmc_sd_suspend,
.resume = mmc_sd_resume, .resume = mmc_sd_resume,
.power_restore = mmc_sd_power_restore, .power_restore = mmc_sd_power_restore,
.alive = mmc_sd_alive, .alive = mmc_sd_alive,
.shutdown = mmc_sd_suspend,
}; };
static void mmc_sd_attach_bus_ops(struct mmc_host *host) static void mmc_sd_attach_bus_ops(struct mmc_host *host)

View File

@ -563,10 +563,18 @@ static int mmc_sdio_init_uhs_card(struct mmc_card *card)
if (err) if (err)
goto out; 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, err = card->host->ops->execute_tuning(card->host,
MMC_SEND_TUNING_BLOCK); MMC_SEND_TUNING_BLOCK);
mmc_host_clk_release(card->host);
}
out: 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 * Therefore all registered functions must have drivers with suspend
* and resume methods. Failing that we simply remove the whole card. * 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; int i, err = 0;
@ -917,8 +925,26 @@ static int mmc_sdio_suspend(struct mmc_host *host)
if (!pmops || !pmops->suspend || !pmops->resume) { if (!pmops || !pmops->suspend || !pmops->resume) {
/* force removal of entire card in that case */ /* force removal of entire card in that case */
err = -ENOSYS; err = -ENOSYS;
} else break;
err = pmops->suspend(&func->dev); }
}
}
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) if (err)
break; break;
} }
@ -937,6 +963,9 @@ static int mmc_sdio_suspend(struct mmc_host *host)
mmc_release_host(host); mmc_release_host(host);
} }
if (!err && !mmc_card_keep_power(host))
mmc_power_off(host);
return err; return err;
} }
@ -950,6 +979,23 @@ static int mmc_sdio_resume(struct mmc_host *host)
/* Basic card reinitialization. */ /* Basic card reinitialization. */
mmc_claim_host(host); 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 */ /* No need to reinitialize powered-resumed nonremovable cards */
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(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; return err;
} }
@ -1051,11 +1098,28 @@ out:
return ret; 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 = { static const struct mmc_bus_ops mmc_sdio_ops = {
.remove = mmc_sdio_remove, .remove = mmc_sdio_remove,
.detect = mmc_sdio_detect, .detect = mmc_sdio_detect,
.pre_suspend = mmc_sdio_pre_suspend,
.suspend = mmc_sdio_suspend, .suspend = mmc_sdio_suspend,
.resume = mmc_sdio_resume, .resume = mmc_sdio_resume,
.runtime_suspend = mmc_sdio_runtime_suspend,
.runtime_resume = mmc_sdio_runtime_resume,
.power_restore = mmc_sdio_power_restore, .power_restore = mmc_sdio_power_restore,
.alive = mmc_sdio_alive, .alive = mmc_sdio_alive,
}; };

View File

@ -249,6 +249,17 @@ config MMC_SDHCI_S3C_DMA
YMMV. 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 config MMC_SDHCI_BCM2835
tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" tristate "SDHCI platform support for the BCM2835 SD/MMC Controller"
depends on ARCH_BCM2835 depends on ARCH_BCM2835
@ -556,6 +567,14 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's. 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 config MMC_DW_PCI
tristate "Synopsys Designware MCI support on PCI bus" tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI depends on MMC_DW && PCI

View File

@ -42,6 +42,7 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.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_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.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_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.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_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
ifeq ($(CONFIG_CB710_DEBUG),y) ifeq ($(CONFIG_CB710_DEBUG),y)

View File

@ -546,8 +546,6 @@ static int goldfish_mmc_remove(struct platform_device *pdev)
{ {
struct goldfish_mmc_host *host = platform_get_drvdata(pdev); struct goldfish_mmc_host *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
BUG_ON(host == NULL); BUG_ON(host == NULL);
mmc_remove_host(host->mmc); mmc_remove_host(host->mmc);

View File

@ -40,8 +40,6 @@
#include <asm/io.h> #include <asm/io.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include <mach/cpu.h>
#include "atmel-mci-regs.h" #include "atmel-mci-regs.h"
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE) #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); struct atmel_mci *host = platform_get_drvdata(pdev);
unsigned int i; unsigned int i;
platform_set_drvdata(pdev, NULL);
if (host->buffer) if (host->buffer)
dma_free_coherent(&pdev->dev, host->buf_size, dma_free_coherent(&pdev->dev, host->buf_size,
host->buffer, host->buf_phys_addr); host->buffer, host->buf_phys_addr);
@ -2504,7 +2500,7 @@ static int __exit atmci_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int atmci_suspend(struct device *dev) static int atmci_suspend(struct device *dev)
{ {
struct atmel_mci *host = dev_get_drvdata(dev); struct atmel_mci *host = dev_get_drvdata(dev);
@ -2559,17 +2555,15 @@ static int atmci_resume(struct device *dev)
return ret; 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 #endif
static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
static struct platform_driver atmci_driver = { static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove), .remove = __exit_p(atmci_remove),
.driver = { .driver = {
.name = "atmel_mci", .name = "atmel_mci",
.pm = ATMCI_PM_OPS, .pm = &atmci_pm,
.of_match_table = of_match_ptr(atmci_dt_ids), .of_match_table = of_match_ptr(atmci_dt_ids),
}, },
}; };

View File

@ -1149,7 +1149,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
kfree(host->ioarea); kfree(host->ioarea);
mmc_free_host(host->mmc); mmc_free_host(host->mmc);
platform_set_drvdata(pdev, NULL);
} }
return 0; return 0;
} }

View File

@ -621,8 +621,6 @@ static int sdh_remove(struct platform_device *pdev)
{ {
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (mmc) { if (mmc) {
struct sdh_host *host = mmc_priv(mmc); struct sdh_host *host = mmc_priv(mmc);

View File

@ -703,7 +703,7 @@ static int cb710_mmc_init(struct platform_device *pdev)
if (!mmc) if (!mmc)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(&pdev->dev, mmc); platform_set_drvdata(pdev, mmc);
/* harmless (maybe) magic */ /* harmless (maybe) magic */
pci_read_config_dword(chip->pdev, 0x48, &val); pci_read_config_dword(chip->pdev, 0x48, &val);

View File

@ -24,7 +24,7 @@ struct cb710_mmc_reader {
static inline struct mmc_host *cb710_slot_to_mmc(struct cb710_slot *slot) 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) static inline struct cb710_slot *cb710_mmc_to_slot(struct mmc_host *mmc)

View File

@ -1407,7 +1407,6 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev)
{ {
struct mmc_davinci_host *host = platform_get_drvdata(pdev); struct mmc_davinci_host *host = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (host) { if (host) {
mmc_davinci_cpufreq_deregister(host); mmc_davinci_cpufreq_deregister(host);

View File

@ -31,8 +31,6 @@
SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \
SDMMC_CLKSEL_CCLK_DIVIDER(z)) SDMMC_CLKSEL_CCLK_DIVIDER(z))
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 #define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4

View File

@ -21,7 +21,6 @@
#include "dw_mmc.h" #include "dw_mmc.h"
#define PCI_BAR_NO 2 #define PCI_BAR_NO 2
#define COMPLETE_BAR 0
#define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 #define SYNOPSYS_DW_MCI_VENDOR_ID 0x700
#define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 #define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107
/* Defining the Capabilities */ /* 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, 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; struct dw_mci *host;
int ret; int ret;
ret = pci_enable_device(pdev); ret = pcim_enable_device(pdev);
if (ret) if (ret)
return 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); host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host) { if (!host)
ret = -ENOMEM; return -ENOMEM;
goto err_release;
}
host->irq = pdev->irq; host->irq = pdev->irq;
host->irq_flags = IRQF_SHARED; host->irq_flags = IRQF_SHARED;
host->dev = &pdev->dev; host->dev = &pdev->dev;
host->pdata = &pci_board_data; host->pdata = &pci_board_data;
host->regs = pci_iomap(pdev, PCI_BAR_NO, COMPLETE_BAR); ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev));
if (!host->regs) { if (ret)
ret = -EIO; return ret;
goto err_unmap;
} host->regs = pcim_iomap_table(pdev)[0];
pci_set_drvdata(pdev, host);
ret = dw_mci_probe(host); ret = dw_mci_probe(host);
if (ret) if (ret)
goto err_probe_failed; return ret;
return ret;
err_probe_failed: pci_set_drvdata(pdev, host);
pci_iounmap(pdev, host->regs);
err_unmap: return 0;
kfree(host);
err_release:
pci_release_regions(pdev);
err_disable_dev:
pci_disable_device(pdev);
return ret;
} }
static void dw_mci_pci_remove(struct pci_dev *pdev) 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); struct dw_mci *host = pci_get_drvdata(pdev);
dw_mci_remove(host); 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 #ifdef CONFIG_PM_SLEEP
static int dw_mci_pci_suspend(struct device *dev) static int dw_mci_pci_suspend(struct device *dev)
{ {
int ret;
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev); struct dw_mci *host = pci_get_drvdata(pdev);
ret = dw_mci_suspend(host); return dw_mci_suspend(host);
return ret;
} }
static int dw_mci_pci_resume(struct device *dev) static int dw_mci_pci_resume(struct device *dev)
{ {
int ret;
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
struct dw_mci *host = pci_get_drvdata(pdev); struct dw_mci *host = pci_get_drvdata(pdev);
ret = dw_mci_resume(host); return dw_mci_resume(host);
return ret;
} }
#else #else
#define dw_mci_pci_suspend NULL #define dw_mci_pci_suspend NULL

View File

@ -24,8 +24,17 @@
#include "dw_mmc.h" #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, 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 dw_mci *host;
struct resource *regs; struct resource *regs;
@ -35,10 +44,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs)
return -ENXIO;
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) if (host->irq < 0)
return host->irq; return host->irq;
@ -47,6 +52,8 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
host->dev = &pdev->dev; host->dev = &pdev->dev;
host->irq_flags = 0; host->irq_flags = 0;
host->pdata = pdev->dev.platform_data; host->pdata = pdev->dev.platform_data;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->regs = devm_ioremap_resource(&pdev->dev, regs); host->regs = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(host->regs)) if (IS_ERR(host->regs))
return PTR_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); platform_set_drvdata(pdev, host);
ret = dw_mci_probe(host); return dw_mci_probe(host);
return ret;
} }
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); 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 #ifdef CONFIG_PM_SLEEP
/* /*
* TODO: we should probably disable the clock to the card in the suspend path. * TODO: we should probably disable the clock to the card in the suspend path.
*/ */
static int dw_mci_pltfm_suspend(struct device *dev) static int dw_mci_pltfm_suspend(struct device *dev)
{ {
int ret;
struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci *host = dev_get_drvdata(dev);
ret = dw_mci_suspend(host); return dw_mci_suspend(host);
if (ret)
return ret;
return 0;
} }
static int dw_mci_pltfm_resume(struct device *dev) static int dw_mci_pltfm_resume(struct device *dev)
{ {
int ret;
struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci *host = dev_get_drvdata(dev);
ret = dw_mci_resume(host); return dw_mci_resume(host);
if (ret)
return ret;
return 0;
} }
#else #else
#define dw_mci_pltfm_suspend NULL #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[] = { static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", }, { .compatible = "snps,dw-mshc", },
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rockchip_drv_data },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); 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 = { static struct platform_driver dw_mci_pltfm_driver = {
.probe = dw_mci_pltfm_probe, .probe = dw_mci_pltfm_probe,
.remove = dw_mci_pltfm_remove, .remove = dw_mci_pltfm_remove,

View File

@ -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");

View File

@ -39,7 +39,7 @@
#include "dw_mmc.h" #include "dw_mmc.h"
/* Common flag combinations */ /* 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_HTO | SDMMC_INT_SBE | \
SDMMC_INT_EBE) SDMMC_INT_EBE)
#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ #define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \
@ -51,6 +51,11 @@
#define DW_MCI_DMA_THRESHOLD 16 #define DW_MCI_DMA_THRESHOLD 16
#ifdef CONFIG_MMC_DW_IDMAC #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 { struct idmac_desc {
u32 des0; /* Control Descriptor */ u32 des0; /* Control Descriptor */
#define IDMAC_DES0_DIC BIT(1) #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); mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
/* Mask out interrupts - get Tx & Rx complete only */ /* 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 | mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
SDMMC_IDMAC_INT_TI); SDMMC_IDMAC_INT_TI);
@ -1087,7 +1093,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
status = host->data_status; status = host->data_status;
if (status & DW_MCI_DATA_ERROR_FLAGS) { if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DTO) { if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT; data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) { } else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ; 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 */ #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)) if (dw_mci_get_cd(mmc))
set_bit(DW_MMC_CARD_PRESENT, &slot->flags); set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
else 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; struct device_node *np = dev->of_node;
const struct dw_mci_drv_data *drv_data = host->drv_data; const struct dw_mci_drv_data *drv_data = host->drv_data;
int idx, ret; int idx, ret;
u32 clock_frequency;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) { 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); 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) { if (drv_data && drv_data->parse_dt) {
ret = drv_data->parse_dt(host); ret = drv_data->parse_dt(host);
if (ret) if (ret)
@ -2207,18 +2204,23 @@ int dw_mci_probe(struct dw_mci *host)
host->ciu_clk = devm_clk_get(host->dev, "ciu"); host->ciu_clk = devm_clk_get(host->dev, "ciu");
if (IS_ERR(host->ciu_clk)) { if (IS_ERR(host->ciu_clk)) {
dev_dbg(host->dev, "ciu clock not available\n"); dev_dbg(host->dev, "ciu clock not available\n");
host->bus_hz = host->pdata->bus_hz;
} else { } else {
ret = clk_prepare_enable(host->ciu_clk); ret = clk_prepare_enable(host->ciu_clk);
if (ret) { if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n"); dev_err(host->dev, "failed to enable ciu clock\n");
goto err_clk_biu; goto err_clk_biu;
} }
}
if (IS_ERR(host->ciu_clk)) if (host->pdata->bus_hz) {
host->bus_hz = host->pdata->bus_hz; ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
else 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); host->bus_hz = clk_get_rate(host->ciu_clk);
}
if (drv_data && drv_data->setup_clock) { if (drv_data && drv_data->setup_clock) {
ret = drv_data->setup_clock(host); 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) { if (!host->bus_hz) {
dev_err(host->dev, dev_err(host->dev,
"Platform data must supply bus speed\n"); "Platform data must supply bus speed\n");
ret = -ENODEV; ret = -ENODEV;
goto err_clk_ciu; goto err_regulator;
} }
host->quirks = host->pdata->quirks; 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); tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host);
host->card_workqueue = alloc_workqueue("dw-mci-card", host->card_workqueue = alloc_workqueue("dw-mci-card",
WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1); WQ_MEM_RECLAIM | WQ_NON_REENTRANT, 1);
if (!host->card_workqueue) if (!host->card_workqueue) {
ret = -ENOMEM;
goto err_dmaunmap; goto err_dmaunmap;
}
INIT_WORK(&host->card_work, dw_mci_work_routine_card); INIT_WORK(&host->card_work, dw_mci_work_routine_card);
ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt,
host->irq_flags, "dw-mci", host); host->irq_flags, "dw-mci", host);
@ -2378,6 +2400,7 @@ err_dmaunmap:
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
err_regulator:
if (host->vmmc) if (host->vmmc)
regulator_disable(host->vmmc); regulator_disable(host->vmmc);

View File

@ -98,7 +98,7 @@
#define SDMMC_INT_HLE BIT(12) #define SDMMC_INT_HLE BIT(12)
#define SDMMC_INT_FRUN BIT(11) #define SDMMC_INT_FRUN BIT(11)
#define SDMMC_INT_HTO BIT(10) #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_RTO BIT(8)
#define SDMMC_INT_DCRC BIT(7) #define SDMMC_INT_DCRC BIT(7)
#define SDMMC_INT_RCRC BIT(6) #define SDMMC_INT_RCRC BIT(6)
@ -111,6 +111,7 @@
#define SDMMC_INT_ERROR 0xbfc2 #define SDMMC_INT_ERROR 0xbfc2
/* Command register defines */ /* Command register defines */
#define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_START BIT(31)
#define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define SDMMC_CMD_CCS_EXP BIT(23) #define SDMMC_CMD_CCS_EXP BIT(23)
#define SDMMC_CMD_CEATA_RD BIT(22) #define SDMMC_CMD_CEATA_RD BIT(22)
#define SDMMC_CMD_UPD_CLK BIT(21) #define SDMMC_CMD_UPD_CLK BIT(21)

View File

@ -14,6 +14,7 @@
*/ */
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/irq.h> #include <linux/irq.h>
@ -120,7 +121,6 @@ struct jz4740_mmc_host {
int irq; int irq;
int card_detect_irq; int card_detect_irq;
struct resource *mem;
void __iomem *base; void __iomem *base;
struct mmc_request *req; struct mmc_request *req;
struct mmc_command *cmd; 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; host->req->cmd->error = -EIO;
data->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) if (cmd->data)
cmd->data->error = -EIO; cmd->data->error = -EIO;
cmd->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); 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, gpio_set_value(host->pdata->gpio_power,
!host->pdata->power_active_low); !host->pdata->power_active_low);
host->cmdat |= JZ_MMC_CMDAT_INIT; host->cmdat |= JZ_MMC_CMDAT_INIT;
clk_enable(host->clk); clk_prepare_enable(host->clk);
break; break;
case MMC_POWER_ON: case MMC_POWER_ON:
break; 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)) if (gpio_is_valid(host->pdata->gpio_power))
gpio_set_value(host->pdata->gpio_power, gpio_set_value(host->pdata->gpio_power,
host->pdata->power_active_low); host->pdata->power_active_low);
clk_disable(host->clk); clk_disable_unprepare(host->clk);
break; 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) static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
{ {
struct jz4740_mmc_host *host = mmc_priv(mmc); 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 = { static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request, .request = jz4740_mmc_request,
.set_ios = jz4740_mmc_set_ios, .set_ios = jz4740_mmc_set_ios,
.get_ro = jz4740_mmc_get_ro, .get_ro = mmc_gpio_get_ro,
.get_cd = jz4740_mmc_get_cd, .get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq, .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; 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; struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data;
int ret = 0;
if (!pdata) if (!pdata)
return 0; return 0;
ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_card_detect, if (!pdata->card_detect_active_low)
"MMC detect change", false, 0); mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
if (ret) if (!pdata->read_only_active_low)
goto err; mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_read_only, if (gpio_is_valid(pdata->gpio_card_detect)) {
"MMC read only", false, 0); ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect);
if (ret) if (ret)
goto err_free_gpio_card_detect; return ret;
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;
} }
return request_irq(host->card_detect_irq, jz4740_mmc_card_detect_irq, if (gpio_is_valid(pdata->gpio_read_only)) {
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only);
"MMC card detect", host); 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) 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)) if (gpio_is_valid(pdata->gpio_power))
gpio_free(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) 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 mmc_host *mmc;
struct jz4740_mmc_host *host; struct jz4740_mmc_host *host;
struct jz4740_mmc_platform_data *pdata; struct jz4740_mmc_platform_data *pdata;
struct resource *res;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
@ -827,42 +774,28 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_host; goto err_free_host;
} }
host->clk = clk_get(&pdev->dev, "mmc"); host->clk = devm_clk_get(&pdev->dev, "mmc");
if (IS_ERR(host->clk)) { if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk); ret = PTR_ERR(host->clk);
dev_err(&pdev->dev, "Failed to get mmc clock\n"); dev_err(&pdev->dev, "Failed to get mmc clock\n");
goto err_free_host; goto err_free_host;
} }
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) { host->base = devm_ioremap_resource(&pdev->dev, res);
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));
if (!host->base) { if (!host->base) {
ret = -EBUSY; ret = -EBUSY;
dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 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)); ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", 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) if (ret)
goto err_gpio_bulk_free; goto err_gpio_bulk_free;
@ -885,17 +818,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
host->irq_mask = 0xffff; 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, ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
dev_name(&pdev->dev), host); dev_name(&pdev->dev), host);
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", 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); jz4740_mmc_reset(host);
@ -918,21 +845,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
err_free_irq: err_free_irq:
free_irq(host->irq, host); 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: err_free_gpios:
jz4740_mmc_free_gpios(pdev); jz4740_mmc_free_gpios(pdev);
err_gpio_bulk_free: err_gpio_bulk_free:
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 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: err_free_host:
platform_set_drvdata(pdev, NULL);
mmc_free_host(mmc); mmc_free_host(mmc);
return ret; return ret;
@ -949,24 +866,16 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
mmc_remove_host(host->mmc); mmc_remove_host(host->mmc);
free_irq(host->irq, host); free_irq(host->irq, host);
if (host->card_detect_irq >= 0)
free_irq(host->card_detect_irq, host);
jz4740_mmc_free_gpios(pdev); jz4740_mmc_free_gpios(pdev);
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 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); mmc_free_host(host->mmc);
return 0; return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int jz4740_mmc_suspend(struct device *dev) static int jz4740_mmc_suspend(struct device *dev)
{ {
@ -990,13 +899,8 @@ static int jz4740_mmc_resume(struct device *dev)
return 0; return 0;
} }
const struct dev_pm_ops jz4740_mmc_pm_ops = { static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
.suspend = jz4740_mmc_suspend, jz4740_mmc_resume);
.resume = jz4740_mmc_resume,
.poweroff = jz4740_mmc_suspend,
.restore = jz4740_mmc_resume,
};
#define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops) #define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops)
#else #else
#define JZ4740_MMC_PM_OPS NULL #define JZ4740_MMC_PM_OPS NULL

View File

@ -35,7 +35,7 @@
#define DRIVER_NAME "mvsdio" #define DRIVER_NAME "mvsdio"
static int maxfreq = MVSD_CLOCKRATE_MAX; static int maxfreq;
static int nodma; static int nodma;
struct mvsd_host { struct mvsd_host {
@ -685,7 +685,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
const struct mbus_dram_target_info *dram; const struct mbus_dram_target_info *dram;
struct resource *r; struct resource *r;
int ret, irq; int ret, irq;
int gpio_card_detect, gpio_write_protect;
struct pinctrl *pinctrl; struct pinctrl *pinctrl;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 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)) if (!IS_ERR(host->clk))
clk_prepare_enable(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 (np) {
if (IS_ERR(host->clk)) { if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "DT platforms must have a clock associated\n"); 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; host->base_clock = clk_get_rate(host->clk) / 2;
gpio_card_detect = of_get_named_gpio(np, "cd-gpios", 0); ret = mmc_of_parse(mmc);
gpio_write_protect = of_get_named_gpio(np, "wp-gpios", 0); if (ret < 0)
goto out;
} else { } else {
const struct mvsdio_platform_data *mvsd_data; const struct mvsdio_platform_data *mvsd_data;
mvsd_data = pdev->dev.platform_data; mvsd_data = pdev->dev.platform_data;
if (!mvsd_data) { if (!mvsd_data) {
ret = -ENXIO; ret = -ENXIO;
goto out; 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; host->base_clock = mvsd_data->clock / 2;
gpio_card_detect = mvsd_data->gpio_card_detect ? : -EINVAL; /* GPIO 0 regarded as invalid for backward compatibility */
gpio_write_protect = mvsd_data->gpio_write_protect ? : -EINVAL; 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; if (maxfreq)
mmc->f_max = maxfreq;
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;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
@ -777,15 +793,6 @@ static int __init mvsd_probe(struct platform_device *pdev)
goto out; 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); setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host);
platform_set_drvdata(pdev, mmc); platform_set_drvdata(pdev, mmc);
ret = mmc_add_host(mmc); ret = mmc_add_host(mmc);
@ -793,10 +800,10 @@ static int __init mvsd_probe(struct platform_device *pdev)
goto out; goto out;
if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
dev_notice(&pdev->dev, "using GPIO %d for card detection\n", dev_notice(&pdev->dev, "using GPIO for card detection\n");
gpio_card_detect);
else 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; return 0;
out: out:
@ -827,7 +834,6 @@ static int __exit mvsd_remove(struct platform_device *pdev)
clk_disable_unprepare(host->clk); clk_disable_unprepare(host->clk);
mmc_free_host(mmc); mmc_free_host(mmc);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -1067,7 +1067,9 @@ static int mxcmci_probe(struct platform_device *pdev)
goto out_release_mem; goto out_release_mem;
} }
mmc_of_parse(mmc); ret = mmc_of_parse(mmc);
if (ret)
goto out_free;
mmc->ops = &mxcmci_ops; mmc->ops = &mxcmci_ops;
/* For devicetree parsing, the bus width is read from devicetree */ /* 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 mmc_host *mmc = platform_get_drvdata(pdev);
struct mxcmci_host *host = mmc_priv(mmc); struct mxcmci_host *host = mmc_priv(mmc);
platform_set_drvdata(pdev, NULL);
mmc_remove_host(mmc); mmc_remove_host(mmc);
if (host->vcc) if (host->vcc)

View File

@ -41,7 +41,6 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/stmp_device.h> #include <linux/stmp_device.h>
#include <linux/spi/mxs-spi.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 mxs_mmc_host *host;
struct mmc_host *mmc; struct mmc_host *mmc;
struct resource *iores; struct resource *iores;
struct pinctrl *pinctrl;
int ret = 0, irq_err; int ret = 0, irq_err;
struct regulator *reg_vmmc; struct regulator *reg_vmmc;
enum of_gpio_flags flags; 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); ssp->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(ssp->clk)) { if (IS_ERR(ssp->clk)) {
ret = PTR_ERR(ssp->clk); ret = PTR_ERR(ssp->clk);
@ -639,6 +631,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
if (!ssp->dmach) { if (!ssp->dmach) {
dev_err(mmc_dev(host->mmc), dev_err(mmc_dev(host->mmc),
"%s: failed to request dma\n", __func__); "%s: failed to request dma\n", __func__);
ret = -ENODEV;
goto out_clk_put; goto out_clk_put;
} }
@ -708,8 +701,6 @@ static int mxs_mmc_remove(struct platform_device *pdev)
mmc_remove_host(mmc); mmc_remove_host(mmc);
platform_set_drvdata(pdev, NULL);
if (ssp->dmach) if (ssp->dmach)
dma_release_channel(ssp->dmach); dma_release_channel(ssp->dmach);

View File

@ -1413,33 +1413,17 @@ static int mmc_omap_probe(struct platform_device *pdev)
else else
sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX;
host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); 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) if (!host->dma_tx)
dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n",
sig); sig);
#endif
if (mmc_omap2()) if (mmc_omap2())
sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX;
else else
sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX;
host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); 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) if (!host->dma_rx)
dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n", dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n",
sig); sig);
#endif
ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
if (ret) if (ret)
@ -1500,8 +1484,6 @@ static int mmc_omap_remove(struct platform_device *pdev)
struct mmc_omap_host *host = platform_get_drvdata(pdev); struct mmc_omap_host *host = platform_get_drvdata(pdev);
int i; int i;
platform_set_drvdata(pdev, NULL);
BUG_ON(host == NULL); BUG_ON(host == NULL);
for (i = 0; i < host->nr_slots; i++) for (i = 0; i < host->nr_slots; i++)

View File

@ -2047,7 +2047,6 @@ err_irq:
} }
err1: err1:
iounmap(host->base); iounmap(host->base);
platform_set_drvdata(pdev, NULL);
mmc_free_host(mmc); mmc_free_host(mmc);
err_alloc: err_alloc:
omap_hsmmc_gpio_free(pdata); 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res) if (res)
release_mem_region(res->start, resource_size(res)); release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -834,8 +834,6 @@ static int pxamci_remove(struct platform_device *pdev)
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
int gpio_cd = -1, gpio_ro = -1, gpio_power = -1; int gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
platform_set_drvdata(pdev, NULL);
if (mmc) { if (mmc) {
struct pxamci_host *host = mmc_priv(mmc); struct pxamci_host *host = mmc_priv(mmc);

View File

@ -1316,8 +1316,6 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
mmc_remove_host(mmc); mmc_remove_host(mmc);
mmc_free_host(mmc); mmc_free_host(mmc);
platform_set_drvdata(pdev, NULL);
dev_dbg(&(pdev->dev), dev_dbg(&(pdev->dev),
": Realtek PCI-E SDMMC controller has been removed\n"); ": Realtek PCI-E SDMMC controller has been removed\n");

View File

@ -31,10 +31,13 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/gpio.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/acpi_gpio.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/pm.h> #include <linux/mmc/pm.h>
@ -83,12 +86,37 @@ static int sdhci_acpi_enable_dma(struct sdhci_host *host)
return 0; 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 = { static const struct sdhci_ops sdhci_acpi_ops_dflt = {
.enable_dma = sdhci_acpi_enable_dma, .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 = { 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, .caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM, .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 = { 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 { struct sdhci_acpi_uid_slot {
@ -161,6 +191,59 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
return slot; 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) static int sdhci_acpi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -171,7 +254,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
struct resource *iomem; struct resource *iomem;
resource_size_t len; resource_size_t len;
const char *hid; const char *hid;
int err; int err, gpio;
if (acpi_bus_get_device(handle, &device)) if (acpi_bus_get_device(handle, &device))
return -ENODEV; return -ENODEV;
@ -196,6 +279,8 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
gpio = acpi_get_gpio_by_index(dev, 0, NULL);
c = sdhci_priv(host); c = sdhci_priv(host);
c->host = host; c->host = host;
c->slot = sdhci_acpi_get_slot(handle, hid); c->slot = sdhci_acpi_get_slot(handle, hid);
@ -251,6 +336,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (err) if (err)
goto err_free; 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) { if (c->use_runtime_pm) {
pm_runtime_set_active(dev); pm_runtime_set_active(dev);
pm_suspend_ignore_children(dev, 1); pm_suspend_ignore_children(dev, 1);
@ -262,7 +352,6 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
return 0; return 0;
err_free: err_free:
platform_set_drvdata(pdev, NULL);
sdhci_free_host(c->host); sdhci_free_host(c->host);
return err; return err;
} }
@ -281,7 +370,6 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
sdhci_remove_host(c->host, dead); sdhci_remove_host(c->host, dead);
platform_set_drvdata(pdev, NULL);
sdhci_free_host(c->host); sdhci_free_host(c->host);
return 0; return 0;

View File

@ -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");

View File

@ -148,7 +148,7 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
int ret; int ret;
host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata); host = sdhci_pltfm_init(pdev, &bcm2835_sdhci_pdata, 0);
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);

View File

@ -96,7 +96,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
static int sdhci_cns3xxx_probe(struct platform_device *pdev) 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) static int sdhci_cns3xxx_remove(struct platform_device *pdev)

View File

@ -130,7 +130,7 @@ static int sdhci_dove_probe(struct platform_device *pdev)
gpio_direction_input(priv->gpio_cd); 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)) { if (IS_ERR(host)) {
ret = PTR_ERR(host); ret = PTR_ERR(host);
goto err_sdhci_pltfm_init; goto err_sdhci_pltfm_init;

View File

@ -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) static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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; 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) static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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_l = esdhc_writel_le,
.write_w = esdhc_writew_le, .write_w = esdhc_writew_le,
.write_b = esdhc_writeb_le, .write_b = esdhc_writeb_le,
.set_clock = esdhc_set_clock, .set_clock = esdhc_pltfm_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock, .get_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro, .get_ro = esdhc_pltfm_get_ro,
.platform_bus_width = esdhc_pltfm_bus_width, .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, "bus-width", &boarddata->max_bus_width);
of_property_read_u32(np, "max-frequency", &boarddata->f_max);
return 0; return 0;
} }
#else #else
@ -503,7 +527,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
int err; int err;
struct pltfm_imx_data *imx_data; 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)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);

View File

@ -36,13 +36,21 @@
/* pltfm-specific */ /* pltfm-specific */
#define ESDHC_HOST_CONTROL_LE 0x20 #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 */ /* OF-specific */
#define ESDHC_DMA_SYSCTL 0x40c #define ESDHC_DMA_SYSCTL 0x40c
#define ESDHC_DMA_SNOOP 0x00000040 #define ESDHC_DMA_SNOOP 0x00000040
#define ESDHC_HOST_CONTROL_RES 0x05 #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 pre_div = 2;
int div = 1; int div = 1;
@ -56,14 +64,14 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock)
| ESDHC_CLOCK_MASK); | ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); 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; pre_div *= 2;
while (host->max_clk / pre_div / div > clock && div < 16) while (host_clock / pre_div / div > clock && div < 16)
div++; div++;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", 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; pre_div >>= 1;
div--; div--;

View File

@ -13,6 +13,7 @@
* your option) any later version. * your option) any later version.
*/ */
#include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/delay.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) { if (reg == SDHCI_HOST_CONTROL) {
u32 dma_bits; 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 select is 22,23 bits in Protocol Control Register */
dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5; dma_bits = (val & SDHCI_CTRL_DMA_MASK) << 5;
clrsetbits_be32(host->ioaddr + reg , 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 */ /* Set the clock */
esdhc_set_clock(host, clock); esdhc_set_clock(host, clock, host->max_clk);
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -230,6 +238,30 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; 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 = { static const struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl, .read_l = esdhc_readl,
.read_w = esdhc_readw, .read_w = esdhc_readw,
@ -247,6 +279,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
.platform_resume = esdhc_of_resume, .platform_resume = esdhc_of_resume,
#endif #endif
.adma_workaround = esdhci_of_adma_workaround, .adma_workaround = esdhci_of_adma_workaround,
.platform_bus_width = esdhc_pltfm_bus_width,
}; };
static const struct sdhci_pltfm_data sdhci_esdhc_pdata = { 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) 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) static int sdhci_esdhc_remove(struct platform_device *pdev)

View File

@ -68,7 +68,7 @@ static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
static int sdhci_hlwd_probe(struct platform_device *pdev) 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) static int sdhci_hlwd_remove(struct platform_device *pdev)

View File

@ -36,6 +36,7 @@
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14 #define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
/* /*
* PCI registers * PCI registers
@ -77,6 +78,8 @@ struct sdhci_pci_slot {
int rst_n_gpio; int rst_n_gpio;
int cd_gpio; int cd_gpio;
int cd_irq; int cd_irq;
void (*hw_reset)(struct sdhci_host *host);
}; };
struct sdhci_pci_chip { struct sdhci_pci_chip {
@ -307,10 +310,27 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = {
.probe_slot = pch_hc_probe_slot, .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) 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->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
slot->hw_reset = sdhci_pci_int_hw_reset;
return 0; 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 = { 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 */ /* O2Micro extra registers */
@ -909,6 +931,14 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, .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, .vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120, .device = PCI_DEVICE_ID_O2_8120,
@ -1014,7 +1044,7 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
return 0; 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); struct sdhci_pci_slot *slot = sdhci_priv(host);
int rst_n_gpio = slot->rst_n_gpio; 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); 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 = { static const struct sdhci_ops sdhci_pci_ops = {
.enable_dma = sdhci_pci_enable_dma, .enable_dma = sdhci_pci_enable_dma,
.platform_bus_width = sdhci_pci_bus_width, .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")) { if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) {
gpio_direction_output(slot->rst_n_gpio, 1); gpio_direction_output(slot->rst_n_gpio, 1);
slot->host->mmc->caps |= MMC_CAP_HW_RESET; slot->host->mmc->caps |= MMC_CAP_HW_RESET;
slot->hw_reset = sdhci_pci_gpio_hw_reset;
} else { } else {
dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); dev_warn(&pdev->dev, "failed to request rst_n_gpio\n");
slot->rst_n_gpio = -EINVAL; slot->rst_n_gpio = -EINVAL;

View File

@ -115,10 +115,10 @@ void sdhci_get_of_property(struct platform_device *pdev) {}
EXPORT_SYMBOL_GPL(sdhci_get_of_property); EXPORT_SYMBOL_GPL(sdhci_get_of_property);
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, 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_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
struct resource *iomem; struct resource *iomem;
int ret; int ret;
@ -134,24 +134,27 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
/* Some PCI-based MFD need the parent here */ /* Some PCI-based MFD need the parent here */
if (pdev->dev.parent != &platform_bus && !np) 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 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)) { if (IS_ERR(host)) {
ret = PTR_ERR(host); ret = PTR_ERR(host);
goto err; goto err;
} }
pltfm_host = sdhci_priv(host);
host->hw_name = dev_name(&pdev->dev); host->hw_name = dev_name(&pdev->dev);
if (pdata && pdata->ops) if (pdata && pdata->ops)
host->ops = pdata->ops; host->ops = pdata->ops;
else else
host->ops = &sdhci_pltfm_ops; host->ops = &sdhci_pltfm_ops;
if (pdata) if (pdata) {
host->quirks = pdata->quirks; host->quirks = pdata->quirks;
host->quirks2 = pdata->quirks2;
}
host->irq = platform_get_irq(pdev, 0); host->irq = platform_get_irq(pdev, 0);
if (!request_mem_region(iomem->start, resource_size(iomem), if (!request_mem_region(iomem->start, resource_size(iomem),
@ -197,17 +200,17 @@ void sdhci_pltfm_free(struct platform_device *pdev)
iounmap(host->ioaddr); iounmap(host->ioaddr);
release_mem_region(iomem->start, resource_size(iomem)); release_mem_region(iomem->start, resource_size(iomem));
sdhci_free_host(host); sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
} }
EXPORT_SYMBOL_GPL(sdhci_pltfm_free); EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
int sdhci_pltfm_register(struct platform_device *pdev, 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; struct sdhci_host *host;
int ret = 0; int ret = 0;
host = sdhci_pltfm_init(pdev, pdata); host = sdhci_pltfm_init(pdev, pdata, priv_size);
if (IS_ERR(host)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);

View File

@ -18,6 +18,7 @@
struct sdhci_pltfm_data { struct sdhci_pltfm_data {
const struct sdhci_ops *ops; const struct sdhci_ops *ops;
unsigned int quirks; unsigned int quirks;
unsigned int quirks2;
}; };
struct sdhci_pltfm_host { struct sdhci_pltfm_host {
@ -27,6 +28,8 @@ struct sdhci_pltfm_host {
/* migrate from sdhci_of_host */ /* migrate from sdhci_of_host */
unsigned int clock; unsigned int clock;
u16 xfer_mode_shadow; u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned;
}; };
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER #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 void sdhci_get_of_property(struct platform_device *pdev);
extern struct sdhci_host *sdhci_pltfm_init(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 void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_register(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 int sdhci_pltfm_unregister(struct platform_device *pdev);
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host); 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 #ifdef CONFIG_PM
extern const struct dev_pm_ops sdhci_pltfm_pmops; extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops) #define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)

View File

@ -175,7 +175,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
if (!pxa) if (!pxa)
return -ENOMEM; return -ENOMEM;
host = sdhci_pltfm_init(pdev, NULL); host = sdhci_pltfm_init(pdev, NULL, 0);
if (IS_ERR(host)) { if (IS_ERR(host)) {
kfree(pxa); kfree(pxa);
return PTR_ERR(host); return PTR_ERR(host);
@ -253,8 +253,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -230,7 +230,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!pxa) if (!pxa)
return -ENOMEM; return -ENOMEM;
host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata); host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0);
if (IS_ERR(host)) { if (IS_ERR(host)) {
kfree(pxa); kfree(pxa);
return PTR_ERR(host); 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); match = of_match_device(of_match_ptr(sdhci_pxav3_of_match), &pdev->dev);
if (match) { if (match) {
mmc_of_parse(host->mmc); ret = mmc_of_parse(host->mmc);
if (ret)
goto err_of_parse;
sdhci_get_of_property(pdev); sdhci_get_of_property(pdev);
pdata = pxav3_get_mmc_pdata(dev); pdata = pxav3_get_mmc_pdata(dev);
} else if (pdata) { } 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_enable(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS);
pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1); pm_suspend_ignore_children(&pdev->dev, 1);
pm_runtime_get_noresume(&pdev->dev);
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to add host\n"); dev_err(&pdev->dev, "failed to add host\n");
pm_runtime_forbid(&pdev->dev);
pm_runtime_disable(&pdev->dev);
goto err_add_host; goto err_add_host;
} }
@ -313,10 +312,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
return 0; return 0;
err_of_parse:
err_cd_req:
err_add_host: err_add_host:
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(clk); clk_disable_unprepare(clk);
clk_put(clk); clk_put(clk);
err_cd_req:
err_clk_get: err_clk_get:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
@ -339,8 +341,6 @@ static int sdhci_pxav3_remove(struct platform_device *pdev)
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
kfree(pxa); kfree(pxa);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -745,7 +745,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
clk_disable_unprepare(sc->clk_io); clk_disable_unprepare(sc->clk_io);
sdhci_free_host(host); sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
return 0; return 0;
} }

View File

@ -13,7 +13,6 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h> #include <linux/mmc/slot-gpio.h>
#include <linux/pinctrl/consumer.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
struct sdhci_sirf_priv { struct sdhci_sirf_priv {
@ -24,7 +23,7 @@ struct sdhci_sirf_priv {
static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host) static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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); 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_host *host;
struct sdhci_pltfm_host *pltfm_host; struct sdhci_pltfm_host *pltfm_host;
struct sdhci_sirf_priv *priv; struct sdhci_sirf_priv *priv;
struct pinctrl *pinctrl; struct clk *clk;
int gpio_cd;
int ret; int ret;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev); clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(pinctrl)) { if (IS_ERR(clk)) {
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)) {
dev_err(&pdev->dev, "unable to get clock"); dev_err(&pdev->dev, "unable to get clock");
return PTR_ERR(priv->clk); return PTR_ERR(clk);
} }
if (pdev->dev.of_node) { if (pdev->dev.of_node)
priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, gpio_cd = of_get_named_gpio(pdev->dev.of_node, "cd-gpios", 0);
"cd-gpios", 0); else
} else { gpio_cd = -EINVAL;
priv->gpio_cd = -EINVAL;
}
host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata); host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, sizeof(struct sdhci_sirf_priv));
if (IS_ERR(host)) { if (IS_ERR(host))
ret = PTR_ERR(host); return PTR_ERR(host);
goto err_sdhci_pltfm_init;
}
pltfm_host = sdhci_priv(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); 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); ret = sdhci_add_host(host);
if (ret) if (ret)
@ -111,8 +98,8 @@ err_request_cd:
sdhci_remove_host(host, 0); sdhci_remove_host(host, 0);
err_sdhci_add: err_sdhci_add:
clk_disable_unprepare(priv->clk); clk_disable_unprepare(priv->clk);
err_clk_prepare:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
err_sdhci_pltfm_init:
return ret; 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_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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);
sdhci_pltfm_unregister(pdev); 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_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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);
int ret; int ret;
ret = sdhci_suspend_host(host); 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_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(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);
int ret; int ret;
ret = clk_enable(priv->clk); ret = clk_enable(priv->clk);

View File

@ -258,7 +258,6 @@ static int sdhci_probe(struct platform_device *pdev)
return 0; return 0;
set_drvdata: set_drvdata:
platform_set_drvdata(pdev, NULL);
sdhci_remove_host(host, 1); sdhci_remove_host(host, 1);
free_host: free_host:
sdhci_free_host(host); sdhci_free_host(host);
@ -278,7 +277,6 @@ static int sdhci_remove(struct platform_device *pdev)
int dead = 0; int dead = 0;
u32 scratch; u32 scratch;
platform_set_drvdata(pdev, NULL);
scratch = readl(host->ioaddr + SDHCI_INT_STATUS); scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1) if (scratch == (u32)-1)
dead = 1; dead = 1;

View File

@ -205,7 +205,7 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
}; };
MODULE_DEVICE_TABLE(of, 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 device_node *np = dev->of_node;
struct sdhci_host *host = dev_get_drvdata(dev); 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; struct sdhci_tegra *tegra_host = pltfm_host->priv;
tegra_host->power_gpio = of_get_named_gpio(np, "power-gpios", 0); 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) static int sdhci_tegra_probe(struct platform_device *pdev)
@ -231,7 +231,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
return -EINVAL; return -EINVAL;
soc_data = match->data; 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)) if (IS_ERR(host))
return PTR_ERR(host); return PTR_ERR(host);
pltfm_host = sdhci_priv(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; tegra_host->soc_data = soc_data;
pltfm_host->priv = tegra_host; 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)) { if (gpio_is_valid(tegra_host->power_gpio)) {
rc = gpio_request(tegra_host->power_gpio, "sdhci_power"); rc = gpio_request(tegra_host->power_gpio, "sdhci_power");
@ -279,6 +281,7 @@ err_clk_get:
if (gpio_is_valid(tegra_host->power_gpio)) if (gpio_is_valid(tegra_host->power_gpio))
gpio_free(tegra_host->power_gpio); gpio_free(tegra_host->power_gpio);
err_power_req: err_power_req:
err_parse_dt:
err_alloc_tegra_host: err_alloc_tegra_host:
sdhci_pltfm_free(pdev); sdhci_pltfm_free(pdev);
return rc; return rc;

View File

@ -58,6 +58,8 @@ static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
static int sdhci_runtime_pm_get(struct sdhci_host *host); static int sdhci_runtime_pm_get(struct sdhci_host *host);
static int sdhci_runtime_pm_put(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 #else
static inline int sdhci_runtime_pm_get(struct sdhci_host *host) 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; 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 #endif
static void sdhci_dumpregs(struct sdhci_host *host) 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); sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
if (mask & SDHCI_RESET_ALL) if (mask & SDHCI_RESET_ALL) {
host->clock = 0; 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 */ /* Wait max 100 ms */
timeout = 100; timeout = 100;
@ -1268,6 +1280,8 @@ static int sdhci_set_power(struct sdhci_host *host, unsigned short power)
if (pwr == 0) { if (pwr == 0) {
sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); 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; 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); 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 * Some controllers need an extra 10ms delay of 10ms before they
* can apply clock after applying power * 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); ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */ /* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
if (ios->timing == MMC_TIMING_MMC_HS200) if ((ios->timing == MMC_TIMING_MMC_HS200) ||
ctrl_2 |= SDHCI_CTRL_HS_SDR200; (ios->timing == MMC_TIMING_UHS_SDR104))
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (ios->timing == MMC_TIMING_UHS_SDR12) else if (ios->timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12; ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if (ios->timing == MMC_TIMING_UHS_SDR25) else if (ios->timing == MMC_TIMING_UHS_SDR25)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25; ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (ios->timing == MMC_TIMING_UHS_SDR50) else if (ios->timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_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) else if (ios->timing == MMC_TIMING_UHS_DDR50)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50; ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 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) && if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) &&
(host->flags & SDHCI_SDR50_NEEDS_TUNING || (host->flags & SDHCI_SDR50_NEEDS_TUNING ||
host->flags & SDHCI_HS200_NEEDS_TUNING)) host->flags & SDHCI_SDR104_NEEDS_TUNING))
requires_tuning_nonuhs = true; requires_tuning_nonuhs = true;
if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || 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); struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags; 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); spin_lock_irqsave(&host->lock, flags);
/* Check host->mrq first in case we are runtime suspended */ /* Check host->mrq first in case we are runtime suspended */
if (host->mrq && if (host->mrq && !sdhci_do_get_cd(host)) {
!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
pr_err("%s: Card removed during transfer!\n", pr_err("%s: Card removed during transfer!\n",
mmc_hostname(host->mmc)); mmc_hostname(host->mmc));
pr_err("%s: Resetting controller.\n", 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); 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) int sdhci_runtime_suspend_host(struct sdhci_host *host)
{ {
unsigned long flags; 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; mmc->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
/* SDR104 supports also implies SDR50 support */ /* 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; 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; mmc->caps |= MMC_CAP_UHS_SDR50;
if (caps[1] & SDHCI_SUPPORT_DDR50) 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) if (caps[1] & SDHCI_USE_SDR50_TUNING)
host->flags |= SDHCI_SDR50_NEEDS_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) 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 */ /* Driver Type(s) (A, C, D) supported by the host */
if (caps[1] & SDHCI_DRIVER_TYPE_A) if (caps[1] & SDHCI_DRIVER_TYPE_A)

View File

@ -294,6 +294,7 @@ struct sdhci_ops {
void (*platform_resume)(struct sdhci_host *host); void (*platform_resume)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*platform_init)(struct sdhci_host *host); void (*platform_init)(struct sdhci_host *host);
void (*card_event)(struct sdhci_host *host);
}; };
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS

View File

@ -1244,7 +1244,8 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id)
u32 state; u32 state;
state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); 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); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN);
if (state & ~MASK_CLEAN) if (state & ~MASK_CLEAN)
@ -1369,7 +1370,11 @@ static int sh_mmcif_probe(struct platform_device *pdev)
ret = -ENOMEM; ret = -ENOMEM;
goto ealloch; goto ealloch;
} }
mmc_of_parse(mmc);
ret = mmc_of_parse(mmc);
if (ret < 0)
goto eofparse;
host = mmc_priv(mmc); host = mmc_priv(mmc);
host->mmc = mmc; host->mmc = mmc;
host->addr = reg; host->addr = reg;
@ -1464,6 +1469,7 @@ eclkupdate:
clk_put(host->hclk); clk_put(host->hclk);
eclkget: eclkget:
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
eofparse:
mmc_free_host(mmc); mmc_free_host(mmc);
ealloch: ealloch:
iounmap(reg); iounmap(reg);
@ -1501,8 +1507,6 @@ static int sh_mmcif_remove(struct platform_device *pdev)
if (irq[1] >= 0) if (irq[1] >= 0)
free_irq(irq[1], host); free_irq(irq[1], host);
platform_set_drvdata(pdev, NULL);
clk_disable(host->hclk); clk_disable(host->hclk);
mmc_free_host(host->mmc); mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);

View File

@ -46,14 +46,12 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
struct sh_mobile_sdhi { struct sh_mobile_sdhi {
struct clk *clk; struct clk *clk;
struct tmio_mmc_data mmc_data; struct tmio_mmc_data mmc_data;
struct sh_dmae_slave param_tx;
struct sh_dmae_slave param_rx;
struct tmio_mmc_dma dma_priv; struct tmio_mmc_dma dma_priv;
}; };
static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int *f) 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 tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
int ret = clk_enable(priv->clk); 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) 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 tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data); struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
clk_disable(priv->clk); 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) 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 = { 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; struct tmio_mmc_host *host;
int irq, ret, i = 0; int irq, ret, i = 0;
bool multiplexed_isr = true; bool multiplexed_isr = true;
struct tmio_mmc_dma *dma_priv;
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) { if (priv == NULL) {
@ -154,6 +153,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
} }
mmc_data = &priv->mmc_data; mmc_data = &priv->mmc_data;
dma_priv = &priv->dma_priv;
if (p) { if (p) {
if (p->init) { 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; mmc_data->get_cd = sh_mobile_sdhi_get_cd;
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { 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; * Yes, we have to provide slave IDs twice to TMIO:
priv->dma_priv.chan_priv_tx = &priv->param_tx.shdma_slave; * once as a filter parameter and once for channel
priv->dma_priv.chan_priv_rx = &priv->param_rx.shdma_slave; * configuration as an explicit slave ID
priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */ */
mmc_data->dma = &priv->dma_priv; 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 * All SDHI blocks support 2-byte and larger block sizes in 4-bit
* bus width mode. * 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 */ /* There must be at least one IRQ source */
if (!i) if (!i) {
ret = irq;
goto eirq; goto eirq;
}
} }
dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n",

View File

@ -112,8 +112,6 @@ static int tmio_mmc_remove(struct platform_device *pdev)
const struct mfd_cell *cell = mfd_get_cell(pdev); const struct mfd_cell *cell = mfd_get_cell(pdev);
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
if (mmc) { if (mmc) {
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
free_irq(platform_get_irq(pdev, 0), host); free_irq(platform_get_irq(pdev, 0), host);

View File

@ -40,6 +40,22 @@
struct tmio_mmc_data; 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 { struct tmio_mmc_host {
void __iomem *ctl; void __iomem *ctl;
unsigned long bus_shift; unsigned long bus_shift;
@ -48,8 +64,8 @@ struct tmio_mmc_host {
struct mmc_data *data; struct mmc_data *data;
struct mmc_host *mmc; struct mmc_host *mmc;
/* Controller power state */ /* Controller and card power state */
bool power; enum tmio_mmc_power power;
/* Callbacks for clock / power control */ /* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
@ -85,6 +101,7 @@ struct tmio_mmc_host {
unsigned long last_req_ts; unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */ struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug; bool native_hotplug;
bool resuming;
}; };
int tmio_mmc_host_probe(struct tmio_mmc_host **host, int tmio_mmc_host_probe(struct tmio_mmc_host **host,

View File

@ -261,42 +261,62 @@ out:
spin_unlock_irq(&host->lock); 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) 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 */ /* 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; return;
if (!host->chan_tx && !host->chan_rx) { 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; dma_cap_mask_t mask;
int ret;
if (!res)
return;
dma_cap_zero(mask); dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_SLAVE, mask);
host->chan_tx = dma_request_channel(mask, tmio_mmc_filter, host->chan_tx = dma_request_slave_channel_compat(mask,
pdata->dma->chan_priv_tx); pdata->dma->filter, pdata->dma->chan_priv_tx,
&host->pdev->dev, "tx");
dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__, dev_dbg(&host->pdev->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx); host->chan_tx);
if (!host->chan_tx) if (!host->chan_tx)
return; return;
host->chan_rx = dma_request_channel(mask, tmio_mmc_filter, if (pdata->dma->chan_priv_tx)
pdata->dma->chan_priv_rx); 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__, dev_dbg(&host->pdev->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx); host->chan_rx);
if (!host->chan_rx) if (!host->chan_rx)
goto ereqrx; 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); host->bounce_buf = (u8 *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!host->bounce_buf) if (!host->bounce_buf)
goto ebouncebuf; goto ebouncebuf;
@ -310,9 +330,11 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
return; return;
ebouncebuf: ebouncebuf:
ecfgrx:
dma_release_channel(host->chan_rx); dma_release_channel(host->chan_rx);
host->chan_rx = NULL; host->chan_rx = NULL;
ereqrx: ereqrx:
ecfgtx:
dma_release_channel(host->chan_tx); dma_release_channel(host->chan_tx);
host->chan_tx = NULL; host->chan_tx = NULL;
} }

View File

@ -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. * is kept positive, so no suspending actually takes place.
*/ */
if (ios->power_mode == MMC_POWER_ON && ios->clock) { if (ios->power_mode == MMC_POWER_ON && ios->clock) {
if (!host->power) { if (host->power != TMIO_MMC_ON_RUN) {
tmio_mmc_clk_update(mmc); tmio_mmc_clk_update(mmc);
pm_runtime_get_sync(dev); 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); tmio_mmc_set_clock(host, ios->clock);
if (!host->power) { if (host->power == TMIO_MMC_OFF_STOP)
/* power up SD card and the bus */ /* power up SD card and the bus */
tmio_mmc_power_on(host, ios->vdd); tmio_mmc_power_on(host, ios->vdd);
host->power = true; host->power = TMIO_MMC_ON_RUN;
}
/* start bus clock */ /* start bus clock */
tmio_mmc_clk_start(host); tmio_mmc_clk_start(host);
} else if (ios->power_mode != MMC_POWER_UP) { } else if (ios->power_mode != MMC_POWER_UP) {
if (host->power) { struct tmio_mmc_data *pdata = host->pdata;
struct tmio_mmc_data *pdata = host->pdata; unsigned int old_power = host->power;
if (ios->power_mode == MMC_POWER_OFF)
if (old_power != TMIO_MMC_OFF_STOP) {
if (ios->power_mode == MMC_POWER_OFF) {
tmio_mmc_power_off(host); 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); tmio_mmc_clk_stop(host);
host->power = false;
pm_runtime_put(dev); pm_runtime_put(dev);
if (pdata->clk_disable) if (pdata->clk_disable)
pdata->clk_disable(host->pdev); pdata->clk_disable(host->pdev);
} }
} }
if (host->power) { if (host->power != TMIO_MMC_OFF_STOP) {
switch (ios->bus_width) { switch (ios->bus_width) {
case MMC_BUS_WIDTH_1: case MMC_BUS_WIDTH_1:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0); 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) if (!mmc)
return -ENOMEM; return -ENOMEM;
mmc_of_parse(mmc); ret = mmc_of_parse(mmc);
if (ret < 0)
goto host_free;
pdata->dev = &pdev->dev; pdata->dev = &pdev->dev;
_host = mmc_priv(mmc); _host = mmc_priv(mmc);
@ -1025,7 +1040,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
mmc->caps & MMC_CAP_NONREMOVABLE || mmc->caps & MMC_CAP_NONREMOVABLE ||
mmc->slot.cd_irq >= 0); mmc->slot.cd_irq >= 0);
_host->power = false; _host->power = TMIO_MMC_OFF_STOP;
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev); ret = pm_runtime_resume(&pdev->dev);
if (ret < 0) if (ret < 0)
@ -1154,10 +1169,10 @@ int tmio_mmc_host_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true); tmio_mmc_enable_dma(host, true);
/* The MMC core will perform the complete set up */ /* The MMC core will perform the complete set up */
host->resuming = true;
return mmc_resume_host(mmc); return mmc_resume_host(mmc);
} }
EXPORT_SYMBOL(tmio_mmc_host_resume); 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 mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
tmio_mmc_reset(host);
tmio_mmc_enable_dma(host, true); tmio_mmc_enable_dma(host, true);
return 0; return 0;

View File

@ -927,8 +927,6 @@ static int wmt_mci_remove(struct platform_device *pdev)
mmc_free_host(mmc); mmc_free_host(mmc);
platform_set_drvdata(pdev, NULL);
dev_info(&pdev->dev, "WMT MCI device removed\n"); dev_info(&pdev->dev, "WMT MCI device removed\n");
return 0; return 0;

View File

@ -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_pwr(void __iomem *cnf, int shift, int state);
void tmio_core_mmc_clk_div(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 { struct tmio_mmc_dma {
void *chan_priv_tx; void *chan_priv_tx;
void *chan_priv_rx; void *chan_priv_rx;
int slave_id_tx;
int slave_id_rx;
int alignment_shift; int alignment_shift;
bool (*filter)(struct dma_chan *chan, void *arg);
}; };
struct tmio_mmc_host; struct tmio_mmc_host;

View File

@ -94,7 +94,11 @@ struct mmc_ext_csd {
u8 raw_ext_csd_structure; /* 194 */ u8 raw_ext_csd_structure; /* 194 */
u8 raw_card_type; /* 196 */ u8 raw_card_type; /* 196 */
u8 out_of_int_time; /* 198 */ 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_hc_erase_gap_size; /* 221 */
u8 raw_erase_timeout_mult; /* 223 */ u8 raw_erase_timeout_mult; /* 223 */
u8 raw_hc_erase_grp_size; /* 224 */ u8 raw_hc_erase_grp_size; /* 224 */
@ -102,6 +106,10 @@ struct mmc_ext_csd {
u8 raw_sec_erase_mult; /* 230 */ u8 raw_sec_erase_mult; /* 230 */
u8 raw_sec_feature_support;/* 231 */ u8 raw_sec_feature_support;/* 231 */
u8 raw_trim_mult; /* 232 */ 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_bkops_status; /* 246 */
u8 raw_sectors[4]; /* 212 - 4 bytes */ u8 raw_sectors[4]; /* 212 - 4 bytes */
@ -512,6 +520,7 @@ struct mmc_driver {
void (*remove)(struct mmc_card *); void (*remove)(struct mmc_card *);
int (*suspend)(struct mmc_card *); int (*suspend)(struct mmc_card *);
int (*resume)(struct mmc_card *); int (*resume)(struct mmc_card *);
void (*shutdown)(struct mmc_card *);
}; };
extern int mmc_register_driver(struct mmc_driver *); extern int mmc_register_driver(struct mmc_driver *);

View File

@ -96,6 +96,8 @@ struct mmc_command {
*/ */
unsigned int cmd_timeout_ms; /* in milliseconds */ 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_data *data; /* data segment associated with cmd */
struct mmc_request *mrq; /* associated request */ 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 void mmc_release_host(struct mmc_host *host);
extern int mmc_try_claim_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_flush_cache(struct mmc_card *);
extern int mmc_detect_card_removed(struct mmc_host *host); extern int mmc_detect_card_removed(struct mmc_host *host);

View File

@ -239,7 +239,7 @@ struct mmc_host {
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */ #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_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_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_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */ #define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */ #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_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_CACHE_CTRL (1 << 1) /* Allow cache control */ #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_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_NO_SLEEP_CMD (1 << 4) /* Don't allow sleep command */
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */ #define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
@ -272,7 +272,6 @@ struct mmc_host {
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
MMC_CAP2_HS200_1_2V_SDR) MMC_CAP2_HS200_1_2V_SDR)
#define MMC_CAP2_BROKEN_VOLTAGE (1 << 7) /* Use the broken voltage */ #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_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_CD_ACTIVE_HIGH (1 << 10) /* Card-detect signal active high */
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11) /* Write-protect 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 | \ #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
MMC_CAP2_PACKED_WR) MMC_CAP2_PACKED_WR)
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ #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 */ 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 *); int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *); void mmc_remove_host(struct mmc_host *);
void mmc_free_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) 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 #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 *); int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
/* Module parameter */ /* Module parameter */

View File

@ -95,6 +95,9 @@ struct sdhci_host {
/* The system physically doesn't support 1.8v, even if the host does */ /* 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_NO_1_8_V (1<<2)
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) #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 */ int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */ void __iomem *ioaddr; /* Mapped address */
@ -126,7 +129,7 @@ struct sdhci_host {
#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
#define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq 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 */ #define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
unsigned int version; /* SDHCI spec. version */ unsigned int version; /* SDHCI spec. version */
@ -139,6 +142,7 @@ struct sdhci_host {
u8 pwr; /* Current voltage */ u8 pwr; /* Current voltage */
bool runtime_suspended; /* Host is runtime suspended */ bool runtime_suspended; /* Host is runtime suspended */
bool bus_on; /* Bus power prevents runtime suspend */
struct mmc_request *mrq; /* Current request */ struct mmc_request *mrq; /* Current request */
struct mmc_command *cmd; /* Current command */ struct mmc_command *cmd; /* Current command */

View File

@ -40,5 +40,6 @@ struct esdhc_platform_data {
enum wp_types wp_type; enum wp_types wp_type;
enum cd_types cd_type; enum cd_types cd_type;
int max_bus_width; int max_bus_width;
unsigned int f_max;
}; };
#endif /* __ASM_ARCH_IMX_ESDHC_H */ #endif /* __ASM_ARCH_IMX_ESDHC_H */