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:
commit
c2ac2ae44d
|
@ -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:
|
||||||
|
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.
|
|
||||||
*/
|
*/
|
||||||
static int mmc_resume(struct mmc_host *host)
|
if (mmc_can_poweroff_notify(host->card) &&
|
||||||
{
|
!(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE))
|
||||||
int err;
|
err = _mmc_resume(host);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
err = __mmc_send_status(card, &status, ignore_crc);
|
||||||
if (err)
|
if (err)
|
||||||
return 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)
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
|
||||||
host->ocr |= R4_18V_PRESENT;
|
|
||||||
|
|
||||||
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)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
|
||||||
card = host->card;
|
card = host->card;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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,6 +1809,7 @@ 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 (data) {
|
||||||
if (status & ATMCI_DTOE) {
|
if (status & ATMCI_DTOE) {
|
||||||
data->error = -ETIMEDOUT;
|
data->error = -ETIMEDOUT;
|
||||||
} else if (status & ATMCI_DCRCE) {
|
} else if (status & ATMCI_DCRCE) {
|
||||||
|
@ -1811,6 +1818,7 @@ static void atmci_tasklet_func(unsigned long priv)
|
||||||
data->error = -EIO;
|
data->error = -EIO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
atmci_request_end(host, host->mrq);
|
atmci_request_end(host, host->mrq);
|
||||||
state = STATE_IDLE;
|
state = STATE_IDLE;
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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);
|
|
||||||
if (!ret) {
|
|
||||||
writel(0, host->base + DAVINCI_MMCIM);
|
writel(0, host->base + DAVINCI_MMCIM);
|
||||||
mmc_davinci_reset_ctrl(host, 1);
|
mmc_davinci_reset_ctrl(host, 1);
|
||||||
clk_disable(host->clk);
|
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 = {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
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);
|
of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div);
|
||||||
priv->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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,9 +250,14 @@ 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
|
||||||
|
if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
|
||||||
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
||||||
|
|
||||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||||
|
@ -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 */
|
/* Data transfer was stopped by the interrupt handler */
|
||||||
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
|
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;
|
||||||
|
|
||||||
|
if ((clock << div) != slot->__clk_old || force_clkinit)
|
||||||
dev_info(&slot->mmc->class_dev,
|
dev_info(&slot->mmc->class_dev,
|
||||||
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ"
|
"Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
|
||||||
" div = %d)\n", slot->id, host->bus_hz, slot->clock,
|
slot->id, host->bus_hz, clock,
|
||||||
div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div);
|
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) {
|
return cmd->error;
|
||||||
dw_mci_stop_dma(host);
|
}
|
||||||
host->data = NULL;
|
|
||||||
|
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->error = -EILSEQ;
|
|
||||||
} else if (status & SDMMC_INT_EBE &&
|
|
||||||
host->dir_status ==
|
|
||||||
DW_MCI_SEND_STATUS) {
|
|
||||||
/*
|
|
||||||
* No data CRC status was returned.
|
|
||||||
* The number of bytes transferred will
|
|
||||||
* be exaggerated in PIO mode.
|
|
||||||
*/
|
|
||||||
data->bytes_xfered = 0;
|
|
||||||
data->error = -ETIMEDOUT;
|
|
||||||
} else {
|
|
||||||
dev_err(host->dev,
|
|
||||||
"data FIFO error "
|
|
||||||
"(status=%08x)\n",
|
|
||||||
status);
|
|
||||||
data->error = -EIO;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* After an error, there may be data lingering
|
|
||||||
* in the FIFO, so reset it - doing so
|
|
||||||
* generates a block interrupt, hence setting
|
|
||||||
* the scatter-gather pointer to NULL.
|
|
||||||
*/
|
|
||||||
sg_miter_stop(&host->sg_miter);
|
|
||||||
host->sg = NULL;
|
|
||||||
ctrl = mci_readl(host, CTRL);
|
|
||||||
ctrl |= SDMMC_CTRL_FIFO_RESET;
|
|
||||||
mci_writel(host, CTRL, ctrl);
|
|
||||||
} else {
|
|
||||||
data->bytes_xfered = data->blocks * data->blksz;
|
|
||||||
data->error = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data->stop) {
|
|
||||||
dw_mci_request_end(host, host->mrq);
|
|
||||||
goto unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (host->mrq->sbc && !data->error) {
|
|
||||||
data->stop->error = 0;
|
data->stop->error = 0;
|
||||||
dw_mci_request_end(host, host->mrq);
|
dw_mci_request_end(host, mrq);
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* stop command for open-ended transfer*/
|
||||||
|
if (data->stop)
|
||||||
|
send_stop_abort(host, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If err has non-zero,
|
||||||
|
* stop-abort command has been already issued.
|
||||||
|
*/
|
||||||
prev_state = state = STATE_SENDING_STOP;
|
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,10 +1953,9 @@ 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:
|
||||||
|
if (mrq->stop)
|
||||||
mrq->stop->error = -ENOMEDIUM;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_ */
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
ret = mmc_suspend_host(mmc);
|
|
||||||
if (ret == 0) {
|
|
||||||
pm_runtime_get_sync(dev);
|
pm_runtime_get_sync(dev);
|
||||||
writel(0, host->base + MMCIMASK0);
|
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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
||||||
rc = mmc_suspend_host(mmc);
|
|
||||||
if (!rc)
|
|
||||||
msmsdcc_writel(host, 0, MMCIMASK0);
|
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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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[] = {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
if (!vub300 || !vub300->mmc) {
|
|
||||||
return 0;
|
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);
|
|
||||||
if (!vub300 || !vub300->mmc) {
|
|
||||||
return 0;
|
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
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,15 +925,11 @@ 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);
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
|
reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
|
||||||
writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
|
writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
|
||||||
SDMMC_BUSMODE);
|
SDMMC_BUSMODE);
|
||||||
|
@ -959,8 +941,7 @@ static int wmt_mci_suspend(struct device *dev)
|
||||||
writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
|
writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
|
||||||
|
|
||||||
clk_disable(priv->clk_sdmmc);
|
clk_disable(priv->clk_sdmmc);
|
||||||
}
|
return 0;
|
||||||
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 = {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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*/
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue