MMC core:
- Allow an invalid regulator in mmc_regulator_set_ocr() - Log about empty non-removable slots - Add helpers to enable/disable the vqmmc regulator MMC host: - mtk-sd: Add support for the mt8365 variant - renesas_sdhi: Remove support for R-Car H3 ES1.* variants - sdhci_am654: Add power management support - sdhci-cadence: Add support for eMMC hardware reset - sdhci-cadence: Add support for AMD Pensando Elba variant - sdhci-msm: Add support for the IPQ5018 variant - sdhci-msm: Add support for the QCM2290 variant - sdhci-of-arasan: Skip setting clock delay for 400KHz - sdhci-of-arasan: Add support for the Xilinx Versal Net variant - sdhci-of-arasan: Remove Intel Thunder Bay SOC support - sdhci-of-arasan: Add support to request the "gate" clock - sdhci-of-dwcmshc: Properly determine max clock on Rockchip - sdhci-of-esdhc: Fix quirk to ignore command inhibit for data - sdhci-pci-o2micro: Fix SDR50 mode timing issue MEMSTICK: - r592: Fix use-after-free bug in r592_remove due to race condition -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmRGi1YXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCn2BQ//e5pre/zrviB4FrWG+Qaq9Guv YXJG4/NTU5l/qjL5anxUekPiueK4wmMTywOdHEtM1DpVqizZuIWQmx7Z7U0pyJQu 9g5g73IiCxwjT/XFTeAru95Wxm0sNrM0r4vxSQM/iNv3niAXlRMo3n1jtWU+zfZZ G5vlVDFMduQ9fJz31hE0fYqEOAhyP2Aj2OGsIS4YNjN4o6G9OKYJksmsSFpYOehi c4X1TEcckDbW6gZyaOGXMnvQCQatMtYFeALPhnd+7UYYu76fgGrDQRFNC2VZqg0B XpbfA6Zopq05OG/kCGEG5ekLe05VtfgoUkN1blbtqfnSCeZZ5erqN9vHEPQLQ1UV SXair8Kh5fgP2iLRMXIZYgCWf1PGkztDTXxflVqS3VqjvF7tD3IPImBZoxHX8Gfe s9vb+woRshLS7L89iNGovSMp+ApDvaluNDuW0Y3LD6ejoRCK7Lni1l7tQbfkTU5H NqInOJvlx3QatQ5PSI9CxOY3Gz+6MN2qtLfUP84CKerlb1MR7dPsONXzUCmLOH1U xZE2XhZ9LYeWExxZendbYGuTUScwMOQTj1vF3IkyBI17+PG3Bkfv9oqB7qrlKGdL 6+A1MyIdzatV8xS1uQ/ylyUjzlPInfTncuZ30ZHwyh5mFofD+5a3JI4cv5wzmR8v m1H+hhQGZS8Gk3i1lN4= =qt1U -----END PGP SIGNATURE----- Merge tag 'mmc-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Allow an invalid regulator in mmc_regulator_set_ocr() - Log about empty non-removable slots - Add helpers to enable/disable the vqmmc regulator MMC host: - mtk-sd: Add support for the mt8365 variant - renesas_sdhi: Remove support for R-Car H3 ES1.* variants - sdhci_am654: Add power management support - sdhci-cadence: Add support for eMMC hardware reset - sdhci-cadence: Add support for AMD Pensando Elba variant - sdhci-msm: Add support for the IPQ5018 variant - sdhci-msm: Add support for the QCM2290 variant - sdhci-of-arasan: Skip setting clock delay for 400KHz - sdhci-of-arasan: Add support for the Xilinx Versal Net variant - sdhci-of-arasan: Remove Intel Thunder Bay SOC support - sdhci-of-arasan: Add support to request the "gate" clock - sdhci-of-dwcmshc: Properly determine max clock on Rockchip - sdhci-of-esdhc: Fix quirk to ignore command inhibit for data - sdhci-pci-o2micro: Fix SDR50 mode timing issue MEMSTICK: - r592: Fix use-after-free bug in r592_remove due to race condition" * tag 'mmc-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (40 commits) dt-bindings: mmc: sdhci-msm: Document the IPQ5018 compatible mmc: vub300: remove unreachable code mmc: sdhci-cadence: Support mmc hardware reset mmc: sdhci-cadence: Add AMD Pensando Elba SoC support mmc: sdhci-cadence: Support device specific init during probe mmc: sdhci-cadence: Enable device specific override of writel() dt-bindings: mmc: cdns: Add AMD Pensando Elba SoC mmc: core: Remove unused macro mmc_req_rel_wr mmc: sdhci-of-arasan: Skip setting clock delay for 400KHz mmc: sdhci-of-arasan: Add support for eMMC5.1 on Xilinx Versal Net platform dt-bindings: mmc: arasan,sdci: Add Xilinx Versal Net compatible mmc: sdhci_am654: Add support for PM suspend/resume mmc: core: remove unnecessary (void*) conversions dt-bindings: mmc: fsl-imx-esdhc: ref sdhci-common.yaml mmc: sdhci-of-esdhc: fix quirk to ignore command inhibit for data mmc: core: Log about empty non-removable slots dt-bindings: mmc: fujitsu: Add Socionext Synquacer mmc: sdricoh_cs: remove unused sdricoh_readw function dt-bindings: mmc: Remove bindings for Intel Thunder Bay SoC" mmc: sdhci-of-arasan: Remove Intel Thunder Bay SOC support ...
This commit is contained in:
commit
c5c06e2780
@ -27,6 +27,7 @@ allOf:
|
||||
enum:
|
||||
- xlnx,zynqmp-8.9a
|
||||
- xlnx,versal-8.9a
|
||||
- xlnx,versal-net-emmc
|
||||
then:
|
||||
properties:
|
||||
clock-output-names:
|
||||
@ -62,6 +63,10 @@ properties:
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
clock-output-names and '#clock-cells'.
|
||||
- const: xlnx,versal-net-emmc # Versal Net eMMC PHY
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
clock-output-names and '#clock-cells'.
|
||||
- items:
|
||||
- const: intel,lgm-sdhci-5.1-emmc # Intel LGM eMMC PHY
|
||||
- const: arasan,sdhci-5.1
|
||||
@ -88,12 +93,6 @@ properties:
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
arasan,soc-ctl-syscon.
|
||||
- items:
|
||||
- const: intel,thunderbay-sdhci-5.1 # Intel Thunder Bay eMMC PHY
|
||||
- const: arasan,sdhci-5.1
|
||||
description:
|
||||
For this device it is strongly suggested to include
|
||||
clock-output-names and '#clock-cells'.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -309,22 +308,3 @@ examples:
|
||||
<&scmi_clk KEEM_BAY_PSS_SD0>;
|
||||
arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
|
||||
};
|
||||
|
||||
- |
|
||||
#define EMMC_XIN_CLK
|
||||
#define EMMC_AXI_CLK
|
||||
#define TBH_PSS_EMMC_RST_N
|
||||
mmc@80420000 {
|
||||
compatible = "intel,thunderbay-sdhci-5.1", "arasan,sdhci-5.1";
|
||||
interrupts = <GIC_SPI 714 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0x80420000 0x400>;
|
||||
clocks = <&scmi_clk EMMC_XIN_CLK>,
|
||||
<&scmi_clk EMMC_AXI_CLK>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
phys = <&emmc_phy>;
|
||||
phy-names = "phy_arasan";
|
||||
assigned-clocks = <&scmi_clk EMMC_XIN_CLK>;
|
||||
clock-output-names = "emmc_cardclock";
|
||||
resets = <&rst_pss1 TBH_PSS_EMMC_RST_N>;
|
||||
#clock-cells = <0x0>;
|
||||
};
|
||||
|
@ -9,19 +9,18 @@ title: Cadence SD/SDIO/eMMC Host Controller (SD4HC)
|
||||
maintainers:
|
||||
- Masahiro Yamada <yamada.masahiro@socionext.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- amd,pensando-elba-sd4hc
|
||||
- microchip,mpfs-sd4hc
|
||||
- socionext,uniphier-sd4hc
|
||||
- const: cdns,sd4hc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@ -120,6 +119,26 @@ required:
|
||||
- interrupts
|
||||
- clocks
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: amd,pensando-elba-sd4hc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Host controller registers
|
||||
- description: Elba byte-lane enable register for writes
|
||||
required:
|
||||
- resets
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
@ -10,7 +10,7 @@ maintainers:
|
||||
- Shawn Guo <shawnguo@kernel.org>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml
|
||||
- $ref: sdhci-common.yaml#
|
||||
|
||||
description: |
|
||||
The Enhanced Secure Digital Host Controller on Freescale i.MX family
|
||||
|
@ -14,9 +14,13 @@ allOf:
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- fujitsu,mb86s70-sdhci-3.0
|
||||
- socionext,f-sdh30-e51-mmc
|
||||
oneOf:
|
||||
- items:
|
||||
- const: socionext,synquacer-sdhci
|
||||
- const: fujitsu,mb86s70-sdhci-3.0
|
||||
- enum:
|
||||
- fujitsu,mb86s70-sdhci-3.0
|
||||
- socionext,f-sdh30-e51-mmc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -29,6 +33,11 @@ properties:
|
||||
- const: iface
|
||||
- const: core
|
||||
|
||||
dma-coherent: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 2
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
|
@ -34,6 +34,7 @@ properties:
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8192-mmc
|
||||
- mediatek,mt8195-mmc
|
||||
- mediatek,mt8365-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
|
||||
reg:
|
||||
|
@ -34,8 +34,10 @@ properties:
|
||||
- const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,ipq5018-sdhci
|
||||
- qcom,ipq5332-sdhci
|
||||
- qcom,ipq9574-sdhci
|
||||
- qcom,qcm2290-sdhci
|
||||
- qcom,qcs404-sdhci
|
||||
- qcom,sc7180-sdhci
|
||||
- qcom,sc7280-sdhci
|
||||
|
@ -829,7 +829,7 @@ static void r592_remove(struct pci_dev *pdev)
|
||||
/* Stop the processing thread.
|
||||
That ensures that we won't take any more requests */
|
||||
kthread_stop(dev->io_thread);
|
||||
|
||||
del_timer_sync(&dev->detect_timer);
|
||||
r592_enable_device(dev, false);
|
||||
|
||||
while (!error && dev->req) {
|
||||
|
@ -76,8 +76,6 @@ MODULE_ALIAS("mmc:block");
|
||||
#define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16)
|
||||
#define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8)
|
||||
|
||||
#define mmc_req_rel_wr(req) ((req->cmd_flags & REQ_FUA) && \
|
||||
(rq_data_dir(req) == WRITE))
|
||||
static DEFINE_MUTEX(block_mutex);
|
||||
|
||||
/*
|
||||
|
@ -2257,6 +2257,11 @@ void mmc_rescan(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
|
||||
/* A non-removable card should have been detected by now. */
|
||||
if (!mmc_card_is_removable(host) && !host->bus_ops)
|
||||
pr_info("%s: Failed to initialize a non-removable card",
|
||||
mmc_hostname(host));
|
||||
|
||||
/*
|
||||
* Ignore the command timeout errors observed during
|
||||
* the card init as those are excepted.
|
||||
|
@ -246,7 +246,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(mmc_err_state, mmc_err_state_get, NULL, "%llu\n");
|
||||
|
||||
static int mmc_err_stats_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mmc_host *host = (struct mmc_host *)file->private;
|
||||
struct mmc_host *host = file->private;
|
||||
const char *desc[MMC_ERR_MAX] = {
|
||||
[MMC_ERR_CMD_TIMEOUT] = "Command Timeout Occurred",
|
||||
[MMC_ERR_CMD_CRC] = "Command CRC Errors Occurred",
|
||||
|
@ -3045,7 +3045,7 @@ static LIST_HEAD(mmc_test_file_test);
|
||||
|
||||
static int mtf_test_show(struct seq_file *sf, void *data)
|
||||
{
|
||||
struct mmc_card *card = (struct mmc_card *)sf->private;
|
||||
struct mmc_card *card = sf->private;
|
||||
struct mmc_test_general_result *gr;
|
||||
|
||||
mutex_lock(&mmc_test_lock);
|
||||
@ -3079,8 +3079,8 @@ static int mtf_test_open(struct inode *inode, struct file *file)
|
||||
static ssize_t mtf_test_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *pos)
|
||||
{
|
||||
struct seq_file *sf = (struct seq_file *)file->private_data;
|
||||
struct mmc_card *card = (struct mmc_card *)sf->private;
|
||||
struct seq_file *sf = file->private_data;
|
||||
struct mmc_card *card = sf->private;
|
||||
struct mmc_test_card *test;
|
||||
long testcase;
|
||||
int ret;
|
||||
|
@ -110,6 +110,9 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
|
||||
int result = 0;
|
||||
int min_uV, max_uV;
|
||||
|
||||
if (IS_ERR(supply))
|
||||
return 0;
|
||||
|
||||
if (vdd_bit) {
|
||||
mmc_ocrbitnum_to_vdd(vdd_bit, &min_uV, &max_uV);
|
||||
|
||||
@ -271,3 +274,44 @@ int mmc_regulator_get_supply(struct mmc_host *mmc)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_get_supply);
|
||||
|
||||
/**
|
||||
* mmc_regulator_enable_vqmmc - enable VQMMC regulator for a host
|
||||
* @mmc: the host to regulate
|
||||
*
|
||||
* Returns 0 or errno. Enables the regulator for vqmmc.
|
||||
* Keeps track of the enable status for ensuring that calls to
|
||||
* regulator_enable/disable are balanced.
|
||||
*/
|
||||
int mmc_regulator_enable_vqmmc(struct mmc_host *mmc)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && !mmc->vqmmc_enabled) {
|
||||
ret = regulator_enable(mmc->supply.vqmmc);
|
||||
if (ret < 0)
|
||||
dev_err(mmc_dev(mmc), "enabling vqmmc regulator failed\n");
|
||||
else
|
||||
mmc->vqmmc_enabled = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_enable_vqmmc);
|
||||
|
||||
/**
|
||||
* mmc_regulator_disable_vqmmc - disable VQMMC regulator for a host
|
||||
* @mmc: the host to regulate
|
||||
*
|
||||
* Returns 0 or errno. Disables the regulator for vqmmc.
|
||||
* Keeps track of the enable status for ensuring that calls to
|
||||
* regulator_enable/disable are balanced.
|
||||
*/
|
||||
void mmc_regulator_disable_vqmmc(struct mmc_host *mmc)
|
||||
{
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && mmc->vqmmc_enabled) {
|
||||
regulator_disable(mmc->supply.vqmmc);
|
||||
mmc->vqmmc_enabled = false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(mmc_regulator_disable_vqmmc);
|
||||
|
@ -255,6 +255,7 @@ config MMC_SDHCI_CADENCE
|
||||
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Cadence SD/SDIO/eMMC driver.
|
||||
|
||||
|
@ -46,8 +46,7 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
|
||||
host->irq_flags = 0;
|
||||
host->pdata = pdev->dev.platform_data;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->regs = devm_ioremap_resource(&pdev->dev, regs);
|
||||
host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s);
|
||||
if (IS_ERR(host->regs))
|
||||
return PTR_ERR(host->regs);
|
||||
|
||||
|
@ -1079,8 +1079,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(&pdev->dev, host->mem_res);
|
||||
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto err_free_host;
|
||||
|
@ -174,7 +174,6 @@ struct meson_host {
|
||||
|
||||
int irq;
|
||||
|
||||
bool vqmmc_enabled;
|
||||
bool needs_pre_post_req;
|
||||
|
||||
spinlock_t lock;
|
||||
@ -604,32 +603,18 @@ static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
*/
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
|
||||
regulator_disable(mmc->supply.vqmmc);
|
||||
host->vqmmc_enabled = false;
|
||||
}
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
|
||||
mmc_regulator_disable_vqmmc(mmc);
|
||||
|
||||
break;
|
||||
|
||||
case MMC_POWER_UP:
|
||||
if (!IS_ERR(mmc->supply.vmmc))
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
|
||||
|
||||
break;
|
||||
|
||||
case MMC_POWER_ON:
|
||||
if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
|
||||
int ret = regulator_enable(mmc->supply.vqmmc);
|
||||
|
||||
if (ret < 0)
|
||||
dev_err(host->dev,
|
||||
"failed to enable vqmmc regulator\n");
|
||||
else
|
||||
host->vqmmc_enabled = true;
|
||||
}
|
||||
mmc_regulator_enable_vqmmc(mmc);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -1181,7 +1166,6 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
||||
"amlogic,dram-access-quirk");
|
||||
|
||||
/* Get regulators and the supported OCR mask */
|
||||
host->vqmmc_enabled = false;
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1962,28 +1962,28 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (of_get_property(np, "st,sig-dir-dat0", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir-dat0"))
|
||||
host->pwr_reg_add |= MCI_ST_DATA0DIREN;
|
||||
if (of_get_property(np, "st,sig-dir-dat2", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir-dat2"))
|
||||
host->pwr_reg_add |= MCI_ST_DATA2DIREN;
|
||||
if (of_get_property(np, "st,sig-dir-dat31", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir-dat31"))
|
||||
host->pwr_reg_add |= MCI_ST_DATA31DIREN;
|
||||
if (of_get_property(np, "st,sig-dir-dat74", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir-dat74"))
|
||||
host->pwr_reg_add |= MCI_ST_DATA74DIREN;
|
||||
if (of_get_property(np, "st,sig-dir-cmd", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir-cmd"))
|
||||
host->pwr_reg_add |= MCI_ST_CMDDIREN;
|
||||
if (of_get_property(np, "st,sig-pin-fbclk", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-pin-fbclk"))
|
||||
host->pwr_reg_add |= MCI_ST_FBCLKEN;
|
||||
if (of_get_property(np, "st,sig-dir", NULL))
|
||||
if (of_property_read_bool(np, "st,sig-dir"))
|
||||
host->pwr_reg_add |= MCI_STM32_DIRPOL;
|
||||
if (of_get_property(np, "st,neg-edge", NULL))
|
||||
if (of_property_read_bool(np, "st,neg-edge"))
|
||||
host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
|
||||
if (of_get_property(np, "st,use-ckin", NULL))
|
||||
if (of_property_read_bool(np, "st,use-ckin"))
|
||||
mmci_probe_level_translator(mmc);
|
||||
|
||||
if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
|
||||
if (of_property_read_bool(np, "mmc-cap-mmc-highspeed"))
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
|
||||
if (of_get_property(np, "mmc-cap-sd-highspeed", NULL))
|
||||
if (of_property_read_bool(np, "mmc-cap-sd-highspeed"))
|
||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
|
||||
return 0;
|
||||
|
@ -1345,8 +1345,7 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->virt_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
host->virt_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(host->virt_base))
|
||||
return PTR_ERR(host->virt_base);
|
||||
|
||||
|
@ -1736,18 +1736,18 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
|
||||
if (legacy && legacy->name)
|
||||
pdata->name = legacy->name;
|
||||
|
||||
if (of_find_property(np, "ti,dual-volt", NULL))
|
||||
if (of_property_read_bool(np, "ti,dual-volt"))
|
||||
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
|
||||
|
||||
if (of_find_property(np, "ti,non-removable", NULL)) {
|
||||
if (of_property_read_bool(np, "ti,non-removable")) {
|
||||
pdata->nonremovable = true;
|
||||
pdata->no_regulator_off_init = true;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "ti,needs-special-reset", NULL))
|
||||
if (of_property_read_bool(np, "ti,needs-special-reset"))
|
||||
pdata->features |= HSMMC_HAS_UPDATED_RESET;
|
||||
|
||||
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
|
||||
if (of_property_read_bool(np, "ti,needs-special-hs-handling"))
|
||||
pdata->features |= HSMMC_HAS_HSPE_SUPPORT;
|
||||
|
||||
return pdata;
|
||||
|
@ -578,8 +578,7 @@ static int owl_mmc_probe(struct platform_device *pdev)
|
||||
owl_host->mmc = mmc;
|
||||
spin_lock_init(&owl_host->lock);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
owl_host->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
owl_host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(owl_host->base)) {
|
||||
ret = PTR_ERR(owl_host->base);
|
||||
goto err_free_host;
|
||||
|
@ -72,11 +72,10 @@ enum renesas_sdhi_dma_cookie {
|
||||
|
||||
static unsigned long global_flags;
|
||||
/*
|
||||
* Workaround for avoiding to use RX DMAC by multiple channels.
|
||||
* On R-Car H3 ES1.* and M3-W ES1.0, when multiple SDHI channels use
|
||||
* RX DMAC simultaneously, sometimes hundreds of bytes data are not
|
||||
* stored into the system memory even if the DMAC interrupt happened.
|
||||
* So, this driver then uses one RX DMAC channel only.
|
||||
* Workaround for avoiding to use RX DMAC by multiple channels. On R-Car M3-W
|
||||
* ES1.0, when multiple SDHI channels use RX DMAC simultaneously, sometimes
|
||||
* hundreds of data bytes are not stored into the system memory even if the
|
||||
* DMAC interrupt happened. So, this driver then uses one RX DMAC channel only.
|
||||
*/
|
||||
#define SDHI_INTERNAL_DMAC_RX_IN_USE 0
|
||||
|
||||
@ -222,7 +221,6 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r9a09g011 = {
|
||||
*/
|
||||
static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||
{ .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400_one_rx },
|
||||
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.0", .data = &sdhi_quirks_4tap_nohs400_one_rx },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.[12]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
@ -66,7 +67,11 @@ struct sdhci_cdns_phy_param {
|
||||
|
||||
struct sdhci_cdns_priv {
|
||||
void __iomem *hrs_addr;
|
||||
void __iomem *ctl_addr; /* write control */
|
||||
spinlock_t wrlock; /* write lock */
|
||||
bool enhanced_strobe;
|
||||
void (*priv_writel)(struct sdhci_cdns_priv *priv, u32 val, void __iomem *reg);
|
||||
struct reset_control *rst_hw;
|
||||
unsigned int nr_phy_params;
|
||||
struct sdhci_cdns_phy_param phy_params[];
|
||||
};
|
||||
@ -76,6 +81,11 @@ struct sdhci_cdns_phy_cfg {
|
||||
u8 addr;
|
||||
};
|
||||
|
||||
struct sdhci_cdns_drv_data {
|
||||
int (*init)(struct platform_device *pdev);
|
||||
const struct sdhci_pltfm_data pltfm_data;
|
||||
};
|
||||
|
||||
static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
|
||||
{ "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, },
|
||||
{ "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, },
|
||||
@ -90,6 +100,12 @@ static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = {
|
||||
{ "cdns,phy-dll-delay-strobe", SDHCI_CDNS_PHY_DLY_STROBE, },
|
||||
};
|
||||
|
||||
static inline void cdns_writel(struct sdhci_cdns_priv *priv, u32 val,
|
||||
void __iomem *reg)
|
||||
{
|
||||
writel(val, reg);
|
||||
}
|
||||
|
||||
static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
||||
u8 addr, u8 data)
|
||||
{
|
||||
@ -104,17 +120,17 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
||||
|
||||
tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
|
||||
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
|
||||
writel(tmp, reg);
|
||||
priv->priv_writel(priv, tmp, reg);
|
||||
|
||||
tmp |= SDHCI_CDNS_HRS04_WR;
|
||||
writel(tmp, reg);
|
||||
priv->priv_writel(priv, tmp, reg);
|
||||
|
||||
ret = readl_poll_timeout(reg, tmp, tmp & SDHCI_CDNS_HRS04_ACK, 0, 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp &= ~SDHCI_CDNS_HRS04_WR;
|
||||
writel(tmp, reg);
|
||||
priv->priv_writel(priv, tmp, reg);
|
||||
|
||||
ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
|
||||
0, 10);
|
||||
@ -191,7 +207,7 @@ static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode)
|
||||
tmp = readl(priv->hrs_addr + SDHCI_CDNS_HRS06);
|
||||
tmp &= ~SDHCI_CDNS_HRS06_MODE;
|
||||
tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_MODE, mode);
|
||||
writel(tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
|
||||
priv->priv_writel(priv, tmp, priv->hrs_addr + SDHCI_CDNS_HRS06);
|
||||
}
|
||||
|
||||
static u32 sdhci_cdns_get_emmc_mode(struct sdhci_cdns_priv *priv)
|
||||
@ -223,7 +239,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val)
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
tmp |= SDHCI_CDNS_HRS06_TUNE_UP;
|
||||
writel(tmp, reg);
|
||||
priv->priv_writel(priv, tmp, reg);
|
||||
|
||||
ret = readl_poll_timeout(reg, tmp,
|
||||
!(tmp & SDHCI_CDNS_HRS06_TUNE_UP),
|
||||
@ -309,6 +325,91 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host,
|
||||
sdhci_set_uhs_signaling(host, timing);
|
||||
}
|
||||
|
||||
/* Elba control register bits [6:3] are byte-lane enables */
|
||||
#define ELBA_BYTE_ENABLE_MASK(x) ((x) << 3)
|
||||
|
||||
/*
|
||||
* The Pensando Elba SoC explicitly controls byte-lane enabling on writes
|
||||
* which includes writes to the HRS registers. The write lock (wrlock)
|
||||
* is used to ensure byte-lane enable, using write control (ctl_addr),
|
||||
* occurs before the data write.
|
||||
*/
|
||||
static void elba_priv_writel(struct sdhci_cdns_priv *priv, u32 val,
|
||||
void __iomem *reg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&priv->wrlock, flags);
|
||||
writel(GENMASK(7, 3), priv->ctl_addr);
|
||||
writel(val, reg);
|
||||
spin_unlock_irqrestore(&priv->wrlock, flags);
|
||||
}
|
||||
|
||||
static void elba_write_l(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
elba_priv_writel(sdhci_cdns_priv(host), val, host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static void elba_write_w(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
||||
u32 shift = reg & GENMASK(1, 0);
|
||||
unsigned long flags;
|
||||
u32 byte_enables;
|
||||
|
||||
byte_enables = GENMASK(1, 0) << shift;
|
||||
spin_lock_irqsave(&priv->wrlock, flags);
|
||||
writel(ELBA_BYTE_ENABLE_MASK(byte_enables), priv->ctl_addr);
|
||||
writew(val, host->ioaddr + reg);
|
||||
spin_unlock_irqrestore(&priv->wrlock, flags);
|
||||
}
|
||||
|
||||
static void elba_write_b(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
||||
u32 shift = reg & GENMASK(1, 0);
|
||||
unsigned long flags;
|
||||
u32 byte_enables;
|
||||
|
||||
byte_enables = BIT(0) << shift;
|
||||
spin_lock_irqsave(&priv->wrlock, flags);
|
||||
writel(ELBA_BYTE_ENABLE_MASK(byte_enables), priv->ctl_addr);
|
||||
writeb(val, host->ioaddr + reg);
|
||||
spin_unlock_irqrestore(&priv->wrlock, flags);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_elba_ops = {
|
||||
.write_l = elba_write_l,
|
||||
.write_w = elba_write_w,
|
||||
.write_b = elba_write_b,
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_timeout_clock = sdhci_cdns_get_timeout_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static int elba_drv_init(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
||||
void __iomem *ioaddr;
|
||||
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_8_BIT_DATA;
|
||||
spin_lock_init(&priv->wrlock);
|
||||
|
||||
/* Byte-lane control register */
|
||||
ioaddr = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(ioaddr))
|
||||
return PTR_ERR(ioaddr);
|
||||
|
||||
priv->ctl_addr = ioaddr;
|
||||
priv->priv_writel = elba_priv_writel;
|
||||
writel(ELBA_BYTE_ENABLE_MASK(0xf), priv->ctl_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_cdns_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.get_timeout_clock = sdhci_cdns_get_timeout_clock,
|
||||
@ -318,13 +419,24 @@ static const struct sdhci_ops sdhci_cdns_ops = {
|
||||
.set_uhs_signaling = sdhci_cdns_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_cdns_uniphier_pltfm_data = {
|
||||
.ops = &sdhci_cdns_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = {
|
||||
.pltfm_data = {
|
||||
.ops = &sdhci_cdns_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_cdns_pltfm_data = {
|
||||
.ops = &sdhci_cdns_ops,
|
||||
static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = {
|
||||
.init = elba_drv_init,
|
||||
.pltfm_data = {
|
||||
.ops = &sdhci_elba_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = {
|
||||
.pltfm_data = {
|
||||
.ops = &sdhci_cdns_ops,
|
||||
},
|
||||
};
|
||||
|
||||
static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||
@ -347,10 +459,26 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||
SDHCI_CDNS_HRS06_MODE_MMC_HS400);
|
||||
}
|
||||
|
||||
static void sdhci_cdns_mmc_hw_reset(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "emmc hardware reset\n");
|
||||
|
||||
reset_control_assert(priv->rst_hw);
|
||||
/* For eMMC, minimum is 1us but give it 3us for good measure */
|
||||
udelay(3);
|
||||
|
||||
reset_control_deassert(priv->rst_hw);
|
||||
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
||||
usleep_range(300, 1000);
|
||||
}
|
||||
|
||||
static int sdhci_cdns_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
const struct sdhci_pltfm_data *data;
|
||||
const struct sdhci_cdns_drv_data *data;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_cdns_priv *priv;
|
||||
struct clk *clk;
|
||||
@ -369,10 +497,10 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
|
||||
|
||||
data = of_device_get_match_data(dev);
|
||||
if (!data)
|
||||
data = &sdhci_cdns_pltfm_data;
|
||||
data = &sdhci_cdns_drv_data;
|
||||
|
||||
nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node);
|
||||
host = sdhci_pltfm_init(pdev, data,
|
||||
host = sdhci_pltfm_init(pdev, &data->pltfm_data,
|
||||
struct_size(priv, phy_params, nr_phy_params));
|
||||
if (IS_ERR(host)) {
|
||||
ret = PTR_ERR(host);
|
||||
@ -386,9 +514,15 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
|
||||
priv->nr_phy_params = nr_phy_params;
|
||||
priv->hrs_addr = host->ioaddr;
|
||||
priv->enhanced_strobe = false;
|
||||
priv->priv_writel = cdns_writel;
|
||||
host->ioaddr += SDHCI_CDNS_SRS_BASE;
|
||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||
sdhci_cdns_hs400_enhanced_strobe;
|
||||
if (data->init) {
|
||||
ret = data->init(pdev);
|
||||
if (ret)
|
||||
goto free;
|
||||
}
|
||||
sdhci_enable_v4_mode(host);
|
||||
__sdhci_read_caps(host, &version, NULL, NULL);
|
||||
|
||||
@ -404,6 +538,15 @@ static int sdhci_cdns_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
if (host->mmc->caps & MMC_CAP_HW_RESET) {
|
||||
priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL);
|
||||
if (IS_ERR(priv->rst_hw))
|
||||
return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw),
|
||||
"reset controller error\n");
|
||||
if (priv->rst_hw)
|
||||
host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset;
|
||||
}
|
||||
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto free;
|
||||
@ -453,7 +596,11 @@ static const struct dev_pm_ops sdhci_cdns_pm_ops = {
|
||||
static const struct of_device_id sdhci_cdns_match[] = {
|
||||
{
|
||||
.compatible = "socionext,uniphier-sd4hc",
|
||||
.data = &sdhci_cdns_uniphier_pltfm_data,
|
||||
.data = &sdhci_cdns_uniphier_drv_data,
|
||||
},
|
||||
{
|
||||
.compatible = "amd,pensando-elba-sd4hc",
|
||||
.data = &sdhci_elba_drv_data,
|
||||
},
|
||||
{ .compatible = "cdns,sd4hc" },
|
||||
{ /* sentinel */ }
|
||||
|
@ -1597,7 +1597,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
|
||||
int ret;
|
||||
|
||||
if (of_get_property(np, "fsl,wp-controller", NULL))
|
||||
if (of_property_read_bool(np, "fsl,wp-controller"))
|
||||
boarddata->wp_type = ESDHC_WP_CONTROLLER;
|
||||
|
||||
/*
|
||||
@ -1614,7 +1614,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
||||
|
||||
of_property_read_u32(np, "fsl,strobe-dll-delay-target",
|
||||
&boarddata->strobe_dll_delay_target);
|
||||
if (of_find_property(np, "no-1-8-v", NULL))
|
||||
if (of_property_read_bool(np, "no-1-8-v"))
|
||||
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
|
||||
|
||||
if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line))
|
||||
|
@ -41,11 +41,41 @@
|
||||
#define VENDOR_ENHANCED_STROBE BIT(0)
|
||||
|
||||
#define PHY_CLK_TOO_SLOW_HZ 400000
|
||||
#define MIN_PHY_CLK_HZ 50000000
|
||||
|
||||
#define SDHCI_ITAPDLY_CHGWIN 0x200
|
||||
#define SDHCI_ITAPDLY_ENABLE 0x100
|
||||
#define SDHCI_OTAPDLY_ENABLE 0x40
|
||||
|
||||
#define PHY_CTRL_REG1 0x270
|
||||
#define PHY_CTRL_ITAPDLY_ENA_MASK BIT(0)
|
||||
#define PHY_CTRL_ITAPDLY_SEL_MASK GENMASK(5, 1)
|
||||
#define PHY_CTRL_ITAPDLY_SEL_SHIFT 1
|
||||
#define PHY_CTRL_ITAP_CHG_WIN_MASK BIT(6)
|
||||
#define PHY_CTRL_OTAPDLY_ENA_MASK BIT(8)
|
||||
#define PHY_CTRL_OTAPDLY_SEL_MASK GENMASK(15, 12)
|
||||
#define PHY_CTRL_OTAPDLY_SEL_SHIFT 12
|
||||
#define PHY_CTRL_STRB_SEL_MASK GENMASK(23, 16)
|
||||
#define PHY_CTRL_STRB_SEL_SHIFT 16
|
||||
#define PHY_CTRL_TEST_CTRL_MASK GENMASK(31, 24)
|
||||
|
||||
#define PHY_CTRL_REG2 0x274
|
||||
#define PHY_CTRL_EN_DLL_MASK BIT(0)
|
||||
#define PHY_CTRL_DLL_RDY_MASK BIT(1)
|
||||
#define PHY_CTRL_FREQ_SEL_MASK GENMASK(6, 4)
|
||||
#define PHY_CTRL_FREQ_SEL_SHIFT 4
|
||||
#define PHY_CTRL_SEL_DLY_TX_MASK BIT(16)
|
||||
#define PHY_CTRL_SEL_DLY_RX_MASK BIT(17)
|
||||
#define FREQSEL_200M_170M 0x0
|
||||
#define FREQSEL_170M_140M 0x1
|
||||
#define FREQSEL_140M_110M 0x2
|
||||
#define FREQSEL_110M_80M 0x3
|
||||
#define FREQSEL_80M_50M 0x4
|
||||
#define FREQSEL_275M_250M 0x5
|
||||
#define FREQSEL_250M_225M 0x6
|
||||
#define FREQSEL_225M_200M 0x7
|
||||
#define PHY_DLL_TIMEOUT_MS 100
|
||||
|
||||
/* Default settings for ZynqMP Clock Phases */
|
||||
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
|
||||
#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
|
||||
@ -53,6 +83,11 @@
|
||||
#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
|
||||
#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
|
||||
|
||||
#define VERSAL_NET_EMMC_ICLK_PHASE {0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0}
|
||||
#define VERSAL_NET_EMMC_OCLK_PHASE {0, 113, 0, 0, 0, 0, 0, 0, 113, 79, 45}
|
||||
|
||||
#define VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL 0X77
|
||||
|
||||
/*
|
||||
* On some SoCs the syscon area has a feature where the upper 16-bits of
|
||||
* each 32-bit register act as a write mask for the lower 16-bits. This allows
|
||||
@ -135,6 +170,7 @@ struct sdhci_arasan_clk_data {
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
* @phy: Pointer to the generic phy
|
||||
* @is_phy_on: True if the PHY is on; false if not.
|
||||
* @internal_phy_reg: True if the PHY is within the Host controller.
|
||||
* @has_cqe: True if controller has command queuing engine.
|
||||
* @clk_data: Struct for the Arasan Controller Clock Data.
|
||||
* @clk_ops: Struct for the Arasan Controller Clock Operations.
|
||||
@ -147,6 +183,7 @@ struct sdhci_arasan_data {
|
||||
struct clk *clk_ahb;
|
||||
struct phy *phy;
|
||||
bool is_phy_on;
|
||||
bool internal_phy_reg;
|
||||
|
||||
bool has_cqe;
|
||||
struct sdhci_arasan_clk_data clk_data;
|
||||
@ -193,13 +230,6 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
|
||||
.hiword_update = false,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_soc_ctl_map thunderbay_soc_ctl_map = {
|
||||
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
||||
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
||||
.support64b = { .reg = 0x4, .width = 1, .shift = 24 },
|
||||
.hiword_update = false,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
|
||||
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
||||
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
||||
@ -207,6 +237,61 @@ static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
|
||||
.hiword_update = false,
|
||||
};
|
||||
|
||||
static void sdhci_arasan_phy_set_delaychain(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(host->ioaddr + PHY_CTRL_REG2);
|
||||
if (enable)
|
||||
reg |= (PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK);
|
||||
else
|
||||
reg &= ~(PHY_CTRL_SEL_DLY_TX_MASK | PHY_CTRL_SEL_DLY_RX_MASK);
|
||||
|
||||
writel(reg, host->ioaddr + PHY_CTRL_REG2);
|
||||
}
|
||||
|
||||
static int sdhci_arasan_phy_set_dll(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl(host->ioaddr + PHY_CTRL_REG2);
|
||||
if (enable)
|
||||
reg |= PHY_CTRL_EN_DLL_MASK;
|
||||
else
|
||||
reg &= ~PHY_CTRL_EN_DLL_MASK;
|
||||
|
||||
writel(reg, host->ioaddr + PHY_CTRL_REG2);
|
||||
|
||||
if (!enable)
|
||||
return 0;
|
||||
|
||||
return readl_relaxed_poll_timeout(host->ioaddr + PHY_CTRL_REG2, reg,
|
||||
(reg & PHY_CTRL_DLL_RDY_MASK), 10,
|
||||
1000 * PHY_DLL_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static void sdhci_arasan_phy_dll_set_freq(struct sdhci_host *host, int clock)
|
||||
{
|
||||
u32 reg, freq_sel, freq;
|
||||
|
||||
freq = DIV_ROUND_CLOSEST(clock, 1000000);
|
||||
if (freq <= 200 && freq > 170)
|
||||
freq_sel = FREQSEL_200M_170M;
|
||||
else if (freq <= 170 && freq > 140)
|
||||
freq_sel = FREQSEL_170M_140M;
|
||||
else if (freq <= 140 && freq > 110)
|
||||
freq_sel = FREQSEL_140M_110M;
|
||||
else if (freq <= 110 && freq > 80)
|
||||
freq_sel = FREQSEL_110M_80M;
|
||||
else
|
||||
freq_sel = FREQSEL_80M_50M;
|
||||
|
||||
reg = readl(host->ioaddr + PHY_CTRL_REG2);
|
||||
reg &= ~PHY_CTRL_FREQ_SEL_MASK;
|
||||
reg |= (freq_sel << PHY_CTRL_FREQ_SEL_SHIFT);
|
||||
writel(reg, host->ioaddr + PHY_CTRL_REG2);
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
|
||||
*
|
||||
@ -321,11 +406,24 @@ static void sdhci_arasan_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
}
|
||||
|
||||
/* Set the Input and Output Clock Phase Delays */
|
||||
if (clk_data->set_clk_delays)
|
||||
if (clk_data->set_clk_delays && clock > PHY_CLK_TOO_SLOW_HZ)
|
||||
clk_data->set_clk_delays(host);
|
||||
|
||||
if (sdhci_arasan->internal_phy_reg && clock >= MIN_PHY_CLK_HZ) {
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
sdhci_arasan_phy_set_dll(host, 0);
|
||||
sdhci_arasan_phy_set_delaychain(host, 0);
|
||||
sdhci_arasan_phy_dll_set_freq(host, clock);
|
||||
} else if (sdhci_arasan->internal_phy_reg) {
|
||||
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
|
||||
sdhci_arasan_phy_set_delaychain(host, 1);
|
||||
}
|
||||
|
||||
sdhci_set_clock(host, clock);
|
||||
|
||||
if (sdhci_arasan->internal_phy_reg && clock >= MIN_PHY_CLK_HZ)
|
||||
sdhci_arasan_phy_set_dll(host, 1);
|
||||
|
||||
if (sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_CLOCK_UNSTABLE)
|
||||
/*
|
||||
* Some controllers immediately report SDHCI_CLOCK_INT_STABLE
|
||||
@ -465,15 +563,6 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_thunderbay_pdata = {
|
||||
.ops = &sdhci_arasan_cqe_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* sdhci_arasan_suspend - Suspend method for the driver
|
||||
@ -897,6 +986,101 @@ static const struct clk_ops versal_sampleclk_ops = {
|
||||
.set_phase = sdhci_versal_sampleclk_set_phase,
|
||||
};
|
||||
|
||||
static int sdhci_versal_net_emmc_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
tap_max = 16;
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
/* For 200MHz clock, 32 Taps are available */
|
||||
tap_max = 32;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
if (tap_delay) {
|
||||
u32 regval;
|
||||
|
||||
regval = sdhci_readl(host, PHY_CTRL_REG1);
|
||||
regval |= PHY_CTRL_OTAPDLY_ENA_MASK;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
regval &= ~PHY_CTRL_OTAPDLY_SEL_MASK;
|
||||
regval |= tap_delay << PHY_CTRL_OTAPDLY_SEL_SHIFT;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops versal_net_sdcardclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
|
||||
.set_phase = sdhci_versal_net_emmc_sdcardclk_set_phase,
|
||||
};
|
||||
|
||||
static int sdhci_versal_net_emmc_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
u32 regval;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
tap_max = 32;
|
||||
break;
|
||||
case MMC_TIMING_MMC_HS400:
|
||||
/* Strobe select tap point for strb90 and strb180 */
|
||||
regval = sdhci_readl(host, PHY_CTRL_REG1);
|
||||
regval &= ~PHY_CTRL_STRB_SEL_MASK;
|
||||
regval |= VERSAL_NET_PHY_CTRL_STRB90_STRB180_VAL << PHY_CTRL_STRB_SEL_SHIFT;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
if (tap_delay) {
|
||||
regval = sdhci_readl(host, PHY_CTRL_REG1);
|
||||
regval |= PHY_CTRL_ITAP_CHG_WIN_MASK;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
regval |= PHY_CTRL_ITAPDLY_ENA_MASK;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
regval &= ~PHY_CTRL_ITAPDLY_SEL_MASK;
|
||||
regval |= tap_delay << PHY_CTRL_ITAPDLY_SEL_SHIFT;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
regval &= ~PHY_CTRL_ITAP_CHG_WIN_MASK;
|
||||
sdhci_writel(host, regval, PHY_CTRL_REG1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops versal_net_sampleclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
|
||||
.set_phase = sdhci_versal_net_emmc_sampleclk_set_phase,
|
||||
};
|
||||
|
||||
static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
|
||||
{
|
||||
u16 clk;
|
||||
@ -1107,7 +1291,17 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
clk_data->clk_phase_out[i] = versal_oclk_phase[i];
|
||||
}
|
||||
}
|
||||
if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) {
|
||||
u32 versal_net_iclk_phase[MMC_TIMING_MMC_HS400 + 1] =
|
||||
VERSAL_NET_EMMC_ICLK_PHASE;
|
||||
u32 versal_net_oclk_phase[MMC_TIMING_MMC_HS400 + 1] =
|
||||
VERSAL_NET_EMMC_OCLK_PHASE;
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = versal_net_iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = versal_net_oclk_phase[i];
|
||||
}
|
||||
}
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
|
||||
"clk-phase-legacy");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
|
||||
@ -1150,12 +1344,6 @@ static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_of_data sdhci_arasan_thunderbay_data = {
|
||||
.soc_ctl_map = &thunderbay_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_thunderbay_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
|
||||
.ops = &sdhci_arasan_cqe_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
@ -1225,6 +1413,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_versal_net_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = {
|
||||
.sdcardclk_ops = &zynqmp_sdcardclk_ops,
|
||||
.sampleclk_ops = &zynqmp_sampleclk_ops,
|
||||
@ -1245,6 +1441,16 @@ static struct sdhci_arasan_of_data sdhci_arasan_versal_data = {
|
||||
.clk_ops = &versal_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_clk_ops versal_net_clk_ops = {
|
||||
.sdcardclk_ops = &versal_net_sdcardclk_ops,
|
||||
.sampleclk_ops = &versal_net_sampleclk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_versal_net_data = {
|
||||
.pdata = &sdhci_arasan_versal_net_pdata,
|
||||
.clk_ops = &versal_net_clk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_keembay_emmc_data = {
|
||||
.soc_ctl_map = &intel_keembay_soc_ctl_map,
|
||||
.pdata = &sdhci_keembay_emmc_pdata,
|
||||
@ -1289,10 +1495,6 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
.compatible = "intel,keembay-sdhci-5.1-sdio",
|
||||
.data = &intel_keembay_sdio_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,thunderbay-sdhci-5.1",
|
||||
.data = &sdhci_arasan_thunderbay_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.compatible = "arasan,sdhci-8.9a",
|
||||
@ -1314,6 +1516,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
.compatible = "xlnx,versal-8.9a",
|
||||
.data = &sdhci_arasan_versal_data,
|
||||
},
|
||||
{
|
||||
.compatible = "xlnx,versal-net-emmc",
|
||||
.data = &sdhci_arasan_versal_net_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||
@ -1434,7 +1640,7 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
if (!of_find_property(np, "#clock-cells", NULL))
|
||||
if (!of_property_present(np, "#clock-cells"))
|
||||
return;
|
||||
|
||||
of_clk_del_provider(dev->of_node);
|
||||
@ -1630,6 +1836,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
int ret;
|
||||
struct device_node *node;
|
||||
struct clk *clk_xin;
|
||||
struct clk *clk_dll;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct device *dev = &pdev->dev;
|
||||
@ -1703,6 +1910,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
goto clk_dis_ahb;
|
||||
}
|
||||
|
||||
clk_dll = devm_clk_get_optional_enabled(dev, "gate");
|
||||
if (IS_ERR(clk_dll)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(clk_dll), "failed to get dll clk\n");
|
||||
goto clk_disable_all;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "xlnx,fails-without-test-cd"))
|
||||
sdhci_arasan->quirks |= SDHCI_ARASAN_QUIRK_FORCE_CDTEST;
|
||||
|
||||
@ -1716,8 +1929,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
|
||||
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
|
||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
|
||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio") ||
|
||||
of_device_is_compatible(np, "intel,thunderbay-sdhci-5.1")) {
|
||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
|
||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
||||
sdhci_arasan_update_support64b(host, 0x0);
|
||||
|
||||
@ -1781,6 +1993,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE_DCMD;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "xlnx,versal-net-emmc"))
|
||||
sdhci_arasan->internal_phy_reg = true;
|
||||
|
||||
ret = sdhci_arasan_add_host(sdhci_arasan);
|
||||
if (ret)
|
||||
goto err_add_host;
|
||||
|
@ -547,8 +547,7 @@ static int aspeed_sdc_probe(struct platform_device *pdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sdc->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sdc->regs = devm_ioremap_resource(&pdev->dev, sdc->res);
|
||||
sdc->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &sdc->res);
|
||||
if (IS_ERR(sdc->regs)) {
|
||||
ret = PTR_ERR(sdc->regs);
|
||||
goto err_clk;
|
||||
|
@ -126,6 +126,13 @@ static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
|
||||
return pltfm_host->clock;
|
||||
}
|
||||
|
||||
static unsigned int rk35xx_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_round_rate(pltfm_host->clk, ULONG_MAX);
|
||||
}
|
||||
|
||||
static void dwcmshc_check_auto_cmd23(struct mmc_host *mmc,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
@ -343,7 +350,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
|
||||
.set_clock = dwcmshc_rk3568_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.get_max_clock = rk35xx_get_max_clock,
|
||||
.reset = rk35xx_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
@ -133,6 +133,7 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The DAT[3:0] line signal levels and the CMD line signal level are
|
||||
* not compatible with standard SDHC register. The line signal levels
|
||||
@ -144,6 +145,16 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
|
||||
ret = value & 0x000fffff;
|
||||
ret |= (value >> 4) & SDHCI_DATA_LVL_MASK;
|
||||
ret |= (value << 1) & SDHCI_CMD_LVL;
|
||||
|
||||
/*
|
||||
* Some controllers have unreliable Data Line Active
|
||||
* bit for commands with busy signal. This affects
|
||||
* Command Inhibit (data) bit. Just ignore it since
|
||||
* MMC core driver has already polled card status
|
||||
* with CMD13 after any command with busy siganl.
|
||||
*/
|
||||
if (esdhc->quirk_ignore_data_inhibit)
|
||||
ret &= ~SDHCI_DATA_INHIBIT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -158,19 +169,6 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some controllers have unreliable Data Line Active
|
||||
* bit for commands with busy signal. This affects
|
||||
* Command Inhibit (data) bit. Just ignore it since
|
||||
* MMC core driver has already polled card status
|
||||
* with CMD13 after any command with busy siganl.
|
||||
*/
|
||||
if ((spec_reg == SDHCI_PRESENT_STATE) &&
|
||||
(esdhc->quirk_ignore_data_inhibit == true)) {
|
||||
ret = value & ~SDHCI_DATA_INHIBIT;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = value;
|
||||
return ret;
|
||||
}
|
||||
|
@ -339,22 +339,24 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
reg_val &= ~SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, reg_val, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
/* UnLock WP */
|
||||
pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8);
|
||||
scratch_8 &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8);
|
||||
if ((host->timing == MMC_TIMING_MMC_HS200) ||
|
||||
(host->timing == MMC_TIMING_UHS_SDR104)) {
|
||||
/* UnLock WP */
|
||||
pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8);
|
||||
scratch_8 &= 0x7f;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8);
|
||||
|
||||
/* Set pcr 0x354[16] to choose dll clock, and set the default phase */
|
||||
pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, ®_val);
|
||||
reg_val &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK);
|
||||
reg_val |= (O2_SD_SEL_DLL | O2_SD_FIX_PHASE);
|
||||
pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, reg_val);
|
||||
|
||||
/* Lock WP */
|
||||
pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8);
|
||||
scratch_8 |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8);
|
||||
/* Set pcr 0x354[16] to choose dll clock, and set the default phase */
|
||||
pci_read_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, ®_val);
|
||||
reg_val &= ~(O2_SD_SEL_DLL | O2_SD_PHASE_MASK);
|
||||
reg_val |= (O2_SD_SEL_DLL | O2_SD_FIX_PHASE);
|
||||
pci_write_config_dword(chip->pdev, O2_SD_OUTPUT_CLK_SOURCE_SWITCH, reg_val);
|
||||
|
||||
/* Lock WP */
|
||||
pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch_8);
|
||||
scratch_8 |= 0x80;
|
||||
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch_8);
|
||||
}
|
||||
/* Start clk */
|
||||
reg_val = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
reg_val |= SDHCI_CLOCK_CARD_EN;
|
||||
|
@ -54,7 +54,6 @@ static bool sdhci_wp_inverted(struct device *dev)
|
||||
#endif /* CONFIG_PPC */
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static void sdhci_get_compatibility(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
@ -72,9 +71,6 @@ static void sdhci_get_compatibility(struct platform_device *pdev)
|
||||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
}
|
||||
#else
|
||||
void sdhci_get_compatibility(struct platform_device *pdev) {}
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
void sdhci_get_property(struct platform_device *pdev)
|
||||
{
|
||||
|
@ -228,7 +228,7 @@ static struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev)
|
||||
if (!pdata)
|
||||
return NULL;
|
||||
|
||||
if (of_find_property(np, "non-removable", NULL))
|
||||
if (of_property_read_bool(np, "non-removable"))
|
||||
pdata->flags |= PXA_FLAG_CARD_PERMANENT;
|
||||
|
||||
of_property_read_u32(np, "bus-width", &bus_width);
|
||||
|
@ -437,12 +437,12 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
||||
pdata->max_width = max_width;
|
||||
|
||||
/* get the card detection method */
|
||||
if (of_get_property(node, "broken-cd", NULL)) {
|
||||
if (of_property_read_bool(node, "broken-cd")) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (of_get_property(node, "non-removable", NULL)) {
|
||||
if (of_property_read_bool(node, "non-removable")) {
|
||||
pdata->cd_type = S3C_SDHCI_CD_PERMANENT;
|
||||
return 0;
|
||||
}
|
||||
|
@ -85,6 +85,7 @@
|
||||
#define DRIVER_STRENGTH_40_OHM 0x4
|
||||
|
||||
#define CLOCK_TOO_SLOW_HZ 50000000
|
||||
#define SDHCI_AM654_AUTOSUSPEND_DELAY -1
|
||||
|
||||
/* Command Queue Host Controller Interface Base address */
|
||||
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
|
||||
@ -808,16 +809,10 @@ static int sdhci_am654_probe(struct platform_device *pdev)
|
||||
|
||||
pltfm_host->clk = clk_xin;
|
||||
|
||||
/* Clocks are enabled using pm_runtime */
|
||||
pm_runtime_enable(dev);
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
goto pm_runtime_disable;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto pm_runtime_put;
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
sdhci_am654->base = devm_regmap_init_mmio(dev, base,
|
||||
@ -825,31 +820,47 @@ static int sdhci_am654_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(sdhci_am654->base)) {
|
||||
dev_err(dev, "Failed to initialize regmap\n");
|
||||
ret = PTR_ERR(sdhci_am654->base);
|
||||
goto pm_runtime_put;
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
ret = sdhci_am654_get_of_property(pdev, sdhci_am654);
|
||||
if (ret)
|
||||
goto pm_runtime_put;
|
||||
goto err_pltfm_free;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "parsing dt failed\n");
|
||||
goto pm_runtime_put;
|
||||
goto err_pltfm_free;
|
||||
}
|
||||
|
||||
host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning;
|
||||
|
||||
pm_runtime_get_noresume(dev);
|
||||
ret = pm_runtime_set_active(dev);
|
||||
if (ret)
|
||||
goto pm_put;
|
||||
pm_runtime_enable(dev);
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
goto pm_disable;
|
||||
|
||||
ret = sdhci_am654_init(host);
|
||||
if (ret)
|
||||
goto pm_runtime_put;
|
||||
goto clk_disable;
|
||||
|
||||
/* Setting up autosuspend */
|
||||
pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_autosuspend(dev);
|
||||
return 0;
|
||||
|
||||
pm_runtime_put:
|
||||
pm_runtime_put_sync(dev);
|
||||
pm_runtime_disable:
|
||||
clk_disable:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
pm_disable:
|
||||
pm_runtime_disable(dev);
|
||||
pm_put:
|
||||
pm_runtime_put_noidle(dev);
|
||||
err_pltfm_free:
|
||||
sdhci_pltfm_free(pdev);
|
||||
return ret;
|
||||
@ -858,23 +869,127 @@ err_pltfm_free:
|
||||
static int sdhci_am654_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
sdhci_remove_host(host, true);
|
||||
ret = pm_runtime_put_sync(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sdhci_remove_host(host, true);
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
sdhci_pltfm_free(pdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int sdhci_am654_restore(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 ctl_cfg_2 = 0;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
if (sdhci_am654->flags & DLL_CALIB) {
|
||||
regmap_read(sdhci_am654->base, PHY_STAT1, &val);
|
||||
if (~val & CALDONE_MASK) {
|
||||
/* Calibrate IO lines */
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
PDB_MASK, PDB_MASK);
|
||||
ret = regmap_read_poll_timeout(sdhci_am654->base,
|
||||
PHY_STAT1, val,
|
||||
val & CALDONE_MASK,
|
||||
1, 20);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable pins by setting IO mux to 0 */
|
||||
if (sdhci_am654->flags & IOMUX_PRESENT)
|
||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1,
|
||||
IOMUX_ENABLE_MASK, 0);
|
||||
|
||||
/* Set slot type based on SD or eMMC */
|
||||
if (host->mmc->caps & MMC_CAP_NONREMOVABLE)
|
||||
ctl_cfg_2 = SLOTTYPE_EMBEDDED;
|
||||
|
||||
regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK,
|
||||
ctl_cfg_2);
|
||||
|
||||
regmap_read(sdhci_am654->base, CTL_CFG_3, &val);
|
||||
if (~val & TUNINGFORSDR50_MASK)
|
||||
/* Enable tuning for SDR50 */
|
||||
regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK,
|
||||
TUNINGFORSDR50_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_am654_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
if (host->tuning_mode != SDHCI_TUNING_MODE_3)
|
||||
mmc_retune_needed(host->mmc);
|
||||
|
||||
ret = cqhci_suspend(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sdhci_runtime_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* disable the clock */
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_am654_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
/* Enable the clock */
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sdhci_am654_restore(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sdhci_runtime_resume_host(host, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cqhci_resume(host->mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_am654_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend,
|
||||
sdhci_am654_runtime_resume, NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_am654_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-am654",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &sdhci_am654_dev_pm_ops,
|
||||
.of_match_table = sdhci_am654_of_match,
|
||||
},
|
||||
.probe = sdhci_am654_probe,
|
||||
|
@ -101,14 +101,6 @@ static inline void sdricoh_writel(struct sdricoh_host *host, unsigned int reg,
|
||||
|
||||
}
|
||||
|
||||
static inline unsigned int sdricoh_readw(struct sdricoh_host *host,
|
||||
unsigned int reg)
|
||||
{
|
||||
unsigned int value = readw(host->iobase + reg);
|
||||
dev_vdbg(host->dev, "rb %x 0x%x\n", reg, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void sdricoh_writew(struct sdricoh_host *host, unsigned int reg,
|
||||
unsigned short value)
|
||||
{
|
||||
|
@ -1084,7 +1084,7 @@ static void tmio_mmc_of_parse(struct platform_device *pdev,
|
||||
* For new platforms, please use "disable-wp" instead of
|
||||
* "toshiba,mmc-wrprotect-disable"
|
||||
*/
|
||||
if (of_get_property(np, "toshiba,mmc-wrprotect-disable", NULL))
|
||||
if (of_property_read_bool(np, "toshiba,mmc-wrprotect-disable"))
|
||||
mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
|
||||
}
|
||||
|
||||
|
@ -1790,8 +1790,7 @@ static int usdhi6_probe(struct platform_device *pdev)
|
||||
|
||||
host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(dev, res);
|
||||
host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto e_free_mmc;
|
||||
|
@ -1341,8 +1341,6 @@ static void __download_offload_pseudocode(struct vub300_mmc_host *vub300,
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
copy_error_message:
|
||||
strncpy(vub300->vub_name, "SDIO pseudocode download failed",
|
||||
sizeof(vub300->vub_name));
|
||||
|
@ -802,10 +802,8 @@ static int wmt_mci_probe(struct platform_device *pdev)
|
||||
priv->power_inverted = 0;
|
||||
priv->cd_inverted = 0;
|
||||
|
||||
if (of_get_property(np, "sdon-inverted", NULL))
|
||||
priv->power_inverted = 1;
|
||||
if (of_get_property(np, "cd-inverted", NULL))
|
||||
priv->cd_inverted = 1;
|
||||
priv->power_inverted = of_property_read_bool(np, "sdon-inverted");
|
||||
priv->cd_inverted = of_property_read_bool(np, "cd-inverted");
|
||||
|
||||
priv->sdmmc_base = of_iomap(np, 0);
|
||||
if (!priv->sdmmc_base) {
|
||||
|
@ -450,6 +450,7 @@ struct mmc_host {
|
||||
unsigned int retune_paused:1; /* re-tuning is temporarily disabled */
|
||||
unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */
|
||||
unsigned int can_dma_map_merge:1; /* merging can be used */
|
||||
unsigned int vqmmc_enabled:1; /* vqmmc regulator is enabled */
|
||||
|
||||
int rescan_disable; /* disable card detection */
|
||||
int rescan_entered; /* used with nonremovable devices */
|
||||
@ -598,6 +599,8 @@ static inline int mmc_regulator_set_vqmmc(struct mmc_host *mmc,
|
||||
#endif
|
||||
|
||||
int mmc_regulator_get_supply(struct mmc_host *mmc);
|
||||
int mmc_regulator_enable_vqmmc(struct mmc_host *mmc);
|
||||
void mmc_regulator_disable_vqmmc(struct mmc_host *mmc);
|
||||
|
||||
static inline int mmc_card_is_removable(struct mmc_host *host)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user