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: Optional properties:
- fsl,cd-controller : Indicate to use controller internal card detection - fsl,cd-controller : Indicate to use controller internal card detection
- fsl,wp-controller : Indicate to use controller internal write protection - 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: 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 is specified and the ciu clock is specified then we'll try to set the ciu
clock to this at probe time. 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. * num-slots: specifies the number of slots supported by the controller.
The number of physical slots actually used could be equal or less than the The number of physical slots actually used could be equal or less than the
value specified by num-slots. If this property is not specified, the value value specified by num-slots. If this property is not specified, the value
@ -66,6 +69,10 @@ Optional properties:
* supports-highspeed: Enables support for high speed cards (up to 50MHz) * 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. * broken-cd: as documented in mmc core bindings.
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is * 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 { dwmmc0@12200000 {
clock-frequency = <400000000>; clock-frequency = <400000000>;
clock-freq-min-max = <400000 200000000>;
num-slots = <1>; num-slots = <1>;
supports-highspeed; supports-highspeed;
caps2-mmc-hs200-1_8v;
broken-cd; broken-cd;
fifo-depth = <0x80>; fifo-depth = <0x80>;
card-detect-delay = <200>; card-detect-delay = <200>;

View File

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

View File

@ -15,6 +15,7 @@
#include <linux/mmc/sh_mmcif.h> #include <linux/mmc/sh_mmcif.h>
#include <linux/mmc/sh_mobile_sdhi.h> #include <linux/mmc/sh_mobile_sdhi.h>
#include <linux/mtd/physmap.h> #include <linux/mtd/physmap.h>
#include <linux/mfd/tmio.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.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); struct mmc_blk_data *md = mmc_get_drvdata(card);
if (md) { if (md) {
pm_runtime_get_sync(&card->dev);
mmc_queue_suspend(&md->queue); mmc_queue_suspend(&md->queue);
list_for_each_entry(part_md, &md->part, part) { list_for_each_entry(part_md, &md->part, part) {
mmc_queue_suspend(&part_md->queue); mmc_queue_suspend(&part_md->queue);
@ -2483,7 +2482,6 @@ static int mmc_blk_resume(struct mmc_card *card)
list_for_each_entry(part_md, &md->part, part) { list_for_each_entry(part_md, &md->part, part) {
mmc_queue_resume(&part_md->queue); mmc_queue_resume(&part_md->queue);
} }
pm_runtime_put(&card->dev);
} }
return 0; return 0;
} }

View File

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

View File

@ -23,6 +23,7 @@
#include <linux/log2.h> #include <linux/log2.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/pm_wakeup.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/fault-inject.h> #include <linux/fault-inject.h>
#include <linux/random.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, 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) { if (err) {
pr_warn("%s: Error %d starting bkops\n", pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err); 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); 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 * mmc_release_host - release a host
* @host: mmc host to release * @host: mmc host to release
@ -1382,22 +1358,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
{ {
int bit; 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; ocr &= host->ocr_avail;
if (!ocr) {
dev_warn(mmc_dev(host), "no support for card's volts\n");
return 0;
}
bit = ffs(ocr); if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
if (bit) { bit = ffs(ocr) - 1;
bit -= 1;
ocr &= 3 << bit; ocr &= 3 << bit;
mmc_power_cycle(host, ocr);
mmc_host_clk_hold(host);
host->ios.vdd = bit;
mmc_set_ios(host);
mmc_host_clk_release(host);
} else { } else {
pr_warning("%s: host doesn't support card's voltages\n", bit = fls(ocr) - 1;
mmc_hostname(host)); ocr &= 3 << bit;
ocr = 0; if (bit != host->ios.vdd)
dev_warn(mmc_dev(host), "exceeding card's volts\n");
} }
return ocr; 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}; struct mmc_command cmd = {0};
int err = 0; int err = 0;
@ -1504,7 +1489,7 @@ power_cycle:
if (err) { if (err) {
pr_debug("%s: Signal voltage switch failed, " pr_debug("%s: Signal voltage switch failed, "
"power cycling card\n", mmc_hostname(host)); "power cycling card\n", mmc_hostname(host));
mmc_power_cycle(host); mmc_power_cycle(host, ocr);
} }
mmc_host_clk_release(host); 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 * If a host does all the power sequencing itself, ignore the
* initial MMC_POWER_UP stage. * 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) if (host->ios.power_mode == MMC_POWER_ON)
return; return;
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
/* If ocr is set, we use it */ host->ios.vdd = fls(ocr) - 1;
if (host->ocr)
bit = ffs(host->ocr) - 1;
else
bit = fls(host->ocr_avail) - 1;
host->ios.vdd = bit;
if (mmc_host_is_spi(host)) if (mmc_host_is_spi(host))
host->ios.chip_select = MMC_CS_HIGH; host->ios.chip_select = MMC_CS_HIGH;
else else
@ -1604,13 +1581,6 @@ void mmc_power_off(struct mmc_host *host)
host->ios.clock = 0; host->ios.clock = 0;
host->ios.vdd = 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)) { if (!mmc_host_is_spi(host)) {
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
host->ios.chip_select = MMC_CS_DONTCARE; host->ios.chip_select = MMC_CS_DONTCARE;
@ -1630,12 +1600,12 @@ void mmc_power_off(struct mmc_host *host)
mmc_host_clk_release(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); mmc_power_off(host);
/* Wait at least 1 ms according to SD spec */ /* Wait at least 1 ms according to SD spec */
mmc_delay(1); 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); 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 * mmc_detect_change - process change of state on a MMC socket
* @host: host which changed state. * @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) void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{ {
#ifdef CONFIG_MMC_DEBUG _mmc_detect_change(host, delay, true);
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);
} }
EXPORT_SYMBOL(mmc_detect_change); EXPORT_SYMBOL(mmc_detect_change);
void mmc_init_erase(struct mmc_card *card) 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", pr_info("%s: %s: trying to init card at %u Hz\n",
mmc_hostname(host), __func__, host->f_init); mmc_hostname(host), __func__, host->f_init);
#endif #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 * 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. * rescan handle the card removal.
*/ */
cancel_delayed_work(&host->detect); 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) if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host); mmc_power_off(host);
else else
mmc_power_up(host); mmc_power_up(host, host->ocr_avail);
mmc_detect_change(host, 0); _mmc_detect_change(host, 0, false);
} }
void mmc_stop_host(struct mmc_host *host) void mmc_stop_host(struct mmc_host *host)
@ -2583,7 +2567,7 @@ int mmc_power_restore_host(struct mmc_host *host)
return -EINVAL; return -EINVAL;
} }
mmc_power_up(host); mmc_power_up(host, host->card->ocr);
ret = host->bus_ops->power_restore(host); ret = host->bus_ops->power_restore(host);
mmc_bus_put(host); mmc_bus_put(host);
@ -2657,28 +2641,6 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
#ifdef CONFIG_PM #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 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 * Do that in pm notifier while userspace isn't yet frozen, so we will be able
to sync the card. to sync the card.
@ -2724,7 +2686,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
host->rescan_disable = 0; host->rescan_disable = 0;
spin_unlock_irqrestore(&host->lock, flags); 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_mode(struct mmc_host *host, unsigned int mode);
void mmc_set_bus_width(struct mmc_host *host, unsigned int width); void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); 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); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_timing(struct mmc_host *host, unsigned int timing);
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
void mmc_power_up(struct mmc_host *host); void mmc_power_up(struct mmc_host *host, u32 ocr);
void mmc_power_off(struct mmc_host *host); void mmc_power_off(struct mmc_host *host);
void mmc_power_cycle(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr);
static inline void mmc_delay(unsigned int ms) static inline void mmc_delay(unsigned int ms)
{ {

View File

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

View File

@ -23,6 +23,40 @@
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ #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) static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
{ {
int err; 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_ms: timeout (ms) for operation performed by register write,
* timeout of zero implies maximum possible timeout * timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type * @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. * Modifies the EXT_CSD register for selected card.
*/ */
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, 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; int err;
struct mmc_command cmd = {0}; struct mmc_command cmd = {0};
unsigned long timeout; unsigned long timeout;
u32 status; u32 status = 0;
bool ignore_crc = false;
BUG_ON(!card); BUG_ON(!card);
BUG_ON(!card->host); 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) if (!use_busy_signal)
return 0; 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); timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
do { do {
err = mmc_send_status(card, &status); if (send_status) {
if (err) err = __mmc_send_status(card, &status, ignore_crc);
return err; if (err)
return err;
}
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
break; break;
if (mmc_host_is_spi(card->host)) if (mmc_host_is_spi(card->host))
break; 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. */ /* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) { if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n", 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, int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms) 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); 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 static int
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
u8 len) u8 len)

View File

@ -13,6 +13,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/pm_runtime.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/card.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; int err;
u32 max_current; u32 max_current;
int retries = 10; int retries = 10;
u32 pocr = ocr;
try_again: try_again:
if (!retries) { if (!retries) {
@ -773,7 +775,8 @@ try_again:
*/ */
if (!mmc_host_is_spi(host) && rocr && if (!mmc_host_is_spi(host) && rocr &&
((*rocr & 0x41000000) == 0x41000000)) { ((*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) { if (err == -EAGAIN) {
retries--; retries--;
goto try_again; goto try_again;
@ -935,6 +938,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
if (IS_ERR(card)) if (IS_ERR(card))
return PTR_ERR(card); return PTR_ERR(card);
card->ocr = ocr;
card->type = MMC_TYPE_SD; card->type = MMC_TYPE_SD;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
} }
@ -1064,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host)
} }
} }
/* static int _mmc_sd_suspend(struct mmc_host *host)
* Suspend callback from host.
*/
static int mmc_sd_suspend(struct mmc_host *host)
{ {
int err = 0; int err = 0;
@ -1075,34 +1076,77 @@ static int mmc_sd_suspend(struct mmc_host *host)
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
if (mmc_card_suspended(host->card))
goto out;
if (!mmc_host_is_spi(host)) if (!mmc_host_is_spi(host))
err = mmc_deselect_cards(host); err = mmc_deselect_cards(host);
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
if (!err) if (!err) {
mmc_power_off(host); mmc_power_off(host);
mmc_card_set_suspended(host->card);
}
out:
mmc_release_host(host); 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; return err;
} }
/* /*
* Resume callback from host.
*
* This function tries to determine if the same card is still present * This function tries to determine if the same card is still present
* and, if so, restore all state to it. * 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);
BUG_ON(!host->card); BUG_ON(!host->card);
mmc_claim_host(host); mmc_claim_host(host);
mmc_power_up(host);
mmc_select_voltage(host, host->ocr); if (!mmc_card_suspended(host->card))
err = mmc_sd_init_card(host, host->ocr, 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); 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; return err;
} }
@ -1117,18 +1161,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
return 0; 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", pr_err("%s: error %d doing aggessive suspend\n",
mmc_hostname(host), err); mmc_hostname(host), err);
goto out;
}
mmc_power_off(host);
out:
mmc_release_host(host);
return err; return err;
} }
@ -1139,18 +1176,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
{ {
int err; int err;
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
return 0; return 0;
mmc_claim_host(host); err = _mmc_sd_resume(host);
mmc_power_up(host);
err = mmc_sd_resume(host);
if (err) if (err)
pr_err("%s: error %d doing aggessive resume\n", pr_err("%s: error %d doing aggessive resume\n",
mmc_hostname(host), err); mmc_hostname(host), err);
mmc_release_host(host);
return 0; return 0;
} }
@ -1160,7 +1193,7 @@ static int mmc_sd_power_restore(struct mmc_host *host)
host->card->state &= ~MMC_STATE_HIGHSPEED; host->card->state &= ~MMC_STATE_HIGHSPEED;
mmc_claim_host(host); 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); mmc_release_host(host);
return ret; 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 mmc_attach_sd(struct mmc_host *host)
{ {
int err; int err;
u32 ocr; u32 ocr, rocr;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
@ -1229,31 +1262,12 @@ int mmc_attach_sd(struct mmc_host *host)
goto err; goto err;
} }
/* rocr = mmc_select_voltage(host, ocr);
* 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);
/* /*
* Can we support the voltage(s) of the card(s)? * Can we support the voltage(s) of the card(s)?
*/ */
if (!host->ocr) { if (!rocr) {
err = -EINVAL; err = -EINVAL;
goto err; goto err;
} }
@ -1261,7 +1275,7 @@ int mmc_attach_sd(struct mmc_host *host)
/* /*
* Detect and init the card. * Detect and init the card.
*/ */
err = mmc_sd_init_card(host, host->ocr, NULL); err = mmc_sd_init_card(host, rocr, NULL);
if (err) if (err)
goto 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; struct mmc_card *card;
int err; int err;
int retries = 10; int retries = 10;
u32 rocr = 0;
u32 ocr_card = ocr;
BUG_ON(!host); BUG_ON(!host);
WARN_ON(!host->claimed); WARN_ON(!host->claimed);
/* to query card if 1.8V signalling is supported */
if (mmc_host_uhs(host))
ocr |= R4_18V_PRESENT;
try_again: try_again:
if (!retries) { if (!retries) {
pr_warning("%s: Skipping voltage switch\n", pr_warning("%s: Skipping voltage switch\n",
mmc_hostname(host)); mmc_hostname(host));
ocr &= ~R4_18V_PRESENT; ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
} }
/* /*
* Inform the card of the voltage * Inform the card of the voltage
*/ */
if (!powered_resume) { if (!powered_resume) {
err = mmc_send_io_op_cond(host, host->ocr, &ocr); err = mmc_send_io_op_cond(host, ocr, &rocr);
if (err) if (err)
goto err; goto err;
} }
@ -632,8 +637,8 @@ try_again:
goto err; goto err;
} }
if ((ocr & R4_MEMORY_PRESENT) && if ((rocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
card->type = MMC_TYPE_SD_COMBO; card->type = MMC_TYPE_SD_COMBO;
if (oldcard && (oldcard->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 * systems that claim 1.8v signalling in fact do not support
* it. * it.
*/ */
if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
ocr);
if (err == -EAGAIN) { if (err == -EAGAIN) {
sdio_reset(host); sdio_reset(host);
mmc_go_idle(host); mmc_go_idle(host);
@ -674,12 +680,10 @@ try_again:
goto try_again; goto try_again;
} else if (err) { } else if (err) {
ocr &= ~R4_18V_PRESENT; ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
} }
err = 0; err = 0;
} else { } else {
ocr &= ~R4_18V_PRESENT; ocr &= ~R4_18V_PRESENT;
host->ocr &= ~R4_18V_PRESENT;
} }
/* /*
@ -759,6 +763,7 @@ try_again:
card = oldcard; card = oldcard;
} }
card->ocr = ocr_card;
mmc_fixup_device(card, NULL); mmc_fixup_device(card, NULL);
if (card->type == MMC_TYPE_SD_COMBO) { if (card->type == MMC_TYPE_SD_COMBO) {
@ -981,8 +986,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
/* Restore power if needed */ /* Restore power if needed */
if (!mmc_card_keep_power(host)) { if (!mmc_card_keep_power(host)) {
mmc_power_up(host); mmc_power_up(host, host->card->ocr);
mmc_select_voltage(host, host->ocr);
/* /*
* Tell runtime PM core we just powered up the card, * Tell runtime PM core we just powered up the card,
* since it still believes the card is powered off. * 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)) { if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
sdio_reset(host); sdio_reset(host);
mmc_go_idle(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)); mmc_card_keep_power(host));
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
/* We may have switched to 1-bit mode during suspend */ /* 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) static int mmc_sdio_power_restore(struct mmc_host *host)
{ {
int ret; int ret;
u32 ocr;
BUG_ON(!host); BUG_ON(!host);
BUG_ON(!host->card); 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 * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
* harmless in other situations. * 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); sdio_reset(host);
mmc_go_idle(host); mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail); 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) if (ret)
goto out; goto out;
if (host->ocr_avail_sdio) ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
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,
mmc_card_keep_power(host)); mmc_card_keep_power(host));
if (!ret && host->sdio_irqs) if (!ret && host->sdio_irqs)
mmc_signal_sdio_irq(host); 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) static int mmc_sdio_runtime_resume(struct mmc_host *host)
{ {
/* Restore power and re-initialize. */ /* Restore power and re-initialize. */
mmc_power_up(host); mmc_power_up(host, host->card->ocr);
return mmc_sdio_power_restore(host); 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 mmc_attach_sdio(struct mmc_host *host)
{ {
int err, i, funcs; int err, i, funcs;
u32 ocr; u32 ocr, rocr;
struct mmc_card *card; struct mmc_card *card;
BUG_ON(!host); BUG_ON(!host);
@ -1145,23 +1133,13 @@ int mmc_attach_sdio(struct mmc_host *host)
if (host->ocr_avail_sdio) if (host->ocr_avail_sdio)
host->ocr_avail = 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)? * Can we support the voltage(s) of the card(s)?
*/ */
if (!host->ocr) { if (!rocr) {
err = -EINVAL; err = -EINVAL;
goto err; goto err;
} }
@ -1169,22 +1147,10 @@ int mmc_attach_sdio(struct mmc_host *host)
/* /*
* Detect and init the card. * Detect and init the card.
*/ */
if (mmc_host_uhs(host)) err = mmc_sdio_init_card(host, rocr, NULL, 0);
/* to query card if 1.8V signalling is supported */ if (err)
host->ocr |= R4_18V_PRESENT; 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; card = host->card;
/* /*

View File

@ -255,7 +255,6 @@ struct atmel_mci_slot {
#define ATMCI_CARD_PRESENT 0 #define ATMCI_CARD_PRESENT 0
#define ATMCI_CARD_NEED_INIT 1 #define ATMCI_CARD_NEED_INIT 1
#define ATMCI_SHUTDOWN 2 #define ATMCI_SHUTDOWN 2
#define ATMCI_SUSPENDED 3
int detect_pin; int detect_pin;
int wp_pin; int wp_pin;
@ -589,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data)
if (host->mrq->cmd->data) { if (host->mrq->cmd->data) {
host->mrq->cmd->data->error = -ETIMEDOUT; host->mrq->cmd->data->error = -ETIMEDOUT;
host->data = NULL; 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 { } else {
host->mrq->cmd->error = -ETIMEDOUT; host->mrq->cmd->error = -ETIMEDOUT;
host->cmd = NULL; host->cmd = NULL;
@ -1803,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv)
if (unlikely(status)) { if (unlikely(status)) {
host->stop_transfer(host); host->stop_transfer(host);
host->data = NULL; host->data = NULL;
if (status & ATMCI_DTOE) { if (data) {
data->error = -ETIMEDOUT; if (status & ATMCI_DTOE) {
} else if (status & ATMCI_DCRCE) { data->error = -ETIMEDOUT;
data->error = -EILSEQ; } else if (status & ATMCI_DCRCE) {
} else { data->error = -EILSEQ;
data->error = -EIO; } else {
data->error = -EIO;
}
} }
} }
@ -2520,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver atmci_driver = {
.remove = __exit_p(atmci_remove), .remove = __exit_p(atmci_remove),
.driver = { .driver = {
.name = "atmel_mci", .name = "atmel_mci",
.pm = &atmci_pm,
.of_match_table = of_match_ptr(atmci_dt_ids), .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) static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct au1xmmc_host *host = platform_get_drvdata(pdev); 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_CONFIG2(host));
au_writel(0, HOST_CONFIG(host)); au_writel(0, HOST_CONFIG(host));
@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev)
au1xmmc_reset_controller(host); au1xmmc_reset_controller(host);
return mmc_resume_host(host->mmc); return 0;
} }
#else #else
#define au1xmmc_suspend NULL #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 */ /* Disable 4 bit SDIO */
cfg &= ~SD4E; cfg &= ~SD4E;
} }
bfin_write_SDH_CFG(cfg);
host->power_mode = ios->power_mode; host->power_mode = ios->power_mode;
#ifndef RSI_BLKSZ #ifndef RSI_BLKSZ
@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
cfg &= ~SD_CMD_OD; cfg &= ~SD_CMD_OD;
# endif # endif
if (ios->power_mode != MMC_POWER_OFF) if (ios->power_mode != MMC_POWER_OFF)
cfg |= PWR_ON; cfg |= PWR_ON;
else else
@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_ctl |= CLK_E; clk_ctl |= CLK_E;
host->clk_div = clk_div; host->clk_div = clk_div;
bfin_write_SDH_CLK_CTL(clk_ctl); bfin_write_SDH_CLK_CTL(clk_ctl);
} else } else
sdh_stop_clock(host); sdh_stop_clock(host);
@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int sdh_suspend(struct platform_device *dev, pm_message_t state) 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); 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); peripheral_free_list(drv_data->pin_req);
return ret; return 0;
} }
static int sdh_resume(struct platform_device *dev) 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); struct bfin_sd_host *drv_data = get_sdh_data(dev);
int ret = 0; int ret = 0;
@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev)
} }
sdh_reset(); sdh_reset();
if (mmc)
ret = mmc_resume_host(mmc);
return ret; return ret;
} }
#else #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) static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
{ {
struct cb710_slot *slot = cb710_pdev_to_slot(pdev); 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); cb710_mmc_enable_irq(slot, 0, ~0);
return 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) static int cb710_mmc_resume(struct platform_device *pdev)
{ {
struct cb710_slot *slot = cb710_pdev_to_slot(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); cb710_mmc_enable_irq(slot, 0, ~0);
return 0;
return mmc_resume_host(mmc);
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */

View File

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

View File

@ -14,8 +14,10 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/slab.h>
#include "dw_mmc.h" #include "dw_mmc.h"
#include "dw_mmc-pltfm.h" #include "dw_mmc-pltfm.h"
@ -30,16 +32,39 @@
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \
SDMMC_CLKSEL_CCLK_DIVIDER(z)) SDMMC_CLKSEL_CCLK_DIVIDER(z))
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2 #define EXYNOS4210_FIXED_CIU_CLK_DIV 2
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4
/* 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 */ /* Variations in Exynos specific dw-mshc controller */
enum dw_mci_exynos_type { enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS4210, DW_MCI_TYPE_EXYNOS4210,
DW_MCI_TYPE_EXYNOS4412, DW_MCI_TYPE_EXYNOS4412,
DW_MCI_TYPE_EXYNOS5250, DW_MCI_TYPE_EXYNOS5250,
DW_MCI_TYPE_EXYNOS5420, DW_MCI_TYPE_EXYNOS5420,
DW_MCI_TYPE_EXYNOS5420_SMU,
}; };
/* Exynos implementation specific driver private data */ /* Exynos implementation specific driver private data */
@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {
u8 ciu_div; u8 ciu_div;
u32 sdr_timing; u32 sdr_timing;
u32 ddr_timing; u32 ddr_timing;
u32 cur_speed;
}; };
static struct dw_mci_exynos_compatible { static struct dw_mci_exynos_compatible {
@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {
}, { }, {
.compatible = "samsung,exynos5420-dw-mshc", .compatible = "samsung,exynos5420-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS5420, .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) static int dw_mci_exynos_priv_init(struct dw_mci *host)
{ {
struct dw_mci_exynos_priv_data *priv; struct dw_mci_exynos_priv_data *priv = host->priv;
int idx;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
if (!priv) { mci_writel(host, MPSBEGIN0, 0);
dev_err(host->dev, "mem alloc failed for private data\n"); mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
return -ENOMEM; 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; return 0;
} }
static int dw_mci_exynos_setup_clock(struct dw_mci *host) static int dw_mci_exynos_setup_clock(struct dw_mci *host)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv; 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 || host->bus_hz = rate / (priv->ciu_div + 1);
priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420) return 0;
host->bus_hz /= (priv->ciu_div + 1); }
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV; #ifdef CONFIG_PM_SLEEP
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) static int dw_mci_exynos_suspend(struct device *dev)
host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV; {
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; 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) 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) static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv; 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); 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); 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) 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; struct device_node *np = host->dev->of_node;
u32 timing[2]; u32 timing[2];
u32 div = 0; u32 div = 0;
int idx;
int ret; int ret;
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
priv->ciu_div = div; 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, ret = of_property_read_u32_array(np,
"samsung,dw-mshc-sdr-timing", timing, 2); "samsung,dw-mshc-sdr-timing", timing, 2);
@ -152,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
return ret; return ret;
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
host->priv = priv;
return 0; 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 */ /* Common capabilities of Exynos4/Exynos5 SoC */
static unsigned long exynos_dwmmc_caps[4] = { static unsigned long exynos_dwmmc_caps[4] = {
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | 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, .prepare_command = dw_mci_exynos_prepare_command,
.set_ios = dw_mci_exynos_set_ios, .set_ios = dw_mci_exynos_set_ios,
.parse_dt = dw_mci_exynos_parse_dt, .parse_dt = dw_mci_exynos_parse_dt,
.execute_tuning = dw_mci_exynos_execute_tuning,
}; };
static const struct of_device_id dw_mci_exynos_match[] = { 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, }, .data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc", { .compatible = "samsung,exynos5420-dw-mshc",
.data = &exynos_drv_data, }, .data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
.data = &exynos_drv_data, },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); 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); 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 = { static struct platform_driver dw_mci_exynos_pltfm_driver = {
.probe = dw_mci_exynos_probe, .probe = dw_mci_exynos_probe,
.remove = __exit_p(dw_mci_pltfm_remove), .remove = __exit_p(dw_mci_pltfm_remove),
.driver = { .driver = {
.name = "dwmmc_exynos", .name = "dwmmc_exynos",
.of_match_table = dw_mci_exynos_match, .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 dw_mci *host;
struct resource *regs; struct resource *regs;
int ret;
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
if (!host) if (!host)
@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
if (IS_ERR(host->regs)) if (IS_ERR(host->regs))
return PTR_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); platform_set_drvdata(pdev, host);
return dw_mci_probe(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) 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; 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) 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; struct device_node *np = host->dev->of_node;
u32 timing[2]; u32 timing[2];
u32 div = 0; u32 div = 0;
int ret; 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); ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
if (ret) if (ret)
dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1"); 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; return ret;
priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]); priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
host->priv = priv;
return 0; return 0;
} }
@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = {
}; };
MODULE_DEVICE_TABLE(of, 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 dw_mci_drv_data *drv_data;
const struct of_device_id *match; 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), .remove = __exit_p(dw_mci_pltfm_remove),
.driver = { .driver = {
.name = "dwmmc_socfpga", .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, .pm = &dw_mci_pltfm_pmops,
}, },
}; };

