MMC highlights for 3.13:

Core:
  - Improve runtime PM support, remove mmc_{suspend,resume}_host().
  - Add MMC_CAP_RUNTIME_RESUME, for delaying MMC resume until we're
    outside of the resume sequence (in runtime_resume) to decrease
    system resume time.
 
 Drivers:
  - dw_mmc: Support HS200 mode.
  - sdhci-eshdc-imx: Support SD3.0 SDR clock tuning, DDR on IMX6.
  - sdhci-pci: Add support for Intel Clovertrail and Merrifield.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.13 (GNU/Linux)
 
 iQIcBAABAgAGBQJSiDC6AAoJEHNBYZ7TNxYMlfUP/RReKYyFc5A6X/1WEkpc4jZ8
 tjJcoygrjhGc2lGg/ewZAHZ4oZJlHYwDe3cY+82X8MKuZdjQlWqHM0TpZB33d0NP
 jzTtjzT92A4BOlJ2jDOzLxPWR7Q/bpbycGJZ6w6rNxf3SOSom0JyCSBobB0Uwymi
 UI9EaMj4NS69GDPIk5IWjVPuYApVkmt16LKI0qB8bUbMWYcn9WwGknWe0uh+bKRn
 i3npn6JFMmmc03qhuhaRYL8FG5qJfnZbNL9mBRCt/D9InsJlcsN/isyvhoI9m9W6
 mYJtE6mJrnN+mVQJy90BnN7lSbDPpeDod8hB42We/kB+vzLpKGhGTsFPkdD0esHI
 CScHkpc1i3z+RdFqf/ZdL3mTk5DaIWyxBHZwqjWc1Cyf+O7xfYuTQwXWX7gDyDo+
 xKkttR5Hn9mJUkCUxww3ky7XDh3d7zeoIcQpnlJMIZZL8MG6QRnPJfVQLJXT+BF8
 2iQAj/qw47gt5+KMvkqHwB0iO1hlQoVoH3bRU7bywULe0zn5pGa2W4BFkN2nNJe/
 eKZsx3aB5ToaXHbZMIvw4zrVnPJfTp1A+GBTcztpHUk1weRseoqIzqNhSH/L+YGt
 JeyMhST/B6DGGXVkiO2kFhfqIppcN5vJgB/tyHzF0gkAq1K1GJSYeH+9AVL6QXeS
 G956hL5mY8CP0/ONNs93
 =JDGc
 -----END PGP SIGNATURE-----

Merge tag 'mmc-updates-for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC updates from Chris Ball:
 "MMC highlights for 3.13:

  Core:
   - Improve runtime PM support, remove mmc_{suspend,resume}_host().
   - Add MMC_CAP_RUNTIME_RESUME, for delaying MMC resume until we're
     outside of the resume sequence (in runtime_resume) to decrease
     system resume time.

  Drivers:
   - dw_mmc: Support HS200 mode.
   - sdhci-eshdc-imx: Support SD3.0 SDR clock tuning, DDR on IMX6.
   - sdhci-pci: Add support for Intel Clovertrail and Merrifield"

* tag 'mmc-updates-for-3.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (108 commits)
  mmc: wbsd: Silence compiler warning
  mmc: core: Silence compiler warning in __mmc_switch
  mmc: sh_mmcif: Convert to clk_prepare|unprepare
  mmc: sh_mmcif: Convert to PM macros when defining dev_pm_ops
  mmc: dw_mmc: exynos: Revert the sdr_timing assignment
  mmc: sdhci: Avoid needless loop while handling SDIO interrupts in sdhci_irq
  mmc: core: Add MMC_CAP_RUNTIME_RESUME to resume at runtime_resume
  mmc: core: Improve runtime PM support during suspend/resume for sd/mmc
  mmc: core: Remove redundant mmc_power_up|off at runtime callbacks
  mmc: Don't force card to active state when entering suspend/shutdown
  MIPS: db1235: Don't use MMC_CLKGATE
  mmc: core: Remove deprecated mmc_suspend|resume_host APIs
  mmc: mmci: Move away from using deprecated APIs
  mmc: via-sdmmc: Move away from using deprecated APIs
  mmc: tmio: Move away from using deprecated APIs
  mmc: sh_mmcif: Move away from using deprecated APIs
  mmc: sdricoh_cs: Move away from using deprecated APIs
  mmc: rtsx: Remove redundant suspend and resume callbacks
  mmc: wbsd: Move away from using deprecated APIs
  mmc: pxamci: Remove redundant suspend and resume callbacks
  ...
This commit is contained in:
Linus Torvalds 2013-11-18 14:47:30 -08:00
commit c2ac2ae44d
54 changed files with 1826 additions and 1248 deletions

View file

@ -12,6 +12,11 @@ Required properties:
Optional properties:
- fsl,cd-controller : Indicate to use controller internal card detection
- fsl,wp-controller : Indicate to use controller internal write protection
- fsl,delay-line : Specify the number of delay cells for override mode.
This is used to set the clock delay for DLL(Delay Line) on override mode
to select a proper data sampling window in case the clock quality is not good
due to signal path is too long on the board. Please refer to eSDHC/uSDHC
chapter, DLL (Delay Line) section in RM for details.
Examples:

View file

@ -52,6 +52,9 @@ Optional properties:
is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time.
* clock-freq-min-max: Minimum and Maximum clock frequency for card output
clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
* num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value
@ -66,6 +69,10 @@ Optional properties:
* supports-highspeed: Enables support for high speed cards (up to 50MHz)
* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode
* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode
* broken-cd: as documented in mmc core bindings.
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
@ -93,8 +100,10 @@ board specific portions as listed below.
dwmmc0@12200000 {
clock-frequency = <400000000>;
clock-freq-min-max = <400000 200000000>;
num-slots = <1>;
supports-highspeed;
caps2-mmc-hs200-1_8v;
broken-cd;
fifo-depth = <0x80>;
card-detect-delay = <200>;

View file

@ -351,7 +351,6 @@ CONFIG_USB_OHCI_HCD=y
CONFIG_USB_OHCI_HCD_PLATFORM=y
CONFIG_USB_STORAGE=y
CONFIG_MMC=y
CONFIG_MMC_CLKGATE=y
CONFIG_MMC_AU1X=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y

View file

@ -15,6 +15,7 @@
#include <linux/mmc/sh_mmcif.h>
#include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mtd/physmap.h>
#include <linux/mfd/tmio.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/io.h>

View file

@ -2448,7 +2448,6 @@ static int _mmc_blk_suspend(struct mmc_card *card)
struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) {
pm_runtime_get_sync(&card->dev);
mmc_queue_suspend(&md->queue);
list_for_each_entry(part_md, &md->part, part) {
mmc_queue_suspend(&part_md->queue);
@ -2483,7 +2482,6 @@ static int mmc_blk_resume(struct mmc_card *card)
list_for_each_entry(part_md, &md->part, part) {
mmc_queue_resume(&part_md->queue);
}
pm_runtime_put(&card->dev);
}
return 0;
}

View file

