You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge tag 'mmc-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:
@@ -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
|
||||
};
|
||||
|
||||
|
||||
+39
-7
@@ -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;
|
||||
}
|
||||
|
||||
+21
-21
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+197
-87
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
+224
-131
@@ -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);
|
||||
|
||||
+115
-87
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user