View File

@ -29,6 +29,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
@ -50,6 +51,9 @@
#define DW_MCI_RECV_STATUS 2 #define DW_MCI_RECV_STATUS 2
#define DW_MCI_DMA_THRESHOLD 16 #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 #ifdef CONFIG_MMC_DW_IDMAC
#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \
SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
@ -76,42 +80,39 @@ struct idmac_desc {
}; };
#endif /* CONFIG_MMC_DW_IDMAC */ #endif /* CONFIG_MMC_DW_IDMAC */
/** static const u8 tuning_blk_pattern_4bit[] = {
* struct dw_mci_slot - MMC slot state 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
* @mmc: The mmc_host representing this slot. 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
* @host: The MMC controller this slot is using. 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
* @wp_gpio: If gpio_is_valid() we'll use this to read write protect. 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
* @ctype: Card type for this slot. 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
* @mrq: mmc_request currently being processed or waiting to be 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
* processed, or NULL when the slot is idle. 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
* @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_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) #if defined(CONFIG_DEBUG_FS)
static int dw_mci_req_show(struct seq_file *s, void *v) 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; 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; cmdr |= SDMMC_CMD_STOP;
else 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) { if (cmd->flags & MMC_RSP_PRESENT) {
/* We expect a response, so set this bit */ /* 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; 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, static void dw_mci_start_command(struct dw_mci *host,
struct mmc_command *cmd, u32 cmd_flags) 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); 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 */ /* DMA interface functions */
@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host)
if (host->using_dma) { if (host->using_dma) {
host->dma_ops->stop(host); host->dma_ops->stop(host);
host->dma_ops->cleanup(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) 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)); 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) static void dw_mci_idmac_stop_dma(struct dw_mci *host)
{ {
u32 temp; u32 temp;
@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host)
/* Stop the IDMAC running */ /* Stop the IDMAC running */
temp = mci_readl(host, BMOD); temp = mci_readl(host, BMOD);
temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB);
temp |= SDMMC_IDMAC_SWRESET;
mci_writel(host, BMOD, temp); 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->des3 = host->sg_dma;
p->des0 = IDMAC_DES0_ER; 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 */ /* Mask out interrupts - get Tx & Rx complete only */
mci_writel(host, IDSTS, IDMAC_INT_CLR); 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; 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) static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
{ {
int sg_len; 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, (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma,
sg_len); 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 */ /* Enable the DMA interface */
temp = mci_readl(host, CTRL); temp = mci_readl(host, CTRL);
temp |= SDMMC_CTRL_DMA_ENABLE; 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->sg = NULL;
host->data = data; host->data = data;
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ) {
host->dir_status = DW_MCI_RECV_STATUS; host->dir_status = DW_MCI_RECV_STATUS;
else dw_mci_ctrl_rd_thld(host, data);
} else {
host->dir_status = DW_MCI_SEND_STATUS; host->dir_status = DW_MCI_SEND_STATUS;
}
if (dw_mci_submit_data_dma(host, data)) { if (dw_mci_submit_data_dma(host, data)) {
int flags = SG_MITER_ATOMIC; 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 = mci_readl(host, CTRL);
temp &= ~SDMMC_CTRL_DMA_ENABLE; temp &= ~SDMMC_CTRL_DMA_ENABLE;
mci_writel(host, CTRL, temp); 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) static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
{ {
struct dw_mci *host = slot->host; struct dw_mci *host = slot->host;
unsigned int clock = slot->clock;
u32 div; u32 div;
u32 clk_en_a; u32 clk_en_a;
if (slot->clock != host->current_speed || force_clkinit) { if (!clock) {
div = host->bus_hz / slot->clock; mci_writel(host, CLKENA, 0);
if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) 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 * move the + 1 after the divide to prevent
* over-clocking the card. * over-clocking the card.
*/ */
div += 1; 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, if ((clock << div) != slot->__clk_old || force_clkinit)
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" dev_info(&slot->mmc->class_dev,
" div = %d)\n", slot->id, host->bus_hz, slot->clock, "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); slot->id, host->bus_hz, clock,
div ? ((host->bus_hz / div) >> 1) :
host->bus_hz, div);
/* disable clock */ /* disable clock */
mci_writel(host, CLKENA, 0); 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, mci_send_cmd(slot,
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); 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 */ /* Set the current slot bus width */
mci_writel(host, CTYPE, (slot->ctype << slot->id)); 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->pending_events = 0;
host->completed_events = 0; host->completed_events = 0;
host->cmd_status = 0;
host->data_status = 0; host->data_status = 0;
host->dir_status = 0;
data = cmd->data; data = cmd->data;
if (data) { if (data) {
@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host,
if (mrq->stop) if (mrq->stop)
host->stop_cmdr = dw_mci_prepare_command(slot->mmc, 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, 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); regs &= ~((0x1 << slot->id) << 16);
mci_writel(slot->host, UHS_REG, regs); 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
* Use mirror of ios->clock to prevent race with mmc * core ios update when finding the minimum.
* core ios update when finding the minimum. */
*/ slot->clock = ios->clock;
slot->clock = ios->clock;
}
if (drv_data && drv_data->set_ios) if (drv_data && drv_data->set_ios)
drv_data->set_ios(slot->host, 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 = { static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request, .request = dw_mci_request,
.pre_req = dw_mci_pre_req, .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_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd, .get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq, .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) 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); 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; 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 */ /* newer ip versions need a delay between retries */
if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY)
mdelay(20); 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) 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 dw_mci *host = (struct dw_mci *)priv;
struct mmc_data *data; struct mmc_data *data;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_request *mrq;
enum dw_mci_state state; enum dw_mci_state state;
enum dw_mci_state prev_state; enum dw_mci_state prev_state;
u32 status, ctrl; unsigned int err;
spin_lock(&host->lock); spin_lock(&host->lock);
state = host->state; state = host->state;
data = host->data; data = host->data;
mrq = host->mrq;
do { do {
prev_state = state; prev_state = state;
@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv)
cmd = host->cmd; cmd = host->cmd;
host->cmd = NULL; host->cmd = NULL;
set_bit(EVENT_CMD_COMPLETE, &host->completed_events); set_bit(EVENT_CMD_COMPLETE, &host->completed_events);
dw_mci_command_complete(host, cmd); err = dw_mci_command_complete(host, cmd);
if (cmd == host->mrq->sbc && !cmd->error) { if (cmd == mrq->sbc && !err) {
prev_state = state = STATE_SENDING_CMD; prev_state = state = STATE_SENDING_CMD;
__dw_mci_start_request(host, host->cur_slot, __dw_mci_start_request(host, host->cur_slot,
host->mrq->cmd); mrq->cmd);
goto unlock; goto unlock;
} }
if (!host->mrq->data || cmd->error) { if (cmd->data && err) {
dw_mci_request_end(host, host->mrq); 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; goto unlock;
} }
@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (test_and_clear_bit(EVENT_DATA_ERROR, if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) { &host->pending_events)) {
dw_mci_stop_dma(host); dw_mci_stop_dma(host);
if (data->stop) send_stop_abort(host, data);
send_stop_cmd(host, data);
state = STATE_DATA_ERROR; state = STATE_DATA_ERROR;
break; break;
} }
@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv)
host->data = NULL; host->data = NULL;
set_bit(EVENT_DATA_COMPLETE, &host->completed_events); 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 (!err) {
if (status & SDMMC_INT_DRTO) { if (!data->stop || mrq->sbc) {
data->error = -ETIMEDOUT; if (mrq->sbc)
} else if (status & SDMMC_INT_DCRC) { data->stop->error = 0;
data->error = -EILSEQ; dw_mci_request_end(host, mrq);
} else if (status & SDMMC_INT_EBE && goto unlock;
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;
} }
/*
* After an error, there may be data lingering /* stop command for open-ended transfer*/
* in the FIFO, so reset it - doing so if (data->stop)
* generates a block interrupt, hence setting send_stop_abort(host, data);
* 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;
} }
/*
* If err has non-zero,
* stop-abort command has been already issued.
*/
prev_state = state = STATE_SENDING_STOP; prev_state = state = STATE_SENDING_STOP;
if (!data->error)
send_stop_cmd(host, data);
/* fall through */ /* fall through */
case STATE_SENDING_STOP: case STATE_SENDING_STOP:
@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv)
&host->pending_events)) &host->pending_events))
break; break;
/* CMD error in data command */
if (mrq->cmd->error && mrq->data)
dw_mci_fifo_reset(host);
host->cmd = NULL; host->cmd = NULL;
dw_mci_command_complete(host, host->mrq->stop); host->data = NULL;
dw_mci_request_end(host, host->mrq);
if (mrq->stop)
dw_mci_command_complete(host, mrq->stop);
else
host->cmd_status = 0;
dw_mci_request_end(host, mrq);
goto unlock; goto unlock;
case STATE_DATA_ERROR: 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_host *mmc = slot->mmc;
struct mmc_request *mrq; struct mmc_request *mrq;
int present; int present;
u32 ctrl;
present = dw_mci_get_cd(mmc); present = dw_mci_get_cd(mmc);
while (present != slot->last_detect_state) { 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: case STATE_DATA_ERROR:
if (mrq->data->error == -EINPROGRESS) if (mrq->data->error == -EINPROGRESS)
mrq->data->error = -ENOMEDIUM; mrq->data->error = -ENOMEDIUM;
if (!mrq->stop)
break;
/* fall through */ /* fall through */
case STATE_SENDING_STOP: case STATE_SENDING_STOP:
mrq->stop->error = -ENOMEDIUM; if (mrq->stop)
mrq->stop->error = -ENOMEDIUM;
break; break;
} }
@ -1763,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work)
if (present == 0) { if (present == 0) {
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
/* /* Clear down the FIFO */
* Clear down the FIFO - doing so generates a dw_mci_fifo_reset(host);
* 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);
#ifdef CONFIG_MMC_DW_IDMAC #ifdef CONFIG_MMC_DW_IDMAC
ctrl = mci_readl(host, BMOD); dw_mci_idmac_reset(host);
/* Software reset of DMA */
ctrl |= SDMMC_IDMAC_SWRESET;
mci_writel(host, BMOD, ctrl);
#endif #endif
} }
@ -1901,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
struct dw_mci_slot *slot; struct dw_mci_slot *slot;
const struct dw_mci_drv_data *drv_data = host->drv_data; const struct dw_mci_drv_data *drv_data = host->drv_data;
int ctrl_id, ret; int ctrl_id, ret;
u32 freq[2];
u8 bus_width; u8 bus_width;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); 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); slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id);
mmc->ops = &dw_mci_ops; mmc->ops = &dw_mci_ops;
mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); if (of_property_read_u32_array(host->dev->of_node,
mmc->f_max = host->bus_hz; "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) if (host->pdata->get_ocr)
mmc->ocr_avail = host->pdata->get_ocr(id); 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; 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) { if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_segs = host->pdata->blk_settings->max_segs;
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; 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 */ /* Card initially undetected */
slot->last_detect_state = 0; 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; return 0;
err_setup_bus: err_setup_bus:
@ -2074,36 +2275,57 @@ no_dma:
return; 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 long timeout = jiffies + msecs_to_jiffies(500);
unsigned int ctrl; u32 ctrl;
mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | ctrl = mci_readl(host, CTRL);
SDMMC_CTRL_DMA_RESET)); ctrl |= reset;
mci_writel(host, CTRL, ctrl);
/* wait till resets clear */ /* wait till resets clear */
do { do {
ctrl = mci_readl(host, CTRL); ctrl = mci_readl(host, CTRL);
if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | if (!(ctrl & reset))
SDMMC_CTRL_DMA_RESET)))
return true; return true;
} while (time_before(jiffies, timeout)); } 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; 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 #ifdef CONFIG_OF
static struct dw_mci_of_quirks { static struct dw_mci_of_quirks {
char *quirk; char *quirk;
int id; int id;
} of_quirks[] = { } of_quirks[] = {
{ {
.quirk = "supports-highspeed",
.id = DW_MCI_QUIRK_HIGHSPEED,
}, {
.quirk = "broken-cd", .quirk = "broken-cd",
.id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, .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)) if (of_find_property(np, "enable-sdio-wakeup", NULL))
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; 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; return pdata;
} }
@ -2221,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host)
host->bus_hz = clk_get_rate(host->ciu_clk); 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) { if (drv_data && drv_data->setup_clock) {
ret = drv_data->setup_clock(host); ret = drv_data->setup_clock(host);
if (ret) { if (ret) {
@ -2287,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host)
} }
/* Reset all blocks */ /* Reset all blocks */
if (!mci_wait_reset(host->dev, host)) if (!dw_mci_ctrl_all_reset(host))
return -ENODEV; return -ENODEV;
host->dma_ops = host->pdata->dma_ops; 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; fifo_size = host->pdata->fifo_depth;
} }
host->fifo_depth = fifo_size; host->fifo_depth = fifo_size;
host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | host->fifoth_val =
((fifo_size/2) << 0)); SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2);
mci_writel(host, FIFOTH, host->fifoth_val); mci_writel(host, FIFOTH, host->fifoth_val);
/* disable clock to CIU */ /* disable clock to CIU */
@ -2456,23 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove);
*/ */
int dw_mci_suspend(struct dw_mci *host) 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) if (host->vmmc)
regulator_disable(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; ret = -ENODEV;
return ret; return ret;
} }
@ -2501,8 +2724,15 @@ int dw_mci_resume(struct dw_mci *host)
if (host->use_dma && host->dma_ops->init) if (host->use_dma && host->dma_ops->init)
host->dma_ops->init(host); 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); 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, RINTSTS, 0xFFFFFFFF);
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | 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_set_ios(slot->mmc, &slot->mmc->ios);
dw_mci_setup_bus(slot, true); dw_mci_setup_bus(slot, true);
} }
ret = mmc_resume_host(host->slot[i]->mmc);
if (ret < 0)
return ret;
} }
return 0; return 0;
} }

