You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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:
@@ -12,6 +12,11 @@ Required properties:
|
||||
Optional properties:
|
||||
- fsl,cd-controller : Indicate to use controller internal card detection
|
||||
- fsl,wp-controller : Indicate to use controller internal write protection
|
||||
- fsl,delay-line : Specify the number of delay cells for override mode.
|
||||
This is used to set the clock delay for DLL(Delay Line) on override mode
|
||||
to select a proper data sampling window in case the clock quality is not good
|
||||
due to signal path is too long on the board. Please refer to eSDHC/uSDHC
|
||||
chapter, DLL (Delay Line) section in RM for details.
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ Optional properties:
|
||||
is specified and the ciu clock is specified then we'll try to set the ciu
|
||||
clock to this at probe time.
|
||||
|
||||
* clock-freq-min-max: Minimum and Maximum clock frequency for card output
|
||||
clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default.
|
||||
|
||||
* num-slots: specifies the number of slots supported by the controller.
|
||||
The number of physical slots actually used could be equal or less than the
|
||||
value specified by num-slots. If this property is not specified, the value
|
||||
@@ -66,6 +69,10 @@ Optional properties:
|
||||
|
||||
* supports-highspeed: Enables support for high speed cards (up to 50MHz)
|
||||
|
||||
* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode
|
||||
|
||||
* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode
|
||||
|
||||
* broken-cd: as documented in mmc core bindings.
|
||||
|
||||
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
|
||||
@@ -93,8 +100,10 @@ board specific portions as listed below.
|
||||
|
||||
dwmmc0@12200000 {
|
||||
clock-frequency = <400000000>;
|
||||
clock-freq-min-max = <400000 200000000>;
|
||||
num-slots = <1>;
|
||||
supports-highspeed;
|
||||
caps2-mmc-hs200-1_8v;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
|
||||
@@ -351,7 +351,6 @@ CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_CLKGATE=y
|
||||
CONFIG_MMC_AU1X=y
|
||||
CONFIG_NEW_LEDS=y
|
||||
CONFIG_LEDS_CLASS=y
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/mmc/sh_mmcif.h>
|
||||
#include <linux/mmc/sh_mobile_sdhi.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
@@ -2448,7 +2448,6 @@ static int _mmc_blk_suspend(struct mmc_card *card)
|
||||
struct mmc_blk_data *md = mmc_get_drvdata(card);
|
||||
|
||||
if (md) {
|
||||
pm_runtime_get_sync(&card->dev);
|
||||
mmc_queue_suspend(&md->queue);
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_suspend(&part_md->queue);
|
||||
@@ -2483,7 +2482,6 @@ static int mmc_blk_resume(struct mmc_card *card)
|
||||
list_for_each_entry(part_md, &md->part, part) {
|
||||
mmc_queue_resume(&part_md->queue);
|
||||
}
|
||||
pm_runtime_put(&card->dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -342,7 +342,7 @@ int mmc_add_card(struct mmc_card *card)
|
||||
break;
|
||||
}
|
||||
|
||||
if (mmc_sd_card_uhs(card) &&
|
||||
if (mmc_card_uhs(card) &&
|
||||
(card->sd_bus_speed < ARRAY_SIZE(uhs_speeds)))
|
||||
uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed];
|
||||
|
||||
|
||||
+58
-96
@@ -23,6 +23,7 @@
|
||||
#include <linux/log2.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/fault-inject.h>
|
||||
#include <linux/random.h>
|
||||
@@ -301,7 +302,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
|
||||
}
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal);
|
||||
EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
|
||||
if (err) {
|
||||
pr_warn("%s: Error %d starting bkops\n",
|
||||
mmc_hostname(card->host), err);
|
||||
@@ -917,31 +918,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort)
|
||||
|
||||
EXPORT_SYMBOL(__mmc_claim_host);
|
||||
|
||||
/**
|
||||
* mmc_try_claim_host - try exclusively to claim a host
|
||||
* @host: mmc host to claim
|
||||
*
|
||||
* Returns %1 if the host is claimed, %0 otherwise.
|
||||
*/
|
||||
int mmc_try_claim_host(struct mmc_host *host)
|
||||
{
|
||||
int claimed_host = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (!host->claimed || host->claimer == current) {
|
||||
host->claimed = 1;
|
||||
host->claimer = current;
|
||||
host->claim_cnt += 1;
|
||||
claimed_host = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
if (host->ops->enable && claimed_host && host->claim_cnt == 1)
|
||||
host->ops->enable(host);
|
||||
return claimed_host;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_try_claim_host);
|
||||
|
||||
/**
|
||||
* mmc_release_host - release a host
|
||||
* @host: mmc host to release
|
||||
@@ -1382,22 +1358,31 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
int bit;
|
||||
|
||||
/*
|
||||
* Sanity check the voltages that the card claims to
|
||||
* support.
|
||||
*/
|
||||
if (ocr & 0x7F) {
|
||||
dev_warn(mmc_dev(host),
|
||||
"card claims to support voltages below defined range\n");
|
||||
ocr &= ~0x7F;
|
||||
}
|
||||
|
||||
ocr &= host->ocr_avail;
|
||||
if (!ocr) {
|
||||
dev_warn(mmc_dev(host), "no support for card's volts\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bit = ffs(ocr);
|
||||
if (bit) {
|
||||
bit -= 1;
|
||||
|
||||
if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) {
|
||||
bit = ffs(ocr) - 1;
|
||||
ocr &= 3 << bit;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
host->ios.vdd = bit;
|
||||
mmc_set_ios(host);
|
||||
mmc_host_clk_release(host);
|
||||
mmc_power_cycle(host, ocr);
|
||||
} else {
|
||||
pr_warning("%s: host doesn't support card's voltages\n",
|
||||
mmc_hostname(host));
|
||||
ocr = 0;
|
||||
bit = fls(ocr) - 1;
|
||||
ocr &= 3 << bit;
|
||||
if (bit != host->ios.vdd)
|
||||
dev_warn(mmc_dev(host), "exceeding card's volts\n");
|
||||
}
|
||||
|
||||
return ocr;
|
||||
@@ -1422,7 +1407,7 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||
|
||||
}
|
||||
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage)
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
int err = 0;
|
||||
@@ -1504,7 +1489,7 @@ power_cycle:
|
||||
if (err) {
|
||||
pr_debug("%s: Signal voltage switch failed, "
|
||||
"power cycling card\n", mmc_hostname(host));
|
||||
mmc_power_cycle(host);
|
||||
mmc_power_cycle(host, ocr);
|
||||
}
|
||||
|
||||
mmc_host_clk_release(host);
|
||||
@@ -1545,22 +1530,14 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type)
|
||||
* If a host does all the power sequencing itself, ignore the
|
||||
* initial MMC_POWER_UP stage.
|
||||
*/
|
||||
void mmc_power_up(struct mmc_host *host)
|
||||
void mmc_power_up(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (host->ios.power_mode == MMC_POWER_ON)
|
||||
return;
|
||||
|
||||
mmc_host_clk_hold(host);
|
||||
|
||||
/* If ocr is set, we use it */
|
||||
if (host->ocr)
|
||||
bit = ffs(host->ocr) - 1;
|
||||
else
|
||||
bit = fls(host->ocr_avail) - 1;
|
||||
|
||||
host->ios.vdd = bit;
|
||||
host->ios.vdd = fls(ocr) - 1;
|
||||
if (mmc_host_is_spi(host))
|
||||
host->ios.chip_select = MMC_CS_HIGH;
|
||||
else
|
||||
@@ -1604,13 +1581,6 @@ void mmc_power_off(struct mmc_host *host)
|
||||
host->ios.clock = 0;
|
||||
host->ios.vdd = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Reset ocr mask to be the highest possible voltage supported for
|
||||
* this mmc host. This value will be used at next power up.
|
||||
*/
|
||||
host->ocr = 1 << (fls(host->ocr_avail) - 1);
|
||||
|
||||
if (!mmc_host_is_spi(host)) {
|
||||
host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
|
||||
host->ios.chip_select = MMC_CS_DONTCARE;
|
||||
@@ -1630,12 +1600,12 @@ void mmc_power_off(struct mmc_host *host)
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
|
||||
void mmc_power_cycle(struct mmc_host *host)
|
||||
void mmc_power_cycle(struct mmc_host *host, u32 ocr)
|
||||
{
|
||||
mmc_power_off(host);
|
||||
/* Wait at least 1 ms according to SD spec */
|
||||
mmc_delay(1);
|
||||
mmc_power_up(host);
|
||||
mmc_power_up(host, ocr);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1723,6 +1693,28 @@ void mmc_detach_bus(struct mmc_host *host)
|
||||
mmc_bus_put(host);
|
||||
}
|
||||
|
||||
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
|
||||
bool cd_irq)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
WARN_ON(host->removed);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the device is configured as wakeup, we prevent a new sleep for
|
||||
* 5 s to give provision for user space to consume the event.
|
||||
*/
|
||||
if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) &&
|
||||
device_can_wakeup(mmc_dev(host)))
|
||||
pm_wakeup_event(mmc_dev(host), 5000);
|
||||
|
||||
host->detect_change = 1;
|
||||
mmc_schedule_delayed_work(&host->detect, delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_detect_change - process change of state on a MMC socket
|
||||
* @host: host which changed state.
|
||||
@@ -1735,16 +1727,8 @@ void mmc_detach_bus(struct mmc_host *host)
|
||||
*/
|
||||
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
|
||||
{
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
WARN_ON(host->removed);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
#endif
|
||||
host->detect_change = 1;
|
||||
mmc_schedule_delayed_work(&host->detect, delay);
|
||||
_mmc_detect_change(host, delay, true);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_detect_change);
|
||||
|
||||
void mmc_init_erase(struct mmc_card *card)
|
||||
@@ -2334,7 +2318,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
|
||||
pr_info("%s: %s: trying to init card at %u Hz\n",
|
||||
mmc_hostname(host), __func__, host->f_init);
|
||||
#endif
|
||||
mmc_power_up(host);
|
||||
mmc_power_up(host, host->ocr_avail);
|
||||
|
||||
/*
|
||||
* Some eMMCs (with VCCQ always on) may not be reset after power up, so
|
||||
@@ -2423,7 +2407,7 @@ int mmc_detect_card_removed(struct mmc_host *host)
|
||||
* rescan handle the card removal.
|
||||
*/
|
||||
cancel_delayed_work(&host->detect);
|
||||
mmc_detect_change(host, 0);
|
||||
_mmc_detect_change(host, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2504,8 +2488,8 @@ void mmc_start_host(struct mmc_host *host)
|
||||
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
|
||||
mmc_power_off(host);
|
||||
else
|
||||
mmc_power_up(host);
|
||||
mmc_detect_change(host, 0);
|
||||
mmc_power_up(host, host->ocr_avail);
|
||||
_mmc_detect_change(host, 0, false);
|
||||
}
|
||||
|
||||
void mmc_stop_host(struct mmc_host *host)
|
||||
@@ -2583,7 +2567,7 @@ int mmc_power_restore_host(struct mmc_host *host)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mmc_power_up(host);
|
||||
mmc_power_up(host, host->card->ocr);
|
||||
ret = host->bus_ops->power_restore(host);
|
||||
|
||||
mmc_bus_put(host);
|
||||
@@ -2657,28 +2641,6 @@ EXPORT_SYMBOL(mmc_cache_ctrl);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* mmc_suspend_host - suspend a host
|
||||
* @host: mmc host
|
||||
*/
|
||||
int mmc_suspend_host(struct mmc_host *host)
|
||||
{
|
||||
/* This function is deprecated */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_suspend_host);
|
||||
|
||||
/**
|
||||
* mmc_resume_host - resume a previously suspended host
|
||||
* @host: mmc host
|
||||
*/
|
||||
int mmc_resume_host(struct mmc_host *host)
|
||||
{
|
||||
/* This function is deprecated */
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_resume_host);
|
||||
|
||||
/* Do the card removal on suspend if card is assumed removeable
|
||||
* Do that in pm notifier while userspace isn't yet frozen, so we will be able
|
||||
to sync the card.
|
||||
@@ -2724,7 +2686,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
host->rescan_disable = 0;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mmc_detect_change(host, 0);
|
||||
_mmc_detect_change(host, 0, false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -42,13 +42,13 @@ void mmc_set_ungated(struct mmc_host *host);
|
||||
void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode);
|
||||
void mmc_set_bus_width(struct mmc_host *host, unsigned int width);
|
||||
u32 mmc_select_voltage(struct mmc_host *host, u32 ocr);
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr);
|
||||
int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage);
|
||||
void mmc_set_timing(struct mmc_host *host, unsigned int timing);
|
||||
void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type);
|
||||
void mmc_power_up(struct mmc_host *host);
|
||||
void mmc_power_up(struct mmc_host *host, u32 ocr);
|
||||
void mmc_power_off(struct mmc_host *host);
|
||||
void mmc_power_cycle(struct mmc_host *host);
|
||||
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
||||
|
||||
static inline void mmc_delay(unsigned int ms)
|
||||
{
|
||||
|
||||
+81
-56
@@ -13,6 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
@@ -934,6 +935,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||
goto err;
|
||||
}
|
||||
|
||||
card->ocr = ocr;
|
||||
card->type = MMC_TYPE_MMC;
|
||||
card->rca = 1;
|
||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||
@@ -1404,9 +1406,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
|
||||
if (notify_type == EXT_CSD_POWER_OFF_LONG)
|
||||
timeout = card->ext_csd.power_off_longtime;
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||
notify_type, timeout);
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_POWER_OFF_NOTIFICATION,
|
||||
notify_type, timeout, true, false);
|
||||
if (err)
|
||||
pr_err("%s: Power Off Notification timed out, %u\n",
|
||||
mmc_hostname(card->host), timeout);
|
||||
@@ -1477,6 +1479,9 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
if (mmc_card_suspended(host->card))
|
||||
goto out;
|
||||
|
||||
if (mmc_card_doing_bkops(host->card)) {
|
||||
err = mmc_stop_bkops(host->card);
|
||||
if (err)
|
||||
@@ -1496,19 +1501,54 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
|
||||
if (!err)
|
||||
if (!err) {
|
||||
mmc_power_off(host);
|
||||
mmc_card_set_suspended(host->card);
|
||||
}
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback from host.
|
||||
* Suspend callback
|
||||
*/
|
||||
static int mmc_suspend(struct mmc_host *host)
|
||||
{
|
||||
return _mmc_suspend(host, true);
|
||||
int err;
|
||||
|
||||
err = _mmc_suspend(host, true);
|
||||
if (!err) {
|
||||
pm_runtime_disable(&host->card->dev);
|
||||
pm_runtime_set_suspended(&host->card->dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function tries to determine if the same card is still present
|
||||
* and, if so, restore all state to it.
|
||||
*/
|
||||
static int _mmc_resume(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
if (!mmc_card_suspended(host->card))
|
||||
goto out;
|
||||
|
||||
mmc_power_up(host, host->card->ocr);
|
||||
err = mmc_init_card(host, host->card->ocr, host->card);
|
||||
mmc_card_clr_suspended(host->card);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1516,31 +1556,38 @@ static int mmc_suspend(struct mmc_host *host)
|
||||
*/
|
||||
static int mmc_shutdown(struct mmc_host *host)
|
||||
{
|
||||
return _mmc_suspend(host, false);
|
||||
}
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* Resume callback from host.
|
||||
*
|
||||
* This function tries to determine if the same card is still present
|
||||
* and, if so, restore all state to it.
|
||||
*/
|
||||
static int mmc_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
/*
|
||||
* In a specific case for poweroff notify, we need to resume the card
|
||||
* before we can shutdown it properly.
|
||||
*/
|
||||
if (mmc_can_poweroff_notify(host->card) &&
|
||||
!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
|
||||
err = _mmc_resume(host);
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
err = mmc_init_card(host, host->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
if (!err)
|
||||
err = _mmc_suspend(host, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for resume.
|
||||
*/
|
||||
static int mmc_resume(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
|
||||
err = _mmc_resume(host);
|
||||
pm_runtime_set_active(&host->card->dev);
|
||||
pm_runtime_mark_last_busy(&host->card->dev);
|
||||
}
|
||||
pm_runtime_enable(&host->card->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for runtime_suspend.
|
||||
@@ -1552,18 +1599,11 @@ static int mmc_runtime_suspend(struct mmc_host *host)
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
err = mmc_suspend(host);
|
||||
if (err) {
|
||||
err = _mmc_suspend(host, true);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive suspend\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
mmc_power_off(host);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1574,18 +1614,14 @@ static int mmc_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
err = mmc_resume(host);
|
||||
err = _mmc_resume(host);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive resume\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
mmc_release_host(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1595,7 +1631,7 @@ static int mmc_power_restore(struct mmc_host *host)
|
||||
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_init_card(host, host->card->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
@@ -1640,7 +1676,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host)
|
||||
int mmc_attach_mmc(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
u32 ocr;
|
||||
u32 ocr, rocr;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
@@ -1666,23 +1702,12 @@ int mmc_attach_mmc(struct mmc_host *host)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check the voltages that the card claims to
|
||||
* support.
|
||||
*/
|
||||
if (ocr & 0x7F) {
|
||||
pr_warning("%s: card claims to support voltages "
|
||||
"below the defined range. These will be ignored.\n",
|
||||
mmc_hostname(host));
|
||||
ocr &= ~0x7F;
|
||||
}
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr);
|
||||
rocr = mmc_select_voltage(host, ocr);
|
||||
|
||||
/*
|
||||
* Can we support the voltage of the card?
|
||||
*/
|
||||
if (!host->ocr) {
|
||||
if (!rocr) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
@@ -1690,7 +1715,7 @@ int mmc_attach_mmc(struct mmc_host *host)
|
||||
/*
|
||||
* Detect and init the card.
|
||||
*/
|
||||
err = mmc_init_card(host, host->ocr, NULL);
|
||||
err = mmc_init_card(host, rocr, NULL);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
|
||||
+63
-33
@@ -23,6 +23,40 @@
|
||||
|
||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
|
||||
static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
|
||||
bool ignore_crc)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
if (ignore_crc)
|
||||
cmd.flags &= ~MMC_RSP_CRC;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* NOTE: callers are required to understand the difference
|
||||
* between "native" and SPI format status words!
|
||||
*/
|
||||
if (status)
|
||||
*status = cmd.resp[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
return __mmc_send_status(card, status, false);
|
||||
}
|
||||
|
||||
static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
@@ -370,16 +404,18 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
|
||||
* @timeout_ms: timeout (ms) for operation performed by register write,
|
||||
* timeout of zero implies maximum possible timeout
|
||||
* @use_busy_signal: use the busy signal as response type
|
||||
* @send_status: send status cmd to poll for busy
|
||||
*
|
||||
* Modifies the EXT_CSD register for selected card.
|
||||
*/
|
||||
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms, bool use_busy_signal)
|
||||
unsigned int timeout_ms, bool use_busy_signal, bool send_status)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
unsigned long timeout;
|
||||
u32 status;
|
||||
u32 status = 0;
|
||||
bool ignore_crc = false;
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
@@ -408,17 +444,37 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
if (!use_busy_signal)
|
||||
return 0;
|
||||
|
||||
/* Must check status to be sure of no errors */
|
||||
/*
|
||||
* Must check status to be sure of no errors
|
||||
* If CMD13 is to check the busy completion of the timing change,
|
||||
* disable the check of CRC error.
|
||||
*/
|
||||
if (index == EXT_CSD_HS_TIMING &&
|
||||
!(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
|
||||
ignore_crc = true;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
|
||||
do {
|
||||
err = mmc_send_status(card, &status);
|
||||
if (err)
|
||||
return err;
|
||||
if (send_status) {
|
||||
err = __mmc_send_status(card, &status, ignore_crc);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
|
||||
break;
|
||||
if (mmc_host_is_spi(card->host))
|
||||
break;
|
||||
|
||||
/*
|
||||
* We are not allowed to issue a status command and the host
|
||||
* does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
|
||||
* rely on waiting for the stated timeout to be sufficient.
|
||||
*/
|
||||
if (!send_status) {
|
||||
mmc_delay(timeout_ms);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Timeout if the device never leaves the program state. */
|
||||
if (time_after(jiffies, timeout)) {
|
||||
pr_err("%s: Card stuck in programming state! %s\n",
|
||||
@@ -445,36 +501,10 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
|
||||
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
||||
unsigned int timeout_ms)
|
||||
{
|
||||
return __mmc_switch(card, set, index, value, timeout_ms, true);
|
||||
return __mmc_switch(card, set, index, value, timeout_ms, true, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_switch);
|
||||
|
||||
int mmc_send_status(struct mmc_card *card, u32 *status)
|
||||
{
|
||||
int err;
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
BUG_ON(!card);
|
||||
BUG_ON(!card->host);
|
||||
|
||||
cmd.opcode = MMC_SEND_STATUS;
|
||||
if (!mmc_host_is_spi(card->host))
|
||||
cmd.arg = card->rca << 16;
|
||||
cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC;
|
||||
|
||||
err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* NOTE: callers are required to understand the difference
|
||||
* between "native" and SPI format status words!
|
||||
*/
|
||||
if (status)
|
||||
*status = cmd.resp[0];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode,
|
||||
u8 len)
|
||||
|
||||
+66
-52
@@ -13,6 +13,7 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
@@ -721,6 +722,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
|
||||
int err;
|
||||
u32 max_current;
|
||||
int retries = 10;
|
||||
u32 pocr = ocr;
|
||||
|
||||
try_again:
|
||||
if (!retries) {
|
||||
@@ -773,7 +775,8 @@ try_again:
|
||||
*/
|
||||
if (!mmc_host_is_spi(host) && rocr &&
|
||||
((*rocr & 0x41000000) == 0x41000000)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
|
||||
pocr);
|
||||
if (err == -EAGAIN) {
|
||||
retries--;
|
||||
goto try_again;
|
||||
@@ -935,6 +938,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
||||
if (IS_ERR(card))
|
||||
return PTR_ERR(card);
|
||||
|
||||
card->ocr = ocr;
|
||||
card->type = MMC_TYPE_SD;
|
||||
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
|
||||
}
|
||||
@@ -1064,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Suspend callback from host.
|
||||
*/
|
||||
static int mmc_sd_suspend(struct mmc_host *host)
|
||||
static int _mmc_sd_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
@@ -1075,34 +1076,77 @@ static int mmc_sd_suspend(struct mmc_host *host)
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
if (mmc_card_suspended(host->card))
|
||||
goto out;
|
||||
|
||||
if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
if (!err)
|
||||
if (!err) {
|
||||
mmc_power_off(host);
|
||||
mmc_card_set_suspended(host->card);
|
||||
}
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for suspend
|
||||
*/
|
||||
static int mmc_sd_suspend(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = _mmc_sd_suspend(host);
|
||||
if (!err) {
|
||||
pm_runtime_disable(&host->card->dev);
|
||||
pm_runtime_set_suspended(&host->card->dev);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resume callback from host.
|
||||
*
|
||||
* This function tries to determine if the same card is still present
|
||||
* and, if so, restore all state to it.
|
||||
*/
|
||||
static int mmc_sd_resume(struct mmc_host *host)
|
||||
static int _mmc_sd_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
||||
mmc_claim_host(host);
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
err = mmc_sd_init_card(host, host->ocr, host->card);
|
||||
|
||||
if (!mmc_card_suspended(host->card))
|
||||
goto out;
|
||||
|
||||
mmc_power_up(host, host->card->ocr);
|
||||
err = mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||
mmc_card_clr_suspended(host->card);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback for resume
|
||||
*/
|
||||
static int mmc_sd_resume(struct mmc_host *host)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
|
||||
err = _mmc_sd_resume(host);
|
||||
pm_runtime_set_active(&host->card->dev);
|
||||
pm_runtime_mark_last_busy(&host->card->dev);
|
||||
}
|
||||
pm_runtime_enable(&host->card->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1117,18 +1161,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host)
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
err = mmc_sd_suspend(host);
|
||||
if (err) {
|
||||
err = _mmc_sd_suspend(host);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive suspend\n",
|
||||
mmc_hostname(host), err);
|
||||
goto out;
|
||||
}
|
||||
mmc_power_off(host);
|
||||
|
||||
out:
|
||||
mmc_release_host(host);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1139,18 +1176,14 @@ static int mmc_sd_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!(host->caps & MMC_CAP_AGGRESSIVE_PM))
|
||||
if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME)))
|
||||
return 0;
|
||||
|
||||
mmc_claim_host(host);
|
||||
|
||||
mmc_power_up(host);
|
||||
err = mmc_sd_resume(host);
|
||||
err = _mmc_sd_resume(host);
|
||||
if (err)
|
||||
pr_err("%s: error %d doing aggessive resume\n",
|
||||
mmc_hostname(host), err);
|
||||
|
||||
mmc_release_host(host);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1160,7 +1193,7 @@ static int mmc_sd_power_restore(struct mmc_host *host)
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_sd_init_card(host, host->ocr, host->card);
|
||||
ret = mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
||||
return ret;
|
||||
@@ -1205,7 +1238,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host)
|
||||
int mmc_attach_sd(struct mmc_host *host)
|
||||
{
|
||||
int err;
|
||||
u32 ocr;
|
||||
u32 ocr, rocr;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
@@ -1229,31 +1262,12 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sanity check the voltages that the card claims to
|
||||
* support.
|
||||
*/
|
||||
if (ocr & 0x7F) {
|
||||
pr_warning("%s: card claims to support voltages "
|
||||
"below the defined range. These will be ignored.\n",
|
||||
mmc_hostname(host));
|
||||
ocr &= ~0x7F;
|
||||
}
|
||||
|
||||
if ((ocr & MMC_VDD_165_195) &&
|
||||
!(host->ocr_avail_sd & MMC_VDD_165_195)) {
|
||||
pr_warning("%s: SD card claims to support the "
|
||||
"incompletely defined 'low voltage range'. This "
|
||||
"will be ignored.\n", mmc_hostname(host));
|
||||
ocr &= ~MMC_VDD_165_195;
|
||||
}
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr);
|
||||
rocr = mmc_select_voltage(host, ocr);
|
||||
|
||||
/*
|
||||
* Can we support the voltage(s) of the card(s)?
|
||||
*/
|
||||
if (!host->ocr) {
|
||||
if (!rocr) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
@@ -1261,7 +1275,7 @@ int mmc_attach_sd(struct mmc_host *host)
|
||||
/*
|
||||
* Detect and init the card.
|
||||
*/
|
||||
err = mmc_sd_init_card(host, host->ocr, NULL);
|
||||
err = mmc_sd_init_card(host, rocr, NULL);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
|
||||
+24
-58
@@ -593,23 +593,28 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
||||
struct mmc_card *card;
|
||||
int err;
|
||||
int retries = 10;
|
||||
u32 rocr = 0;
|
||||
u32 ocr_card = ocr;
|
||||
|
||||
BUG_ON(!host);
|
||||
WARN_ON(!host->claimed);
|
||||
|
||||
/* to query card if 1.8V signalling is supported */
|
||||
if (mmc_host_uhs(host))
|
||||
ocr |= R4_18V_PRESENT;
|
||||
|
||||
try_again:
|
||||
if (!retries) {
|
||||
pr_warning("%s: Skipping voltage switch\n",
|
||||
mmc_hostname(host));
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inform the card of the voltage
|
||||
*/
|
||||
if (!powered_resume) {
|
||||
err = mmc_send_io_op_cond(host, host->ocr, &ocr);
|
||||
err = mmc_send_io_op_cond(host, ocr, &rocr);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
@@ -632,8 +637,8 @@ try_again:
|
||||
goto err;
|
||||
}
|
||||
|
||||
if ((ocr & R4_MEMORY_PRESENT) &&
|
||||
mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) {
|
||||
if ((rocr & R4_MEMORY_PRESENT) &&
|
||||
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
|
||||
card->type = MMC_TYPE_SD_COMBO;
|
||||
|
||||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
@@ -663,8 +668,9 @@ try_again:
|
||||
* systems that claim 1.8v signalling in fact do not support
|
||||
* it.
|
||||
*/
|
||||
if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) {
|
||||
err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180,
|
||||
ocr);
|
||||
if (err == -EAGAIN) {
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
@@ -674,12 +680,10 @@ try_again:
|
||||
goto try_again;
|
||||
} else if (err) {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
err = 0;
|
||||
} else {
|
||||
ocr &= ~R4_18V_PRESENT;
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -759,6 +763,7 @@ try_again:
|
||||
|
||||
card = oldcard;
|
||||
}
|
||||
card->ocr = ocr_card;
|
||||
mmc_fixup_device(card, NULL);
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
@@ -981,8 +986,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
|
||||
/* Restore power if needed */
|
||||
if (!mmc_card_keep_power(host)) {
|
||||
mmc_power_up(host);
|
||||
mmc_select_voltage(host, host->ocr);
|
||||
mmc_power_up(host, host->card->ocr);
|
||||
/*
|
||||
* Tell runtime PM core we just powered up the card,
|
||||
* since it still believes the card is powered off.
|
||||
@@ -1000,7 +1004,7 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) {
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
err = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
err = mmc_sdio_init_card(host, host->card->ocr, host->card,
|
||||
mmc_card_keep_power(host));
|
||||
} else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
/* We may have switched to 1-bit mode during suspend */
|
||||
@@ -1040,7 +1044,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
||||
static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||
{
|
||||
int ret;
|
||||
u32 ocr;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
@@ -1062,32 +1065,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
|
||||
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
|
||||
* harmless in other situations.
|
||||
*
|
||||
* With these steps taken, mmc_select_voltage() is also required to
|
||||
* restore the correct voltage setting of the card.
|
||||
*/
|
||||
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
|
||||
ret = mmc_send_io_op_cond(host, 0, &ocr);
|
||||
ret = mmc_send_io_op_cond(host, 0, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (host->ocr_avail_sdio)
|
||||
host->ocr_avail = host->ocr_avail_sdio;
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr & ~0x7F);
|
||||
if (!host->ocr) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mmc_host_uhs(host))
|
||||
/* to query card if 1.8V signalling is supported */
|
||||
host->ocr |= R4_18V_PRESENT;
|
||||
|
||||
ret = mmc_sdio_init_card(host, host->ocr, host->card,
|
||||
ret = mmc_sdio_init_card(host, host->card->ocr, host->card,
|
||||
mmc_card_keep_power(host));
|
||||
if (!ret && host->sdio_irqs)
|
||||
mmc_signal_sdio_irq(host);
|
||||
@@ -1108,7 +1096,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host)
|
||||
static int mmc_sdio_runtime_resume(struct mmc_host *host)
|
||||
{
|
||||
/* Restore power and re-initialize. */
|
||||
mmc_power_up(host);
|
||||
mmc_power_up(host, host->card->ocr);
|
||||
return mmc_sdio_power_restore(host);
|
||||
}
|
||||
|
||||
@@ -1131,7 +1119,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = {
|
||||
int mmc_attach_sdio(struct mmc_host *host)
|
||||
{
|
||||
int err, i, funcs;
|
||||
u32 ocr;
|
||||
u32 ocr, rocr;
|
||||
struct mmc_card *card;
|
||||
|
||||
BUG_ON(!host);
|
||||
@@ -1145,23 +1133,13 @@ int mmc_attach_sdio(struct mmc_host *host)
|
||||
if (host->ocr_avail_sdio)
|
||||
host->ocr_avail = host->ocr_avail_sdio;
|
||||
|
||||
/*
|
||||
* Sanity check the voltages that the card claims to
|
||||
* support.
|
||||
*/
|
||||
if (ocr & 0x7F) {
|
||||
pr_warning("%s: card claims to support voltages "
|
||||
"below the defined range. These will be ignored.\n",
|
||||
mmc_hostname(host));
|
||||
ocr &= ~0x7F;
|
||||
}
|
||||
|
||||
host->ocr = mmc_select_voltage(host, ocr);
|
||||
rocr = mmc_select_voltage(host, ocr);
|
||||
|
||||
/*
|
||||
* Can we support the voltage(s) of the card(s)?
|
||||
*/
|
||||
if (!host->ocr) {
|
||||
if (!rocr) {
|
||||
err = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
@@ -1169,22 +1147,10 @@ int mmc_attach_sdio(struct mmc_host *host)
|
||||
/*
|
||||
* Detect and init the card.
|
||||
*/
|
||||
if (mmc_host_uhs(host))
|
||||
/* to query card if 1.8V signalling is supported */
|
||||
host->ocr |= R4_18V_PRESENT;
|
||||
err = mmc_sdio_init_card(host, rocr, NULL, 0);
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
|
||||
if (err) {
|
||||
if (err == -EAGAIN) {
|
||||
/*
|
||||
* Retry initialization with S18R set to 0.
|
||||
*/
|
||||
host->ocr &= ~R4_18V_PRESENT;
|
||||
err = mmc_sdio_init_card(host, host->ocr, NULL, 0);
|
||||
}
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
card = host->card;
|
||||
|
||||
/*
|
||||
|
||||
@@ -255,7 +255,6 @@ struct atmel_mci_slot {
|
||||
#define ATMCI_CARD_PRESENT 0
|
||||
#define ATMCI_CARD_NEED_INIT 1
|
||||
#define ATMCI_SHUTDOWN 2
|
||||
#define ATMCI_SUSPENDED 3
|
||||
|
||||
int detect_pin;
|
||||
int wp_pin;
|
||||
@@ -589,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data)
|
||||
if (host->mrq->cmd->data) {
|
||||
host->mrq->cmd->data->error = -ETIMEDOUT;
|
||||
host->data = NULL;
|
||||
/*
|
||||
* With some SDIO modules, sometimes DMA transfer hangs. If
|
||||
* stop_transfer() is not called then the DMA request is not
|
||||
* removed, following ones are queued and never computed.
|
||||
*/
|
||||
if (host->state == STATE_DATA_XFER)
|
||||
host->stop_transfer(host);
|
||||
} else {
|
||||
host->mrq->cmd->error = -ETIMEDOUT;
|
||||
host->cmd = NULL;
|
||||
@@ -1803,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv)
|
||||
if (unlikely(status)) {
|
||||
host->stop_transfer(host);
|
||||
host->data = NULL;
|
||||
if (status & ATMCI_DTOE) {
|
||||
data->error = -ETIMEDOUT;
|
||||
} else if (status & ATMCI_DCRCE) {
|
||||
data->error = -EILSEQ;
|
||||
} else {
|
||||
data->error = -EIO;
|
||||
if (data) {
|
||||
if (status & ATMCI_DTOE) {
|
||||
data->error = -ETIMEDOUT;
|
||||
} else if (status & ATMCI_DCRCE) {
|
||||
data->error = -EILSEQ;
|
||||
} else {
|
||||
data->error = -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2520,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int atmci_suspend(struct device *dev)
|
||||
{
|
||||
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
||||
struct atmel_mci_slot *slot = host->slot[i];
|
||||
int ret;
|
||||
|
||||
if (!slot)
|
||||
continue;
|
||||
ret = mmc_suspend_host(slot->mmc);
|
||||
if (ret < 0) {
|
||||
while (--i >= 0) {
|
||||
slot = host->slot[i];
|
||||
if (slot
|
||||
&& test_bit(ATMCI_SUSPENDED, &slot->flags)) {
|
||||
mmc_resume_host(host->slot[i]->mmc);
|
||||
clear_bit(ATMCI_SUSPENDED, &slot->flags);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
set_bit(ATMCI_SUSPENDED, &slot->flags);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmci_resume(struct device *dev)
|
||||
{
|
||||
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
|
||||
struct atmel_mci_slot *slot = host->slot[i];
|
||||
int err;
|
||||
|
||||
slot = host->slot[i];
|
||||
if (!slot)
|
||||
continue;
|
||||
if (!test_bit(ATMCI_SUSPENDED, &slot->flags))
|
||||
continue;
|
||||
err = mmc_resume_host(slot->mmc);
|
||||
if (err < 0)
|
||||
ret = err;
|
||||
else
|
||||
clear_bit(ATMCI_SUSPENDED, &slot->flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume);
|
||||
|
||||
static struct platform_driver atmci_driver = {
|
||||
.remove = __exit_p(atmci_remove),
|
||||
.driver = {
|
||||
.name = "atmel_mci",
|
||||
.pm = &atmci_pm,
|
||||
.of_match_table = of_match_ptr(atmci_dt_ids),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev)
|
||||
static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct au1xmmc_host *host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
au_writel(0, HOST_CONFIG2(host));
|
||||
au_writel(0, HOST_CONFIG(host));
|
||||
@@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev)
|
||||
|
||||
au1xmmc_reset_controller(host);
|
||||
|
||||
return mmc_resume_host(host->mmc);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define au1xmmc_suspend NULL
|
||||
|
||||
@@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
/* Disable 4 bit SDIO */
|
||||
cfg &= ~SD4E;
|
||||
}
|
||||
bfin_write_SDH_CFG(cfg);
|
||||
|
||||
host->power_mode = ios->power_mode;
|
||||
#ifndef RSI_BLKSZ
|
||||
@@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
cfg &= ~SD_CMD_OD;
|
||||
# endif
|
||||
|
||||
|
||||
if (ios->power_mode != MMC_POWER_OFF)
|
||||
cfg |= PWR_ON;
|
||||
else
|
||||
@@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
clk_ctl |= CLK_E;
|
||||
host->clk_div = clk_div;
|
||||
bfin_write_SDH_CLK_CTL(clk_ctl);
|
||||
|
||||
} else
|
||||
sdh_stop_clock(host);
|
||||
|
||||
@@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev)
|
||||
#ifdef CONFIG_PM
|
||||
static int sdh_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
struct bfin_sd_host *drv_data = get_sdh_data(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (mmc)
|
||||
ret = mmc_suspend_host(mmc);
|
||||
|
||||
peripheral_free_list(drv_data->pin_req);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdh_resume(struct platform_device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = platform_get_drvdata(dev);
|
||||
struct bfin_sd_host *drv_data = get_sdh_data(dev);
|
||||
int ret = 0;
|
||||
|
||||
@@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev)
|
||||
}
|
||||
|
||||
sdh_reset();
|
||||
|
||||
if (mmc)
|
||||
ret = mmc_resume_host(mmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
|
||||
@@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = {
|
||||
static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
{
|
||||
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
|
||||
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
|
||||
int err;
|
||||
|
||||
err = mmc_suspend_host(mmc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cb710_mmc_enable_irq(slot, 0, ~0);
|
||||
return 0;
|
||||
@@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state)
|
||||
static int cb710_mmc_resume(struct platform_device *pdev)
|
||||
{
|
||||
struct cb710_slot *slot = cb710_pdev_to_slot(pdev);
|
||||
struct mmc_host *mmc = cb710_slot_to_mmc(slot);
|
||||
|
||||
cb710_mmc_enable_irq(slot, 0, ~0);
|
||||
|
||||
return mmc_resume_host(mmc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
@@ -193,7 +193,6 @@ struct mmc_davinci_host {
|
||||
#define DAVINCI_MMC_DATADIR_READ 1
|
||||
#define DAVINCI_MMC_DATADIR_WRITE 2
|
||||
unsigned char data_dir;
|
||||
unsigned char suspended;
|
||||
|
||||
/* buffer is used during PIO of one scatterlist segment, and
|
||||
* is updated along with buffer_bytes_left. bytes_left applies
|
||||
@@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
ret = mmc_suspend_host(host->mmc);
|
||||
if (!ret) {
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
mmc_davinci_reset_ctrl(host, 1);
|
||||
clk_disable(host->clk);
|
||||
host->suspended = 1;
|
||||
} else {
|
||||
host->suspended = 0;
|
||||
}
|
||||
writel(0, host->base + DAVINCI_MMCIM);
|
||||
mmc_davinci_reset_ctrl(host, 1);
|
||||
clk_disable(host->clk);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int davinci_mmcsd_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct mmc_davinci_host *host = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (!host->suspended)
|
||||
return 0;
|
||||
|
||||
clk_enable(host->clk);
|
||||
|
||||
mmc_davinci_reset_ctrl(host, 0);
|
||||
ret = mmc_resume_host(host->mmc);
|
||||
if (!ret)
|
||||
host->suspended = 0;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops davinci_mmcsd_pm = {
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/dw_mmc.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "dw_mmc.h"
|
||||
#include "dw_mmc-pltfm.h"
|
||||
@@ -30,16 +32,39 @@
|
||||
#define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \
|
||||
SDMMC_CLKSEL_CCLK_DRIVE(y) | \
|
||||
SDMMC_CLKSEL_CCLK_DIVIDER(z))
|
||||
#define SDMMC_CLKSEL_WAKEUP_INT BIT(11)
|
||||
|
||||
#define EXYNOS4210_FIXED_CIU_CLK_DIV 2
|
||||
#define EXYNOS4412_FIXED_CIU_CLK_DIV 4
|
||||
|
||||
/* Block number in eMMC */
|
||||
#define DWMCI_BLOCK_NUM 0xFFFFFFFF
|
||||
|
||||
#define SDMMC_EMMCP_BASE 0x1000
|
||||
#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010)
|
||||
#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200)
|
||||
#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204)
|
||||
#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C)
|
||||
|
||||
/* SMU control bits */
|
||||
#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7)
|
||||
#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5)
|
||||
#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4)
|
||||
#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3)
|
||||
#define DWMCI_MPSCTRL_ECB_MODE BIT(2)
|
||||
#define DWMCI_MPSCTRL_ENCRYPTION BIT(1)
|
||||
#define DWMCI_MPSCTRL_VALID BIT(0)
|
||||
|
||||
#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */
|
||||
|
||||
/* Variations in Exynos specific dw-mshc controller */
|
||||
enum dw_mci_exynos_type {
|
||||
DW_MCI_TYPE_EXYNOS4210,
|
||||
DW_MCI_TYPE_EXYNOS4412,
|
||||
DW_MCI_TYPE_EXYNOS5250,
|
||||
DW_MCI_TYPE_EXYNOS5420,
|
||||
DW_MCI_TYPE_EXYNOS5420_SMU,
|
||||
};
|
||||
|
||||
/* Exynos implementation specific driver private data */
|
||||
@@ -48,6 +73,7 @@ struct dw_mci_exynos_priv_data {
|
||||
u8 ciu_div;
|
||||
u32 sdr_timing;
|
||||
u32 ddr_timing;
|
||||
u32 cur_speed;
|
||||
};
|
||||
|
||||
static struct dw_mci_exynos_compatible {
|
||||
@@ -66,44 +92,80 @@ static struct dw_mci_exynos_compatible {
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-dw-mshc",
|
||||
.ctrl_type = DW_MCI_TYPE_EXYNOS5420,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5420-dw-mshc-smu",
|
||||
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
|
||||
},
|
||||
};
|
||||
|
||||
static int dw_mci_exynos_priv_init(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv;
|
||||
int idx;
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) {
|
||||
mci_writel(host, MPSBEGIN0, 0);
|
||||
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
|
||||
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
|
||||
DWMCI_MPSCTRL_NON_SECURE_READ_BIT |
|
||||
DWMCI_MPSCTRL_VALID |
|
||||
DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT);
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
|
||||
if (of_device_is_compatible(host->dev->of_node,
|
||||
exynos_compat[idx].compatible))
|
||||
priv->ctrl_type = exynos_compat[idx].ctrl_type;
|
||||
}
|
||||
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_setup_clock(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned long rate = clk_get_rate(host->ciu_clk);
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 ||
|
||||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420)
|
||||
host->bus_hz /= (priv->ciu_div + 1);
|
||||
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
|
||||
host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV;
|
||||
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
|
||||
host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV;
|
||||
host->bus_hz = rate / (priv->ciu_div + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dw_mci_exynos_suspend(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
return dw_mci_suspend(host);
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_resume(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
dw_mci_exynos_priv_init(host);
|
||||
return dw_mci_resume(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* dw_mci_exynos_resume_noirq - Exynos-specific resume code
|
||||
*
|
||||
* On exynos5420 there is a silicon errata that will sometimes leave the
|
||||
* WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate
|
||||
* that it fired and we can clear it by writing a 1 back. Clear it to prevent
|
||||
* interrupts from going off constantly.
|
||||
*
|
||||
* We run this code on all exynos variants because it doesn't hurt.
|
||||
*/
|
||||
|
||||
static int dw_mci_exynos_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct dw_mci *host = dev_get_drvdata(dev);
|
||||
u32 clksel;
|
||||
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
if (clksel & SDMMC_CLKSEL_WAKEUP_INT)
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define dw_mci_exynos_suspend NULL
|
||||
#define dw_mci_exynos_resume NULL
|
||||
#define dw_mci_exynos_resume_noirq NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
{
|
||||
@@ -121,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
unsigned int wanted = ios->clock;
|
||||
unsigned long actual;
|
||||
u8 div = priv->ciu_div + 1;
|
||||
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||||
mci_writel(host, CLKSEL, priv->ddr_timing);
|
||||
else
|
||||
/* Should be double rate for DDR mode */
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
wanted <<= 1;
|
||||
} else {
|
||||
mci_writel(host, CLKSEL, priv->sdr_timing);
|
||||
}
|
||||
|
||||
/* Don't care if wanted clock is zero */
|
||||
if (!wanted)
|
||||
return;
|
||||
|
||||
/* Guaranteed minimum frequency for cclkin */
|
||||
if (wanted < EXYNOS_CCLKIN_MIN)
|
||||
wanted = EXYNOS_CCLKIN_MIN;
|
||||
|
||||
if (wanted != priv->cur_speed) {
|
||||
int ret = clk_set_rate(host->ciu_clk, wanted * div);
|
||||
if (ret)
|
||||
dev_warn(host->dev,
|
||||
"failed to set clk-rate %u error: %d\n",
|
||||
wanted * div, ret);
|
||||
actual = clk_get_rate(host->ciu_clk);
|
||||
host->bus_hz = actual / div;
|
||||
priv->cur_speed = wanted;
|
||||
host->current_speed = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_exynos_priv_data *priv = host->priv;
|
||||
struct dw_mci_exynos_priv_data *priv;
|
||||
struct device_node *np = host->dev->of_node;
|
||||
u32 timing[2];
|
||||
u32 div = 0;
|
||||
int idx;
|
||||
int ret;
|
||||
|
||||
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
|
||||
priv->ciu_div = div;
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) {
|
||||
if (of_device_is_compatible(np, exynos_compat[idx].compatible))
|
||||
priv->ctrl_type = exynos_compat[idx].ctrl_type;
|
||||
}
|
||||
|
||||
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412)
|
||||
priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1;
|
||||
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210)
|
||||
priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1;
|
||||
else {
|
||||
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
|
||||
priv->ciu_div = div;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32_array(np,
|
||||
"samsung,dw-mshc-sdr-timing", timing, 2);
|
||||
@@ -152,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
|
||||
return ret;
|
||||
|
||||
priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div);
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
|
||||
{
|
||||
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
|
||||
}
|
||||
|
||||
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
|
||||
{
|
||||
u32 clksel;
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
}
|
||||
|
||||
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
|
||||
{
|
||||
u32 clksel;
|
||||
u8 sample;
|
||||
|
||||
clksel = mci_readl(host, CLKSEL);
|
||||
sample = (clksel + 1) & 0x7;
|
||||
clksel = (clksel & ~0x7) | sample;
|
||||
mci_writel(host, CLKSEL, clksel);
|
||||
return sample;
|
||||
}
|
||||
|
||||
static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates)
|
||||
{
|
||||
const u8 iter = 8;
|
||||
u8 __c;
|
||||
s8 i, loc = -1;
|
||||
|
||||
for (i = 0; i < iter; i++) {
|
||||
__c = ror8(candiates, i);
|
||||
if ((__c & 0xc7) == 0xc7) {
|
||||
loc = i;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < iter; i++) {
|
||||
__c = ror8(candiates, i);
|
||||
if ((__c & 0x83) == 0x83) {
|
||||
loc = i;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return loc;
|
||||
}
|
||||
|
||||
static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
|
||||
struct dw_mci_tuning_data *tuning_data)
|
||||
{
|
||||
struct dw_mci *host = slot->host;
|
||||
struct mmc_host *mmc = slot->mmc;
|
||||
const u8 *blk_pattern = tuning_data->blk_pattern;
|
||||
u8 *blk_test;
|
||||
unsigned int blksz = tuning_data->blksz;
|
||||
u8 start_smpl, smpl, candiates = 0;
|
||||
s8 found = -1;
|
||||
int ret = 0;
|
||||
|
||||
blk_test = kmalloc(blksz, GFP_KERNEL);
|
||||
if (!blk_test)
|
||||
return -ENOMEM;
|
||||
|
||||
start_smpl = dw_mci_exynos_get_clksmpl(host);
|
||||
|
||||
do {
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_command stop = {0};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
||||
|
||||
stop.opcode = MMC_STOP_TRANSMISSION;
|
||||
stop.arg = 0;
|
||||
stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
|
||||
data.blksz = blksz;
|
||||
data.blocks = 1;
|
||||
data.flags = MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, blk_test, blksz);
|
||||
mrq.cmd = &cmd;
|
||||
mrq.stop = &stop;
|
||||
mrq.data = &data;
|
||||
host->mrq = &mrq;
|
||||
|
||||
mci_writel(host, TMOUT, ~0);
|
||||
smpl = dw_mci_exynos_move_next_clksmpl(host);
|
||||
|
||||
mmc_wait_for_req(mmc, &mrq);
|
||||
|
||||
if (!cmd.error && !data.error) {
|
||||
if (!memcmp(blk_pattern, blk_test, blksz))
|
||||
candiates |= (1 << smpl);
|
||||
} else {
|
||||
dev_dbg(host->dev,
|
||||
"Tuning error: cmd.error:%d, data.error:%d\n",
|
||||
cmd.error, data.error);
|
||||
}
|
||||
} while (start_smpl != smpl);
|
||||
|
||||
found = dw_mci_exynos_get_best_clksmpl(candiates);
|
||||
if (found >= 0)
|
||||
dw_mci_exynos_set_clksmpl(host, found);
|
||||
else
|
||||
ret = -EIO;
|
||||
|
||||
kfree(blk_test);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Common capabilities of Exynos4/Exynos5 SoC */
|
||||
static unsigned long exynos_dwmmc_caps[4] = {
|
||||
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
|
||||
@@ -171,6 +400,7 @@ static const struct dw_mci_drv_data exynos_drv_data = {
|
||||
.prepare_command = dw_mci_exynos_prepare_command,
|
||||
.set_ios = dw_mci_exynos_set_ios,
|
||||
.parse_dt = dw_mci_exynos_parse_dt,
|
||||
.execute_tuning = dw_mci_exynos_execute_tuning,
|
||||
};
|
||||
|
||||
static const struct of_device_id dw_mci_exynos_match[] = {
|
||||
@@ -180,6 +410,8 @@ static const struct of_device_id dw_mci_exynos_match[] = {
|
||||
.data = &exynos_drv_data, },
|
||||
{ .compatible = "samsung,exynos5420-dw-mshc",
|
||||
.data = &exynos_drv_data, },
|
||||
{ .compatible = "samsung,exynos5420-dw-mshc-smu",
|
||||
.data = &exynos_drv_data, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
|
||||
@@ -194,13 +426,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
|
||||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
|
||||
.resume_noirq = dw_mci_exynos_resume_noirq,
|
||||
.thaw_noirq = dw_mci_exynos_resume_noirq,
|
||||
.restore_noirq = dw_mci_exynos_resume_noirq,
|
||||
};
|
||||
|
||||
static struct platform_driver dw_mci_exynos_pltfm_driver = {
|
||||
.probe = dw_mci_exynos_probe,
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dwmmc_exynos",
|
||||
.of_match_table = dw_mci_exynos_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
.pm = &dw_mci_exynos_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
{
|
||||
struct dw_mci *host;
|
||||
struct resource *regs;
|
||||
int ret;
|
||||
|
||||
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
|
||||
if (!host)
|
||||
@@ -59,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
if (IS_ERR(host->regs))
|
||||
return PTR_ERR(host->regs);
|
||||
|
||||
if (drv_data && drv_data->init) {
|
||||
ret = drv_data->init(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
return dw_mci_probe(host);
|
||||
}
|
||||
|
||||
@@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data {
|
||||
|
||||
static int dw_mci_socfpga_priv_init(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
|
||||
if (IS_ERR(priv->sysreg)) {
|
||||
dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
|
||||
return PTR_ERR(priv->sysreg);
|
||||
}
|
||||
host->priv = priv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
|
||||
|
||||
static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
|
||||
{
|
||||
struct dw_mci_socfpga_priv_data *priv = host->priv;
|
||||
struct dw_mci_socfpga_priv_data *priv;
|
||||
struct device_node *np = host->dev->of_node;
|
||||
u32 timing[2];
|
||||
u32 div = 0;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(host->dev, "mem alloc failed for private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
|
||||
if (IS_ERR(priv->sysreg)) {
|
||||
dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
|
||||
return PTR_ERR(priv->sysreg);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
|
||||
if (ret)
|
||||
dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
|
||||
@@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
|
||||
return ret;
|
||||
|
||||
priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
|
||||
host->priv = priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
|
||||
|
||||
int dw_mci_socfpga_probe(struct platform_device *pdev)
|
||||
static int dw_mci_socfpga_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct dw_mci_drv_data *drv_data;
|
||||
const struct of_device_id *match;
|
||||
@@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = {
|
||||
.remove = __exit_p(dw_mci_pltfm_remove),
|
||||
.driver = {
|
||||
.name = "dwmmc_socfpga",
|
||||
.of_match_table = of_match_ptr(dw_mci_socfpga_match),
|
||||
.of_match_table = dw_mci_socfpga_match,
|
||||
.pm = &dw_mci_pltfm_pmops,
|
||||
},
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user