@ -342,7 +342,7 @@ int mmc_add_card(struct mmc_card *card)
break;
}
if (mmc_sd_card_uhs(card) &&
if (mmc_card_uhs(card) &&
(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];

View file

@ -23,6 +23,7 @@
#include <linux/log2.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
#include <linux/suspend.h>
#include <linux/fault-inject.h>
#include <linux/random.h>
@ -301,7 +302,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
}
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
@ -917,31 +918,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
EXPORT_SYMBOL(__mmc_claim_host);
/**
* mmc_try_claim_host - try exclusively to claim a host
* @host: mmc host to claim
*
* Returns %1 if the host is claimed, %0 otherwise.
*/
int mmc_try_claim_host(struct mmc_host *host)
{
int claimed_host = 0;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
if (!host->claimed || host->claimer == current) {
host->claimed = 1;
host->claimer = current;
host->claim_cnt += 1;
claimed_host = 1;
}
spin_unlock_irqrestore(&host->lock, flags);
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
host->ops->enable(host);
return claimed_host;
}
EXPORT_SYMBOL(mmc_try_claim_host);
/**
* mmc_release_host - release a host
* @host: mmc host to release
@ -1382,22 +1358,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{
int bit;
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
dev_warn(mmc_dev(host),
"card claims to support voltages below defined range\n");
ocr &= ~0x7F;
}
ocr &= host->ocr_avail;
if (!ocr) {
dev_warn(mmc_dev(host), "no support for card's volts\n");
return 0;
}
bit = ffs(ocr);
if (bit) {
bit -= 1;
if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
bit = ffs(ocr) - 1;
ocr &= 3 << bit;
mmc_host_clk_hold(host);
host->ios.vdd = bit;
mmc_set_ios(host);
mmc_host_clk_release(host);
mmc_power_cycle(host, ocr);
} else {
pr_warning("%s: host doesn't support card's voltages\n",
mmc_hostname(host));
ocr = 0;
bit = fls(ocr) - 1;
ocr &= 3 << bit;
if (bit != host->ios.vdd)
dev_warn(mmc_dev(host), "exceeding card's volts\n");
}
return ocr;
@ -1422,7 +1407,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, u32 ocr)
{
struct mmc_command cmd = {0};
int err = 0;
@ -1504,7 +1489,7 @@ power_cycle:
if (err) {
pr_debug("%s: Signal voltage switch failed, "
"power cycling card\n", mmc_hostname(host));
mmc_power_cycle(host);
mmc_power_cycle(host, ocr);
}
mmc_host_clk_release(host);
@ -1545,22 +1530,14 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
* If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage.
*/
void mmc_power_up(struct mmc_host *host)
void mmc_power_up(struct mmc_host *host, u32 ocr)
{
int bit;
if (host->ios.power_mode == MMC_POWER_ON)
return;
mmc_host_clk_hold(host);
/* If ocr is set, we use it */
if (host->ocr)
bit = ffs(host->ocr) - 1;
else
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
host->ios.vdd = fls(ocr) - 1;
if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH;
else
@ -1604,13 +1581,6 @@ void mmc_power_off(struct mmc_host *host)
host->ios.clock = 0;
host->ios.vdd = 0;
/*
* Reset ocr mask to be the highest possible voltage supported for
* this mmc host. This value will be used at next power up.
*/
host->ocr = 1 << (fls(host->ocr_avail) - 1);
if (!mmc_host_is_spi(host)) {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE;
@ -1630,12 +1600,12 @@ void mmc_power_off(struct mmc_host *host)
mmc_host_clk_release(host);
}
void mmc_power_cycle(struct mmc_host *host)
void mmc_power_cycle(struct mmc_host *host, u32 ocr)
{
mmc_power_off(host);
/* Wait at least 1 ms according to SD spec */
mmc_delay(1);
mmc_power_up(host);
mmc_power_up(host, ocr);
}
/*
@ -1723,6 +1693,28 @@ void mmc_detach_bus(struct mmc_host *host)
mmc_bus_put(host);
}
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
bool cd_irq)
{
#ifdef CONFIG_MMC_DEBUG
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags);
#endif
/*
* If the device is configured as wakeup, we prevent a new sleep for
* 5 s to give provision for user space to consume the event.
*/
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
device_can_wakeup(mmc_dev(host)))
pm_wakeup_event(mmc_dev(host), 5000);
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
}
/**
* mmc_detect_change - process change of state on a MMC socket
* @host: host which changed state.
@ -1735,16 +1727,8 @@ void mmc_detach_bus(struct mmc_host *host)
*/
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->removed);
spin_unlock_irqrestore(&host->lock, flags);
#endif
host->detect_change = 1;
mmc_schedule_delayed_work(&host->detect, delay);
_mmc_detect_change(host, delay, true);
}
EXPORT_SYMBOL(mmc_detect_change);
void mmc_init_erase(struct mmc_card *card)
@ -2334,7 +2318,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
pr_info("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init);
#endif
mmc_power_up(host);
mmc_power_up(host, host->ocr_avail);
/*
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
@ -2423,7 +2407,7 @@ int mmc_detect_card_removed(struct mmc_host *host)
* rescan handle the card removal.
*/
cancel_delayed_work(&host->detect);
mmc_detect_change(host, 0);
_mmc_detect_change(host, 0, false);
}
}
@ -2504,8 +2488,8 @@ void mmc_start_host(struct mmc_host *host)
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host);
else
mmc_power_up(host);
mmc_detect_change(host, 0);
mmc_power_up(host, host->ocr_avail);
_mmc_detect_change(host, 0, false);
}
void mmc_stop_host(struct mmc_host *host)
@ -2583,7 +2567,7 @@ int mmc_power_restore_host(struct mmc_host *host)
return -EINVAL;
}
mmc_power_up(host);
mmc_power_up(host, host->card->ocr);
ret = host->bus_ops->power_restore(host);
mmc_bus_put(host);
@ -2657,28 +2641,6 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
#ifdef CONFIG_PM
/**
* mmc_suspend_host - suspend a host
* @host: mmc host
*/
int mmc_suspend_host(struct mmc_host *host)
{
/* This function is deprecated */
return 0;
}
EXPORT_SYMBOL(mmc_suspend_host);
/**
* mmc_resume_host - resume a previously suspended host
* @host: mmc host
*/
int mmc_resume_host(struct mmc_host *host)
{
/* This function is deprecated */
return 0;
}
EXPORT_SYMBOL(mmc_resume_host);
/* Do the card removal on suspend if card is assumed removeable
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
to sync the card.
@ -2724,7 +2686,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags);
mmc_detect_change(host, 0);
_mmc_detect_change(host, 0, false);
}

View file

@ -42,13 +42,13 @@ void mmc_set_ungated(struct mmc_host *host);
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
void mmc_power_up(struct mmc_host *host);
void mmc_power_up(struct mmc_host *host, u32 ocr);
void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
static inline void mmc_delay(unsigned int ms)
{

View file

@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@ -934,6 +935,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto err;
}
card->ocr = ocr;
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
@ -1404,9 +1406,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
if (notify_type == EXT_CSD_POWER_OFF_LONG)
timeout = card->ext_csd.power_off_longtime;
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout);
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
notify_type, timeout, true, false);
if (err)
pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout);
@ -1477,6 +1479,9 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
if (mmc_card_doing_bkops(host->card)) {
err = mmc_stop_bkops(host->card);
if (err)
@ -1496,19 +1501,54 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
err = mmc_deselect_cards(host);
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
if (!err)
if (!err) {
mmc_power_off(host);
mmc_card_set_suspended(host->card);
}
out:
mmc_release_host(host);
return err;
}
/*
* Suspend callback from host.
* Suspend callback
*/
static int mmc_suspend(struct mmc_host *host)
{
return _mmc_suspend(host, true);
int err;
err = _mmc_suspend(host, true);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
return err;
}
/*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
*/
static int _mmc_resume(struct mmc_host *host)
{
int err = 0;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
if (!mmc_card_suspended(host->card))
goto out;
mmc_power_up(host, host->card->ocr);
err = mmc_init_card(host, host->card->ocr, host->card);
mmc_card_clr_suspended(host->card);
out:
mmc_release_host(host);
return err;
}
/*
@ -1516,31 +1556,38 @@ static int mmc_suspend(struct mmc_host *host)
*/
static int mmc_shutdown(struct mmc_host *host)
{
return _mmc_suspend(host, false);
}
int err = 0;
/*
* Resume callback from host.
*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
*/
static int mmc_resume(struct mmc_host *host)
{
int err;
/*
* In a specific case for poweroff notify, we need to resume the card
* before we can shutdown it properly.
*/
if (mmc_can_poweroff_notify(host->card) &&
!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
err = _mmc_resume(host);
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
err = mmc_init_card(host, host->ocr, host->card);
mmc_release_host(host);
if (!err)
err = _mmc_suspend(host, false);
return err;
}
/*
* Callback for resume.
*/
static int mmc_resume(struct mmc_host *host)
{
int err = 0;
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
err = _mmc_resume(host);
pm_runtime_set_active(&host->card->dev);
pm_runtime_mark_last_busy(&host->card->dev);
}
pm_runtime_enable(&host->card->dev);
return err;
}
/*
* Callback for runtime_suspend.
@ -1552,18 +1599,11 @@ static int mmc_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
mmc_claim_host(host);
err = mmc_suspend(host);
if (err) {
err = _mmc_suspend(host, true);
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;
}
@ -1574,18 +1614,14 @@ static int mmc_runtime_resume(struct mmc_host *host)
{
int err;
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
return 0;
mmc_claim_host(host);
mmc_power_up(host);
err = mmc_resume(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;
}
@ -1595,7 +1631,7 @@ static int mmc_power_restore(struct mmc_host *host)
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
mmc_claim_host(host);
ret = mmc_init_card(host, host->ocr, host->card);
ret = mmc_init_card(host, host->card->ocr, host->card);
mmc_release_host(host);
return ret;
@ -1640,7 +1676,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
int mmc_attach_mmc(struct mmc_host *host)
{
int err;
u32 ocr;
u32 ocr, rocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
@ -1666,23 +1702,12 @@ int mmc_attach_mmc(struct mmc_host *host)
goto err;
}
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
host->ocr = mmc_select_voltage(host, ocr);
rocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage of the card?
*/
if (!host->ocr) {
if (!rocr) {
err = -EINVAL;
goto err;
}
@ -1690,7 +1715,7 @@ int mmc_attach_mmc(struct mmc_host *host)
/*
* Detect and init the card.
*/
err = mmc_init_card(host, host->ocr, NULL);
err = mmc_init_card(host, rocr, NULL);
if (err)
goto err;

View file

@ -23,6 +23,40 @@
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
bool ignore_crc)
{
int err;
struct mmc_command cmd = {0};
BUG_ON(!card);
BUG_ON(!card->host);
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
if (ignore_crc)
cmd.flags &= ~MMC_RSP_CRC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status)
*status = cmd.resp[0];
return 0;
}
int mmc_send_status(struct mmc_card *card, u32 *status)
{
return __mmc_send_status(card, status, false);
}
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{
int err;
@ -370,16 +404,18 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
* @timeout_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type
* @send_status: send status cmd to poll for busy
*
* Modifies the EXT_CSD register for selected card.
*/
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms, bool use_busy_signal)
unsigned int timeout_ms, bool use_busy_signal, bool send_status)
{
int err;
struct mmc_command cmd = {0};
unsigned long timeout;
u32 status;
u32 status = 0;
bool ignore_crc = false;
BUG_ON(!card);
BUG_ON(!card->host);
@ -408,17 +444,37 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (!use_busy_signal)
return 0;
/* Must check status to be sure of no errors */
/*
* Must check status to be sure of no errors
* If CMD13 is to check the busy completion of the timing change,
* disable the check of CRC error.
*/
if (index == EXT_CSD_HS_TIMING &&
!(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
ignore_crc = true;
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
do {
err = mmc_send_status(card, &status);
if (err)
return err;
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
return err;
}
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
break;
if (mmc_host_is_spi(card->host))
break;
/*
* We are not allowed to issue a status command and the host
* does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
* rely on waiting for the stated timeout to be sufficient.
*/
if (!send_status) {
mmc_delay(timeout_ms);
return 0;
}
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
@ -445,36 +501,10 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{
return __mmc_switch(card, set, index, value, timeout_ms, true);
return __mmc_switch(card, set, index, value, timeout_ms, true, true);
}
EXPORT_SYMBOL_GPL(mmc_switch);
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
struct mmc_command cmd = {0};
BUG_ON(!card);
BUG_ON(!card->host);
cmd.opcode = MMC_SEND_STATUS;
if (!mmc_host_is_spi(card->host))
cmd.arg = card->rca << 16;
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
/* NOTE: callers are required to understand the difference
* between "native" and SPI format status words!
*/
if (status)
*status = cmd.resp[0];
return 0;
}
static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len)

View file

@ -13,6 +13,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@ -721,6 +722,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
int err;
u32 max_current;
int retries = 10;
u32 pocr = ocr;
try_again:
if (!retries) {
@ -773,7 +775,8 @@ try_again:
*/
if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
pocr);
if (err == -EAGAIN) {
retries--;
goto try_again;
@ -935,6 +938,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (IS_ERR(card))
return PTR_ERR(card);
card->ocr = ocr;
card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
@ -1064,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host)
}
}
/*
* Suspend callback from host.
*/
static int mmc_sd_suspend(struct mmc_host *host)
static int _mmc_sd_suspend(struct mmc_host *host)
{
int err = 0;
@ -1075,34 +1076,77 @@ static int mmc_sd_suspend(struct mmc_host *host)
BUG_ON(!host->card);
mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED;
if (!err)
if (!err) {
mmc_power_off(host);
mmc_card_set_suspended(host->card);
}
out:
mmc_release_host(host);
return err;
}
/*
* Callback for suspend
*/
static int mmc_sd_suspend(struct mmc_host *host)
{
int err;
err = _mmc_sd_suspend(host);
if (!err) {
pm_runtime_disable(&host->card->dev);
pm_runtime_set_suspended(&host->card->dev);
}
return err;
}
/*
* Resume callback from host.
*
* This function tries to determine if the same card is still present
* and, if so, restore all state to it.
*/
static int mmc_sd_resume(struct mmc_host *host)
static int _mmc_sd_resume(struct mmc_host *host)
{
int err;
int err = 0;
BUG_ON(!host);
BUG_ON(!host->card);
mmc_claim_host(host);
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
err = mmc_sd_init_card(host, host->ocr, host->card);
if (!mmc_card_suspended(host->card))
goto out;
mmc_power_up(host, host->card->ocr);
err = mmc_sd_init_card(host, host->card->ocr, host->card);
mmc_card_clr_suspended(host->card);
out:
mmc_release_host(host);
return err;
}
/*
* Callback for resume
*/
static int mmc_sd_resume(struct mmc_host *host)
{
int err = 0;
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
err = _mmc_sd_resume(host);
pm_runtime_set_active(&host->card->dev);
pm_runtime_mark_last_busy(&host->card->dev);
}
pm_runtime_enable(&host->card->dev);
return err;
}
@ -1117,18 +1161,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0;
mmc_claim_host(host);
err = mmc_sd_suspend(host);
if (err) {
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;
}
@ -1139,18 +1176,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
{
int err;
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
return 0;
mmc_claim_host(host);
mmc_power_up(host);
err = mmc_sd_resume(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;
}
@ -1160,7 +1193,7 @@ static int mmc_sd_power_restore(struct mmc_host *host)
host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host);
ret = mmc_sd_init_card(host, host->ocr, host->card);
ret = mmc_sd_init_card(host, host->card->ocr, host->card);
mmc_release_host(host);
return ret;
@ -1205,7 +1238,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
int mmc_attach_sd(struct mmc_host *host)
{
int err;
u32 ocr;
u32 ocr, rocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
@ -1229,31 +1262,12 @@ int mmc_attach_sd(struct mmc_host *host)
goto err;
}
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
if ((ocr & MMC_VDD_165_195) &&
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
pr_warning("%s: SD card claims to support the "
"incompletely defined 'low voltage range'. This "
"will be ignored.\n", mmc_hostname(host));
ocr &= ~MMC_VDD_165_195;
}
host->ocr = mmc_select_voltage(host, ocr);
rocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
if (!rocr) {
err = -EINVAL;
goto err;
}
@ -1261,7 +1275,7 @@ int mmc_attach_sd(struct mmc_host *host)
/*
* Detect and init the card.
*/
err = mmc_sd_init_card(host, host->ocr, NULL);
err = mmc_sd_init_card(host, rocr, NULL);
if (err)
goto err;

View file

@ -593,23 +593,28 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *card;
int err;
int retries = 10;
u32 rocr = 0;
u32 ocr_card = ocr;
BUG_ON(!host);
WARN_ON(!host->claimed);
/* to query card if 1.8V signalling is supported */
if (mmc_host_uhs(host))
ocr |= R4_18V_PRESENT;
try_again:
if (!retries) {
pr_warning("%s: Skipping voltage switch\n",
mmc_hostname(host));
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
/*
* Inform the card of the voltage
*/
if (!powered_resume) {
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
err = mmc_send_io_op_cond(host, ocr, &rocr);
if (err)
goto err;
}
@ -632,8 +637,8 @@ try_again:
goto err;
}
if ((ocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) {
if ((rocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
@ -663,8 +668,9 @@ try_again:
* systems that claim 1.8v signalling in fact do not support
* it.
*/
if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
ocr);
if (err == -EAGAIN) {
sdio_reset(host);
mmc_go_idle(host);
@ -674,12 +680,10 @@ try_again:
goto try_again;
} else if (err) {
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
err = 0;
} else {
ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
}
/*
@ -759,6 +763,7 @@ try_again:
card = oldcard;
}
card->ocr = ocr_card;
mmc_fixup_device(card, NULL);
if (card->type == MMC_TYPE_SD_COMBO) {
@ -981,8 +986,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
/* Restore power if needed */
if (!mmc_card_keep_power(host)) {
mmc_power_up(host);
mmc_select_voltage(host, host->ocr);
mmc_power_up(host, host->card->ocr);
/*
* Tell runtime PM core we just powered up the card,
* since it still believes the card is powered off.
@ -1000,7 +1004,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(host);
mmc_go_idle(host);
err = mmc_sdio_init_card(host, host->ocr, host->card,
err = mmc_sdio_init_card(host, host->card->ocr, host->card,
mmc_card_keep_power(host));
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */
@ -1040,7 +1044,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
static int mmc_sdio_power_restore(struct mmc_host *host)
{
int ret;
u32 ocr;
BUG_ON(!host);
BUG_ON(!host->card);
@ -1062,32 +1065,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
* harmless in other situations.
*
* With these steps taken, mmc_select_voltage() is also required to
* restore the correct voltage setting of the card.
*/
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
ret = mmc_send_io_op_cond(host, 0, &ocr);
ret = mmc_send_io_op_cond(host, 0, NULL);
if (ret)
goto out;
if (host->ocr_avail_sdio)
host->ocr_avail = host->ocr_avail_sdio;
host->ocr = mmc_select_voltage(host, ocr & ~0x7F);
if (!host->ocr) {
ret = -EINVAL;
goto out;
}
if (mmc_host_uhs(host))
/* to query card if 1.8V signalling is supported */
host->ocr |= R4_18V_PRESENT;
ret = mmc_sdio_init_card(host, host->ocr, host->card,
ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
mmc_card_keep_power(host));
if (!ret && host->sdio_irqs)
mmc_signal_sdio_irq(host);
@ -1108,7 +1096,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host)
static int mmc_sdio_runtime_resume(struct mmc_host *host)
{
/* Restore power and re-initialize. */
mmc_power_up(host);
mmc_power_up(host, host->card->ocr);
return mmc_sdio_power_restore(host);
}
@ -1131,7 +1119,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
int mmc_attach_sdio(struct mmc_host *host)
{
int err, i, funcs;
u32 ocr;
u32 ocr, rocr;
struct mmc_card *card;
BUG_ON(!host);
@ -1145,23 +1133,13 @@ int mmc_attach_sdio(struct mmc_host *host)
if (host->ocr_avail_sdio)
host->ocr_avail = host->ocr_avail_sdio;
/*
* Sanity check the voltages that the card claims to
* support.
*/
if (ocr & 0x7F) {
pr_warning("%s: card claims to support voltages "
"below the defined range. These will be ignored.\n",
mmc_hostname(host));
ocr &= ~0x7F;
}
host->ocr = mmc_select_voltage(host, ocr);
rocr = mmc_select_voltage(host, ocr);
/*
* Can we support the voltage(s) of the card(s)?
*/
if (!host->ocr) {
if (!rocr) {
err = -EINVAL;
goto err;
}
@ -1169,22 +1147,10 @@ int mmc_attach_sdio(struct mmc_host *host)
/*
* Detect and init the card.
*/
if (mmc_host_uhs(host))
/* to query card if 1.8V signalling is supported */
host->ocr |= R4_18V_PRESENT;
err = mmc_sdio_init_card(host, rocr, NULL, 0);
if (err)
goto err;
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
if (err) {
if (err == -EAGAIN) {
/*
* Retry initialization with S18R set to 0.
*/
host->ocr &= ~R4_18V_PRESENT;
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
}
if (err)
goto err;
}
card = host->card;
/*

View file

@ -255,7 +255,6 @@ struct atmel_mci_slot {
#define ATMCI_CARD_PRESENT 0
#define ATMCI_CARD_NEED_INIT 1
#define ATMCI_SHUTDOWN 2
#define ATMCI_SUSPENDED 3
int detect_pin;
int wp_pin;
@ -589,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data)
if (host->mrq->cmd->data) {
host->mrq->cmd->data->error = -ETIMEDOUT;
host->data = NULL;
/*
* With some SDIO modules, sometimes DMA transfer hangs. If
* stop_transfer() is not called then the DMA request is not
* removed, following ones are queued and never computed.
*/
if (host->state == STATE_DATA_XFER)
host->stop_transfer(host);
} else {
host->mrq->cmd->error = -ETIMEDOUT;
host->cmd = NULL;
@ -1803,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv)
if (unlikely(status)) {
host->stop_transfer(host);
host->data = NULL;
if (status & ATMCI_DTOE) {
data->error = -ETIMEDOUT;
} else if (status & ATMCI_DCRCE) {
data->error = -EILSEQ;
} else {
data->error = -EIO;
if (data) {
if (status & ATMCI_DTOE) {
data->error = -ETIMEDOUT;
} else if (status & ATMCI_DCRCE) {
data->error = -EILSEQ;
} else {
data->error = -EIO;
}
}
}
@ -2520,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int atmci_suspend(struct device *dev)
{
struct atmel_mci *host = dev_get_drvdata(dev);
int i;
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
struct atmel_mci_slot *slot = host->slot[i];
int ret;
if (!slot)
continue;
ret = mmc_suspend_host(slot->mmc);
if (ret < 0) {
while (--i >= 0) {
slot = host->slot[i];
if (slot
&& test_bit(ATMCI_SUSPENDED, &slot->flags)) {
mmc_resume_host(host->slot[i]->mmc);
clear_bit(ATMCI_SUSPENDED, &slot->flags);
}
}
return ret;
} else {
set_bit(ATMCI_SUSPENDED, &slot->flags);
}
}
return 0;
}
static int atmci_resume(struct device *dev)
{
struct atmel_mci *host = dev_get_drvdata(dev);
int i;
int ret = 0;
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
struct atmel_mci_slot *slot = host->slot[i];
int err;
slot = host->slot[i];
if (!slot)
continue;
if (!test_bit(ATMCI_SUSPENDED, &slot->flags))
continue;
err = mmc_resume_host(slot->mmc);
if (err < 0)
ret = err;
else
clear_bit(ATMCI_SUSPENDED, &slot->flags);
}
return ret;
}
#endif
static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove),
.driver = {
.name = "atmel_mci",
.pm = &atmci_pm,
.of_match_table = of_match_ptr(atmci_dt_ids),
},
};

View file

@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct au1xmmc_host *host = platform_get_drvdata(pdev);
int ret;
ret = mmc_suspend_host(host->mmc);
if (ret)
return ret;
au_writel(0, HOST_CONFIG2(host));
au_writel(0, HOST_CONFIG(host));
@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev)
au1xmmc_reset_controller(host);
return mmc_resume_host(host->mmc);
return 0;
}
#else
#define au1xmmc_suspend NULL