View File

@ -53,6 +53,7 @@
#define SDMMC_IDINTEN 0x090 #define SDMMC_IDINTEN 0x090
#define SDMMC_DSCADDR 0x094 #define SDMMC_DSCADDR 0x094
#define SDMMC_BUFADDR 0x098 #define SDMMC_BUFADDR 0x098
#define SDMMC_CDTHRCTL 0x100
#define SDMMC_DATA(x) (x) #define SDMMC_DATA(x) (x)
/* /*
@ -128,6 +129,10 @@
#define SDMMC_CMD_INDX(n) ((n) & 0x1F) #define SDMMC_CMD_INDX(n) ((n) & 0x1F)
/* Status register defines */ /* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) #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 */ /* Internal DMAC interrupt defines */
#define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_AI BIT(9)
#define SDMMC_IDMAC_INT_NI BIT(8) #define SDMMC_IDMAC_INT_NI BIT(8)
@ -142,6 +147,8 @@
#define SDMMC_IDMAC_SWRESET BIT(0) #define SDMMC_IDMAC_SWRESET BIT(0)
/* Version ID register define */ /* Version ID register define */
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF) #define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Card read threshold */
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
/* Register access macros */ /* Register access macros */
#define mci_readl(dev, reg) \ #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); extern int dw_mci_resume(struct dw_mci *host);
#endif #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. * dw_mci driver data - dw-mshc implementation specific driver data.
* @caps: mmc subsystem specified capabilities of the controller(s). * @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 (*prepare_command)(struct dw_mci *host, u32 *cmdr);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host); 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_ */ #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); 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)); jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
return 0; 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)); jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
mmc_resume_host(host->mmc);
return 0; return 0;
} }

