MMC highlights for 3.16:
Core: - support HS400 mode of eMMC 5.0, via DT bindings mmc-hs400-1_{2,8}v - if card init at 3.3v doesn't work, try 1.8v and 1.2v too Drivers: - moxart: New driver for MOXA ART SoCs - rtsx_usb_sdmmc: New driver for Realtek USB card readers - sdhci: Large rework around IRQ/regulator handling, remove card_tasklet - sdhci-pci-o2micro: Add SeaBird SeaEagle SD3 support - sunxi: New driver for Allwinner sunxi SoCs - usdhi6rol0: New driver for Renesas SD/SDIO controller -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJTl199AAoJEHNBYZ7TNxYMutUQAIHOkbFeEmHiwhhM5snpywAg wRLIdiOWTYGrqRzvINDGsH4MVpt+acK5QuS5rNO5sEqhSovAZCNbQAvI4wXP60Uy bnXyqnq7uPHjQxUWu9ES1l3gvX1djLVCD0f5sqyG1nL4SA3h8CFbEKkGHEMEXpWY tOwUGxrM6IHk1Z7l1FzWiN4nidvsX9maKDiCEyCF86xMEyqsxd/gz5pjhF3GgZBY z174+XU7Vlb5HkpeH/anhSVfYALURlRNNdtJaMQoR1Goldhr/KfnaqkuQNLanqOS R1BselxeZxFUcSGJ87WrLE7jFa9i9qUzMdxJRrzssNCA7JZl1hOwTE4EAFCxMADZ fbZl/kwoQo/nO6HHQtJ3J+UeinA9aFGUR0jpN8l9jZkwb9VsyYenrd27+OcUcJGP ZyQ636oT+mB44RwHBz2mZVLLTrV84x9d4VVqjZ3QrMYNWVhXyIyjFSinoJvcNGOk xQ4zsAsyPXHzr6pR6diKab8tLYC+VoeWKdmqPDQtPY+L+D23qQNAarvS1oPjsgx8 IZIIdh4ikftsXALriU2OqVi3zIw+mVTDlq7xp3gSI/Zf4Srlx+6V0X7KVUVoMB0h O19+HgKJv6pCoL54+FgFDdKcgdUANWG2s/BSmhRNgFJY97/tvahHlyoTmrVhXqil L/nmuknP3lB4RTQ7E5Kk =OZao -----END PGP SIGNATURE----- Merge tag 'mmc-updates-for-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc Pull MMC update from Chris Ball: "MMC highlights for 3.16: Core: - support HS400 mode of eMMC 5.0, via DT bindings mmc-hs400-1_{2,8}v - if card init at 3.3v doesn't work, try 1.8v and 1.2v too Drivers: - moxart: New driver for MOXA ART SoCs - rtsx_usb_sdmmc: New driver for Realtek USB card readers - sdhci: Large rework around IRQ/regulator handling, remove card_tasklet - sdhci-pci-o2micro: Add SeaBird SeaEagle SD3 support - sunxi: New driver for Allwinner sunxi SoCs - usdhi6rol0: New driver for Renesas SD/SDIO controller" * tag 'mmc-updates-for-3.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (95 commits) mmc: sdhci-s3c: use mmc_of_parse and remove the card_tasklet mmc: add a driver for the Renesas usdhi6rol0 SD/SDIO host controller mmc: sdhci-of-esdhc: Fixup compile error mmc: tegra: fix reporting of base clock frequency mmc: tegra: disable UHS modes mmc: sdhci-dove: use mmc_of_parse() and remove card_tasklet CD handler MAINTAINERS: mmc: Add path to git tree mmc: dove: fix missing MACH_DOVE dependency mmc: sdhci: SD tuning is broken for some controllers mmc: sdhci-esdhc-imx: fix mmc ddr mode regression issue mmc: sdhci-pci-o2micro: Add SeaBird SeaEagle SD3 support mmc: omap_hsmmc: split omap-dma header file mmc: omap_hsmmc: fix cmd23 multiblock read/write mmc: omap_hsmmc: use devm_ioremap_resource mmc: omap_hsmmc: use devm_request_threaded_irq mmc: omap_hsmmc: use devm_request_irq mmc: omap_hsmmc: use devm_clk_get mmc: sunxi: Add driver for SD/MMC hosts found on Allwinner sunxi SoCs mmc: wmt-sdmmc: Use GFP_KERNEL instead of hard-coded value mmc: omap: Use DIV_ROUND_UP instead of open coded ...
This commit is contained in:
commit
07888238f5
65 changed files with 4110 additions and 1495 deletions
|
@ -38,6 +38,8 @@ Optional properties:
|
|||
- mmc-highspeed-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported
|
||||
- mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported
|
||||
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
|
||||
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
|
||||
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
|
||||
|
||||
*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"
|
||||
|
|
30
Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
Normal file
30
Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
MOXA ART MMC Host Controller Interface
|
||||
|
||||
Inherits from mmc binding[1].
|
||||
|
||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : Must be "moxa,moxart-mmc" or "faraday,ftsdc010"
|
||||
- reg : Should contain registers location and length
|
||||
- interrupts : Should contain the interrupt number
|
||||
- clocks : Should contain phandle for the clock feeding the MMC controller
|
||||
|
||||
Optional properties:
|
||||
|
||||
- dmas : Should contain two DMA channels, line request number must be 5 for
|
||||
both channels
|
||||
- dma-names : Must be "tx", "rx"
|
||||
|
||||
Example:
|
||||
|
||||
mmc: mmc@98e00000 {
|
||||
compatible = "moxa,moxart-mmc";
|
||||
reg = <0x98e00000 0x5C>;
|
||||
interrupts = <5 0>;
|
||||
clocks = <&clk_apb>;
|
||||
dmas = <&dma 5>,
|
||||
<&dma 5>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
|
@ -69,10 +69,6 @@ Optional properties:
|
|||
|
||||
* supports-highspeed: Enables support for high speed cards (up to 50MHz)
|
||||
|
||||
* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode
|
||||
|
||||
* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode
|
||||
|
||||
* broken-cd: as documented in mmc core bindings.
|
||||
|
||||
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
|
||||
|
@ -103,7 +99,6 @@ board specific portions as listed below.
|
|||
clock-freq-min-max = <400000 200000000>;
|
||||
num-slots = <1>;
|
||||
supports-highspeed;
|
||||
caps2-mmc-hs200-1_8v;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
|
|
33
Documentation/devicetree/bindings/mmc/usdhi6rol0.txt
Normal file
33
Documentation/devicetree/bindings/mmc/usdhi6rol0.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
* Renesas usdhi6rol0 SD/SDIO host controller
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: must be
|
||||
"renesas,usdhi6rol0"
|
||||
- interrupts: 3 interrupts, named "card detect", "data" and "SDIO" must be
|
||||
specified
|
||||
- clocks: a clock binding for the IMCLK input
|
||||
|
||||
Optional properties:
|
||||
|
||||
- vmmc-supply: a phandle of a regulator, supplying Vcc to the card
|
||||
- vqmmc-supply: a phandle of a regulator, supplying VccQ to the card
|
||||
|
||||
Additionally any standard mmc bindings from mmc.txt can be used.
|
||||
|
||||
Example:
|
||||
|
||||
sd0: sd@ab000000 {
|
||||
compatible = "renesas,usdhi6rol0";
|
||||
reg = <0xab000000 0x200>;
|
||||
interrupts = <0 23 0x4
|
||||
0 24 0x4
|
||||
0 25 0x4>;
|
||||
interrupt-names = "card detect", "data", "SDIO";
|
||||
bus-width = <4>;
|
||||
max-frequency = <50000000>;
|
||||
cap-power-off-card;
|
||||
clocks = <&imclk>;
|
||||
vmmc-supply = <&vcc_sd0>;
|
||||
vqmmc-supply = <&vccq_sd0>;
|
||||
};
|
|
@ -5974,6 +5974,7 @@ M: Chris Ball <chris@printf.net>
|
|||
M: Ulf Hansson <ulf.hansson@linaro.org>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
|
||||
T: git git://git.linaro.org/people/ulf.hansson/mmc.git
|
||||
S: Maintained
|
||||
F: drivers/mmc/
|
||||
F: include/linux/mmc/
|
||||
|
@ -9103,7 +9104,7 @@ F: include/linux/toshiba.h
|
|||
F: include/uapi/linux/toshiba.h
|
||||
|
||||
TMIO MMC DRIVER
|
||||
M: Ian Molton <ian@mnementh.co.uk>
|
||||
M: Ian Molton <ian.molton@codethink.co.uk>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/tmio_mmc*
|
||||
|
|
|
@ -341,16 +341,17 @@ int mmc_add_card(struct mmc_card *card)
|
|||
if (mmc_host_is_spi(card->host)) {
|
||||
pr_info("%s: new %s%s%s card on SPI\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_highspeed(card) ? "high speed " : "",
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
mmc_card_hs(card) ? "high speed " : "",
|
||||
mmc_card_ddr52(card) ? "DDR " : "",
|
||||
type);
|
||||
} else {
|
||||
pr_info("%s: new %s%s%s%s%s card at address %04x\n",
|
||||
mmc_hostname(card->host),
|
||||
mmc_card_uhs(card) ? "ultra high speed " :
|
||||
(mmc_card_highspeed(card) ? "high speed " : ""),
|
||||
(mmc_card_hs(card) ? "high speed " : ""),
|
||||
mmc_card_hs400(card) ? "HS400 " :
|
||||
(mmc_card_hs200(card) ? "HS200 " : ""),
|
||||
mmc_card_ddr_mode(card) ? "DDR " : "",
|
||||
mmc_card_ddr52(card) ? "DDR " : "",
|
||||
uhs_bus_speed_mode, type, card->rca);
|
||||
}
|
||||
|
||||
|
|
|
@ -800,6 +800,10 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
|
|||
data->timeout_ns = limit_us * 1000;
|
||||
data->timeout_clks = 0;
|
||||
}
|
||||
|
||||
/* assign limit value if invalid */
|
||||
if (timeout_us == 0)
|
||||
data->timeout_ns = limit_us * 1000;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1310,31 +1314,38 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_set_ocr);
|
||||
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
struct device *dev = mmc_dev(mmc);
|
||||
struct regulator *supply;
|
||||
int ret;
|
||||
|
||||
supply = devm_regulator_get(dev, "vmmc");
|
||||
mmc->supply.vmmc = supply;
|
||||
mmc->supply.vmmc = devm_regulator_get_optional(dev, "vmmc");
|
||||
mmc->supply.vqmmc = devm_regulator_get_optional(dev, "vqmmc");
|
||||
|
||||
if (IS_ERR(supply))
|
||||
return PTR_ERR(supply);
|
||||
if (IS_ERR(mmc->supply.vmmc)) {
|
||||
if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_info(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);
|
||||
}
|
||||
|
||||
ret = mmc_regulator_get_ocrmask(supply);
|
||||
if (ret > 0)
|
||||
mmc->ocr_avail = ret;
|
||||
else
|
||||
dev_warn(mmc_dev(mmc), "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_info(dev, "No vqmmc regulator found\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
|
||||
|
||||
#endif /* CONFIG_REGULATOR */
|
||||
|
||||
/*
|
||||
* Mask off any voltages we don't support and select
|
||||
* the lowest voltage
|
||||
|
@ -1533,8 +1544,13 @@ void mmc_power_up(struct mmc_host *host, u32 ocr)
|
|||
host->ios.timing = MMC_TIMING_LEGACY;
|
||||
mmc_set_ios(host);
|
||||
|
||||
/* Set signal voltage to 3.3V */
|
||||
__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
|
||||
/* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */
|
||||
if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330) == 0)
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n");
|
||||
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180) == 0)
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n");
|
||||
else if (__mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120) == 0)
|
||||
dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n");
|
||||
|
||||
/*
|
||||
* This delay should be sufficient to allow the power supply
|
||||
|
@ -2183,7 +2199,7 @@ int mmc_set_blocklen(struct mmc_card *card, unsigned int blocklen)
|
|||
{
|
||||
struct mmc_command cmd = {0};
|
||||
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr_mode(card))
|
||||
if (mmc_card_blockaddr(card) || mmc_card_ddr52(card))
|
||||
return 0;
|
||||
|
||||
cmd.opcode = MMC_SET_BLOCKLEN;
|
||||
|
@ -2263,7 +2279,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
|
|||
}
|
||||
}
|
||||
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_DDR);
|
||||
if (mmc_host_is_spi(host)) {
|
||||
host->ios.chip_select = MMC_CS_HIGH;
|
||||
host->ios.bus_mode = MMC_BUSMODE_PUSHPULL;
|
||||
|
@ -2403,6 +2418,11 @@ void mmc_rescan(struct work_struct *work)
|
|||
container_of(work, struct mmc_host, detect.work);
|
||||
int i;
|
||||
|
||||
if (host->trigger_card_event && host->ops->card_event) {
|
||||
host->ops->card_event(host);
|
||||
host->trigger_card_event = false;
|
||||
}
|
||||
|
||||
if (host->rescan_disable)
|
||||
return;
|
||||
|
||||
|
|
|
@ -135,8 +135,14 @@ static int mmc_ios_show(struct seq_file *s, void *data)
|
|||
case MMC_TIMING_UHS_DDR50:
|
||||
str = "sd uhs DDR50";
|
||||
break;
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
str = "mmc DDR52";
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
str = "mmc high-speed SDR200";
|
||||
str = "mmc HS200";
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
str = "mmc HS400";
|
||||
break;
|
||||
default:
|
||||
str = "invalid";
|
||||
|
|
|
@ -447,6 +447,10 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
|
||||
if (of_find_property(np, "mmc-hs200-1_2v", &len))
|
||||
host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
||||
if (of_find_property(np, "mmc-hs400-1_8v", &len))
|
||||
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
|
||||
if (of_find_property(np, "mmc-hs400-1_2v", &len))
|
||||
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -240,31 +240,62 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
|
|||
static void mmc_select_card_type(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u8 card_type = card->ext_csd.raw_card_type & EXT_CSD_CARD_TYPE_MASK;
|
||||
u8 card_type = card->ext_csd.raw_card_type;
|
||||
u32 caps = host->caps, caps2 = host->caps2;
|
||||
unsigned int hs_max_dtr = 0;
|
||||
|
||||
if (card_type & EXT_CSD_CARD_TYPE_26)
|
||||
hs_max_dtr = MMC_HIGH_26_MAX_DTR;
|
||||
unsigned int hs_max_dtr = 0, hs200_max_dtr = 0;
|
||||
unsigned int avail_type = 0;
|
||||
|
||||
if (caps & MMC_CAP_MMC_HIGHSPEED &&
|
||||
card_type & EXT_CSD_CARD_TYPE_52)
|
||||
card_type & EXT_CSD_CARD_TYPE_HS_26) {
|
||||
hs_max_dtr = MMC_HIGH_26_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS_26;
|
||||
}
|
||||
|
||||
if (caps & MMC_CAP_MMC_HIGHSPEED &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS_52) {
|
||||
hs_max_dtr = MMC_HIGH_52_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS_52;
|
||||
}
|
||||
|
||||
if ((caps & MMC_CAP_1_8V_DDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) ||
|
||||
(caps & MMC_CAP_1_2V_DDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_DDR_1_2V))
|
||||
if (caps & MMC_CAP_1_8V_DDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_DDR_1_8V) {
|
||||
hs_max_dtr = MMC_HIGH_DDR_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_DDR_1_8V;
|
||||
}
|
||||
|
||||
if ((caps2 & MMC_CAP2_HS200_1_8V_SDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_SDR_1_8V) ||
|
||||
(caps2 & MMC_CAP2_HS200_1_2V_SDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_SDR_1_2V))
|
||||
hs_max_dtr = MMC_HS200_MAX_DTR;
|
||||
if (caps & MMC_CAP_1_2V_DDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {
|
||||
hs_max_dtr = MMC_HIGH_DDR_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_DDR_1_2V;
|
||||
}
|
||||
|
||||
if (caps2 & MMC_CAP2_HS200_1_8V_SDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS200_1_8V) {
|
||||
hs200_max_dtr = MMC_HS200_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS200_1_8V;
|
||||
}
|
||||
|
||||
if (caps2 & MMC_CAP2_HS200_1_2V_SDR &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS200_1_2V) {
|
||||
hs200_max_dtr = MMC_HS200_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS200_1_2V;
|
||||
}
|
||||
|
||||
if (caps2 & MMC_CAP2_HS400_1_8V &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS400_1_8V) {
|
||||
hs200_max_dtr = MMC_HS200_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_8V;
|
||||
}
|
||||
|
||||
if (caps2 & MMC_CAP2_HS400_1_2V &&
|
||||
card_type & EXT_CSD_CARD_TYPE_HS400_1_2V) {
|
||||
hs200_max_dtr = MMC_HS200_MAX_DTR;
|
||||
avail_type |= EXT_CSD_CARD_TYPE_HS400_1_2V;
|
||||
}
|
||||
|
||||
card->ext_csd.hs_max_dtr = hs_max_dtr;
|
||||
card->ext_csd.card_type = card_type;
|
||||
card->ext_csd.hs200_max_dtr = hs200_max_dtr;
|
||||
card->mmc_avail_type = avail_type;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -480,6 +511,8 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
ext_csd[EXT_CSD_PWR_CL_DDR_52_195];
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_DDR_52_360];
|
||||
card->ext_csd.raw_pwr_cl_ddr_200_360 =
|
||||
ext_csd[EXT_CSD_PWR_CL_DDR_200_360];
|
||||
}
|
||||
|
||||
if (card->ext_csd.rev >= 5) {
|
||||
|
@ -646,7 +679,10 @@ static int mmc_compare_ext_csds(struct mmc_card *card, unsigned bus_width)
|
|||
(card->ext_csd.raw_pwr_cl_ddr_52_195 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) &&
|
||||
(card->ext_csd.raw_pwr_cl_ddr_52_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]));
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) &&
|
||||
(card->ext_csd.raw_pwr_cl_ddr_200_360 ==
|
||||
bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360]));
|
||||
|
||||
if (err)
|
||||
err = -EINVAL;
|
||||
|
||||
|
@ -694,18 +730,10 @@ static struct attribute *mmc_std_attrs[] = {
|
|||
&dev_attr_rel_sectors.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group mmc_std_attr_group = {
|
||||
.attrs = mmc_std_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *mmc_attr_groups[] = {
|
||||
&mmc_std_attr_group,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(mmc_std);
|
||||
|
||||
static struct device_type mmc_type = {
|
||||
.groups = mmc_attr_groups,
|
||||
.groups = mmc_std_groups,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -714,17 +742,13 @@ static struct device_type mmc_type = {
|
|||
* extended CSD register, select it by executing the
|
||||
* mmc_switch command.
|
||||
*/
|
||||
static int mmc_select_powerclass(struct mmc_card *card,
|
||||
unsigned int bus_width)
|
||||
static int __mmc_select_powerclass(struct mmc_card *card,
|
||||
unsigned int bus_width)
|
||||
{
|
||||
int err = 0;
|
||||
struct mmc_host *host = card->host;
|
||||
struct mmc_ext_csd *ext_csd = &card->ext_csd;
|
||||
unsigned int pwrclass_val = 0;
|
||||
struct mmc_host *host;
|
||||
|
||||
BUG_ON(!card);
|
||||
|
||||
host = card->host;
|
||||
BUG_ON(!host);
|
||||
int err = 0;
|
||||
|
||||
/* Power class selection is supported for versions >= 4.0 */
|
||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
||||
|
@ -736,14 +760,14 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
|||
|
||||
switch (1 << host->ios.vdd) {
|
||||
case MMC_VDD_165_195:
|
||||
if (host->ios.clock <= 26000000)
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_26_195;
|
||||
else if (host->ios.clock <= 52000000)
|
||||
if (host->ios.clock <= MMC_HIGH_26_MAX_DTR)
|
||||
pwrclass_val = ext_csd->raw_pwr_cl_26_195;
|
||||
else if (host->ios.clock <= MMC_HIGH_52_MAX_DTR)
|
||||
pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
card->ext_csd.raw_pwr_cl_52_195 :
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_195;
|
||||
else if (host->ios.clock <= 200000000)
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_200_195;
|
||||
ext_csd->raw_pwr_cl_52_195 :
|
||||
ext_csd->raw_pwr_cl_ddr_52_195;
|
||||
else if (host->ios.clock <= MMC_HS200_MAX_DTR)
|
||||
pwrclass_val = ext_csd->raw_pwr_cl_200_195;
|
||||
break;
|
||||
case MMC_VDD_27_28:
|
||||
case MMC_VDD_28_29:
|
||||
|
@ -754,14 +778,16 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
|||
case MMC_VDD_33_34:
|
||||
case MMC_VDD_34_35:
|
||||
case MMC_VDD_35_36:
|
||||
if (host->ios.clock <= 26000000)
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_26_360;
|
||||
else if (host->ios.clock <= 52000000)
|
||||
if (host->ios.clock <= MMC_HIGH_26_MAX_DTR)
|
||||
pwrclass_val = ext_csd->raw_pwr_cl_26_360;
|
||||
else if (host->ios.clock <= MMC_HIGH_52_MAX_DTR)
|
||||
pwrclass_val = (bus_width <= EXT_CSD_BUS_WIDTH_8) ?
|
||||
card->ext_csd.raw_pwr_cl_52_360 :
|
||||
card->ext_csd.raw_pwr_cl_ddr_52_360;
|
||||
else if (host->ios.clock <= 200000000)
|
||||
pwrclass_val = card->ext_csd.raw_pwr_cl_200_360;
|
||||
ext_csd->raw_pwr_cl_52_360 :
|
||||
ext_csd->raw_pwr_cl_ddr_52_360;
|
||||
else if (host->ios.clock <= MMC_HS200_MAX_DTR)
|
||||
pwrclass_val = (bus_width == EXT_CSD_DDR_BUS_WIDTH_8) ?
|
||||
ext_csd->raw_pwr_cl_ddr_200_360 :
|
||||
ext_csd->raw_pwr_cl_200_360;
|
||||
break;
|
||||
default:
|
||||
pr_warning("%s: Voltage range not supported "
|
||||
|
@ -787,40 +813,79 @@ static int mmc_select_powerclass(struct mmc_card *card,
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Selects the desired buswidth and switch to the HS200 mode
|
||||
* if bus width set without error
|
||||
*/
|
||||
static int mmc_select_hs200(struct mmc_card *card)
|
||||
static int mmc_select_powerclass(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u32 bus_width, ext_csd_bits;
|
||||
int err, ddr;
|
||||
|
||||
/* Power class selection is supported for versions >= 4.0 */
|
||||
if (card->csd.mmca_vsn < CSD_SPEC_VER_4)
|
||||
return 0;
|
||||
|
||||
bus_width = host->ios.bus_width;
|
||||
/* Power class values are defined only for 4/8 bit bus */
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
return 0;
|
||||
|
||||
ddr = card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52;
|
||||
if (ddr)
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
|
||||
else
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
|
||||
|
||||
err = __mmc_select_powerclass(card, ext_csd_bits);
|
||||
if (err)
|
||||
pr_warn("%s: power class selection to bus width %d ddr %d failed\n",
|
||||
mmc_hostname(host), 1 << bus_width, ddr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the bus speed for the selected speed mode.
|
||||
*/
|
||||
static void mmc_set_bus_speed(struct mmc_card *card)
|
||||
{
|
||||
unsigned int max_dtr = (unsigned int)-1;
|
||||
|
||||
if ((mmc_card_hs200(card) || mmc_card_hs400(card)) &&
|
||||
max_dtr > card->ext_csd.hs200_max_dtr)
|
||||
max_dtr = card->ext_csd.hs200_max_dtr;
|
||||
else if (mmc_card_hs(card) && max_dtr > card->ext_csd.hs_max_dtr)
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
else if (max_dtr > card->csd.max_dtr)
|
||||
max_dtr = card->csd.max_dtr;
|
||||
|
||||
mmc_set_clock(card->host, max_dtr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Select the bus width amoung 4-bit and 8-bit(SDR).
|
||||
* If the bus width is changed successfully, return the selected width value.
|
||||
* Zero is returned instead of error value if the wide width is not supported.
|
||||
*/
|
||||
static int mmc_select_bus_width(struct mmc_card *card)
|
||||
{
|
||||
int idx, err = -EINVAL;
|
||||
struct mmc_host *host;
|
||||
static unsigned ext_csd_bits[] = {
|
||||
EXT_CSD_BUS_WIDTH_4,
|
||||
EXT_CSD_BUS_WIDTH_8,
|
||||
EXT_CSD_BUS_WIDTH_4,
|
||||
};
|
||||
static unsigned bus_widths[] = {
|
||||
MMC_BUS_WIDTH_4,
|
||||
MMC_BUS_WIDTH_8,
|
||||
MMC_BUS_WIDTH_4,
|
||||
};
|
||||
struct mmc_host *host = card->host;
|
||||
unsigned idx, bus_width = 0;
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!card);
|
||||
if ((card->csd.mmca_vsn < CSD_SPEC_VER_4) &&
|
||||
!(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
|
||||
return 0;
|
||||
|
||||
host = card->host;
|
||||
|
||||
if (card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_2V &&
|
||||
host->caps2 & MMC_CAP2_HS200_1_2V_SDR)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
|
||||
if (err && card->ext_csd.card_type & EXT_CSD_CARD_TYPE_SDR_1_8V &&
|
||||
host->caps2 & MMC_CAP2_HS200_1_8V_SDR)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 1 : 0;
|
||||
idx = (host->caps & MMC_CAP_8_BIT_DATA) ? 0 : 1;
|
||||
|
||||
/*
|
||||
* Unlike SD, MMC cards dont have a configuration register to notify
|
||||
|
@ -828,8 +893,7 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||
* the supported bus width or compare the ext csd values of current
|
||||
* bus width and ext csd values of 1 bit mode read earlier.
|
||||
*/
|
||||
for (; idx >= 0; idx--) {
|
||||
|
||||
for (; idx < ARRAY_SIZE(bus_widths); idx++) {
|
||||
/*
|
||||
* Host is capable of 8bit transfer, then switch
|
||||
* the device to work in 8bit transfer mode. If the
|
||||
|
@ -844,26 +908,265 @@ static int mmc_select_hs200(struct mmc_card *card)
|
|||
if (err)
|
||||
continue;
|
||||
|
||||
mmc_set_bus_width(card->host, bus_widths[idx]);
|
||||
bus_width = bus_widths[idx];
|
||||
mmc_set_bus_width(host, bus_width);
|
||||
|
||||
/*
|
||||
* If controller can't handle bus width test,
|
||||
* compare ext_csd previously read in 1 bit mode
|
||||
* against ext_csd at new bus width
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
|
||||
err = mmc_compare_ext_csds(card, bus_widths[idx]);
|
||||
err = mmc_compare_ext_csds(card, bus_width);
|
||||
else
|
||||
err = mmc_bus_test(card, bus_widths[idx]);
|
||||
if (!err)
|
||||
err = mmc_bus_test(card, bus_width);
|
||||
|
||||
if (!err) {
|
||||
err = bus_width;
|
||||
break;
|
||||
} else {
|
||||
pr_warn("%s: switch to bus width %d failed\n",
|
||||
mmc_hostname(host), ext_csd_bits[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
/* switch to HS200 mode if bus width set successfully */
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch to the high-speed mode
|
||||
*/
|
||||
static int mmc_select_hs(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
if (!err)
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate wide bus and DDR if supported.
|
||||
*/
|
||||
static int mmc_select_hs_ddr(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
u32 bus_width, ext_csd_bits;
|
||||
int err = 0;
|
||||
|
||||
if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_52))
|
||||
return 0;
|
||||
|
||||
bus_width = host->ios.bus_width;
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
return 0;
|
||||
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_DDR_BUS_WIDTH_8 : EXT_CSD_DDR_BUS_WIDTH_4;
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err) {
|
||||
pr_warn("%s: switch to bus width %d ddr failed\n",
|
||||
mmc_hostname(host), 1 << bus_width);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* eMMC cards can support 3.3V to 1.2V i/o (vccq)
|
||||
* signaling.
|
||||
*
|
||||
* EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
|
||||
*
|
||||
* 1.8V vccq at 3.3V core voltage (vcc) is not required
|
||||
* in the JEDEC spec for DDR.
|
||||
*
|
||||
* Do not force change in vccq since we are obviously
|
||||
* working and no change to vccq is needed.
|
||||
*
|
||||
* WARNING: eMMC rules are NOT the same as SD DDR
|
||||
*/
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) {
|
||||
err = __mmc_set_signal_voltage(host,
|
||||
MMC_SIGNAL_VOLTAGE_120);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mmc_select_hs400(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* HS400 mode requires 8-bit bus width
|
||||
*/
|
||||
if (!(card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
|
||||
host->ios.bus_width == MMC_BUS_WIDTH_8))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Before switching to dual data rate operation for HS400,
|
||||
* it is required to convert from HS200 mode to HS mode.
|
||||
*/
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
mmc_set_bus_speed(card);
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
if (err) {
|
||||
pr_warn("%s: switch to high-speed from hs200 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
EXT_CSD_DDR_BUS_WIDTH_8,
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err) {
|
||||
pr_warn("%s: switch to bus width for hs400 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
if (err) {
|
||||
pr_warn("%s: switch to hs400 failed, err:%d\n",
|
||||
mmc_hostname(host), err);
|
||||
return err;
|
||||
}
|
||||
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS400);
|
||||
mmc_set_bus_speed(card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* For device supporting HS200 mode, the following sequence
|
||||
* should be done before executing the tuning process.
|
||||
* 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported)
|
||||
* 2. switch to HS200 mode
|
||||
* 3. set the clock to > 52Mhz and <=200MHz
|
||||
*/
|
||||
static int mmc_select_hs200(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
|
||||
|
||||
if (err && card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_8V)
|
||||
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
|
||||
|
||||
/* If fails try again during next card power cycle */
|
||||
if (err)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* Set the bus width(4 or 8) with host's support and
|
||||
* switch to HS200 mode if bus width is set successfully.
|
||||
*/
|
||||
err = mmc_select_bus_width(card);
|
||||
if (!IS_ERR_VALUE(err)) {
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 2,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
if (!err)
|
||||
mmc_set_timing(host, MMC_TIMING_MMC_HS200);
|
||||
}
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate High Speed or HS200 mode if supported.
|
||||
*/
|
||||
static int mmc_select_timing(struct mmc_card *card)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if ((card->csd.mmca_vsn < CSD_SPEC_VER_4 &&
|
||||
card->ext_csd.hs_max_dtr == 0))
|
||||
goto bus_speed;
|
||||
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200)
|
||||
err = mmc_select_hs200(card);
|
||||
else if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS)
|
||||
err = mmc_select_hs(card);
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
return err;
|
||||
|
||||
if (err) {
|
||||
pr_warn("%s: switch to %s failed\n",
|
||||
mmc_card_hs(card) ? "high-speed" :
|
||||
(mmc_card_hs200(card) ? "hs200" : ""),
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
}
|
||||
|
||||
bus_speed:
|
||||
/*
|
||||
* Set the bus speed to the selected bus timing.
|
||||
* If timing is not selected, backward compatible is the default.
|
||||
*/
|
||||
mmc_set_bus_speed(card);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute tuning sequence to seek the proper bus operating
|
||||
* conditions for HS200 and HS400, which sends CMD21 to the device.
|
||||
*/
|
||||
static int mmc_hs200_tuning(struct mmc_card *card)
|
||||
{
|
||||
struct mmc_host *host = card->host;
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* Timing should be adjusted to the HS400 target
|
||||
* operation frequency for tuning process
|
||||
*/
|
||||
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS400 &&
|
||||
host->ios.bus_width == MMC_BUS_WIDTH_8)
|
||||
if (host->ops->prepare_hs400_tuning)
|
||||
host->ops->prepare_hs400_tuning(host, &host->ios);
|
||||
|
||||
if (host->ops->execute_tuning) {
|
||||
mmc_host_clk_hold(host);
|
||||
err = host->ops->execute_tuning(host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
mmc_host_clk_release(host);
|
||||
|
||||
if (err)
|
||||
pr_warn("%s: tuning execution failed\n",
|
||||
mmc_hostname(host));
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
|
@ -874,9 +1177,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
struct mmc_card *oldcard)
|
||||
{
|
||||
struct mmc_card *card;
|
||||
int err, ddr = 0;
|
||||
int err;
|
||||
u32 cid[4];
|
||||
unsigned int max_dtr;
|
||||
u32 rocr;
|
||||
u8 *ext_csd = NULL;
|
||||
|
||||
|
@ -1068,206 +1370,34 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
|||
}
|
||||
|
||||
/*
|
||||
* Activate high speed (if supported)
|
||||
* Select timing interface
|
||||
*/
|
||||
if (card->ext_csd.hs_max_dtr != 0) {
|
||||
err = 0;
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
host->caps2 & MMC_CAP2_HS200)
|
||||
err = mmc_select_hs200(card);
|
||||
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
|
||||
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_HS_TIMING, 1,
|
||||
card->ext_csd.generic_cmd6_time,
|
||||
true, true, true);
|
||||
err = mmc_select_timing(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
if (err && err != -EBADMSG)
|
||||
goto free_card;
|
||||
|
||||
if (err) {
|
||||
pr_warning("%s: switch to highspeed failed\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
} else {
|
||||
if (card->ext_csd.hs_max_dtr > 52000000 &&
|
||||
host->caps2 & MMC_CAP2_HS200) {
|
||||
mmc_card_set_hs200(card);
|
||||
mmc_set_timing(card->host,
|
||||
MMC_TIMING_MMC_HS200);
|
||||
} else {
|
||||
mmc_card_set_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute bus speed.
|
||||
*/
|
||||
max_dtr = (unsigned int)-1;
|
||||
|
||||
if (mmc_card_highspeed(card) || mmc_card_hs200(card)) {
|
||||
if (max_dtr > card->ext_csd.hs_max_dtr)
|
||||
max_dtr = card->ext_csd.hs_max_dtr;
|
||||
if (mmc_card_highspeed(card) && (max_dtr > 52000000))
|
||||
max_dtr = 52000000;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
max_dtr = card->csd.max_dtr;
|
||||
}
|
||||
|
||||
mmc_set_clock(host, max_dtr);
|
||||
|
||||
/*
|
||||
* Indicate DDR mode (if supported).
|
||||
*/
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
|
||||
&& (host->caps & MMC_CAP_1_8V_DDR))
|
||||
ddr = MMC_1_8V_DDR_MODE;
|
||||
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
&& (host->caps & MMC_CAP_1_2V_DDR))
|
||||
ddr = MMC_1_2V_DDR_MODE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Indicate HS200 SDR mode (if supported).
|
||||
*/
|
||||
if (mmc_card_hs200(card)) {
|
||||
u32 ext_csd_bits;
|
||||
u32 bus_width = card->host->ios.bus_width;
|
||||
|
||||
/*
|
||||
* For devices supporting HS200 mode, the bus width has
|
||||
* to be set before executing the tuning function. If
|
||||
* set before tuning, then device will respond with CRC
|
||||
* errors for responses on CMD line. So for HS200 the
|
||||
* sequence will be
|
||||
* 1. set bus width 4bit / 8 bit (1 bit not supported)
|
||||
* 2. switch to HS200 mode
|
||||
* 3. set the clock to > 52Mhz <=200MHz and
|
||||
* 4. execute tuning for HS200
|
||||
*/
|
||||
if ((host->caps2 & MMC_CAP2_HS200) &&
|
||||
card->host->ops->execute_tuning) {
|
||||
mmc_host_clk_hold(card->host);
|
||||
err = card->host->ops->execute_tuning(card->host,
|
||||
MMC_SEND_TUNING_BLOCK_HS200);
|
||||
mmc_host_clk_release(card->host);
|
||||
}
|
||||
if (err) {
|
||||
pr_warning("%s: tuning execution failed\n",
|
||||
mmc_hostname(card->host));
|
||||
goto err;
|
||||
}
|
||||
|
||||
ext_csd_bits = (bus_width == MMC_BUS_WIDTH_8) ?
|
||||
EXT_CSD_BUS_WIDTH_8 : EXT_CSD_BUS_WIDTH_4;
|
||||
err = mmc_select_powerclass(card, ext_csd_bits);
|
||||
err = mmc_hs200_tuning(card);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to bus width %d"
|
||||
" failed\n", mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
goto err;
|
||||
|
||||
err = mmc_select_hs400(card);
|
||||
if (err)
|
||||
goto err;
|
||||
} else if (mmc_card_hs(card)) {
|
||||
/* Select the desired bus width optionally */
|
||||
err = mmc_select_bus_width(card);
|
||||
if (!IS_ERR_VALUE(err)) {
|
||||
err = mmc_select_hs_ddr(card);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate wide bus and DDR (if supported).
|
||||
* Choose the power class with selected bus interface
|
||||
*/
|
||||
if (!mmc_card_hs200(card) &&
|
||||
(card->csd.mmca_vsn >= CSD_SPEC_VER_4) &&
|
||||
(host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) {
|
||||
static unsigned ext_csd_bits[][2] = {
|
||||
{ EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 },
|
||||
{ EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 },
|
||||
{ EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 },
|
||||
};
|
||||
static unsigned bus_widths[] = {
|
||||
MMC_BUS_WIDTH_8,
|
||||
MMC_BUS_WIDTH_4,
|
||||
MMC_BUS_WIDTH_1
|
||||
};
|
||||
unsigned idx, bus_width = 0;
|
||||
|
||||
if (host->caps & MMC_CAP_8_BIT_DATA)
|
||||
idx = 0;
|
||||
else
|
||||
idx = 1;
|
||||
for (; idx < ARRAY_SIZE(bus_widths); idx++) {
|
||||
bus_width = bus_widths[idx];
|
||||
if (bus_width == MMC_BUS_WIDTH_1)
|
||||
ddr = 0; /* no DDR for 1-bit width */
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][0]);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d failed\n",
|
||||
mmc_hostname(card->host),
|
||||
1 << bus_width);
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx][0],
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (!err) {
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
|
||||
/*
|
||||
* If controller can't handle bus width test,
|
||||
* compare ext_csd previously read in 1 bit mode
|
||||
* against ext_csd at new bus width
|
||||
*/
|
||||
if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST))
|
||||
err = mmc_compare_ext_csds(card,
|
||||
bus_width);
|
||||
else
|
||||
err = mmc_bus_test(card, bus_width);
|
||||
if (!err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && ddr) {
|
||||
err = mmc_select_powerclass(card, ext_csd_bits[idx][1]);
|
||||
if (err)
|
||||
pr_warning("%s: power class selection to "
|
||||
"bus width %d ddr %d failed\n",
|
||||
mmc_hostname(card->host),
|
||||
1 << bus_width, ddr);
|
||||
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BUS_WIDTH,
|
||||
ext_csd_bits[idx][1],
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
}
|
||||
if (err) {
|
||||
pr_warning("%s: switch to bus width %d ddr %d "
|
||||
"failed\n", mmc_hostname(card->host),
|
||||
1 << bus_width, ddr);
|
||||
goto free_card;
|
||||
} else if (ddr) {
|
||||
/*
|
||||
* eMMC cards can support 3.3V to 1.2V i/o (vccq)
|
||||
* signaling.
|
||||
*
|
||||
* EXT_CSD_CARD_TYPE_DDR_1_8V means 3.3V or 1.8V vccq.
|
||||
*
|
||||
* 1.8V vccq at 3.3V core voltage (vcc) is not required
|
||||
* in the JEDEC spec for DDR.
|
||||
*
|
||||
* Do not force change in vccq since we are obviously
|
||||
* working and no change to vccq is needed.
|
||||
*
|
||||
* WARNING: eMMC rules are NOT the same as SD DDR
|
||||
*/
|
||||
if (ddr == MMC_1_2V_DDR_MODE) {
|
||||
err = __mmc_set_signal_voltage(host,
|
||||
MMC_SIGNAL_VOLTAGE_120);
|
||||
if (err)
|
||||
goto err;
|
||||
}
|
||||
mmc_card_set_ddr_mode(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_UHS_DDR50);
|
||||
mmc_set_bus_width(card->host, bus_width);
|
||||
}
|
||||
}
|
||||
mmc_select_powerclass(card);
|
||||
|
||||
/*
|
||||
* Enable HPI feature (if supported)
|
||||
|
@ -1507,7 +1637,6 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
|
|||
err = mmc_sleep(host);
|
||||
else if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
|
||||
if (!err) {
|
||||
mmc_power_off(host);
|
||||
|
@ -1637,7 +1766,6 @@ static int mmc_power_restore(struct mmc_host *host)
|
|||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200);
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_init_card(host, host->card->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -707,18 +707,10 @@ static struct attribute *sd_std_attrs[] = {
|
|||
&dev_attr_serial.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group sd_std_attr_group = {
|
||||
.attrs = sd_std_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *sd_attr_groups[] = {
|
||||
&sd_std_attr_group,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(sd_std);
|
||||
|
||||
struct device_type sd_type = {
|
||||
.groups = sd_attr_groups,
|
||||
.groups = sd_std_groups,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -895,7 +887,7 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
|
|||
{
|
||||
unsigned max_dtr = (unsigned int)-1;
|
||||
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if (mmc_card_hs(card)) {
|
||||
if (max_dtr > card->sw_caps.hs_max_dtr)
|
||||
max_dtr = card->sw_caps.hs_max_dtr;
|
||||
} else if (max_dtr > card->csd.max_dtr) {
|
||||
|
@ -905,12 +897,6 @@ unsigned mmc_sd_get_max_clock(struct mmc_card *card)
|
|||
return max_dtr;
|
||||
}
|
||||
|
||||
void mmc_sd_go_highspeed(struct mmc_card *card)
|
||||
{
|
||||
mmc_card_set_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the detection and initialisation of a card.
|
||||
*
|
||||
|
@ -985,16 +971,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
|
|||
err = mmc_sd_init_uhs_card(card);
|
||||
if (err)
|
||||
goto free_card;
|
||||
|
||||
/* Card is an ultra-high-speed card */
|
||||
mmc_card_set_uhs(card);
|
||||
} else {
|
||||
/*
|
||||
* Attempt to change to high-speed (if supported)
|
||||
*/
|
||||
err = mmc_sd_switch_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
||||
else if (err)
|
||||
goto free_card;
|
||||
|
||||
|
@ -1089,7 +1072,7 @@ static int _mmc_sd_suspend(struct mmc_host *host)
|
|||
|
||||
if (!mmc_host_is_spi(host))
|
||||
err = mmc_deselect_cards(host);
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
|
||||
if (!err) {
|
||||
mmc_power_off(host);
|
||||
mmc_card_set_suspended(host->card);
|
||||
|
@ -1198,7 +1181,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)
|
|||
{
|
||||
int ret;
|
||||
|
||||
host->card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
mmc_claim_host(host);
|
||||
ret = mmc_sd_init_card(host, host->card->ocr, host->card);
|
||||
mmc_release_host(host);
|
||||
|
|
|
@ -12,6 +12,5 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
|
|||
bool reinit);
|
||||
unsigned mmc_sd_get_max_clock(struct mmc_card *card);
|
||||
int mmc_sd_switch_hs(struct mmc_card *card);
|
||||
void mmc_sd_go_highspeed(struct mmc_card *card);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -363,7 +363,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
|||
{
|
||||
unsigned max_dtr;
|
||||
|
||||
if (mmc_card_highspeed(card)) {
|
||||
if (mmc_card_hs(card)) {
|
||||
/*
|
||||
* The SDIO specification doesn't mention how
|
||||
* the CIS transfer speed register relates to
|
||||
|
@ -733,7 +733,6 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
mmc_set_clock(host, card->cis.max_dtr);
|
||||
|
||||
if (card->cccr.high_speed) {
|
||||
mmc_card_set_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
||||
}
|
||||
|
||||
|
@ -792,16 +791,13 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
|
|||
err = mmc_sdio_init_uhs_card(card);
|
||||
if (err)
|
||||
goto remove;
|
||||
|
||||
/* Card is an ultra-high-speed card */
|
||||
mmc_card_set_uhs(card);
|
||||
} else {
|
||||
/*
|
||||
* Switch to high-speed (if supported).
|
||||
*/
|
||||
err = sdio_enable_hs(card);
|
||||
if (err > 0)
|
||||
mmc_sd_go_highspeed(card);
|
||||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
||||
else if (err)
|
||||
goto remove;
|
||||
|
||||
|
@ -943,40 +939,21 @@ static int mmc_sdio_pre_suspend(struct mmc_host *host)
|
|||
*/
|
||||
static int mmc_sdio_suspend(struct mmc_host *host)
|
||||
{
|
||||
int i, err = 0;
|
||||
|
||||
for (i = 0; i < host->card->sdio_funcs; i++) {
|
||||
struct sdio_func *func = host->card->sdio_func[i];
|
||||
if (func && sdio_func_present(func) && func->dev.driver) {
|
||||
const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
||||
err = pmops->suspend(&func->dev);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (err && --i >= 0) {
|
||||
struct sdio_func *func = host->card->sdio_func[i];
|
||||
if (func && sdio_func_present(func) && func->dev.driver) {
|
||||
const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
||||
pmops->resume(&func->dev);
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) {
|
||||
mmc_claim_host(host);
|
||||
sdio_disable_wide(host->card);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
|
||||
if (!err && !mmc_card_keep_power(host))
|
||||
if (!mmc_card_keep_power(host))
|
||||
mmc_power_off(host);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_sdio_resume(struct mmc_host *host)
|
||||
{
|
||||
int i, err = 0;
|
||||
int err = 0;
|
||||
|
||||
BUG_ON(!host);
|
||||
BUG_ON(!host->card);
|
||||
|
@ -1019,24 +996,6 @@ static int mmc_sdio_resume(struct mmc_host *host)
|
|||
wake_up_process(host->sdio_irq_thread);
|
||||
mmc_release_host(host);
|
||||
|
||||
/*
|
||||
* If the card looked to be the same as before suspending, then
|
||||
* we proceed to resume all card functions. If one of them returns
|
||||
* an error then we simply return that error to the core and the
|
||||
* card will be redetected as new. It is the responsibility of
|
||||
* the function driver to perform further tests with the extra
|
||||
* knowledge it has of the card to confirm the card is indeed the
|
||||
* same as before suspending (same MAC address for network cards,
|
||||
* etc.) and return an error otherwise.
|
||||
*/
|
||||
for (i = 0; !err && i < host->card->sdio_funcs; i++) {
|
||||
struct sdio_func *func = host->card->sdio_func[i];
|
||||
if (func && sdio_func_present(func) && func->dev.driver) {
|
||||
const struct dev_pm_ops *pmops = func->dev.driver->pm;
|
||||
err = pmops->resume(&func->dev);
|
||||
}
|
||||
}
|
||||
|
||||
host->pm_flags &= ~MMC_PM_KEEP_POWER;
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -197,20 +197,8 @@ static int sdio_bus_remove(struct device *dev)
|
|||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int pm_no_operation(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* Prevent the PM core from calling SDIO device drivers' suspend
|
||||
* callback routines, which it is not supposed to do, by using this
|
||||
* empty function as the bus type suspend callaback for SDIO.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdio_bus_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_no_operation, pm_no_operation)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_generic_suspend, pm_generic_resume)
|
||||
SET_RUNTIME_PM_OPS(
|
||||
pm_generic_runtime_suspend,
|
||||
pm_generic_runtime_resume,
|
||||
|
|
|
@ -90,6 +90,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void sdio_run_irqs(struct mmc_host *host)
|
||||
{
|
||||
mmc_claim_host(host);
|
||||
host->sdio_irq_pending = true;
|
||||
process_sdio_pending_irqs(host);
|
||||
mmc_release_host(host);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdio_run_irqs);
|
||||
|
||||
static int sdio_irq_thread(void *_host)
|
||||
{
|
||||
struct mmc_host *host = _host;
|
||||
|
@ -189,14 +198,20 @@ static int sdio_card_irq_get(struct mmc_card *card)
|
|||
WARN_ON(!host->claimed);
|
||||
|
||||
if (!host->sdio_irqs++) {
|
||||
atomic_set(&host->sdio_irq_thread_abort, 0);
|
||||
host->sdio_irq_thread =
|
||||
kthread_run(sdio_irq_thread, host, "ksdioirqd/%s",
|
||||
mmc_hostname(host));
|
||||
if (IS_ERR(host->sdio_irq_thread)) {
|
||||
int err = PTR_ERR(host->sdio_irq_thread);
|
||||
host->sdio_irqs--;
|
||||
return err;
|
||||
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
|
||||
atomic_set(&host->sdio_irq_thread_abort, 0);
|
||||
host->sdio_irq_thread =
|
||||
kthread_run(sdio_irq_thread, host,
|
||||
"ksdioirqd/%s", mmc_hostname(host));
|
||||
if (IS_ERR(host->sdio_irq_thread)) {
|
||||
int err = PTR_ERR(host->sdio_irq_thread);
|
||||
host->sdio_irqs--;
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->enable_sdio_irq(host, 1);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,8 +226,14 @@ static int sdio_card_irq_put(struct mmc_card *card)
|
|||
BUG_ON(host->sdio_irqs < 1);
|
||||
|
||||
if (!--host->sdio_irqs) {
|
||||
atomic_set(&host->sdio_irq_thread_abort, 1);
|
||||
kthread_stop(host->sdio_irq_thread);
|
||||
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
|
||||
atomic_set(&host->sdio_irq_thread_abort, 1);
|
||||
kthread_stop(host->sdio_irq_thread);
|
||||
} else {
|
||||
mmc_host_clk_hold(host);
|
||||
host->ops->enable_sdio_irq(host, 0);
|
||||
mmc_host_clk_release(host);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -32,9 +32,7 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
|
|||
/* Schedule a card detection after a debounce timeout */
|
||||
struct mmc_host *host = dev_id;
|
||||
|
||||
if (host->ops->card_event)
|
||||
host->ops->card_event(host);
|
||||
|
||||
host->trigger_card_event = true;
|
||||
mmc_detect_change(host, msecs_to_jiffies(200));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
|
@ -168,7 +168,7 @@ config MMC_SDHCI_ESDHC_IMX
|
|||
|
||||
config MMC_SDHCI_DOVE
|
||||
tristate "SDHCI support on Marvell's Dove SoC"
|
||||
depends on ARCH_DOVE
|
||||
depends on ARCH_DOVE || MACH_DOVE
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
|
@ -283,6 +283,15 @@ config MMC_SDHCI_BCM2835
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MOXART
|
||||
tristate "MOXART SD/MMC Host Controller support"
|
||||
depends on ARCH_MOXART && MMC
|
||||
help
|
||||
This selects support for the MOXART SD/MMC Host Controller.
|
||||
MOXA provides one multi-functional card reader which can
|
||||
be found on some embedded hardware such as UC-7112-LX.
|
||||
If you have a controller with this interface, say Y here.
|
||||
|
||||
config MMC_OMAP
|
||||
tristate "TI OMAP Multimedia Card Interface support"
|
||||
depends on ARCH_OMAP
|
||||
|
@ -688,6 +697,12 @@ config MMC_WMT
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called wmt-sdmmc.
|
||||
|
||||
config MMC_USDHI6ROL0
|
||||
tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support"
|
||||
help
|
||||
This selects support for the Renesas USDHI6ROL0 SD/SDIO
|
||||
Host Controller
|
||||
|
||||
config MMC_REALTEK_PCI
|
||||
tristate "Realtek PCI-E SD/MMC Card Interface Driver"
|
||||
depends on MFD_RTSX_PCI
|
||||
|
|
|
@ -50,7 +50,9 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
|
|||
obj-$(CONFIG_MMC_VUB300) += vub300.o
|
||||
obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
||||
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
||||
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
|
||||
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
|
||||
|
||||
obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o
|
||||
obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
|
||||
|
|
|
@ -820,16 +820,9 @@ static void atmci_pdc_complete(struct atmel_mci *host)
|
|||
|
||||
atmci_pdc_cleanup(host);
|
||||
|
||||
/*
|
||||
* If the card was removed, data will be NULL. No point trying
|
||||
* to send the stop command or waiting for NBUSY in this case.
|
||||
*/
|
||||
if (host->data) {
|
||||
dev_dbg(&host->pdev->dev,
|
||||
"(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
dev_dbg(&host->pdev->dev, "(%s) set pending xfer complete\n", __func__);
|
||||
atmci_set_pending(host, EVENT_XFER_COMPLETE);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
static void atmci_dma_cleanup(struct atmel_mci *host)
|
||||
|
|
|
@ -187,7 +187,7 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
|
|||
unsigned long actual;
|
||||
u8 div = priv->ciu_div + 1;
|
||||
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50) {
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52) {
|
||||
mci_writel(host, CLKSEL, priv->ddr_timing);
|
||||
/* Should be double rate for DDR mode */
|
||||
if (ios->bus_width == MMC_BUS_WIDTH_8)
|
||||
|
@ -386,8 +386,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode,
|
|||
|
||||
/* Common capabilities of Exynos4/Exynos5 SoC */
|
||||
static unsigned long exynos_dwmmc_caps[4] = {
|
||||
MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
|
||||
MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA | MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
MMC_CAP_CMD23,
|
||||
|
@ -426,7 +425,7 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
|
|||
return dw_mci_pltfm_register(pdev, drv_data);
|
||||
}
|
||||
|
||||
const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
static const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
|
||||
.resume_noirq = dw_mci_exynos_resume_noirq,
|
||||
.thaw_noirq = dw_mci_exynos_resume_noirq,
|
||||
|
|
|
@ -235,12 +235,6 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
|
|||
}
|
||||
#endif /* defined(CONFIG_DEBUG_FS) */
|
||||
|
||||
static void dw_mci_set_timeout(struct dw_mci *host)
|
||||
{
|
||||
/* timeout (maximum) */
|
||||
mci_writel(host, TMOUT, 0xffffffff);
|
||||
}
|
||||
|
||||
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
||||
{
|
||||
struct mmc_data *data;
|
||||
|
@ -257,9 +251,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
(cmd->opcode == SD_IO_RW_DIRECT &&
|
||||
((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT))
|
||||
cmdr |= SDMMC_CMD_STOP;
|
||||
else
|
||||
if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
|
||||
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
||||
else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
|
||||
cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
|
||||
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
/* We expect a response, so set this bit */
|
||||
|
@ -850,8 +843,6 @@ static void __dw_mci_start_request(struct dw_mci *host,
|
|||
u32 cmdflags;
|
||||
|
||||
mrq = slot->mrq;
|
||||
if (host->pdata->select_slot)
|
||||
host->pdata->select_slot(slot->id);
|
||||
|
||||
host->cur_slot = slot;
|
||||
host->mrq = mrq;
|
||||
|
@ -864,7 +855,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
|
|||
|
||||
data = cmd->data;
|
||||
if (data) {
|
||||
dw_mci_set_timeout(host);
|
||||
mci_writel(host, TMOUT, 0xFFFFFFFF);
|
||||
mci_writel(host, BYTCNT, data->blksz*data->blocks);
|
||||
mci_writel(host, BLKSIZ, data->blksz);
|
||||
}
|
||||
|
@ -962,7 +953,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
regs = mci_readl(slot->host, UHS_REG);
|
||||
|
||||
/* DDR mode set */
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52)
|
||||
regs |= ((0x1 << slot->id) << 16);
|
||||
else
|
||||
regs &= ~((0x1 << slot->id) << 16);
|
||||
|
@ -985,17 +976,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
switch (ios->power_mode) {
|
||||
case MMC_POWER_UP:
|
||||
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
|
||||
/* Power up slot */
|
||||
if (slot->host->pdata->setpower)
|
||||
slot->host->pdata->setpower(slot->id, mmc->ocr_avail);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs |= (1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
break;
|
||||
case MMC_POWER_OFF:
|
||||
/* Power down slot */
|
||||
if (slot->host->pdata->setpower)
|
||||
slot->host->pdata->setpower(slot->id, 0);
|
||||
regs = mci_readl(slot->host, PWREN);
|
||||
regs &= ~(1 << slot->id);
|
||||
mci_writel(slot->host, PWREN, regs);
|
||||
|
@ -1009,15 +994,13 @@ static int dw_mci_get_ro(struct mmc_host *mmc)
|
|||
{
|
||||
int read_only;
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci_board *brd = slot->host->pdata;
|
||||
int gpio_ro = mmc_gpio_get_ro(mmc);
|
||||
|
||||
/* Use platform get_ro function, else try on board write protect */
|
||||
if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT)
|
||||
read_only = 0;
|
||||
else if (brd->get_ro)
|
||||
read_only = brd->get_ro(slot->id);
|
||||
else if (gpio_is_valid(slot->wp_gpio))
|
||||
read_only = gpio_get_value(slot->wp_gpio);
|
||||
else if (!IS_ERR_VALUE(gpio_ro))
|
||||
read_only = gpio_ro;
|
||||
else
|
||||
read_only =
|
||||
mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0;
|
||||
|
@ -1039,8 +1022,6 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
|
|||
/* Use platform get_cd function, else try onboard card detect */
|
||||
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
present = 1;
|
||||
else if (brd->get_cd)
|
||||
present = !brd->get_cd(slot->id);
|
||||
else if (!IS_ERR_VALUE(gpio_cd))
|
||||
present = gpio_cd;
|
||||
else
|
||||
|
@ -1248,7 +1229,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
|
|||
data->error = -EIO;
|
||||
}
|
||||
|
||||
dev_err(host->dev, "data error, status 0x%08x\n", status);
|
||||
dev_dbg(host->dev, "data error, status 0x%08x\n", status);
|
||||
|
||||
/*
|
||||
* After an error, there may be data lingering
|
||||
|
@ -2045,86 +2026,15 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
|||
|
||||
return quirks;
|
||||
}
|
||||
|
||||
/* find out bus-width for a given slot */
|
||||
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
|
||||
{
|
||||
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
|
||||
u32 bus_wd = 1;
|
||||
|
||||
if (!np)
|
||||
return 1;
|
||||
|
||||
if (of_property_read_u32(np, "bus-width", &bus_wd))
|
||||
dev_err(dev, "bus-width property not found, assuming width"
|
||||
" as 1\n");
|
||||
return bus_wd;
|
||||
}
|
||||
|
||||
/* find the write protect gpio for a given slot; or -1 if none specified */
|
||||
static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
|
||||
{
|
||||
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
|
||||
int gpio;
|
||||
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
gpio = of_get_named_gpio(np, "wp-gpios", 0);
|
||||
|
||||
/* Having a missing entry is valid; return silently */
|
||||
if (!gpio_is_valid(gpio))
|
||||
return -EINVAL;
|
||||
|
||||
if (devm_gpio_request(dev, gpio, "dw-mci-wp")) {
|
||||
dev_warn(dev, "gpio [%d] request failed\n", gpio);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return gpio;
|
||||
}
|
||||
|
||||
/* find the cd gpio for a given slot */
|
||||
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
|
||||
int gpio;
|
||||
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
gpio = of_get_named_gpio(np, "cd-gpios", 0);
|
||||
|
||||
/* Having a missing entry is valid; return silently */
|
||||
if (!gpio_is_valid(gpio))
|
||||
return;
|
||||
|
||||
if (mmc_gpio_request_cd(mmc, gpio, 0))
|
||||
dev_warn(dev, "gpio [%d] request failed\n", gpio);
|
||||
}
|
||||
#else /* CONFIG_OF */
|
||||
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static u32 dw_mci_of_get_bus_wd(struct device *dev, u8 slot)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
|
||||
struct mmc_host *mmc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
||||
|
@ -2134,7 +2044,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int ctrl_id, ret;
|
||||
u32 freq[2];
|
||||
u8 bus_width;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
|
||||
if (!mmc)
|
||||
|
@ -2158,17 +2067,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
mmc->f_max = freq[1];
|
||||
}
|
||||
|
||||
if (host->pdata->get_ocr)
|
||||
mmc->ocr_avail = host->pdata->get_ocr(id);
|
||||
else
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
/*
|
||||
* Start with slot power disabled, it will be enabled when a card
|
||||
* is detected.
|
||||
*/
|
||||
if (host->pdata->setpower)
|
||||
host->pdata->setpower(id, 0);
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
|
@ -2189,19 +2088,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
if (host->pdata->caps2)
|
||||
mmc->caps2 = host->pdata->caps2;
|
||||
|
||||
if (host->pdata->get_bus_wd)
|
||||
bus_width = host->pdata->get_bus_wd(slot->id);
|
||||
else if (host->dev->of_node)
|
||||
bus_width = dw_mci_of_get_bus_wd(host->dev, slot->id);
|
||||
else
|
||||
bus_width = 1;
|
||||
|
||||
switch (bus_width) {
|
||||
case 8:
|
||||
mmc->caps |= MMC_CAP_8_BIT_DATA;
|
||||
case 4:
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
}
|
||||
mmc_of_parse(mmc);
|
||||
|
||||
if (host->pdata->blk_settings) {
|
||||
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
||||
|
@ -2226,8 +2113,10 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
}
|
||||
|
||||
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
|
||||
dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc);
|
||||
if (dw_mci_get_cd(mmc))
|
||||
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
else
|
||||
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret)
|
||||
|
@ -2249,10 +2138,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
|
||||
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
|
||||
{
|
||||
/* Shutdown detect IRQ */
|
||||
if (slot->host->pdata->exit)
|
||||
slot->host->pdata->exit(id);
|
||||
|
||||
/* Debugfs stuff is cleaned up by mmc core */
|
||||
mmc_remove_host(slot->mmc);
|
||||
slot->host->slot[id] = NULL;
|
||||
|
@ -2399,24 +2284,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (of_find_property(np, "keep-power-in-suspend", NULL))
|
||||
pdata->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
|
||||
if (of_find_property(np, "enable-sdio-wakeup", NULL))
|
||||
pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
|
||||
if (of_find_property(np, "supports-highspeed", NULL))
|
||||
pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL))
|
||||
pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
|
||||
|
||||
if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
|
||||
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
||||
|
||||
if (of_get_property(np, "cd-inverted", NULL))
|
||||
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
||||
|
@ -2442,9 +2312,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (!host->pdata->select_slot && host->pdata->num_slots > 1) {
|
||||
if (host->pdata->num_slots > 1) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply select_slot function\n");
|
||||
"Platform data must supply num_slots.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -2474,12 +2344,19 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz);
|
||||
if (ret)
|
||||
dev_warn(host->dev,
|
||||
"Unable to set bus rate to %ul\n",
|
||||
"Unable to set bus rate to %uHz\n",
|
||||
host->pdata->bus_hz);
|
||||
}
|
||||
host->bus_hz = clk_get_rate(host->ciu_clk);
|
||||
}
|
||||
|
||||
if (!host->bus_hz) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply bus speed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_clk_ciu;
|
||||
}
|
||||
|
||||
if (drv_data && drv_data->init) {
|
||||
ret = drv_data->init(host);
|
||||
if (ret) {
|
||||
|
@ -2516,13 +2393,6 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (!host->bus_hz) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply bus speed\n");
|
||||
ret = -ENODEV;
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
@ -2666,8 +2536,6 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
err_dmaunmap:
|
||||
if (host->use_dma && host->dma_ops->exit)
|
||||
host->dma_ops->exit(host);
|
||||
|
||||
err_regulator:
|
||||
if (host->vmmc)
|
||||
regulator_disable(host->vmmc);
|
||||
|
||||
|
|
|
@ -195,7 +195,6 @@ extern int dw_mci_resume(struct dw_mci *host);
|
|||
* @mmc: The mmc_host representing this slot.
|
||||
* @host: The MMC controller this slot is using.
|
||||
* @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX)
|
||||
* @wp_gpio: If gpio_is_valid() we'll use this to read write protect.
|
||||
* @ctype: Card type for this slot.
|
||||
* @mrq: mmc_request currently being processed or waiting to be
|
||||
* processed, or NULL when the slot is idle.
|
||||
|
@ -214,7 +213,6 @@ struct dw_mci_slot {
|
|||
struct dw_mci *host;
|
||||
|
||||
int quirks;
|
||||
int wp_gpio;
|
||||
|
||||
u32 ctype;
|
||||
|
||||
|
|
|
@ -515,10 +515,13 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
|
|||
|
||||
jz4740_mmc_send_command(host, req->stop);
|
||||
|
||||
timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE);
|
||||
if (timeout) {
|
||||
host->state = JZ4740_MMC_STATE_DONE;
|
||||
break;
|
||||
if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) {
|
||||
timeout = jz4740_mmc_poll_irq(host,
|
||||
JZ_MMC_IRQ_PRG_DONE);
|
||||
if (timeout) {
|
||||
host->state = JZ4740_MMC_STATE_DONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case JZ4740_MMC_STATE_DONE:
|
||||
break;
|
||||
|
|
|
@ -301,7 +301,8 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
|
|||
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
|
||||
clk |= MCI_ST_8BIT_BUS;
|
||||
|
||||
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
|
||||
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
|
||||
clk |= MCI_ST_UX500_NEG_EDGE;
|
||||
|
||||
mmci_write_clkreg(host, clk);
|
||||
|
@ -764,7 +765,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
|
|||
mmci_write_clkreg(host, clk);
|
||||
}
|
||||
|
||||
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
|
||||
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
|
||||
datactrl |= MCI_ST_DPSM_DDRMODE;
|
||||
|
||||
/*
|
||||
|
|
730
drivers/mmc/host/moxart-mmc.c
Normal file
730
drivers/mmc/host/moxart-mmc.c
Normal file
|
@ -0,0 +1,730 @@
|
|||
/*
|
||||
* MOXA ART MMC host driver.
|
||||
*
|
||||
* Copyright (C) 2014 Jonas Jensen
|
||||
*
|
||||
* Jonas Jensen <jonas.jensen@gmail.com>
|
||||
*
|
||||
* Based on code from
|
||||
* Moxa Technologies Co., Ltd. <www.moxa.com>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/of_dma.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define REG_COMMAND 0
|
||||
#define REG_ARGUMENT 4
|
||||
#define REG_RESPONSE0 8
|
||||
#define REG_RESPONSE1 12
|
||||
#define REG_RESPONSE2 16
|
||||
#define REG_RESPONSE3 20
|
||||
#define REG_RESPONSE_COMMAND 24
|
||||
#define REG_DATA_CONTROL 28
|
||||
#define REG_DATA_TIMER 32
|
||||
#define REG_DATA_LENGTH 36
|
||||
#define REG_STATUS 40
|
||||
#define REG_CLEAR 44
|
||||
#define REG_INTERRUPT_MASK 48
|
||||
#define REG_POWER_CONTROL 52
|
||||
#define REG_CLOCK_CONTROL 56
|
||||
#define REG_BUS_WIDTH 60
|
||||
#define REG_DATA_WINDOW 64
|
||||
#define REG_FEATURE 68
|
||||
#define REG_REVISION 72
|
||||
|
||||
/* REG_COMMAND */
|
||||
#define CMD_SDC_RESET BIT(10)
|
||||
#define CMD_EN BIT(9)
|
||||
#define CMD_APP_CMD BIT(8)
|
||||
#define CMD_LONG_RSP BIT(7)
|
||||
#define CMD_NEED_RSP BIT(6)
|
||||
#define CMD_IDX_MASK 0x3f
|
||||
|
||||
/* REG_RESPONSE_COMMAND */
|
||||
#define RSP_CMD_APP BIT(6)
|
||||
#define RSP_CMD_IDX_MASK 0x3f
|
||||
|
||||
/* REG_DATA_CONTROL */
|
||||
#define DCR_DATA_FIFO_RESET BIT(8)
|
||||
#define DCR_DATA_THRES BIT(7)
|
||||
#define DCR_DATA_EN BIT(6)
|
||||
#define DCR_DMA_EN BIT(5)
|
||||
#define DCR_DATA_WRITE BIT(4)
|
||||
#define DCR_BLK_SIZE 0x0f
|
||||
|
||||
/* REG_DATA_LENGTH */
|
||||
#define DATA_LEN_MASK 0xffffff
|
||||
|
||||
/* REG_STATUS */
|
||||
#define WRITE_PROT BIT(12)
|
||||
#define CARD_DETECT BIT(11)
|
||||
/* 1-10 below can be sent to either registers, interrupt or clear. */
|
||||
#define CARD_CHANGE BIT(10)
|
||||
#define FIFO_ORUN BIT(9)
|
||||
#define FIFO_URUN BIT(8)
|
||||
#define DATA_END BIT(7)
|
||||
#define CMD_SENT BIT(6)
|
||||
#define DATA_CRC_OK BIT(5)
|
||||
#define RSP_CRC_OK BIT(4)
|
||||
#define DATA_TIMEOUT BIT(3)
|
||||
#define RSP_TIMEOUT BIT(2)
|
||||
#define DATA_CRC_FAIL BIT(1)
|
||||
#define RSP_CRC_FAIL BIT(0)
|
||||
|
||||
#define MASK_RSP (RSP_TIMEOUT | RSP_CRC_FAIL | \
|
||||
RSP_CRC_OK | CARD_DETECT | CMD_SENT)
|
||||
|
||||
#define MASK_DATA (DATA_CRC_OK | DATA_END | \
|
||||
DATA_CRC_FAIL | DATA_TIMEOUT)
|
||||
|
||||
#define MASK_INTR_PIO (FIFO_URUN | FIFO_ORUN | CARD_CHANGE)
|
||||
|
||||
/* REG_POWER_CONTROL */
|
||||
#define SD_POWER_ON BIT(4)
|
||||
#define SD_POWER_MASK 0x0f
|
||||
|
||||
/* REG_CLOCK_CONTROL */
|
||||
#define CLK_HISPD BIT(9)
|
||||
#define CLK_OFF BIT(8)
|
||||
#define CLK_SD BIT(7)
|
||||
#define CLK_DIV_MASK 0x7f
|
||||
|
||||
/* REG_BUS_WIDTH */
|
||||
#define BUS_WIDTH_8 BIT(2)
|
||||
#define BUS_WIDTH_4 BIT(1)
|
||||
#define BUS_WIDTH_1 BIT(0)
|
||||
|
||||
#define MMC_VDD_360 23
|
||||
#define MIN_POWER (MMC_VDD_360 - SD_POWER_MASK)
|
||||
#define MAX_RETRIES 500000
|
||||
|
||||
struct moxart_host {
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
phys_addr_t reg_phys;
|
||||
|
||||
struct dma_chan *dma_chan_tx;
|
||||
struct dma_chan *dma_chan_rx;
|
||||
struct dma_async_tx_descriptor *tx_desc;
|
||||
struct mmc_host *mmc;
|
||||
struct mmc_request *mrq;
|
||||
struct scatterlist *cur_sg;
|
||||
struct completion dma_complete;
|
||||
struct completion pio_complete;
|
||||
|
||||
u32 num_sg;
|
||||
u32 data_remain;
|
||||
u32 data_len;
|
||||
u32 fifo_width;
|
||||
u32 timeout;
|
||||
u32 rate;
|
||||
|
||||
long sysclk;
|
||||
|
||||
bool have_dma;
|
||||
bool is_removed;
|
||||
};
|
||||
|
||||
static inline void moxart_init_sg(struct moxart_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
host->cur_sg = data->sg;
|
||||
host->num_sg = data->sg_len;
|
||||
host->data_remain = host->cur_sg->length;
|
||||
|
||||
if (host->data_remain > host->data_len)
|
||||
host->data_remain = host->data_len;
|
||||
}
|
||||
|
||||
static inline int moxart_next_sg(struct moxart_host *host)
|
||||
{
|
||||
int remain;
|
||||
struct mmc_data *data = host->mrq->cmd->data;
|
||||
|
||||
host->cur_sg++;
|
||||
host->num_sg--;
|
||||
|
||||
if (host->num_sg > 0) {
|
||||
host->data_remain = host->cur_sg->length;
|
||||
remain = host->data_len - data->bytes_xfered;
|
||||
if (remain > 0 && remain < host->data_remain)
|
||||
host->data_remain = remain;
|
||||
}
|
||||
|
||||
return host->num_sg;
|
||||
}
|
||||
|
||||
static int moxart_wait_for_status(struct moxart_host *host,
|
||||
u32 mask, u32 *status)
|
||||
{
|
||||
int ret = -ETIMEDOUT;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAX_RETRIES; i++) {
|
||||
*status = readl(host->base + REG_STATUS);
|
||||
if (!(*status & mask)) {
|
||||
udelay(5);
|
||||
continue;
|
||||
}
|
||||
writel(*status & mask, host->base + REG_CLEAR);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
dev_err(mmc_dev(host->mmc), "timed out waiting for status\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void moxart_send_command(struct moxart_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
u32 status, cmdctrl;
|
||||
|
||||
writel(RSP_TIMEOUT | RSP_CRC_OK |
|
||||
RSP_CRC_FAIL | CMD_SENT, host->base + REG_CLEAR);
|
||||
writel(cmd->arg, host->base + REG_ARGUMENT);
|
||||
|
||||
cmdctrl = cmd->opcode & CMD_IDX_MASK;
|
||||
if (cmdctrl == SD_APP_SET_BUS_WIDTH || cmdctrl == SD_APP_OP_COND ||
|
||||
cmdctrl == SD_APP_SEND_SCR || cmdctrl == SD_APP_SD_STATUS ||
|
||||
cmdctrl == SD_APP_SEND_NUM_WR_BLKS)
|
||||
cmdctrl |= CMD_APP_CMD;
|
||||
|
||||
if (cmd->flags & MMC_RSP_PRESENT)
|
||||
cmdctrl |= CMD_NEED_RSP;
|
||||
|
||||
if (cmd->flags & MMC_RSP_136)
|
||||
cmdctrl |= CMD_LONG_RSP;
|
||||
|
||||
writel(cmdctrl | CMD_EN, host->base + REG_COMMAND);
|
||||
|
||||
if (moxart_wait_for_status(host, MASK_RSP, &status) == -ETIMEDOUT)
|
||||
cmd->error = -ETIMEDOUT;
|
||||
|
||||
if (status & RSP_TIMEOUT) {
|
||||
cmd->error = -ETIMEDOUT;
|
||||
return;
|
||||
}
|
||||
if (status & RSP_CRC_FAIL) {
|
||||
cmd->error = -EIO;
|
||||
return;
|
||||
}
|
||||
if (status & RSP_CRC_OK) {
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
cmd->resp[3] = readl(host->base + REG_RESPONSE0);
|
||||
cmd->resp[2] = readl(host->base + REG_RESPONSE1);
|
||||
cmd->resp[1] = readl(host->base + REG_RESPONSE2);
|
||||
cmd->resp[0] = readl(host->base + REG_RESPONSE3);
|
||||
} else {
|
||||
cmd->resp[0] = readl(host->base + REG_RESPONSE0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void moxart_dma_complete(void *param)
|
||||
{
|
||||
struct moxart_host *host = param;
|
||||
|
||||
complete(&host->dma_complete);
|
||||
}
|
||||
|
||||
static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
|
||||
{
|
||||
u32 len, dir_data, dir_slave;
|
||||
unsigned long dma_time;
|
||||
struct dma_async_tx_descriptor *desc = NULL;
|
||||
struct dma_chan *dma_chan;
|
||||
|
||||
if (host->data_len == data->bytes_xfered)
|
||||
return;
|
||||
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
dma_chan = host->dma_chan_tx;
|
||||
dir_data = DMA_TO_DEVICE;
|
||||
dir_slave = DMA_MEM_TO_DEV;
|
||||
} else {
|
||||
dma_chan = host->dma_chan_rx;
|
||||
dir_data = DMA_FROM_DEVICE;
|
||||
dir_slave = DMA_DEV_TO_MEM;
|
||||
}
|
||||
|
||||
len = dma_map_sg(dma_chan->device->dev, data->sg,
|
||||
data->sg_len, dir_data);
|
||||
|
||||
if (len > 0) {
|
||||
desc = dmaengine_prep_slave_sg(dma_chan, data->sg,
|
||||
len, dir_slave,
|
||||
DMA_PREP_INTERRUPT |
|
||||
DMA_CTRL_ACK);
|
||||
} else {
|
||||
dev_err(mmc_dev(host->mmc), "dma_map_sg returned zero length\n");
|
||||
}
|
||||
|
||||
if (desc) {
|
||||
host->tx_desc = desc;
|
||||
desc->callback = moxart_dma_complete;
|
||||
desc->callback_param = host;
|
||||
dmaengine_submit(desc);
|
||||
dma_async_issue_pending(dma_chan);
|
||||
}
|
||||
|
||||
data->bytes_xfered += host->data_remain;
|
||||
|
||||
dma_time = wait_for_completion_interruptible_timeout(
|
||||
&host->dma_complete, host->timeout);
|
||||
|
||||
dma_unmap_sg(dma_chan->device->dev,
|
||||
data->sg, data->sg_len,
|
||||
dir_data);
|
||||
}
|
||||
|
||||
|
||||
static void moxart_transfer_pio(struct moxart_host *host)
|
||||
{
|
||||
struct mmc_data *data = host->mrq->cmd->data;
|
||||
u32 *sgp, len = 0, remain, status;
|
||||
|
||||
if (host->data_len == data->bytes_xfered)
|
||||
return;
|
||||
|
||||
sgp = sg_virt(host->cur_sg);
|
||||
remain = host->data_remain;
|
||||
|
||||
if (data->flags & MMC_DATA_WRITE) {
|
||||
while (remain > 0) {
|
||||
if (moxart_wait_for_status(host, FIFO_URUN, &status)
|
||||
== -ETIMEDOUT) {
|
||||
data->error = -ETIMEDOUT;
|
||||
complete(&host->pio_complete);
|
||||
return;
|
||||
}
|
||||
for (len = 0; len < remain && len < host->fifo_width;) {
|
||||
iowrite32(*sgp, host->base + REG_DATA_WINDOW);
|
||||
sgp++;
|
||||
len += 4;
|
||||
}
|
||||
remain -= len;
|
||||
}
|
||||
|
||||
} else {
|
||||
while (remain > 0) {
|
||||
if (moxart_wait_for_status(host, FIFO_ORUN, &status)
|
||||
== -ETIMEDOUT) {
|
||||
data->error = -ETIMEDOUT;
|
||||
complete(&host->pio_complete);
|
||||
return;
|
||||
}
|
||||
for (len = 0; len < remain && len < host->fifo_width;) {
|
||||
/* SCR data must be read in big endian. */
|
||||
if (data->mrq->cmd->opcode == SD_APP_SEND_SCR)
|
||||
*sgp = ioread32be(host->base +
|
||||
REG_DATA_WINDOW);
|
||||
else
|
||||
*sgp = ioread32(host->base +
|
||||
REG_DATA_WINDOW);
|
||||
sgp++;
|
||||
len += 4;
|
||||
}
|
||||
remain -= len;
|
||||
}
|
||||
}
|
||||
|
||||
data->bytes_xfered += host->data_remain - remain;
|
||||
host->data_remain = remain;
|
||||
|
||||
if (host->data_len != data->bytes_xfered)
|
||||
moxart_next_sg(host);
|
||||
else
|
||||
complete(&host->pio_complete);
|
||||
}
|
||||
|
||||
static void moxart_prepare_data(struct moxart_host *host)
|
||||
{
|
||||
struct mmc_data *data = host->mrq->cmd->data;
|
||||
u32 datactrl;
|
||||
int blksz_bits;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
host->data_len = data->blocks * data->blksz;
|
||||
blksz_bits = ffs(data->blksz) - 1;
|
||||
BUG_ON(1 << blksz_bits != data->blksz);
|
||||
|
||||
moxart_init_sg(host, data);
|
||||
|
||||
datactrl = DCR_DATA_EN | (blksz_bits & DCR_BLK_SIZE);
|
||||
|
||||
if (data->flags & MMC_DATA_WRITE)
|
||||
datactrl |= DCR_DATA_WRITE;
|
||||
|
||||
if ((host->data_len > host->fifo_width) && host->have_dma)
|
||||
datactrl |= DCR_DMA_EN;
|
||||
|
||||
writel(DCR_DATA_FIFO_RESET, host->base + REG_DATA_CONTROL);
|
||||
writel(MASK_DATA | FIFO_URUN | FIFO_ORUN, host->base + REG_CLEAR);
|
||||
writel(host->rate, host->base + REG_DATA_TIMER);
|
||||
writel(host->data_len, host->base + REG_DATA_LENGTH);
|
||||
writel(datactrl, host->base + REG_DATA_CONTROL);
|
||||
}
|
||||
|
||||
static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct moxart_host *host = mmc_priv(mmc);
|
||||
unsigned long pio_time, flags;
|
||||
u32 status;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
init_completion(&host->dma_complete);
|
||||
init_completion(&host->pio_complete);
|
||||
|
||||
host->mrq = mrq;
|
||||
|
||||
if (readl(host->base + REG_STATUS) & CARD_DETECT) {
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
goto request_done;
|
||||
}
|
||||
|
||||
moxart_prepare_data(host);
|
||||
moxart_send_command(host, host->mrq->cmd);
|
||||
|
||||
if (mrq->cmd->data) {
|
||||
if ((host->data_len > host->fifo_width) && host->have_dma) {
|
||||
|
||||
writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
moxart_transfer_dma(mrq->cmd->data, host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
} else {
|
||||
|
||||
writel(MASK_INTR_PIO, host->base + REG_INTERRUPT_MASK);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
/* PIO transfers start from interrupt. */
|
||||
pio_time = wait_for_completion_interruptible_timeout(
|
||||
&host->pio_complete, host->timeout);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
}
|
||||
|
||||
if (host->is_removed) {
|
||||
dev_err(mmc_dev(host->mmc), "card removed\n");
|
||||
mrq->cmd->error = -ETIMEDOUT;
|
||||
goto request_done;
|
||||
}
|
||||
|
||||
if (moxart_wait_for_status(host, MASK_DATA, &status)
|
||||
== -ETIMEDOUT) {
|
||||
mrq->cmd->data->error = -ETIMEDOUT;
|
||||
goto request_done;
|
||||
}
|
||||
|
||||
if (status & DATA_CRC_FAIL)
|
||||
mrq->cmd->data->error = -ETIMEDOUT;
|
||||
|
||||
if (mrq->cmd->data->stop)
|
||||
moxart_send_command(host, mrq->cmd->data->stop);
|
||||
}
|
||||
|
||||
request_done:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static irqreturn_t moxart_irq(int irq, void *devid)
|
||||
{
|
||||
struct moxart_host *host = (struct moxart_host *)devid;
|
||||
u32 status;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
status = readl(host->base + REG_STATUS);
|
||||
if (status & CARD_CHANGE) {
|
||||
host->is_removed = status & CARD_DETECT;
|
||||
if (host->is_removed && host->have_dma) {
|
||||
dmaengine_terminate_all(host->dma_chan_tx);
|
||||
dmaengine_terminate_all(host->dma_chan_rx);
|
||||
}
|
||||
host->mrq = NULL;
|
||||
writel(MASK_INTR_PIO, host->base + REG_CLEAR);
|
||||
writel(CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
|
||||
mmc_detect_change(host->mmc, 0);
|
||||
}
|
||||
if (status & (FIFO_ORUN | FIFO_URUN) && host->mrq)
|
||||
moxart_transfer_pio(host);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct moxart_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
u8 power, div;
|
||||
u32 ctrl;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (ios->clock) {
|
||||
for (div = 0; div < CLK_DIV_MASK; ++div) {
|
||||
if (ios->clock >= host->sysclk / (2 * (div + 1)))
|
||||
break;
|
||||
}
|
||||
ctrl = CLK_SD | div;
|
||||
host->rate = host->sysclk / (2 * (div + 1));
|
||||
if (host->rate > host->sysclk)
|
||||
ctrl |= CLK_HISPD;
|
||||
writel(ctrl, host->base + REG_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
if (ios->power_mode == MMC_POWER_OFF) {
|
||||
writel(readl(host->base + REG_POWER_CONTROL) & ~SD_POWER_ON,
|
||||
host->base + REG_POWER_CONTROL);
|
||||
} else {
|
||||
if (ios->vdd < MIN_POWER)
|
||||
power = 0;
|
||||
else
|
||||
power = ios->vdd - MIN_POWER;
|
||||
|
||||
writel(SD_POWER_ON | (u32) power,
|
||||
host->base + REG_POWER_CONTROL);
|
||||
}
|
||||
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_4:
|
||||
writel(BUS_WIDTH_4, host->base + REG_BUS_WIDTH);
|
||||
break;
|
||||
case MMC_BUS_WIDTH_8:
|
||||
writel(BUS_WIDTH_8, host->base + REG_BUS_WIDTH);
|
||||
break;
|
||||
default:
|
||||
writel(BUS_WIDTH_1, host->base + REG_BUS_WIDTH);
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
static int moxart_get_ro(struct mmc_host *mmc)
|
||||
{
|
||||
struct moxart_host *host = mmc_priv(mmc);
|
||||
|
||||
return !!(readl(host->base + REG_STATUS) & WRITE_PROT);
|
||||
}
|
||||
|
||||
static struct mmc_host_ops moxart_ops = {
|
||||
.request = moxart_request,
|
||||
.set_ios = moxart_set_ios,
|
||||
.get_ro = moxart_get_ro,
|
||||
};
|
||||
|
||||
static int moxart_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct resource res_mmc;
|
||||
struct mmc_host *mmc;
|
||||
struct moxart_host *host = NULL;
|
||||
struct dma_slave_config cfg;
|
||||
struct clk *clk;
|
||||
void __iomem *reg_mmc;
|
||||
dma_cap_mask_t mask;
|
||||
int irq, ret;
|
||||
u32 i;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
|
||||
if (!mmc) {
|
||||
dev_err(dev, "mmc_alloc_host failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = of_address_to_resource(node, 0, &res_mmc);
|
||||
if (ret) {
|
||||
dev_err(dev, "of_address_to_resource failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(dev, "irq_of_parse_and_map failed\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
clk = of_clk_get(node, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "of_clk_get failed\n");
|
||||
ret = PTR_ERR(clk);
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg_mmc = devm_ioremap_resource(dev, &res_mmc);
|
||||
if (IS_ERR(reg_mmc)) {
|
||||
ret = PTR_ERR(reg_mmc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
mmc_of_parse(mmc);
|
||||
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->base = reg_mmc;
|
||||
host->reg_phys = res_mmc.start;
|
||||
host->timeout = msecs_to_jiffies(1000);
|
||||
host->sysclk = clk_get_rate(clk);
|
||||
host->fifo_width = readl(host->base + REG_FEATURE) << 2;
|
||||
host->dma_chan_tx = of_dma_request_slave_channel(node, "tx");
|
||||
host->dma_chan_rx = of_dma_request_slave_channel(node, "rx");
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
mmc->ops = &moxart_ops;
|
||||
mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
|
||||
mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
|
||||
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
|
||||
|
||||
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
|
||||
dev_dbg(dev, "PIO mode transfer enabled\n");
|
||||
host->have_dma = false;
|
||||
} else {
|
||||
dev_dbg(dev, "DMA channels found (%p,%p)\n",
|
||||
host->dma_chan_tx, host->dma_chan_rx);
|
||||
host->have_dma = true;
|
||||
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.src_addr = 0;
|
||||
cfg.dst_addr = host->reg_phys + REG_DATA_WINDOW;
|
||||
dmaengine_slave_config(host->dma_chan_tx, &cfg);
|
||||
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
|
||||
cfg.dst_addr = 0;
|
||||
dmaengine_slave_config(host->dma_chan_rx, &cfg);
|
||||
}
|
||||
|
||||
switch ((readl(host->base + REG_BUS_WIDTH) >> 3) & 3) {
|
||||
case 1:
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
break;
|
||||
case 2:
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writel(0, host->base + REG_INTERRUPT_MASK);
|
||||
|
||||
writel(CMD_SDC_RESET, host->base + REG_COMMAND);
|
||||
for (i = 0; i < MAX_RETRIES; i++) {
|
||||
if (!(readl(host->base + REG_COMMAND) & CMD_SDC_RESET))
|
||||
break;
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
dev_set_drvdata(dev, mmc);
|
||||
mmc_add_host(mmc);
|
||||
|
||||
dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (mmc)
|
||||
mmc_free_host(mmc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int moxart_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
|
||||
struct moxart_host *host = mmc_priv(mmc);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
if (mmc) {
|
||||
if (!IS_ERR(host->dma_chan_tx))
|
||||
dma_release_channel(host->dma_chan_tx);
|
||||
if (!IS_ERR(host->dma_chan_rx))
|
||||
dma_release_channel(host->dma_chan_rx);
|
||||
mmc_remove_host(mmc);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
writel(0, host->base + REG_INTERRUPT_MASK);
|
||||
writel(0, host->base + REG_POWER_CONTROL);
|
||||
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
||||
host->base + REG_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
kfree(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id moxart_mmc_match[] = {
|
||||
{ .compatible = "moxa,moxart-mmc" },
|
||||
{ .compatible = "faraday,ftsdc010" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver moxart_mmc_driver = {
|
||||
.probe = moxart_probe,
|
||||
.remove = moxart_remove,
|
||||
.driver = {
|
||||
.name = "mmc-moxart",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = moxart_mmc_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(moxart_mmc_driver);
|
||||
|
||||
MODULE_ALIAS("platform:mmc-moxart");
|
||||
MODULE_DESCRIPTION("MOXA ART MMC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
|
|
@ -354,6 +354,20 @@ static irqreturn_t mvsd_irq(int irq, void *dev)
|
|||
intr_status, mvsd_read(MVSD_NOR_INTR_EN),
|
||||
mvsd_read(MVSD_HW_STATE));
|
||||
|
||||
/*
|
||||
* It looks like, SDIO IP can issue one late, spurious irq
|
||||
* although all irqs should be disabled. To work around this,
|
||||
* bail out early, if we didn't expect any irqs to occur.
|
||||
*/
|
||||
if (!mvsd_read(MVSD_NOR_INTR_EN) && !mvsd_read(MVSD_ERR_INTR_EN)) {
|
||||
dev_dbg(host->dev, "spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x\n",
|
||||
mvsd_read(MVSD_NOR_INTR_STATUS),
|
||||
mvsd_read(MVSD_NOR_INTR_EN),
|
||||
mvsd_read(MVSD_ERR_INTR_STATUS),
|
||||
mvsd_read(MVSD_ERR_INTR_EN));
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_lock(&host->lock);
|
||||
|
||||
/* PIO handling, if needed. Messy business... */
|
||||
|
@ -801,10 +815,10 @@ static int mvsd_probe(struct platform_device *pdev)
|
|||
goto out;
|
||||
|
||||
if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
|
||||
dev_notice(&pdev->dev, "using GPIO for card detection\n");
|
||||
dev_dbg(&pdev->dev, "using GPIO for card detection\n");
|
||||
else
|
||||
dev_notice(&pdev->dev,
|
||||
"lacking card detect (fall back to polling)\n");
|
||||
dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n");
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
|
|
@ -124,9 +124,8 @@ enum mxcmci_type {
|
|||
|
||||
struct mxcmci_host {
|
||||
struct mmc_host *mmc;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
dma_addr_t phys_base;
|
||||
int detect_irq;
|
||||
struct dma_chan *dma;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
|
@ -154,8 +153,6 @@ struct mxcmci_host {
|
|||
struct work_struct datawork;
|
||||
spinlock_t lock;
|
||||
|
||||
struct regulator *vcc;
|
||||
|
||||
int burstlen;
|
||||
int dmareq;
|
||||
struct dma_slave_config dma_slave_config;
|
||||
|
@ -241,37 +238,15 @@ static inline void mxcmci_writew(struct mxcmci_host *host, u16 val, int reg)
|
|||
|
||||
static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios);
|
||||
|
||||
static inline void mxcmci_init_ocr(struct mxcmci_host *host)
|
||||
static void mxcmci_set_power(struct mxcmci_host *host, unsigned int vdd)
|
||||
{
|
||||
host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
|
||||
|
||||
if (IS_ERR(host->vcc)) {
|
||||
host->vcc = NULL;
|
||||
} else {
|
||||
host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
|
||||
if (host->pdata && host->pdata->ocr_avail)
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"pdata->ocr_avail will not be used\n");
|
||||
}
|
||||
|
||||
if (host->vcc == NULL) {
|
||||
/* fall-back to platform data */
|
||||
if (host->pdata && host->pdata->ocr_avail)
|
||||
host->mmc->ocr_avail = host->pdata->ocr_avail;
|
||||
else
|
||||
host->mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mxcmci_set_power(struct mxcmci_host *host,
|
||||
unsigned char power_mode,
|
||||
unsigned int vdd)
|
||||
{
|
||||
if (host->vcc) {
|
||||
if (power_mode == MMC_POWER_UP)
|
||||
mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
else if (power_mode == MMC_POWER_OFF)
|
||||
mmc_regulator_set_ocr(host->mmc, host->vcc, 0);
|
||||
if (!IS_ERR(host->mmc->supply.vmmc)) {
|
||||
if (host->power_mode == MMC_POWER_UP)
|
||||
mmc_regulator_set_ocr(host->mmc,
|
||||
host->mmc->supply.vmmc, vdd);
|
||||
else if (host->power_mode == MMC_POWER_OFF)
|
||||
mmc_regulator_set_ocr(host->mmc,
|
||||
host->mmc->supply.vmmc, 0);
|
||||
}
|
||||
|
||||
if (host->pdata && host->pdata->setpower)
|
||||
|
@ -299,7 +274,6 @@ static void mxcmci_softreset(struct mxcmci_host *host)
|
|||
|
||||
mxcmci_writew(host, 0xff, MMC_REG_RES_TO);
|
||||
}
|
||||
static int mxcmci_setup_dma(struct mmc_host *mmc);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PPC_MPC512x)
|
||||
static inline void buffer_swap32(u32 *buf, int len)
|
||||
|
@ -868,8 +842,8 @@ static int mxcmci_setup_dma(struct mmc_host *mmc)
|
|||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
struct dma_slave_config *config = &host->dma_slave_config;
|
||||
|
||||
config->dst_addr = host->res->start + MMC_REG_BUFFER_ACCESS;
|
||||
config->src_addr = host->res->start + MMC_REG_BUFFER_ACCESS;
|
||||
config->dst_addr = host->phys_base + MMC_REG_BUFFER_ACCESS;
|
||||
config->src_addr = host->phys_base + MMC_REG_BUFFER_ACCESS;
|
||||
config->dst_addr_width = 4;
|
||||
config->src_addr_width = 4;
|
||||
config->dst_maxburst = host->burstlen;
|
||||
|
@ -911,8 +885,8 @@ static void mxcmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
host->cmdat &= ~CMD_DAT_CONT_BUS_WIDTH_4;
|
||||
|
||||
if (host->power_mode != ios->power_mode) {
|
||||
mxcmci_set_power(host, ios->power_mode, ios->vdd);
|
||||
host->power_mode = ios->power_mode;
|
||||
mxcmci_set_power(host, ios->vdd);
|
||||
|
||||
if (ios->power_mode == MMC_POWER_ON)
|
||||
host->cmdat |= CMD_DAT_CONT_INIT;
|
||||
|
@ -1040,8 +1014,8 @@ static const struct mmc_host_ops mxcmci_ops = {
|
|||
static int mxcmci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mmc_host *mmc;
|
||||
struct mxcmci_host *host = NULL;
|
||||
struct resource *iores, *r;
|
||||
struct mxcmci_host *host;
|
||||
struct resource *res;
|
||||
int ret = 0, irq;
|
||||
bool dat3_card_detect = false;
|
||||
dma_cap_mask_t mask;
|
||||
|
@ -1052,21 +1026,25 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
|
||||
of_id = of_match_device(mxcmci_of_match, &pdev->dev);
|
||||
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!iores || irq < 0)
|
||||
if (irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = request_mem_region(iores->start, resource_size(iores), pdev->name);
|
||||
if (!r)
|
||||
return -EBUSY;
|
||||
mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
|
||||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev);
|
||||
if (!mmc) {
|
||||
ret = -ENOMEM;
|
||||
goto out_release_mem;
|
||||
host = mmc_priv(mmc);
|
||||
|
||||
host->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
host->phys_base = res->start;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
@ -1084,13 +1062,6 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->base = ioremap(r->start, resource_size(r));
|
||||
if (!host->base) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (of_id) {
|
||||
const struct platform_device_id *id_entry = of_id->data;
|
||||
host->devtype = id_entry->driver_data;
|
||||
|
@ -1112,7 +1083,14 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
&& !of_property_read_bool(pdev->dev.of_node, "cd-gpios"))
|
||||
dat3_card_detect = true;
|
||||
|
||||
mxcmci_init_ocr(host);
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
if (ret) {
|
||||
if (pdata && ret != -EPROBE_DEFER)
|
||||
mmc->ocr_avail = pdata->ocr_avail ? :
|
||||
MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
else
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (dat3_card_detect)
|
||||
host->default_irq_mask =
|
||||
|
@ -1120,19 +1098,16 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
else
|
||||
host->default_irq_mask = 0;
|
||||
|
||||
host->res = r;
|
||||
host->irq = irq;
|
||||
|
||||
host->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(host->clk_ipg)) {
|
||||
ret = PTR_ERR(host->clk_ipg);
|
||||
goto out_iounmap;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
host->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||
if (IS_ERR(host->clk_per)) {
|
||||
ret = PTR_ERR(host->clk_per);
|
||||
goto out_iounmap;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
clk_prepare_enable(host->clk_per);
|
||||
|
@ -1159,9 +1134,9 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
if (!host->pdata) {
|
||||
host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx");
|
||||
} else {
|
||||
r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (r) {
|
||||
host->dmareq = r->start;
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (res) {
|
||||
host->dmareq = res->start;
|
||||
host->dma_data.peripheral_type = IMX_DMATYPE_SDHC;
|
||||
host->dma_data.priority = DMA_PRIO_LOW;
|
||||
host->dma_data.dma_request = host->dmareq;
|
||||
|
@ -1178,7 +1153,8 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
|
||||
INIT_WORK(&host->datawork, mxcmci_datawork);
|
||||
|
||||
ret = request_irq(host->irq, mxcmci_irq, 0, DRIVER_NAME, host);
|
||||
ret = devm_request_irq(&pdev->dev, irq, mxcmci_irq, 0,
|
||||
dev_name(&pdev->dev), host);
|
||||
if (ret)
|
||||
goto out_free_dma;
|
||||
|
||||
|
@ -1188,7 +1164,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
ret = host->pdata->init(&pdev->dev, mxcmci_detect_irq,
|
||||
host->mmc);
|
||||
if (ret)
|
||||
goto out_free_irq;
|
||||
goto out_free_dma;
|
||||
}
|
||||
|
||||
init_timer(&host->watchdog);
|
||||
|
@ -1199,20 +1175,17 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
out_free_irq:
|
||||
free_irq(host->irq, host);
|
||||
out_free_dma:
|
||||
if (host->dma)
|
||||
dma_release_channel(host->dma);
|
||||
|
||||
out_clk_put:
|
||||
clk_disable_unprepare(host->clk_per);
|
||||
clk_disable_unprepare(host->clk_ipg);
|
||||
out_iounmap:
|
||||
iounmap(host->base);
|
||||
|
||||
out_free:
|
||||
mmc_free_host(mmc);
|
||||
out_release_mem:
|
||||
release_mem_region(iores->start, resource_size(iores));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1223,30 +1196,21 @@ static int mxcmci_remove(struct platform_device *pdev)
|
|||
|
||||
mmc_remove_host(mmc);
|
||||
|
||||
if (host->vcc)
|
||||
regulator_put(host->vcc);
|
||||
|
||||
if (host->pdata && host->pdata->exit)
|
||||
host->pdata->exit(&pdev->dev, mmc);
|
||||
|
||||
free_irq(host->irq, host);
|
||||
iounmap(host->base);
|
||||
|
||||
if (host->dma)
|
||||
dma_release_channel(host->dma);
|
||||
|
||||
clk_disable_unprepare(host->clk_per);
|
||||
clk_disable_unprepare(host->clk_ipg);
|
||||
|
||||
release_mem_region(host->res->start, resource_size(host->res));
|
||||
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mxcmci_suspend(struct device *dev)
|
||||
static int __maybe_unused mxcmci_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
|
@ -1256,7 +1220,7 @@ static int mxcmci_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mxcmci_resume(struct device *dev)
|
||||
static int __maybe_unused mxcmci_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct mxcmci_host *host = mmc_priv(mmc);
|
||||
|
@ -1266,11 +1230,7 @@ static int mxcmci_resume(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxcmci_pm_ops = {
|
||||
.suspend = mxcmci_suspend,
|
||||
.resume = mxcmci_resume,
|
||||
};
|
||||
#endif
|
||||
static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume);
|
||||
|
||||
static struct platform_driver mxcmci_driver = {
|
||||
.probe = mxcmci_probe,
|
||||
|
@ -1279,9 +1239,7 @@ static struct platform_driver mxcmci_driver = {
|
|||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mxcmci_pm_ops,
|
||||
#endif
|
||||
.of_match_table = mxcmci_of_match,
|
||||
}
|
||||
};
|
||||
|
|
|
@ -70,6 +70,7 @@ struct mxs_mmc_host {
|
|||
unsigned char bus_width;
|
||||
spinlock_t lock;
|
||||
int sdio_irq_en;
|
||||
bool broken_cd;
|
||||
};
|
||||
|
||||
static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
||||
|
@ -78,6 +79,9 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc)
|
|||
struct mxs_ssp *ssp = &host->ssp;
|
||||
int present, ret;
|
||||
|
||||
if (host->broken_cd)
|
||||
return -ENOSYS;
|
||||
|
||||
ret = mmc_gpio_get_cd(mmc);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
@ -568,6 +572,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
{
|
||||
const struct of_device_id *of_id =
|
||||
of_match_device(mxs_mmc_dt_ids, &pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct mxs_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *iores;
|
||||
|
@ -634,6 +639,8 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
|
||||
|
||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
||||
|
||||
mmc->f_min = 400000;
|
||||
mmc->f_max = 288000000;
|
||||
|
||||
|
|
|
@ -177,7 +177,7 @@ static void mmc_omap_fclk_offdelay(struct mmc_omap_slot *slot)
|
|||
unsigned long tick_ns;
|
||||
|
||||
if (slot != NULL && slot->host->fclk_enabled && slot->fclk_freq > 0) {
|
||||
tick_ns = (1000000000 + slot->fclk_freq - 1) / slot->fclk_freq;
|
||||
tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq);
|
||||
ndelay(8 * tick_ns);
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ static void mmc_omap_send_stop_work(struct work_struct *work)
|
|||
struct mmc_data *data = host->stop_data;
|
||||
unsigned long tick_ns;
|
||||
|
||||
tick_ns = (1000000000 + slot->fclk_freq - 1)/slot->fclk_freq;
|
||||
tick_ns = DIV_ROUND_UP(NSEC_PER_SEC, slot->fclk_freq);
|
||||
ndelay(8*tick_ns);
|
||||
|
||||
mmc_omap_start_command(host, data->stop);
|
||||
|
@ -477,7 +477,7 @@ mmc_omap_send_abort(struct mmc_omap_host *host, int maxloops)
|
|||
u16 stat = 0;
|
||||
|
||||
/* Sending abort takes 80 clocks. Have some extra and round up */
|
||||
timeout = (120*1000000 + slot->fclk_freq - 1)/slot->fclk_freq;
|
||||
timeout = DIV_ROUND_UP(120 * USEC_PER_SEC, slot->fclk_freq);
|
||||
restarts = 0;
|
||||
while (restarts < maxloops) {
|
||||
OMAP_MMC_WRITE(host, STAT, 0xFFFF);
|
||||
|
@ -677,8 +677,8 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
|
|||
if (n > host->buffer_bytes_left)
|
||||
n = host->buffer_bytes_left;
|
||||
|
||||
nwords = n / 2;
|
||||
nwords += n & 1; /* handle odd number of bytes to transfer */
|
||||
/* Round up to handle odd number of bytes to transfer */
|
||||
nwords = DIV_ROUND_UP(n, 2);
|
||||
|
||||
host->buffer_bytes_left -= n;
|
||||
host->total_bytes_left -= n;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/omap-dma.h>
|
||||
#include <linux/omap-dmaengine.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
|
@ -582,7 +582,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
|
|||
* - MMC/SD clock coming out of controller > 25MHz
|
||||
*/
|
||||
if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) &&
|
||||
(ios->timing != MMC_TIMING_UHS_DDR50) &&
|
||||
(ios->timing != MMC_TIMING_MMC_DDR52) &&
|
||||
((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) {
|
||||
regval = OMAP_HSMMC_READ(host->base, HCTL);
|
||||
if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000)
|
||||
|
@ -602,7 +602,7 @@ static void omap_hsmmc_set_bus_width(struct omap_hsmmc_host *host)
|
|||
u32 con;
|
||||
|
||||
con = OMAP_HSMMC_READ(host->base, CON);
|
||||
if (ios->timing == MMC_TIMING_UHS_DDR50)
|
||||
if (ios->timing == MMC_TIMING_MMC_DDR52)
|
||||
con |= DDR; /* configure in DDR mode */
|
||||
else
|
||||
con &= ~DDR;
|
||||
|
@ -920,16 +920,17 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
|
|||
static void
|
||||
omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
host->cmd = NULL;
|
||||
|
||||
if (host->mrq->sbc && (host->cmd == host->mrq->sbc) &&
|
||||
!host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) {
|
||||
host->cmd = NULL;
|
||||
omap_hsmmc_start_dma_transfer(host);
|
||||
omap_hsmmc_start_command(host, host->mrq->cmd,
|
||||
host->mrq->data);
|
||||
return;
|
||||
}
|
||||
|
||||
host->cmd = NULL;
|
||||
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
/* response type 2 */
|
||||
|
@ -1851,6 +1852,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
unsigned tx_req, rx_req;
|
||||
struct pinctrl *pinctrl;
|
||||
const struct omap_mmc_of_data *data;
|
||||
void __iomem *base;
|
||||
|
||||
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
|
||||
if (match) {
|
||||
|
@ -1881,9 +1883,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
if (res == NULL || irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (res == NULL)
|
||||
return -EBUSY;
|
||||
base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
ret = omap_hsmmc_gpio_init(pdata);
|
||||
if (ret)
|
||||
|
@ -1904,7 +1906,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
host->irq = irq;
|
||||
host->slot_id = 0;
|
||||
host->mapbase = res->start + pdata->reg_offset;
|
||||
host->base = ioremap(host->mapbase, SZ_4K);
|
||||
host->base = base + pdata->reg_offset;
|
||||
host->power_mode = MMC_POWER_OFF;
|
||||
host->next_data.cookie = 1;
|
||||
host->pbias_enabled = 0;
|
||||
|
@ -1922,7 +1924,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&host->irq_lock);
|
||||
|
||||
host->fclk = clk_get(&pdev->dev, "fck");
|
||||
host->fclk = devm_clk_get(&pdev->dev, "fck");
|
||||
if (IS_ERR(host->fclk)) {
|
||||
ret = PTR_ERR(host->fclk);
|
||||
host->fclk = NULL;
|
||||
|
@ -1941,7 +1943,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
omap_hsmmc_context_save(host);
|
||||
|
||||
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
|
||||
host->dbclk = devm_clk_get(&pdev->dev, "mmchsdb_fck");
|
||||
/*
|
||||
* MMC can still work without debounce clock.
|
||||
*/
|
||||
|
@ -1949,7 +1951,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
host->dbclk = NULL;
|
||||
} else if (clk_prepare_enable(host->dbclk) != 0) {
|
||||
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
|
||||
clk_put(host->dbclk);
|
||||
host->dbclk = NULL;
|
||||
}
|
||||
|
||||
|
@ -2018,7 +2019,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* Request IRQ for MMC operations */
|
||||
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
|
||||
ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n");
|
||||
|
@ -2029,7 +2030,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
if (pdata->init(&pdev->dev) != 0) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Unable to configure MMC IRQs\n");
|
||||
goto err_irq_cd_init;
|
||||
goto err_irq;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2044,9 +2045,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
/* Request IRQ for card detect */
|
||||
if ((mmc_slot(host).card_detect_irq)) {
|
||||
ret = request_threaded_irq(mmc_slot(host).card_detect_irq,
|
||||
NULL,
|
||||
omap_hsmmc_detect,
|
||||
ret = devm_request_threaded_irq(&pdev->dev,
|
||||
mmc_slot(host).card_detect_irq,
|
||||
NULL, omap_hsmmc_detect,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
mmc_hostname(mmc), host);
|
||||
if (ret) {
|
||||
|
@ -2089,15 +2090,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
err_slot_name:
|
||||
mmc_remove_host(mmc);
|
||||
free_irq(mmc_slot(host).card_detect_irq, host);
|
||||
err_irq_cd:
|
||||
if (host->use_reg)
|
||||
omap_hsmmc_reg_put(host);
|
||||
err_reg:
|
||||
if (host->pdata->cleanup)
|
||||
host->pdata->cleanup(&pdev->dev);
|
||||
err_irq_cd_init:
|
||||
free_irq(host->irq, host);
|
||||
err_irq:
|
||||
if (host->tx_chan)
|
||||
dma_release_channel(host->tx_chan);
|
||||
|
@ -2105,27 +2103,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
dma_release_channel(host->rx_chan);
|
||||
pm_runtime_put_sync(host->dev);
|
||||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->dbclk) {
|
||||
if (host->dbclk)
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
err1:
|
||||
iounmap(host->base);
|
||||
mmc_free_host(mmc);
|
||||
err_alloc:
|
||||
omap_hsmmc_gpio_free(pdata);
|
||||
err:
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_hsmmc_host *host = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
mmc_remove_host(host->mmc);
|
||||
|
@ -2133,9 +2123,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||
omap_hsmmc_reg_put(host);
|
||||
if (host->pdata->cleanup)
|
||||
host->pdata->cleanup(&pdev->dev);
|
||||
free_irq(host->irq, host);
|
||||
if (mmc_slot(host).card_detect_irq)
|
||||
free_irq(mmc_slot(host).card_detect_irq, host);
|
||||
|
||||
if (host->tx_chan)
|
||||
dma_release_channel(host->tx_chan);
|
||||
|
@ -2144,20 +2131,12 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_put_sync(host->dev);
|
||||
pm_runtime_disable(host->dev);
|
||||
clk_put(host->fclk);
|
||||
if (host->dbclk) {
|
||||
if (host->dbclk)
|
||||
clk_disable_unprepare(host->dbclk);
|
||||
clk_put(host->dbclk);
|
||||
}
|
||||
|
||||
omap_hsmmc_gpio_free(host->pdata);
|
||||
iounmap(host->base);
|
||||
mmc_free_host(host->mmc);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (res)
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -236,6 +236,9 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
|
|||
case MMC_RSP_R1:
|
||||
rsp_type = SD_RSP_TYPE_R1;
|
||||
break;
|
||||
case MMC_RSP_R1 & ~MMC_RSP_CRC:
|
||||
rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
|
||||
break;
|
||||
case MMC_RSP_R1B:
|
||||
rsp_type = SD_RSP_TYPE_R1b;
|
||||
break;
|
||||
|
@ -816,6 +819,7 @@ static int sd_set_timing(struct realtek_pci_sdmmc *host, unsigned char timing)
|
|||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0);
|
||||
break;
|
||||
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1,
|
||||
0x0C | SD_ASYNC_FIFO_NOT_RST,
|
||||
|
@ -896,6 +900,7 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
host->vpclk = true;
|
||||
host->double_clk = false;
|
||||
break;
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
host->ssc_depth = RTSX_SSC_DEPTH_1M;
|
||||
|
|
|
@ -34,7 +34,8 @@
|
|||
#include <linux/mfd/rtsx_usb.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
#if defined(CONFIG_LEDS_CLASS) || (defined(CONFIG_LEDS_CLASS_MODULE) && \
|
||||
defined(CONFIG_MMC_REALTEK_USB_MODULE))
|
||||
#include <linux/leds.h>
|
||||
#include <linux/workqueue.h>
|
||||
#define RTSX_USB_USE_LEDS_CLASS
|
||||
|
@ -59,7 +60,7 @@ struct rtsx_usb_sdmmc {
|
|||
|
||||
unsigned char power_mode;
|
||||
|
||||
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
|
||||
#ifdef RTSX_USB_USE_LEDS_CLASS
|
||||
struct led_classdev led;
|
||||
char led_name[32];
|
||||
struct work_struct led_work;
|
||||
|
|
|
@ -102,11 +102,19 @@ static void sdhci_acpi_int_hw_reset(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_dflt = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_acpi_ops_int = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_acpi_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.hw_reset = sdhci_acpi_int_hw_reset,
|
||||
};
|
||||
|
||||
|
|
|
@ -206,9 +206,13 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
|
|||
}
|
||||
|
||||
static struct sdhci_ops sdhci_bcm_kona_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_bcm_kona_get_max_clk,
|
||||
.get_timeout_clock = sdhci_bcm_kona_get_timeout_clock,
|
||||
.platform_send_init_74_clocks = sdhci_bcm_kona_init_74_clocks,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.card_event = sdhci_bcm_kona_card_event,
|
||||
};
|
||||
|
||||
|
|
|
@ -131,8 +131,12 @@ static const struct sdhci_ops bcm2835_sdhci_ops = {
|
|||
.read_l = bcm2835_sdhci_readl,
|
||||
.read_w = bcm2835_sdhci_readw,
|
||||
.read_b = bcm2835_sdhci_readb,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_min_clock = bcm2835_sdhci_get_min_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data bcm2835_sdhci_pdata = {
|
||||
|
|
|
@ -30,13 +30,12 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
u16 clk;
|
||||
unsigned long timeout;
|
||||
|
||||
if (clock == host->clock)
|
||||
return;
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (clock == 0)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
while (host->max_clk / div > clock) {
|
||||
/*
|
||||
|
@ -75,13 +74,14 @@ static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
|
||||
clk |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_cns3xxx_ops = {
|
||||
.get_max_clock = sdhci_cns3xxx_get_max_clk,
|
||||
.set_clock = sdhci_cns3xxx_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
|
||||
|
@ -90,8 +90,7 @@ static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
|
|||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_NONSTANDARD_CLOCK,
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
};
|
||||
|
||||
static int sdhci_cns3xxx_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -21,28 +21,17 @@
|
|||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
struct sdhci_dove_priv {
|
||||
struct clk *clk;
|
||||
int gpio_cd;
|
||||
};
|
||||
|
||||
static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data)
|
||||
{
|
||||
struct sdhci_host *host = data;
|
||||
|
||||
tasklet_schedule(&host->card_tasklet);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u16 ret;
|
||||
|
@ -60,8 +49,6 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)
|
|||
|
||||
static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_dove_priv *priv = pltfm_host->priv;
|
||||
u32 ret;
|
||||
|
||||
ret = readl(host->ioaddr + reg);
|
||||
|
@ -71,14 +58,6 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
|
|||
/* Mask the support for 3.0V */
|
||||
ret &= ~SDHCI_CAN_VDD_300;
|
||||
break;
|
||||
case SDHCI_PRESENT_STATE:
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
if (gpio_get_value(priv->gpio_cd) == 0)
|
||||
ret |= SDHCI_CARD_PRESENT;
|
||||
else
|
||||
ret &= ~SDHCI_CARD_PRESENT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -86,6 +65,10 @@ static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)
|
|||
static const struct sdhci_ops sdhci_dove_ops = {
|
||||
.read_w = sdhci_dove_readw,
|
||||
.read_l = sdhci_dove_readl,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_dove_pdata = {
|
||||
|
@ -113,28 +96,9 @@ static int sdhci_dove_probe(struct platform_device *pdev)
|
|||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
|
||||
if (pdev->dev.of_node) {
|
||||
priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node,
|
||||
"cd-gpios", 0);
|
||||
} else {
|
||||
priv->gpio_cd = -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
ret = gpio_request(priv->gpio_cd, "sdhci-cd");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "card detect gpio request failed: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
gpio_direction_input(priv->gpio_cd);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0);
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
goto err_sdhci_pltfm_init;
|
||||
}
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
@ -142,39 +106,20 @@ static int sdhci_dove_probe(struct platform_device *pdev)
|
|||
if (!IS_ERR(priv->clk))
|
||||
clk_prepare_enable(priv->clk);
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_sdhci_add;
|
||||
|
||||
/*
|
||||
* We must request the IRQ after sdhci_add_host(), as the tasklet only
|
||||
* gets setup in sdhci_add_host() and we oops.
|
||||
*/
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
ret = request_irq(gpio_to_irq(priv->gpio_cd),
|
||||
sdhci_dove_carddetect_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||
mmc_hostname(host->mmc), host);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "card detect irq request failed: %d\n",
|
||||
ret);
|
||||
goto err_request_irq;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_request_irq:
|
||||
sdhci_remove_host(host, 0);
|
||||
err_sdhci_add:
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_sdhci_pltfm_init:
|
||||
if (gpio_is_valid(priv->gpio_cd))
|
||||
gpio_free(priv->gpio_cd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -186,11 +131,6 @@ static int sdhci_dove_remove(struct platform_device *pdev)
|
|||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
if (gpio_is_valid(priv->gpio_cd)) {
|
||||
free_irq(gpio_to_irq(priv->gpio_cd), host);
|
||||
gpio_free(priv->gpio_cd);
|
||||
}
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable_unprepare(priv->clk);
|
||||
|
||||
|
|
|
@ -160,7 +160,6 @@ struct pltfm_imx_data {
|
|||
MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
|
||||
WAIT_FOR_INT, /* sent CMD12, waiting for response INT */
|
||||
} multiblock_status;
|
||||
u32 uhs_mode;
|
||||
u32 is_ddr;
|
||||
};
|
||||
|
||||
|
@ -382,7 +381,6 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
|
|||
if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
|
||||
ret |= SDHCI_CTRL_TUNED_CLK;
|
||||
|
||||
ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK);
|
||||
ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
|
||||
|
||||
return ret;
|
||||
|
@ -429,7 +427,6 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
else
|
||||
new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
|
||||
writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK;
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
if (val & SDHCI_CTRL_TUNED_CLK)
|
||||
|
@ -600,12 +597,14 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
|||
u32 temp, val;
|
||||
|
||||
if (clock == 0) {
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
val = readl(host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON,
|
||||
host->ioaddr + ESDHC_VENDOR_SPEC);
|
||||
}
|
||||
goto out;
|
||||
return;
|
||||
}
|
||||
|
||||
if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr)
|
||||
|
@ -645,8 +644,6 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
|
|||
}
|
||||
|
||||
mdelay(1);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
|
||||
|
@ -668,7 +665,7 @@ static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
||||
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
|
@ -686,8 +683,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
|||
|
||||
esdhc_clrset_le(host, ESDHC_CTRL_BUSWIDTH_MASK, ctrl,
|
||||
SDHCI_HOST_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
||||
|
@ -697,6 +692,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
|||
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
||||
mdelay(1);
|
||||
|
||||
/* This is balanced by the runtime put in sdhci_tasklet_finish */
|
||||
pm_runtime_get_sync(host->mmc->parent);
|
||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||
|
@ -713,13 +709,12 @@ static void esdhc_request_done(struct mmc_request *mrq)
|
|||
complete(&mrq->completion);
|
||||
}
|
||||
|
||||
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
|
||||
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode,
|
||||
struct scatterlist *sg)
|
||||
{
|
||||
struct mmc_command cmd = {0};
|
||||
struct mmc_request mrq = {NULL};
|
||||
struct mmc_data data = {0};
|
||||
struct scatterlist sg;
|
||||
char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
|
||||
|
||||
cmd.opcode = opcode;
|
||||
cmd.arg = 0;
|
||||
|
@ -728,11 +723,9 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
|
|||
data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN;
|
||||
data.blocks = 1;
|
||||
data.flags = MMC_DATA_READ;
|
||||
data.sg = &sg;
|
||||
data.sg = sg;
|
||||
data.sg_len = 1;
|
||||
|
||||
sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern));
|
||||
|
||||
mrq.cmd = &cmd;
|
||||
mrq.cmd->mrq = &mrq;
|
||||
mrq.data = &data;
|
||||
|
@ -742,14 +735,12 @@ static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
|
|||
mrq.done = esdhc_request_done;
|
||||
init_completion(&(mrq.completion));
|
||||
|
||||
disable_irq(host->irq);
|
||||
spin_lock(&host->lock);
|
||||
spin_lock_irq(&host->lock);
|
||||
host->mrq = &mrq;
|
||||
|
||||
sdhci_send_command(host, mrq.cmd);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
enable_irq(host->irq);
|
||||
spin_unlock_irq(&host->lock);
|
||||
|
||||
wait_for_completion(&mrq.completion);
|
||||
|
||||
|
@ -772,13 +763,21 @@ static void esdhc_post_tuning(struct sdhci_host *host)
|
|||
|
||||
static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
||||
{
|
||||
struct scatterlist sg;
|
||||
char *tuning_pattern;
|
||||
int min, max, avg, ret;
|
||||
|
||||
tuning_pattern = kmalloc(ESDHC_TUNING_BLOCK_PATTERN_LEN, GFP_KERNEL);
|
||||
if (!tuning_pattern)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_one(&sg, tuning_pattern, ESDHC_TUNING_BLOCK_PATTERN_LEN);
|
||||
|
||||
/* find the mininum delay first which can pass tuning */
|
||||
min = ESDHC_TUNE_CTRL_MIN;
|
||||
while (min < ESDHC_TUNE_CTRL_MAX) {
|
||||
esdhc_prepare_tuning(host, min);
|
||||
if (!esdhc_send_tuning_cmd(host, opcode))
|
||||
if (!esdhc_send_tuning_cmd(host, opcode, &sg))
|
||||
break;
|
||||
min += ESDHC_TUNE_CTRL_STEP;
|
||||
}
|
||||
|
@ -787,7 +786,7 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
|||
max = min + ESDHC_TUNE_CTRL_STEP;
|
||||
while (max < ESDHC_TUNE_CTRL_MAX) {
|
||||
esdhc_prepare_tuning(host, max);
|
||||
if (esdhc_send_tuning_cmd(host, opcode)) {
|
||||
if (esdhc_send_tuning_cmd(host, opcode, &sg)) {
|
||||
max -= ESDHC_TUNE_CTRL_STEP;
|
||||
break;
|
||||
}
|
||||
|
@ -797,9 +796,11 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode)
|
|||
/* use average delay to get the best timing */
|
||||
avg = (min + max) / 2;
|
||||
esdhc_prepare_tuning(host, avg);
|
||||
ret = esdhc_send_tuning_cmd(host, opcode);
|
||||
ret = esdhc_send_tuning_cmd(host, opcode, &sg);
|
||||
esdhc_post_tuning(host);
|
||||
|
||||
kfree(tuning_pattern);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n",
|
||||
ret ? "failed" : "passed", avg, ret);
|
||||
|
||||
|
@ -837,28 +838,21 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
|
||||
}
|
||||
|
||||
static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
|
||||
switch (uhs) {
|
||||
switch (timing) {
|
||||
case MMC_TIMING_UHS_SDR12:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50;
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
|
||||
ESDHC_MIX_CTRL_DDREN,
|
||||
host->ioaddr + ESDHC_MIX_CTRL);
|
||||
|
@ -875,7 +869,15 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
|||
break;
|
||||
}
|
||||
|
||||
return esdhc_change_pinstate(host, uhs);
|
||||
esdhc_change_pinstate(host, timing);
|
||||
}
|
||||
|
||||
static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
|
@ -888,8 +890,9 @@ static struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.get_max_clock = esdhc_pltfm_get_max_clock,
|
||||
.get_min_clock = esdhc_pltfm_get_min_clock,
|
||||
.get_ro = esdhc_pltfm_get_ro,
|
||||
.platform_bus_width = esdhc_pltfm_bus_width,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.set_uhs_signaling = esdhc_set_uhs_signaling,
|
||||
.reset = esdhc_reset,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
|
||||
|
@ -1170,8 +1173,10 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev)
|
|||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
if (!sdhci_sdio_irq_enabled(host)) {
|
||||
clk_disable_unprepare(imx_data->clk_per);
|
||||
clk_disable_unprepare(imx_data->clk_ipg);
|
||||
}
|
||||
clk_disable_unprepare(imx_data->clk_ahb);
|
||||
|
||||
return ret;
|
||||
|
@ -1183,8 +1188,10 @@ static int sdhci_esdhc_runtime_resume(struct device *dev)
|
|||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_imx_data *imx_data = pltfm_host->priv;
|
||||
|
||||
clk_prepare_enable(imx_data->clk_per);
|
||||
clk_prepare_enable(imx_data->clk_ipg);
|
||||
if (!sdhci_sdio_irq_enabled(host)) {
|
||||
clk_prepare_enable(imx_data->clk_per);
|
||||
clk_prepare_enable(imx_data->clk_ipg);
|
||||
}
|
||||
clk_prepare_enable(imx_data->clk_ahb);
|
||||
|
||||
return sdhci_runtime_resume_host(host);
|
||||
|
|
|
@ -20,10 +20,8 @@
|
|||
|
||||
#define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ | \
|
||||
SDHCI_QUIRK_NONSTANDARD_CLOCK | \
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
|
||||
SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY)
|
||||
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
|
|
|
@ -52,8 +52,12 @@ static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
static struct sdhci_ops sdhci_arasan_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_timeout_clock = sdhci_arasan_get_timeout_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
|
|
|
@ -199,13 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
|||
|
||||
static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
|
||||
int pre_div = 2;
|
||||
int div = 1;
|
||||
u32 temp;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
if (clock == 0)
|
||||
goto out;
|
||||
return;
|
||||
|
||||
/* Workaround to reduce the clock frequency for p1010 esdhc */
|
||||
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
|
||||
|
@ -238,24 +239,8 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
mdelay(1);
|
||||
out:
|
||||
host->clock = clock;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static u32 esdhc_proctl;
|
||||
static void esdhc_of_suspend(struct sdhci_host *host)
|
||||
{
|
||||
esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static void esdhc_of_resume(struct sdhci_host *host)
|
||||
{
|
||||
esdhc_of_enable_dma(host);
|
||||
sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void esdhc_of_platform_init(struct sdhci_host *host)
|
||||
{
|
||||
u32 vvn;
|
||||
|
@ -269,7 +254,7 @@ static void esdhc_of_platform_init(struct sdhci_host *host)
|
|||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
}
|
||||
|
||||
static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
||||
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
|
@ -289,8 +274,6 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width)
|
|||
|
||||
clrsetbits_be32(host->ioaddr + SDHCI_HOST_CONTROL,
|
||||
ESDHC_CTRL_BUSWIDTH_MASK, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_esdhc_ops = {
|
||||
|
@ -305,14 +288,47 @@ static const struct sdhci_ops sdhci_esdhc_ops = {
|
|||
.get_max_clock = esdhc_of_get_max_clock,
|
||||
.get_min_clock = esdhc_of_get_min_clock,
|
||||
.platform_init = esdhc_of_platform_init,
|
||||
#ifdef CONFIG_PM
|
||||
.platform_suspend = esdhc_of_suspend,
|
||||
.platform_resume = esdhc_of_resume,
|
||||
#endif
|
||||
.adma_workaround = esdhci_of_adma_workaround,
|
||||
.platform_bus_width = esdhc_pltfm_bus_width,
|
||||
.set_bus_width = esdhc_pltfm_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static u32 esdhc_proctl;
|
||||
static int esdhc_of_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
|
||||
esdhc_proctl = sdhci_be32bs_readl(host, SDHCI_HOST_CONTROL);
|
||||
|
||||
return sdhci_suspend_host(host);
|
||||
}
|
||||
|
||||
static int esdhc_of_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret = sdhci_resume_host(host);
|
||||
|
||||
if (ret == 0) {
|
||||
/* Isn't this already done by sdhci_resume_host() ? --rmk */
|
||||
esdhc_of_enable_dma(host);
|
||||
sdhci_be32bs_writel(host, esdhc_proctl, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops esdhc_pmops = {
|
||||
.suspend = esdhc_of_suspend,
|
||||
.resume = esdhc_of_resume,
|
||||
};
|
||||
#define ESDHC_PMOPS (&esdhc_pmops)
|
||||
#else
|
||||
#define ESDHC_PMOPS NULL
|
||||
#endif
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_pdata = {
|
||||
/*
|
||||
* card detection could be handled via GPIO
|
||||
|
@ -374,7 +390,7 @@ static struct platform_driver sdhci_esdhc_driver = {
|
|||
.name = "sdhci-esdhc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = sdhci_esdhc_of_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
.pm = ESDHC_PMOPS,
|
||||
},
|
||||
.probe = sdhci_esdhc_probe,
|
||||
.remove = sdhci_esdhc_remove,
|
||||
|
|
|
@ -58,6 +58,10 @@ static const struct sdhci_ops sdhci_hlwd_ops = {
|
|||
.write_l = sdhci_hlwd_writel,
|
||||
.write_w = sdhci_hlwd_writew,
|
||||
.write_b = sdhci_hlwd_writeb,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_hlwd_pdata = {
|
||||
|
|
|
@ -21,6 +21,45 @@
|
|||
#include "sdhci-pci.h"
|
||||
#include "sdhci-pci-o2micro.h"
|
||||
|
||||
static void o2_pci_set_baseclk(struct sdhci_pci_chip *chip, u32 value)
|
||||
{
|
||||
u32 scratch_32;
|
||||
pci_read_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, &scratch_32);
|
||||
|
||||
scratch_32 &= 0x0000FFFF;
|
||||
scratch_32 |= value;
|
||||
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_PLL_SETTING, scratch_32);
|
||||
}
|
||||
|
||||
static void o2_pci_led_enable(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u32 scratch_32;
|
||||
|
||||
/* Set led of SD host function enable */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG0, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
scratch_32 &= ~O2_SD_FREG0_LEDOFF;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG0, scratch_32);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_TEST_REG, &scratch_32);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
scratch_32 |= O2_SD_LED_ENABLE;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_TEST_REG, scratch_32);
|
||||
|
||||
}
|
||||
|
||||
void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
u32 scratch_32;
|
||||
|
@ -216,6 +255,40 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||
scratch &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
/* DevId=8520 subId= 0x11 or 0x12 Type Chip support */
|
||||
if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2) {
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG0,
|
||||
&scratch_32);
|
||||
scratch_32 = ((scratch_32 & 0xFF000000) >> 24);
|
||||
|
||||
/* Check Whether subId is 0x11 or 0x12 */
|
||||
if ((scratch_32 == 0x11) || (scratch_32 == 0x12)) {
|
||||
scratch_32 = 0x2c280000;
|
||||
|
||||
/* Set Base Clock to 208MZ */
|
||||
o2_pci_set_baseclk(chip, scratch_32);
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG4,
|
||||
&scratch_32);
|
||||
|
||||
/* Enable Base Clk setting change */
|
||||
scratch_32 |= O2_SD_FREG4_ENABLE_CLK_SET;
|
||||
pci_write_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG4,
|
||||
scratch_32);
|
||||
|
||||
/* Set Tuning Window to 4 */
|
||||
pci_write_config_byte(chip->pdev,
|
||||
O2_SD_TUNING_CTRL, 0x44);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable 8520 led function */
|
||||
o2_pci_led_enable(chip);
|
||||
|
||||
/* Set timeout CLK */
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_CLK_SETTING, &scratch_32);
|
||||
|
@ -276,7 +349,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
|
||||
|
||||
ret = pci_read_config_dword(chip->pdev,
|
||||
O2_SD_FUNC_REG0, &scratch_32);
|
||||
O2_SD_PLL_SETTING, &scratch_32);
|
||||
|
||||
if ((scratch_32 & 0xff000000) == 0x01000000) {
|
||||
scratch_32 &= 0x0000FFFF;
|
||||
|
@ -299,6 +372,9 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||
O2_SD_FUNC_REG4, scratch_32);
|
||||
}
|
||||
|
||||
/* Set Tuning Windows to 5 */
|
||||
pci_write_config_byte(chip->pdev,
|
||||
O2_SD_TUNING_CTRL, 0x55);
|
||||
/* Lock WP */
|
||||
ret = pci_read_config_byte(chip->pdev,
|
||||
O2_SD_LOCK_WP, &scratch);
|
||||
|
|
|
@ -57,6 +57,9 @@
|
|||
#define O2_SD_UHS2_L1_CTRL 0x35C
|
||||
#define O2_SD_FUNC_REG3 0x3E0
|
||||
#define O2_SD_FUNC_REG4 0x3E4
|
||||
#define O2_SD_LED_ENABLE BIT(6)
|
||||
#define O2_SD_FREG0_LEDOFF BIT(13)
|
||||
#define O2_SD_FREG4_ENABLE_CLK_SET BIT(22)
|
||||
|
||||
#define O2_SD_VENDOR_SETTING 0x110
|
||||
#define O2_SD_VENDOR_SETTING2 0x1C8
|
||||
|
|
|
@ -1031,7 +1031,7 @@ static int sdhci_pci_enable_dma(struct sdhci_host *host)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
|
||||
static void sdhci_pci_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
||||
|
@ -1052,8 +1052,6 @@ static int sdhci_pci_bus_width(struct sdhci_host *host, int width)
|
|||
}
|
||||
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sdhci_pci_gpio_hw_reset(struct sdhci_host *host)
|
||||
|
@ -1080,8 +1078,11 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_pci_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.platform_bus_width = sdhci_pci_bus_width,
|
||||
.set_bus_width = sdhci_pci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.hw_reset = sdhci_pci_hw_reset,
|
||||
};
|
||||
|
||||
|
|
|
@ -45,6 +45,10 @@ unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
|
|||
EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
|
||||
|
||||
static const struct sdhci_ops sdhci_pltfm_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -51,11 +51,13 @@
|
|||
#define MMC_CARD 0x1000
|
||||
#define MMC_WIDTH 0x0100
|
||||
|
||||
static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
|
||||
static void pxav2_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
||||
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
if (mask == SDHCI_RESET_ALL) {
|
||||
u16 tmp = 0;
|
||||
|
||||
|
@ -88,7 +90,7 @@ static void pxav2_set_private_registers(struct sdhci_host *host, u8 mask)
|
|||
}
|
||||
}
|
||||
|
||||
static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
|
||||
static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
u16 tmp;
|
||||
|
@ -107,14 +109,14 @@ static int pxav2_mmc_set_width(struct sdhci_host *host, int width)
|
|||
}
|
||||
writew(tmp, host->ioaddr + SD_CE_ATA_2);
|
||||
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops pxav2_sdhci_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.platform_reset_exit = pxav2_set_private_registers,
|
||||
.platform_bus_width = pxav2_mmc_set_width,
|
||||
.set_bus_width = pxav2_mmc_set_bus_width,
|
||||
.reset = pxav2_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -112,11 +112,13 @@ static int mv_conf_mbus_windows(struct platform_device *pdev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
|
||||
static void pxav3_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
|
||||
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
if (mask == SDHCI_RESET_ALL) {
|
||||
/*
|
||||
* tune timing of read data/command when crc error happen
|
||||
|
@ -184,7 +186,7 @@ static void pxav3_gen_init_74_clocks(struct sdhci_host *host, u8 power_mode)
|
|||
pxa->power_mode = power_mode;
|
||||
}
|
||||
|
||||
static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
|
||||
|
@ -218,15 +220,16 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
|
|||
dev_dbg(mmc_dev(host->mmc),
|
||||
"%s uhs = %d, ctrl_2 = %04X\n",
|
||||
__func__, uhs, ctrl_2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops pxav3_sdhci_ops = {
|
||||
.platform_reset_exit = pxav3_set_private_registers,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_uhs_signaling = pxav3_set_uhs_signaling,
|
||||
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = pxav3_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_pxav3_pdata = {
|
||||
|
|
|
@ -33,9 +33,6 @@
|
|||
|
||||
#define MAX_BUS_CLK (4)
|
||||
|
||||
/* Number of gpio's used is max data bus width + command and clock lines */
|
||||
#define NUM_GPIOS(x) (x + 2)
|
||||
|
||||
/**
|
||||
* struct sdhci_s3c - S3C SDHCI instance
|
||||
* @host: The SDHCI host created
|
||||
|
@ -58,6 +55,8 @@ struct sdhci_s3c {
|
|||
struct clk *clk_io;
|
||||
struct clk *clk_bus[MAX_BUS_CLK];
|
||||
unsigned long clk_rates[MAX_BUS_CLK];
|
||||
|
||||
bool no_divider;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -70,6 +69,7 @@ struct sdhci_s3c {
|
|||
*/
|
||||
struct sdhci_s3c_drv_data {
|
||||
unsigned int sdhci_quirks;
|
||||
bool no_divider;
|
||||
};
|
||||
|
||||
static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
|
||||
|
@ -119,7 +119,7 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
|
|||
* If controller uses a non-standard clock division, find the best clock
|
||||
* speed possible with selected clock source and skip the division.
|
||||
*/
|
||||
if (ourhost->host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
|
||||
if (ourhost->no_divider) {
|
||||
rate = clk_round_rate(clksrc, wanted);
|
||||
return wanted - rate;
|
||||
}
|
||||
|
@ -161,9 +161,13 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
int src;
|
||||
u32 ctrl;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
/* don't bother if the clock is going off. */
|
||||
if (clock == 0)
|
||||
if (clock == 0) {
|
||||
sdhci_set_clock(host, clock);
|
||||
return;
|
||||
}
|
||||
|
||||
for (src = 0; src < MAX_BUS_CLK; src++) {
|
||||
delta = sdhci_s3c_consider_clock(ourhost, src, clock);
|
||||
|
@ -215,6 +219,8 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
if (clock < 25 * 1000000)
|
||||
ctrl |= (S3C_SDHCI_CTRL3_FCSEL3 | S3C_SDHCI_CTRL3_FCSEL2);
|
||||
writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL3);
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -295,10 +301,11 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
unsigned long timeout;
|
||||
u16 clk = 0;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
/* If the clock is going off, set to 0 at clock control register */
|
||||
if (clock == 0) {
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
host->clock = clock;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -306,8 +313,6 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
|
||||
clk_set_rate(ourhost->clk_bus[ourhost->cur_clk], clock);
|
||||
|
||||
host->clock = clock;
|
||||
|
||||
clk = SDHCI_CLOCK_INT_EN;
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
|
@ -329,14 +334,14 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
|
||||
/**
|
||||
* sdhci_s3c_platform_bus_width - support 8bit buswidth
|
||||
* sdhci_s3c_set_bus_width - support 8bit buswidth
|
||||
* @host: The SDHCI host being queried
|
||||
* @width: MMC_BUS_WIDTH_ macro for the bus width being requested
|
||||
*
|
||||
* We have 8-bit width support but is not a v3 controller.
|
||||
* So we add platform_bus_width() and support 8bit width.
|
||||
*/
|
||||
static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)
|
||||
static void sdhci_s3c_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
||||
|
@ -358,93 +363,23 @@ static int sdhci_s3c_platform_bus_width(struct sdhci_host *host, int width)
|
|||
}
|
||||
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_s3c_ops = {
|
||||
.get_max_clock = sdhci_s3c_get_max_clk,
|
||||
.set_clock = sdhci_s3c_set_clock,
|
||||
.get_min_clock = sdhci_s3c_get_min_clock,
|
||||
.platform_bus_width = sdhci_s3c_platform_bus_width,
|
||||
.set_bus_width = sdhci_s3c_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static void sdhci_s3c_notify_change(struct platform_device *dev, int state)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(dev);
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
struct sdhci_s3c *sc = sdhci_priv(host);
|
||||
#endif
|
||||
unsigned long flags;
|
||||
|
||||
if (host) {
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
if (state) {
|
||||
dev_dbg(&dev->dev, "card inserted.\n");
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
clk_prepare_enable(sc->clk_io);
|
||||
#endif
|
||||
host->flags &= ~SDHCI_DEVICE_DEAD;
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
} else {
|
||||
dev_dbg(&dev->dev, "card removed.\n");
|
||||
host->flags |= SDHCI_DEVICE_DEAD;
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
#endif
|
||||
}
|
||||
tasklet_schedule(&host->card_tasklet);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t sdhci_s3c_gpio_card_detect_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct sdhci_s3c *sc = dev_id;
|
||||
int status = gpio_get_value(sc->ext_cd_gpio);
|
||||
if (sc->pdata->ext_cd_gpio_invert)
|
||||
status = !status;
|
||||
sdhci_s3c_notify_change(sc->pdev, status);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc)
|
||||
{
|
||||
struct s3c_sdhci_platdata *pdata = sc->pdata;
|
||||
struct device *dev = &sc->pdev->dev;
|
||||
|
||||
if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) {
|
||||
sc->ext_cd_gpio = pdata->ext_cd_gpio;
|
||||
sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio);
|
||||
if (sc->ext_cd_irq &&
|
||||
request_threaded_irq(sc->ext_cd_irq, NULL,
|
||||
sdhci_s3c_gpio_card_detect_thread,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
dev_name(dev), sc) == 0) {
|
||||
int status = gpio_get_value(sc->ext_cd_gpio);
|
||||
if (pdata->ext_cd_gpio_invert)
|
||||
status = !status;
|
||||
sdhci_s3c_notify_change(sc->pdev, status);
|
||||
} else {
|
||||
dev_warn(dev, "cannot request irq for card detect\n");
|
||||
sc->ext_cd_irq = 0;
|
||||
}
|
||||
} else {
|
||||
dev_err(dev, "cannot request gpio for card detect\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
struct sdhci_host *host, struct s3c_sdhci_platdata *pdata)
|
||||
{
|
||||
struct device_node *node = dev->of_node;
|
||||
struct sdhci_s3c *ourhost = to_s3c(host);
|
||||
u32 max_width;
|
||||
int gpio;
|
||||
|
||||
/* if the bus-width property is not specified, assume width as 1 */
|
||||
if (of_property_read_u32(node, "bus-width", &max_width))
|
||||
|
@ -462,18 +397,8 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
gpio = of_get_named_gpio(node, "cd-gpios", 0);
|
||||
if (gpio_is_valid(gpio)) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_GPIO;
|
||||
pdata->ext_cd_gpio = gpio;
|
||||
ourhost->ext_cd_gpio = -1;
|
||||
if (of_get_property(node, "cd-inverted", NULL))
|
||||
pdata->ext_cd_gpio_invert = 1;
|
||||
if (of_get_named_gpio(node, "cd-gpios", 0))
|
||||
return 0;
|
||||
} else if (gpio != -ENOENT) {
|
||||
dev_err(dev, "invalid card detect gpio specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* assuming internal card detect that will be configured by pinctrl */
|
||||
pdata->cd_type = S3C_SDHCI_CD_INTERNAL;
|
||||
|
@ -606,8 +531,10 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
/* Setup quirks for the controller */
|
||||
host->quirks |= SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC;
|
||||
host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT;
|
||||
if (drv_data)
|
||||
if (drv_data) {
|
||||
host->quirks |= drv_data->sdhci_quirks;
|
||||
sc->no_divider = drv_data->no_divider;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_MMC_SDHCI_S3C_DMA
|
||||
|
||||
|
@ -656,7 +583,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
* If controller does not have internal clock divider,
|
||||
* we can use overriding functions instead of default.
|
||||
*/
|
||||
if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) {
|
||||
if (sc->no_divider) {
|
||||
sdhci_s3c_ops.set_clock = sdhci_cmu_set_clock;
|
||||
sdhci_s3c_ops.get_min_clock = sdhci_cmu_get_min_clock;
|
||||
sdhci_s3c_ops.get_max_clock = sdhci_cmu_get_max_clock;
|
||||
|
@ -674,6 +601,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_suspend_ignore_children(&pdev->dev, 1);
|
||||
|
||||
mmc_of_parse(host->mmc);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret) {
|
||||
dev_err(dev, "sdhci_add_host() failed\n");
|
||||
|
@ -682,15 +611,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
goto err_req_regs;
|
||||
}
|
||||
|
||||
/* The following two methods of card detection might call
|
||||
sdhci_s3c_notify_change() immediately, so they can be called
|
||||
only after sdhci_add_host(). Setup errors are ignored. */
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_init)
|
||||
pdata->ext_cd_init(&sdhci_s3c_notify_change);
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_GPIO &&
|
||||
gpio_is_valid(pdata->ext_cd_gpio))
|
||||
sdhci_s3c_setup_card_detect_gpio(sc);
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
||||
clk_disable_unprepare(sc->clk_io);
|
||||
|
@ -711,16 +631,12 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_s3c *sc = sdhci_priv(host);
|
||||
struct s3c_sdhci_platdata *pdata = sc->pdata;
|
||||
|
||||
if (pdata->cd_type == S3C_SDHCI_CD_EXTERNAL && pdata->ext_cd_cleanup)
|
||||
pdata->ext_cd_cleanup(&sdhci_s3c_notify_change);
|
||||
|
||||
if (sc->ext_cd_irq)
|
||||
free_irq(sc->ext_cd_irq, sc);
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
||||
if (sc->pdata->cd_type != S3C_SDHCI_CD_INTERNAL)
|
||||
clk_prepare_enable(sc->clk_io);
|
||||
#endif
|
||||
sdhci_remove_host(host, 1);
|
||||
|
@ -797,7 +713,7 @@ static const struct dev_pm_ops sdhci_s3c_pmops = {
|
|||
|
||||
#if defined(CONFIG_CPU_EXYNOS4210) || defined(CONFIG_SOC_EXYNOS4212)
|
||||
static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = {
|
||||
.sdhci_quirks = SDHCI_QUIRK_NONSTANDARD_CLOCK,
|
||||
.no_divider = true,
|
||||
};
|
||||
#define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)&exynos4_sdhci_drv_data)
|
||||
#else
|
||||
|
|
|
@ -28,7 +28,11 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
static struct sdhci_ops sdhci_sirf_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_max_clock = sdhci_sirf_get_max_clk,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static struct sdhci_pltfm_data sdhci_sirf_pdata = {
|
||||
|
|
|
@ -38,7 +38,10 @@ struct spear_sdhci {
|
|||
|
||||
/* sdhci ops */
|
||||
static const struct sdhci_ops sdhci_pltfm_ops = {
|
||||
/* Nothing to do for now. */
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -32,11 +32,17 @@
|
|||
|
||||
/* Tegra SDHOST controller vendor register definitions */
|
||||
#define SDHCI_TEGRA_VENDOR_MISC_CTRL 0x120
|
||||
#define SDHCI_MISC_CTRL_ENABLE_SDR104 0x8
|
||||
#define SDHCI_MISC_CTRL_ENABLE_SDR50 0x10
|
||||
#define SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
|
||||
#define SDHCI_MISC_CTRL_ENABLE_DDR50 0x200
|
||||
|
||||
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
|
||||
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
|
||||
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
|
||||
#define NVQUIRK_DISABLE_SDR50 BIT(3)
|
||||
#define NVQUIRK_DISABLE_SDR104 BIT(4)
|
||||
#define NVQUIRK_DISABLE_DDR50 BIT(5)
|
||||
|
||||
struct sdhci_tegra_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
|
@ -48,19 +54,6 @@ struct sdhci_tegra {
|
|||
int power_gpio;
|
||||
};
|
||||
|
||||
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (unlikely(reg == SDHCI_PRESENT_STATE)) {
|
||||
/* Use wp_gpio here instead? */
|
||||
val = readl(host->ioaddr + reg);
|
||||
return val | SDHCI_WRITE_PROTECT;
|
||||
}
|
||||
|
||||
return readl(host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -108,26 +101,33 @@ static unsigned int tegra_sdhci_get_ro(struct sdhci_host *host)
|
|||
return mmc_gpio_get_ro(host->mmc);
|
||||
}
|
||||
|
||||
static void tegra_sdhci_reset_exit(struct sdhci_host *host, u8 mask)
|
||||
static void tegra_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = pltfm_host->priv;
|
||||
const struct sdhci_tegra_soc_data *soc_data = tegra_host->soc_data;
|
||||
u32 misc_ctrl;
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
if (!(mask & SDHCI_RESET_ALL))
|
||||
return;
|
||||
|
||||
misc_ctrl = sdhci_readw(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
/* Erratum: Enable SDHCI spec v3.00 support */
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300) {
|
||||
u32 misc_ctrl;
|
||||
|
||||
misc_ctrl = sdhci_readb(host, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
if (soc_data->nvquirks & NVQUIRK_ENABLE_SDHCI_SPEC_300)
|
||||
misc_ctrl |= SDHCI_MISC_CTRL_ENABLE_SDHCI_SPEC_300;
|
||||
sdhci_writeb(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
}
|
||||
/* Don't advertise UHS modes which aren't supported yet */
|
||||
if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR50)
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_DISABLE_DDR50)
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_DDR50;
|
||||
if (soc_data->nvquirks & NVQUIRK_DISABLE_SDR104)
|
||||
misc_ctrl &= ~SDHCI_MISC_CTRL_ENABLE_SDR104;
|
||||
sdhci_writew(host, misc_ctrl, SDHCI_TEGRA_VENDOR_MISC_CTRL);
|
||||
}
|
||||
|
||||
static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
|
||||
static void tegra_sdhci_set_bus_width(struct sdhci_host *host, int bus_width)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
|
@ -144,23 +144,25 @@ static int tegra_sdhci_buswidth(struct sdhci_host *host, int bus_width)
|
|||
ctrl &= ~SDHCI_CTRL_4BITBUS;
|
||||
}
|
||||
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops tegra_sdhci_ops = {
|
||||
.get_ro = tegra_sdhci_get_ro,
|
||||
.read_l = tegra_sdhci_readl,
|
||||
.read_w = tegra_sdhci_readw,
|
||||
.write_l = tegra_sdhci_writel,
|
||||
.platform_bus_width = tegra_sdhci_buswidth,
|
||||
.platform_reset_exit = tegra_sdhci_reset_exit,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = tegra_sdhci_set_bus_width,
|
||||
.reset = tegra_sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra20_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.ops = &tegra_sdhci_ops,
|
||||
};
|
||||
|
||||
|
@ -175,13 +177,16 @@ static const struct sdhci_pltfm_data sdhci_tegra30_pdata = {
|
|||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.ops = &tegra_sdhci_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_tegra_soc_data soc_data_tegra30 = {
|
||||
.pdata = &sdhci_tegra30_pdata,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300,
|
||||
.nvquirks = NVQUIRK_ENABLE_SDHCI_SPEC_300 |
|
||||
NVQUIRK_DISABLE_SDR50 |
|
||||
NVQUIRK_DISABLE_SDR104,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
||||
|
@ -189,12 +194,16 @@ static const struct sdhci_pltfm_data sdhci_tegra114_pdata = {
|
|||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_NO_HISPD_BIT |
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC,
|
||||
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.ops = &tegra_sdhci_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_tegra_soc_data soc_data_tegra114 = {
|
||||
.pdata = &sdhci_tegra114_pdata,
|
||||
.nvquirks = NVQUIRK_DISABLE_SDR50 |
|
||||
NVQUIRK_DISABLE_DDR50 |
|
||||
NVQUIRK_DISABLE_SDR104,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_tegra_dt_match[] = {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -281,18 +281,14 @@ struct sdhci_ops {
|
|||
unsigned int (*get_max_clock)(struct sdhci_host *host);
|
||||
unsigned int (*get_min_clock)(struct sdhci_host *host);
|
||||
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
|
||||
int (*platform_bus_width)(struct sdhci_host *host,
|
||||
int width);
|
||||
void (*set_bus_width)(struct sdhci_host *host, int width);
|
||||
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
|
||||
u8 power_mode);
|
||||
unsigned int (*get_ro)(struct sdhci_host *host);
|
||||
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
|
||||
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
|
||||
void (*reset)(struct sdhci_host *host, u8 mask);
|
||||
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
|
||||
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
|
||||
void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
|
||||
void (*hw_reset)(struct sdhci_host *host);
|
||||
void (*platform_suspend)(struct sdhci_host *host);
|
||||
void (*platform_resume)(struct sdhci_host *host);
|
||||
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
|
||||
void (*platform_init)(struct sdhci_host *host);
|
||||
void (*card_event)(struct sdhci_host *host);
|
||||
|
@ -397,6 +393,16 @@ extern void sdhci_remove_host(struct sdhci_host *host, int dead);
|
|||
extern void sdhci_send_command(struct sdhci_host *host,
|
||||
struct mmc_command *cmd);
|
||||
|
||||
static inline bool sdhci_sdio_irq_enabled(struct sdhci_host *host)
|
||||
{
|
||||
return !!(host->flags & SDHCI_SDIO_IRQ_ENABLED);
|
||||
}
|
||||
|
||||
void sdhci_set_clock(struct sdhci_host *host, unsigned int clock);
|
||||
void sdhci_set_bus_width(struct sdhci_host *host, int width);
|
||||
void sdhci_reset(struct sdhci_host *host, u8 mask);
|
||||
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int sdhci_suspend_host(struct sdhci_host *host);
|
||||
extern int sdhci_resume_host(struct sdhci_host *host);
|
||||
|
|
|
@ -803,12 +803,13 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host,
|
|||
break;
|
||||
}
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
/*
|
||||
* MMC core will only set this timing, if the host
|
||||
* advertises the MMC_CAP_UHS_DDR50 capability. MMCIF
|
||||
* implementations with this capability, e.g. sh73a0,
|
||||
* will have to set it in their platform data.
|
||||
* advertises the MMC_CAP_1_8V_DDR/MMC_CAP_1_2V_DDR
|
||||
* capability. MMCIF implementations with this
|
||||
* capability, e.g. sh73a0, will have to set it
|
||||
* in their platform data.
|
||||
*/
|
||||
tmp |= CMD_SET_DARS;
|
||||
break;
|
||||
|
|
1847
drivers/mmc/host/usdhi6rol0.c
Normal file
1847
drivers/mmc/host/usdhi6rol0.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -840,7 +840,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
|||
priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev,
|
||||
mmc->max_blk_count * 16,
|
||||
&priv->dma_desc_device_addr,
|
||||
208);
|
||||
GFP_KERNEL);
|
||||
if (!priv->dma_desc_buffer) {
|
||||
dev_err(&pdev->dev, "DMA alloc fail\n");
|
||||
ret = -EPERM;
|
||||
|
|
|
@ -285,7 +285,6 @@ static void rsi_reset_card(struct sdio_func *pfunction)
|
|||
if (err) {
|
||||
rsi_dbg(ERR_ZONE, "%s: CCCR speed reg read failed: %d\n",
|
||||
__func__, err);
|
||||
card->state &= ~MMC_STATE_HIGHSPEED;
|
||||
} else {
|
||||
err = rsi_cmd52writebyte(card,
|
||||
SDIO_CCCR_SPEED,
|
||||
|
@ -296,14 +295,13 @@ static void rsi_reset_card(struct sdio_func *pfunction)
|
|||
__func__, err);
|
||||
return;
|
||||
}
|
||||
mmc_card_set_highspeed(card);
|
||||
host->ios.timing = MMC_TIMING_SD_HS;
|
||||
host->ops->set_ios(host, &host->ios);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set clock */
|
||||
if (mmc_card_highspeed(card))
|
||||
if (mmc_card_hs(card))
|
||||
clock = 50000000;
|
||||
else
|
||||
clock = card->cis.max_dtr;
|
||||
|
|
|
@ -63,12 +63,12 @@ struct mmc_ext_csd {
|
|||
unsigned int power_off_longtime; /* Units: ms */
|
||||
u8 power_off_notification; /* state */
|
||||
unsigned int hs_max_dtr;
|
||||
unsigned int hs200_max_dtr;
|
||||
#define MMC_HIGH_26_MAX_DTR 26000000
|
||||
#define MMC_HIGH_52_MAX_DTR 52000000
|
||||
#define MMC_HIGH_DDR_MAX_DTR 52000000
|
||||
#define MMC_HS200_MAX_DTR 200000000
|
||||
unsigned int sectors;
|
||||
unsigned int card_type;
|
||||
unsigned int hc_erase_size; /* In sectors */
|
||||
unsigned int hc_erase_timeout; /* In milliseconds */
|
||||
unsigned int sec_trim_mult; /* Secure trim multiplier */
|
||||
|
@ -110,6 +110,7 @@ struct mmc_ext_csd {
|
|||
u8 raw_pwr_cl_200_360; /* 237 */
|
||||
u8 raw_pwr_cl_ddr_52_195; /* 238 */
|
||||
u8 raw_pwr_cl_ddr_52_360; /* 239 */
|
||||
u8 raw_pwr_cl_ddr_200_360; /* 253 */
|
||||
u8 raw_bkops_status; /* 246 */
|
||||
u8 raw_sectors[4]; /* 212 - 4 bytes */
|
||||
|
||||
|
@ -194,6 +195,7 @@ struct sdio_cis {
|
|||
};
|
||||
|
||||
struct mmc_host;
|
||||
struct mmc_ios;
|
||||
struct sdio_func;
|
||||
struct sdio_func_tuple;
|
||||
|
||||
|
@ -250,15 +252,11 @@ struct mmc_card {
|
|||
unsigned int state; /* (our) card state */
|
||||
#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */
|
||||
#define MMC_STATE_READONLY (1<<1) /* card is read-only */
|
||||
#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */
|
||||
#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */
|
||||
#define MMC_STATE_HIGHSPEED_DDR (1<<4) /* card is in high speed mode */
|
||||
#define MMC_STATE_ULTRAHIGHSPEED (1<<5) /* card is in ultra high speed mode */
|
||||
#define MMC_CARD_SDXC (1<<6) /* card is SDXC */
|
||||
#define MMC_CARD_REMOVED (1<<7) /* card has been removed */
|
||||
#define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */
|
||||
#define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */
|
||||
#define MMC_STATE_SUSPENDED (1<<11) /* card is suspended */
|
||||
#define MMC_STATE_BLOCKADDR (1<<2) /* card uses block-addressing */
|
||||
#define MMC_CARD_SDXC (1<<3) /* card is SDXC */
|
||||
#define MMC_CARD_REMOVED (1<<4) /* card has been removed */
|
||||
#define MMC_STATE_DOING_BKOPS (1<<5) /* card is doing BKOPS */
|
||||
#define MMC_STATE_SUSPENDED (1<<6) /* card is suspended */
|
||||
unsigned int quirks; /* card quirks */
|
||||
#define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */
|
||||
#define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */
|
||||
|
@ -301,6 +299,7 @@ struct mmc_card {
|
|||
struct sdio_func_tuple *tuples; /* unknown common tuples */
|
||||
|
||||
unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */
|
||||
unsigned int mmc_avail_type; /* supported device type by both host and card */
|
||||
|
||||
struct dentry *debugfs_root;
|
||||
struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
|
||||
|
@ -353,7 +352,7 @@ struct mmc_fixup {
|
|||
#define CID_OEMID_ANY ((unsigned short) -1)
|
||||
#define CID_NAME_ANY (NULL)
|
||||
|
||||
#define END_FIXUP { 0 }
|
||||
#define END_FIXUP { NULL }
|
||||
|
||||
#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
|
||||
_cis_vendor, _cis_device, \
|
||||
|
@ -418,11 +417,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
|||
|
||||
#define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT)
|
||||
#define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY)
|
||||
#define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_hs200(c) ((c)->state & MMC_STATE_HIGHSPEED_200)
|
||||
#define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR)
|
||||
#define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC)
|
||||
#define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED))
|
||||
#define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS)
|
||||
|
@ -430,11 +425,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
|
|||
|
||||
#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT)
|
||||
#define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY)
|
||||
#define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED)
|
||||
#define mmc_card_set_hs200(c) ((c)->state |= MMC_STATE_HIGHSPEED_200)
|
||||
#define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR)
|
||||
#define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR)
|
||||
#define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED)
|
||||
#define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC)
|
||||
#define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED)
|
||||
#define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS)
|
||||
|
|
|
@ -248,20 +248,6 @@ struct dw_mci_board {
|
|||
/* delay in mS before detecting cards after interrupt */
|
||||
u32 detect_delay_ms;
|
||||
|
||||
int (*init)(u32 slot_id, irq_handler_t , void *);
|
||||
int (*get_ro)(u32 slot_id);
|
||||
int (*get_cd)(u32 slot_id);
|
||||
int (*get_ocr)(u32 slot_id);
|
||||
int (*get_bus_wd)(u32 slot_id);
|
||||
/*
|
||||
* Enable power to selected slot and set voltage to desired level.
|
||||
* Voltage levels are specified using MMC_VDD_xxx defines defined
|
||||
* in linux/mmc/host.h file.
|
||||
*/
|
||||
void (*setpower)(u32 slot_id, u32 volt);
|
||||
void (*exit)(u32 slot_id);
|
||||
void (*select_slot)(u32 slot_id);
|
||||
|
||||
struct dw_mci_dma_ops *dma_ops;
|
||||
struct dma_pdata *data;
|
||||
struct block_settings *blk_settings;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/fault-inject.h>
|
||||
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/pm.h>
|
||||
|
||||
struct mmc_ios {
|
||||
|
@ -58,13 +59,9 @@ struct mmc_ios {
|
|||
#define MMC_TIMING_UHS_SDR50 5
|
||||
#define MMC_TIMING_UHS_SDR104 6
|
||||
#define MMC_TIMING_UHS_DDR50 7
|
||||
#define MMC_TIMING_MMC_HS200 8
|
||||
|
||||
#define MMC_SDR_MODE 0
|
||||
#define MMC_1_2V_DDR_MODE 1
|
||||
#define MMC_1_8V_DDR_MODE 2
|
||||
#define MMC_1_2V_SDR_MODE 3
|
||||
#define MMC_1_8V_SDR_MODE 4
|
||||
#define MMC_TIMING_MMC_DDR52 8
|
||||
#define MMC_TIMING_MMC_HS200 9
|
||||
#define MMC_TIMING_MMC_HS400 10
|
||||
|
||||
unsigned char signal_voltage; /* signalling voltage (1.8V or 3.3V) */
|
||||
|
||||
|
@ -136,6 +133,9 @@ struct mmc_host_ops {
|
|||
|
||||
/* The tuning command opcode value is different for SD and eMMC cards */
|
||||
int (*execute_tuning)(struct mmc_host *host, u32 opcode);
|
||||
|
||||
/* Prepare HS400 target operating frequency depending host driver */
|
||||
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
|
||||
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
|
||||
void (*hw_reset)(struct mmc_host *host);
|
||||
void (*card_event)(struct mmc_host *host);
|
||||
|
@ -278,6 +278,11 @@ struct mmc_host {
|
|||
#define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \
|
||||
MMC_CAP2_PACKED_WR)
|
||||
#define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */
|
||||
#define MMC_CAP2_HS400_1_8V (1 << 15) /* Can support HS400 1.8V */
|
||||
#define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */
|
||||
#define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \
|
||||
MMC_CAP2_HS400_1_2V)
|
||||
#define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
|
||||
|
||||
mmc_pm_flag_t pm_caps; /* supported pm features */
|
||||
|
||||
|
@ -318,6 +323,8 @@ struct mmc_host {
|
|||
int rescan_disable; /* disable card detection */
|
||||
int rescan_entered; /* used with nonremovable devices */
|
||||
|
||||
bool trigger_card_event; /* card_event necessary */
|
||||
|
||||
struct mmc_card *card; /* device attached to this host */
|
||||
|
||||
wait_queue_head_t wq;
|
||||
|
@ -391,12 +398,13 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
|||
wake_up_process(host->sdio_irq_thread);
|
||||
}
|
||||
|
||||
void sdio_run_irqs(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_get_supply(struct mmc_host *mmc);
|
||||
#else
|
||||
static inline int mmc_regulator_get_ocrmask(struct regulator *supply)
|
||||
{
|
||||
|
@ -409,13 +417,10 @@ static inline int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc);
|
||||
|
||||
int mmc_pm_notify(struct notifier_block *notify_block, unsigned long, void *);
|
||||
|
||||
static inline int mmc_card_is_removable(struct mmc_host *host)
|
||||
|
@ -475,4 +480,32 @@ static inline unsigned int mmc_host_clk_rate(struct mmc_host *host)
|
|||
return host->ios.clock;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int mmc_card_hs(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_SD_HS ||
|
||||
card->host->ios.timing == MMC_TIMING_MMC_HS;
|
||||
}
|
||||
|
||||
static inline int mmc_card_uhs(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing >= MMC_TIMING_UHS_SDR12 &&
|
||||
card->host->ios.timing <= MMC_TIMING_UHS_DDR50;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs200(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_HS200;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_ddr52(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_DDR52;
|
||||
}
|
||||
|
||||
static inline bool mmc_card_hs400(struct mmc_card *card)
|
||||
{
|
||||
return card->host->ios.timing == MMC_TIMING_MMC_HS400;
|
||||
}
|
||||
|
||||
#endif /* LINUX_MMC_HOST_H */
|
||||
|
|
|
@ -325,6 +325,7 @@ struct _mmc_csd {
|
|||
#define EXT_CSD_POWER_OFF_LONG_TIME 247 /* RO */
|
||||
#define EXT_CSD_GENERIC_CMD6_TIME 248 /* RO */
|
||||
#define EXT_CSD_CACHE_SIZE 249 /* RO, 4 bytes */
|
||||
#define EXT_CSD_PWR_CL_DDR_200_360 253 /* RO */
|
||||
#define EXT_CSD_TAG_UNIT_SIZE 498 /* RO */
|
||||
#define EXT_CSD_DATA_TAG_SUPPORT 499 /* RO */
|
||||
#define EXT_CSD_MAX_PACKED_WRITES 500 /* RO */
|
||||
|
@ -354,18 +355,25 @@ struct _mmc_csd {
|
|||
#define EXT_CSD_CMD_SET_SECURE (1<<1)
|
||||
#define EXT_CSD_CMD_SET_CPSECURE (1<<2)
|
||||
|
||||
#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */
|
||||
#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */
|
||||
#define EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */
|
||||
#define EXT_CSD_CARD_TYPE_HS_26 (1<<0) /* Card can run at 26MHz */
|
||||
#define EXT_CSD_CARD_TYPE_HS_52 (1<<1) /* Card can run at 52MHz */
|
||||
#define EXT_CSD_CARD_TYPE_HS (EXT_CSD_CARD_TYPE_HS_26 | \
|
||||
EXT_CSD_CARD_TYPE_HS_52)
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_8V (1<<2) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.8V or 3V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_1_2V (1<<3) /* Card can run at 52MHz */
|
||||
/* DDR mode @1.2V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_DDR_52 (EXT_CSD_CARD_TYPE_DDR_1_8V \
|
||||
| EXT_CSD_CARD_TYPE_DDR_1_2V)
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_8V (1<<4) /* Card can run at 200MHz */
|
||||
#define EXT_CSD_CARD_TYPE_SDR_1_2V (1<<5) /* Card can run at 200MHz */
|
||||
#define EXT_CSD_CARD_TYPE_HS200_1_8V (1<<4) /* Card can run at 200MHz */
|
||||
#define EXT_CSD_CARD_TYPE_HS200_1_2V (1<<5) /* Card can run at 200MHz */
|
||||
/* SDR mode @1.2V I/O */
|
||||
#define EXT_CSD_CARD_TYPE_HS200 (EXT_CSD_CARD_TYPE_HS200_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_HS200_1_2V)
|
||||
#define EXT_CSD_CARD_TYPE_HS400_1_8V (1<<6) /* Card can run at 200MHz DDR, 1.8V */
|
||||
#define EXT_CSD_CARD_TYPE_HS400_1_2V (1<<7) /* Card can run at 200MHz DDR, 1.2V */
|
||||
#define EXT_CSD_CARD_TYPE_HS400 (EXT_CSD_CARD_TYPE_HS400_1_8V | \
|
||||
EXT_CSD_CARD_TYPE_HS400_1_2V)
|
||||
|
||||
#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
|
||||
#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
|
||||
|
@ -373,6 +381,11 @@ struct _mmc_csd {
|
|||
#define EXT_CSD_DDR_BUS_WIDTH_4 5 /* Card is in 4 bit DDR mode */
|
||||
#define EXT_CSD_DDR_BUS_WIDTH_8 6 /* Card is in 8 bit DDR mode */
|
||||
|
||||
#define EXT_CSD_TIMING_BC 0 /* Backwards compatility */
|
||||
#define EXT_CSD_TIMING_HS 1 /* High speed */
|
||||
#define EXT_CSD_TIMING_HS200 2 /* HS200 */
|
||||
#define EXT_CSD_TIMING_HS400 3 /* HS400 */
|
||||
|
||||
#define EXT_CSD_SEC_ER_EN BIT(0)
|
||||
#define EXT_CSD_SEC_BD_BLK_EN BIT(2)
|
||||
#define EXT_CSD_SEC_GB_CL_EN BIT(4)
|
||||
|
|
|
@ -57,12 +57,8 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15)
|
||||
/* Controller reports inverted write-protect state */
|
||||
#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16)
|
||||
/* Controller has nonstandard clock management */
|
||||
#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17)
|
||||
/* Controller does not like fast PIO transfers */
|
||||
#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18)
|
||||
/* Controller losing signal/interrupt enable states after reset */
|
||||
#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19)
|
||||
/* Controller has to be forced to use block size of 2048 bytes */
|
||||
#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20)
|
||||
/* Controller cannot do multi-block transfers */
|
||||
|
@ -147,6 +143,7 @@ struct sdhci_host {
|
|||
|
||||
bool runtime_suspended; /* Host is runtime suspended */
|
||||
bool bus_on; /* Bus power prevents runtime suspend */
|
||||
bool preset_enabled; /* Preset is enabled */
|
||||
|
||||
struct mmc_request *mrq; /* Current request */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
|
@ -164,8 +161,7 @@ struct sdhci_host {
|
|||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
struct tasklet_struct card_tasklet; /* Tasklet structures */
|
||||
struct tasklet_struct finish_tasklet;
|
||||
struct tasklet_struct finish_tasklet; /* Tasklet structures */
|
||||
|
||||
struct timer_list timer; /* Timer for timeouts */
|
||||
|
||||
|
@ -177,6 +173,13 @@ struct sdhci_host {
|
|||
unsigned int ocr_avail_mmc;
|
||||
u32 ocr_mask; /* available voltages */
|
||||
|
||||
unsigned timing; /* Current timing */
|
||||
|
||||
u32 thread_isr;
|
||||
|
||||
/* cached registers */
|
||||
u32 ier;
|
||||
|
||||
wait_queue_head_t buf_ready_int; /* Waitqueue for Buffer Read Ready interrupt */
|
||||
unsigned int tuning_done; /* Condition flag set when CMD19 succeeds */
|
||||
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
/*
|
||||
* OMAP DMA Engine support
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __LINUX_OMAP_DMA_H
|
||||
#define __LINUX_OMAP_DMA_H
|
||||
|
||||
struct dma_chan;
|
||||
|
||||
#if defined(CONFIG_DMA_OMAP) || (defined(CONFIG_DMA_OMAP_MODULE) && defined(MODULE))
|
||||
bool omap_dma_filter_fn(struct dma_chan *, void *);
|
||||
#else
|
||||
static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#include <linux/omap-dmaengine.h>
|
||||
|
||||
/*
|
||||
* Legacy OMAP DMA handling defines and functions
|
||||
|
|
21
include/linux/omap-dmaengine.h
Normal file
21
include/linux/omap-dmaengine.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* OMAP DMA Engine support
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef __LINUX_OMAP_DMAENGINE_H
|
||||
#define __LINUX_OMAP_DMAENGINE_H
|
||||
|
||||
struct dma_chan;
|
||||
|
||||
#if defined(CONFIG_DMA_OMAP) || (defined(CONFIG_DMA_OMAP_MODULE) && defined(MODULE))
|
||||
bool omap_dma_filter_fn(struct dma_chan *, void *);
|
||||
#else
|
||||
static inline bool omap_dma_filter_fn(struct dma_chan *c, void *d)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif /* __LINUX_OMAP_DMAENGINE_H */
|
Loading…
Reference in a new issue