View file

@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Disable 4 bit SDIO */
cfg &= ~SD4E;
}
bfin_write_SDH_CFG(cfg);
host->power_mode = ios->power_mode;
#ifndef RSI_BLKSZ
@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
cfg &= ~SD_CMD_OD;
# endif
if (ios->power_mode != MMC_POWER_OFF)
cfg |= PWR_ON;
else
@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_ctl |= CLK_E;
host->clk_div = clk_div;
bfin_write_SDH_CLK_CTL(clk_ctl);
} else
sdh_stop_clock(host);
@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int sdh_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct bfin_sd_host *drv_data = get_sdh_data(dev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc);
peripheral_free_list(drv_data->pin_req);
return ret;
return 0;
}
static int sdh_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct bfin_sd_host *drv_data = get_sdh_data(dev);
int ret = 0;
@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev)
}
sdh_reset();
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
}
#else

View file

@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = {
static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
int err;
err = mmc_suspend_host(mmc);
if (err)
return err;
cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
static int cb710_mmc_resume(struct platform_device *pdev)
{
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
cb710_mmc_enable_irq(slot, 0, ~0);
return mmc_resume_host(mmc);
return 0;
}
#endif /* CONFIG_PM */

View file

@ -193,7 +193,6 @@ struct mmc_davinci_host {
#define DAVINCI_MMC_DATADIR_READ 1
#define DAVINCI_MMC_DATADIR_WRITE 2
unsigned char data_dir;
unsigned char suspended;
/* buffer is used during PIO of one scatterlist segment, and
* is updated along with buffer_bytes_left. bytes_left applies
@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
int ret;
ret = mmc_suspend_host(host->mmc);
if (!ret) {
writel(0, host->base + DAVINCI_MMCIM);
mmc_davinci_reset_ctrl(host, 1);
clk_disable(host->clk);
host->suspended = 1;
} else {
host->suspended = 0;
}
writel(0, host->base + DAVINCI_MMCIM);
mmc_davinci_reset_ctrl(host, 1);
clk_disable(host->clk);
return ret;
return 0;
}
static int davinci_mmcsd_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
int ret;
if (!host->suspended)
return 0;
clk_enable(host->clk);
mmc_davinci_reset_ctrl(host, 0);
ret = mmc_resume_host(host->mmc);
if (!ret)
host->suspended = 0;
return ret;
return 0;
}
static const struct dev_pm_ops davinci_mmcsd_pm = {

View file

@ -14,8 +14,10 @@
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
@ -30,16 +32,39 @@
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
SDMMC_CLKSEL_CCLK_DIVIDER(z))
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
/* Block number in eMMC */
#define DWMCI_BLOCK_NUM 0xFFFFFFFF
#define SDMMC_EMMCP_BASE 0x1000
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
/* SMU control bits */
#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
#define DWMCI_MPSCTRL_VALID BIT(0)
#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
/* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210,
DW_MCI_TYPE_EXYNOS4412,
DW_MCI_TYPE_EXYNOS5250,
DW_MCI_TYPE_EXYNOS5420,
DW_MCI_TYPE_EXYNOS5420_SMU,
};
/* Exynos implementation specific driver private data */
@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {
u8 ciu_div;
u32 sdr_timing;
u32 ddr_timing;
u32 cur_speed;
};
static struct dw_mci_exynos_compatible {
@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {
}, {
.compatible = "samsung,exynos5420-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS5420,
}, {
.compatible = "samsung,exynos5420-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
},
};
static int dw_mci_exynos_priv_init(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv;
int idx;
struct dw_mci_exynos_priv_data *priv = host->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;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
DWMCI_MPSCTRL_VALID |
DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
}
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
if (of_device_is_compatible(host->dev->of_node,
exynos_compat[idx].compatible))
priv->ctrl_type = exynos_compat[idx].ctrl_type;
}
host->priv = priv;
return 0;
}
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
unsigned long rate = clk_get_rate(host->ciu_clk);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420)
host->bus_hz /= (priv->ciu_div + 1);
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
host->bus_hz = rate / (priv->ciu_div + 1);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dw_mci_exynos_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
return dw_mci_suspend(host);
}
static int dw_mci_exynos_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
dw_mci_exynos_priv_init(host);
return dw_mci_resume(host);
}
/**
* dw_mci_exynos_resume_noirq - Exynos-specific resume code
*
* On exynos5420 there is a silicon errata that will sometimes leave the
* WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate
* that it fired and we can clear it by writing a 1 back. Clear it to prevent
* interrupts from going off constantly.
*
* We run this code on all exynos variants because it doesn't hurt.
*/
static int dw_mci_exynos_resume_noirq(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
u32 clksel;
clksel = mci_readl(host, CLKSEL);
if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
mci_writel(host, CLKSEL, clksel);
return 0;
}
#else
#define dw_mci_exynos_suspend NULL
#define dw_mci_exynos_resume NULL
#define dw_mci_exynos_resume_noirq NULL
#endif /* CONFIG_PM_SLEEP */
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
{
@ -121,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
unsigned int wanted = ios->clock;
unsigned long actual;
u8 div = priv->ciu_div + 1;
if (ios->timing == MMC_TIMING_UHS_DDR50)
if (ios->timing == MMC_TIMING_UHS_DDR50) {
mci_writel(host, CLKSEL, priv->ddr_timing);
else
/* Should be double rate for DDR mode */
if (ios->bus_width == MMC_BUS_WIDTH_8)
wanted <<= 1;
} else {
mci_writel(host, CLKSEL, priv->sdr_timing);
}
/* Don't care if wanted clock is zero */
if (!wanted)
return;
/* Guaranteed minimum frequency for cclkin */
if (wanted < EXYNOS_CCLKIN_MIN)
wanted = EXYNOS_CCLKIN_MIN;
if (wanted != priv->cur_speed) {
int ret = clk_set_rate(host->ciu_clk, wanted * div);
if (ret)
dev_warn(host->dev,
"failed to set clk-rate %u error: %d\n",
wanted * div, ret);
actual = clk_get_rate(host->ciu_clk);
host->bus_hz = actual / div;
priv->cur_speed = wanted;
host->current_speed = 0;
}
}
static int dw_mci_exynos_parse_dt(struct dw_mci *host)
{
struct dw_mci_exynos_priv_data *priv = host->priv;
struct dw_mci_exynos_priv_data *priv;
struct device_node *np = host->dev->of_node;
u32 timing[2];
u32 div = 0;
int idx;
int ret;
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
priv->ciu_div = div;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(host->dev, "mem alloc failed for private data\n");
return -ENOMEM;
}
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
priv->ctrl_type = exynos_compat[idx].ctrl_type;
}
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
else {
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
priv->ciu_div = div;
}
ret = of_property_read_u32_array(np,
"samsung,dw-mshc-sdr-timing", timing, 2);
@ -152,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
return ret;
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
host->priv = priv;
return 0;
}
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
{
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
}
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
{
u32 clksel;
clksel = mci_readl(host, CLKSEL);
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
mci_writel(host, CLKSEL, clksel);
}
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
{
u32 clksel;
u8 sample;
clksel = mci_readl(host, CLKSEL);
sample = (clksel + 1) & 0x7;
clksel = (clksel & ~0x7) | sample;
mci_writel(host, CLKSEL, clksel);
return sample;
}
static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
{
const u8 iter = 8;
u8 __c;
s8 i, loc = -1;
for (i = 0; i < iter; i++) {
__c = ror8(candiates, i);
if ((__c & 0xc7) == 0xc7) {
loc = i;
goto out;
}
}
for (i = 0; i < iter; i++) {
__c = ror8(candiates, i);
if ((__c & 0x83) == 0x83) {
loc = i;
goto out;
}
}
out:
return loc;
}
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
struct dw_mci_tuning_data *tuning_data)
{
struct dw_mci *host = slot->host;
struct mmc_host *mmc = slot->mmc;
const u8 *blk_pattern = tuning_data->blk_pattern;
u8 *blk_test;
unsigned int blksz = tuning_data->blksz;
u8 start_smpl, smpl, candiates = 0;
s8 found = -1;
int ret = 0;
blk_test = kmalloc(blksz, GFP_KERNEL);
if (!blk_test)
return -ENOMEM;
start_smpl = dw_mci_exynos_get_clksmpl(host);
do {
struct mmc_request mrq = {NULL};
struct mmc_command cmd = {0};
struct mmc_command stop = {0};
struct mmc_data data = {0};
struct scatterlist sg;
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
stop.opcode = MMC_STOP_TRANSMISSION;
stop.arg = 0;
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
data.blksz = blksz;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, blk_test, blksz);
mrq.cmd = &cmd;
mrq.stop = &stop;
mrq.data = &data;
host->mrq = &mrq;
mci_writel(host, TMOUT, ~0);
smpl = dw_mci_exynos_move_next_clksmpl(host);
mmc_wait_for_req(mmc, &mrq);
if (!cmd.error && !data.error) {
if (!memcmp(blk_pattern, blk_test, blksz))
candiates |= (1 << smpl);
} else {
dev_dbg(host->dev,
"Tuning error: cmd.error:%d, data.error:%d\n",
cmd.error, data.error);
}
} while (start_smpl != smpl);
found = dw_mci_exynos_get_best_clksmpl(candiates);
if (found >= 0)
dw_mci_exynos_set_clksmpl(host, found);
else
ret = -EIO;
kfree(blk_test);
return ret;
}
/* Common capabilities of Exynos4/Exynos5 SoC */
static unsigned long exynos_dwmmc_caps[4] = {
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
@ -171,6 +400,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
.prepare_command = dw_mci_exynos_prepare_command,
.set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt,
.execute_tuning = dw_mci_exynos_execute_tuning,
};
static const struct of_device_id dw_mci_exynos_match[] = {
@ -180,6 +410,8 @@ static const struct of_device_id dw_mci_exynos_match[] = {
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc",
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
.data = &exynos_drv_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
@ -194,13 +426,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, drv_data);
}
const struct dev_pm_ops dw_mci_exynos_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
.resume_noirq = dw_mci_exynos_resume_noirq,
.thaw_noirq = dw_mci_exynos_resume_noirq,
.restore_noirq = dw_mci_exynos_resume_noirq,
};
static struct platform_driver dw_mci_exynos_pltfm_driver = {
.probe = dw_mci_exynos_probe,
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dwmmc_exynos",
.of_match_table = dw_mci_exynos_match,
.pm = &dw_mci_pltfm_pmops,
.pm = &dw_mci_exynos_pmops,
},
};

View file

@ -39,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
{
struct dw_mci *host;
struct resource *regs;
int ret;
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host)
@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
if (drv_data && drv_data->init) {
ret = drv_data->init(host);
if (ret)
return ret;
}
platform_set_drvdata(pdev, host);
return dw_mci_probe(host);
}

View file

@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data {
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;
}
@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
{
struct dw_mci_socfpga_priv_data *priv = host->priv;
struct dw_mci_socfpga_priv_data *priv;
struct device_node *np = host->dev->of_node;
u32 timing[2];
u32 div = 0;
int ret;
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);
}
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");
@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
return ret;
priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
host->priv = priv;
return 0;
}
@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = {
};
MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
int dw_mci_socfpga_probe(struct platform_device *pdev)
static int dw_mci_socfpga_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = {
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dwmmc_socfpga",
.of_match_table = of_match_ptr(dw_mci_socfpga_match),
.of_match_table = dw_mci_socfpga_match,
.pm = &dw_mci_pltfm_pmops,
},
};

View file