View File

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

View File

@ -1416,28 +1416,10 @@ ioremap_free:
} }
#ifdef CONFIG_PM #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 static int
msmsdcc_suspend(struct platform_device *dev, pm_message_t state) msmsdcc_suspend(struct platform_device *dev, pm_message_t state)
{ {
struct mmc_host *mmc = mmc_get_drvdata(dev); struct mmc_host *mmc = mmc_get_drvdata(dev);
int rc = 0;
if (mmc) { if (mmc) {
struct msmsdcc_host *host = mmc_priv(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) if (host->stat_irq)
disable_irq(host->stat_irq); disable_irq(host->stat_irq);
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) msmsdcc_writel(host, 0, MMCIMASK0);
rc = mmc_suspend_host(mmc);
if (!rc)
msmsdcc_writel(host, 0, MMCIMASK0);
if (host->clks_on) if (host->clks_on)
msmsdcc_disable_clocks(host, 0); msmsdcc_disable_clocks(host, 0);
} }
return rc; return 0;
} }
static int static int
@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev)
msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);
if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
mmc_resume_host(mmc);
if (host->stat_irq) if (host->stat_irq)
enable_irq(host->stat_irq); enable_irq(host->stat_irq);
#if BUSCLK_PWRSAVE #if BUSCLK_PWRSAVE

