mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 18:30:06 +00:00
MMC core:
- mmc: core: Fixup max_discard/trim calculations - mmc: core: Announce SD specs greater than 4.0 - mmc: core: Add discard support for SD cards - mmc: core: Don't do retries for CMD6 (SWITCH command) - mmc: core: Various cleanups and re-structuring MMC host: - cqhci: Add maintainers for eMMC CQHCI driver - sdhci: Consolidate WP GPIO code - sdhci: Add ADMA3 DMA support for V4 enabled host - sdhci-pci-o2micro: Fixup card detect support - sdhci-tegra: Add support for CMDQ - sdhci-tegra: Support SDMMC pads auto-calibration - sdhci-esdhc-imx: Add DCMD support and CMDQ support - sdhci-esdhc-imx: Add support for i.MX6ULL variant - sdhci-esdhc-imx: Fixup HS400 timing issue - sdhci-esdhc-imx: Add HS400_ES support for i.MX8QXP - renesas_sdhi: Avoid CRC errors by adjusting settings to speed mode - renesas_sdhi: Fixup card initialization for high speed mode - omap: Fixup timeout settings - atmel-mci: Enable 8 bits bus-width support - jz4740: Convert some legacy code to use modern APIs - mmci: Send a CMD12 to clear DPSM at errors for STM32 sdmmc -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAlx9H6IXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmuXhAAvsaR7GvKGwuUNLN1aR+wHKA4 toS8zG0bEF/mS+xDU/OYX6f96FBQ0rJURCr/LoskVVesw9Z7zU9sn1XQ3EogkM22 EwhDJJ4yHkCMsF1GuCyx/ayeiV6ahMflJIMRJkujSD7S3XZdaAhWoHc0M/VvPM68 2WLjFR185fZAshkvtfN1lLDDGOfsQL8Net/U560nNCVFU0QSPPQcXejfn2ikhtja P32XsUEVhll+zRAtbuU0IP2rJ0xx0Br5rKSE2fpfbtN51B8KN27BgQcj78IbZzLF MWqqDADLCXZMLMnN+U6RUKF65Bog6mxLhcuO1x+/GcMcIB5ypIF6DZP44mE+IVyp 4M99Mm/07KxARfsJCwUTCCuVbI1C00T/fTmHiDHn7NhXXVmMq2s823Fvxxo3C59l 5l23EXV/xEPw2doZP6fQp5Wi/uXG0guvWaXAeX7FMWOgatneRRfynOOnigk4zdfX LqQ17G7CNmNQR3NtP4pNQ0PQPVbQR3D9+Pn/Cecpg1tz5gvWGzJ5DUm3DxMhr66r CrR32+1PtmsM9SuwiMSADcXDV8H2Bg4OV/VFL6JKP82+2YkfeXYG/9W4FlemnM5L 4zrT+cNuEHhDjQZDg15U7qcMVbNzFN9mUSSc6PLIZc1O5bIMl2NzlXXOHIsPrjb9 TN8TINem5b65j43annE= =umka -----END PGP SIGNATURE----- Merge tag 'mmc-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Fixup max_discard/trim calculations - Announce SD specs greater than 4.0 - Add discard support for SD cards - Don't do retries for CMD6 (SWITCH command) - Various cleanups and re-structuring MMC host: - cqhci: * Add maintainers for eMMC CQHCI driver - sdhci: * Consolidate WP GPIO code * Add ADMA3 DMA support for V4 enabled host * Fixup card detect support in pci-o2micro driver * Add support for CMDQ and SDMMC pads auto-calibration in tegra driver * Add DCMD support and CMDQ support, support for i.MX6ULL variant, fixup HS400 timing issue and add HS400_ES support for i.MX8QXP to esdhc-imx driver * Avoid CRC errors by adjusting settings to speed mode and fixup card initialization for high speed mode in renesas_sdhi * Fixup timeout settings for omap * Enable 8 bits bus-width support in atmel-mci * Convert some legacy code in jz4740 driver to use modern APIs * Send a CMD12 to clear DPSM at errors for STM32 sdmmc mmci driver" * tag 'mmc-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (69 commits) mmc:fix a bug when max_discard is 0 mmc: core: Add a debug print when the card may have been replaced mmc: core: Add sd discard timeout mmc: core: Add discard support to sd mmc: sdhci-esdhc-imx: clear the HALT bit when enable CQE mmc: core: do not retry CMD6 in __mmc_switch() mmc: core: Convert mmc_align_data_size() into an SDIO specific function mmc: core: Move mmc_of_parse_voltage() to host.c mmc: core: Convert mmc_regulator_get_ocrmask() to static mmc: core: Move regulator helpers to separate file mmc: of_mmc_spi: Convert to mmc_of_parse_voltage() mmc: core: Drop retries as in-parameter to mmc_wait_for_app_cmd() mmc: core: Convert mmc_wait_for_app_cmd() to static mmc: renesas_sdhi: Change HW adjustment register according to speed mode mmc: mmci: Send a CMD12 to clear the DPSM at errors mmc: sdhci-xenon: Fixup already marked switch fall-through mmc: sdhci-tegra: drop ->get_ro() implementation mmc: sdhci-omap: drop ->get_ro() implementation mmc: sdhci: use WP GPIO in sdhci_check_ro() mmc: wmt-sdmmc: Drop unused include ...
This commit is contained in:
commit
42eaf1851e
59 changed files with 1198 additions and 686 deletions
|
@ -15,6 +15,7 @@ Required properties:
|
|||
"fsl,imx6q-usdhc"
|
||||
"fsl,imx6sl-usdhc"
|
||||
"fsl,imx6sx-usdhc"
|
||||
"fsl,imx6ull-usdhc"
|
||||
"fsl,imx7d-usdhc"
|
||||
"fsl,imx8qxp-usdhc"
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ Optional properties:
|
|||
be referred to mmc-pwrseq-simple.txt. But now it's reused as a tunable delay
|
||||
waiting for I/O signalling and card power supply to be stable, regardless of
|
||||
whether pwrseq-simple is used. Default to 10ms if no available.
|
||||
- supports-cqe : The presence of this property indicates that the corresponding
|
||||
MMC host controller supports HW command queue feature.
|
||||
|
||||
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
|
||||
polarity properties, we have to fix the meaning of the "normal" and "inverted"
|
||||
|
|
|
@ -39,12 +39,16 @@ sdhci@c8000200 {
|
|||
bus-width = <8>;
|
||||
};
|
||||
|
||||
Optional properties for Tegra210 and Tegra186:
|
||||
Optional properties for Tegra210, Tegra186 and Tegra194:
|
||||
- pinctrl-names, pinctrl-0, pinctrl-1 : Specify pad voltage
|
||||
configurations. Valid pinctrl-names are "sdmmc-3v3" and "sdmmc-1v8"
|
||||
for controllers supporting multiple voltage levels. The order of names
|
||||
should correspond to the pin configuration states in pinctrl-0 and
|
||||
pinctrl-1.
|
||||
- pinctrl-names : "sdmmc-3v3-drv" and "sdmmc-1v8-drv" are applicable for
|
||||
Tegra210 where pad config registers are in the pinmux register domain
|
||||
for pull-up-strength and pull-down-strength values configuration when
|
||||
using pads at 3V3 and 1V8 levels.
|
||||
- nvidia,only-1-8-v : The presence of this property indicates that the
|
||||
controller operates at a 1.8 V fixed I/O voltage.
|
||||
- nvidia,pad-autocal-pull-up-offset-3v3,
|
||||
|
|
|
@ -24,31 +24,3 @@ Examples:
|
|||
dmas = <&sdma 61 &sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
||||
* TI MMC host controller for OMAP1 and 2420
|
||||
|
||||
The MMC Host Controller on TI OMAP1 and 2420 family provides
|
||||
an interface for MMC, SD, and SDIO types of memory cards.
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the omap mmc driver.
|
||||
|
||||
Note that this driver will not work with omap2430 or later omaps,
|
||||
please see the omap hsmmc driver for the current omaps.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "ti,omap2420-mmc", for OMAP2420 controllers
|
||||
- ti,hwmods: For 2420, must be "msdi<n>", where n is controller
|
||||
instance starting 1
|
||||
|
||||
Examples:
|
||||
|
||||
msdi1: mmc@4809c000 {
|
||||
compatible = "ti,omap2420-mmc";
|
||||
ti,hwmods = "msdi1";
|
||||
reg = <0x4809c000 0x80>;
|
||||
interrupts = <83>;
|
||||
dmas = <&sdma 61 &sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
||||
|
|
|
@ -13616,11 +13616,18 @@ F: drivers/mmc/host/sdhci-brcmstb*
|
|||
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
|
||||
M: Adrian Hunter <adrian.hunter@intel.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.infradead.org/users/ahunter/linux-sdhci.git
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci*
|
||||
F: include/linux/mmc/sdhci*
|
||||
|
||||
EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER
|
||||
M: Adrian Hunter <adrian.hunter@intel.com>
|
||||
M: Ritesh Harjani <riteshh@codeaurora.org>
|
||||
M: Asutosh Das <asutoshd@codeaurora.org>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/cqhci*
|
||||
|
||||
SYNOPSYS SDHCI COMPLIANT DWC MSHC DRIVER
|
||||
M: Prabu Thangamuthu <prabu.t@synopsys.com>
|
||||
M: Manjunath M B <manjumb@synopsys.com>
|
||||
|
|
|
@ -8,7 +8,7 @@ mmc_core-y := core.o bus.o host.o \
|
|||
mmc.o mmc_ops.o sd.o sd_ops.o \
|
||||
sdio.o sdio_ops.o sdio_bus.o \
|
||||
sdio_cis.o sdio_io.o sdio_irq.o \
|
||||
slot-gpio.o
|
||||
slot-gpio.o regulator.o
|
||||
mmc_core-$(CONFIG_OF) += pwrseq.o
|
||||
obj-$(CONFIG_PWRSEQ_SIMPLE) += pwrseq_simple.o
|
||||
obj-$(CONFIG_PWRSEQ_SD8787) += pwrseq_sd8787.o
|
||||
|
|
|
@ -1124,7 +1124,7 @@ static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
struct mmc_card *card = md->queue.card;
|
||||
unsigned int from, nr, arg;
|
||||
unsigned int from, nr;
|
||||
int err = 0, type = MMC_BLK_DISCARD;
|
||||
blk_status_t status = BLK_STS_OK;
|
||||
|
||||
|
@ -1136,24 +1136,18 @@ static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
from = blk_rq_pos(req);
|
||||
nr = blk_rq_sectors(req);
|
||||
|
||||
if (mmc_can_discard(card))
|
||||
arg = MMC_DISCARD_ARG;
|
||||
else if (mmc_can_trim(card))
|
||||
arg = MMC_TRIM_ARG;
|
||||
else
|
||||
arg = MMC_ERASE_ARG;
|
||||
do {
|
||||
err = 0;
|
||||
if (card->quirks & MMC_QUIRK_INAND_CMD38) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
arg == MMC_TRIM_ARG ?
|
||||
card->erase_arg == MMC_TRIM_ARG ?
|
||||
INAND_CMD38_ARG_TRIM :
|
||||
INAND_CMD38_ARG_ERASE,
|
||||
0);
|
||||
}
|
||||
if (!err)
|
||||
err = mmc_erase(card, from, nr, arg);
|
||||
err = mmc_erase(card, from, nr, card->erase_arg);
|
||||
} while (err == -EIO && !mmc_blk_reset(md, card->host, type));
|
||||
if (err)
|
||||
status = BLK_STS_IOERR;
|
||||
|
@ -2768,8 +2762,8 @@ static int mmc_dbg_card_status_get(void *data, u64 *val)
|
|||
|
||||
return ret;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
|
||||
NULL, "%08llx\n");
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get,
|
||||
NULL, "%08llx\n");
|
||||
|
||||
/* That is two digits * 512 + 1 for newline */
|
||||
#define EXT_CSD_STR_LEN 1025
|
||||
|
@ -2857,8 +2851,9 @@ static int mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
|
|||
|
||||
if (mmc_card_mmc(card) || mmc_card_sd(card)) {
|
||||
md->status_dentry =
|
||||
debugfs_create_file("status", S_IRUSR, root, card,
|
||||
&mmc_dbg_card_status_fops);
|
||||
debugfs_create_file_unsafe("status", 0400, root,
|
||||
card,
|
||||
&mmc_dbg_card_status_fops);
|
||||
if (!md->status_dentry)
|
||||
return -EIO;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/leds.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/suspend.h>
|
||||
|
@ -52,6 +51,7 @@
|
|||
|
||||
/* The max erase timeout, used when host->max_busy_timeout isn't specified */
|
||||
#define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */
|
||||
#define SD_DISCARD_TIMEOUT_MS (250)
|
||||
|
||||
static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
|
||||
|
||||
|
@ -758,33 +758,6 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
|||
}
|
||||
EXPORT_SYMBOL(mmc_set_data_timeout);
|
||||
|
||||
/**
|
||||
* mmc_align_data_size - pads a transfer size to a more optimal value
|
||||
* @card: the MMC card associated with the data transfer
|
||||
* @sz: original transfer size
|
||||
*
|
||||
* Pads the original data size with a number of extra bytes in
|
||||
* order to avoid controller bugs and/or performance hits
|
||||
* (e.g. some controllers revert to PIO for certain sizes).
|
||||
*
|
||||
* Returns the improved size, which might be unmodified.
|
||||
*
|
||||
* Note that this function is only relevant when issuing a
|
||||
* single scatter gather entry.
|
||||
*/
|
||||
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz)
|
||||
{
|
||||
/*
|
||||
* FIXME: We don't have a system for the controller to tell
|
||||
* the core about its problems yet, so for now we just 32-bit
|
||||
* align the size.
|
||||
*/
|
||||
sz = ((sz + 3) / 4) * 4;
|
||||
|
||||
return sz;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_align_data_size);
|
||||
|
||||
/*
|
||||
* Allow claiming an already claimed host if the context is the same or there is
|
||||
* no context but the task is the same.
|
||||
|
@ -1112,55 +1085,6 @@ u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max)
|
|||
|
||||
return mask;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_vddrange_to_ocrmask);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
||||
/**
|
||||
* mmc_of_parse_voltage - return mask of supported voltages
|
||||
* @np: The device node need to be parsed.
|
||||
* @mask: mask of voltages available for MMC/SD/SDIO
|
||||
*
|
||||
* Parse the "voltage-ranges" DT property, returning zero if it is not
|
||||
* found, negative errno if the voltage-range specification is invalid,
|
||||
* or one if the voltage-range is specified and successfully parsed.
|
||||
*/
|
||||
int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
{
|
||||
const u32 *voltage_ranges;
|
||||
int num_ranges, i;
|
||||
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
if (!voltage_ranges) {
|
||||
pr_debug("%pOF: voltage-ranges unspecified\n", np);
|
||||
return 0;
|
||||
}
|
||||
if (!num_ranges) {
|
||||
pr_err("%pOF: voltage-ranges empty\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ranges; i++) {
|
||||
const int j = i * 2;
|
||||
u32 ocr_mask;
|
||||
|
||||
ocr_mask = mmc_vddrange_to_ocrmask(
|
||||
be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!ocr_mask) {
|
||||
pr_err("%pOF: voltage-range #%d is invalid\n",
|
||||
np, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
*mask |= ocr_mask;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int mmc_of_get_func_num(struct device_node *node)
|
||||
{
|
||||
|
@ -1190,246 +1114,6 @@ struct device_node *mmc_of_find_child_device(struct mmc_host *host,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
|
||||
/**
|
||||
* mmc_ocrbitnum_to_vdd - Convert a OCR bit number to its voltage
|
||||
* @vdd_bit: OCR bit number
|
||||
* @min_uV: minimum voltage value (mV)
|
||||
* @max_uV: maximum voltage value (mV)
|
||||
*
|
||||
* This function returns the voltage range according to the provided OCR
|
||||
* bit number. If conversion is not possible a negative errno value returned.
|
||||
*/
|
||||
static int mmc_ocrbitnum_to_vdd(int vdd_bit, int *min_uV, int *max_uV)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
if (!vdd_bit)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT mmc_vddrange_to_ocrmask() may have set some
|
||||
* bits this regulator doesn't quite support ... don't
|
||||
* be too picky, most cards and regulators are OK with
|
||||
* a 0.1V range goof (it's a small error percentage).
|
||||
*/
|
||||
tmp = vdd_bit - ilog2(MMC_VDD_165_195);
|
||||
if (tmp == 0) {
|
||||
*min_uV = 1650 * 1000;
|
||||
*max_uV = 1950 * 1000;
|
||||
} else {
|
||||
*min_uV = 1900 * 1000 + tmp * 100 * 1000;
|
||||
*max_uV = *min_uV + 100 * 1000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_regulator_get_ocrmask - return mask of supported voltages
|
||||
* @supply: regulator to use
|
||||
*
|
||||
* This returns either a negative errno, or a mask of voltages that
|
||||
* can be provided to MMC/SD/SDIO devices using the specified voltage
|
||||
* regulator. This would normally be called before registering the
|
||||
* MMC host adapter.
|
||||
*/
|
||||
int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
int result = 0;
|
||||
int count;
|
||||
int i;
|
||||
int vdd_uV;
|
||||
int vdd_mV;
|
||||
|
||||
count = regulator_count_voltages(supply);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
vdd_uV = regulator_list_voltage(supply, i);
|
||||
if (vdd_uV <= 0)
|
||||
continue;
|
||||
|
||||
vdd_mV = vdd_uV / 1000;
|
||||
result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
vdd_uV = regulator_get_voltage(supply);
|
||||
if (vdd_uV <= 0)
|
||||
return vdd_uV;
|
||||
|
||||
vdd_mV = vdd_uV / 1000;
|
||||
result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask);
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
|
||||
* @mmc: the host to regulate
|
||||
* @supply: regulator to use
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* MMC host drivers may use this to enable or disable a regulator using
|
||||
* a particular supply voltage. This would normally be called from the
|
||||
* set_ios() method.
|
||||
*/
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
{
|
||||
int result = 0;
|
||||
int min_uV, max_uV;
|
||||
|
||||
if (vdd_bit) {
|
||||
mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);
|
||||
|
||||
result = regulator_set_voltage(supply, min_uV, max_uV);
|
||||
if (result == 0 && !mmc->regulator_enabled) {
|
||||
result = regulator_enable(supply);
|
||||
if (!result)
|
||||
mmc->regulator_enabled = true;
|
||||
}
|
||||
} else if (mmc->regulator_enabled) {
|
||||
result = regulator_disable(supply);
|
||||
if (result == 0)
|
||||
mmc->regulator_enabled = false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
dev_err(mmc_dev(mmc),
|
||||
"could not set regulator OCR (%d)\n", result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
|
||||
|
||||
static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
|
||||
int min_uV, int target_uV,
|
||||
int max_uV)
|
||||
{
|
||||
/*
|
||||
* Check if supported first to avoid errors since we may try several
|
||||
* signal levels during power up and don't want to show errors.
|
||||
*/
|
||||
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
|
||||
return -EINVAL;
|
||||
|
||||
return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
|
||||
max_uV);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_vqmmc - Set VQMMC as per the ios
|
||||
*
|
||||
* For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
|
||||
* That will match the behavior of old boards where VQMMC and VMMC were supplied
|
||||
* by the same supply. The Bus Operating conditions for 3.3V signaling in the
|
||||
* SD card spec also define VQMMC in terms of VMMC.
|
||||
* If this is not possible we'll try the full 2.7-3.6V of the spec.
|
||||
*
|
||||
* For 1.2V and 1.8V signaling we'll try to get as close as possible to the
|
||||
* requested voltage. This is definitely a good idea for UHS where there's a
|
||||
* separate regulator on the card that's trying to make 1.8V and it's best if
|
||||
* we match.
|
||||
*
|
||||
* This function is expected to be used by a controller's
|
||||
* start_signal_voltage_switch() function.
|
||||
*/
|
||||
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
int ret, volt, min_uV, max_uV;
|
||||
|
||||
/* If no vqmmc supply then we can't change the voltage */
|
||||
if (IS_ERR(mmc->supply.vqmmc))
|
||||
return -EINVAL;
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_120:
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
1100000, 1200000, 1300000);
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
1700000, 1800000, 1950000);
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
|
||||
__func__, volt, max_uV);
|
||||
|
||||
min_uV = max(volt - 300000, 2700000);
|
||||
max_uV = min(max_uV + 200000, 3600000);
|
||||
|
||||
/*
|
||||
* Due to a limitation in the current implementation of
|
||||
* regulator_set_voltage_triplet() which is taking the lowest
|
||||
* voltage possible if below the target, search for a suitable
|
||||
* voltage in two steps and try to stay close to vmmc
|
||||
* with a 0.3V tolerance at first.
|
||||
*/
|
||||
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
min_uV, volt, max_uV))
|
||||
return 0;
|
||||
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
2700000, volt, 3600000);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
|
||||
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/**
|
||||
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
|
||||
* @mmc: the host to regulate
|
||||
*
|
||||
* Returns 0 or errno. errno should be handled, it is either a critical error
|
||||
* or -EPROBE_DEFER. 0 means no critical error but it does not mean all
|
||||
* regulators have been found because they all are optional. If you require
|
||||
* certain regulators, you need to check separately in your driver if they got
|
||||
* populated after calling this function.
|
||||
*/
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
int ret;
|
||||
|
||||
mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
|
||||
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
|
||||
|
||||
if (IS_ERR(mmc->supply.vmmc)) {
|
||||
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_dbg(dev, "No vmmc regulator found\n");
|
||||
} else {
|
||||
ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
|
||||
if (ret > 0)
|
||||
mmc->ocr_avail = ret;
|
||||
else
|
||||
dev_warn(dev, "Failed getting OCR mask: %d\n", ret);
|
||||
}
|
||||
|
||||
if (IS_ERR(mmc->supply.vqmmc)) {
|
||||
if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_dbg(dev, "No vqmmc regulator found\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
|
||||
|
||||
/*
|
||||
* Mask off any voltages we don't support and select
|
||||
* the lowest voltage
|
||||
|
@ -1936,6 +1620,12 @@ static unsigned int mmc_sd_erase_timeout(struct mmc_card *card,
|
|||
{
|
||||
unsigned int erase_timeout;
|
||||
|
||||
/* for DISCARD none of the below calculation applies.
|
||||
* the busy timeout is 250msec per discard command.
|
||||
*/
|
||||
if (arg == SD_DISCARD_ARG)
|
||||
return SD_DISCARD_TIMEOUT_MS;
|
||||
|
||||
if (card->ssr.erase_timeout) {
|
||||
/* Erase timeout specified in SD Status Register (SSR) */
|
||||
erase_timeout = card->ssr.erase_timeout * qty +
|
||||
|
@ -2164,7 +1854,7 @@ static unsigned int mmc_align_erase_size(struct mmc_card *card,
|
|||
* @card: card to erase
|
||||
* @from: first sector to erase
|
||||
* @nr: number of sectors to erase
|
||||
* @arg: erase command argument (SD supports only %MMC_ERASE_ARG)
|
||||
* @arg: erase command argument
|
||||
*
|
||||
* Caller must claim host before calling this function.
|
||||
*/
|
||||
|
@ -2181,14 +1871,14 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
|||
if (!card->erase_size)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mmc_card_sd(card) && arg != MMC_ERASE_ARG)
|
||||
if (mmc_card_sd(card) && arg != SD_ERASE_ARG && arg != SD_DISCARD_ARG)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((arg & MMC_SECURE_ARGS) &&
|
||||
if (mmc_card_mmc(card) && (arg & MMC_SECURE_ARGS) &&
|
||||
!(card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if ((arg & MMC_TRIM_ARGS) &&
|
||||
if (mmc_card_mmc(card) && (arg & MMC_TRIM_ARGS) &&
|
||||
!(card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
|
@ -2381,9 +2071,9 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
|
|||
return card->pref_erase;
|
||||
|
||||
max_discard = mmc_do_calc_max_discard(card, MMC_ERASE_ARG);
|
||||
if (max_discard && mmc_can_trim(card)) {
|
||||
if (mmc_can_trim(card)) {
|
||||
max_trim = mmc_do_calc_max_discard(card, MMC_TRIM_ARG);
|
||||
if (max_trim < max_discard)
|
||||
if (max_trim < max_discard || max_discard == 0)
|
||||
max_discard = max_trim;
|
||||
} else if (max_discard < card->erase_size) {
|
||||
max_discard = 0;
|
||||
|
|
|
@ -59,6 +59,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr);
|
|||
void mmc_power_off(struct mmc_host *host);
|
||||
void mmc_power_cycle(struct mmc_host *host, u32 ocr);
|
||||
void mmc_set_initial_state(struct mmc_host *host);
|
||||
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
|
||||
|
||||
static inline void mmc_delay(unsigned int ms)
|
||||
{
|
||||
|
|
|
@ -194,7 +194,7 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
switch (bus_width) {
|
||||
case 8:
|
||||
host->caps |= MMC_CAP_8_BIT_DATA;
|
||||
/* Hosts capable of 8-bit transfers can also do 4 bits */
|
||||
/* fall through - Hosts capable of 8-bit can also do 4 bits */
|
||||
case 4:
|
||||
host->caps |= MMC_CAP_4_BIT_DATA;
|
||||
break;
|
||||
|
@ -260,7 +260,7 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
/* Parse Write Protection */
|
||||
ro_cap_invert = device_property_read_bool(dev, "wp-inverted");
|
||||
|
||||
ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &ro_gpio_invert);
|
||||
ret = mmc_gpiod_request_ro(host, "wp", 0, 0, &ro_gpio_invert);
|
||||
if (!ret)
|
||||
dev_info(host->parent, "Got WP GPIO\n");
|
||||
else if (ret != -ENOENT && ret != -ENOSYS)
|
||||
|
@ -348,6 +348,50 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
|
||||
EXPORT_SYMBOL(mmc_of_parse);
|
||||
|
||||
/**
|
||||
* mmc_of_parse_voltage - return mask of supported voltages
|
||||
* @np: The device node need to be parsed.
|
||||
* @mask: mask of voltages available for MMC/SD/SDIO
|
||||
*
|
||||
* Parse the "voltage-ranges" DT property, returning zero if it is not
|
||||
* found, negative errno if the voltage-range specification is invalid,
|
||||
* or one if the voltage-range is specified and successfully parsed.
|
||||
*/
|
||||
int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||
{
|
||||
const u32 *voltage_ranges;
|
||||
int num_ranges, i;
|
||||
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
if (!voltage_ranges) {
|
||||
pr_debug("%pOF: voltage-ranges unspecified\n", np);
|
||||
return 0;
|
||||
}
|
||||
if (!num_ranges) {
|
||||
pr_err("%pOF: voltage-ranges empty\n", np);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ranges; i++) {
|
||||
const int j = i * 2;
|
||||
u32 ocr_mask;
|
||||
|
||||
ocr_mask = mmc_vddrange_to_ocrmask(
|
||||
be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!ocr_mask) {
|
||||
pr_err("%pOF: voltage-range #%d is invalid\n",
|
||||
np, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
*mask |= ocr_mask;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||
|
||||
/**
|
||||
* mmc_alloc_host - initialise the per-host structure.
|
||||
* @extra: sizeof private data structure
|
||||
|
|
|
@ -1594,6 +1594,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
|
||||
if (oldcard) {
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
err = -ENOENT;
|
||||
goto err;
|
||||
}
|
||||
|
@ -1743,6 +1745,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
card->ext_csd.power_off_notification = EXT_CSD_POWER_ON;
|
||||
}
|
||||
|
||||
/* set erase_arg */
|
||||
if (mmc_can_discard(card))
|
||||
card->erase_arg = MMC_DISCARD_ARG;
|
||||
else if (mmc_can_trim(card))
|
||||
card->erase_arg = MMC_TRIM_ARG;
|
||||
else
|
||||
card->erase_arg = MMC_ERASE_ARG;
|
||||
|
||||
/*
|
||||
* Select timing interface
|
||||
*/
|
||||
|
|
|
@ -562,7 +562,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
if (index == EXT_CSD_SANITIZE_START)
|
||||
cmd.sanitize_busy = true;
|
||||
|
||||
err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
|
||||
err = mmc_wait_for_cmd(host, &cmd, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
|
260
drivers/mmc/core/regulator.c
Normal file
260
drivers/mmc/core/regulator.c
Normal file
|
@ -0,0 +1,260 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Helper functions for MMC regulators.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "host.h"
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
|
||||
/**
|
||||
* mmc_ocrbitnum_to_vdd - Convert a OCR bit number to its voltage
|
||||
* @vdd_bit: OCR bit number
|
||||
* @min_uV: minimum voltage value (mV)
|
||||
* @max_uV: maximum voltage value (mV)
|
||||
*
|
||||
* This function returns the voltage range according to the provided OCR
|
||||
* bit number. If conversion is not possible a negative errno value returned.
|
||||
*/
|
||||
static int mmc_ocrbitnum_to_vdd(int vdd_bit, int *min_uV, int *max_uV)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
if (!vdd_bit)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* REVISIT mmc_vddrange_to_ocrmask() may have set some
|
||||
* bits this regulator doesn't quite support ... don't
|
||||
* be too picky, most cards and regulators are OK with
|
||||
* a 0.1V range goof (it's a small error percentage).
|
||||
*/
|
||||
tmp = vdd_bit - ilog2(MMC_VDD_165_195);
|
||||
if (tmp == 0) {
|
||||
*min_uV = 1650 * 1000;
|
||||
*max_uV = 1950 * 1000;
|
||||
} else {
|
||||
*min_uV = 1900 * 1000 + tmp * 100 * 1000;
|
||||
*max_uV = *min_uV + 100 * 1000;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_regulator_get_ocrmask - return mask of supported voltages
|
||||
* @supply: regulator to use
|
||||
*
|
||||
* This returns either a negative errno, or a mask of voltages that
|
||||
* can be provided to MMC/SD/SDIO devices using the specified voltage
|
||||
* regulator. This would normally be called before registering the
|
||||
* MMC host adapter.
|
||||
*/
|
||||
static int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
int result = 0;
|
||||
int count;
|
||||
int i;
|
||||
int vdd_uV;
|
||||
int vdd_mV;
|
||||
|
||||
count = regulator_count_voltages(supply);
|
||||
if (count < 0)
|
||||
return count;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
vdd_uV = regulator_list_voltage(supply, i);
|
||||
if (vdd_uV <= 0)
|
||||
continue;
|
||||
|
||||
vdd_mV = vdd_uV / 1000;
|
||||
result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
vdd_uV = regulator_get_voltage(supply);
|
||||
if (vdd_uV <= 0)
|
||||
return vdd_uV;
|
||||
|
||||
vdd_mV = vdd_uV / 1000;
|
||||
result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_ocr - set regulator to match host->ios voltage
|
||||
* @mmc: the host to regulate
|
||||
* @supply: regulator to use
|
||||
* @vdd_bit: zero for power off, else a bit number (host->ios.vdd)
|
||||
*
|
||||
* Returns zero on success, else negative errno.
|
||||
*
|
||||
* MMC host drivers may use this to enable or disable a regulator using
|
||||
* a particular supply voltage. This would normally be called from the
|
||||
* set_ios() method.
|
||||
*/
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
{
|
||||
int result = 0;
|
||||
int min_uV, max_uV;
|
||||
|
||||
if (vdd_bit) {
|
||||
mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);
|
||||
|
||||
result = regulator_set_voltage(supply, min_uV, max_uV);
|
||||
if (result == 0 && !mmc->regulator_enabled) {
|
||||
result = regulator_enable(supply);
|
||||
if (!result)
|
||||
mmc->regulator_enabled = true;
|
||||
}
|
||||
} else if (mmc->regulator_enabled) {
|
||||
result = regulator_disable(supply);
|
||||
if (result == 0)
|
||||
mmc->regulator_enabled = false;
|
||||
}
|
||||
|
||||
if (result)
|
||||
dev_err(mmc_dev(mmc),
|
||||
"could not set regulator OCR (%d)\n", result);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
|
||||
|
||||
static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
|
||||
int min_uV, int target_uV,
|
||||
int max_uV)
|
||||
{
|
||||
/*
|
||||
* Check if supported first to avoid errors since we may try several
|
||||
* signal levels during power up and don't want to show errors.
|
||||
*/
|
||||
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
|
||||
return -EINVAL;
|
||||
|
||||
return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
|
||||
max_uV);
|
||||
}
|
||||
|
||||
/**
|
||||
* mmc_regulator_set_vqmmc - Set VQMMC as per the ios
|
||||
*
|
||||
* For 3.3V signaling, we try to match VQMMC to VMMC as closely as possible.
|
||||
* That will match the behavior of old boards where VQMMC and VMMC were supplied
|
||||
* by the same supply. The Bus Operating conditions for 3.3V signaling in the
|
||||
* SD card spec also define VQMMC in terms of VMMC.
|
||||
* If this is not possible we'll try the full 2.7-3.6V of the spec.
|
||||
*
|
||||
* For 1.2V and 1.8V signaling we'll try to get as close as possible to the
|
||||
* requested voltage. This is definitely a good idea for UHS where there's a
|
||||
* separate regulator on the card that's trying to make 1.8V and it's best if
|
||||
* we match.
|
||||
*
|
||||
* This function is expected to be used by a controller's
|
||||
* start_signal_voltage_switch() function.
|
||||
*/
|
||||
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
int ret, volt, min_uV, max_uV;
|
||||
|
||||
/* If no vqmmc supply then we can't change the voltage */
|
||||
if (IS_ERR(mmc->supply.vqmmc))
|
||||
return -EINVAL;
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_120:
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
1100000, 1200000, 1300000);
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
1700000, 1800000, 1950000);
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
ret = mmc_ocrbitnum_to_vdd(mmc->ios.vdd, &volt, &max_uV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "%s: found vmmc voltage range of %d-%duV\n",
|
||||
__func__, volt, max_uV);
|
||||
|
||||
min_uV = max(volt - 300000, 2700000);
|
||||
max_uV = min(max_uV + 200000, 3600000);
|
||||
|
||||
/*
|
||||
* Due to a limitation in the current implementation of
|
||||
* regulator_set_voltage_triplet() which is taking the lowest
|
||||
* voltage possible if below the target, search for a suitable
|
||||
* voltage in two steps and try to stay close to vmmc
|
||||
* with a 0.3V tolerance at first.
|
||||
*/
|
||||
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
min_uV, volt, max_uV))
|
||||
return 0;
|
||||
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
2700000, volt, 3600000);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_vqmmc);
|
||||
|
||||
#else
|
||||
|
||||
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/**
|
||||
* mmc_regulator_get_supply - try to get VMMC and VQMMC regulators for a host
|
||||
* @mmc: the host to regulate
|
||||
*
|
||||
* Returns 0 or errno. errno should be handled, it is either a critical error
|
||||
* or -EPROBE_DEFER. 0 means no critical error but it does not mean all
|
||||
* regulators have been found because they all are optional. If you require
|
||||
* certain regulators, you need to check separately in your driver if they got
|
||||
* populated after calling this function.
|
||||
*/
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
int ret;
|
||||
|
||||
mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
|
||||
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
|
||||
|
||||
if (IS_ERR(mmc->supply.vmmc)) {
|
||||
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_dbg(dev, "No vmmc regulator found\n");
|
||||
} else {
|
||||
ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
|
||||
if (ret > 0)
|
||||
mmc->ocr_avail = ret;
|
||||
else
|
||||
dev_warn(dev, "Failed getting OCR mask: %d\n", ret);
|
||||
}
|
||||
|
||||
if (IS_ERR(mmc->supply.vqmmc)) {
|
||||
if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_dbg(dev, "No vqmmc regulator found\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
|
|
@ -209,6 +209,11 @@ static int mmc_decode_scr(struct mmc_card *card)
|
|||
/* Check if Physical Layer Spec v3.0 is supported */
|
||||
scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1);
|
||||
|
||||
if (scr->sda_spec3) {
|
||||
scr->sda_spec4 = UNSTUFF_BITS(resp, 42, 1);
|
||||
scr->sda_specx = UNSTUFF_BITS(resp, 38, 4);
|
||||
}
|
||||
|
||||
if (UNSTUFF_BITS(resp, 55, 1))
|
||||
card->erased_byte = 0xFF;
|
||||
else
|
||||
|
@ -226,6 +231,8 @@ static int mmc_read_ssr(struct mmc_card *card)
|
|||
{
|
||||
unsigned int au, es, et, eo;
|
||||
__be32 *raw_ssr;
|
||||
u32 resp[4] = {};
|
||||
u8 discard_support;
|
||||
int i;
|
||||
|
||||
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
|
||||
|
@ -271,6 +278,14 @@ static int mmc_read_ssr(struct mmc_card *card)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* starting SD5.1 discard is supported if DISCARD_SUPPORT (b313) is set
|
||||
*/
|
||||
resp[3] = card->raw_ssr[6];
|
||||
discard_support = UNSTUFF_BITS(resp, 313 - 288, 1);
|
||||
card->erase_arg = (card->scr.sda_specx && discard_support) ?
|
||||
SD_DISCARD_ARG : SD_ERASE_ARG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -936,8 +951,11 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|||
return err;
|
||||
|
||||
if (oldcard) {
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0)
|
||||
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
card = oldcard;
|
||||
} else {
|
||||
|
|
|
@ -52,36 +52,17 @@ int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_app_cmd);
|
||||
|
||||
/**
|
||||
* mmc_wait_for_app_cmd - start an application command and wait for
|
||||
completion
|
||||
* @host: MMC host to start command
|
||||
* @card: Card to send MMC_APP_CMD to
|
||||
* @cmd: MMC command to start
|
||||
* @retries: maximum number of retries
|
||||
*
|
||||
* Sends a MMC_APP_CMD, checks the card response, sends the command
|
||||
* in the parameter and waits for it to complete. Return any error
|
||||
* that occurred while the command was executing. Do not attempt to
|
||||
* parse the response.
|
||||
*/
|
||||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd, int retries)
|
||||
static int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
struct mmc_request mrq = {};
|
||||
|
||||
int i, err;
|
||||
|
||||
if (retries < 0)
|
||||
retries = MMC_CMD_RETRIES;
|
||||
|
||||
err = -EIO;
|
||||
int i, err = -EIO;
|
||||
|
||||
/*
|
||||
* We have to resend MMC_APP_CMD for each attempt so
|
||||
* we cannot use the retries field in mmc_command.
|
||||
*/
|
||||
for (i = 0;i <= retries;i++) {
|
||||
for (i = 0; i <= MMC_CMD_RETRIES; i++) {
|
||||
err = mmc_app_cmd(host, card);
|
||||
if (err) {
|
||||
/* no point in retrying; no APP commands allowed */
|
||||
|
@ -116,8 +97,6 @@ int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
|||
return err;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(mmc_wait_for_app_cmd);
|
||||
|
||||
int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
||||
{
|
||||
struct mmc_command cmd = {};
|
||||
|
@ -136,7 +115,7 @@ int mmc_app_set_bus_width(struct mmc_card *card, int width)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
return mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES);
|
||||
return mmc_wait_for_app_cmd(card->host, card, &cmd);
|
||||
}
|
||||
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
||||
|
@ -152,7 +131,7 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
|
|||
cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
|
||||
|
||||
for (i = 100; i; i--) {
|
||||
err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES);
|
||||
err = mmc_wait_for_app_cmd(host, NULL, &cmd);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
struct mmc_card;
|
||||
struct mmc_host;
|
||||
struct mmc_command;
|
||||
|
||||
int mmc_app_set_bus_width(struct mmc_card *card, int width);
|
||||
int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
|
||||
|
@ -27,8 +26,6 @@ int mmc_sd_switch(struct mmc_card *card, int mode, int group,
|
|||
u8 value, u8 *resp);
|
||||
int mmc_app_sd_status(struct mmc_card *card, void *ssr);
|
||||
int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card);
|
||||
int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card,
|
||||
struct mmc_command *cmd, int retries);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -617,6 +617,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
||||
mmc_remove_card(card);
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
}
|
||||
} else {
|
||||
|
@ -624,6 +626,8 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
|
||||
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
||||
mmc_remove_card(card);
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
@ -736,8 +740,11 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
int same = (card->cis.vendor == oldcard->cis.vendor &&
|
||||
card->cis.device == oldcard->cis.device);
|
||||
mmc_remove_card(card);
|
||||
if (!same)
|
||||
if (!same) {
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
card = oldcard;
|
||||
}
|
||||
|
|
|
@ -179,7 +179,6 @@ static int sdio_bus_remove(struct device *dev)
|
|||
{
|
||||
struct sdio_driver *drv = to_sdio_driver(dev->driver);
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* Make sure card is powered before invoking ->remove() */
|
||||
if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD)
|
||||
|
@ -205,7 +204,7 @@ static int sdio_bus_remove(struct device *dev)
|
|||
|
||||
dev_pm_domain_detach(dev, false);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
|
@ -203,6 +204,21 @@ static inline unsigned int sdio_max_byte_size(struct sdio_func *func)
|
|||
return min(mval, 512u); /* maximum size for byte mode */
|
||||
}
|
||||
|
||||
/*
|
||||
* This is legacy code, which needs to be re-worked some day. Basically we need
|
||||
* to take into account the properties of the host, as to enable the SDIO func
|
||||
* driver layer to allocate optimal buffers.
|
||||
*/
|
||||
static inline unsigned int _sdio_align_size(unsigned int sz)
|
||||
{
|
||||
/*
|
||||
* FIXME: We don't have a system for the controller to tell
|
||||
* the core about its problems yet, so for now we just 32-bit
|
||||
* align the size.
|
||||
*/
|
||||
return ALIGN(sz, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* sdio_align_size - pads a transfer size to a more optimal value
|
||||
* @func: SDIO function
|
||||
|
@ -230,7 +246,7 @@ unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
|
|||
* wants to increase the size up to a point where it
|
||||
* might need more than one block.
|
||||
*/
|
||||
sz = mmc_align_data_size(func->card, sz);
|
||||
sz = _sdio_align_size(sz);
|
||||
|
||||
/*
|
||||
* If we can still do this with just a byte transfer, then
|
||||
|
@ -252,7 +268,7 @@ unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
|
|||
*/
|
||||
blk_sz = ((sz + func->cur_blksize - 1) /
|
||||
func->cur_blksize) * func->cur_blksize;
|
||||
blk_sz = mmc_align_data_size(func->card, blk_sz);
|
||||
blk_sz = _sdio_align_size(blk_sz);
|
||||
|
||||
/*
|
||||
* This value is only good if it is still just
|
||||
|
@ -265,8 +281,7 @@ unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
|
|||
* We failed to do one request, but at least try to
|
||||
* pad the remainder properly.
|
||||
*/
|
||||
byte_sz = mmc_align_data_size(func->card,
|
||||
sz % func->cur_blksize);
|
||||
byte_sz = _sdio_align_size(sz % func->cur_blksize);
|
||||
if (byte_sz <= sdio_max_byte_size(func)) {
|
||||
blk_sz = sz / func->cur_blksize;
|
||||
return blk_sz * func->cur_blksize + byte_sz;
|
||||
|
@ -276,16 +291,14 @@ unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz)
|
|||
* We need multiple requests, so first check that the
|
||||
* controller can handle the chunk size;
|
||||
*/
|
||||
chunk_sz = mmc_align_data_size(func->card,
|
||||
sdio_max_byte_size(func));
|
||||
chunk_sz = _sdio_align_size(sdio_max_byte_size(func));
|
||||
if (chunk_sz == sdio_max_byte_size(func)) {
|
||||
/*
|
||||
* Fix up the size of the remainder (if any)
|
||||
*/
|
||||
byte_sz = orig_sz % chunk_sz;
|
||||
if (byte_sz) {
|
||||
byte_sz = mmc_align_data_size(func->card,
|
||||
byte_sz);
|
||||
byte_sz = _sdio_align_size(byte_sz);
|
||||
}
|
||||
|
||||
return (orig_sz / chunk_sz) * chunk_sz + byte_sz;
|
||||
|
|
|
@ -25,7 +25,6 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
|
|||
int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
||||
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz);
|
||||
int sdio_reset(struct mmc_host *host);
|
||||
unsigned int mmc_align_data_size(struct mmc_card *card, unsigned int sz);
|
||||
void sdio_irq_work(struct work_struct *work);
|
||||
|
||||
static inline bool sdio_is_io_busy(u32 opcode, u32 arg)
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
struct mmc_gpio {
|
||||
struct gpio_desc *ro_gpio;
|
||||
struct gpio_desc *cd_gpio;
|
||||
bool override_ro_active_level;
|
||||
bool override_cd_active_level;
|
||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||
char *ro_label;
|
||||
|
@ -71,10 +70,6 @@ int mmc_gpio_get_ro(struct mmc_host *host)
|
|||
if (!ctx || !ctx->ro_gpio)
|
||||
return -ENOSYS;
|
||||
|
||||
if (ctx->override_ro_active_level)
|
||||
return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^
|
||||
!!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
|
||||
|
||||
return gpiod_get_value_cansleep(ctx->ro_gpio);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_gpio_get_ro);
|
||||
|
@ -225,7 +220,6 @@ EXPORT_SYMBOL(mmc_can_gpio_cd);
|
|||
* @host: mmc host
|
||||
* @con_id: function within the GPIO consumer
|
||||
* @idx: index of the GPIO to obtain in the consumer
|
||||
* @override_active_level: ignore %GPIO_ACTIVE_LOW flag
|
||||
* @debounce: debounce time in microseconds
|
||||
* @gpio_invert: will return whether the GPIO line is inverted or not,
|
||||
* set to NULL to ignore
|
||||
|
@ -233,7 +227,7 @@ EXPORT_SYMBOL(mmc_can_gpio_cd);
|
|||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int idx,
|
||||
unsigned int debounce, bool *gpio_invert)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
|
@ -253,7 +247,6 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
|||
if (gpio_invert)
|
||||
*gpio_invert = !gpiod_is_active_low(desc);
|
||||
|
||||
ctx->override_ro_active_level = override_active_level;
|
||||
ctx->ro_gpio = desc;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -224,6 +224,7 @@ config MMC_SDHCI_ESDHC_IMX
|
|||
depends on ARCH_MXC
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
select MMC_CQHCI
|
||||
help
|
||||
This selects the Freescale eSDHC/uSDHC controller support
|
||||
found on i.MX25, i.MX35 i.MX5x and i.MX6x.
|
||||
|
@ -250,6 +251,7 @@ config MMC_SDHCI_TEGRA
|
|||
depends on ARCH_TEGRA
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
select MMC_CQHCI
|
||||
help
|
||||
This selects the Tegra SD/MMC controller. If you have a Tegra
|
||||
platform with SD or MMC devices, say Y or M here.
|
||||
|
|
|
@ -1410,6 +1410,9 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
case MMC_BUS_WIDTH_4:
|
||||
slot->sdc_reg |= ATMCI_SDCBUS_4BIT;
|
||||
break;
|
||||
case MMC_BUS_WIDTH_8:
|
||||
slot->sdc_reg |= ATMCI_SDCBUS_8BIT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ios->clock) {
|
||||
|
@ -2275,8 +2278,11 @@ static int atmci_init_slot(struct atmel_mci *host,
|
|||
* use only one bit for data to prevent fifo underruns and overruns
|
||||
* which will corrupt data.
|
||||
*/
|
||||
if ((slot_data->bus_width >= 4) && host->caps.has_rwproof)
|
||||
if ((slot_data->bus_width >= 4) && host->caps.has_rwproof) {
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
if (slot_data->bus_width >= 8)
|
||||
mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
}
|
||||
|
||||
if (atmci_get_version(host) < 0x200) {
|
||||
mmc->max_segs = 256;
|
||||
|
|
|
@ -148,7 +148,6 @@ struct bcm2835_host {
|
|||
void __iomem *ioaddr;
|
||||
u32 phys_addr;
|
||||
|
||||
struct mmc_host *mmc;
|
||||
struct platform_device *pdev;
|
||||
|
||||
int clock; /* Current clock speed */
|
||||
|
@ -618,7 +617,7 @@ static void bcm2835_finish_request(struct bcm2835_host *host)
|
|||
"failed to terminate DMA (%d)\n", err);
|
||||
}
|
||||
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
mmc_request_done(mmc_from_priv(host), mrq);
|
||||
}
|
||||
|
||||
static
|
||||
|
@ -837,7 +836,7 @@ static void bcm2835_timeout(struct work_struct *work)
|
|||
dev_err(dev, "timeout waiting for hardware interrupt.\n");
|
||||
bcm2835_dumpregs(host);
|
||||
|
||||
bcm2835_reset(host->mmc);
|
||||
bcm2835_reset(mmc_from_priv(host));
|
||||
|
||||
if (host->data) {
|
||||
host->data->error = -ETIMEDOUT;
|
||||
|
@ -1100,6 +1099,7 @@ static void bcm2835_dma_complete_work(struct work_struct *work)
|
|||
|
||||
static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
|
||||
{
|
||||
struct mmc_host *mmc = mmc_from_priv(host);
|
||||
int div;
|
||||
|
||||
/* The SDCDIV register has 11 bits, and holds (div - 2). But
|
||||
|
@ -1143,18 +1143,18 @@ static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock)
|
|||
div = SDCDIV_MAX_CDIV;
|
||||
|
||||
clock = host->max_clk / (div + 2);
|
||||
host->mmc->actual_clock = clock;
|
||||
mmc->actual_clock = clock;
|
||||
|
||||
/* Calibrate some delays */
|
||||
|
||||
host->ns_per_fifo_word = (1000000000 / clock) *
|
||||
((host->mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32);
|
||||
((mmc->caps & MMC_CAP_4_BIT_DATA) ? 8 : 32);
|
||||
|
||||
host->cdiv = div;
|
||||
writel(host->cdiv, host->ioaddr + SDCDIV);
|
||||
|
||||
/* Set the timeout to 500ms */
|
||||
writel(host->mmc->actual_clock / 2, host->ioaddr + SDTOUT);
|
||||
writel(mmc->actual_clock / 2, host->ioaddr + SDTOUT);
|
||||
}
|
||||
|
||||
static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
|
@ -1264,7 +1264,7 @@ static const struct mmc_host_ops bcm2835_ops = {
|
|||
|
||||
static int bcm2835_add_host(struct bcm2835_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct mmc_host *mmc = mmc_from_priv(host);
|
||||
struct device *dev = &host->pdev->dev;
|
||||
char pio_limit_string[20];
|
||||
int ret;
|
||||
|
@ -1286,7 +1286,7 @@ static int bcm2835_add_host(struct bcm2835_host *host)
|
|||
spin_lock_init(&host->lock);
|
||||
mutex_init(&host->mutex);
|
||||
|
||||
if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) {
|
||||
if (!host->dma_chan_rxtx) {
|
||||
dev_warn(dev, "unable to initialise DMA channel. Falling back to PIO\n");
|
||||
host->use_dma = false;
|
||||
} else {
|
||||
|
@ -1370,7 +1370,6 @@ static int bcm2835_probe(struct platform_device *pdev)
|
|||
|
||||
mmc->ops = &bcm2835_ops;
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->pdev = pdev;
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
|
@ -1441,8 +1440,9 @@ static int bcm2835_probe(struct platform_device *pdev)
|
|||
static int bcm2835_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bcm2835_host *host = platform_get_drvdata(pdev);
|
||||
struct mmc_host *mmc = mmc_from_priv(host);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
writel(SDVDD_POWER_OFF, host->ioaddr + SDVDD);
|
||||
|
||||
|
@ -1454,8 +1454,7 @@ static int bcm2835_remove(struct platform_device *pdev)
|
|||
if (host->dma_chan_rxtx)
|
||||
dma_release_channel(host->dma_chan_rxtx);
|
||||
|
||||
mmc_free_host(host->mmc);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -566,30 +566,32 @@ static void cb710_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
cb710_mmc_select_clock_divider(mmc, ios->clock);
|
||||
|
||||
if (ios->power_mode != reader->last_power_mode)
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_ON:
|
||||
err = cb710_mmc_powerup(slot);
|
||||
if (err) {
|
||||
dev_warn(cb710_slot_dev(slot),
|
||||
"powerup failed (%d)- retrying\n", err);
|
||||
cb710_mmc_powerdown(slot);
|
||||
udelay(1);
|
||||
if (ios->power_mode != reader->last_power_mode) {
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_ON:
|
||||
err = cb710_mmc_powerup(slot);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_warn(cb710_slot_dev(slot),
|
||||
"powerup retry failed (%d) - expect errors\n",
|
||||
"powerup failed (%d)- retrying\n", err);
|
||||
cb710_mmc_powerdown(slot);
|
||||
udelay(1);
|
||||
err = cb710_mmc_powerup(slot);
|
||||
if (err)
|
||||
dev_warn(cb710_slot_dev(slot),
|
||||
"powerup retry failed (%d) - expect errors\n",
|
||||
err);
|
||||
}
|
||||
reader->last_power_mode = MMC_POWER_ON;
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
cb710_mmc_powerdown(slot);
|
||||
reader->last_power_mode = MMC_POWER_OFF;
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
default:
|
||||
/* ignore */
|
||||
break;
|
||||
}
|
||||
reader->last_power_mode = MMC_POWER_ON;
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
cb710_mmc_powerdown(slot);
|
||||
reader->last_power_mode = MMC_POWER_OFF;
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
default:
|
||||
/* ignore */;
|
||||
}
|
||||
|
||||
cb710_mmc_enable_4bit_data(slot, ios->bus_width != MMC_BUS_WIDTH_1);
|
||||
|
|
|
@ -1193,7 +1193,7 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc)
|
|||
else if (ret)
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
|
@ -36,7 +35,6 @@
|
|||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <asm/mach-jz4740/dma.h>
|
||||
#include <asm/mach-jz4740/jz4740_mmc.h>
|
||||
|
||||
#define JZ_REG_MMC_STRPCL 0x00
|
||||
#define JZ_REG_MMC_STATUS 0x04
|
||||
|
@ -148,9 +146,7 @@ enum jz4780_cookie {
|
|||
struct jz4740_mmc_host {
|
||||
struct mmc_host *mmc;
|
||||
struct platform_device *pdev;
|
||||
struct jz4740_mmc_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
struct gpio_desc *power;
|
||||
|
||||
enum jz4740_mmc_version version;
|
||||
|
||||
|
@ -743,6 +739,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
|
|||
break;
|
||||
|
||||
jz_mmc_prepare_data_transfer(host);
|
||||
/* fall through */
|
||||
|
||||
case JZ4740_MMC_STATE_TRANSFER_DATA:
|
||||
if (host->use_dma) {
|
||||
|
@ -777,6 +774,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
|
|||
break;
|
||||
}
|
||||
jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
|
||||
/* fall through */
|
||||
|
||||
case JZ4740_MMC_STATE_SEND_STOP:
|
||||
if (!req->stop)
|
||||
|
@ -894,16 +892,16 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
jz4740_mmc_reset(host);
|
||||
if (host->power)
|
||||
gpiod_set_value(host->power, 1);
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
host->cmdat |= JZ_MMC_CMDAT_INIT;
|
||||
clk_prepare_enable(host->clk);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
break;
|
||||
default:
|
||||
if (host->power)
|
||||
gpiod_set_value(host->power, 0);
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
clk_disable_unprepare(host->clk);
|
||||
break;
|
||||
}
|
||||
|
@ -936,38 +934,6 @@ static const struct mmc_host_ops jz4740_mmc_ops = {
|
|||
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static int jz4740_mmc_request_gpios(struct jz4740_mmc_host *host,
|
||||
struct mmc_host *mmc,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
int ret = 0;
|
||||
|
||||
if (!pdata)
|
||||
return 0;
|
||||
|
||||
if (!pdata->card_detect_active_low)
|
||||
mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
if (!pdata->read_only_active_low)
|
||||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
/*
|
||||
* Get optional card detect and write protect GPIOs,
|
||||
* only back out on probe deferral.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
host->power = devm_gpiod_get_optional(&pdev->dev, "power",
|
||||
GPIOD_OUT_HIGH);
|
||||
return PTR_ERR_OR_ZERO(host->power);
|
||||
}
|
||||
|
||||
static const struct of_device_id jz4740_mmc_of_match[] = {
|
||||
{ .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 },
|
||||
{ .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B },
|
||||
|
@ -982,9 +948,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
struct mmc_host *mmc;
|
||||
struct jz4740_mmc_host *host;
|
||||
const struct of_device_id *match;
|
||||
struct jz4740_mmc_platform_data *pdata;
|
||||
|
||||
pdata = dev_get_platdata(&pdev->dev);
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
|
||||
if (!mmc) {
|
||||
|
@ -993,29 +956,25 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
}
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->pdata = pdata;
|
||||
|
||||
match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
|
||||
if (match) {
|
||||
host->version = (enum jz4740_mmc_version)match->data;
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"could not parse of data: %d\n", ret);
|
||||
goto err_free_host;
|
||||
}
|
||||
} else {
|
||||
/* JZ4740 should be the only one using legacy probe */
|
||||
host->version = JZ_MMC_JZ4740;
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ;
|
||||
if (!(pdata && pdata->data_1bit))
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
ret = jz4740_mmc_request_gpios(host, mmc, pdev);
|
||||
if (ret)
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev,
|
||||
"could not parse device properties: %d\n", ret);
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
mmc_regulator_get_supply(mmc);
|
||||
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0) {
|
||||
ret = host->irq;
|
||||
|
|
|
@ -1453,7 +1453,7 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
mmc_detect_change(mmc, 0);
|
||||
|
||||
/* Index 1 is write protect/read only */
|
||||
status = mmc_gpiod_request_ro(mmc, NULL, 1, false, 0, NULL);
|
||||
status = mmc_gpiod_request_ro(mmc, NULL, 1, 0, NULL);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto fail_add_host;
|
||||
if (!status)
|
||||
|
|
|
@ -1127,6 +1127,12 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
|
|||
writel(c, base + MMCICOMMAND);
|
||||
}
|
||||
|
||||
static void mmci_stop_command(struct mmci_host *host)
|
||||
{
|
||||
host->stop_abort.error = 0;
|
||||
mmci_start_command(host, &host->stop_abort, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
||||
unsigned int status)
|
||||
|
@ -1196,10 +1202,16 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
|
|||
/* The error clause is handled above, success! */
|
||||
data->bytes_xfered = data->blksz * data->blocks;
|
||||
|
||||
if (!data->stop || (host->mrq->sbc && !data->error))
|
||||
if (!data->stop) {
|
||||
if (host->variant->cmdreg_stop && data->error)
|
||||
mmci_stop_command(host);
|
||||
else
|
||||
mmci_request_end(host, data->mrq);
|
||||
} else if (host->mrq->sbc && !data->error) {
|
||||
mmci_request_end(host, data->mrq);
|
||||
else
|
||||
} else {
|
||||
mmci_start_command(host, data->stop, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1298,6 +1310,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||
mmci_dma_error(host);
|
||||
|
||||
mmci_stop_data(host);
|
||||
if (host->variant->cmdreg_stop && cmd->error) {
|
||||
mmci_stop_command(host);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mmci_request_end(host, host->mrq);
|
||||
} else if (sbc) {
|
||||
|
@ -1956,6 +1972,11 @@ static int mmci_probe(struct amba_device *dev,
|
|||
mmc->max_busy_timeout = 0;
|
||||
}
|
||||
|
||||
/* Prepare a CMD12 - needed to clear the DPSM on some variants. */
|
||||
host->stop_abort.opcode = MMC_STOP_TRANSMISSION;
|
||||
host->stop_abort.arg = 0;
|
||||
host->stop_abort.flags = MMC_RSP_R1B | MMC_CMD_AC;
|
||||
|
||||
mmc->ops = &mmci_ops;
|
||||
|
||||
/* We support these PM capabilities. */
|
||||
|
@ -2011,7 +2032,7 @@ static int mmci_probe(struct amba_device *dev,
|
|||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_disable;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_disable;
|
||||
}
|
||||
|
|
|
@ -377,6 +377,7 @@ struct mmci_host {
|
|||
void __iomem *base;
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
struct mmc_command stop_abort;
|
||||
struct mmc_data *data;
|
||||
struct mmc_host *mmc;
|
||||
struct clk *clk;
|
||||
|
|
|
@ -31,14 +31,12 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -39,7 +38,6 @@
|
|||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stmp_device.h>
|
||||
|
|
|
@ -61,9 +61,6 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
|||
struct device *dev = &spi->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct of_mmc_spi *oms;
|
||||
const __be32 *voltage_ranges;
|
||||
int num_ranges;
|
||||
int i;
|
||||
|
||||
if (dev->platform_data || !np)
|
||||
return dev->platform_data;
|
||||
|
@ -72,25 +69,8 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi)
|
|||
if (!oms)
|
||||
return NULL;
|
||||
|
||||
voltage_ranges = of_get_property(np, "voltage-ranges", &num_ranges);
|
||||
num_ranges = num_ranges / sizeof(*voltage_ranges) / 2;
|
||||
if (!voltage_ranges || !num_ranges) {
|
||||
dev_err(dev, "OF: voltage-ranges unspecified\n");
|
||||
if (mmc_of_parse_voltage(np, &oms->pdata.ocr_mask) <= 0)
|
||||
goto err_ocr;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_ranges; i++) {
|
||||
const int j = i * 2;
|
||||
u32 mask;
|
||||
|
||||
mask = mmc_vddrange_to_ocrmask(be32_to_cpu(voltage_ranges[j]),
|
||||
be32_to_cpu(voltage_ranges[j + 1]));
|
||||
if (!mask) {
|
||||
dev_err(dev, "OF: voltage-range #%d is invalid\n", i);
|
||||
goto err_ocr;
|
||||
}
|
||||
oms->pdata.ocr_mask |= mask;
|
||||
}
|
||||
|
||||
oms->detect_irq = irq_of_parse_and_map(np, 0);
|
||||
if (oms->detect_irq != 0) {
|
||||
|
|
|
@ -920,7 +920,7 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques
|
|||
reg &= ~(1 << 5);
|
||||
OMAP_MMC_WRITE(host, SDIO, reg);
|
||||
/* Set maximum timeout */
|
||||
OMAP_MMC_WRITE(host, CTO, 0xff);
|
||||
OMAP_MMC_WRITE(host, CTO, 0xfd);
|
||||
}
|
||||
|
||||
static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
|
||||
|
|
|
@ -743,7 +743,7 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
}
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
if (ret && ret != -ENOENT) {
|
||||
dev_err(dev, "Failed requesting gpio_ro\n");
|
||||
goto out;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
struct renesas_sdhi_scc {
|
||||
unsigned long clk_rate; /* clock rate for SDR104 */
|
||||
u32 tap; /* sampling clock position for SDR104 */
|
||||
u32 tap_hs400; /* sampling clock position for HS400 */
|
||||
};
|
||||
|
||||
struct renesas_sdhi_of_data {
|
||||
|
@ -49,6 +50,7 @@ struct renesas_sdhi {
|
|||
struct pinctrl_state *pins_default, *pins_uhs;
|
||||
void __iomem *scc_ctl;
|
||||
u32 scc_tappos;
|
||||
u32 scc_tappos_hs400;
|
||||
};
|
||||
|
||||
#define host_to_priv(host) \
|
||||
|
|
|
@ -337,6 +337,10 @@ static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host)
|
|||
/* Set HS400 mode */
|
||||
sd_ctrl_write16(host, CTL_SDIF_MODE, 0x0001 |
|
||||
sd_ctrl_read16(host, CTL_SDIF_MODE));
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF,
|
||||
priv->scc_tappos_hs400);
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
|
||||
(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
|
||||
SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) |
|
||||
|
@ -396,6 +400,9 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
|
|||
/* Reset HS400 mode */
|
||||
sd_ctrl_write16(host, CTL_SDIF_MODE, ~0x0001 &
|
||||
sd_ctrl_read16(host, CTL_SDIF_MODE));
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos);
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2,
|
||||
~(SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN |
|
||||
SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
|
||||
|
@ -723,6 +730,13 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
host->ops.start_signal_voltage_switch =
|
||||
renesas_sdhi_start_signal_voltage_switch;
|
||||
host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27;
|
||||
|
||||
/* SDR and HS200/400 registers requires HW reset */
|
||||
if (of_data && of_data->scc_offset) {
|
||||
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
||||
host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
host->hw_reset = renesas_sdhi_hw_reset;
|
||||
}
|
||||
}
|
||||
|
||||
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
|
||||
|
@ -775,12 +789,11 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
const struct renesas_sdhi_scc *taps = of_data->taps;
|
||||
bool hit = false;
|
||||
|
||||
host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||
|
||||
for (i = 0; i < of_data->taps_num; i++) {
|
||||
if (taps[i].clk_rate == 0 ||
|
||||
taps[i].clk_rate == host->mmc->f_max) {
|
||||
priv->scc_tappos = taps->tap;
|
||||
priv->scc_tappos_hs400 = taps->tap_hs400;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
|
@ -789,12 +802,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
if (!hit)
|
||||
dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
|
||||
|
||||
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
||||
host->init_tuning = renesas_sdhi_init_tuning;
|
||||
host->prepare_tuning = renesas_sdhi_prepare_tuning;
|
||||
host->select_tuning = renesas_sdhi_select_tuning;
|
||||
host->check_scc_error = renesas_sdhi_check_scc_error;
|
||||
host->hw_reset = renesas_sdhi_hw_reset;
|
||||
host->prepare_hs400_tuning =
|
||||
renesas_sdhi_prepare_hs400_tuning;
|
||||
host->hs400_downgrade = renesas_sdhi_disable_scc;
|
||||
|
|
|
@ -81,6 +81,7 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
|||
{
|
||||
.clk_rate = 0,
|
||||
.tap = 0x00000300,
|
||||
.tap_hs400 = 0x00000704,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1530,7 +1530,7 @@ static int s3cmci_probe_pdata(struct s3cmci_host *host)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0, NULL);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
|
||||
ret);
|
||||
|
|
|
@ -18,12 +18,10 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
|
|
@ -55,7 +55,9 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
mmc_of_parse(host->mmc);
|
||||
res = mmc_of_parse(host->mmc);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Supply the existing CAPS, but clear the UHS modes. This
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci-esdhc.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f
|
||||
#define ESDHC_CTRL_D3CD 0x08
|
||||
|
@ -50,6 +51,7 @@
|
|||
#define ESDHC_MIX_CTRL_AUTO_TUNE_EN (1 << 24)
|
||||
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
||||
#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
|
||||
#define ESDHC_MIX_CTRL_HS400_ES_EN (1 << 27)
|
||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
|
||||
/* Tuning bits */
|
||||
|
@ -76,6 +78,9 @@
|
|||
#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
|
||||
#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
|
||||
|
||||
#define ESDHC_VEND_SPEC2 0xc8
|
||||
#define ESDHC_VEND_SPEC2_EN_BUSY_IRQ (1 << 8)
|
||||
|
||||
#define ESDHC_TUNING_CTRL 0xcc
|
||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||
|
@ -103,6 +108,9 @@
|
|||
*/
|
||||
#define ESDHC_INT_VENDOR_SPEC_DMA_ERR (1 << 28)
|
||||
|
||||
/* the address offset of CQHCI */
|
||||
#define ESDHC_CQHCI_ADDR_OFFSET 0x100
|
||||
|
||||
/*
|
||||
* The CMDTYPE of the CMD register (offset 0xE) should be set to
|
||||
* "11" when the STOP CMD12 is issued on imx53 to abort one
|
||||
|
@ -138,51 +146,71 @@
|
|||
#define ESDHC_FLAG_HS200 BIT(8)
|
||||
/* The IP supports HS400 mode */
|
||||
#define ESDHC_FLAG_HS400 BIT(9)
|
||||
|
||||
/* A clock frequency higher than this rate requires strobe dll control */
|
||||
#define ESDHC_STROBE_DLL_CLK_FREQ 100000000
|
||||
/*
|
||||
* The IP has errata ERR010450
|
||||
* uSDHC: Due to the I/O timing limit, for SDR mode, SD card clock can't
|
||||
* exceed 150MHz, for DDR mode, SD card clock can't exceed 45MHz.
|
||||
*/
|
||||
#define ESDHC_FLAG_ERR010450 BIT(10)
|
||||
/* The IP supports HS400ES mode */
|
||||
#define ESDHC_FLAG_HS400_ES BIT(11)
|
||||
/* The IP has Host Controller Interface for Command Queuing */
|
||||
#define ESDHC_FLAG_CQHCI BIT(12)
|
||||
|
||||
struct esdhc_soc_data {
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data esdhc_imx25_data = {
|
||||
static const struct esdhc_soc_data esdhc_imx25_data = {
|
||||
.flags = ESDHC_FLAG_ERR004536,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data esdhc_imx35_data = {
|
||||
static const struct esdhc_soc_data esdhc_imx35_data = {
|
||||
.flags = ESDHC_FLAG_ERR004536,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data esdhc_imx51_data = {
|
||||
static const struct esdhc_soc_data esdhc_imx51_data = {
|
||||
.flags = 0,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data esdhc_imx53_data = {
|
||||
static const struct esdhc_soc_data esdhc_imx53_data = {
|
||||
.flags = ESDHC_FLAG_MULTIBLK_NO_INT,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx6q_data = {
|
||||
static const struct esdhc_soc_data usdhc_imx6q_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx6sl_data = {
|
||||
static const struct esdhc_soc_data usdhc_imx6sl_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
|
||||
| ESDHC_FLAG_HS200,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx6sx_data = {
|
||||
static const struct esdhc_soc_data usdhc_imx6sx_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx7d_data = {
|
||||
static const struct esdhc_soc_data usdhc_imx6ull_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_ERR010450,
|
||||
};
|
||||
|
||||
static const struct esdhc_soc_data usdhc_imx7d_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx8qxp_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES
|
||||
| ESDHC_FLAG_CQHCI,
|
||||
};
|
||||
|
||||
struct pltfm_imx_data {
|
||||
u32 scratchpad;
|
||||
struct pinctrl *pinctrl;
|
||||
|
@ -227,7 +255,9 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
|
||||
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
|
||||
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
|
||||
{ .compatible = "fsl,imx6ull-usdhc", .data = &usdhc_imx6ull_data, },
|
||||
{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
|
||||
{ .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
||||
|
@ -733,6 +763,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
|||
| ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_ERR010450) {
|
||||
unsigned int max_clock;
|
||||
|
||||
max_clock = imx_data->is_ddr ? 45000000 : 150000000;
|
||||
|
||||
clock = min(clock, max_clock);
|
||||
}
|
||||
|
||||
while (host_clock / (16 * pre_div * ddr_pre_div) > clock &&
|
||||
pre_div < 256)
|
||||
pre_div *= 2;
|
||||
|
@ -801,6 +839,20 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
|||
SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
/*
|
||||
* i.MX uSDHC internally already uses a fixed optimized timing for
|
||||
* DDR50, normally does not require tuning for DDR50 mode.
|
||||
*/
|
||||
if (host->timing == MMC_TIMING_UHS_DDR50)
|
||||
return 0;
|
||||
|
||||
return sdhci_execute_tuning(mmc, opcode);
|
||||
}
|
||||
|
||||
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
||||
{
|
||||
u32 reg;
|
||||
|
@ -864,6 +916,19 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void esdhc_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
u32 m;
|
||||
|
||||
m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
if (ios->enhanced_strobe)
|
||||
m |= ESDHC_MIX_CTRL_HS400_ES_EN;
|
||||
else
|
||||
m &= ~ESDHC_MIX_CTRL_HS400_ES_EN;
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
}
|
||||
|
||||
static int esdhc_change_pinstate(struct sdhci_host *host,
|
||||
unsigned int uhs)
|
||||
{
|
||||
|
@ -905,39 +970,35 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
* edge of data_strobe line. Due to the time delay between CLK line and
|
||||
* data_strobe line, if the delay time is larger than one clock cycle,
|
||||
* then CLK and data_strobe line will be misaligned, read error shows up.
|
||||
* So when the CLK is higher than 100MHz, each clock cycle is short enough,
|
||||
* host should configure the delay target.
|
||||
*/
|
||||
static void esdhc_set_strobe_dll(struct sdhci_host *host)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
|
||||
/* disable clock before enabling strobe dll */
|
||||
writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
|
||||
~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
|
||||
host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
/* disable clock before enabling strobe dll */
|
||||
writel(readl(host->ioaddr + ESDHC_VENDOR_SPEC) &
|
||||
~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
|
||||
host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
|
||||
/* force a reset on strobe dll */
|
||||
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
||||
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/*
|
||||
* enable strobe dll ctrl and adjust the delay target
|
||||
* for the uSDHC loopback read clock
|
||||
*/
|
||||
v = ESDHC_STROBE_DLL_CTRL_ENABLE |
|
||||
(7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
|
||||
writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/* wait 1us to make sure strobe dll status register stable */
|
||||
udelay(1);
|
||||
v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status REF not lock!\n");
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status SLV not lock!\n");
|
||||
}
|
||||
/* force a reset on strobe dll */
|
||||
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
||||
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/*
|
||||
* enable strobe dll ctrl and adjust the delay target
|
||||
* for the uSDHC loopback read clock
|
||||
*/
|
||||
v = ESDHC_STROBE_DLL_CTRL_ENABLE |
|
||||
(7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
|
||||
writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/* wait 1us to make sure strobe dll status register stable */
|
||||
udelay(1);
|
||||
v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status REF not lock!\n");
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status SLV not lock!\n");
|
||||
}
|
||||
|
||||
static void esdhc_reset_tuning(struct sdhci_host *host)
|
||||
|
@ -979,6 +1040,7 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
|||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
break;
|
||||
|
@ -1042,6 +1104,19 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
SDHCI_TIMEOUT_CONTROL);
|
||||
}
|
||||
|
||||
static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
int data_error = 0;
|
||||
|
||||
if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
|
||||
return intmask;
|
||||
|
||||
cqhci_irq(host->mmc, intmask, cmd_error, data_error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.read_l = esdhc_readl_le,
|
||||
.read_w = esdhc_readw_le,
|
||||
|
@ -1058,6 +1133,7 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.set_uhs_signaling = esdhc_set_uhs_signaling,
|
||||
.reset = esdhc_reset,
|
||||
.irq = esdhc_cqhci_irq,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||
|
@ -1106,6 +1182,23 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
|||
/* disable DLL_CTRL delay line settings */
|
||||
writel(0x0, host->ioaddr + ESDHC_DLL_CTRL);
|
||||
|
||||
/*
|
||||
* For the case of command with busy, if set the bit
|
||||
* ESDHC_VEND_SPEC2_EN_BUSY_IRQ, USDHC will generate a
|
||||
* transfer complete interrupt when busy is deasserted.
|
||||
* When CQHCI use DCMD to send a CMD need R1b respons,
|
||||
* CQHCI require to set ESDHC_VEND_SPEC2_EN_BUSY_IRQ,
|
||||
* otherwise DCMD will always meet timeout waiting for
|
||||
* hardware interrupt issue.
|
||||
*/
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
|
||||
tmp = readl(host->ioaddr + ESDHC_VEND_SPEC2);
|
||||
tmp |= ESDHC_VEND_SPEC2_EN_BUSY_IRQ;
|
||||
writel(tmp, host->ioaddr + ESDHC_VEND_SPEC2);
|
||||
|
||||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
tmp |= ESDHC_STD_TUNING_EN |
|
||||
|
@ -1121,10 +1214,81 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
|||
<< ESDHC_TUNING_STEP_SHIFT;
|
||||
}
|
||||
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||
/*
|
||||
* ESDHC_STD_TUNING_EN may be configed in bootloader
|
||||
* or ROM code, so clear this bit here to make sure
|
||||
* the manual tuning can work.
|
||||
*/
|
||||
tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
tmp &= ~ESDHC_STD_TUNING_EN;
|
||||
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void esdhc_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
u32 reg;
|
||||
u16 mode;
|
||||
int count = 10;
|
||||
|
||||
/*
|
||||
* CQE gets stuck if it sees Buffer Read Enable bit set, which can be
|
||||
* the case after tuning, so ensure the buffer is drained.
|
||||
*/
|
||||
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
while (reg & SDHCI_DATA_AVAILABLE) {
|
||||
sdhci_readl(host, SDHCI_BUFFER);
|
||||
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
if (count-- == 0) {
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"CQE may get stuck because the Buffer Read Enable bit is set\n");
|
||||
break;
|
||||
}
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Runtime resume will reset the entire host controller, which
|
||||
* will also clear the DMAEN/BCEN of register ESDHC_MIX_CTRL.
|
||||
* Here set DMAEN and BCEN when enable CMDQ.
|
||||
*/
|
||||
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
|
||||
if (host->flags & SDHCI_REQ_USE_DMA)
|
||||
mode |= SDHCI_TRNS_DMA;
|
||||
if (!(host->quirks2 & SDHCI_QUIRK2_SUPPORT_SINGLE))
|
||||
mode |= SDHCI_TRNS_BLK_CNT_EN;
|
||||
sdhci_writew(host, mode, SDHCI_TRANSFER_MODE);
|
||||
|
||||
/*
|
||||
* Though Runtime resume reset the entire host controller,
|
||||
* but do not impact the CQHCI side, need to clear the
|
||||
* HALT bit, avoid CQHCI stuck in the first request when
|
||||
* system resume back.
|
||||
*/
|
||||
cqhci_writel(cq_host, 0, CQHCI_CTL);
|
||||
if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to exit halt state when enable CQE\n");
|
||||
|
||||
|
||||
sdhci_cqe_enable(mmc);
|
||||
}
|
||||
|
||||
static void esdhc_sdhci_dumpregs(struct mmc_host *mmc)
|
||||
{
|
||||
sdhci_dumpregs(mmc_priv(mmc));
|
||||
}
|
||||
|
||||
static const struct cqhci_host_ops esdhc_cqhci_ops = {
|
||||
.enable = esdhc_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
.dumpregs = esdhc_sdhci_dumpregs,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int
|
||||
sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
|
@ -1201,7 +1365,7 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
|
|||
host->mmc->parent->platform_data);
|
||||
/* write_protect */
|
||||
if (boarddata->wp_type == ESDHC_WP_GPIO) {
|
||||
err = mmc_gpiod_request_ro(host->mmc, "wp", 0, false, 0, NULL);
|
||||
err = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0, NULL);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request write-protect gpio!\n");
|
||||
|
@ -1256,6 +1420,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
of_match_device(imx_esdhc_dt_ids, &pdev->dev);
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
struct cqhci_host *cq_host;
|
||||
int err;
|
||||
struct pltfm_imx_data *imx_data;
|
||||
|
||||
|
@ -1322,6 +1487,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
writel(0x0, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
writel(0x0, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
|
||||
writel(0x0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
|
||||
|
||||
/*
|
||||
* Link usdhc specific mmc_host_ops execute_tuning function,
|
||||
* to replace the standard one in sdhci_ops.
|
||||
*/
|
||||
host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
|
||||
|
@ -1334,6 +1505,28 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||
host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400_ES) {
|
||||
host->mmc->caps2 |= MMC_CAP2_HS400_ES;
|
||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||
esdhc_hs400_enhanced_strobe;
|
||||
}
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_CQHCI) {
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL);
|
||||
if (!cq_host) {
|
||||
err = -ENOMEM;
|
||||
goto disable_ahb_clk;
|
||||
}
|
||||
|
||||
cq_host->mmio = host->ioaddr + ESDHC_CQHCI_ADDR_OFFSET;
|
||||
cq_host->ops = &esdhc_cqhci_ops;
|
||||
|
||||
err = cqhci_init(cq_host, host->mmc, false);
|
||||
if (err)
|
||||
goto disable_ahb_clk;
|
||||
}
|
||||
|
||||
if (of_id)
|
||||
err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
|
||||
else
|
||||
|
@ -1341,6 +1534,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto disable_ahb_clk;
|
||||
|
||||
host->tuning_delay = 1;
|
||||
|
||||
sdhci_esdhc_imx_hwinit(host);
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
|
@ -1392,6 +1587,13 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
|
|||
static int sdhci_esdhc_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
ret = cqhci_suspend(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
|
||||
mmc_retune_needed(host->mmc);
|
||||
|
@ -1402,11 +1604,19 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
|||
static int sdhci_esdhc_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* re-initialize hw state in case it's lost in low power mode */
|
||||
sdhci_esdhc_imx_hwinit(host);
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
ret = sdhci_resume_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||
ret = cqhci_resume(host->mmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1418,6 +1628,12 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
|||
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
ret = cqhci_suspend(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1461,7 +1677,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
|
|||
if (err)
|
||||
goto disable_ipg_clk;
|
||||
|
||||
return 0;
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||
err = cqhci_resume(host->mmc);
|
||||
|
||||
return err;
|
||||
|
||||
disable_ipg_clk:
|
||||
if (!sdhci_sdio_irq_enabled(host))
|
||||
|
|
|
@ -1097,7 +1097,6 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||
goto err_put_sync;
|
||||
}
|
||||
|
||||
host->mmc_host_ops.get_ro = mmc_gpio_get_ro;
|
||||
host->mmc_host_ops.start_signal_voltage_switch =
|
||||
sdhci_omap_start_signal_voltage_switch;
|
||||
host->mmc_host_ops.set_ios = sdhci_omap_set_ios;
|
||||
|
|
|
@ -1257,16 +1257,6 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
|
|||
}
|
||||
#endif
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_o2 = {
|
||||
.probe = sdhci_pci_o2_probe,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD,
|
||||
.probe_slot = sdhci_pci_o2_probe_slot,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.resume = sdhci_pci_o2_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct sdhci_pci_fixes sdhci_jmicron = {
|
||||
.probe = jmicron_probe,
|
||||
|
||||
|
|
|
@ -60,6 +60,13 @@
|
|||
#define O2_SD_VENDOR_SETTING2 0x1C8
|
||||
#define O2_SD_HW_TUNING_DISABLE BIT(4)
|
||||
|
||||
#define O2_PLL_WDT_CONTROL1 0x1CC
|
||||
#define O2_PLL_FORCE_ACTIVE BIT(18)
|
||||
#define O2_PLL_LOCK_STATUS BIT(14)
|
||||
#define O2_PLL_SOFT_RESET BIT(12)
|
||||
|
||||
#define O2_SD_DETECT_SETTING 0x324
|
||||
|
||||
static void sdhci_o2_set_tuning_mode(struct sdhci_host *host)
|
||||
{
|
||||
u16 reg;
|
||||
|
@ -283,6 +290,113 @@ static void sdhci_pci_o2_enable_msi(struct sdhci_pci_chip *chip,
|
|||
host->irq = pci_irq_vector(chip->pdev, 0);
|
||||
}
|
||||
|
||||
static void sdhci_o2_wait_card_detect_stable(struct sdhci_host *host)
|
||||
{
|
||||
ktime_t timeout;
|
||||
u32 scratch32;
|
||||
|
||||
/* Wait max 50 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 50);
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
scratch32 = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
if ((scratch32 & SDHCI_CARD_PRESENT) >> SDHCI_CARD_PRES_SHIFT
|
||||
== (scratch32 & SDHCI_CD_LVL) >> SDHCI_CD_LVL_SHIFT)
|
||||
break;
|
||||
|
||||
if (timedout) {
|
||||
pr_err("%s: Card Detect debounce never finished.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_o2_enable_internal_clock(struct sdhci_host *host)
|
||||
{
|
||||
ktime_t timeout;
|
||||
u16 scratch;
|
||||
u32 scratch32;
|
||||
|
||||
/* PLL software reset */
|
||||
scratch32 = sdhci_readl(host, O2_PLL_WDT_CONTROL1);
|
||||
scratch32 |= O2_PLL_SOFT_RESET;
|
||||
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
|
||||
udelay(1);
|
||||
scratch32 &= ~(O2_PLL_SOFT_RESET);
|
||||
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
|
||||
|
||||
/* PLL force active */
|
||||
scratch32 |= O2_PLL_FORCE_ACTIVE;
|
||||
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
|
||||
|
||||
/* Wait max 20 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
scratch = sdhci_readw(host, O2_PLL_WDT_CONTROL1);
|
||||
if (scratch & O2_PLL_LOCK_STATUS)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_dumpregs(host);
|
||||
goto out;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/* Wait for card detect finish */
|
||||
udelay(1);
|
||||
sdhci_o2_wait_card_detect_stable(host);
|
||||
|
||||
out:
|
||||
/* Cancel PLL force active */
|
||||
scratch32 = sdhci_readl(host, O2_PLL_WDT_CONTROL1);
|
||||
scratch32 &= ~O2_PLL_FORCE_ACTIVE;
|
||||
sdhci_writel(host, scratch32, O2_PLL_WDT_CONTROL1);
|
||||
}
|
||||
|
||||
static int sdhci_o2_get_cd(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
sdhci_o2_enable_internal_clock(host);
|
||||
|
||||
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
||||
}
|
||||
|
||||
static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk)
|
||||
{
|
||||
/* Enable internal clock */
|
||||
clk |= SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (sdhci_o2_get_cd(host->mmc)) {
|
||||
clk |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
}
|
||||
}
|
||||
|
||||
void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (clock == 0)
|
||||
return;
|
||||
|
||||
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
|
||||
sdhci_o2_enable_clk(host, clk);
|
||||
}
|
||||
|
||||
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct sdhci_pci_chip *chip;
|
||||
|
@ -314,9 +428,14 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
|||
mmc_hostname(host->mmc));
|
||||
host->flags &= ~SDHCI_SIGNALING_330;
|
||||
host->flags |= SDHCI_SIGNALING_180;
|
||||
host->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_SD;
|
||||
host->mmc->caps2 |= MMC_CAP2_NO_SDIO;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_DETECT_SETTING, 3);
|
||||
}
|
||||
|
||||
slot->host->mmc_host_ops.get_cd = sdhci_o2_get_cd;
|
||||
}
|
||||
|
||||
host->mmc_host_ops.execute_tuning = sdhci_o2_execute_tuning;
|
||||
|
@ -490,9 +609,6 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
break;
|
||||
case PCI_DEVICE_ID_O2_SEABIRD0:
|
||||
if (chip->pdev->revision == 0x01)
|
||||
chip->quirks |= SDHCI_QUIRK_DELAY_AFTER_POWER;
|
||||
/* fall through */
|
||||
case PCI_DEVICE_ID_O2_SEABIRD1:
|
||||
/* UnLock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
|
@ -550,3 +666,21 @@ int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
|
|||
return sdhci_pci_resume_host(chip);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct sdhci_ops sdhci_pci_o2_ops = {
|
||||
.set_clock = sdhci_pci_o2_set_clock,
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
const struct sdhci_pci_fixes sdhci_o2 = {
|
||||
.probe = sdhci_pci_o2_probe,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.probe_slot = sdhci_pci_o2_probe_slot,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.resume = sdhci_pci_o2_resume,
|
||||
#endif
|
||||
.ops = &sdhci_pci_o2_ops,
|
||||
};
|
||||
|
|
|
@ -179,13 +179,9 @@ static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
|
|||
int sdhci_pci_resume_host(struct sdhci_pci_chip *chip);
|
||||
#endif
|
||||
int sdhci_pci_enable_dma(struct sdhci_host *host);
|
||||
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
|
||||
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
|
||||
#endif
|
||||
|
||||
extern const struct sdhci_pci_fixes sdhci_arasan;
|
||||
extern const struct sdhci_pci_fixes sdhci_snps;
|
||||
extern const struct sdhci_pci_fixes sdhci_o2;
|
||||
|
||||
#endif /* __SDHCI_PCI_H */
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <linux/clk.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/platform_data/pxa_sdhci.h>
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <linux/ktime.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
/* Tegra SDHOST controller vendor register definitions */
|
||||
#define SDHCI_TEGRA_VENDOR_CLOCK_CTRL 0x100
|
||||
|
@ -75,6 +76,7 @@
|
|||
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_MASK 0x0000000f
|
||||
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_VREF_SEL_VAL 0x7
|
||||
#define SDHCI_TEGRA_SDMEM_COMP_PADCTRL_E_INPUT_E_PWRD BIT(31)
|
||||
#define SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK 0x07FFF000
|
||||
|
||||
#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
|
||||
#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
|
||||
|
@ -89,6 +91,9 @@
|
|||
#define NVQUIRK_NEEDS_PAD_CONTROL BIT(7)
|
||||
#define NVQUIRK_DIS_CARD_CLK_CONFIG_TAP BIT(8)
|
||||
|
||||
/* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
|
||||
#define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
u32 nvquirks;
|
||||
|
@ -121,6 +126,8 @@ struct sdhci_tegra {
|
|||
struct pinctrl *pinctrl_sdmmc;
|
||||
struct pinctrl_state *pinctrl_state_3v3;
|
||||
struct pinctrl_state *pinctrl_state_1v8;
|
||||
struct pinctrl_state *pinctrl_state_3v3_drv;
|
||||
struct pinctrl_state *pinctrl_state_1v8_drv;
|
||||
|
||||
struct sdhci_tegra_autocal_offsets autocal_offsets;
|
||||
ktime_t last_calib;
|
||||
|
@ -128,6 +135,7 @@ struct sdhci_tegra {
|
|||
u32 default_tap;
|
||||
u32 default_trim;
|
||||
u32 dqs_trim;
|
||||
bool enable_hwcq;
|
||||
};
|
||||
|
||||
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
||||
|
@ -237,11 +245,6 @@ static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
|
||||
{
|
||||
return mmc_gpio_get_ro(host->mmc);
|
||||
}
|
||||
|
||||
static bool tegra_sdhci_is_pad_and_regulator_valid(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -411,6 +414,76 @@ static void tegra_sdhci_set_pad_autocal_offset(struct sdhci_host *host,
|
|||
sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
|
||||
}
|
||||
|
||||
static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage,
|
||||
bool state_drvupdn)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct sdhci_tegra_autocal_offsets *offsets =
|
||||
&tegra_host->autocal_offsets;
|
||||
struct pinctrl_state *pinctrl_drvupdn = NULL;
|
||||
int ret = 0;
|
||||
u8 drvup = 0, drvdn = 0;
|
||||
u32 reg;
|
||||
|
||||
if (!state_drvupdn) {
|
||||
/* PADS Drive Strength */
|
||||
if (voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
if (tegra_host->pinctrl_state_1v8_drv) {
|
||||
pinctrl_drvupdn =
|
||||
tegra_host->pinctrl_state_1v8_drv;
|
||||
} else {
|
||||
drvup = offsets->pull_up_1v8_timeout;
|
||||
drvdn = offsets->pull_down_1v8_timeout;
|
||||
}
|
||||
} else {
|
||||
if (tegra_host->pinctrl_state_3v3_drv) {
|
||||
pinctrl_drvupdn =
|
||||
tegra_host->pinctrl_state_3v3_drv;
|
||||
} else {
|
||||
drvup = offsets->pull_up_3v3_timeout;
|
||||
drvdn = offsets->pull_down_3v3_timeout;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinctrl_drvupdn != NULL) {
|
||||
ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
|
||||
pinctrl_drvupdn);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed pads drvupdn, ret: %d\n", ret);
|
||||
} else if ((drvup) || (drvdn)) {
|
||||
reg = sdhci_readl(host,
|
||||
SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
|
||||
reg &= ~SDHCI_COMP_PADCTRL_DRVUPDN_OFFSET_MASK;
|
||||
reg |= (drvup << 20) | (drvdn << 12);
|
||||
sdhci_writel(host, reg,
|
||||
SDHCI_TEGRA_SDMEM_COMP_PADCTRL);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Dual Voltage PADS Voltage selection */
|
||||
if (!tegra_host->pad_control_available)
|
||||
return 0;
|
||||
|
||||
if (voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
|
||||
tegra_host->pinctrl_state_1v8);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"setting 1.8V failed, ret: %d\n", ret);
|
||||
} else {
|
||||
ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
|
||||
tegra_host->pinctrl_state_3v3);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"setting 3.3V failed, ret: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -437,6 +510,7 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
|
|||
pdpu = offsets.pull_down_3v3 << 8 | offsets.pull_up_3v3;
|
||||
}
|
||||
|
||||
/* Set initial offset before auto-calibration */
|
||||
tegra_sdhci_set_pad_autocal_offset(host, pdpu);
|
||||
|
||||
card_clk_enabled = tegra_sdhci_configure_card_clk(host, false);
|
||||
|
@ -460,19 +534,15 @@ static void tegra_sdhci_pad_autocalib(struct sdhci_host *host)
|
|||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc), "Pad autocal timed out\n");
|
||||
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
|
||||
pdpu = offsets.pull_down_1v8_timeout << 8 |
|
||||
offsets.pull_up_1v8_timeout;
|
||||
else
|
||||
pdpu = offsets.pull_down_3v3_timeout << 8 |
|
||||
offsets.pull_up_3v3_timeout;
|
||||
|
||||
/* Disable automatic calibration and use fixed offsets */
|
||||
/* Disable automatic cal and use fixed Drive Strengths */
|
||||
reg = sdhci_readl(host, SDHCI_TEGRA_AUTO_CAL_CONFIG);
|
||||
reg &= ~SDHCI_AUTO_CAL_ENABLE;
|
||||
sdhci_writel(host, reg, SDHCI_TEGRA_AUTO_CAL_CONFIG);
|
||||
|
||||
tegra_sdhci_set_pad_autocal_offset(host, pdpu);
|
||||
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, false);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Setting drive strengths failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,26 +581,46 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
|
|||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
|
||||
&autocal->pull_up_3v3_timeout);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
|
||||
(tegra_host->pinctrl_state_3v3_drv == NULL))
|
||||
pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
|
||||
mmc_hostname(host->mmc));
|
||||
autocal->pull_up_3v3_timeout = 0;
|
||||
}
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-3v3-timeout",
|
||||
&autocal->pull_down_3v3_timeout);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (!IS_ERR(tegra_host->pinctrl_state_3v3) &&
|
||||
(tegra_host->pinctrl_state_3v3_drv == NULL))
|
||||
pr_warn("%s: Missing autocal timeout 3v3-pad drvs\n",
|
||||
mmc_hostname(host->mmc));
|
||||
autocal->pull_down_3v3_timeout = 0;
|
||||
}
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-1v8-timeout",
|
||||
&autocal->pull_up_1v8_timeout);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
|
||||
(tegra_host->pinctrl_state_1v8_drv == NULL))
|
||||
pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
|
||||
mmc_hostname(host->mmc));
|
||||
autocal->pull_up_1v8_timeout = 0;
|
||||
}
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-1v8-timeout",
|
||||
&autocal->pull_down_1v8_timeout);
|
||||
if (err)
|
||||
if (err) {
|
||||
if (!IS_ERR(tegra_host->pinctrl_state_1v8) &&
|
||||
(tegra_host->pinctrl_state_1v8_drv == NULL))
|
||||
pr_warn("%s: Missing autocal timeout 1v8-pad drvs\n",
|
||||
mmc_hostname(host->mmc));
|
||||
autocal->pull_down_1v8_timeout = 0;
|
||||
}
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-sdr104",
|
||||
|
@ -595,6 +685,20 @@ static void tegra_sdhci_parse_tap_and_trim(struct sdhci_host *host)
|
|||
tegra_host->dqs_trim = 0x11;
|
||||
}
|
||||
|
||||
static void tegra_sdhci_parse_dt(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (device_property_read_bool(host->mmc->parent, "supports-cqe"))
|
||||
tegra_host->enable_hwcq = true;
|
||||
else
|
||||
tegra_host->enable_hwcq = false;
|
||||
|
||||
tegra_sdhci_parse_pad_autocal_dt(host);
|
||||
tegra_sdhci_parse_tap_and_trim(host);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -743,32 +847,6 @@ static int tegra_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||
return mmc_send_tuning(host->mmc, opcode, NULL);
|
||||
}
|
||||
|
||||
static int tegra_sdhci_set_padctrl(struct sdhci_host *host, int voltage)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
if (!tegra_host->pad_control_available)
|
||||
return 0;
|
||||
|
||||
if (voltage == MMC_SIGNAL_VOLTAGE_180) {
|
||||
ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
|
||||
tegra_host->pinctrl_state_1v8);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"setting 1.8V failed, ret: %d\n", ret);
|
||||
} else {
|
||||
ret = pinctrl_select_state(tegra_host->pinctrl_sdmmc,
|
||||
tegra_host->pinctrl_state_3v3);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"setting 3.3V failed, ret: %d\n", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
|
@ -778,7 +856,7 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
int ret = 0;
|
||||
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
|
||||
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
|
||||
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = sdhci_start_signal_voltage_switch(mmc, ios);
|
||||
|
@ -786,7 +864,7 @@ static int sdhci_tegra_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
ret = sdhci_start_signal_voltage_switch(mmc, ios);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage);
|
||||
ret = tegra_sdhci_set_padctrl(host, ios->signal_voltage, true);
|
||||
}
|
||||
|
||||
if (tegra_host->pad_calib_required)
|
||||
|
@ -805,6 +883,20 @@ static int tegra_sdhci_init_pinctrl_info(struct device *dev,
|
|||
return -1;
|
||||
}
|
||||
|
||||
tegra_host->pinctrl_state_1v8_drv = pinctrl_lookup_state(
|
||||
tegra_host->pinctrl_sdmmc, "sdmmc-1v8-drv");
|
||||
if (IS_ERR(tegra_host->pinctrl_state_1v8_drv)) {
|
||||
if (PTR_ERR(tegra_host->pinctrl_state_1v8_drv) == -ENODEV)
|
||||
tegra_host->pinctrl_state_1v8_drv = NULL;
|
||||
}
|
||||
|
||||
tegra_host->pinctrl_state_3v3_drv = pinctrl_lookup_state(
|
||||
tegra_host->pinctrl_sdmmc, "sdmmc-3v3-drv");
|
||||
if (IS_ERR(tegra_host->pinctrl_state_3v3_drv)) {
|
||||
if (PTR_ERR(tegra_host->pinctrl_state_3v3_drv) == -ENODEV)
|
||||
tegra_host->pinctrl_state_3v3_drv = NULL;
|
||||
}
|
||||
|
||||
tegra_host->pinctrl_state_3v3 =
|
||||
pinctrl_lookup_state(tegra_host->pinctrl_sdmmc, "sdmmc-3v3");
|
||||
if (IS_ERR(tegra_host->pinctrl_state_3v3)) {
|
||||
|
@ -836,8 +928,50 @@ static void tegra_sdhci_voltage_switch(struct sdhci_host *host)
|
|||
tegra_host->pad_calib_required = true;
|
||||
}
|
||||
|
||||
static void sdhci_tegra_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||
u32 cqcfg = 0;
|
||||
|
||||
/*
|
||||
* Tegra SDMMC Controller design prevents write access to BLOCK_COUNT
|
||||
* registers when CQE is enabled.
|
||||
*/
|
||||
cqcfg = cqhci_readl(cq_host, CQHCI_CFG);
|
||||
if (cqcfg & CQHCI_ENABLE)
|
||||
cqhci_writel(cq_host, (cqcfg & ~CQHCI_ENABLE), CQHCI_CFG);
|
||||
|
||||
sdhci_cqe_enable(mmc);
|
||||
|
||||
if (cqcfg & CQHCI_ENABLE)
|
||||
cqhci_writel(cq_host, cqcfg, CQHCI_CFG);
|
||||
}
|
||||
|
||||
static void sdhci_tegra_dumpregs(struct mmc_host *mmc)
|
||||
{
|
||||
sdhci_dumpregs(mmc_priv(mmc));
|
||||
}
|
||||
|
||||
static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
int data_error = 0;
|
||||
|
||||
if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
|
||||
return intmask;
|
||||
|
||||
cqhci_irq(host->mmc, intmask, cmd_error, data_error);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cqhci_host_ops sdhci_tegra_cqhci_ops = {
|
||||
.enable = sdhci_tegra_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
.dumpregs = sdhci_tegra_dumpregs,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.set_clock = tegra_sdhci_set_clock,
|
||||
|
@ -893,7 +1027,6 @@ static const struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
|||
};
|
||||
|
||||
static const struct sdhci_ops tegra114_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_w = tegra_sdhci_writew,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
|
@ -947,7 +1080,6 @@ static const struct sdhci_tegra_soc_data soc_data_tegra124 = {
|
|||
};
|
||||
|
||||
static const struct sdhci_ops tegra210_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_w = tegra210_sdhci_writew,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
|
@ -980,7 +1112,6 @@ static const struct sdhci_tegra_soc_data soc_data_tegra210 = {
|
|||
};
|
||||
|
||||
static const struct sdhci_ops tegra186_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.set_clock = tegra_sdhci_set_clock,
|
||||
|
@ -989,6 +1120,7 @@ static const struct sdhci_ops tegra186_sdhci_ops = {
|
|||
.set_uhs_signaling = tegra_sdhci_set_uhs_signaling,
|
||||
.voltage_switch = tegra_sdhci_voltage_switch,
|
||||
.get_max_clock = tegra_sdhci_get_max_clock,
|
||||
.irq = sdhci_tegra_cqhci_irq,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
|
||||
|
@ -1030,6 +1162,54 @@ static const struct of_device_id sdhci_tegra_dt_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_tegra_dt_match);
|
||||
|
||||
static int sdhci_tegra_add_host(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct cqhci_host *cq_host;
|
||||
bool dma64;
|
||||
int ret;
|
||||
|
||||
if (!tegra_host->enable_hwcq)
|
||||
return sdhci_add_host(host);
|
||||
|
||||
sdhci_enable_v4_mode(host);
|
||||
|
||||
ret = sdhci_setup_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
|
||||
cq_host = devm_kzalloc(host->mmc->parent,
|
||||
sizeof(*cq_host), GFP_KERNEL);
|
||||
if (!cq_host) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cq_host->mmio = host->ioaddr + SDHCI_TEGRA_CQE_BASE_ADDR;
|
||||
cq_host->ops = &sdhci_tegra_cqhci_ops;
|
||||
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
if (dma64)
|
||||
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
|
||||
ret = cqhci_init(cq_host, host->mmc, dma64);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
ret = __sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
sdhci_cleanup_host(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
@ -1077,9 +1257,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50)
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
tegra_sdhci_parse_pad_autocal_dt(host);
|
||||
|
||||
tegra_sdhci_parse_tap_and_trim(host);
|
||||
tegra_sdhci_parse_dt(host);
|
||||
|
||||
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
|
||||
GPIOD_OUT_HIGH);
|
||||
|
@ -1117,7 +1295,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
|
||||
usleep_range(2000, 4000);
|
||||
|
||||
rc = sdhci_add_host(host);
|
||||
rc = sdhci_tegra_add_host(host);
|
||||
if (rc)
|
||||
goto err_add_host;
|
||||
|
||||
|
|
|
@ -530,7 +530,7 @@ static bool xenon_emmc_phy_slow_mode(struct sdhci_host *host,
|
|||
ret = true;
|
||||
break;
|
||||
}
|
||||
/* else: fall through */
|
||||
/* fall through */
|
||||
default:
|
||||
reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
|
||||
ret = false;
|
||||
|
|
|
@ -883,7 +883,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
|||
bool *too_big)
|
||||
{
|
||||
u8 count;
|
||||
struct mmc_data *data = cmd->data;
|
||||
struct mmc_data *data;
|
||||
unsigned target_timeout, current_timeout;
|
||||
|
||||
*too_big = true;
|
||||
|
@ -897,6 +897,11 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd,
|
|||
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
|
||||
return 0xE;
|
||||
|
||||
/* Unspecified command, asume max */
|
||||
if (cmd == NULL)
|
||||
return 0xE;
|
||||
|
||||
data = cmd->data;
|
||||
/* Unspecified timeout, assume max */
|
||||
if (!data && !cmd->busy_timeout)
|
||||
return 0xE;
|
||||
|
@ -2048,6 +2053,8 @@ static int sdhci_check_ro(struct sdhci_host *host)
|
|||
is_readonly = 0;
|
||||
else if (host->ops->get_ro)
|
||||
is_readonly = host->ops->get_ro(host);
|
||||
else if (mmc_can_gpio_ro(host->mmc))
|
||||
is_readonly = mmc_gpio_get_ro(host->mmc);
|
||||
else
|
||||
is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE)
|
||||
& SDHCI_WRITE_PROTECT);
|
||||
|
@ -2376,6 +2383,10 @@ static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Spec does not require a delay between tuning cycles */
|
||||
if (host->tuning_delay > 0)
|
||||
mdelay(host->tuning_delay);
|
||||
|
||||
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) {
|
||||
if (ctrl & SDHCI_CTRL_TUNED_CLK)
|
||||
|
@ -2383,9 +2394,6 @@ static int __sdhci_execute_tuning(struct sdhci_host *host, u32 opcode)
|
|||
break;
|
||||
}
|
||||
|
||||
/* Spec does not require a delay between tuning cycles */
|
||||
if (host->tuning_delay > 0)
|
||||
mdelay(host->tuning_delay);
|
||||
}
|
||||
|
||||
pr_info("%s: Tuning failed, falling back to fixed sampling clock\n",
|
||||
|
@ -3353,7 +3361,14 @@ void sdhci_cqe_enable(struct mmc_host *mmc)
|
|||
|
||||
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
|
||||
ctrl &= ~SDHCI_CTRL_DMA_MASK;
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
/*
|
||||
* Host from V4.10 supports ADMA3 DMA type.
|
||||
* ADMA3 performs integrated descriptor which is more suitable
|
||||
* for cmd queuing to fetch both command and transfer descriptors.
|
||||
*/
|
||||
if (host->v4_mode && (host->caps1 & SDHCI_CAN_DO_ADMA3))
|
||||
ctrl |= SDHCI_CTRL_ADMA3;
|
||||
else if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
ctrl |= SDHCI_CTRL_ADMA64;
|
||||
else
|
||||
ctrl |= SDHCI_CTRL_ADMA32;
|
||||
|
@ -3363,7 +3378,7 @@ void sdhci_cqe_enable(struct mmc_host *mmc)
|
|||
SDHCI_BLOCK_SIZE);
|
||||
|
||||
/* Set maximum timeout */
|
||||
sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL);
|
||||
sdhci_set_timeout(host, NULL);
|
||||
|
||||
host->ier = host->cqe_ier;
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@
|
|||
#define SDHCI_SPACE_AVAILABLE 0x00000400
|
||||
#define SDHCI_DATA_AVAILABLE 0x00000800
|
||||
#define SDHCI_CARD_PRESENT 0x00010000
|
||||
#define SDHCI_CARD_PRES_SHIFT 16
|
||||
#define SDHCI_CD_STABLE 0x00020000
|
||||
#define SDHCI_CD_LVL 0x00040000
|
||||
#define SDHCI_CD_LVL_SHIFT 18
|
||||
#define SDHCI_WRITE_PROTECT 0x00080000
|
||||
#define SDHCI_DATA_LVL_MASK 0x00F00000
|
||||
#define SDHCI_DATA_LVL_SHIFT 20
|
||||
|
@ -88,6 +92,7 @@
|
|||
#define SDHCI_CTRL_ADMA1 0x08
|
||||
#define SDHCI_CTRL_ADMA32 0x10
|
||||
#define SDHCI_CTRL_ADMA64 0x18
|
||||
#define SDHCI_CTRL_ADMA3 0x18
|
||||
#define SDHCI_CTRL_8BITBUS 0x20
|
||||
#define SDHCI_CTRL_CDTEST_INS 0x40
|
||||
#define SDHCI_CTRL_CDTEST_EN 0x80
|
||||
|
@ -230,6 +235,7 @@
|
|||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
||||
#define SDHCI_CAN_DO_ADMA3 0x08000000
|
||||
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
|
|
|
@ -158,7 +158,7 @@ static void sdhci_am654_set_power(struct sdhci_host *host, unsigned char mode,
|
|||
sdhci_set_power_noreg(host, mode, vdd);
|
||||
}
|
||||
|
||||
struct sdhci_ops sdhci_am654_ops = {
|
||||
static struct sdhci_ops sdhci_am654_ops = {
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -32,7 +31,6 @@
|
|||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
|
|
@ -1073,7 +1073,7 @@ static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
|||
|
||||
/* use ocr_mask if no regulator */
|
||||
if (!mmc->ocr_avail)
|
||||
mmc->ocr_avail = pdata->ocr_mask;
|
||||
mmc->ocr_avail = pdata->ocr_mask;
|
||||
|
||||
/*
|
||||
* try again.
|
||||
|
@ -1294,6 +1294,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
|||
cancel_delayed_work_sync(&host->delayed_reset_work);
|
||||
tmio_mmc_release_dma(host);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
|
|
|
@ -133,6 +133,8 @@ struct mmc_ext_csd {
|
|||
struct sd_scr {
|
||||
unsigned char sda_vsn;
|
||||
unsigned char sda_spec3;
|
||||
unsigned char sda_spec4;
|
||||
unsigned char sda_specx;
|
||||
unsigned char bus_widths;
|
||||
#define SD_SCR_BUS_WIDTH_1 (1<<0)
|
||||
#define SD_SCR_BUS_WIDTH_4 (1<<2)
|
||||
|
@ -277,6 +279,7 @@ struct mmc_card {
|
|||
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||
unsigned int pref_erase; /* in sectors */
|
||||
unsigned int eg_boundary; /* don't cross erase-group boundaries */
|
||||
unsigned int erase_arg; /* erase / trim / discard */
|
||||
u8 erased_byte; /* value of erased bytes */
|
||||
|
||||
u32 raw_cid[4]; /* raw card CID */
|
||||
|
|
|
@ -478,6 +478,11 @@ static inline void *mmc_priv(struct mmc_host *host)
|
|||
return (void *)host->private;
|
||||
}
|
||||
|
||||
static inline struct mmc_host *mmc_from_priv(void *priv)
|
||||
{
|
||||
return container_of(priv, struct mmc_host, private);
|
||||
}
|
||||
|
||||
#define mmc_host_is_spi(host) ((host)->caps & MMC_CAP_SPI)
|
||||
|
||||
#define mmc_dev(x) ((x)->parent)
|
||||
|
@ -502,17 +507,11 @@ void sdio_run_irqs(struct mmc_host *host);
|
|||
void sdio_signal_irq(struct mmc_host *host);
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
int mmc_regulator_get_ocrmask(struct regulator *supply);
|
||||
int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit);
|
||||
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);
|
||||
#else
|
||||
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
struct regulator *supply,
|
||||
unsigned short vdd_bit)
|
||||
|
@ -527,7 +526,6 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
|
|||
}
|
||||
#endif
|
||||
|
||||
u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max);
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc);
|
||||
|
||||
static inline int mmc_card_is_removable(struct mmc_host *host)
|
||||
|
|
|
@ -91,4 +91,10 @@
|
|||
#define SD_SWITCH_ACCESS_DEF 0
|
||||
#define SD_SWITCH_ACCESS_HS 1
|
||||
|
||||
/*
|
||||
* Erase/discard
|
||||
*/
|
||||
#define SD_ERASE_ARG 0x00000000
|
||||
#define SD_DISCARD_ARG 0x00000001
|
||||
|
||||
#endif /* LINUX_MMC_SD_H */
|
||||
|
|
|
@ -22,7 +22,7 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
|||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert);
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int idx,
|
||||
unsigned int debounce, bool *gpio_invert);
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id));
|
||||
|
|
Loading…
Reference in a new issue