@ -29,6 +29,7 @@
#include <linux/irq.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h>
#include <linux/regulator/consumer.h>
@ -50,6 +51,9 @@
#define DW_MCI_RECV_STATUS 2
#define DW_MCI_DMA_THRESHOLD 16
#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */
#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */
#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 | \
@ -76,42 +80,39 @@ struct idmac_desc {
};
#endif /* CONFIG_MMC_DW_IDMAC */
/**
* struct dw_mci_slot - MMC slot state
* @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using.
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
* @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
* @ctype: Card type for this slot.
* @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle.
* @queue_node: List node for placing this node in the @queue list of
* &struct dw_mci.
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
* @flags: Random state bits associated with the slot.
* @id: Number of this slot.
* @last_detect_state: Most recently observed card detect state.
*/
struct dw_mci_slot {
struct mmc_host *mmc;
struct dw_mci *host;
int quirks;
int wp_gpio;
u32 ctype;
struct mmc_request *mrq;
struct list_head queue_node;
unsigned int clock;
unsigned long flags;
#define DW_MMC_CARD_PRESENT 0
#define DW_MMC_CARD_NEED_INIT 1
int id;
int last_detect_state;
static const u8 tuning_blk_pattern_4bit[] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
static const u8 tuning_blk_pattern_8bit[] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
static inline bool dw_mci_fifo_reset(struct dw_mci *host);
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host);
#if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v)
{
@ -249,10 +250,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
cmdr = cmd->opcode;
if (cmdr == MMC_STOP_TRANSMISSION)
if (cmd->opcode == MMC_STOP_TRANSMISSION ||
cmd->opcode == MMC_GO_IDLE_STATE ||
cmd->opcode == MMC_GO_INACTIVE_STATE ||
(cmd->opcode == SD_IO_RW_DIRECT &&
((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))
cmdr |= SDMMC_CMD_STOP;
else
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->flags & MMC_RSP_PRESENT) {
/* We expect a response, so set this bit */
@ -279,6 +285,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
return cmdr;
}
static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
{
struct mmc_command *stop;
u32 cmdr;
if (!cmd->data)
return 0;
stop = &host->stop_abort;
cmdr = cmd->opcode;
memset(stop, 0, sizeof(struct mmc_command));
if (cmdr == MMC_READ_SINGLE_BLOCK ||
cmdr == MMC_READ_MULTIPLE_BLOCK ||
cmdr == MMC_WRITE_BLOCK ||
cmdr == MMC_WRITE_MULTIPLE_BLOCK) {
stop->opcode = MMC_STOP_TRANSMISSION;
stop->arg = 0;
stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
} else if (cmdr == SD_IO_RW_EXTENDED) {
stop->opcode = SD_IO_RW_DIRECT;
stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) |
((cmd->arg >> 28) & 0x7);
stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC;
} else {
return 0;
}
cmdr = stop->opcode | SDMMC_CMD_STOP |
SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP;
return cmdr;
}
static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
@ -293,9 +333,10 @@ static void dw_mci_start_command(struct dw_mci *host,
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
}
static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
{
dw_mci_start_command(host, data->stop, host->stop_cmdr);
struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
dw_mci_start_command(host, stop, host->stop_cmdr);
}
/* DMA interface functions */
@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host)
if (host->using_dma) {
host->dma_ops->stop(host);
host->dma_ops->cleanup(host);
} else {
/* Data transfer was stopped by the interrupt handler */
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
}
/* Data transfer was stopped by the interrupt handler */
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
}
static int dw_mci_get_dma_dir(struct mmc_data *data)
@ -331,6 +372,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host)
dw_mci_get_dma_dir(data));
}
static void dw_mci_idmac_reset(struct dw_mci *host)
{
u32 bmod = mci_readl(host, BMOD);
/* Software reset of DMA */
bmod |= SDMMC_IDMAC_SWRESET;
mci_writel(host, BMOD, bmod);
}
static void dw_mci_idmac_stop_dma(struct dw_mci *host)
{
u32 temp;
@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
/* Stop the IDMAC running */
temp = mci_readl(host, BMOD);
temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB);
temp |= SDMMC_IDMAC_SWRESET;
mci_writel(host, BMOD, temp);
}
@ -435,7 +485,7 @@ static int dw_mci_idmac_init(struct dw_mci *host)
p->des3 = host->sg_dma;
p->des0 = IDMAC_DES0_ER;
mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
dw_mci_idmac_reset(host);
/* Mask out interrupts - get Tx & Rx complete only */
mci_writel(host, IDSTS, IDMAC_INT_CLR);
@ -532,6 +582,78 @@ static void dw_mci_post_req(struct mmc_host *mmc,
data->host_cookie = 0;
}
static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
{
#ifdef CONFIG_MMC_DW_IDMAC
unsigned int blksz = data->blksz;
const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256};
u32 fifo_width = 1 << host->data_shift;
u32 blksz_depth = blksz / fifo_width, fifoth_val;
u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1;
tx_wmark = (host->fifo_depth) / 2;
tx_wmark_invers = host->fifo_depth - tx_wmark;
/*
* MSIZE is '1',
* if blksz is not a multiple of the FIFO width
*/
if (blksz % fifo_width) {
msize = 0;
rx_wmark = 1;
goto done;
}
do {
if (!((blksz_depth % mszs[idx]) ||
(tx_wmark_invers % mszs[idx]))) {
msize = idx;
rx_wmark = mszs[idx] - 1;
break;
}
} while (--idx > 0);
/*
* If idx is '0', it won't be tried
* Thus, initial values are uesed
*/
done:
fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark);
mci_writel(host, FIFOTH, fifoth_val);
#endif
}
static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data)
{
unsigned int blksz = data->blksz;
u32 blksz_depth, fifo_depth;
u16 thld_size;
WARN_ON(!(data->flags & MMC_DATA_READ));
if (host->timing != MMC_TIMING_MMC_HS200 &&
host->timing != MMC_TIMING_UHS_SDR104)
goto disable;
blksz_depth = blksz / (1 << host->data_shift);
fifo_depth = host->fifo_depth;
if (blksz_depth > fifo_depth)
goto disable;
/*
* If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz'
* If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz
* Currently just choose blksz.
*/
thld_size = blksz;
mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1));
return;
disable:
mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0));
}
static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
{
int sg_len;
@ -556,6 +678,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
(unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len);
/*
* Decide the MSIZE and RX/TX Watermark.
* If current block size is same with previous size,
* no need to update fifoth.
*/
if (host->prev_blksz != data->blksz)
dw_mci_adjust_fifoth(host, data);
/* Enable the DMA interface */
temp = mci_readl(host, CTRL);
temp |= SDMMC_CTRL_DMA_ENABLE;
@ -581,10 +711,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
host->sg = NULL;
host->data = data;
if (data->flags & MMC_DATA_READ)
if (data->flags & MMC_DATA_READ) {
host->dir_status = DW_MCI_RECV_STATUS;
else
dw_mci_ctrl_rd_thld(host, data);
} else {
host->dir_status = DW_MCI_SEND_STATUS;
}
if (dw_mci_submit_data_dma(host, data)) {
int flags = SG_MITER_ATOMIC;
@ -606,6 +738,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
temp = mci_readl(host, CTRL);
temp &= ~SDMMC_CTRL_DMA_ENABLE;
mci_writel(host, CTRL, temp);
/*
* Use the initial fifoth_val for PIO mode.
* If next issued data may be transfered by DMA mode,
* prev_blksz should be invalidated.
*/
mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
} else {
/*
* Keep the current block size.
* It will be used to decide whether to update
* fifoth register next time.
*/
host->prev_blksz = data->blksz;
}
}
@ -632,24 +779,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
{
struct dw_mci *host = slot->host;
unsigned int clock = slot->clock;
u32 div;
u32 clk_en_a;
if (slot->clock != host->current_speed || force_clkinit) {
div = host->bus_hz / slot->clock;
if (host->bus_hz % slot->clock && host->bus_hz > slot->clock)
if (!clock) {
mci_writel(host, CLKENA, 0);
mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
} else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock)
/*
* move the + 1 after the divide to prevent
* over-clocking the card.
*/
div += 1;
div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0;
div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;
dev_info(&slot->mmc->class_dev,
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
" div = %d)\n", slot->id, host->bus_hz, slot->clock,
div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
if ((clock << div) != slot->__clk_old || force_clkinit)
dev_info(&slot->mmc->class_dev,
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
slot->id, host->bus_hz, clock,
div ? ((host->bus_hz / div) >> 1) :
host->bus_hz, div);
/* disable clock */
mci_writel(host, CLKENA, 0);
@ -676,9 +830,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
host->current_speed = slot->clock;
/* keep the clock with reflecting clock dividor */
slot->__clk_old = clock << div;
}
host->current_speed = clock;
/* Set the current slot bus width */
mci_writel(host, CTYPE, (slot->ctype << slot->id));
}
@ -700,7 +857,9 @@ static void __dw_mci_start_request(struct dw_mci *host,
host->pending_events = 0;
host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0;
host->dir_status = 0;
data = cmd->data;
if (data) {
@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host,
if (mrq->stop)
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop);
else
host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd);
}
static void dw_mci_start_request(struct dw_mci *host,
@ -806,14 +967,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
regs &= ~((0x1 << slot->id) << 16);
mci_writel(slot->host, UHS_REG, regs);
slot->host->timing = ios->timing;
if (ios->clock) {
/*
* Use mirror of ios->clock to prevent race with mmc
* core ios update when finding the minimum.
*/
slot->clock = ios->clock;
}
/*
* Use mirror of ios->clock to prevent race with mmc
* core ios update when finding the minimum.
*/
slot->clock = ios->clock;
if (drv_data && drv_data->set_ios)
drv_data->set_ios(slot->host, ios);
@ -939,6 +1099,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb)
}
}
static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = host->drv_data;
struct dw_mci_tuning_data tuning_data;
int err = -ENOSYS;
if (opcode == MMC_SEND_TUNING_BLOCK_HS200) {
if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
tuning_data.blk_pattern = tuning_blk_pattern_8bit;
tuning_data.blksz = sizeof(tuning_blk_pattern_8bit);
} else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
} else {
return -EINVAL;
}
} else if (opcode == MMC_SEND_TUNING_BLOCK) {
tuning_data.blk_pattern = tuning_blk_pattern_4bit;
tuning_data.blksz = sizeof(tuning_blk_pattern_4bit);
} else {
dev_err(host->dev,
"Undefined command(%d) for tuning\n", opcode);
return -EINVAL;
}
if (drv_data && drv_data->execute_tuning)
err = drv_data->execute_tuning(slot, opcode, &tuning_data);
return err;
}
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
@ -947,6 +1139,7 @@ static const struct mmc_host_ops dw_mci_ops = {
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
.execute_tuning = dw_mci_execute_tuning,
};
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@ -978,7 +1171,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
spin_lock(&host->lock);
}
static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd)
{
u32 status = host->cmd_status;
@ -1012,12 +1205,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd
/* newer ip versions need a delay between retries */
if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
mdelay(20);
if (cmd->data) {
dw_mci_stop_dma(host);
host->data = NULL;
}
}
return cmd->error;
}
static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
{
u32 status = host->data_status;
if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
} else if (status & SDMMC_INT_EBE) {
if (host->dir_status ==
DW_MCI_SEND_STATUS) {
/*
* No data CRC status was returned.
* The number of bytes transferred
* will be exaggerated in PIO mode.
*/
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else if (host->dir_status ==
DW_MCI_RECV_STATUS) {
data->error = -EIO;
}
} else {
/* SDMMC_INT_SBE is included */
data->error = -EIO;
}
dev_err(host->dev, "data error, status 0x%08x\n", status);
/*
* After an error, there may be data lingering
* in the FIFO
*/
dw_mci_fifo_reset(host);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
}
return data->error;
}
static void dw_mci_tasklet_func(unsigned long priv)
@ -1025,14 +1258,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
struct dw_mci *host = (struct dw_mci *)priv;
struct mmc_data *data;
struct mmc_command *cmd;
struct mmc_request *mrq;
enum dw_mci_state state;
enum dw_mci_state prev_state;
u32 status, ctrl;
unsigned int err;
spin_lock(&host->lock);
state = host->state;
data = host->data;
mrq = host->mrq;
do {
prev_state = state;
@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
cmd = host->cmd;
host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
dw_mci_command_complete(host, cmd);
if (cmd == host->mrq->sbc && !cmd->error) {
err = dw_mci_command_complete(host, cmd);
if (cmd == mrq->sbc && !err) {
prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->cur_slot,
host->mrq->cmd);
mrq->cmd);
goto unlock;
}
if (!host->mrq->data || cmd->error) {
dw_mci_request_end(host, host->mrq);
if (cmd->data && err) {
dw_mci_stop_dma(host);
send_stop_abort(host, data);
state = STATE_SENDING_STOP;
break;
}
if (!cmd->data || err) {
dw_mci_request_end(host, mrq);
goto unlock;
}
@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
if (data->stop)
send_stop_cmd(host, data);
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
break;
}
@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->data = NULL;
set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
status = host->data_status;
err = dw_mci_data_complete(host, data);
if (status & DW_MCI_DATA_ERROR_FLAGS) {
if (status & SDMMC_INT_DRTO) {
data->error = -ETIMEDOUT;
} else if (status & SDMMC_INT_DCRC) {
data->error = -EILSEQ;
} else if (status & SDMMC_INT_EBE &&
host->dir_status ==
DW_MCI_SEND_STATUS) {
/*
* No data CRC status was returned.
* The number of bytes transferred will
* be exaggerated in PIO mode.
*/
data->bytes_xfered = 0;
data->error = -ETIMEDOUT;
} else {
dev_err(host->dev,
"data FIFO error "
"(status=%08x)\n",
status);
data->error = -EIO;
if (!err) {
if (!data->stop || mrq->sbc) {
if (mrq->sbc)
data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
}
/*
* After an error, there may be data lingering
* in the FIFO, so reset it - doing so
* generates a block interrupt, hence setting
* the scatter-gather pointer to NULL.
*/
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
ctrl = mci_readl(host, CTRL);
ctrl |= SDMMC_CTRL_FIFO_RESET;
mci_writel(host, CTRL, ctrl);
} else {
data->bytes_xfered = data->blocks * data->blksz;
data->error = 0;
}
if (!data->stop) {
dw_mci_request_end(host, host->mrq);
goto unlock;
}
if (host->mrq->sbc && !data->error) {
data->stop->error = 0;
dw_mci_request_end(host, host->mrq);
goto unlock;
/* stop command for open-ended transfer*/
if (data->stop)
send_stop_abort(host, data);
}
/*
* If err has non-zero,
* stop-abort command has been already issued.
*/
prev_state = state = STATE_SENDING_STOP;
if (!data->error)
send_stop_cmd(host, data);
/* fall through */
case STATE_SENDING_STOP:
@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv)
&host->pending_events))
break;
/* CMD error in data command */
if (mrq->cmd->error && mrq->data)
dw_mci_fifo_reset(host);
host->cmd = NULL;
dw_mci_command_complete(host, host->mrq->stop);
dw_mci_request_end(host, host->mrq);
host->data = NULL;
if (mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
dw_mci_request_end(host, mrq);
goto unlock;
case STATE_DATA_ERROR:
@ -1697,7 +1915,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
struct mmc_host *mmc = slot->mmc;
struct mmc_request *mrq;
int present;
u32 ctrl;
present = dw_mci_get_cd(mmc);
while (present != slot->last_detect_state) {
@ -1736,11 +1953,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
case STATE_DATA_ERROR:
if (mrq->data->error == -EINPROGRESS)
mrq->data->error = -ENOMEDIUM;
if (!mrq->stop)
break;
/* fall through */
case STATE_SENDING_STOP:
mrq->stop->error = -ENOMEDIUM;
if (mrq->stop)
mrq->stop->error = -ENOMEDIUM;
break;
}
@ -1763,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
if (present == 0) {
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
/*
* Clear down the FIFO - doing so generates a
* block interrupt, hence setting the
* scatter-gather pointer to NULL.
*/
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
ctrl = mci_readl(host, CTRL);
ctrl |= SDMMC_CTRL_FIFO_RESET;
mci_writel(host, CTRL, ctrl);
/* Clear down the FIFO */
dw_mci_fifo_reset(host);
#ifdef CONFIG_MMC_DW_IDMAC
ctrl = mci_readl(host, BMOD);
/* Software reset of DMA */
ctrl |= SDMMC_IDMAC_SWRESET;
mci_writel(host, BMOD, ctrl);
dw_mci_idmac_reset(host);
#endif
}
@ -1901,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct dw_mci_slot *slot;
const struct dw_mci_drv_data *drv_data = host->drv_data;
int ctrl_id, ret;
u32 freq[2];
u8 bus_width;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
@ -1916,8 +2120,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
mmc->ops = &dw_mci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510);
mmc->f_max = host->bus_hz;
if (of_property_read_u32_array(host->dev->of_node,
"clock-freq-min-max", freq, 2)) {
mmc->f_min = DW_MCI_FREQ_MIN;
mmc->f_max = DW_MCI_FREQ_MAX;
} else {
mmc->f_min = freq[0];
mmc->f_max = freq[1];
}
if (host->pdata->get_ocr)
mmc->ocr_avail = host->pdata->get_ocr(id);
@ -1964,9 +2174,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->caps |= MMC_CAP_4_BIT_DATA;
}
if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED)
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
@ -2008,12 +2215,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
/* Card initially undetected */
slot->last_detect_state = 0;
/*
* Card may have been plugged in prior to boot so we
* need to run the detect tasklet
*/
queue_work(host->card_workqueue, &host->card_work);
return 0;
err_setup_bus:
@ -2074,36 +2275,57 @@ no_dma:
return;
}
static bool mci_wait_reset(struct device *dev, struct dw_mci *host)
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
{
unsigned long timeout = jiffies + msecs_to_jiffies(500);
unsigned int ctrl;
u32 ctrl;
mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
SDMMC_CTRL_DMA_RESET));
ctrl = mci_readl(host, CTRL);
ctrl |= reset;
mci_writel(host, CTRL, ctrl);
/* wait till resets clear */
do {
ctrl = mci_readl(host, CTRL);
if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET |
SDMMC_CTRL_DMA_RESET)))
if (!(ctrl & reset))
return true;
} while (time_before(jiffies, timeout));
dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl);
dev_err(host->dev,
"Timeout resetting block (ctrl reset %#x)\n",
ctrl & reset);
return false;
}
static inline bool dw_mci_fifo_reset(struct dw_mci *host)
{
/*
* Reseting generates a block interrupt, hence setting
* the scatter-gather pointer to NULL.
*/
if (host->sg) {
sg_miter_stop(&host->sg_miter);
host->sg = NULL;
}
return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET);
}
static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host)
{
return dw_mci_ctrl_reset(host,
SDMMC_CTRL_FIFO_RESET |
SDMMC_CTRL_RESET |
SDMMC_CTRL_DMA_RESET);
}
#ifdef CONFIG_OF
static struct dw_mci_of_quirks {
char *quirk;
int id;
} of_quirks[] = {
{
.quirk = "supports-highspeed",
.id = DW_MCI_QUIRK_HIGHSPEED,
}, {
.quirk = "broken-cd",
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION,
},
@ -2158,6 +2380,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
if (of_find_property(np, "enable-sdio-wakeup", NULL))
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
if (of_find_property(np, "supports-highspeed", NULL))
pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL))
pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
return pdata;
}
@ -2221,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host)
host->bus_hz = clk_get_rate(host->ciu_clk);
}
if (drv_data && drv_data->init) {
ret = drv_data->init(host);
if (ret) {
dev_err(host->dev,
"implementation specific init failed\n");
goto err_clk_ciu;
}
}
if (drv_data && drv_data->setup_clock) {
ret = drv_data->setup_clock(host);
if (ret) {
@ -2287,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host)
}
/* Reset all blocks */
if (!mci_wait_reset(host->dev, host))
if (!dw_mci_ctrl_all_reset(host))
return -ENODEV;
host->dma_ops = host->pdata->dma_ops;
@ -2317,8 +2557,8 @@ int dw_mci_probe(struct dw_mci *host)
fifo_size = host->pdata->fifo_depth;
}
host->fifo_depth = fifo_size;
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) |
((fifo_size/2) << 0));
host->fifoth_val =
SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
mci_writel(host, FIFOTH, host->fifoth_val);
/* disable clock to CIU */
@ -2456,23 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove);
*/
int dw_mci_suspend(struct dw_mci *host)
{
int i, ret = 0;
for (i = 0; i < host->num_slots; i++) {
struct dw_mci_slot *slot = host->slot[i];
if (!slot)
continue;
ret = mmc_suspend_host(slot->mmc);
if (ret < 0) {
while (--i >= 0) {
slot = host->slot[i];
if (slot)
mmc_resume_host(host->slot[i]->mmc);
}
return ret;
}
}
if (host->vmmc)
regulator_disable(host->vmmc);
@ -2493,7 +2716,7 @@ int dw_mci_resume(struct dw_mci *host)
}
}
if (!mci_wait_reset(host->dev, host)) {
if (!dw_mci_ctrl_all_reset(host)) {
ret = -ENODEV;
return ret;
}
@ -2501,8 +2724,15 @@ int dw_mci_resume(struct dw_mci *host)
if (host->use_dma && host->dma_ops->init)
host->dma_ops->init(host);
/* Restore the old value at FIFOTH register */
/*
* Restore the initial value at FIFOTH register
* And Invalidate the prev_blksz with zero
*/
mci_writel(host, FIFOTH, host->fifoth_val);
host->prev_blksz = 0;
/* Put in max timeout */
mci_writel(host, TMOUT, 0xFFFFFFFF);
mci_writel(host, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
@ -2518,10 +2748,6 @@ int dw_mci_resume(struct dw_mci *host)
dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
dw_mci_setup_bus(slot, true);
}
ret = mmc_resume_host(host->slot[i]->mmc);
if (ret < 0)
return ret;
}
return 0;
}