View File

@ -775,9 +775,9 @@ static int mvsd_probe(struct platform_device *pdev)
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
host->base = devm_request_and_ioremap(&pdev->dev, r); host->base = devm_ioremap_resource(&pdev->dev, r);
if (!host->base) { if (IS_ERR(host->base)) {
ret = -ENOMEM; ret = PTR_ERR(host->base);
goto out; goto out;
} }
@ -838,33 +838,6 @@ static int mvsd_remove(struct platform_device *pdev)
return 0; 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[] = { static const struct of_device_id mvsdio_dt_ids[] = {
{ .compatible = "marvell,orion-sdio" }, { .compatible = "marvell,orion-sdio" },
{ /* sentinel */ } { /* sentinel */ }
@ -874,8 +847,6 @@ MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
static struct platform_driver mvsd_driver = { static struct platform_driver mvsd_driver = {
.probe = mvsd_probe, .probe = mvsd_probe,
.remove = mvsd_remove, .remove = mvsd_remove,
.suspend = mvsd_suspend,
.resume = mvsd_resume,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.of_match_table = mvsdio_dt_ids, .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 mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc); 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_per);
clk_disable_unprepare(host->clk_ipg); clk_disable_unprepare(host->clk_ipg);
return 0;
return ret;
} }
static int mxcmci_resume(struct device *dev) static int mxcmci_resume(struct device *dev)
{ {
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct mxcmci_host *host = mmc_priv(mmc); struct mxcmci_host *host = mmc_priv(mmc);
int ret = 0;
clk_prepare_enable(host->clk_per); clk_prepare_enable(host->clk_per);
clk_prepare_enable(host->clk_ipg); clk_prepare_enable(host->clk_ipg);
if (mmc) return 0;
ret = mmc_resume_host(mmc);
return ret;
} }
static const struct dev_pm_ops mxcmci_pm_ops = { 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 mmc_host *mmc = dev_get_drvdata(dev);
struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp; struct mxs_ssp *ssp = &host->ssp;
int ret = 0;
ret = mmc_suspend_host(mmc);
clk_disable_unprepare(ssp->clk); clk_disable_unprepare(ssp->clk);
return 0;
return ret;
} }
static int mxs_mmc_resume(struct device *dev) 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 mmc_host *mmc = dev_get_drvdata(dev);
struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp; struct mxs_ssp *ssp = &host->ssp;
int ret = 0;
clk_prepare_enable(ssp->clk); clk_prepare_enable(ssp->clk);
return 0;
ret = mmc_resume_host(mmc);
return ret;
} }
static const struct dev_pm_ops mxs_mmc_pm_ops = { static const struct dev_pm_ops mxs_mmc_pm_ops = {

View File

@ -128,7 +128,6 @@ struct mmc_omap_slot {
struct mmc_omap_host { struct mmc_omap_host {
int initialized; int initialized;
int suspended;
struct mmc_request * mrq; struct mmc_request * mrq;
struct mmc_command * cmd; struct mmc_command * cmd;
struct mmc_data * data; struct mmc_data * data;
@ -1513,61 +1512,9 @@ static int mmc_omap_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver mmc_omap_driver = {
.probe = mmc_omap_probe, .probe = mmc_omap_probe,
.remove = mmc_omap_remove, .remove = mmc_omap_remove,
.suspend = mmc_omap_suspend,
.resume = mmc_omap_resume,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,

View File

@ -75,6 +75,7 @@
#define ICE 0x1 #define ICE 0x1
#define ICS 0x2 #define ICS 0x2
#define CEN (1 << 2) #define CEN (1 << 2)
#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */
#define CLKD_MASK 0x0000FFC0 #define CLKD_MASK 0x0000FFC0
#define CLKD_SHIFT 6 #define CLKD_SHIFT 6
#define DTO_MASK 0x000F0000 #define DTO_MASK 0x000F0000
@ -119,7 +120,8 @@
BRR_EN | BWR_EN | TC_EN | CC_EN) BRR_EN | BWR_EN | TC_EN | CC_EN)
#define MMC_AUTOSUSPEND_DELAY 100 #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_MIN_CLOCK 400000
#define OMAP_MMC_MAX_CLOCK 52000000 #define OMAP_MMC_MAX_CLOCK 52000000
#define DRIVER_NAME "omap_hsmmc" #define DRIVER_NAME "omap_hsmmc"
@ -171,6 +173,10 @@ struct omap_hsmmc_host {
unsigned char bus_mode; unsigned char bus_mode;
unsigned char power_mode; unsigned char power_mode;
int suspended; int suspended;
u32 con;
u32 hctl;
u32 sysctl;
u32 capa;
int irq; int irq;
int use_dma, dma_ch; int use_dma, dma_ch;
struct dma_chan *tx_chan; struct dma_chan *tx_chan;
@ -183,7 +189,6 @@ struct omap_hsmmc_host {
int use_reg; int use_reg;
int req_in_progress; int req_in_progress;
struct omap_hsmmc_next next_data; struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata; 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) { if (ios->clock) {
dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock);
if (dsor > 250) if (dsor > CLKD_MAX)
dsor = 250; dsor = CLKD_MAX;
} }
return dsor; 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) static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
{ {
struct mmc_ios *ios = &host->mmc->ios; struct mmc_ios *ios = &host->mmc->ios;
struct omap_mmc_platform_data *pdata = host->pdata;
int context_loss = 0;
u32 hctl, capa; u32 hctl, capa;
unsigned long timeout; 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) if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
return 1; 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->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF && if (host->power_mode != MMC_POWER_OFF &&
(1 << ios->vdd) <= MMC_VDD_23_24) (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); omap_hsmmc_set_bus_mode(host);
out: out:
host->context_loss = context_loss; dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n",
host->context_loss);
dev_dbg(mmc_dev(host->mmc), "context is restored\n");
return 0; return 0;
} }
@ -666,15 +665,10 @@ out:
*/ */
static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) static void omap_hsmmc_context_save(struct omap_hsmmc_host *host)
{ {
struct omap_mmc_platform_data *pdata = host->pdata; host->con = OMAP_HSMMC_READ(host->base, CON);
int context_loss; host->hctl = OMAP_HSMMC_READ(host->base, HCTL);
host->sysctl = OMAP_HSMMC_READ(host->base, SYSCTL);
if (pdata->get_context_loss_count) { host->capa = OMAP_HSMMC_READ(host->base, CAPA);
context_loss = pdata->get_context_loss_count(host->dev);
if (context_loss < 0)
return;
host->context_loss = context_loss;
}
} }
#else #else
@ -975,8 +969,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host,
unsigned long bit) unsigned long bit)
{ {
unsigned long i = 0; unsigned long i = 0;
unsigned long limit = (loops_per_jiffy * unsigned long limit = MMC_TIMEOUT_US;
msecs_to_jiffies(MMC_TIMEOUT_MS));
OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) | bit); 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) { if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) {
while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit))
&& (i++ < limit)) && (i++ < limit))
cpu_relax(); udelay(1);
} }
i = 0; i = 0;
while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) &&
(i++ < limit)) (i++ < limit))
cpu_relax(); udelay(1);
if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit)
dev_err(mmc_dev(host->mmc), 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); struct omap_mmc_slot_data *slot = &mmc_slot(host);
int carddetect; int carddetect;
if (host->suspended)
return IRQ_HANDLED;
sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch");
if (slot->card_detect) 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 mmc_host *mmc = s->private;
struct omap_hsmmc_host *host = mmc_priv(mmc); struct omap_hsmmc_host *host = mmc_priv(mmc);
int context_loss = 0;
if (host->pdata->get_context_loss_count) seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n",
context_loss = host->pdata->get_context_loss_count(host->dev); mmc->index, host->context_loss);
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;
}
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
@ -1838,13 +1819,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->ops = &omap_hsmmc_ops; 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; mmc->f_min = OMAP_MMC_MIN_CLOCK;
if (pdata->max_freq > 0) if (pdata->max_freq > 0)
@ -1874,7 +1848,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host); omap_hsmmc_context_save(host);
/* This can be removed once we support PBIAS with DT */ /* 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->pbias_disable = 1;
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); 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) static int omap_hsmmc_suspend(struct device *dev)
{ {
int ret = 0;
struct omap_hsmmc_host *host = dev_get_drvdata(dev); struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host) if (!host)
return 0; return 0;
if (host && host->suspended)
return 0;
pm_runtime_get_sync(host->dev); 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)) { if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) {
omap_hsmmc_disable_irq(host); omap_hsmmc_disable_irq(host);
@ -2145,23 +2108,19 @@ static int omap_hsmmc_suspend(struct device *dev)
if (host->dbclk) if (host->dbclk)
clk_disable_unprepare(host->dbclk); clk_disable_unprepare(host->dbclk);
err:
pm_runtime_put_sync(host->dev); pm_runtime_put_sync(host->dev);
return ret; return 0;
} }
/* Routine to resume the MMC device */ /* Routine to resume the MMC device */
static int omap_hsmmc_resume(struct device *dev) static int omap_hsmmc_resume(struct device *dev)
{ {
int ret = 0;
struct omap_hsmmc_host *host = dev_get_drvdata(dev); struct omap_hsmmc_host *host = dev_get_drvdata(dev);
if (!host) if (!host)
return 0; return 0;
if (host && !host->suspended)
return 0;
pm_runtime_get_sync(host->dev); pm_runtime_get_sync(host->dev);
if (host->dbclk) if (host->dbclk)
@ -2172,16 +2131,9 @@ static int omap_hsmmc_resume(struct device *dev)
omap_hsmmc_protect_card(host); 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_mark_last_busy(host->dev);
pm_runtime_put_autosuspend(host->dev); pm_runtime_put_autosuspend(host->dev);
return 0;
return ret;
} }
#else #else

