MMC core:
- Fix a race condition in the request handling - Skip trim commands for some buggy kingston eMMCs - An optimization and a correction for erase groups - Set CMD23 quirk for some Sandisk cards MMC host: - sdhci: Give GPIO CD higher precedence and don't poll when it's used - sdhci: Fix DMA memory leakage - sdhci: Some updates for clock management - sdhci-of-at91: introduce driver for the Atmel SDMMC - sdhci-of-arasan: Add support for sdhci-5.1 - sdhci-esdhc-imx: Add support for imx7d which also supports HS400 - sdhci: A collection of fixes and improvements for various sdhci hosts - omap_hsmmc: Modernization of the regulator code - dw_mmc: A couple of fixes for DMA and PIO mode - usdhi6rol0: A few fixes and support probe deferral for regulators - pxamci: Convert to use dmaengine - sh_mmcif: Fix the suspend process in a short term solution - tmio: Adjust timeout for commands - sunxi: Fix timeout while gating/ungating clock -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJV7WBfAAoJEP4mhCVzWIwpzMMQAISg3SvN116lxgPJsFmx2Oxd fiSdksUTrGB4/Ya/gTGyFQx5wOep1Y/yLhuQr+2rDbIPlIFiKQuQCEVXERXy5VJl rmsekgIrMPamiGGYBM5b6n/UhWYktLqoGc88irKYPIaIjLQeH3iqB83y04R6BqBR tOC4PKnnHaJyJMt1oR/xHi1PjFKX4/WQtnCUlvzUMu8XcrXRwqgWb/hw1QXLUUZa 3npFLxlSqJVXJxtjRSeYZp5juvt+l03rK9h/jNmoDfPl7WtdwxkHtShsnS8b74lF ZMhrl3phpyMdPNZ0pCwrQJxhplg0MB0Ey8TErRLBsp90Fre0tedf2w6RElkSXek8 tApr6I/Fkydu5ILnNhnhswsbp4rc9tC7kfr5RkPKQF0icEcZFxNblrDtbD/jFYSu csIZO3xsJTvo4Z7kjYymTn3UpsV8l/hZDNOWHzt7LCTt8X2otEvsLvmabK9pMayI FipP3pbsM4/H70eOOwpWmzM4J4FGRtKGEXqJGfODqlGHlrXz06xtlvBDzDvW86BA hFr3cuDPqpMSm9UUPIVYZrm4OuO6p1JpVpj7/cmg48mJEqVNQZ+hyHC0zU2F/saL 4pogQC1vL4SM4xJB+rY4Fo+E7gNEkYjEymEJ4yCbwdqF9Nxjs7m2rCulty4JCVyY vwMt5mo0d2IsixclFghh =wR1C -----END PGP SIGNATURE----- Merge tag 'mmc-v4.3' of git://git.linaro.org/people/ulf.hansson/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Fix a race condition in the request handling - Skip trim commands for some buggy kingston eMMCs - An optimization and a correction for erase groups - Set CMD23 quirk for some Sandisk cards MMC host: - sdhci: Give GPIO CD higher precedence and don't poll when it's used - sdhci: Fix DMA memory leakage - sdhci: Some updates for clock management - sdhci-of-at91: introduce driver for the Atmel SDMMC - sdhci-of-arasan: Add support for sdhci-5.1 - sdhci-esdhc-imx: Add support for imx7d which also supports HS400 - sdhci: A collection of fixes and improvements for various sdhci hosts - omap_hsmmc: Modernization of the regulator code - dw_mmc: A couple of fixes for DMA and PIO mode - usdhi6rol0: A few fixes and support probe deferral for regulators - pxamci: Convert to use dmaengine - sh_mmcif: Fix the suspend process in a short term solution - tmio: Adjust timeout for commands - sunxi: Fix timeout while gating/ungating clock" * tag 'mmc-v4.3' of git://git.linaro.org/people/ulf.hansson/mmc: (67 commits) mmc: android-goldfish: remove incorrect __iomem annotation mmc: core: fix race condition in mmc_wait_data_done mmc: host: omap_hsmmc: remove CONFIG_REGULATOR check mmc: host: omap_hsmmc: use ios->vdd for setting vmmc voltage mmc: host: omap_hsmmc: use regulator_is_enabled to find pbias status mmc: host: omap_hsmmc: enable/disable vmmc_aux regulator based on previous state mmc: host: omap_hsmmc: don't use ->set_power to set initial regulator state mmc: host: omap_hsmmc: avoid pbias regulator enable on power off mmc: host: omap_hsmmc: add separate function to set pbias mmc: host: omap_hsmmc: add separate functions for enable/disable supply mmc: host: omap_hsmmc: return error if any of the regulator APIs fail mmc: host: omap_hsmmc: remove unnecessary pbias set_voltage mmc: host: omap_hsmmc: use mmc_host's vmmc and vqmmc mmc: host: omap_hsmmc: use the ocrmask provided by the vmmc regulator mmc: host: omap_hsmmc: cleanup omap_hsmmc_reg_get() mmc: host: omap_hsmmc: return on fatal errors from omap_hsmmc_reg_get mmc: host: omap_hsmmc: use devm_regulator_get_optional() for vmmc mmc: sdhci-of-at91: fix platform_no_drv_owner.cocci warnings mmc: sh_mmcif: Fix suspend process mmc: usdhi6rol0: fix error return code ...
This commit is contained in:
commit
85579ad7f1
|
@ -9,7 +9,7 @@ Device Tree Bindings for the Arasan SDHCI Controller
|
|||
|
||||
Required Properties:
|
||||
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or
|
||||
'arasan,sdhci-4.9a'
|
||||
'arasan,sdhci-4.9a' or 'arasan,sdhci-5.1'
|
||||
- reg: From mmc bindings: Register location and length.
|
||||
- clocks: From clock bindings: Handles to clock inputs.
|
||||
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
|
||||
|
|
|
@ -15,6 +15,7 @@ Required properties:
|
|||
"fsl,imx6q-usdhc"
|
||||
"fsl,imx6sl-usdhc"
|
||||
"fsl,imx6sx-usdhc"
|
||||
"fsl,imx7d-usdhc"
|
||||
|
||||
Optional properties:
|
||||
- fsl,wp-controller : Indicate to use controller internal write protection
|
||||
|
@ -27,6 +28,11 @@ Optional properties:
|
|||
transparent level shifters on the outputs of the controller. Two cells are
|
||||
required, first cell specifies minimum slot voltage (mV), second cell
|
||||
specifies maximum slot voltage (mV). Several ranges could be specified.
|
||||
- fsl,tuning-step: Specify the increasing delay cell steps in tuning procedure.
|
||||
The uSDHC use one delay cell as default increasing step to do tuning process.
|
||||
This property allows user to change the tuning step to more than one delay
|
||||
cells which is useful for some special boards or cards when the default
|
||||
tuning step can't find the proper delay window within limited tuning retries.
|
||||
|
||||
Examples:
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
* Atmel SDHCI controller
|
||||
|
||||
This file documents the differences between the core properties in
|
||||
Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the
|
||||
sdhci-of-at91 driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "atmel,sama5d2-sdhci".
|
||||
- clocks: Phandlers to the clocks.
|
||||
- clock-names: Must be "hclock", "multclk", "baseclk";
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
sdmmc0: sdio-host@a0000000 {
|
||||
compatible = "atmel,sama5d2-sdhci";
|
||||
reg = <0xa0000000 0x300>;
|
||||
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>;
|
||||
clock-names = "hclock", "multclk", "baseclk";
|
||||
};
|
|
@ -102,7 +102,7 @@ not every application needs SDIO irq, e.g. MMC cards.
|
|||
pinctrl-1 = <&mmc1_idle>;
|
||||
pinctrl-2 = <&mmc1_sleep>;
|
||||
...
|
||||
interrupts-extended = <&intc 64 &gpio2 28 0>;
|
||||
interrupts-extended = <&intc 64 &gpio2 28 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
mmc1_idle : pinmux_cirq_pin {
|
||||
|
|
|
@ -1905,6 +1905,12 @@ L: linux-mtd@lists.infradead.org
|
|||
S: Supported
|
||||
F: drivers/mtd/nand/atmel_nand*
|
||||
|
||||
ATMEL SDMMC DRIVER
|
||||
M: Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/mmc/host/sdhci-of-at91.c
|
||||
|
||||
ATMEL SPI DRIVER
|
||||
M: Nicolas Ferre <nicolas.ferre@atmel.com>
|
||||
S: Supported
|
||||
|
|
|
@ -47,10 +47,13 @@
|
|||
#include "queue.h"
|
||||
|
||||
MODULE_ALIAS("mmc:block");
|
||||
|
||||
#ifdef KERNEL
|
||||
#ifdef MODULE_PARAM_PREFIX
|
||||
#undef MODULE_PARAM_PREFIX
|
||||
#endif
|
||||
#define MODULE_PARAM_PREFIX "mmcblk."
|
||||
#endif
|
||||
|
||||
#define INAND_CMD38_ARG_EXT_CSD 113
|
||||
#define INAND_CMD38_ARG_ERASE 0x00
|
||||
|
@ -2386,6 +2389,7 @@ force_ro_fail:
|
|||
#define CID_MANFID_TOSHIBA 0x11
|
||||
#define CID_MANFID_MICRON 0x13
|
||||
#define CID_MANFID_SAMSUNG 0x15
|
||||
#define CID_MANFID_KINGSTON 0x70
|
||||
|
||||
static const struct mmc_fixup blk_fixups[] =
|
||||
{
|
||||
|
@ -2408,6 +2412,10 @@ static const struct mmc_fixup blk_fixups[] =
|
|||
*
|
||||
* N.B. This doesn't affect SD cards.
|
||||
*/
|
||||
MMC_FIXUP("SDMB-32", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("SDM032", CID_MANFID_SANDISK, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC08G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_BLK_NO_CMD23),
|
||||
MMC_FIXUP("MMC16G", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc,
|
||||
|
@ -2444,6 +2452,15 @@ static const struct mmc_fixup blk_fixups[] =
|
|||
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
|
||||
|
||||
/*
|
||||
* On Some Kingston eMMCs, performing trim can result in
|
||||
* unrecoverable data conrruption occasionally due to a firmware bug.
|
||||
*/
|
||||
MMC_FIXUP("V10008", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
MMC_FIXUP("V10016", CID_MANFID_KINGSTON, CID_OEMID_ANY, add_quirk_mmc,
|
||||
MMC_QUIRK_TRIM_BROKEN),
|
||||
|
||||
END_FIXUP
|
||||
};
|
||||
|
||||
|
|
|
@ -358,8 +358,10 @@ EXPORT_SYMBOL(mmc_start_bkops);
|
|||
*/
|
||||
static void mmc_wait_data_done(struct mmc_request *mrq)
|
||||
{
|
||||
mrq->host->context_info.is_done_rcv = true;
|
||||
wake_up_interruptible(&mrq->host->context_info.wait);
|
||||
struct mmc_context_info *context_info = &mrq->host->context_info;
|
||||
|
||||
context_info->is_done_rcv = true;
|
||||
wake_up_interruptible(&context_info->wait);
|
||||
}
|
||||
|
||||
static void mmc_wait_done(struct mmc_request *mrq)
|
||||
|
@ -2168,6 +2170,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
|||
unsigned int arg)
|
||||
{
|
||||
unsigned int rem, to = from + nr;
|
||||
int err;
|
||||
|
||||
if (!(card->host->caps & MMC_CAP_ERASE) ||
|
||||
!(card->csd.cmdclass & CCC_ERASE))
|
||||
|
@ -2218,6 +2221,22 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
|||
/* 'from' and 'to' are inclusive */
|
||||
to -= 1;
|
||||
|
||||
/*
|
||||
* Special case where only one erase-group fits in the timeout budget:
|
||||
* If the region crosses an erase-group boundary on this particular
|
||||
* case, we will be trimming more than one erase-group which, does not
|
||||
* fit in the timeout budget of the controller, so we need to split it
|
||||
* and call mmc_do_erase() twice if necessary. This special case is
|
||||
* identified by the card->eg_boundary flag.
|
||||
*/
|
||||
rem = card->erase_size - (from % card->erase_size);
|
||||
if ((arg & MMC_TRIM_ARGS) && (card->eg_boundary) && (nr > rem)) {
|
||||
err = mmc_do_erase(card, from, from + rem - 1, arg);
|
||||
from += rem;
|
||||
if ((err) || (to <= from))
|
||||
return err;
|
||||
}
|
||||
|
||||
return mmc_do_erase(card, from, to, arg);
|
||||
}
|
||||
EXPORT_SYMBOL(mmc_erase);
|
||||
|
@ -2233,7 +2252,8 @@ EXPORT_SYMBOL(mmc_can_erase);
|
|||
|
||||
int mmc_can_trim(struct mmc_card *card)
|
||||
{
|
||||
if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN)
|
||||
if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_GB_CL_EN) &&
|
||||
(!(card->quirks & MMC_QUIRK_TRIM_BROKEN)))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2313,16 +2333,28 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
|
|||
if (!qty)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* When specifying a sector range to trim, chances are we might cross
|
||||
* an erase-group boundary even if the amount of sectors is less than
|
||||
* one erase-group.
|
||||
* If we can only fit one erase-group in the controller timeout budget,
|
||||
* we have to care that erase-group boundaries are not crossed by a
|
||||
* single trim operation. We flag that special case with "eg_boundary".
|
||||
* In all other cases we can just decrement qty and pretend that we
|
||||
* always touch (qty + 1) erase-groups as a simple optimization.
|
||||
*/
|
||||
if (qty == 1)
|
||||
return 1;
|
||||
card->eg_boundary = 1;
|
||||
else
|
||||
qty--;
|
||||
|
||||
/* Convert qty to sectors */
|
||||
if (card->erase_shift)
|
||||
max_discard = --qty << card->erase_shift;
|
||||
max_discard = qty << card->erase_shift;
|
||||
else if (mmc_card_sd(card))
|
||||
max_discard = qty;
|
||||
max_discard = qty + 1;
|
||||
else
|
||||
max_discard = --qty * card->erase_size;
|
||||
max_discard = qty * card->erase_size;
|
||||
|
||||
return max_discard;
|
||||
}
|
||||
|
|
|
@ -398,7 +398,7 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
{
|
||||
struct device_node *np;
|
||||
u32 bus_width;
|
||||
int len, ret;
|
||||
int ret;
|
||||
bool cd_cap_invert, cd_gpio_invert = false;
|
||||
bool ro_cap_invert, ro_gpio_invert = false;
|
||||
|
||||
|
@ -445,12 +445,12 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
*/
|
||||
|
||||
/* Parse Card Detection */
|
||||
if (of_find_property(np, "non-removable", &len)) {
|
||||
if (of_property_read_bool(np, "non-removable")) {
|
||||
host->caps |= MMC_CAP_NONREMOVABLE;
|
||||
} else {
|
||||
cd_cap_invert = of_property_read_bool(np, "cd-inverted");
|
||||
|
||||
if (of_find_property(np, "broken-cd", &len))
|
||||
if (of_property_read_bool(np, "broken-cd"))
|
||||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ret = mmc_gpiod_request_cd(host, "cd", 0, true,
|
||||
|
@ -491,41 +491,41 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
if (ro_cap_invert ^ ro_gpio_invert)
|
||||
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
if (of_find_property(np, "cap-sd-highspeed", &len))
|
||||
if (of_property_read_bool(np, "cap-sd-highspeed"))
|
||||
host->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
if (of_find_property(np, "cap-mmc-highspeed", &len))
|
||||
if (of_property_read_bool(np, "cap-mmc-highspeed"))
|
||||
host->caps |= MMC_CAP_MMC_HIGHSPEED;
|
||||
if (of_find_property(np, "sd-uhs-sdr12", &len))
|
||||
if (of_property_read_bool(np, "sd-uhs-sdr12"))
|
||||
host->caps |= MMC_CAP_UHS_SDR12;
|
||||
if (of_find_property(np, "sd-uhs-sdr25", &len))
|
||||
if (of_property_read_bool(np, "sd-uhs-sdr25"))
|
||||
host->caps |= MMC_CAP_UHS_SDR25;
|
||||
if (of_find_property(np, "sd-uhs-sdr50", &len))
|
||||
if (of_property_read_bool(np, "sd-uhs-sdr50"))
|
||||
host->caps |= MMC_CAP_UHS_SDR50;
|
||||
if (of_find_property(np, "sd-uhs-sdr104", &len))
|
||||
if (of_property_read_bool(np, "sd-uhs-sdr104"))
|
||||
host->caps |= MMC_CAP_UHS_SDR104;
|
||||
if (of_find_property(np, "sd-uhs-ddr50", &len))
|
||||
if (of_property_read_bool(np, "sd-uhs-ddr50"))
|
||||
host->caps |= MMC_CAP_UHS_DDR50;
|
||||
if (of_find_property(np, "cap-power-off-card", &len))
|
||||
if (of_property_read_bool(np, "cap-power-off-card"))
|
||||
host->caps |= MMC_CAP_POWER_OFF_CARD;
|
||||
if (of_find_property(np, "cap-sdio-irq", &len))
|
||||
if (of_property_read_bool(np, "cap-sdio-irq"))
|
||||
host->caps |= MMC_CAP_SDIO_IRQ;
|
||||
if (of_find_property(np, "full-pwr-cycle", &len))
|
||||
if (of_property_read_bool(np, "full-pwr-cycle"))
|
||||
host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
|
||||
if (of_find_property(np, "keep-power-in-suspend", &len))
|
||||
if (of_property_read_bool(np, "keep-power-in-suspend"))
|
||||
host->pm_caps |= MMC_PM_KEEP_POWER;
|
||||
if (of_find_property(np, "enable-sdio-wakeup", &len))
|
||||
if (of_property_read_bool(np, "enable-sdio-wakeup"))
|
||||
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
|
||||
if (of_find_property(np, "mmc-ddr-1_8v", &len))
|
||||
if (of_property_read_bool(np, "mmc-ddr-1_8v"))
|
||||
host->caps |= MMC_CAP_1_8V_DDR;
|
||||
if (of_find_property(np, "mmc-ddr-1_2v", &len))
|
||||
if (of_property_read_bool(np, "mmc-ddr-1_2v"))
|
||||
host->caps |= MMC_CAP_1_2V_DDR;
|
||||
if (of_find_property(np, "mmc-hs200-1_8v", &len))
|
||||
if (of_property_read_bool(np, "mmc-hs200-1_8v"))
|
||||
host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
|
||||
if (of_find_property(np, "mmc-hs200-1_2v", &len))
|
||||
if (of_property_read_bool(np, "mmc-hs200-1_2v"))
|
||||
host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
|
||||
if (of_find_property(np, "mmc-hs400-1_8v", &len))
|
||||
if (of_property_read_bool(np, "mmc-hs400-1_8v"))
|
||||
host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
|
||||
if (of_find_property(np, "mmc-hs400-1_2v", &len))
|
||||
if (of_property_read_bool(np, "mmc-hs400-1_2v"))
|
||||
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
|
||||
|
||||
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
|
||||
|
|
|
@ -129,6 +129,14 @@ config MMC_SDHCI_OF_ARASAN
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_OF_AT91
|
||||
tristate "SDHCI OF support for the Atmel SDMMC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Atmel SDMMC driver
|
||||
|
||||
config MMC_SDHCI_OF_ESDHC
|
||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
|
|
|
@ -67,6 +67,7 @@ obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
|
|||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||
|
|
|
@ -118,7 +118,7 @@ struct goldfish_mmc_host {
|
|||
struct mmc_host *mmc;
|
||||
struct device *dev;
|
||||
unsigned char id; /* 16xx chips have 2 MMC blocks */
|
||||
void __iomem *virt_base;
|
||||
void *virt_base;
|
||||
unsigned int phys_base;
|
||||
int irq;
|
||||
unsigned char bus_mode;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <linux/platform_data/mmc-atmel-mci.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
|
|
|
@ -73,6 +73,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
|||
/* It is slot 8 on Rockchip SoCs */
|
||||
host->sdio_id0 = 8;
|
||||
|
||||
/* It needs this quirk on all Rockchip SoCs */
|
||||
host->pdata->quirks |= DW_MCI_QUIRK_BROKEN_DTO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,6 +99,9 @@ struct idmac_desc {
|
|||
|
||||
__le32 des3; /* buffer 2 physical address */
|
||||
};
|
||||
|
||||
/* Each descriptor can transfer up to 4KB of data in chained mode */
|
||||
#define DW_MCI_DESC_DATA_LENGTH 0x1000
|
||||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
|
||||
static bool dw_mci_reset(struct dw_mci *host);
|
||||
|
@ -235,8 +238,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
|
|||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
|
||||
u32 cmdr;
|
||||
cmd->error = -EINPROGRESS;
|
||||
|
||||
cmd->error = -EINPROGRESS;
|
||||
cmdr = cmd->opcode;
|
||||
|
||||
if (cmd->opcode == MMC_STOP_TRANSMISSION ||
|
||||
|
@ -371,7 +374,7 @@ static void dw_mci_start_command(struct dw_mci *host,
|
|||
cmd->arg, cmd_flags);
|
||||
|
||||
mci_writel(host, CMDARG, cmd->arg);
|
||||
wmb();
|
||||
wmb(); /* drain writebuffer */
|
||||
dw_mci_wait_while_busy(host, cmd_flags);
|
||||
|
||||
mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START);
|
||||
|
@ -380,6 +383,7 @@ static void dw_mci_start_command(struct dw_mci *host,
|
|||
static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data)
|
||||
{
|
||||
struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort;
|
||||
|
||||
dw_mci_start_command(host, stop, host->stop_cmdr);
|
||||
}
|
||||
|
||||
|
@ -462,69 +466,102 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host)
|
|||
static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data,
|
||||
unsigned int sg_len)
|
||||
{
|
||||
unsigned int desc_len;
|
||||
int i;
|
||||
if (host->dma_64bit_address == 1) {
|
||||
struct idmac_desc_64addr *desc = host->sg_cpu;
|
||||
|
||||
for (i = 0; i < sg_len; i++, desc++) {
|
||||
if (host->dma_64bit_address == 1) {
|
||||
struct idmac_desc_64addr *desc_first, *desc_last, *desc;
|
||||
|
||||
desc_first = desc_last = desc = host->sg_cpu;
|
||||
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||
|
||||
u64 mem_addr = sg_dma_address(&data->sg[i]);
|
||||
|
||||
/*
|
||||
* Set the OWN bit and disable interrupts for this
|
||||
* descriptor
|
||||
*/
|
||||
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
|
||||
IDMAC_DES0_CH;
|
||||
/* Buffer length */
|
||||
IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length);
|
||||
for ( ; length ; desc++) {
|
||||
desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ?
|
||||
length : DW_MCI_DESC_DATA_LENGTH;
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des4 = mem_addr & 0xffffffff;
|
||||
desc->des5 = mem_addr >> 32;
|
||||
length -= desc_len;
|
||||
|
||||
/*
|
||||
* Set the OWN bit and disable interrupts
|
||||
* for this descriptor
|
||||
*/
|
||||
desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
|
||||
IDMAC_DES0_CH;
|
||||
|
||||
/* Buffer length */
|
||||
IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len);
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des4 = mem_addr & 0xffffffff;
|
||||
desc->des5 = mem_addr >> 32;
|
||||
|
||||
/* Update physical address for the next desc */
|
||||
mem_addr += desc_len;
|
||||
|
||||
/* Save pointer to the last descriptor */
|
||||
desc_last = desc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set first descriptor */
|
||||
desc = host->sg_cpu;
|
||||
desc->des0 |= IDMAC_DES0_FD;
|
||||
desc_first->des0 |= IDMAC_DES0_FD;
|
||||
|
||||
/* Set last descriptor */
|
||||
desc = host->sg_cpu + (i - 1) *
|
||||
sizeof(struct idmac_desc_64addr);
|
||||
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||
desc->des0 |= IDMAC_DES0_LD;
|
||||
desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||
desc_last->des0 |= IDMAC_DES0_LD;
|
||||
|
||||
} else {
|
||||
struct idmac_desc *desc = host->sg_cpu;
|
||||
struct idmac_desc *desc_first, *desc_last, *desc;
|
||||
|
||||
for (i = 0; i < sg_len; i++, desc++) {
|
||||
desc_first = desc_last = desc = host->sg_cpu;
|
||||
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||
|
||||
u32 mem_addr = sg_dma_address(&data->sg[i]);
|
||||
|
||||
/*
|
||||
* Set the OWN bit and disable interrupts for this
|
||||
* descriptor
|
||||
*/
|
||||
desc->des0 = cpu_to_le32(IDMAC_DES0_OWN |
|
||||
IDMAC_DES0_DIC | IDMAC_DES0_CH);
|
||||
/* Buffer length */
|
||||
IDMAC_SET_BUFFER1_SIZE(desc, length);
|
||||
for ( ; length ; desc++) {
|
||||
desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ?
|
||||
length : DW_MCI_DESC_DATA_LENGTH;
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des2 = cpu_to_le32(mem_addr);
|
||||
length -= desc_len;
|
||||
|
||||
/*
|
||||
* Set the OWN bit and disable interrupts
|
||||
* for this descriptor
|
||||
*/
|
||||
desc->des0 = cpu_to_le32(IDMAC_DES0_OWN |
|
||||
IDMAC_DES0_DIC |
|
||||
IDMAC_DES0_CH);
|
||||
|
||||
/* Buffer length */
|
||||
IDMAC_SET_BUFFER1_SIZE(desc, desc_len);
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des2 = cpu_to_le32(mem_addr);
|
||||
|
||||
/* Update physical address for the next desc */
|
||||
mem_addr += desc_len;
|
||||
|
||||
/* Save pointer to the last descriptor */
|
||||
desc_last = desc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set first descriptor */
|
||||
desc = host->sg_cpu;
|
||||
desc->des0 |= cpu_to_le32(IDMAC_DES0_FD);
|
||||
desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD);
|
||||
|
||||
/* Set last descriptor */
|
||||
desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
|
||||
desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC));
|
||||
desc->des0 |= cpu_to_le32(IDMAC_DES0_LD);
|
||||
desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH |
|
||||
IDMAC_DES0_DIC));
|
||||
desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD);
|
||||
}
|
||||
|
||||
wmb();
|
||||
wmb(); /* drain writebuffer */
|
||||
}
|
||||
|
||||
static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
||||
|
@ -542,6 +579,7 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len)
|
|||
temp |= SDMMC_CTRL_USE_IDMAC;
|
||||
mci_writel(host, CTRL, temp);
|
||||
|
||||
/* drain writebuffer */
|
||||
wmb();
|
||||
|
||||
/* Enable the IDMAC */
|
||||
|
@ -589,7 +627,9 @@ static int dw_mci_idmac_init(struct dw_mci *host)
|
|||
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
||||
|
||||
/* Forward link the descriptor list */
|
||||
for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) {
|
||||
for (i = 0, p = host->sg_cpu;
|
||||
i < host->ring_size - 1;
|
||||
i++, p++) {
|
||||
p->des3 = cpu_to_le32(host->sg_dma +
|
||||
(sizeof(struct idmac_desc) * (i + 1)));
|
||||
p->des1 = 0;
|
||||
|
@ -718,7 +758,7 @@ static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data)
|
|||
u32 fifo_width = 1 << host->data_shift;
|
||||
u32 blksz_depth = blksz / fifo_width, fifoth_val;
|
||||
u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers;
|
||||
int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1;
|
||||
int idx = ARRAY_SIZE(mszs) - 1;
|
||||
|
||||
tx_wmark = (host->fifo_depth) / 2;
|
||||
tx_wmark_invers = host->fifo_depth - tx_wmark;
|
||||
|
@ -843,6 +883,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
|||
static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
||||
{
|
||||
unsigned long irqflags;
|
||||
int flags = SG_MITER_ATOMIC;
|
||||
u32 temp;
|
||||
|
||||
data->error = -EINPROGRESS;
|
||||
|
@ -859,7 +900,6 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data)
|
|||
}
|
||||
|
||||
if (dw_mci_submit_data_dma(host, data)) {
|
||||
int flags = SG_MITER_ATOMIC;
|
||||
if (host->data->flags & MMC_DATA_READ)
|
||||
flags |= SG_MITER_TO_SG;
|
||||
else
|
||||
|
@ -906,7 +946,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg)
|
|||
unsigned int cmd_status = 0;
|
||||
|
||||
mci_writel(host, CMDARG, arg);
|
||||
wmb();
|
||||
wmb(); /* drain writebuffer */
|
||||
dw_mci_wait_while_busy(host, cmd);
|
||||
mci_writel(host, CMD, SDMMC_CMD_START | cmd);
|
||||
|
||||
|
@ -1019,7 +1059,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
|
|||
|
||||
if (data) {
|
||||
dw_mci_submit_data(host, data);
|
||||
wmb();
|
||||
wmb(); /* drain writebuffer */
|
||||
}
|
||||
|
||||
dw_mci_start_command(host, cmd, cmdflags);
|
||||
|
@ -1384,14 +1424,15 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
const struct dw_mci_drv_data *drv_data = host->drv_data;
|
||||
int err = -ENOSYS;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (drv_data && drv_data->execute_tuning)
|
||||
err = drv_data->execute_tuning(slot);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
struct dw_mci_slot *slot = mmc_priv(mmc);
|
||||
struct dw_mci *host = slot->host;
|
||||
|
@ -1533,6 +1574,20 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data)
|
|||
return data->error;
|
||||
}
|
||||
|
||||
static void dw_mci_set_drto(struct dw_mci *host)
|
||||
{
|
||||
unsigned int drto_clks;
|
||||
unsigned int drto_ms;
|
||||
|
||||
drto_clks = mci_readl(host, TMOUT) >> 8;
|
||||
drto_ms = DIV_ROUND_UP(drto_clks, host->bus_hz / 1000);
|
||||
|
||||
/* add a bit spare time */
|
||||
drto_ms += 10;
|
||||
|
||||
mod_timer(&host->dto_timer, jiffies + msecs_to_jiffies(drto_ms));
|
||||
}
|
||||
|
||||
static void dw_mci_tasklet_func(unsigned long priv)
|
||||
{
|
||||
struct dw_mci *host = (struct dw_mci *)priv;
|
||||
|
@ -1610,8 +1665,16 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
}
|
||||
|
||||
if (!test_and_clear_bit(EVENT_XFER_COMPLETE,
|
||||
&host->pending_events))
|
||||
&host->pending_events)) {
|
||||
/*
|
||||
* If all data-related interrupts don't come
|
||||
* within the given time in reading data state.
|
||||
*/
|
||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
||||
dw_mci_set_drto(host);
|
||||
break;
|
||||
}
|
||||
|
||||
set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
|
||||
|
||||
|
@ -1644,8 +1707,17 @@ static void dw_mci_tasklet_func(unsigned long priv)
|
|||
|
||||
case STATE_DATA_BUSY:
|
||||
if (!test_and_clear_bit(EVENT_DATA_COMPLETE,
|
||||
&host->pending_events))
|
||||
&host->pending_events)) {
|
||||
/*
|
||||
* If data error interrupt comes but data over
|
||||
* interrupt doesn't come within the given time.
|
||||
* in reading data state.
|
||||
*/
|
||||
if ((host->quirks & DW_MCI_QUIRK_BROKEN_DTO) &&
|
||||
(host->dir_status == DW_MCI_RECV_STATUS))
|
||||
dw_mci_set_drto(host);
|
||||
break;
|
||||
}
|
||||
|
||||
host->data = NULL;
|
||||
set_bit(EVENT_DATA_COMPLETE, &host->completed_events);
|
||||
|
@ -1743,7 +1815,7 @@ static int dw_mci_push_part_bytes(struct dw_mci *host, void *buf, int cnt)
|
|||
/* pull first bytes from part_buf, only use during pull */
|
||||
static int dw_mci_pull_part_bytes(struct dw_mci *host, void *buf, int cnt)
|
||||
{
|
||||
cnt = min(cnt, (int)host->part_buf_count);
|
||||
cnt = min_t(int, cnt, host->part_buf_count);
|
||||
if (cnt) {
|
||||
memcpy(buf, (void *)&host->part_buf + host->part_buf_start,
|
||||
cnt);
|
||||
|
@ -1769,6 +1841,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
if (host->part_buf_count == 2) {
|
||||
|
@ -1795,6 +1868,7 @@ static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u16 *pdata = buf;
|
||||
|
||||
for (; cnt >= 2; cnt -= 2)
|
||||
mci_fifo_writew(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
|
@ -1819,6 +1893,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
int len = min(cnt & -2, (int)sizeof(aligned_buf));
|
||||
int items = len >> 1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_fifo_readw(host->fifo_reg);
|
||||
/* memcpy from aligned buffer into output buffer */
|
||||
|
@ -1830,6 +1905,7 @@ static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u16 *pdata = buf;
|
||||
|
||||
for (; cnt >= 2; cnt -= 2)
|
||||
*pdata++ = mci_fifo_readw(host->fifo_reg);
|
||||
buf = pdata;
|
||||
|
@ -1848,6 +1924,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
if (host->part_buf_count == 4) {
|
||||
|
@ -1874,6 +1951,7 @@ static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u32 *pdata = buf;
|
||||
|
||||
for (; cnt >= 4; cnt -= 4)
|
||||
mci_fifo_writel(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
|
@ -1898,6 +1976,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
int len = min(cnt & -4, (int)sizeof(aligned_buf));
|
||||
int items = len >> 2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_fifo_readl(host->fifo_reg);
|
||||
/* memcpy from aligned buffer into output buffer */
|
||||
|
@ -1909,6 +1988,7 @@ static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u32 *pdata = buf;
|
||||
|
||||
for (; cnt >= 4; cnt -= 4)
|
||||
*pdata++ = mci_fifo_readl(host->fifo_reg);
|
||||
buf = pdata;
|
||||
|
@ -1927,6 +2007,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
/* try and push anything in the part_buf */
|
||||
if (unlikely(host->part_buf_count)) {
|
||||
int len = dw_mci_push_part_bytes(host, buf, cnt);
|
||||
|
||||
buf += len;
|
||||
cnt -= len;
|
||||
|
||||
|
@ -1954,6 +2035,7 @@ static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u64 *pdata = buf;
|
||||
|
||||
for (; cnt >= 8; cnt -= 8)
|
||||
mci_fifo_writeq(host->fifo_reg, *pdata++);
|
||||
buf = pdata;
|
||||
|
@ -1978,6 +2060,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
int len = min(cnt & -8, (int)sizeof(aligned_buf));
|
||||
int items = len >> 3;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < items; ++i)
|
||||
aligned_buf[i] = mci_fifo_readq(host->fifo_reg);
|
||||
|
||||
|
@ -1990,6 +2073,7 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
|
|||
#endif
|
||||
{
|
||||
u64 *pdata = buf;
|
||||
|
||||
for (; cnt >= 8; cnt -= 8)
|
||||
*pdata++ = mci_fifo_readq(host->fifo_reg);
|
||||
buf = pdata;
|
||||
|
@ -2065,7 +2149,7 @@ static void dw_mci_read_data_pio(struct dw_mci *host, bool dto)
|
|||
done:
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
|
||||
}
|
||||
|
||||
|
@ -2119,7 +2203,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host)
|
|||
done:
|
||||
sg_miter_stop(sg_miter);
|
||||
host->sg = NULL;
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
set_bit(EVENT_XFER_COMPLETE, &host->pending_events);
|
||||
}
|
||||
|
||||
|
@ -2128,7 +2212,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status)
|
|||
if (!host->cmd_status)
|
||||
host->cmd_status = status;
|
||||
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
|
||||
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
|
@ -2192,7 +2276,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
if (pending & DW_MCI_CMD_ERROR_FLAGS) {
|
||||
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
|
||||
host->cmd_status = pending;
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
set_bit(EVENT_CMD_COMPLETE, &host->pending_events);
|
||||
}
|
||||
|
||||
|
@ -2200,16 +2284,19 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
|
|||
/* if there is an error report DATA_ERROR */
|
||||
mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS);
|
||||
host->data_status = pending;
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
set_bit(EVENT_DATA_ERROR, &host->pending_events);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
if (pending & SDMMC_INT_DATA_OVER) {
|
||||
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
|
||||
del_timer(&host->dto_timer);
|
||||
|
||||
mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER);
|
||||
if (!host->data_status)
|
||||
host->data_status = pending;
|
||||
smp_wmb();
|
||||
smp_wmb(); /* drain writebuffer */
|
||||
if (host->dir_status == DW_MCI_RECV_STATUS) {
|
||||
if (host->sg != NULL)
|
||||
dw_mci_read_data_pio(host, true);
|
||||
|
@ -2383,27 +2470,20 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
|
|||
if (ret)
|
||||
goto err_host_allocated;
|
||||
|
||||
if (host->pdata->blk_settings) {
|
||||
mmc->max_segs = host->pdata->blk_settings->max_segs;
|
||||
mmc->max_blk_size = host->pdata->blk_settings->max_blk_size;
|
||||
mmc->max_blk_count = host->pdata->blk_settings->max_blk_count;
|
||||
mmc->max_req_size = host->pdata->blk_settings->max_req_size;
|
||||
mmc->max_seg_size = host->pdata->blk_settings->max_seg_size;
|
||||
} else {
|
||||
/* Useful defaults if platform data is unset. */
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
/* Useful defaults if platform data is unset. */
|
||||
if (host->use_dma) {
|
||||
mmc->max_segs = host->ring_size;
|
||||
mmc->max_blk_size = 65536;
|
||||
mmc->max_seg_size = 0x1000;
|
||||
mmc->max_req_size = mmc->max_seg_size * host->ring_size;
|
||||
mmc->max_blk_count = mmc->max_req_size / 512;
|
||||
#else
|
||||
} else {
|
||||
mmc->max_segs = 64;
|
||||
mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */
|
||||
mmc->max_blk_count = 512;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_req_size = mmc->max_blk_size *
|
||||
mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
#endif /* CONFIG_MMC_DW_IDMAC */
|
||||
}
|
||||
|
||||
if (dw_mci_get_cd(mmc))
|
||||
|
@ -2473,8 +2553,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|||
if (host->dma_ops->init && host->dma_ops->start &&
|
||||
host->dma_ops->stop && host->dma_ops->cleanup) {
|
||||
if (host->dma_ops->init(host)) {
|
||||
dev_err(host->dev, "%s: Unable to initialize "
|
||||
"DMA Controller.\n", __func__);
|
||||
dev_err(host->dev, "%s: Unable to initialize DMA Controller.\n",
|
||||
__func__);
|
||||
goto no_dma;
|
||||
}
|
||||
} else {
|
||||
|
@ -2488,7 +2568,6 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
|||
no_dma:
|
||||
dev_info(host->dev, "Using PIO mode.\n");
|
||||
host->use_dma = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset)
|
||||
|
@ -2542,6 +2621,7 @@ static bool dw_mci_reset(struct dw_mci *host)
|
|||
if (host->use_dma) {
|
||||
unsigned long timeout = jiffies + msecs_to_jiffies(500);
|
||||
u32 status;
|
||||
|
||||
do {
|
||||
status = mci_readl(host, STATUS);
|
||||
if (!(status & SDMMC_STATUS_DMA_REQ))
|
||||
|
@ -2551,8 +2631,8 @@ static bool dw_mci_reset(struct dw_mci *host)
|
|||
|
||||
if (status & SDMMC_STATUS_DMA_REQ) {
|
||||
dev_err(host->dev,
|
||||
"%s: Timeout waiting for dma_req to "
|
||||
"clear during reset\n", __func__);
|
||||
"%s: Timeout waiting for dma_req to clear during reset\n",
|
||||
__func__);
|
||||
goto ciu_out;
|
||||
}
|
||||
|
||||
|
@ -2563,8 +2643,8 @@ static bool dw_mci_reset(struct dw_mci *host)
|
|||
} else {
|
||||
/* if the controller reset bit did clear, then set clock regs */
|
||||
if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) {
|
||||
dev_err(host->dev, "%s: fifo/dma reset bits didn't "
|
||||
"clear but ciu was reset, doing clock update\n",
|
||||
dev_err(host->dev,
|
||||
"%s: fifo/dma reset bits didn't clear but ciu was reset, doing clock update\n",
|
||||
__func__);
|
||||
goto ciu_out;
|
||||
}
|
||||
|
@ -2598,6 +2678,28 @@ static void dw_mci_cmd11_timer(unsigned long arg)
|
|||
tasklet_schedule(&host->tasklet);
|
||||
}
|
||||
|
||||
static void dw_mci_dto_timer(unsigned long arg)
|
||||
{
|
||||
struct dw_mci *host = (struct dw_mci *)arg;
|
||||
|
||||
switch (host->state) {
|
||||
case STATE_SENDING_DATA:
|
||||
case STATE_DATA_BUSY:
|
||||
/*
|
||||
* If DTO interrupt does NOT come in sending data state,
|
||||
* we should notify the driver to terminate current transfer
|
||||
* and report a data timeout to the core.
|
||||
*/
|
||||
host->data_status = SDMMC_INT_DRTO;
|
||||
set_bit(EVENT_DATA_ERROR, &host->pending_events);
|
||||
set_bit(EVENT_DATA_COMPLETE, &host->pending_events);
|
||||
tasklet_schedule(&host->tasklet);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct dw_mci_of_quirks {
|
||||
char *quirk;
|
||||
|
@ -2625,8 +2727,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
/* find out number of slots supported */
|
||||
if (of_property_read_u32(dev->of_node, "num-slots",
|
||||
&pdata->num_slots)) {
|
||||
dev_info(dev, "num-slots property not found, "
|
||||
"assuming 1 slot is available\n");
|
||||
dev_info(dev,
|
||||
"num-slots property not found, assuming 1 slot is available\n");
|
||||
pdata->num_slots = 1;
|
||||
}
|
||||
|
||||
|
@ -2636,8 +2738,8 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
pdata->quirks |= of_quirks[idx].id;
|
||||
|
||||
if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth))
|
||||
dev_info(dev, "fifo-depth property not found, using "
|
||||
"value of FIFOTH register as default\n");
|
||||
dev_info(dev,
|
||||
"fifo-depth property not found, using value of FIFOTH register as default\n");
|
||||
|
||||
of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms);
|
||||
|
||||
|
@ -2650,8 +2752,10 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
|
|||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
if (of_find_property(np, "supports-highspeed", NULL))
|
||||
if (of_find_property(np, "supports-highspeed", NULL)) {
|
||||
dev_info(dev, "supports-highspeed property is deprecated.\n");
|
||||
pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
}
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
@ -2706,7 +2810,7 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (host->pdata->num_slots > 1) {
|
||||
if (host->pdata->num_slots < 1) {
|
||||
dev_err(host->dev,
|
||||
"Platform data must supply num_slots.\n");
|
||||
return -ENODEV;
|
||||
|
@ -2774,6 +2878,10 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
|
||||
host->quirks = host->pdata->quirks;
|
||||
|
||||
if (host->quirks & DW_MCI_QUIRK_BROKEN_DTO)
|
||||
setup_timer(&host->dto_timer,
|
||||
dw_mci_dto_timer, (unsigned long)host);
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
spin_lock_init(&host->irq_lock);
|
||||
INIT_LIST_HEAD(&host->queue);
|
||||
|
@ -2874,11 +2982,11 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER |
|
||||
SDMMC_INT_TXDR | SDMMC_INT_RXDR |
|
||||
DW_MCI_ERROR_FLAGS);
|
||||
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */
|
||||
/* Enable mci interrupt */
|
||||
mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
|
||||
|
||||
dev_info(host->dev, "DW MMC controller at irq %d, "
|
||||
"%d bit host data width, "
|
||||
"%u deep fifo\n",
|
||||
dev_info(host->dev,
|
||||
"DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n",
|
||||
host->irq, width, fifo_size);
|
||||
|
||||
/* We need at least one slot to succeed */
|
||||
|
@ -2893,8 +3001,9 @@ int dw_mci_probe(struct dw_mci *host)
|
|||
if (init_slots) {
|
||||
dev_info(host->dev, "%d slots initialized\n", init_slots);
|
||||
} else {
|
||||
dev_dbg(host->dev, "attempted to initialize %d slots, "
|
||||
"but failed on all\n", host->num_slots);
|
||||
dev_dbg(host->dev,
|
||||
"attempted to initialize %d slots, but failed on all\n",
|
||||
host->num_slots);
|
||||
goto err_dmaunmap;
|
||||
}
|
||||
|
||||
|
@ -2992,6 +3101,7 @@ int dw_mci_resume(struct dw_mci *host)
|
|||
|
||||
for (i = 0; i < host->num_slots; i++) {
|
||||
struct dw_mci_slot *slot = host->slot[i];
|
||||
|
||||
if (!slot)
|
||||
continue;
|
||||
if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
|
||||
|
|
|
@ -948,6 +948,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
|
|||
{
|
||||
struct mmc_data *data = req->data;
|
||||
int i, use_dma = 1, block_size;
|
||||
struct scatterlist *sg;
|
||||
unsigned sg_len;
|
||||
|
||||
host->data = data;
|
||||
|
@ -972,8 +973,8 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
|
|||
sg_len = (data->blocks == 1) ? 1 : data->sg_len;
|
||||
|
||||
/* Only do DMA for entire blocks */
|
||||
for (i = 0; i < sg_len; i++) {
|
||||
if ((data->sg[i].length % block_size) != 0) {
|
||||
for_each_sg(data->sg, sg, sg_len, i) {
|
||||
if ((sg->length % block_size) != 0) {
|
||||
use_dma = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -1419,8 +1420,10 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
|||
host->reg_shift = (mmc_omap7xx() ? 1 : 2);
|
||||
|
||||
host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
|
||||
if (!host->mmc_omap_wq)
|
||||
if (!host->mmc_omap_wq) {
|
||||
ret = -ENOMEM;
|
||||
goto err_plat_cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->nr_slots; i++) {
|
||||
ret = mmc_omap_new_slot(host, i);
|
||||
|
|
|
@ -181,18 +181,9 @@ struct omap_hsmmc_host {
|
|||
struct mmc_data *data;
|
||||
struct clk *fclk;
|
||||
struct clk *dbclk;
|
||||
/*
|
||||
* vcc == configured supply
|
||||
* vcc_aux == optional
|
||||
* - MMC1, supply for DAT4..DAT7
|
||||
* - MMC2/MMC2, external level shifter voltage supply, for
|
||||
* chip (SDIO, eMMC, etc) or transceiver (MMC2 only)
|
||||
*/
|
||||
struct regulator *vcc;
|
||||
struct regulator *vcc_aux;
|
||||
struct regulator *pbias;
|
||||
bool pbias_enabled;
|
||||
void __iomem *base;
|
||||
int vqmmc_enabled;
|
||||
resource_size_t mapbase;
|
||||
spinlock_t irq_lock; /* Prevent races with irq handler */
|
||||
unsigned int dma_len;
|
||||
|
@ -213,7 +204,6 @@ struct omap_hsmmc_host {
|
|||
int context_loss;
|
||||
int protect_card;
|
||||
int reqs_blocked;
|
||||
int use_reg;
|
||||
int req_in_progress;
|
||||
unsigned long clk_rate;
|
||||
unsigned int flags;
|
||||
|
@ -254,32 +244,133 @@ static int omap_hsmmc_get_cover_state(struct device *dev)
|
|||
return mmc_gpio_get_cd(host->mmc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_REGULATOR
|
||||
static int omap_hsmmc_enable_supply(struct mmc_host *mmc)
|
||||
{
|
||||
int ret;
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
struct mmc_ios *ios = &mmc->ios;
|
||||
|
||||
if (mmc->supply.vmmc) {
|
||||
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable interface voltage rail, if needed */
|
||||
if (mmc->supply.vqmmc && !host->vqmmc_enabled) {
|
||||
ret = regulator_enable(mmc->supply.vqmmc);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n");
|
||||
goto err_vqmmc;
|
||||
}
|
||||
host->vqmmc_enabled = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_vqmmc:
|
||||
if (mmc->supply.vmmc)
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_disable_supply(struct mmc_host *mmc)
|
||||
{
|
||||
int ret;
|
||||
int status;
|
||||
struct omap_hsmmc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (mmc->supply.vqmmc && host->vqmmc_enabled) {
|
||||
ret = regulator_disable(mmc->supply.vqmmc);
|
||||
if (ret) {
|
||||
dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n");
|
||||
return ret;
|
||||
}
|
||||
host->vqmmc_enabled = 0;
|
||||
}
|
||||
|
||||
if (mmc->supply.vmmc) {
|
||||
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
if (ret)
|
||||
goto err_set_ocr;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_set_ocr:
|
||||
if (mmc->supply.vqmmc) {
|
||||
status = regulator_enable(mmc->supply.vqmmc);
|
||||
if (status)
|
||||
dev_err(mmc_dev(mmc), "vmmc_aux re-enable failed\n");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on,
|
||||
int vdd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!host->pbias)
|
||||
return 0;
|
||||
|
||||
if (power_on) {
|
||||
if (vdd <= VDD_165_195)
|
||||
ret = regulator_set_voltage(host->pbias, VDD_1V8,
|
||||
VDD_1V8);
|
||||
else
|
||||
ret = regulator_set_voltage(host->pbias, VDD_3V0,
|
||||
VDD_3V0);
|
||||
if (ret < 0) {
|
||||
dev_err(host->dev, "pbias set voltage fail\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!regulator_is_enabled(host->pbias)) {
|
||||
ret = regulator_enable(host->pbias);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "pbias reg enable fail\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (regulator_is_enabled(host->pbias)) {
|
||||
ret = regulator_disable(host->pbias);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "pbias reg disable fail\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
|
||||
{
|
||||
struct omap_hsmmc_host *host =
|
||||
platform_get_drvdata(to_platform_device(dev));
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret = 0;
|
||||
|
||||
if (mmc_pdata(host)->set_power)
|
||||
return mmc_pdata(host)->set_power(dev, power_on, vdd);
|
||||
|
||||
/*
|
||||
* If we don't see a Vcc regulator, assume it's a fixed
|
||||
* voltage always-on regulator.
|
||||
*/
|
||||
if (!host->vcc)
|
||||
if (!mmc->supply.vmmc)
|
||||
return 0;
|
||||
|
||||
if (mmc_pdata(host)->before_set_reg)
|
||||
mmc_pdata(host)->before_set_reg(dev, power_on, vdd);
|
||||
|
||||
if (host->pbias) {
|
||||
if (host->pbias_enabled == 1) {
|
||||
ret = regulator_disable(host->pbias);
|
||||
if (!ret)
|
||||
host->pbias_enabled = 0;
|
||||
}
|
||||
regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0);
|
||||
}
|
||||
ret = omap_hsmmc_set_pbias(host, false, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Assume Vcc regulator is used only to power the card ... OMAP
|
||||
|
@ -295,129 +386,138 @@ static int omap_hsmmc_set_power(struct device *dev, int power_on, int vdd)
|
|||
* chips/cards need an interface voltage rail too.
|
||||
*/
|
||||
if (power_on) {
|
||||
if (host->vcc)
|
||||
ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
|
||||
/* Enable interface voltage rail, if needed */
|
||||
if (ret == 0 && host->vcc_aux) {
|
||||
ret = regulator_enable(host->vcc_aux);
|
||||
if (ret < 0 && host->vcc)
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
ret = omap_hsmmc_enable_supply(mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = omap_hsmmc_set_pbias(host, true, vdd);
|
||||
if (ret)
|
||||
goto err_set_voltage;
|
||||
} else {
|
||||
/* Shut down the rail */
|
||||
if (host->vcc_aux)
|
||||
ret = regulator_disable(host->vcc_aux);
|
||||
if (host->vcc) {
|
||||
/* Then proceed to shut down the local regulator */
|
||||
ret = mmc_regulator_set_ocr(host->mmc,
|
||||
host->vcc, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->pbias) {
|
||||
if (vdd <= VDD_165_195)
|
||||
ret = regulator_set_voltage(host->pbias, VDD_1V8,
|
||||
VDD_1V8);
|
||||
else
|
||||
ret = regulator_set_voltage(host->pbias, VDD_3V0,
|
||||
VDD_3V0);
|
||||
if (ret < 0)
|
||||
goto error_set_power;
|
||||
|
||||
if (host->pbias_enabled == 0) {
|
||||
ret = regulator_enable(host->pbias);
|
||||
if (!ret)
|
||||
host->pbias_enabled = 1;
|
||||
}
|
||||
ret = omap_hsmmc_disable_supply(mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mmc_pdata(host)->after_set_reg)
|
||||
mmc_pdata(host)->after_set_reg(dev, power_on, vdd);
|
||||
|
||||
error_set_power:
|
||||
return 0;
|
||||
|
||||
err_set_voltage:
|
||||
omap_hsmmc_disable_supply(mmc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_disable_boot_regulator(struct regulator *reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!reg)
|
||||
return 0;
|
||||
|
||||
if (regulator_is_enabled(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_disable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_disable_boot_regulators(struct omap_hsmmc_host *host)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* disable regulators enabled during boot and get the usecount
|
||||
* right so that regulators can be enabled/disabled by checking
|
||||
* the return value of regulator_is_enabled
|
||||
*/
|
||||
ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vmmc);
|
||||
if (ret) {
|
||||
dev_err(host->dev, "fail to disable boot enabled vmmc reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = omap_hsmmc_disable_boot_regulator(mmc->supply.vqmmc);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"fail to disable boot enabled vmmc_aux reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = omap_hsmmc_disable_boot_regulator(host->pbias);
|
||||
if (ret) {
|
||||
dev_err(host->dev,
|
||||
"failed to disable boot enabled pbias reg\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
{
|
||||
struct regulator *reg;
|
||||
int ocr_value = 0;
|
||||
int ret;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
reg = devm_regulator_get(host->dev, "vmmc");
|
||||
if (IS_ERR(reg)) {
|
||||
dev_err(host->dev, "unable to get vmmc regulator %ld\n",
|
||||
PTR_ERR(reg));
|
||||
return PTR_ERR(reg);
|
||||
if (mmc_pdata(host)->set_power)
|
||||
return 0;
|
||||
|
||||
mmc->supply.vmmc = devm_regulator_get_optional(host->dev, "vmmc");
|
||||
if (IS_ERR(mmc->supply.vmmc)) {
|
||||
ret = PTR_ERR(mmc->supply.vmmc);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
dev_dbg(host->dev, "unable to get vmmc regulator %ld\n",
|
||||
PTR_ERR(mmc->supply.vmmc));
|
||||
mmc->supply.vmmc = NULL;
|
||||
} else {
|
||||
host->vcc = reg;
|
||||
ocr_value = mmc_regulator_get_ocrmask(reg);
|
||||
if (!mmc_pdata(host)->ocr_mask) {
|
||||
ocr_value = mmc_regulator_get_ocrmask(mmc->supply.vmmc);
|
||||
if (ocr_value > 0)
|
||||
mmc_pdata(host)->ocr_mask = ocr_value;
|
||||
} else {
|
||||
if (!(mmc_pdata(host)->ocr_mask & ocr_value)) {
|
||||
dev_err(host->dev, "ocrmask %x is not supported\n",
|
||||
mmc_pdata(host)->ocr_mask);
|
||||
mmc_pdata(host)->ocr_mask = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
mmc_pdata(host)->set_power = omap_hsmmc_set_power;
|
||||
|
||||
/* Allow an aux regulator */
|
||||
reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
|
||||
host->vcc_aux = IS_ERR(reg) ? NULL : reg;
|
||||
mmc->supply.vqmmc = devm_regulator_get_optional(host->dev, "vmmc_aux");
|
||||
if (IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = PTR_ERR(mmc->supply.vqmmc);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
dev_dbg(host->dev, "unable to get vmmc_aux regulator %ld\n",
|
||||
PTR_ERR(mmc->supply.vqmmc));
|
||||
mmc->supply.vqmmc = NULL;
|
||||
}
|
||||
|
||||
reg = devm_regulator_get_optional(host->dev, "pbias");
|
||||
host->pbias = IS_ERR(reg) ? NULL : reg;
|
||||
host->pbias = devm_regulator_get_optional(host->dev, "pbias");
|
||||
if (IS_ERR(host->pbias)) {
|
||||
ret = PTR_ERR(host->pbias);
|
||||
if (ret != -ENODEV)
|
||||
return ret;
|
||||
dev_dbg(host->dev, "unable to get pbias regulator %ld\n",
|
||||
PTR_ERR(host->pbias));
|
||||
host->pbias = NULL;
|
||||
}
|
||||
|
||||
/* For eMMC do not power off when not in sleep state */
|
||||
if (mmc_pdata(host)->no_regulator_off_init)
|
||||
return 0;
|
||||
/*
|
||||
* To disable boot_on regulator, enable regulator
|
||||
* to increase usecount and then disable it.
|
||||
*/
|
||||
if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
|
||||
(host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
|
||||
int vdd = ffs(mmc_pdata(host)->ocr_mask) - 1;
|
||||
|
||||
mmc_pdata(host)->set_power(host->dev, 1, vdd);
|
||||
mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||
}
|
||||
ret = omap_hsmmc_disable_boot_regulators(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
|
||||
{
|
||||
mmc_pdata(host)->set_power = NULL;
|
||||
}
|
||||
|
||||
static inline int omap_hsmmc_have_reg(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int omap_hsmmc_have_reg(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static irqreturn_t omap_hsmmc_cover_irq(int irq, void *dev_id);
|
||||
|
||||
static int omap_hsmmc_gpio_init(struct mmc_host *mmc,
|
||||
|
@ -1149,11 +1249,11 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
|||
clk_disable_unprepare(host->dbclk);
|
||||
|
||||
/* Turn the power off */
|
||||
ret = mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||
ret = omap_hsmmc_set_power(host->dev, 0, 0);
|
||||
|
||||
/* Turn the power ON with given VDD 1.8 or 3.0v */
|
||||
if (!ret)
|
||||
ret = mmc_pdata(host)->set_power(host->dev, 1, vdd);
|
||||
ret = omap_hsmmc_set_power(host->dev, 1, vdd);
|
||||
pm_runtime_get_sync(host->dev);
|
||||
if (host->dbclk)
|
||||
clk_prepare_enable(host->dbclk);
|
||||
|
@ -1552,10 +1652,10 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
if (ios->power_mode != host->power_mode) {
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
mmc_pdata(host)->set_power(host->dev, 0, 0);
|
||||
omap_hsmmc_set_power(host->dev, 0, 0);
|
||||
break;
|
||||
case MMC_POWER_UP:
|
||||
mmc_pdata(host)->set_power(host->dev, 1, ios->vdd);
|
||||
omap_hsmmc_set_power(host->dev, 1, ios->vdd);
|
||||
break;
|
||||
case MMC_POWER_ON:
|
||||
do_send_init_stream = 1;
|
||||
|
@ -1953,7 +2053,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
host->base = base + pdata->reg_offset;
|
||||
host->power_mode = MMC_POWER_OFF;
|
||||
host->next_data.cookie = 1;
|
||||
host->pbias_enabled = 0;
|
||||
host->vqmmc_enabled = 0;
|
||||
|
||||
ret = omap_hsmmc_gpio_init(mmc, host, pdata);
|
||||
if (ret)
|
||||
|
@ -2078,12 +2178,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
goto err_irq;
|
||||
}
|
||||
|
||||
if (omap_hsmmc_have_reg() && !mmc_pdata(host)->set_power) {
|
||||
ret = omap_hsmmc_reg_get(host);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
host->use_reg = 1;
|
||||
}
|
||||
ret = omap_hsmmc_reg_get(host);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
mmc->ocr_avail = mmc_pdata(host)->ocr_mask;
|
||||
|
||||
|
@ -2125,8 +2222,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
|
||||
err_slot_name:
|
||||
mmc_remove_host(mmc);
|
||||
if (host->use_reg)
|
||||
omap_hsmmc_reg_put(host);
|
||||
err_irq:
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
if (host->tx_chan)
|
||||
|
@ -2150,8 +2245,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
|||
|
||||
pm_runtime_get_sync(host->dev);
|
||||
mmc_remove_host(host->mmc);
|
||||
if (host->use_reg)
|
||||
omap_hsmmc_reg_put(host);
|
||||
|
||||
if (host->tx_chan)
|
||||
dma_release_channel(host->tx_chan);
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma/pxa-dma.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mmc/host.h>
|
||||
|
@ -37,7 +39,6 @@
|
|||
#include <asm/sizes.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/dma.h>
|
||||
#include <linux/platform_data/mmc-pxamci.h>
|
||||
|
||||
#include "pxamci.h"
|
||||
|
@ -58,7 +59,6 @@ struct pxamci_host {
|
|||
struct clk *clk;
|
||||
unsigned long clkrate;
|
||||
int irq;
|
||||
int dma;
|
||||
unsigned int clkrt;
|
||||
unsigned int cmdat;
|
||||
unsigned int imask;
|
||||
|
@ -69,8 +69,10 @@ struct pxamci_host {
|
|||
struct mmc_command *cmd;
|
||||
struct mmc_data *data;
|
||||
|
||||
struct dma_chan *dma_chan_rx;
|
||||
struct dma_chan *dma_chan_tx;
|
||||
dma_cookie_t dma_cookie;
|
||||
dma_addr_t sg_dma;
|
||||
struct pxa_dma_desc *sg_cpu;
|
||||
unsigned int dma_len;
|
||||
|
||||
unsigned int dma_dir;
|
||||
|
@ -173,14 +175,18 @@ static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
|
|||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static void pxamci_dma_irq(void *param);
|
||||
|
||||
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
|
||||
{
|
||||
struct dma_async_tx_descriptor *tx;
|
||||
enum dma_data_direction direction;
|
||||
struct dma_slave_config config;
|
||||
struct dma_chan *chan;
|
||||
unsigned int nob = data->blocks;
|
||||
unsigned long long clks;
|
||||
unsigned int timeout;
|
||||
bool dalgn = 0;
|
||||
u32 dcmd;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
host->data = data;
|
||||
|
||||
|
@ -195,54 +201,48 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
|
|||
timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt);
|
||||
writel((timeout + 255) / 256, host->base + MMC_RDTO);
|
||||
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
config.src_addr = host->res->start + MMC_RXFIFO;
|
||||
config.dst_addr = host->res->start + MMC_TXFIFO;
|
||||
config.src_maxburst = 32;
|
||||
config.dst_maxburst = 32;
|
||||
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
host->dma_dir = DMA_FROM_DEVICE;
|
||||
dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC;
|
||||
DRCMR(host->dma_drcmrtx) = 0;
|
||||
DRCMR(host->dma_drcmrrx) = host->dma | DRCMR_MAPVLD;
|
||||
direction = DMA_DEV_TO_MEM;
|
||||
chan = host->dma_chan_rx;
|
||||
} else {
|
||||
host->dma_dir = DMA_TO_DEVICE;
|
||||
dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
|
||||
DRCMR(host->dma_drcmrrx) = 0;
|
||||
DRCMR(host->dma_drcmrtx) = host->dma | DRCMR_MAPVLD;
|
||||
direction = DMA_MEM_TO_DEV;
|
||||
chan = host->dma_chan_tx;
|
||||
}
|
||||
|
||||
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
|
||||
config.direction = direction;
|
||||
|
||||
host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
ret = dmaengine_slave_config(chan, &config);
|
||||
if (ret < 0) {
|
||||
dev_err(mmc_dev(host->mmc), "dma slave config failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
host->dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
|
||||
host->dma_dir);
|
||||
|
||||
for (i = 0; i < host->dma_len; i++) {
|
||||
unsigned int length = sg_dma_len(&data->sg[i]);
|
||||
host->sg_cpu[i].dcmd = dcmd | length;
|
||||
if (length & 31 && !(data->flags & MMC_DATA_READ))
|
||||
host->sg_cpu[i].dcmd |= DCMD_ENDIRQEN;
|
||||
/* Not aligned to 8-byte boundary? */
|
||||
if (sg_dma_address(&data->sg[i]) & 0x7)
|
||||
dalgn = 1;
|
||||
if (data->flags & MMC_DATA_READ) {
|
||||
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
|
||||
host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
|
||||
} else {
|
||||
host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
|
||||
host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
|
||||
}
|
||||
host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
|
||||
sizeof(struct pxa_dma_desc);
|
||||
tx = dmaengine_prep_slave_sg(chan, data->sg, host->dma_len, direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
if (!tx) {
|
||||
dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
|
||||
return;
|
||||
}
|
||||
host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
|
||||
wmb();
|
||||
|
||||
/*
|
||||
* The PXA27x DMA controller encounters overhead when working with
|
||||
* unaligned (to 8-byte boundaries) data, so switch on byte alignment
|
||||
* mode only if we have unaligned data.
|
||||
*/
|
||||
if (dalgn)
|
||||
DALGN |= (1 << host->dma);
|
||||
else
|
||||
DALGN &= ~(1 << host->dma);
|
||||
DDADR(host->dma) = host->sg_dma;
|
||||
if (!(data->flags & MMC_DATA_READ)) {
|
||||
tx->callback = pxamci_dma_irq;
|
||||
tx->callback_param = host;
|
||||
}
|
||||
|
||||
host->dma_cookie = dmaengine_submit(tx);
|
||||
|
||||
/*
|
||||
* workaround for erratum #91:
|
||||
|
@ -251,7 +251,7 @@ static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
|
|||
* before starting DMA.
|
||||
*/
|
||||
if (!cpu_is_pxa27x() || data->flags & MMC_DATA_READ)
|
||||
DCSR(host->dma) = DCSR_RUN;
|
||||
dma_async_issue_pending(chan);
|
||||
}
|
||||
|
||||
static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat)
|
||||
|
@ -343,7 +343,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
|
|||
* enable DMA late
|
||||
*/
|
||||
if (cpu_is_pxa27x() && host->data->flags & MMC_DATA_WRITE)
|
||||
DCSR(host->dma) = DCSR_RUN;
|
||||
dma_async_issue_pending(host->dma_chan_tx);
|
||||
} else {
|
||||
pxamci_finish_request(host, host->mrq);
|
||||
}
|
||||
|
@ -354,13 +354,17 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
|
|||
static int pxamci_data_done(struct pxamci_host *host, unsigned int stat)
|
||||
{
|
||||
struct mmc_data *data = host->data;
|
||||
struct dma_chan *chan;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
DCSR(host->dma) = 0;
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
host->dma_dir);
|
||||
if (data->flags & MMC_DATA_READ)
|
||||
chan = host->dma_chan_rx;
|
||||
else
|
||||
chan = host->dma_chan_tx;
|
||||
dma_unmap_sg(chan->device->dev,
|
||||
data->sg, data->sg_len, host->dma_dir);
|
||||
|
||||
if (stat & STAT_READ_TIME_OUT)
|
||||
data->error = -ETIMEDOUT;
|
||||
|
@ -552,20 +556,37 @@ static const struct mmc_host_ops pxamci_ops = {
|
|||
.enable_sdio_irq = pxamci_enable_sdio_irq,
|
||||
};
|
||||
|
||||
static void pxamci_dma_irq(int dma, void *devid)
|
||||
static void pxamci_dma_irq(void *param)
|
||||
{
|
||||
struct pxamci_host *host = devid;
|
||||
int dcsr = DCSR(dma);
|
||||
DCSR(dma) = dcsr & ~DCSR_STOPIRQEN;
|
||||
struct pxamci_host *host = param;
|
||||
struct dma_tx_state state;
|
||||
enum dma_status status;
|
||||
struct dma_chan *chan;
|
||||
unsigned long flags;
|
||||
|
||||
if (dcsr & DCSR_ENDINTR) {
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (!host->data)
|
||||
goto out_unlock;
|
||||
|
||||
if (host->data->flags & MMC_DATA_READ)
|
||||
chan = host->dma_chan_rx;
|
||||
else
|
||||
chan = host->dma_chan_tx;
|
||||
|
||||
status = dmaengine_tx_status(chan, host->dma_cookie, &state);
|
||||
|
||||
if (likely(status == DMA_COMPLETE)) {
|
||||
writel(BUF_PART_FULL, host->base + MMC_PRTBUF);
|
||||
} else {
|
||||
pr_err("%s: DMA error on channel %d (DCSR=%#x)\n",
|
||||
mmc_hostname(host->mmc), dma, dcsr);
|
||||
pr_err("%s: DMA error on %s channel\n", mmc_hostname(host->mmc),
|
||||
host->data->flags & MMC_DATA_READ ? "rx" : "tx");
|
||||
host->data->error = -EIO;
|
||||
pxamci_data_done(host, 0);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t pxamci_detect_irq(int irq, void *devid)
|
||||
|
@ -625,7 +646,9 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
struct mmc_host *mmc;
|
||||
struct pxamci_host *host = NULL;
|
||||
struct resource *r, *dmarx, *dmatx;
|
||||
struct pxad_param param_rx, param_tx;
|
||||
int ret, irq, gpio_cd = -1, gpio_ro = -1, gpio_power = -1;
|
||||
dma_cap_mask_t mask;
|
||||
|
||||
ret = pxamci_of_init(pdev);
|
||||
if (ret)
|
||||
|
@ -671,7 +694,6 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->dma = -1;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
host->clkrt = CLKRT_OFF;
|
||||
|
||||
|
@ -702,12 +724,6 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
MMC_CAP_SD_HIGHSPEED;
|
||||
}
|
||||
|
||||
host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL);
|
||||
if (!host->sg_cpu) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
host->res = r;
|
||||
host->irq = irq;
|
||||
|
@ -728,32 +744,45 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
writel(64, host->base + MMC_RESTO);
|
||||
writel(host->imask, host->base + MMC_I_MASK);
|
||||
|
||||
host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
|
||||
pxamci_dma_irq, host);
|
||||
if (host->dma < 0) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (!dmarx) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
if (!pdev->dev.of_node) {
|
||||
dmarx = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!dmarx || !dmatx) {
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
param_rx.prio = PXAD_PRIO_LOWEST;
|
||||
param_rx.drcmr = dmarx->start;
|
||||
param_tx.prio = PXAD_PRIO_LOWEST;
|
||||
param_tx.drcmr = dmatx->start;
|
||||
}
|
||||
host->dma_drcmrrx = dmarx->start;
|
||||
|
||||
dmatx = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
||||
if (!dmatx) {
|
||||
ret = -ENXIO;
|
||||
dma_cap_zero(mask);
|
||||
dma_cap_set(DMA_SLAVE, mask);
|
||||
|
||||
host->dma_chan_rx =
|
||||
dma_request_slave_channel_compat(mask, pxad_filter_fn,
|
||||
¶m_rx, &pdev->dev, "rx");
|
||||
if (host->dma_chan_rx == NULL) {
|
||||
dev_err(&pdev->dev, "unable to request rx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
host->dma_chan_tx =
|
||||
dma_request_slave_channel_compat(mask, pxad_filter_fn,
|
||||
¶m_tx, &pdev->dev, "tx");
|
||||
if (host->dma_chan_tx == NULL) {
|
||||
dev_err(&pdev->dev, "unable to request tx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
host->dma_drcmrtx = dmatx->start;
|
||||
|
||||
if (host->pdata) {
|
||||
gpio_cd = host->pdata->gpio_card_detect;
|
||||
|
@ -814,12 +843,12 @@ err_gpio_ro:
|
|||
gpio_free(gpio_power);
|
||||
out:
|
||||
if (host) {
|
||||
if (host->dma >= 0)
|
||||
pxa_free_dma(host->dma);
|
||||
if (host->dma_chan_rx)
|
||||
dma_release_channel(host->dma_chan_rx);
|
||||
if (host->dma_chan_tx)
|
||||
dma_release_channel(host->dma_chan_tx);
|
||||
if (host->base)
|
||||
iounmap(host->base);
|
||||
if (host->sg_cpu)
|
||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
if (host->clk)
|
||||
clk_put(host->clk);
|
||||
}
|
||||
|
@ -863,13 +892,12 @@ static int pxamci_remove(struct platform_device *pdev)
|
|||
END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
|
||||
host->base + MMC_I_MASK);
|
||||
|
||||
DRCMR(host->dma_drcmrrx) = 0;
|
||||
DRCMR(host->dma_drcmrtx) = 0;
|
||||
|
||||
free_irq(host->irq, host);
|
||||
pxa_free_dma(host->dma);
|
||||
dmaengine_terminate_all(host->dma_chan_rx);
|
||||
dmaengine_terminate_all(host->dma_chan_tx);
|
||||
dma_release_channel(host->dma_chan_rx);
|
||||
dma_release_channel(host->dma_chan_tx);
|
||||
iounmap(host->base);
|
||||
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
|
||||
|
||||
clk_put(host->clk);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include "sdhci-esdhc.h"
|
||||
|
||||
#define ESDHC_CTRL_D3CD 0x08
|
||||
#define ESDHC_BURST_LEN_EN_INCR (1 << 27)
|
||||
/* VENDOR SPEC register */
|
||||
#define ESDHC_VENDOR_SPEC 0xc0
|
||||
#define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
|
||||
|
@ -44,6 +45,7 @@
|
|||
#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
|
||||
#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
|
||||
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
|
||||
#define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
|
||||
/* Bits 3 and 6 are not SDHCI standard definitions */
|
||||
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
|
||||
/* Tuning bits */
|
||||
|
@ -60,10 +62,21 @@
|
|||
#define ESDHC_TUNE_CTRL_MIN 0
|
||||
#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
|
||||
|
||||
/* strobe dll register */
|
||||
#define ESDHC_STROBE_DLL_CTRL 0x70
|
||||
#define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
|
||||
#define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
|
||||
#define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
|
||||
|
||||
#define ESDHC_STROBE_DLL_STATUS 0x74
|
||||
#define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
|
||||
#define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
|
||||
|
||||
#define ESDHC_TUNING_CTRL 0xcc
|
||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||
#define ESDHC_TUNING_START_TAP 0x1
|
||||
#define ESDHC_TUNING_STEP_SHIFT 16
|
||||
|
||||
/* pinctrl state */
|
||||
#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
|
||||
|
@ -120,6 +133,11 @@
|
|||
#define ESDHC_FLAG_ERR004536 BIT(7)
|
||||
/* The IP supports HS200 mode */
|
||||
#define ESDHC_FLAG_HS200 BIT(8)
|
||||
/* The IP supports HS400 mode */
|
||||
#define ESDHC_FLAG_HS400 BIT(9)
|
||||
|
||||
/* A higher clock ferquency than this rate requires strobell dll control */
|
||||
#define ESDHC_STROBE_DLL_CLK_FREQ 100000000
|
||||
|
||||
struct esdhc_soc_data {
|
||||
u32 flags;
|
||||
|
@ -156,6 +174,12 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
|
|||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
|
||||
};
|
||||
|
||||
static struct esdhc_soc_data usdhc_imx7d_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400,
|
||||
};
|
||||
|
||||
struct pltfm_imx_data {
|
||||
u32 scratchpad;
|
||||
struct pinctrl *pinctrl;
|
||||
|
@ -199,6 +223,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = {
|
|||
{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, },
|
||||
{ .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, },
|
||||
{ .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, },
|
||||
{ .compatible = "fsl,imx7d-usdhc", .data = &usdhc_imx7d_data, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids);
|
||||
|
@ -274,6 +299,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
|||
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
||||
| SDHCI_SUPPORT_SDR50
|
||||
| SDHCI_USE_SDR50_TUNING;
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||
val |= SDHCI_SUPPORT_HS400;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -448,6 +476,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
|
||||
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
|
||||
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
u32 tuning_ctrl;
|
||||
if (val & SDHCI_CTRL_TUNED_CLK) {
|
||||
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
|
||||
} else {
|
||||
|
@ -458,6 +487,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
|
|||
if (val & SDHCI_CTRL_EXEC_TUNING) {
|
||||
v |= ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||
tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
tuning_ctrl |= ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP;
|
||||
if (imx_data->boarddata.tuning_step)
|
||||
tuning_ctrl |= imx_data->boarddata.tuning_step << ESDHC_TUNING_STEP_SHIFT;
|
||||
writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
} else {
|
||||
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
|
||||
}
|
||||
|
@ -774,6 +808,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
pinctrl = imx_data->pins_200mhz;
|
||||
break;
|
||||
default:
|
||||
|
@ -784,24 +819,68 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
|
||||
}
|
||||
|
||||
/*
|
||||
* For HS400 eMMC, there is a data_strobe line, this signal is generated
|
||||
* by the device and used for data output and CRC status response output
|
||||
* in HS400 mode. The frequency of this signal follows the frequency of
|
||||
* CLK generated by host. Host receive the data which is aligned to the
|
||||
* edge of data_strobe line. Due to the time delay between CLK line and
|
||||
* data_strobe line, if the delay time is larger than one clock cycle,
|
||||
* then CLK and data_strobe line will misaligned, read error shows up.
|
||||
* So when the CLK is higher than 100MHz, each clock cycle is short enough,
|
||||
* host should config the delay target.
|
||||
*/
|
||||
static void esdhc_set_strobe_dll(struct sdhci_host *host)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
if (host->mmc->actual_clock > ESDHC_STROBE_DLL_CLK_FREQ) {
|
||||
/* force a reset on strobe dll */
|
||||
writel(ESDHC_STROBE_DLL_CTRL_RESET,
|
||||
host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/*
|
||||
* enable strobe dll ctrl and adjust the delay target
|
||||
* for the uSDHC loopback read clock
|
||||
*/
|
||||
v = ESDHC_STROBE_DLL_CTRL_ENABLE |
|
||||
(7 << ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
|
||||
writel(v, host->ioaddr + ESDHC_STROBE_DLL_CTRL);
|
||||
/* wait 1us to make sure strobe dll status register stable */
|
||||
udelay(1);
|
||||
v = readl(host->ioaddr + ESDHC_STROBE_DLL_STATUS);
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_REF_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status REF not lock!\n");
|
||||
if (!(v & ESDHC_STROBE_DLL_STS_SLV_LOCK))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"warning! HS400 strobe DLL status SLV not lock!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
||||
{
|
||||
u32 m;
|
||||
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;
|
||||
|
||||
/* disable ddr mode and disable HS400 mode */
|
||||
m = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||
m &= ~(ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN);
|
||||
imx_data->is_ddr = 0;
|
||||
|
||||
switch (timing) {
|
||||
case MMC_TIMING_UHS_SDR12:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
writel(readl(host->ioaddr + ESDHC_MIX_CTRL) |
|
||||
ESDHC_MIX_CTRL_DDREN,
|
||||
host->ioaddr + ESDHC_MIX_CTRL);
|
||||
m |= ESDHC_MIX_CTRL_DDREN;
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
imx_data->is_ddr = 1;
|
||||
if (boarddata->delay_line) {
|
||||
u32 v;
|
||||
|
@ -813,6 +892,12 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
|
|||
writel(v, host->ioaddr + ESDHC_DLL_CTRL);
|
||||
}
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
m |= ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN;
|
||||
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
|
||||
imx_data->is_ddr = 1;
|
||||
esdhc_set_strobe_dll(host);
|
||||
break;
|
||||
}
|
||||
|
||||
esdhc_change_pinstate(host, timing);
|
||||
|
@ -886,6 +971,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||
if (gpio_is_valid(boarddata->wp_gpio))
|
||||
boarddata->wp_type = ESDHC_WP_GPIO;
|
||||
|
||||
of_property_read_u32(np, "fsl,tuning-step", &boarddata->tuning_step);
|
||||
|
||||
if (of_find_property(np, "no-1-8-v", NULL))
|
||||
boarddata->support_vsel = false;
|
||||
else
|
||||
|
@ -1073,10 +1160,26 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
* to something insane. Change it back here.
|
||||
*/
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
|
||||
writel(0x10401040, host->ioaddr + ESDHC_WTMK_LVL);
|
||||
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR;
|
||||
|
||||
/*
|
||||
* ROM code will change the bit burst_length_enable setting
|
||||
* to zero if this usdhc is choosed to boot system. Change
|
||||
* it back here, otherwise it will impact the performance a
|
||||
* lot. This bit is used to enable/disable the burst length
|
||||
* for the external AHB2AXI bridge, it's usefully especially
|
||||
* for INCR transfer because without burst length indicator,
|
||||
* the AHB2AXI bridge does not know the burst length in
|
||||
* advance. And without burst length indicator, AHB INCR
|
||||
* transfer can only be converted to singles on the AXI side.
|
||||
*/
|
||||
writel(readl(host->ioaddr + SDHCI_HOST_CONTROL)
|
||||
| ESDHC_BURST_LEN_EN_INCR,
|
||||
host->ioaddr + SDHCI_HOST_CONTROL);
|
||||
|
||||
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||
|
||||
|
@ -1100,6 +1203,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_ADMA;
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||
host->quirks2 |= SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400;
|
||||
|
||||
if (of_id)
|
||||
err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
|
||||
else
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
#define ESDHC_DEFAULT_QUIRKS (SDHCI_QUIRK_FORCE_BLK_SZ_2048 | \
|
||||
SDHCI_QUIRK_NO_BUSY_IRQ | \
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | \
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY)
|
||||
SDHCI_QUIRK_PIO_NEEDS_DELAY | \
|
||||
SDHCI_QUIRK_NO_HISPD_BIT)
|
||||
|
||||
#define ESDHC_SYSTEM_CONTROL 0x2c
|
||||
#define ESDHC_CLOCK_MASK 0x0000fff0
|
||||
|
|
|
@ -489,6 +489,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
goto pclk_disable;
|
||||
}
|
||||
|
||||
/* Vote for maximum clock rate for maximum performance */
|
||||
ret = clk_set_rate(msm_host->clk, INT_MAX);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "core clock boost failed\n");
|
||||
|
||||
ret = clk_prepare_enable(msm_host->clk);
|
||||
if (ret)
|
||||
goto pclk_disable;
|
||||
|
|
|
@ -63,6 +63,9 @@ static struct sdhci_ops sdhci_arasan_ops = {
|
|||
|
||||
static struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -214,6 +217,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
|||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
{ .compatible = "arasan,sdhci-8.9a" },
|
||||
{ .compatible = "arasan,sdhci-5.1" },
|
||||
{ .compatible = "arasan,sdhci-4.9a" },
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Atmel SDMMC controller driver.
|
||||
*
|
||||
* Copyright (C) 2015 Atmel,
|
||||
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDMMC_CACR 0x230
|
||||
#define SDMMC_CACR_CAPWREN BIT(0)
|
||||
#define SDMMC_CACR_KEY (0x46 << 8)
|
||||
|
||||
struct sdhci_at91_priv {
|
||||
struct clk *hclock;
|
||||
struct clk *gck;
|
||||
struct clk *mainck;
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
|
||||
.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 soc_data_sama5d2 = {
|
||||
.ops = &sdhci_at91_sama5d2_ops,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_at91_dt_match[] = {
|
||||
{ .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 },
|
||||
{}
|
||||
};
|
||||
|
||||
static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct sdhci_pltfm_data *soc_data;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_at91_priv *priv;
|
||||
unsigned int caps0, caps1;
|
||||
unsigned int clk_base, clk_mul;
|
||||
unsigned int gck_rate, real_gck_rate;
|
||||
int ret;
|
||||
|
||||
match = of_match_device(sdhci_at91_dt_match, &pdev->dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate private data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
|
||||
if (IS_ERR(priv->mainck)) {
|
||||
dev_err(&pdev->dev, "failed to get baseclk\n");
|
||||
return PTR_ERR(priv->mainck);
|
||||
}
|
||||
|
||||
priv->hclock = devm_clk_get(&pdev->dev, "hclock");
|
||||
if (IS_ERR(priv->hclock)) {
|
||||
dev_err(&pdev->dev, "failed to get hclock\n");
|
||||
return PTR_ERR(priv->hclock);
|
||||
}
|
||||
|
||||
priv->gck = devm_clk_get(&pdev->dev, "multclk");
|
||||
if (IS_ERR(priv->gck)) {
|
||||
dev_err(&pdev->dev, "failed to get multclk\n");
|
||||
return PTR_ERR(priv->gck);
|
||||
}
|
||||
|
||||
host = sdhci_pltfm_init(pdev, soc_data, 0);
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
/*
|
||||
* The mult clock is provided by as a generated clock by the PMC
|
||||
* controller. In order to set the rate of gck, we have to get the
|
||||
* base clock rate and the clock mult from capabilities.
|
||||
*/
|
||||
clk_prepare_enable(priv->hclock);
|
||||
caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES);
|
||||
caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1);
|
||||
clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
|
||||
clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
|
||||
gck_rate = clk_base * 1000000 * (clk_mul + 1);
|
||||
ret = clk_set_rate(priv->gck, gck_rate);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set gck");
|
||||
goto hclock_disable_unprepare;
|
||||
return -EINVAL;
|
||||
}
|
||||
/*
|
||||
* We need to check if we have the requested rate for gck because in
|
||||
* some cases this rate could be not supported. If it happens, the rate
|
||||
* is the closest one gck can provide. We have to update the value
|
||||
* of clk mul.
|
||||
*/
|
||||
real_gck_rate = clk_get_rate(priv->gck);
|
||||
if (real_gck_rate != gck_rate) {
|
||||
clk_mul = real_gck_rate / (clk_base * 1000000) - 1;
|
||||
caps1 &= (~SDHCI_CLOCK_MUL_MASK);
|
||||
caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK);
|
||||
/* Set capabilities in r/w mode. */
|
||||
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
|
||||
writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1);
|
||||
/* Set capabilities in ro mode. */
|
||||
writel(0, host->ioaddr + SDMMC_CACR);
|
||||
dev_info(&pdev->dev, "update clk mul to %u as gck rate is %u Hz\n",
|
||||
clk_mul, real_gck_rate);
|
||||
}
|
||||
|
||||
clk_prepare_enable(priv->mainck);
|
||||
clk_prepare_enable(priv->gck);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->priv = priv;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto clocks_disable_unprepare;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto clocks_disable_unprepare;
|
||||
|
||||
return 0;
|
||||
|
||||
clocks_disable_unprepare:
|
||||
clk_disable_unprepare(priv->gck);
|
||||
clk_disable_unprepare(priv->mainck);
|
||||
hclock_disable_unprepare:
|
||||
clk_disable_unprepare(priv->hclock);
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sdhci_at91_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_at91_priv *priv = pltfm_host->priv;
|
||||
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(priv->gck);
|
||||
clk_disable_unprepare(priv->hclock);
|
||||
clk_disable_unprepare(priv->mainck);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_at91_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-at91",
|
||||
.of_match_table = sdhci_at91_dt_match,
|
||||
.pm = SDHCI_PLTFM_PMOPS,
|
||||
},
|
||||
.probe = sdhci_at91_probe,
|
||||
.remove = sdhci_at91_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_at91_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for at91");
|
||||
MODULE_AUTHOR("Ludovic Desroches <ludovic.desroches@atmel.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -208,6 +208,12 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
if (clock == 0)
|
||||
return;
|
||||
|
||||
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
|
||||
temp = esdhc_readw(host, SDHCI_HOST_VERSION);
|
||||
temp = (temp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT;
|
||||
if (temp < VENDOR_V_23)
|
||||
pre_div = 2;
|
||||
|
||||
/* Workaround to reduce the clock frequency for p1010 esdhc */
|
||||
if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) {
|
||||
if (clock > 20000000)
|
||||
|
|
|
@ -618,6 +618,7 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
|
|||
static const struct sdhci_pci_fixes sdhci_o2 = {
|
||||
.probe = sdhci_pci_o2_probe,
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks2 = SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD,
|
||||
.probe_slot = sdhci_pci_o2_probe_slot,
|
||||
.resume = sdhci_pci_o2_resume,
|
||||
};
|
||||
|
|
|
@ -161,8 +161,8 @@ static struct sdhci_pltfm_data sdhci_sirf_pdata = {
|
|||
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
|
||||
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
|
||||
SDHCI_QUIRK_DELAY_AFTER_POWER,
|
||||
SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static int sdhci_sirf_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -54,8 +54,7 @@ static void sdhci_finish_command(struct sdhci_host *);
|
|||
static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
|
||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
struct sdhci_host_next *next);
|
||||
struct mmc_data *data);
|
||||
static int sdhci_do_get_cd(struct sdhci_host *host);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
@ -207,8 +206,7 @@ EXPORT_SYMBOL_GPL(sdhci_reset);
|
|||
static void sdhci_do_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
|
||||
if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
|
||||
SDHCI_CARD_PRESENT))
|
||||
if (!sdhci_do_get_cd(host))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -496,7 +494,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,
|
|||
goto fail;
|
||||
BUG_ON(host->align_addr & host->align_mask);
|
||||
|
||||
host->sg_count = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
host->sg_count = sdhci_pre_dma_transfer(host, data);
|
||||
if (host->sg_count < 0)
|
||||
goto unmap_align;
|
||||
|
||||
|
@ -635,9 +633,11 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
|
|||
}
|
||||
}
|
||||
|
||||
if (!data->host_cookie)
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len, direction);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
|
@ -833,7 +833,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
} else {
|
||||
int sg_cnt;
|
||||
|
||||
sg_cnt = sdhci_pre_dma_transfer(host, data, NULL);
|
||||
sg_cnt = sdhci_pre_dma_transfer(host, data);
|
||||
if (sg_cnt <= 0) {
|
||||
/*
|
||||
* This only happens when someone fed
|
||||
|
@ -949,11 +949,13 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||
if (host->flags & SDHCI_USE_ADMA)
|
||||
sdhci_adma_table_post(host, data);
|
||||
else {
|
||||
if (!data->host_cookie)
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc),
|
||||
data->sg, data->sg_len,
|
||||
(data->flags & MMC_DATA_READ) ?
|
||||
DMA_FROM_DEVICE : DMA_TO_DEVICE);
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1132,6 +1134,7 @@ static u16 sdhci_get_preset_value(struct sdhci_host *host)
|
|||
preset = sdhci_readw(host, SDHCI_PRESET_FOR_SDR104);
|
||||
break;
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
preset = sdhci_readw(host, SDHCI_PRESET_FOR_DDR50);
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
|
@ -1152,6 +1155,7 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
int real_div = div, clk_mul = 1;
|
||||
u16 clk = 0;
|
||||
unsigned long timeout;
|
||||
bool switch_base_clk = false;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
|
@ -1189,15 +1193,25 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
<= clock)
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Set Programmable Clock Mode in the Clock
|
||||
* Control register.
|
||||
*/
|
||||
clk = SDHCI_PROG_CLOCK_MODE;
|
||||
real_div = div;
|
||||
clk_mul = host->clk_mul;
|
||||
div--;
|
||||
} else {
|
||||
if ((host->max_clk * host->clk_mul / div) <= clock) {
|
||||
/*
|
||||
* Set Programmable Clock Mode in the Clock
|
||||
* Control register.
|
||||
*/
|
||||
clk = SDHCI_PROG_CLOCK_MODE;
|
||||
real_div = div;
|
||||
clk_mul = host->clk_mul;
|
||||
div--;
|
||||
} else {
|
||||
/*
|
||||
* Divisor can be too small to reach clock
|
||||
* speed requirement. Then use the base clock.
|
||||
*/
|
||||
switch_base_clk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!host->clk_mul || switch_base_clk) {
|
||||
/* Version 3.00 divisors must be a multiple of 2. */
|
||||
if (host->max_clk <= clock)
|
||||
div = 1;
|
||||
|
@ -1210,6 +1224,9 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
}
|
||||
real_div = div;
|
||||
div >>= 1;
|
||||
if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN)
|
||||
&& !div && host->max_clk <= 25000000)
|
||||
div = 1;
|
||||
}
|
||||
} else {
|
||||
/* Version 2.00 divisors must be a power of 2. */
|
||||
|
@ -1559,7 +1576,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
|
|||
(ios->timing == MMC_TIMING_UHS_SDR25) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR50) ||
|
||||
(ios->timing == MMC_TIMING_UHS_SDR104) ||
|
||||
(ios->timing == MMC_TIMING_UHS_DDR50))) {
|
||||
(ios->timing == MMC_TIMING_UHS_DDR50) ||
|
||||
(ios->timing == MMC_TIMING_MMC_DDR52))) {
|
||||
u16 preset;
|
||||
|
||||
sdhci_enable_preset_value(host, true);
|
||||
|
@ -1601,15 +1619,21 @@ static int sdhci_do_get_cd(struct sdhci_host *host)
|
|||
if (host->flags & SDHCI_DEVICE_DEAD)
|
||||
return 0;
|
||||
|
||||
/* If polling/nonremovable, assume that the card is always present. */
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) ||
|
||||
(host->mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
/* If nonremovable, assume that the card is always present. */
|
||||
if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
||||
return 1;
|
||||
|
||||
/* Try slot gpio detect */
|
||||
/*
|
||||
* Try slot gpio detect, if defined it take precedence
|
||||
* over build in controller functionality
|
||||
*/
|
||||
if (!IS_ERR_VALUE(gpio_cd))
|
||||
return !!gpio_cd;
|
||||
|
||||
/* If polling, assume that the card is always present. */
|
||||
if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION)
|
||||
return 1;
|
||||
|
||||
/* Host native card detect */
|
||||
return !!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT);
|
||||
}
|
||||
|
@ -2097,49 +2121,36 @@ static void sdhci_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
if (data->host_cookie)
|
||||
if (data->host_cookie == COOKIE_GIVEN ||
|
||||
data->host_cookie == COOKIE_MAPPED)
|
||||
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
mrq->data->host_cookie = 0;
|
||||
data->host_cookie = COOKIE_UNMAPPED;
|
||||
}
|
||||
}
|
||||
|
||||
static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
struct sdhci_host_next *next)
|
||||
struct mmc_data *data)
|
||||
{
|
||||
int sg_count;
|
||||
|
||||
if (!next && data->host_cookie &&
|
||||
data->host_cookie != host->next_data.cookie) {
|
||||
pr_debug(DRIVER_NAME "[%s] invalid cookie: %d, next-cookie %d\n",
|
||||
__func__, data->host_cookie, host->next_data.cookie);
|
||||
data->host_cookie = 0;
|
||||
if (data->host_cookie == COOKIE_MAPPED) {
|
||||
data->host_cookie = COOKIE_GIVEN;
|
||||
return data->sg_count;
|
||||
}
|
||||
|
||||
/* Check if next job is already prepared */
|
||||
if (next ||
|
||||
(!next && data->host_cookie != host->next_data.cookie)) {
|
||||
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg,
|
||||
data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
} else {
|
||||
sg_count = host->next_data.sg_count;
|
||||
host->next_data.sg_count = 0;
|
||||
}
|
||||
WARN_ON(data->host_cookie == COOKIE_GIVEN);
|
||||
|
||||
sg_count = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
|
||||
data->flags & MMC_DATA_WRITE ?
|
||||
DMA_TO_DEVICE : DMA_FROM_DEVICE);
|
||||
|
||||
if (sg_count == 0)
|
||||
return -EINVAL;
|
||||
return -ENOSPC;
|
||||
|
||||
if (next) {
|
||||
next->sg_count = sg_count;
|
||||
data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
|
||||
} else
|
||||
host->sg_count = sg_count;
|
||||
data->sg_count = sg_count;
|
||||
data->host_cookie = COOKIE_MAPPED;
|
||||
|
||||
return sg_count;
|
||||
}
|
||||
|
@ -2149,16 +2160,10 @@ static void sdhci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
|
|||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
if (mrq->data->host_cookie) {
|
||||
mrq->data->host_cookie = 0;
|
||||
return;
|
||||
}
|
||||
mrq->data->host_cookie = COOKIE_UNMAPPED;
|
||||
|
||||
if (host->flags & SDHCI_REQ_USE_DMA)
|
||||
if (sdhci_pre_dma_transfer(host,
|
||||
mrq->data,
|
||||
&host->next_data) < 0)
|
||||
mrq->data->host_cookie = 0;
|
||||
sdhci_pre_dma_transfer(host, mrq->data);
|
||||
}
|
||||
|
||||
static void sdhci_card_event(struct mmc_host *mmc)
|
||||
|
@ -3030,7 +3035,6 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
host->max_clk = host->ops->get_max_clock(host);
|
||||
}
|
||||
|
||||
host->next_data.cookie = 1;
|
||||
/*
|
||||
* In case of Host Controller v3.00, find out whether clock
|
||||
* multiplier is supported.
|
||||
|
@ -3126,7 +3130,8 @@ int sdhci_add_host(struct sdhci_host *host)
|
|||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
|
||||
|
||||
if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
|
||||
!(mmc->caps & MMC_CAP_NONREMOVABLE))
|
||||
!(mmc->caps & MMC_CAP_NONREMOVABLE) &&
|
||||
IS_ERR_VALUE(mmc_gpio_get_cd(host->mmc)))
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
/* If there are external regulators, get them */
|
||||
|
|
|
@ -309,9 +309,10 @@ struct sdhci_adma2_64_desc {
|
|||
*/
|
||||
#define SDHCI_MAX_SEGS 128
|
||||
|
||||
struct sdhci_host_next {
|
||||
unsigned int sg_count;
|
||||
s32 cookie;
|
||||
enum sdhci_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_MAPPED,
|
||||
COOKIE_GIVEN,
|
||||
};
|
||||
|
||||
struct sdhci_host {
|
||||
|
@ -409,6 +410,8 @@ struct sdhci_host {
|
|||
#define SDHCI_QUIRK2_SUPPORT_SINGLE (1<<13)
|
||||
/* Controller broken with using ACMD23 */
|
||||
#define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14)
|
||||
/* Broken Clock divider zero in controller */
|
||||
#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15)
|
||||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
|
@ -503,7 +506,6 @@ struct sdhci_host {
|
|||
unsigned int tuning_mode; /* Re-tuning mode supported by host */
|
||||
#define SDHCI_TUNING_MODE_1 0
|
||||
|
||||
struct sdhci_host_next next_data;
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
|
|
|
@ -1632,7 +1632,9 @@ static int sh_mmcif_suspend(struct device *dev)
|
|||
{
|
||||
struct sh_mmcif_host *host = dev_get_drvdata(dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -595,7 +595,7 @@ static irqreturn_t sunxi_mmc_handle_manual_stop(int irq, void *dev_id)
|
|||
|
||||
static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
|
||||
{
|
||||
unsigned long expire = jiffies + msecs_to_jiffies(250);
|
||||
unsigned long expire = jiffies + msecs_to_jiffies(750);
|
||||
u32 rval;
|
||||
|
||||
rval = mmc_readl(host, REG_CLKCR);
|
||||
|
|
|
@ -83,6 +83,8 @@ static int tmio_mmc_next_sg(struct tmio_mmc_host *host)
|
|||
return --host->sg_len;
|
||||
}
|
||||
|
||||
#define CMDREQ_TIMEOUT 5000
|
||||
|
||||
#ifdef CONFIG_MMC_DEBUG
|
||||
|
||||
#define STATUS_TO_TEXT(a, status, i) \
|
||||
|
@ -230,7 +232,7 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
|||
*/
|
||||
if (IS_ERR_OR_NULL(mrq)
|
||||
|| time_is_after_jiffies(host->last_req_ts +
|
||||
msecs_to_jiffies(2000))) {
|
||||
msecs_to_jiffies(CMDREQ_TIMEOUT))) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
@ -818,7 +820,7 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
ret = tmio_mmc_start_command(host, mrq->cmd);
|
||||
if (!ret) {
|
||||
schedule_delayed_work(&host->delayed_reset_work,
|
||||
msecs_to_jiffies(2000));
|
||||
msecs_to_jiffies(CMDREQ_TIMEOUT));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1611,7 +1611,7 @@ static irqreturn_t usdhi6_cd(int irq, void *dev_id)
|
|||
return IRQ_NONE;
|
||||
|
||||
/* Ack */
|
||||
usdhi6_write(host, USDHI6_SD_INFO1, !status);
|
||||
usdhi6_write(host, USDHI6_SD_INFO1, ~status);
|
||||
|
||||
if (!work_pending(&mmc->detect.work) &&
|
||||
(((status & USDHI6_SD_INFO1_CARD_INSERT) &&
|
||||
|
@ -1634,6 +1634,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
|
|||
struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work);
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_data *data = mrq ? mrq->data : NULL;
|
||||
struct scatterlist *sg = host->sg ?: data->sg;
|
||||
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n",
|
||||
|
@ -1669,7 +1670,7 @@ static void usdhi6_timeout_work(struct work_struct *work)
|
|||
"%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n",
|
||||
data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx,
|
||||
host->offset, data->blocks, data->blksz, data->sg_len,
|
||||
sg_dma_len(host->sg), host->sg->offset);
|
||||
sg_dma_len(sg), sg->offset);
|
||||
usdhi6_sg_unmap(host, true);
|
||||
/*
|
||||
* If USDHI6_WAIT_FOR_DATA_END times out, we have already unmapped
|
||||
|
@ -1715,12 +1716,14 @@ static int usdhi6_probe(struct platform_device *pdev)
|
|||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto e_free_mmc;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret < 0)
|
||||
goto e_free_mmc;
|
||||
|
||||
mmc_regulator_get_supply(mmc);
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->wait = USDHI6_WAIT_FOR_REQUEST;
|
||||
|
@ -1734,8 +1737,10 @@ static int usdhi6_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
host->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(host->clk))
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
goto e_free_mmc;
|
||||
}
|
||||
|
||||
host->imclk = clk_get_rate(host->clk);
|
||||
|
||||
|
|
|
@ -279,10 +279,13 @@ struct mmc_card {
|
|||
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
|
||||
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
|
||||
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
|
||||
#define MMC_QUIRK_TRIM_BROKEN (1<<12) /* Skip trim */
|
||||
|
||||
|
||||
unsigned int erase_size; /* erase size in sectors */
|
||||
unsigned int erase_shift; /* if erase unit is power 2 */
|
||||
unsigned int pref_erase; /* in sectors */
|
||||
unsigned int eg_boundary; /* don't cross erase-group boundaries */
|
||||
u8 erased_byte; /* value of erased bytes */
|
||||
|
||||
u32 raw_cid[4]; /* raw card CID */
|
||||
|
|
|
@ -98,6 +98,7 @@ struct mmc_data;
|
|||
* @irq_flags: The flags to be passed to request_irq.
|
||||
* @irq: The irq value to be passed to request_irq.
|
||||
* @sdio_id0: Number of slot0 in the SDIO interrupt registers.
|
||||
* @dto_timer: Timer for broken data transfer over scheme.
|
||||
*
|
||||
* Locking
|
||||
* =======
|
||||
|
@ -153,11 +154,7 @@ struct dw_mci {
|
|||
dma_addr_t sg_dma;
|
||||
void *sg_cpu;
|
||||
const struct dw_mci_dma_ops *dma_ops;
|
||||
#ifdef CONFIG_MMC_DW_IDMAC
|
||||
unsigned int ring_size;
|
||||
#else
|
||||
struct dw_mci_dma_data *dma_data;
|
||||
#endif
|
||||
u32 cmd_status;
|
||||
u32 data_status;
|
||||
u32 stop_cmdr;
|
||||
|
@ -204,6 +201,7 @@ struct dw_mci {
|
|||
int sdio_id0;
|
||||
|
||||
struct timer_list cmd11_timer;
|
||||
struct timer_list dto_timer;
|
||||
};
|
||||
|
||||
/* DMA ops for Internal/External DMAC interface */
|
||||
|
@ -226,6 +224,8 @@ struct dw_mci_dma_ops {
|
|||
#define DW_MCI_QUIRK_HIGHSPEED BIT(2)
|
||||
/* Unreliable card detection */
|
||||
#define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3)
|
||||
/* Timer for broken data transfer over scheme */
|
||||
#define DW_MCI_QUIRK_BROKEN_DTO BIT(4)
|
||||
|
||||
struct dma_pdata;
|
||||
|
||||
|
@ -259,7 +259,6 @@ struct dw_mci_board {
|
|||
|
||||
struct dw_mci_dma_ops *dma_ops;
|
||||
struct dma_pdata *data;
|
||||
struct block_settings *blk_settings;
|
||||
};
|
||||
|
||||
#endif /* LINUX_MMC_DW_MMC_H */
|
||||
|
|
|
@ -412,7 +412,8 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host)
|
|||
{
|
||||
host->ops->enable_sdio_irq(host, 0);
|
||||
host->sdio_irq_pending = true;
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
if (host->sdio_irq_thread)
|
||||
wake_up_process(host->sdio_irq_thread);
|
||||
}
|
||||
|
||||
void sdio_run_irqs(struct mmc_host *host);
|
||||
|
|
|
@ -45,5 +45,6 @@ struct esdhc_platform_data {
|
|||
int max_bus_width;
|
||||
bool support_vsel;
|
||||
unsigned int delay_line;
|
||||
unsigned int tuning_step; /* The delay cell steps in tuning procedure */
|
||||
};
|
||||
#endif /* __ASM_ARCH_IMX_ESDHC_H */
|
||||
|
|
Loading…
Reference in New Issue