View file

@ -53,6 +53,7 @@
#define SDMMC_IDINTEN 0x090
#define SDMMC_DSCADDR 0x094
#define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_DATA(x) (x)
/*
@ -128,6 +129,10 @@
#define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
/* FIFOTH register defines */
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
((r) & 0xFFF) << 16 | \
((t) & 0xFFF))
/* Internal DMAC interrupt defines */
#define SDMMC_IDMAC_INT_AI BIT(9)
#define SDMMC_IDMAC_INT_NI BIT(8)
@ -142,6 +147,8 @@
#define SDMMC_IDMAC_SWRESET BIT(0)
/* Version ID register define */
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Card read threshold */
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
/* Register access macros */
#define mci_readl(dev, reg) \
@ -183,6 +190,52 @@ extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#endif
/**
* struct dw_mci_slot - MMC slot state
* @mmc: The mmc_host representing this slot.
* @host: The MMC controller this slot is using.
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
* @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
* @ctype: Card type for this slot.
* @mrq: mmc_request currently being processed or waiting to be
* processed, or NULL when the slot is idle.
* @queue_node: List node for placing this node in the @queue list of
* &struct dw_mci.
* @clock: Clock rate configured by set_ios(). Protected by host->lock.
* @__clk_old: The last updated clock with reflecting clock divider.
* Keeping track of this helps us to avoid spamming the console
* with CONFIG_MMC_CLKGATE.
* @flags: Random state bits associated with the slot.
* @id: Number of this slot.
* @last_detect_state: Most recently observed card detect state.
*/
struct dw_mci_slot {
struct mmc_host *mmc;
struct dw_mci *host;
int quirks;
int wp_gpio;
u32 ctype;
struct mmc_request *mrq;
struct list_head queue_node;
unsigned int clock;
unsigned int __clk_old;
unsigned long flags;
#define DW_MMC_CARD_PRESENT 0
#define DW_MMC_CARD_NEED_INIT 1
int id;
int last_detect_state;
};
struct dw_mci_tuning_data {
const u8 *blk_pattern;
unsigned int blksz;
};
/**
* dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s).
@ -203,5 +256,7 @@ struct dw_mci_drv_data {
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host);
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode,
struct dw_mci_tuning_data *tuning_data);
};
#endif /* _DW_MMC_H_ */

View file

@ -880,8 +880,6 @@ static int jz4740_mmc_suspend(struct device *dev)
{
struct jz4740_mmc_host *host = dev_get_drvdata(dev);
mmc_suspend_host(host->mmc);
jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
return 0;
@ -893,8 +891,6 @@ static int jz4740_mmc_resume(struct device *dev)
jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
mmc_resume_host(host->mmc);
return 0;
}

View file

@ -1730,37 +1730,28 @@ static int mmci_suspend(struct device *dev)
{
struct amba_device *adev = to_amba_device(dev);
struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
ret = mmc_suspend_host(mmc);
if (ret == 0) {
pm_runtime_get_sync(dev);
writel(0, host->base + MMCIMASK0);
}
pm_runtime_get_sync(dev);
writel(0, host->base + MMCIMASK0);
}
return ret;
return 0;
}
static int mmci_resume(struct device *dev)
{
struct amba_device *adev = to_amba_device(dev);
struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
pm_runtime_put(dev);
ret = mmc_resume_host(mmc);
}
return ret;
return 0;
}
#endif

View file

@ -1416,28 +1416,10 @@ ioremap_free:
}
#ifdef CONFIG_PM
#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
static void
do_resume_work(struct work_struct *work)
{
struct msmsdcc_host *host =
container_of(work, struct msmsdcc_host, resume_task);
struct mmc_host *mmc = host->mmc;
if (mmc) {
mmc_resume_host(mmc);
if (host->stat_irq)
enable_irq(host->stat_irq);
}
}
#endif
static int
msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = mmc_get_drvdata(dev);
int rc = 0;
if (mmc) {
struct msmsdcc_host *host = mmc_priv(mmc);
@ -1445,14 +1427,11 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
if (host->stat_irq)
disable_irq(host->stat_irq);
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
rc = mmc_suspend_host(mmc);
if (!rc)
msmsdcc_writel(host, 0, MMCIMASK0);
msmsdcc_writel(host, 0, MMCIMASK0);
if (host->clks_on)
msmsdcc_disable_clocks(host, 0);
}
return rc;
return 0;
}
static int
@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev)
msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
mmc_resume_host(mmc);
if (host->stat_irq)
enable_irq(host->stat_irq);
#if BUSCLK_PWRSAVE

View file