View File

@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev)
return 0; 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 = { static struct platform_driver pxamci_driver = {
.probe = pxamci_probe, .probe = pxamci_probe,
.remove = pxamci_remove, .remove = pxamci_remove,
@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pxa_mmc_dt_ids), .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_host *mmc = host->mmc;
struct mmc_card *card = mmc->card; struct mmc_card *card = mmc->card;
struct mmc_data *data = mrq->data; 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; int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
u8 cfg2, trans_mode; u8 cfg2, trans_mode;
int err; int err;
@ -1197,37 +1197,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
.execute_tuning = sdmmc_execute_tuning, .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) static void init_extra_caps(struct realtek_pci_sdmmc *host)
{ {
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
@ -1367,8 +1336,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
.probe = rtsx_pci_sdmmc_drv_probe, .probe = rtsx_pci_sdmmc_drv_probe,
.remove = rtsx_pci_sdmmc_drv_remove, .remove = rtsx_pci_sdmmc_drv_remove,
.id_table = rtsx_pci_sdmmc_ids, .id_table = rtsx_pci_sdmmc_ids,
.suspend = rtsx_pci_sdmmc_suspend,
.resume = rtsx_pci_sdmmc_resume,
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = DRV_NAME_RTSX_PCI_SDMMC, .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); 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 = { static struct platform_driver s3cmci_driver = {
.driver = { .driver = {
.name = "s3c-sdi", .name = "s3c-sdi",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = s3cmci_pm_ops,
}, },
.id_table = s3cmci_driver_ids, .id_table = s3cmci_driver_ids,
.probe = s3cmci_probe, .probe = s3cmci_probe,

View File

@ -316,19 +316,7 @@ err_pltfm_free:
static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
{ {
struct sdhci_host *host = platform_get_drvdata(pdev); return sdhci_pltfm_unregister(pdev);
int dead;
u32 scratch;
dead = 0;
scratch = readl(host->ioaddr + SDHCI_INT_STATUS);
if (scratch == (u32)-1)
dead = 1;
sdhci_remove_host(host, dead);
sdhci_free_host(host);
return 0;
} }
static struct platform_driver sdhci_bcm_kona_driver = { static struct platform_driver sdhci_bcm_kona_driver = {

View File

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

View File

@ -34,12 +34,40 @@
/* VENDOR SPEC register */ /* VENDOR SPEC register */
#define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC 0xc0
#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) #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_WTMK_LVL 0x44
#define ESDHC_MIX_CTRL 0x48 #define ESDHC_MIX_CTRL 0x48
#define ESDHC_MIX_CTRL_DDREN (1 << 3)
#define ESDHC_MIX_CTRL_AC23EN (1 << 7) #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 */ /* Bits 3 and 6 are not SDHCI standard definitions */
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 #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 * 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 * As a result, the TC flag is not asserted and SW received timeout
* exeception. Bit1 of Vendor Spec registor is used to fix it. * 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 { struct esdhc_soc_data {
IMX25_ESDHC, u32 flags;
IMX35_ESDHC, };
IMX51_ESDHC,
IMX53_ESDHC, static struct esdhc_soc_data esdhc_imx25_data = {
IMX6Q_USDHC, .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 { struct pltfm_imx_data {
int flags;
u32 scratchpad; u32 scratchpad;
enum imx_esdhc_type devtype;
struct pinctrl *pinctrl; 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 esdhc_platform_data boarddata;
struct clk *clk_ipg; struct clk *clk_ipg;
struct clk *clk_ahb; struct clk *clk_ahb;
@ -90,25 +157,20 @@ struct pltfm_imx_data {
MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
} multiblock_status; } multiblock_status;
u32 uhs_mode;
u32 is_ddr;
}; };
static struct platform_device_id imx_esdhc_devtype[] = { static struct platform_device_id imx_esdhc_devtype[] = {
{ {
.name = "sdhci-esdhc-imx25", .name = "sdhci-esdhc-imx25",
.driver_data = IMX25_ESDHC, .driver_data = (kernel_ulong_t) &esdhc_imx25_data,
}, { }, {
.name = "sdhci-esdhc-imx35", .name = "sdhci-esdhc-imx35",
.driver_data = IMX35_ESDHC, .driver_data = (kernel_ulong_t) &esdhc_imx35_data,
}, { }, {
.name = "sdhci-esdhc-imx51", .name = "sdhci-esdhc-imx51",
.driver_data = IMX51_ESDHC, .driver_data = (kernel_ulong_t) &esdhc_imx51_data,
}, {
.name = "sdhci-esdhc-imx53",
.driver_data = IMX53_ESDHC,
}, {
.name = "sdhci-usdhc-imx6q",
.driver_data = IMX6Q_USDHC,
}, { }, {
/* sentinel */ /* sentinel */
} }
@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = {
MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype); MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype);
static const struct of_device_id imx_esdhc_dt_ids[] = { static const struct of_device_id imx_esdhc_dt_ids[] = {
{ .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], }, { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, },
{ .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, },
{ .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, },
{ .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, },
{ .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], }, { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
{ /* sentinel */ } { /* sentinel */ }
}; };
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
static inline int is_imx25_esdhc(struct pltfm_imx_data *data) static inline int is_imx25_esdhc(struct pltfm_imx_data *data)
{ {
return data->devtype == IMX25_ESDHC; return data->socdata == &esdhc_imx25_data;
}
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;
} }
static inline int is_imx53_esdhc(struct pltfm_imx_data *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) 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) 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; struct pltfm_imx_data *imx_data = pltfm_host->priv;
u32 val = readl(host->ioaddr + reg); 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)) { 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 /* In FSL esdhc IC module, only bit20 is used to indicate the
* ADMA2 capability of esdhc, but this bit is messed up on * ADMA2 capability of esdhc, but this bit is messed up on
* some SOCs (e.g. on MX25, MX35 this bit is set, but they * 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 (unlikely(reg == SDHCI_INT_STATUS)) {
if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
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) && (reg == SDHCI_INT_STATUS)
&& (val & SDHCI_INT_DATA_END))) { && (val & SDHCI_INT_DATA_END))) {
u32 v; 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 sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv; struct pltfm_imx_data *imx_data = pltfm_host->priv;
u16 ret = 0;
u32 val;
if (unlikely(reg == SDHCI_HOST_VERSION)) { if (unlikely(reg == SDHCI_HOST_VERSION)) {
reg ^= 2; reg ^= 2;
if (is_imx6q_usdhc(imx_data)) { if (esdhc_is_usdhc(imx_data)) {
/* /*
* The usdhc register returns a wrong host version. * The usdhc register returns a wrong host version.
* Correct it here. * 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); 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 sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv; struct pltfm_imx_data *imx_data = pltfm_host->priv;
u32 new_val = 0;
switch (reg) { 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: 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->opcode == SD_IO_RW_EXTENDED)
&& (host->cmd->data->blocks > 1) && (host->cmd->data->blocks > 1)
&& (host->cmd->data->flags & MMC_DATA_READ)) { && (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); 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); u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
/* Swap AC23 bit */ /* Swap AC23 bit */
if (val & SDHCI_TRNS_AUTO_CMD23) { 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; val |= SDHCI_CMD_ABORTCMD;
if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) && 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; imx_data->multiblock_status = MULTIBLK_IN_PROCESS;
if (is_imx6q_usdhc(imx_data)) if (esdhc_is_usdhc(imx_data))
writel(val << 16, writel(val << 16,
host->ioaddr + SDHCI_TRANSFER_MODE); host->ioaddr + SDHCI_TRANSFER_MODE);
else 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. * The reset on usdhc fails to clear MIX_CTRL register.
* Do it manually here. * Do it manually here.
*/ */
if (is_imx6q_usdhc(imx_data)) if (esdhc_is_usdhc(imx_data)) {
writel(0, host->ioaddr + ESDHC_MIX_CTRL); 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) unsigned int clock)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 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) 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; 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_l = esdhc_readl_le,
.read_w = esdhc_readw_le, .read_w = esdhc_readw_le,
.write_l = esdhc_writel_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_min_clock = esdhc_pltfm_get_min_clock,
.get_ro = esdhc_pltfm_get_ro, .get_ro = esdhc_pltfm_get_ro,
.platform_bus_width = esdhc_pltfm_bus_width, .platform_bus_width = esdhc_pltfm_bus_width,
.set_uhs_signaling = esdhc_set_uhs_signaling,
}; };
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { 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); 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; return 0;
} }
#else #else
@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
goto free_sdhci; goto free_sdhci;
} }
if (of_id) imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *)
pdev->id_entry = of_id->data; pdev->id_entry->driver_data;
imx_data->devtype = pdev->id_entry->driver_data;
pltfm_host->priv = imx_data; pltfm_host->priv = imx_data;
imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 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_ipg);
clk_prepare_enable(imx_data->clk_ahb); 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)) { if (IS_ERR(imx_data->pinctrl)) {
err = PTR_ERR(imx_data->pinctrl); err = PTR_ERR(imx_data->pinctrl);
goto disable_clk; 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; 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 */ /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA; | 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 * The imx6q ROM code will change the default watermark level setting
* to something insane. Change it back here. * 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); 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; boarddata = &imx_data->boarddata;
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
if (!host->mmc->parent->platform_data) { if (!host->mmc->parent->platform_data) {
@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
break; 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); err = sdhci_add_host(host);
if (err) if (err)
goto disable_clk; goto disable_clk;

View File

@ -49,41 +49,4 @@
#define ESDHC_HOST_CONTROL_RES 0x05 #define ESDHC_HOST_CONTROL_RES 0x05
static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock,
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 */ #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) 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 */ /* Workaround to reduce the clock frequency for p1010 esdhc */
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
if (clock > 20000000) if (clock > 20000000)
@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
clock -= 5000000; clock -= 5000000;
} }
/* Set the clock */ temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
esdhc_set_clock(host, clock, host->max_clk); 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 #ifdef CONFIG_PM