@ -775,9 +775,9 @@ static int mvsd_probe(struct platform_device *pdev)
spin_lock_init(&host->lock);
host->base = devm_request_and_ioremap(&pdev->dev, r);
if (!host->base) {
ret = -ENOMEM;
host->base = devm_ioremap_resource(&pdev->dev, r);
if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base);
goto out;
}
@ -838,33 +838,6 @@ static int mvsd_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int mvsd_suspend(struct platform_device *dev, pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc);
return ret;
}
static int mvsd_resume(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
}
#else
#define mvsd_suspend NULL
#define mvsd_resume NULL
#endif
static const struct of_device_id mvsdio_dt_ids[] = {
{ .compatible = "marvell,orion-sdio" },
{ /* sentinel */ }
@ -874,8 +847,6 @@ MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
static struct platform_driver mvsd_driver = {
.probe = mvsd_probe,
.remove = mvsd_remove,
.suspend = mvsd_suspend,
.resume = mvsd_resume,
.driver = {
.name = DRIVER_NAME,
.of_match_table = mvsdio_dt_ids,

View file

@ -1250,28 +1250,20 @@ static int mxcmci_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc);
clk_disable_unprepare(host->clk_per);
clk_disable_unprepare(host->clk_ipg);
return ret;
return 0;
}
static int mxcmci_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc);
int ret = 0;
clk_prepare_enable(host->clk_per);
clk_prepare_enable(host->clk_ipg);
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
return 0;
}
static const struct dev_pm_ops mxcmci_pm_ops = {

View file

@ -724,13 +724,9 @@ static int mxs_mmc_suspend(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp;
int ret = 0;
ret = mmc_suspend_host(mmc);
clk_disable_unprepare(ssp->clk);
return ret;
return 0;
}
static int mxs_mmc_resume(struct device *dev)
@ -738,13 +734,9 @@ static int mxs_mmc_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp;
int ret = 0;
clk_prepare_enable(ssp->clk);
ret = mmc_resume_host(mmc);
return ret;
return 0;
}
static const struct dev_pm_ops mxs_mmc_pm_ops = {

View file

@ -128,7 +128,6 @@ struct mmc_omap_slot {
struct mmc_omap_host {
int initialized;
int suspended;
struct mmc_request * mrq;
struct mmc_command * cmd;
struct mmc_data * data;
@ -1513,61 +1512,9 @@ static int mmc_omap_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
{
int i, ret = 0;
struct mmc_omap_host *host = platform_get_drvdata(pdev);
if (host == NULL || host->suspended)
return 0;
for (i = 0; i < host->nr_slots; i++) {
struct mmc_omap_slot *slot;
slot = host->slots[i];
ret = mmc_suspend_host(slot->mmc);
if (ret < 0) {
while (--i >= 0) {
slot = host->slots[i];
mmc_resume_host(slot->mmc);
}
return ret;
}
}
host->suspended = 1;
return 0;
}
static int mmc_omap_resume(struct platform_device *pdev)
{
int i, ret = 0;
struct mmc_omap_host *host = platform_get_drvdata(pdev);
if (host == NULL || !host->suspended)
return 0;
for (i = 0; i < host->nr_slots; i++) {
struct mmc_omap_slot *slot;
slot = host->slots[i];
ret = mmc_resume_host(slot->mmc);
if (ret < 0)
return ret;
host->suspended = 0;
}
return 0;
}
#else
#define mmc_omap_suspend NULL
#define mmc_omap_resume NULL
#endif
static struct platform_driver mmc_omap_driver = {
.probe = mmc_omap_probe,
.remove = mmc_omap_remove,
.suspend = mmc_omap_suspend,
.resume = mmc_omap_resume,
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,

View file

@ -75,6 +75,7 @@
#define ICE 0x1
#define ICS 0x2
#define CEN (1 << 2)
#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */
#define CLKD_MASK 0x0000FFC0
#define CLKD_SHIFT 6
#define DTO_MASK 0x000F0000
@ -119,7 +120,8 @@
BRR_EN | BWR_EN | TC_EN | CC_EN)
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
#define OMAP_MMC_MIN_CLOCK 400000
#define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc"
@ -171,6 +173,10 @@ struct omap_hsmmc_host {
unsigned char bus_mode;
unsigned char power_mode;
int suspended;
u32 con;
u32 hctl;
u32 sysctl;
u32 capa;
int irq;
int use_dma, dma_ch;
struct dma_chan *tx_chan;
@ -183,7 +189,6 @@ struct omap_hsmmc_host {
int use_reg;
int req_in_progress;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@ -493,8 +498,8 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
if (ios->clock) {
dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock);
if (dsor > 250)
dsor = 250;
if (dsor > CLKD_MAX)
dsor = CLKD_MAX;
}
return dsor;
@ -597,25 +602,20 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
struct omap_mmc_platform_data *pdata = host->pdata;
int context_loss = 0;
u32 hctl, capa;
unsigned long timeout;
if (pdata->get_context_loss_count) {
context_loss = pdata->get_context_loss_count(host->dev);
if (context_loss < 0)
return 1;
}
dev_dbg(mmc_dev(host->mmc), "context was %slost\n",
context_loss == host->context_loss ? "not " : "");
if (host->context_loss == context_loss)
return 1;
if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
return 1;
if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
host->hctl == OMAP_HSMMC_READ(host->base, HCTL) &&
host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) &&
host->capa == OMAP_HSMMC_READ(host->base, CAPA))
return 0;
host->context_loss++;
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF &&
(1 << ios->vdd) <= MMC_VDD_23_24)
@ -655,9 +655,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
omap_hsmmc_set_bus_mode(host);
out:
host->context_loss = context_loss;
dev_dbg(mmc_dev(host->mmc), "context is restored\n");
dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n",
host->context_loss);
return 0;
}
@ -666,15 +665,10 @@ out:
*/
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{
struct omap_mmc_platform_data *pdata = host->pdata;
int context_loss;
if (pdata->get_context_loss_count) {
context_loss = pdata->get_context_loss_count(host->dev);
if (context_loss < 0)
return;
host->context_loss = context_loss;
}
host->con = OMAP_HSMMC_READ(host->base, CON);
host->hctl = OMAP_HSMMC_READ(host->base, HCTL);
host->sysctl = OMAP_HSMMC_READ(host->base, SYSCTL);
host->capa = OMAP_HSMMC_READ(host->base, CAPA);
}
#else
@ -975,8 +969,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
unsigned long bit)
{
unsigned long i = 0;
unsigned long limit = (loops_per_jiffy *
msecs_to_jiffies(MMC_TIMEOUT_MS));
unsigned long limit = MMC_TIMEOUT_US;
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | bit);
@ -988,13 +981,13 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
&& (i++ < limit))
cpu_relax();
udelay(1);
}
i = 0;
while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
(i++ < limit))
cpu_relax();
udelay(1);
if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)
dev_err(mmc_dev(host->mmc),
@ -1178,9 +1171,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)
struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect;
if (host->suspended)
return IRQ_HANDLED;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
if (slot->card_detect)
@ -1635,18 +1625,9 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data)
{
struct mmc_host *mmc = s->private;
struct omap_hsmmc_host *host = mmc_priv(mmc);
int context_loss = 0;
if (host->pdata->get_context_loss_count)
context_loss = host->pdata->get_context_loss_count(host->dev);
seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n",
mmc->index, host->context_loss, context_loss);
if (host->suspended) {
seq_printf(s, "host suspended, can't read registers\n");
return 0;
}
seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
mmc->index, host->context_loss);
pm_runtime_get_sync(host->dev);
@ -1838,13 +1819,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->ops = &omap_hsmmc_ops;
/*
* If regulator_disable can only put vcc_aux to sleep then there is
* no off state.
*/
if (mmc_slot(host).vcc_aux_disable_is_sleep)
mmc_slot(host).no_off = 1;
mmc->f_min = OMAP_MMC_MIN_CLOCK;
if (pdata->max_freq > 0)
@ -1874,7 +1848,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host);
/* This can be removed once we support PBIAS with DT */
if (host->dev->of_node && host->mapbase == 0x4809c000)
if (host->dev->of_node && res->start == 0x4809c000)
host->pbias_disable = 1;
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
@ -2119,23 +2093,12 @@ static void omap_hsmmc_complete(struct device *dev)
static int omap_hsmmc_suspend(struct device *dev)
{
int ret = 0;
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host)
return 0;
if (host && host->suspended)
return 0;
pm_runtime_get_sync(host->dev);
host->suspended = 1;
ret = mmc_suspend_host(host->mmc);
if (ret) {
host->suspended = 0;
goto err;
}
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
omap_hsmmc_disable_irq(host);
@ -2145,23 +2108,19 @@ static int omap_hsmmc_suspend(struct device *dev)
if (host->dbclk)
clk_disable_unprepare(host->dbclk);
err:
pm_runtime_put_sync(host->dev);
return ret;
return 0;
}
/* Routine to resume the MMC device */
static int omap_hsmmc_resume(struct device *dev)
{
int ret = 0;
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host)
return 0;
if (host && !host->suspended)
return 0;
pm_runtime_get_sync(host->dev);
if (host->dbclk)
@ -2172,16 +2131,9 @@ static int omap_hsmmc_resume(struct device *dev)
omap_hsmmc_protect_card(host);
/* Notify the core to resume the host */
ret = mmc_resume_host(host->mmc);
if (ret == 0)
host->suspended = 0;
pm_runtime_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev);
return ret;
return 0;
}
#else

View file

@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
static int pxamci_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_suspend_host(mmc);
return ret;
}
static int pxamci_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
int ret = 0;
if (mmc)
ret = mmc_resume_host(mmc);
return ret;
}
static const struct dev_pm_ops pxamci_pm_ops = {
.suspend = pxamci_suspend,
.resume = pxamci_resume,
};
#endif
static struct platform_driver pxamci_driver = {
.probe = pxamci_probe,
.remove = pxamci_remove,
@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
#ifdef CONFIG_PM
.pm = &pxamci_pm_ops,
#endif
},
};

View file

@ -364,7 +364,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
struct mmc_host *mmc = host->mmc;
struct mmc_card *card = mmc->card;
struct mmc_data *data = mrq->data;
int uhs = mmc_sd_card_uhs(card);
int uhs = mmc_card_uhs(card);
int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
u8 cfg2, trans_mode;
int err;
@ -1197,37 +1197,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
.execute_tuning = sdmmc_execute_tuning,
};
#ifdef CONFIG_PM
static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
struct mmc_host *mmc = host->mmc;
int err;
dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
err = mmc_suspend_host(mmc);
if (err)
return err;
return 0;
}
static int rtsx_pci_sdmmc_resume(struct platform_device *pdev)
{
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
struct mmc_host *mmc = host->mmc;
dev_dbg(sdmmc_dev(host), "--> %s\n", __func__);
return mmc_resume_host(mmc);
}
#else /* CONFIG_PM */
#define rtsx_pci_sdmmc_suspend NULL
#define rtsx_pci_sdmmc_resume NULL
#endif /* CONFIG_PM */
static void init_extra_caps(struct realtek_pci_sdmmc *host)
{
struct mmc_host *mmc = host->mmc;
@ -1367,8 +1336,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
.probe = rtsx_pci_sdmmc_drv_probe,
.remove = rtsx_pci_sdmmc_drv_remove,
.id_table = rtsx_pci_sdmmc_ids,
.suspend = rtsx_pci_sdmmc_suspend,
.resume = rtsx_pci_sdmmc_resume,
.driver = {
.owner = THIS_MODULE,
.name = DRV_NAME_RTSX_PCI_SDMMC,

View file

@ -1949,39 +1949,10 @@ static struct platform_device_id s3cmci_driver_ids[] = {
MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
#ifdef CONFIG_PM
static int s3cmci_suspend(struct device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
return mmc_suspend_host(mmc);
}
static int s3cmci_resume(struct device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev));
return mmc_resume_host(mmc);
}
static const struct dev_pm_ops s3cmci_pm = {
.suspend = s3cmci_suspend,
.resume = s3cmci_resume,
};
#define s3cmci_pm_ops &s3cmci_pm
#else /* CONFIG_PM */
#define s3cmci_pm_ops NULL
#endif /* CONFIG_PM */
static struct platform_driver s3cmci_driver = {
.driver = {
.name = "s3c-sdi",
.owner = THIS_MODULE,
.pm = s3cmci_pm_ops,
},
.id_table = s3cmci_driver_ids,
.probe = s3cmci_probe,

View file

@ -316,19 +316,7 @@ err_pltfm_free:
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;
return sdhci_pltfm_unregister(pdev);
}
static struct platform_driver sdhci_bcm_kona_driver = {

View file

@ -178,13 +178,7 @@ err:
static int bcm2835_sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
return 0;
return sdhci_pltfm_unregister(pdev);
}
static const struct of_device_id bcm2835_sdhci_of_match[] = {

View file

@ -34,12 +34,40 @@
/* VENDOR SPEC register */
#define ESDHC_VENDOR_SPEC 0xc0
#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
#define ESDHC_WTMK_LVL 0x44
#define ESDHC_MIX_CTRL 0x48
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
#define ESDHC_MIX_CTRL_AC23EN (1 << 7)
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
/* Bits 3 and 6 are not SDHCI standard definitions */
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
/* dll control register */
#define ESDHC_DLL_CTRL 0x60
#define ESDHC_DLL_OVERRIDE_VAL_SHIFT 9
#define ESDHC_DLL_OVERRIDE_EN_SHIFT 8
/* tune control register */
#define ESDHC_TUNE_CTRL_STATUS 0x68
#define ESDHC_TUNE_CTRL_STEP 1
#define ESDHC_TUNE_CTRL_MIN 0
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
#define ESDHC_TUNING_CTRL 0xcc
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP 0x1
#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
/* pinctrl state */
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
/*
* Our interpretation of the SDHCI_HOST_CONTROL register
*/
@ -66,21 +94,60 @@
* As a result, the TC flag is not asserted and SW received timeout
* exeception. Bit1 of Vendor Spec registor is used to fix it.
*/
#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1)
#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1)
/*
* The flag enables the workaround for ESDHC errata ENGcm07207 which
* affects i.MX25 and i.MX35.
*/
#define ESDHC_FLAG_ENGCM07207 BIT(2)
/*
* The flag tells that the ESDHC controller is an USDHC block that is
* integrated on the i.MX6 series.
*/
#define ESDHC_FLAG_USDHC BIT(3)
/* The IP supports manual tuning process */
#define ESDHC_FLAG_MAN_TUNING BIT(4)
/* The IP supports standard tuning process */
#define ESDHC_FLAG_STD_TUNING BIT(5)
/* The IP has SDHCI_CAPABILITIES_1 register */
#define ESDHC_FLAG_HAVE_CAP1 BIT(6)
enum imx_esdhc_type {
IMX25_ESDHC,
IMX35_ESDHC,
IMX51_ESDHC,
IMX53_ESDHC,
IMX6Q_USDHC,
struct esdhc_soc_data {
u32 flags;
};
static struct esdhc_soc_data esdhc_imx25_data = {
.flags = ESDHC_FLAG_ENGCM07207,
};
static struct esdhc_soc_data esdhc_imx35_data = {
.flags = ESDHC_FLAG_ENGCM07207,
};
static struct esdhc_soc_data esdhc_imx51_data = {
.flags = 0,
};
static struct esdhc_soc_data esdhc_imx53_data = {
.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
};
static struct esdhc_soc_data usdhc_imx6q_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING,
};
static struct esdhc_soc_data usdhc_imx6sl_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1,
};
struct pltfm_imx_data {
int flags;
u32 scratchpad;
enum imx_esdhc_type devtype;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_100mhz;
struct pinctrl_state *pins_200mhz;
const struct esdhc_soc_data *socdata;
struct esdhc_platform_data boarddata;
struct clk *clk_ipg;
struct clk *clk_ahb;
@ -90,25 +157,20 @@ struct pltfm_imx_data {
MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
} multiblock_status;
u32 uhs_mode;
u32 is_ddr;
};
static struct platform_device_id imx_esdhc_devtype[] = {
{
.name = "sdhci-esdhc-imx25",
.driver_data = IMX25_ESDHC,
.driver_data = (kernel_ulong_t) &esdhc_imx25_data,
}, {
.name = "sdhci-esdhc-imx35",
.driver_data = IMX35_ESDHC,
.driver_data = (kernel_ulong_t) &esdhc_imx35_data,
}, {
.name = "sdhci-esdhc-imx51",
.driver_data = IMX51_ESDHC,
}, {
.name = "sdhci-esdhc-imx53",
.driver_data = IMX53_ESDHC,
}, {
.name = "sdhci-usdhc-imx6q",
.driver_data = IMX6Q_USDHC,
.driver_data = (kernel_ulong_t) &esdhc_imx51_data,
}, {
/* sentinel */
}
@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = {
MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], },
{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], },
{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], },
{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], },
{ .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], },
{ .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
{ .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
{ .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
{ .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX25_ESDHC;
}
static inline int is_imx35_esdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX35_ESDHC;
}
static inline int is_imx51_esdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX51_ESDHC;
return data->socdata == &esdhc_imx25_data;
}
static inline int is_imx53_esdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX53_ESDHC;
return data->socdata == &esdhc_imx53_data;
}
static inline int is_imx6q_usdhc(struct pltfm_imx_data *data)
{
return data->devtype == IMX6Q_USDHC;
return data->socdata == &usdhc_imx6q_data;
}
static inline int esdhc_is_usdhc(struct pltfm_imx_data *data)
{
return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
}
static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)
@ -164,7 +222,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
struct pltfm_imx_data *imx_data = pltfm_host->priv;
u32 val = readl(host->ioaddr + reg);
if (unlikely(reg == SDHCI_PRESENT_STATE)) {
u32 fsl_prss = val;
/* save the least 20 bits */
val = fsl_prss & 0x000FFFFF;
/* move dat[0-3] bits */
val |= (fsl_prss & 0x0F000000) >> 4;
/* move cmd line bit */
val |= (fsl_prss & 0x00800000) << 1;
}
if (unlikely(reg == SDHCI_CAPABILITIES)) {
/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
val &= 0xffff0000;
/* In FSL esdhc IC module, only bit20 is used to indicate the
* ADMA2 capability of esdhc, but this bit is messed up on
* some SOCs (e.g. on MX25, MX35 this bit is set, but they
@ -178,6 +250,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
}
}
if (unlikely(reg == SDHCI_CAPABILITIES_1)) {
if (esdhc_is_usdhc(imx_data)) {
if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1)
val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF;
else
/* imx6q/dl does not have cap_1 register, fake one */
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
| SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING;
}
}
if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
val = 0;
val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
}
if (unlikely(reg == SDHCI_INT_STATUS)) {
if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
@ -224,7 +315,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
}
}
if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
&& (reg == SDHCI_INT_STATUS)
&& (val & SDHCI_INT_DATA_END))) {
u32 v;
@ -256,10 +347,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
u16 ret = 0;
u32 val;
if (unlikely(reg == SDHCI_HOST_VERSION)) {
reg ^= 2;
if (is_imx6q_usdhc(imx_data)) {
if (esdhc_is_usdhc(imx_data)) {
/*
* The usdhc register returns a wrong host version.
* Correct it here.
@ -268,6 +361,30 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
}
}
if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
if (val & ESDHC_VENDOR_SPEC_VSELECT)
ret |= SDHCI_CTRL_VDD_180;
if (esdhc_is_usdhc(imx_data)) {
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
val = readl(host->ioaddr + ESDHC_MIX_CTRL);
else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
/* the std tuning bits is in ACMD12_ERR for imx6sl */
val = readl(host->ioaddr + SDHCI_ACMD12_ERR);
}
if (val & ESDHC_MIX_CTRL_EXE_TUNE)
ret |= SDHCI_CTRL_EXEC_TUNING;
if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
ret |= SDHCI_CTRL_TUNED_CLK;
ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
return ret;
}
return readw(host->ioaddr + reg);
}
@ -275,10 +392,59 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
u32 new_val = 0;
switch (reg) {
case SDHCI_CLOCK_CONTROL:
new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
if (val & SDHCI_CLOCK_CARD_EN)
new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
else
new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
return;
case SDHCI_HOST_CONTROL2:
new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
if (val & SDHCI_CTRL_VDD_180)
new_val |= ESDHC_VENDOR_SPEC_VSELECT;
else
new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
if (val & SDHCI_CTRL_TUNED_CLK)
new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL;
else
new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
writel(new_val , host->ioaddr + ESDHC_MIX_CTRL);
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
if (val & SDHCI_CTRL_EXEC_TUNING) {
new_val |= ESDHC_STD_TUNING_EN |
ESDHC_TUNING_START_TAP;
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
} else {
new_val &= ~ESDHC_STD_TUNING_EN;
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
}
if (val & SDHCI_CTRL_TUNED_CLK)
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
else
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
}
return;
case SDHCI_TRANSFER_MODE:
if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)
&& (host->cmd->opcode == SD_IO_RW_EXTENDED)
&& (host->cmd->data->blocks > 1)
&& (host->cmd->data->flags & MMC_DATA_READ)) {
@ -288,7 +454,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
writel(v, host->ioaddr + ESDHC_VENDOR_SPEC);
}
if (is_imx6q_usdhc(imx_data)) {
if (esdhc_is_usdhc(imx_data)) {
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
/* Swap AC23 bit */
if (val & SDHCI_TRNS_AUTO_CMD23) {
@ -310,10 +476,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
val |= SDHCI_CMD_ABORTCMD;
if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) &&
(imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
(imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT))
imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
if (is_imx6q_usdhc(imx_data))
if (esdhc_is_usdhc(imx_data))
writel(val << 16,
host->ioaddr + SDHCI_TRANSFER_MODE);
else
@ -379,8 +545,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
* The reset on usdhc fails to clear MIX_CTRL register.
* Do it manually here.
*/
if (is_imx6q_usdhc(imx_data))
if (esdhc_is_usdhc(imx_data)) {
writel(0, host->ioaddr + ESDHC_MIX_CTRL);
imx_data->is_ddr = 0;
}
}
}
@ -409,8 +577,60 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
unsigned int host_clock = clk_get_rate(pltfm_host->clk);
int pre_div = 2;
int div = 1;
u32 temp, val;
esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk));
if (clock == 0) {
if (esdhc_is_usdhc(imx_data)) {
val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
host->ioaddr + ESDHC_VENDOR_SPEC);
}
goto out;
}
if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr)
pre_div = 1;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
while (host_clock / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
while (host_clock / pre_div / div > clock && div < 16)
div++;
host->mmc->actual_clock = host_clock / pre_div / div;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host->mmc->actual_clock);
if (imx_data->is_ddr)
pre_div >>= 2;
else
pre_div >>= 1;
div--;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
if (esdhc_is_usdhc(imx_data)) {
val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
host->ioaddr + ESDHC_VENDOR_SPEC);
}
mdelay(1);
out:
host->clock = clock;
}
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
@ -454,7 +674,192 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
return 0;
}
static const struct sdhci_ops sdhci_esdhc_ops = {
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
{
u32 reg;
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
mdelay(1);
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
ESDHC_MIX_CTRL_FBCLK_SEL;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
dev_dbg(mmc_dev(host->mmc),
"tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n",
val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS));
}
static void esdhc_request_done(struct mmc_request *mrq)
{
complete(&mrq->completion);
}
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
{
struct mmc_command cmd = {0};
struct mmc_request mrq = {0};
struct mmc_data data = {0};
struct scatterlist sg;
char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
cmd.opcode = opcode;
cmd.arg = 0;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
mrq.cmd = &cmd;
mrq.cmd->mrq = &mrq;
mrq.data = &data;
mrq.data->mrq = &mrq;
mrq.cmd->data = mrq.data;
mrq.done = esdhc_request_done;
init_completion(&(mrq.completion));
disable_irq(host->irq);
spin_lock(&host->lock);
host->mrq = &mrq;
sdhci_send_command(host, mrq.cmd);
spin_unlock(&host->lock);
enable_irq(host->irq);
wait_for_completion(&mrq.completion);
if (cmd.error)
return cmd.error;
if (data.error)
return data.error;
return 0;
}
static void esdhc_post_tuning(struct sdhci_host *host)
{
u32 reg;
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
{
int min, max, avg, ret;
/* find the mininum delay first which can pass tuning */
min = ESDHC_TUNE_CTRL_MIN;
while (min < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, min);
if (!esdhc_send_tuning_cmd(host, opcode))
break;
min += ESDHC_TUNE_CTRL_STEP;
}
/* find the maxinum delay which can not pass tuning */
max = min + ESDHC_TUNE_CTRL_STEP;
while (max < ESDHC_TUNE_CTRL_MAX) {
esdhc_prepare_tuning(host, max);
if (esdhc_send_tuning_cmd(host, opcode)) {
max -= ESDHC_TUNE_CTRL_STEP;
break;
}
max += ESDHC_TUNE_CTRL_STEP;
}
/* use average delay to get the best timing */
avg = (min + max) / 2;
esdhc_prepare_tuning(host, avg);
ret = esdhc_send_tuning_cmd(host, opcode);
esdhc_post_tuning(host);
dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
ret ? "failed" : "passed", avg, ret);
return ret;
}
static int esdhc_change_pinstate(struct sdhci_host *host,
unsigned int uhs)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
struct pinctrl_state *pinctrl;
dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
if (IS_ERR(imx_data->pinctrl) ||
IS_ERR(imx_data->pins_default) ||
IS_ERR(imx_data->pins_100mhz) ||
IS_ERR(imx_data->pins_200mhz))
return -EINVAL;
switch (uhs) {
case MMC_TIMING_UHS_SDR50:
pinctrl = imx_data->pins_100mhz;
break;
case MMC_TIMING_UHS_SDR104:
pinctrl = imx_data->pins_200mhz;
break;
default:
/* back to default state for other legacy timing */
pinctrl = imx_data->pins_default;
}
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
}
static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
{
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;
switch (uhs) {
case MMC_TIMING_UHS_SDR12:
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12;
break;
case MMC_TIMING_UHS_SDR25:
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25;
break;
case MMC_TIMING_UHS_SDR50:
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
break;
case MMC_TIMING_UHS_SDR104:
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
break;
case MMC_TIMING_UHS_DDR50:
imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50;
writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
ESDHC_MIX_CTRL_DDREN,
host->ioaddr + ESDHC_MIX_CTRL);
imx_data->is_ddr = 1;
if (boarddata->delay_line) {
u32 v;
v = boarddata->delay_line <<
ESDHC_DLL_OVERRIDE_VAL_SHIFT |
(1 << ESDHC_DLL_OVERRIDE_EN_SHIFT);
if (is_imx53_esdhc(imx_data))
v <<= 1;
writel(v, host->ioaddr + ESDHC_DLL_CTRL);
}
break;
}
return esdhc_change_pinstate(host, uhs);
}
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl_le,
.read_w = esdhc_readw_le,
.write_l = esdhc_writel_le,
@ -465,6 +870,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
.get_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro,
.platform_bus_width = esdhc_pltfm_bus_width,
.set_uhs_signaling = esdhc_set_uhs_signaling,
};
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
@ -506,6 +912,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
of_property_read_u32(np, "max-frequency", &boarddata->f_max);
if (of_find_property(np, "no-1-8-v", NULL))
boarddata->support_vsel = false;
else
boarddata->support_vsel = true;
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
boarddata->delay_line = 0;
return 0;
}
#else
@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
goto free_sdhci;
}
if (of_id)
pdev->id_entry = of_id->data;
imx_data->devtype = pdev->id_entry->driver_data;
imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
pdev->id_entry->driver_data;
pltfm_host->priv = imx_data;
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
@ -568,29 +981,39 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
clk_prepare_enable(imx_data->clk_ipg);
clk_prepare_enable(imx_data->clk_ahb);
imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(imx_data->pinctrl)) {
err = PTR_ERR(imx_data->pinctrl);
goto disable_clk;
}
imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
PINCTRL_STATE_DEFAULT);
if (IS_ERR(imx_data->pins_default)) {
err = PTR_ERR(imx_data->pins_default);
dev_err(mmc_dev(host->mmc), "could not get default state\n");
goto disable_clk;
}
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data))
if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207)
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA;
if (is_imx53_esdhc(imx_data))
imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;
/*
* The imx6q ROM code will change the default watermark level setting
* to something insane. Change it back here.
*/
if (is_imx6q_usdhc(imx_data))
if (esdhc_is_usdhc(imx_data)) {
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
sdhci_esdhc_ops.platform_execute_tuning =
esdhc_executing_tuning;
boarddata = &imx_data->boarddata;
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
if (!host->mmc->parent->platform_data) {
@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
break;
}
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) {
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
ESDHC_PINCTRL_STATE_100MHZ);
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
ESDHC_PINCTRL_STATE_200MHZ);
if (IS_ERR(imx_data->pins_100mhz) ||
IS_ERR(imx_data->pins_200mhz)) {
dev_warn(mmc_dev(host->mmc),
"could not get ultra high speed state, work on normal mode\n");
/* fall back to not support uhs by specify no 1.8v quirk */
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
}
} else {
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
}
err = sdhci_add_host(host);
if (err)
goto disable_clk;

View file

@ -49,41 +49,4 @@
#define ESDHC_HOST_CONTROL_RES 0x05
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
unsigned int host_clock)
{
int pre_div = 2;
int div = 1;
u32 temp;
if (clock == 0)
goto out;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
while (host_clock / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
while (host_clock / pre_div / div > clock && div < 16)
div++;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host_clock / pre_div / div);
pre_div >>= 1;
div--;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
mdelay(1);
out:
host->clock = clock;
}
#endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */

View file

@ -199,6 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
{
int pre_div = 2;
int div = 1;
u32 temp;
if (clock == 0)
goto out;
/* Workaround to reduce the clock frequency for p1010 esdhc */
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
if (clock > 20000000)
@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
clock -= 5000000;
}
/* Set the clock */
esdhc_set_clock(host, clock, host->max_clk);
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK);
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
pre_div *= 2;
while (host->max_clk / pre_div / div > clock && div < 16)
div++;
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
clock, host->max_clk / pre_div / div);
pre_div >>= 1;
div--;
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| (div << ESDHC_DIVIDER_SHIFT)
| (pre_div << ESDHC_PREDIV_SHIFT));
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
mdelay(1);
out:
host->clock = clock;
}
#ifdef CONFIG_PM

View file

@ -37,6 +37,12 @@
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
/*
* PCI registers
@ -356,6 +362,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
.allow_runtime_pm = true,
};
/* Define Host controllers for Intel Merrifield platform */
#define INTEL_MRFL_EMMC_0 0
#define INTEL_MRFL_EMMC_1 1
static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
{
if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) &&
(PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1))
/* SD support is not ready yet */
return -ENODEV;
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_1_8V_DDR;
return 0;
}
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.probe_slot = intel_mrfl_mmc_probe_slot,
};
/* O2Micro extra registers */
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_MULTI_VCC3V 0xEE
@ -939,6 +967,54 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CLV_SDIO0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CLV_SDIO1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CLV_SDIO2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CLV_EMMC0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_CLV_EMMC1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_MRFL_MMC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120,

View file