View File

@ -37,6 +37,12 @@
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 #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 * PCI registers
@ -356,6 +362,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
.allow_runtime_pm = true, .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 */ /* O2Micro extra registers */
#define O2_SD_LOCK_WP 0xD3 #define O2_SD_LOCK_WP 0xD3
#define O2_SD_MULTI_VCC3V 0xEE #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, .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, .vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_8120, .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_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 void sdhci_finish_command(struct sdhci_host *);
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
static void sdhci_tuning_timer(unsigned long data); 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); 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; int flags;
u32 mask; 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); 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) 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 && 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_enable_preset_value(host, false);
sdhci_set_clock(host, ios->clock); sdhci_set_clock(host, ios->clock);
@ -1875,6 +1876,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
return 0; 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); 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) { if (!tuning_loop_counter || !timeout) {
ctrl &= ~SDHCI_CTRL_TUNED_CLK; ctrl &= ~SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
err = -EIO;
} else { } else {
if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
pr_info(DRIVER_NAME ": Tuning procedure" pr_info(DRIVER_NAME ": Tuning procedure"
@ -2491,6 +2501,14 @@ again:
result = IRQ_HANDLED; result = IRQ_HANDLED;
intmask = sdhci_readl(host, SDHCI_INT_STATUS); 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) if (intmask && --max_loops)
goto again; goto again;
out: out:
@ -2546,8 +2564,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host) int sdhci_suspend_host(struct sdhci_host *host)
{ {
int ret;
if (host->ops->platform_suspend) if (host->ops->platform_suspend)
host->ops->platform_suspend(host); host->ops->platform_suspend(host);
@ -2559,19 +2575,6 @@ int sdhci_suspend_host(struct sdhci_host *host)
host->flags &= ~SDHCI_NEEDS_RETUNING; 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))) { if (!device_may_wakeup(mmc_dev(host->mmc))) {
sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
free_irq(host->irq, host); free_irq(host->irq, host);
@ -2579,14 +2582,14 @@ int sdhci_suspend_host(struct sdhci_host *host)
sdhci_enable_irq_wakeups(host); sdhci_enable_irq_wakeups(host);
enable_irq_wake(host->irq); enable_irq_wake(host->irq);
} }
return ret; return 0;
} }
EXPORT_SYMBOL_GPL(sdhci_suspend_host); EXPORT_SYMBOL_GPL(sdhci_suspend_host);
int sdhci_resume_host(struct sdhci_host *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->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) if (host->ops->enable_dma)
@ -2615,7 +2618,6 @@ int sdhci_resume_host(struct sdhci_host *host)
mmiowb(); mmiowb();
} }
ret = mmc_resume_host(host->mmc);
sdhci_enable_card_detection(host); sdhci_enable_card_detection(host);
if (host->ops->platform_resume) if (host->ops->platform_resume)

View File