@ -49,7 +49,6 @@ static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_send_command(struct sdhci_host *, struct mmc_command *);
static void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data);
@ -981,7 +980,7 @@ static void sdhci_finish_data(struct sdhci_host *host)
tasklet_schedule(&host->finish_tasklet);
}
static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
u32 mask;
@ -1053,6 +1052,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
}
EXPORT_SYMBOL_GPL(sdhci_send_command);
static void sdhci_finish_command(struct sdhci_host *host)
{
@ -1435,7 +1435,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
}
if (host->version >= SDHCI_SPEC_300 &&
(ios->power_mode == MMC_POWER_UP))
(ios->power_mode == MMC_POWER_UP) &&
!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
sdhci_enable_preset_value(host, false);
sdhci_set_clock(host, ios->clock);
@ -1875,6 +1876,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
return 0;
}
if (host->ops->platform_execute_tuning) {
spin_unlock(&host->lock);
enable_irq(host->irq);
err = host->ops->platform_execute_tuning(host, opcode);
sdhci_runtime_pm_put(host);
return err;
}
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
/*
@ -1981,6 +1990,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (!tuning_loop_counter || !timeout) {
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
err = -EIO;
} else {
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
pr_info(DRIVER_NAME ": Tuning procedure"
@ -2491,6 +2501,14 @@ again:
result = IRQ_HANDLED;
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
/*
* If we know we'll call the driver to signal SDIO IRQ, disregard
* further indications of Card Interrupt in the status to avoid a
* needless loop.
*/
if (cardint)
intmask &= ~SDHCI_INT_CARD_INT;
if (intmask && --max_loops)
goto again;
out:
@ -2546,8 +2564,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host)
{
int ret;
if (host->ops->platform_suspend)
host->ops->platform_suspend(host);
@ -2559,19 +2575,6 @@ int sdhci_suspend_host(struct sdhci_host *host)
host->flags &= ~SDHCI_NEEDS_RETUNING;
}
ret = mmc_suspend_host(host->mmc);
if (ret) {
if (host->flags & SDHCI_USING_RETUNING_TIMER) {
host->flags |= SDHCI_NEEDS_RETUNING;
mod_timer(&host->tuning_timer, jiffies +
host->tuning_count * HZ);
}
sdhci_enable_card_detection(host);
return ret;
}
if (!device_may_wakeup(mmc_dev(host->mmc))) {
sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
free_irq(host->irq, host);
@ -2579,14 +2582,14 @@ int sdhci_suspend_host(struct sdhci_host *host)
sdhci_enable_irq_wakeups(host);
enable_irq_wake(host->irq);
}
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_suspend_host);
int sdhci_resume_host(struct sdhci_host *host)
{
int ret;
int ret = 0;
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma)
@ -2615,7 +2618,6 @@ int sdhci_resume_host(struct sdhci_host *host)
mmiowb();
}
ret = mmc_resume_host(host->mmc);
sdhci_enable_card_detection(host);
if (host->ops->platform_resume)

View file

@ -288,6 +288,7 @@ struct sdhci_ops {
unsigned int (*get_ro)(struct sdhci_host *host);
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
void (*platform_suspend)(struct sdhci_host *host);
@ -393,6 +394,8 @@ static inline void *sdhci_priv(struct sdhci_host *host)
extern void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
struct mmc_command *cmd);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host);

View file

@ -516,9 +516,7 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link)
#ifdef CONFIG_PM
static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
{
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "suspend\n");
mmc_suspend_host(mmc);
return 0;
}
@ -527,7 +525,6 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "resume\n");
sdricoh_reset(mmc_priv(mmc));
mmc_resume_host(mmc);
return 0;
}
#else

View file

@ -964,7 +964,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq)
static int sh_mmcif_clk_update(struct sh_mmcif_host *host)
{
int ret = clk_enable(host->hclk);
int ret = clk_prepare_enable(host->hclk);
if (!ret) {
host->clk = clk_get_rate(host->hclk);
@ -1018,7 +1018,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
if (host->power) {
pm_runtime_put_sync(&host->pd->dev);
clk_disable(host->hclk);
clk_disable_unprepare(host->hclk);
host->power = false;
if (ios->power_mode == MMC_POWER_OFF)
sh_mmcif_set_power(host, ios);
@ -1466,7 +1466,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
mutex_init(&host->thread_lock);
clk_disable(host->hclk);
clk_disable_unprepare(host->hclk);
ret = mmc_add_host(mmc);
if (ret < 0)
goto emmcaddh;
@ -1487,7 +1487,7 @@ ereqirq1:
ereqirq0:
pm_runtime_suspend(&pdev->dev);
eresume:
clk_disable(host->hclk);
clk_disable_unprepare(host->hclk);
eclkupdate:
clk_put(host->hclk);
eclkget:
@ -1505,7 +1505,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
int irq[2];
host->dying = true;
clk_enable(host->hclk);
clk_prepare_enable(host->hclk);
pm_runtime_get_sync(&pdev->dev);
dev_pm_qos_hide_latency_limit(&pdev->dev);
@ -1530,7 +1530,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
if (irq[1] >= 0)
free_irq(irq[1], host);
clk_disable(host->hclk);
clk_disable_unprepare(host->hclk);
mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@ -1538,28 +1538,21 @@ static int sh_mmcif_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int sh_mmcif_suspend(struct device *dev)
{
struct sh_mmcif_host *host = dev_get_drvdata(dev);
int ret = mmc_suspend_host(host->mmc);
if (!ret)
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
return ret;
return 0;
}
static int sh_mmcif_resume(struct device *dev)
{
struct sh_mmcif_host *host = dev_get_drvdata(dev);
return mmc_resume_host(host->mmc);
return 0;
}
#else
#define sh_mmcif_suspend NULL
#define sh_mmcif_resume NULL
#endif /* CONFIG_PM */
#endif
static const struct of_device_id mmcif_of_match[] = {
{ .compatible = "renesas,sh-mmcif" },
@ -1568,8 +1561,7 @@ static const struct of_device_id mmcif_of_match[] = {
MODULE_DEVICE_TABLE(of, mmcif_of_match);
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
.suspend = sh_mmcif_suspend,
.resume = sh_mmcif_resume,
SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
};
static struct platform_driver sh_mmcif_driver = {

View file

@ -1030,7 +1030,7 @@ static void tifm_sd_remove(struct tifm_dev *sock)
static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state)
{
return mmc_suspend_host(tifm_get_drvdata(sock));
return 0;
}
static int tifm_sd_resume(struct tifm_dev *sock)
@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock)
if (rc)
host->eject = 1;
else
rc = mmc_resume_host(mmc);
return rc;
}

View file

@ -1145,12 +1145,9 @@ int tmio_mmc_host_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
int ret = mmc_suspend_host(mmc);
if (!ret)
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
return ret;
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_suspend);
@ -1163,7 +1160,7 @@ int tmio_mmc_host_resume(struct device *dev)
/* The MMC core will perform the complete set up */
host->resuming = true;
return mmc_resume_host(mmc);
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_resume);

View file

@ -1269,21 +1269,18 @@ static void via_init_sdc_pm(struct via_crdr_mmc_host *host)
static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state)
{
struct via_crdr_mmc_host *host;
int ret = 0;
host = pci_get_drvdata(pcidev);
via_save_pcictrlreg(host);
via_save_sdcreg(host);
ret = mmc_suspend_host(host->mmc);
pci_save_state(pcidev);
pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
pci_disable_device(pcidev);
pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
return ret;
return 0;
}
static int via_sd_resume(struct pci_dev *pcidev)
@ -1316,8 +1313,6 @@ static int via_sd_resume(struct pci_dev *pcidev)
via_restore_pcictrlreg(sdhost);
via_init_sdc_pm(sdhost);
ret = mmc_resume_host(sdhost->mmc);
return ret;
}

View file

@ -2392,26 +2392,12 @@ static void vub300_disconnect(struct usb_interface *interface)
#ifdef CONFIG_PM
static int vub300_suspend(struct usb_interface *intf, pm_message_t message)
{
struct vub300_mmc_host *vub300 = usb_get_intfdata(intf);
if (!vub300 || !vub300->mmc) {
return 0;
} else {
struct mmc_host *mmc = vub300->mmc;
mmc_suspend_host(mmc);
return 0;
}
return 0;
}
static int vub300_resume(struct usb_interface *intf)
{
struct vub300_mmc_host *vub300 = usb_get_intfdata(intf);
if (!vub300 || !vub300->mmc) {
return 0;
} else {
struct mmc_host *mmc = vub300->mmc;
mmc_resume_host(mmc);
return 0;
}
return 0;
}
#else
#define vub300_suspend NULL

View file

@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev)
#ifdef CONFIG_PM
static int wbsd_suspend(struct wbsd_host *host, pm_message_t state)
{
BUG_ON(host == NULL);
return mmc_suspend_host(host->mmc);
}
static int wbsd_resume(struct wbsd_host *host)
{
BUG_ON(host == NULL);
wbsd_init_device(host);
return mmc_resume_host(host->mmc);
}
static int wbsd_platform_suspend(struct platform_device *dev,
pm_message_t state)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct wbsd_host *host;
int ret;
if (mmc == NULL)
return 0;
@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev,
host = mmc_priv(mmc);
ret = wbsd_suspend(host, state);
if (ret)
return ret;
wbsd_chip_poweroff(host);
return 0;
}
@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev)
*/
mdelay(5);
return wbsd_resume(host);
wbsd_init_device(host);
return 0;
}
#ifdef CONFIG_PNP
@ -1880,16 +1859,12 @@ static int wbsd_platform_resume(struct platform_device *dev)
static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
{
struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
struct wbsd_host *host;
if (mmc == NULL)
return 0;
DBGF("Suspending...\n");
host = mmc_priv(mmc);
return wbsd_suspend(host, state);
return 0;
}
static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
@ -1922,7 +1897,8 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
*/
mdelay(5);
return wbsd_resume(host);
wbsd_init_device(host);
return 0;
}
#endif /* CONFIG_PNP */

View file

@ -212,28 +212,14 @@ struct wmt_mci_priv {
static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
{
u32 reg_tmp;
if (enable) {
if (priv->power_inverted) {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp | BM_SD_OFF,
priv->sdmmc_base + SDMMC_BUSMODE);
} else {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp & (~BM_SD_OFF),
priv->sdmmc_base + SDMMC_BUSMODE);
}
} else {
if (priv->power_inverted) {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp & (~BM_SD_OFF),
priv->sdmmc_base + SDMMC_BUSMODE);
} else {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp | BM_SD_OFF,
priv->sdmmc_base + SDMMC_BUSMODE);
}
}
u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
if (enable ^ priv->power_inverted)
reg_tmp &= ~BM_SD_OFF;
else
reg_tmp |= BM_SD_OFF;
writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE);
}
static void wmt_mci_read_response(struct mmc_host *mmc)
@ -939,28 +925,23 @@ static int wmt_mci_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct wmt_mci_priv *priv;
int ret;
if (!mmc)
return 0;
priv = mmc_priv(mmc);
ret = mmc_suspend_host(mmc);
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
SDMMC_BUSMODE);
if (!ret) {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
SDMMC_BUSMODE);
reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
clk_disable(priv->clk_sdmmc);
}
return ret;
clk_disable(priv->clk_sdmmc);
return 0;
}
static int wmt_mci_resume(struct device *dev)
@ -969,7 +950,6 @@ static int wmt_mci_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct wmt_mci_priv *priv;
int ret = 0;
if (mmc) {
priv = mmc_priv(mmc);
@ -987,10 +967,9 @@ static int wmt_mci_resume(struct device *dev)
writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +
SDMMC_INTMASK0);
ret = mmc_resume_host(mmc);
}
return ret;
return 0;
}
static const struct dev_pm_ops wmt_mci_pm = {

View file

@ -240,6 +240,7 @@ struct mmc_part {
struct mmc_card {
struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the device */
u32 ocr; /* the current OCR setting */
unsigned int rca; /* relative card address of device */
unsigned int type; /* card type */
#define MMC_TYPE_MMC 0 /* MMC card */
@ -257,6 +258,7 @@ struct mmc_card {
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
#define MMC_STATE_SUSPENDED (1<<11) /* card is suspended */
unsigned int quirks; /* card quirks */
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
@ -420,10 +422,10 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED)
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
@ -432,11 +434,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
#define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS)
#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED)
#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED)
/*
* Quirk add/remove for MMC products.

View file

@ -151,7 +151,8 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception);
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool);
extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool,
bool);
extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int);
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
@ -188,7 +189,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int);
extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host);
extern int mmc_try_claim_host(struct mmc_host *host);
extern void mmc_get_card(struct mmc_card *card);
extern void mmc_put_card(struct mmc_card *card);

View file

@ -15,6 +15,7 @@
#define LINUX_MMC_DW_MMC_H
#include <linux/scatterlist.h>
#include <linux/mmc/core.h>
#define MAX_MCI_SLOTS 2
@ -129,6 +130,9 @@ struct dw_mci {
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
struct mmc_command stop_abort;
unsigned int prev_blksz;
unsigned char timing;
struct workqueue_struct *card_workqueue;
/* DMA interface members*/

View file

@ -254,6 +254,7 @@ struct mmc_host {
#define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */
#define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */
#define MMC_CAP_RUNTIME_RESUME (1 << 20) /* Resume at runtime_resume. */
#define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */
#define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */
@ -309,7 +310,6 @@ struct mmc_host {
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */
u32 ocr; /* the current OCR setting */
/* group bitfields together to minimize padding */
unsigned int use_spi_crc:1;
@ -382,9 +382,6 @@ static inline void *mmc_priv(struct mmc_host *host)
#define mmc_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) (dev_name(&(x)->class_dev))
int mmc_suspend_host(struct mmc_host *);
int mmc_resume_host(struct mmc_host *);
int mmc_power_save_host(struct mmc_host *host);
int mmc_power_restore_host(struct mmc_host *host);

View file

@ -10,6 +10,8 @@
#ifndef __ASM_ARCH_IMX_ESDHC_H
#define __ASM_ARCH_IMX_ESDHC_H
#include <linux/types.h>
enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@ -32,6 +34,7 @@ enum cd_types {
* @cd_gpio: gpio for card_detect interrupt
* @wp_type: type of write_protect method (see wp_types enum above)
* @cd_type: type of card_detect method (see cd_types enum above)
* @support_vsel: indicate it supports 1.8v switching
*/
struct esdhc_platform_data {
@ -41,5 +44,7 @@ struct esdhc_platform_data {
enum cd_types cd_type;
int max_bus_width;
unsigned int f_max;
bool support_vsel;
unsigned int delay_line;
};
#endif /* __ASM_ARCH_IMX_ESDHC_H */