@ -288,6 +288,7 @@ struct sdhci_ops {
unsigned int (*get_ro)(struct sdhci_host *host); unsigned int (*get_ro)(struct sdhci_host *host);
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(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); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host); void (*hw_reset)(struct sdhci_host *host);
void (*platform_suspend)(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 void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(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_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
struct mmc_command *cmd);
#ifdef CONFIG_PM #ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host); 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 #ifdef CONFIG_PM
static int sdricoh_pcmcia_suspend(struct pcmcia_device *link) static int sdricoh_pcmcia_suspend(struct pcmcia_device *link)
{ {
struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "suspend\n"); dev_dbg(&link->dev, "suspend\n");
mmc_suspend_host(mmc);
return 0; return 0;
} }
@ -527,7 +525,6 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link)
struct mmc_host *mmc = link->priv; struct mmc_host *mmc = link->priv;
dev_dbg(&link->dev, "resume\n"); dev_dbg(&link->dev, "resume\n");
sdricoh_reset(mmc_priv(mmc)); sdricoh_reset(mmc_priv(mmc));
mmc_resume_host(mmc);
return 0; return 0;
} }
#else #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) 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) { if (!ret) {
host->clk = clk_get_rate(host->hclk); 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) { if (host->power) {
pm_runtime_put_sync(&host->pd->dev); pm_runtime_put_sync(&host->pd->dev);
clk_disable(host->hclk); clk_disable_unprepare(host->hclk);
host->power = false; host->power = false;
if (ios->power_mode == MMC_POWER_OFF) if (ios->power_mode == MMC_POWER_OFF)
sh_mmcif_set_power(host, ios); sh_mmcif_set_power(host, ios);
@ -1466,7 +1466,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
mutex_init(&host->thread_lock); mutex_init(&host->thread_lock);
clk_disable(host->hclk); clk_disable_unprepare(host->hclk);
ret = mmc_add_host(mmc); ret = mmc_add_host(mmc);
if (ret < 0) if (ret < 0)
goto emmcaddh; goto emmcaddh;
@ -1487,7 +1487,7 @@ ereqirq1:
ereqirq0: ereqirq0:
pm_runtime_suspend(&pdev->dev); pm_runtime_suspend(&pdev->dev);
eresume: eresume:
clk_disable(host->hclk); clk_disable_unprepare(host->hclk);
eclkupdate: eclkupdate:
clk_put(host->hclk); clk_put(host->hclk);
eclkget: eclkget:
@ -1505,7 +1505,7 @@ static int sh_mmcif_remove(struct platform_device *pdev)
int irq[2]; int irq[2];
host->dying = true; host->dying = true;
clk_enable(host->hclk); clk_prepare_enable(host->hclk);
pm_runtime_get_sync(&pdev->dev); pm_runtime_get_sync(&pdev->dev);
dev_pm_qos_hide_latency_limit(&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) if (irq[1] >= 0)
free_irq(irq[1], host); free_irq(irq[1], host);
clk_disable(host->hclk); clk_disable_unprepare(host->hclk);
mmc_free_host(host->mmc); mmc_free_host(host->mmc);
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
@ -1538,28 +1538,21 @@ static int sh_mmcif_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int sh_mmcif_suspend(struct device *dev) static int sh_mmcif_suspend(struct device *dev)
{ {
struct sh_mmcif_host *host = dev_get_drvdata(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) static int sh_mmcif_resume(struct device *dev)
{ {
struct sh_mmcif_host *host = dev_get_drvdata(dev); return 0;
return mmc_resume_host(host->mmc);
} }
#else #endif
#define sh_mmcif_suspend NULL
#define sh_mmcif_resume NULL
#endif /* CONFIG_PM */
static const struct of_device_id mmcif_of_match[] = { static const struct of_device_id mmcif_of_match[] = {
{ .compatible = "renesas,sh-mmcif" }, { .compatible = "renesas,sh-mmcif" },
@ -1568,8 +1561,7 @@ static const struct of_device_id mmcif_of_match[] = {
MODULE_DEVICE_TABLE(of, mmcif_of_match); MODULE_DEVICE_TABLE(of, mmcif_of_match);
static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { static const struct dev_pm_ops sh_mmcif_dev_pm_ops = {
.suspend = sh_mmcif_suspend, SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume)
.resume = sh_mmcif_resume,
}; };
static struct platform_driver sh_mmcif_driver = { 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) 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) static int tifm_sd_resume(struct tifm_dev *sock)
@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock)
if (rc) if (rc)
host->eject = 1; host->eject = 1;
else
rc = mmc_resume_host(mmc);
return rc; 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 mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); 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);
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); return 0;
return ret;
} }
EXPORT_SYMBOL(tmio_mmc_host_suspend); 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 */ /* The MMC core will perform the complete set up */
host->resuming = true; host->resuming = true;
return mmc_resume_host(mmc); return 0;
} }
EXPORT_SYMBOL(tmio_mmc_host_resume); 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) static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state)
{ {
struct via_crdr_mmc_host *host; struct via_crdr_mmc_host *host;
int ret = 0;
host = pci_get_drvdata(pcidev); host = pci_get_drvdata(pcidev);
via_save_pcictrlreg(host); via_save_pcictrlreg(host);
via_save_sdcreg(host); via_save_sdcreg(host);
ret = mmc_suspend_host(host->mmc);
pci_save_state(pcidev); pci_save_state(pcidev);
pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0);
pci_disable_device(pcidev); pci_disable_device(pcidev);
pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); pci_set_power_state(pcidev, pci_choose_state(pcidev, state));
return ret; return 0;
} }
static int via_sd_resume(struct pci_dev *pcidev) 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_restore_pcictrlreg(sdhost);
via_init_sdc_pm(sdhost); via_init_sdc_pm(sdhost);
ret = mmc_resume_host(sdhost->mmc);
return ret; return ret;
} }

View File

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

View File

@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev)
#ifdef CONFIG_PM #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, static int wbsd_platform_suspend(struct platform_device *dev,
pm_message_t state) pm_message_t state)
{ {
struct mmc_host *mmc = platform_get_drvdata(dev); struct mmc_host *mmc = platform_get_drvdata(dev);
struct wbsd_host *host; struct wbsd_host *host;
int ret;
if (mmc == NULL) if (mmc == NULL)
return 0; return 0;
@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev,
host = mmc_priv(mmc); host = mmc_priv(mmc);
ret = wbsd_suspend(host, state);
if (ret)
return ret;
wbsd_chip_poweroff(host); wbsd_chip_poweroff(host);
return 0; return 0;
} }
@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev)
*/ */
mdelay(5); mdelay(5);
return wbsd_resume(host); wbsd_init_device(host);
return 0;
} }
#ifdef CONFIG_PNP #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) 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 mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev);
struct wbsd_host *host;
if (mmc == NULL) if (mmc == NULL)
return 0; return 0;
DBGF("Suspending...\n"); DBGF("Suspending...\n");
return 0;
host = mmc_priv(mmc);
return wbsd_suspend(host, state);
} }
static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) 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); mdelay(5);
return wbsd_resume(host); wbsd_init_device(host);
return 0;
} }
#endif /* CONFIG_PNP */ #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) static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
{ {
u32 reg_tmp; u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
if (enable) {
if (priv->power_inverted) { if (enable ^ priv->power_inverted)
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); reg_tmp &= ~BM_SD_OFF;
writeb(reg_tmp | BM_SD_OFF, else
priv->sdmmc_base + SDMMC_BUSMODE); reg_tmp |= BM_SD_OFF;
} else {
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); writeb(reg_tmp, 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);
}
}
} }
static void wmt_mci_read_response(struct mmc_host *mmc) 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 platform_device *pdev = to_platform_device(dev);
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
struct wmt_mci_priv *priv; struct wmt_mci_priv *priv;
int ret;
if (!mmc) if (!mmc)
return 0; return 0;
priv = mmc_priv(mmc); 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 = readw(priv->sdmmc_base + SDMMC_BLKLEN);
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
SDMMC_BUSMODE);
reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); clk_disable(priv->clk_sdmmc);
writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); return 0;
clk_disable(priv->clk_sdmmc);
}
return ret;
} }
static int wmt_mci_resume(struct device *dev) 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 platform_device *pdev = to_platform_device(dev);
struct mmc_host *mmc = platform_get_drvdata(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev);
struct wmt_mci_priv *priv; struct wmt_mci_priv *priv;
int ret = 0;
if (mmc) { if (mmc) {
priv = mmc_priv(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 + writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +
SDMMC_INTMASK0); SDMMC_INTMASK0);
ret = mmc_resume_host(mmc);
} }
return ret; return 0;
} }
static const struct dev_pm_ops wmt_mci_pm = { static const struct dev_pm_ops wmt_mci_pm = {

View File

@ -240,6 +240,7 @@ struct mmc_part {
struct mmc_card { struct mmc_card {
struct mmc_host *host; /* the host this device belongs to */ struct mmc_host *host; /* the host this device belongs to */
struct device dev; /* the device */ struct device dev; /* the device */
u32 ocr; /* the current OCR setting */
unsigned int rca; /* relative card address of device */ unsigned int rca; /* relative card address of device */
unsigned int type; /* card type */ unsigned int type; /* card type */
#define MMC_TYPE_MMC 0 /* MMC card */ #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_CARD_REMOVED (1<<7) /* card has been removed */
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ #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_DOING_BKOPS (1<<10) /* card is doing BKOPS */
#define MMC_STATE_SUSPENDED (1<<11) /* card is suspended */
unsigned int quirks; /* card quirks */ 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_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 */ #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_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #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_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_present(c) ((c)->state |= MMC_STATE_PRESENT)
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #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_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) #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_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_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) #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_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_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. * 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 *, extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int); struct mmc_command *, int);
extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); 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_switch(struct mmc_card *, u8, u8, u8, unsigned int);
extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); 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 int __mmc_claim_host(struct mmc_host *host, atomic_t *abort);
extern void mmc_release_host(struct mmc_host *host); extern void mmc_release_host(struct mmc_host *host);
extern int mmc_try_claim_host(struct mmc_host *host);
extern void mmc_get_card(struct mmc_card *card); extern void mmc_get_card(struct mmc_card *card);
extern void mmc_put_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 #define LINUX_MMC_DW_MMC_H
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/mmc/core.h>
#define MAX_MCI_SLOTS 2 #define MAX_MCI_SLOTS 2
@ -129,6 +130,9 @@ struct dw_mci {
struct mmc_request *mrq; struct mmc_request *mrq;
struct mmc_command *cmd; struct mmc_command *cmd;
struct mmc_data *data; struct mmc_data *data;
struct mmc_command stop_abort;
unsigned int prev_blksz;
unsigned char timing;
struct workqueue_struct *card_workqueue; struct workqueue_struct *card_workqueue;
/* DMA interface members*/ /* 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_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */
#define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 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_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_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_C (1 << 24) /* Host supports Driver Type C */
#define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ #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 */ spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios; /* current io bus settings */ struct mmc_ios ios; /* current io bus settings */
u32 ocr; /* the current OCR setting */
/* group bitfields together to minimize padding */ /* group bitfields together to minimize padding */
unsigned int use_spi_crc:1; 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_classdev(x) (&(x)->class_dev)
#define mmc_hostname(x) (dev_name(&(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_save_host(struct mmc_host *host);
int mmc_power_restore_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 #ifndef __ASM_ARCH_IMX_ESDHC_H
#define __ASM_ARCH_IMX_ESDHC_H #define __ASM_ARCH_IMX_ESDHC_H
#include <linux/types.h>
enum wp_types { enum wp_types {
ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ ESDHC_WP_NONE, /* no WP, neither controller nor gpio */
ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ ESDHC_WP_CONTROLLER, /* mmc controller internal WP */
@ -32,6 +34,7 @@ enum cd_types {
* @cd_gpio: gpio for card_detect interrupt * @cd_gpio: gpio for card_detect interrupt
* @wp_type: type of write_protect method (see wp_types enum above) * @wp_type: type of write_protect method (see wp_types enum above)
* @cd_type: type of card_detect method (see cd_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 { struct esdhc_platform_data {
@ -41,5 +44,7 @@ struct esdhc_platform_data {
enum cd_types cd_type; enum cd_types cd_type;
int max_bus_width; int max_bus_width;
unsigned int f_max; unsigned int f_max;
bool support_vsel;
unsigned int delay_line;
}; };
#endif /* __ASM_ARCH_IMX_ESDHC_H */ #endif /* __ASM_ARCH_IMX_ESDHC_H */