From db93caa6a4cf467c90ec11c438f776fc37050c00 Mon Sep 17 00:00:00 2001 From: Shan-Chun Hung Date: Tue, 16 Jul 2024 08:45:26 +0800 Subject: [PATCH 01/41] dt-bindings: mmc: nuvoton,ma35d1-sdhci: Document MA35D1 SDHCI controller Add binding for Nuvoton MA35D1 SDHCI controller. Signed-off-by: Shan-Chun Hung Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240716004527.20378-2-shanchun1218@gmail.com Signed-off-by: Ulf Hansson --- .../bindings/mmc/nuvoton,ma35d1-sdhci.yaml | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/nuvoton,ma35d1-sdhci.yaml diff --git a/Documentation/devicetree/bindings/mmc/nuvoton,ma35d1-sdhci.yaml b/Documentation/devicetree/bindings/mmc/nuvoton,ma35d1-sdhci.yaml new file mode 100644 index 000000000000..4d787147c300 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/nuvoton,ma35d1-sdhci.yaml @@ -0,0 +1,87 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/nuvoton,ma35d1-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Nuvoton MA35D1 SD/SDIO/MMC Controller + +maintainers: + - Shan-Chun Hung + +allOf: + - $ref: sdhci-common.yaml# + +properties: + compatible: + enum: + - nuvoton,ma35d1-sdhci + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: state_uhs + + pinctrl-0: + description: + Should contain default/high speed pin ctrl. + maxItems: 1 + + pinctrl-1: + description: + Should contain uhs mode pin ctrl. + maxItems: 1 + + resets: + maxItems: 1 + + nuvoton,sys: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle to access GCR (Global Control Register) registers. + +required: + - compatible + - reg + - interrupts + - clocks + - pinctrl-names + - pinctrl-0 + - resets + - nuvoton,sys + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + mmc@40190000 { + compatible = "nuvoton,ma35d1-sdhci"; + reg = <0x0 0x40190000 0x0 0x2000>; + interrupts = ; + clocks = <&clk SDH1_GATE>; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&pinctrl_sdhci1>; + pinctrl-1 = <&pinctrl_sdhci1_uhs>; + resets = <&sys MA35D1_RESET_SDH1>; + nuvoton,sys = <&sys>; + vqmmc-supply = <&sdhci1_vqmmc_regulator>; + bus-width = <8>; + max-frequency = <200000000>; + }; + }; From addc9ecb9ddb3fe77be3979cce021ad07442c1fc Mon Sep 17 00:00:00 2001 From: Shan-Chun Hung Date: Tue, 16 Jul 2024 08:45:27 +0800 Subject: [PATCH 02/41] mmc: sdhci-of-ma35d1: Add Nuvoton MA35D1 SDHCI driver Add the SDHCI driver for the MA35D1 platform. It is based upon the SDHCI interface, but requires some extra initialization. Signed-off-by: Shan-Chun Hung Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240716004527.20378-3-shanchun1218@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 12 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-ma35d1.c | 314 +++++++++++++++++++++++++++++ 3 files changed, 327 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-ma35d1.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index eb3ecfe05591..7199cb0bd0b9 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -252,6 +252,18 @@ config MMC_SDHCI_OF_SPARX5 If unsure, say N. +config MMC_SDHCI_OF_MA35D1 + tristate "SDHCI OF support for the MA35D1 SDHCI controller" + depends on ARCH_MA35 || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + help + This selects the MA35D1 Secure Digital Host Controller Interface. + The controller supports SD/MMC/SDIO devices. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_CADENCE tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index f53f86d200ac..3ccffebbe59b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o +obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o obj-$(CONFIG_MMC_SDHCI_NPCM) += sdhci-npcm.o diff --git a/drivers/mmc/host/sdhci-of-ma35d1.c b/drivers/mmc/host/sdhci-of-ma35d1.c new file mode 100644 index 000000000000..b84c2927bd4a --- /dev/null +++ b/drivers/mmc/host/sdhci-of-ma35d1.c @@ -0,0 +1,314 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2024 Nuvoton Technology Corp. + * + * Author: Shan-Chun Hung + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdhci-pltfm.h" +#include "sdhci.h" + +#define MA35_SYS_MISCFCR0 0x070 +#define MA35_SDHCI_MSHCCTL 0x508 +#define MA35_SDHCI_MBIUCTL 0x510 + +#define MA35_SDHCI_CMD_CONFLICT_CHK BIT(0) +#define MA35_SDHCI_INCR_MSK GENMASK(3, 0) +#define MA35_SDHCI_INCR16 BIT(3) +#define MA35_SDHCI_INCR8 BIT(2) + +struct ma35_priv { + struct reset_control *rst; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_uhs; + struct pinctrl_state *pins_default; +}; + +struct ma35_restore_data { + u32 reg; + u32 width; +}; + +static const struct ma35_restore_data restore_data[] = { + { SDHCI_CLOCK_CONTROL, sizeof(u32)}, + { SDHCI_BLOCK_SIZE, sizeof(u32)}, + { SDHCI_INT_ENABLE, sizeof(u32)}, + { SDHCI_SIGNAL_ENABLE, sizeof(u32)}, + { SDHCI_AUTO_CMD_STATUS, sizeof(u32)}, + { SDHCI_HOST_CONTROL, sizeof(u32)}, + { SDHCI_TIMEOUT_CONTROL, sizeof(u8) }, + { MA35_SDHCI_MSHCCTL, sizeof(u16)}, + { MA35_SDHCI_MBIUCTL, sizeof(u16)}, +}; + +/* + * If DMA addr spans 128MB boundary, we split the DMA transfer into two + * so that each DMA transfer doesn't exceed the boundary. + */ +static void ma35_adma_write_desc(struct sdhci_host *host, void **desc, dma_addr_t addr, int len, + unsigned int cmd) +{ + int tmplen, offset; + + if (likely(!len || (ALIGN(addr, SZ_128M) == ALIGN(addr + len - 1, SZ_128M)))) { + sdhci_adma_write_desc(host, desc, addr, len, cmd); + return; + } + + offset = addr & (SZ_128M - 1); + tmplen = SZ_128M - offset; + sdhci_adma_write_desc(host, desc, addr, tmplen, cmd); + + addr += tmplen; + len -= tmplen; + sdhci_adma_write_desc(host, desc, addr, len, cmd); +} + +static void ma35_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u32 ctl; + + /* + * If the clock frequency exceeds MMC_HIGH_52_MAX_DTR, + * disable command conflict check. + */ + ctl = sdhci_readw(host, MA35_SDHCI_MSHCCTL); + if (clock > MMC_HIGH_52_MAX_DTR) + ctl &= ~MA35_SDHCI_CMD_CONFLICT_CHK; + else + ctl |= MA35_SDHCI_CMD_CONFLICT_CHK; + sdhci_writew(host, ctl, MA35_SDHCI_MSHCCTL); + + sdhci_set_clock(host, clock); +} + +static int ma35_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct ma35_priv *priv = sdhci_pltfm_priv(pltfm_host); + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_180: + if (!IS_ERR(priv->pinctrl) && !IS_ERR(priv->pins_uhs)) + pinctrl_select_state(priv->pinctrl, priv->pins_uhs); + break; + case MMC_SIGNAL_VOLTAGE_330: + if (!IS_ERR(priv->pinctrl) && !IS_ERR(priv->pins_default)) + pinctrl_select_state(priv->pinctrl, priv->pins_default); + break; + default: + dev_err(mmc_dev(host->mmc), "Unsupported signal voltage!\n"); + return -EINVAL; + } + + return sdhci_start_signal_voltage_switch(mmc, ios); +} + +static void ma35_voltage_switch(struct sdhci_host *host) +{ + /* Wait for 5ms after set 1.8V signal enable bit */ + fsleep(5000); +} + +static int ma35_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct ma35_priv *priv = sdhci_pltfm_priv(pltfm_host); + int idx; + u32 regs[ARRAY_SIZE(restore_data)] = {}; + + /* + * Limitations require a reset of SD/eMMC before tuning and + * saving the registers before resetting, then restoring + * after the reset. + */ + for (idx = 0; idx < ARRAY_SIZE(restore_data); idx++) { + if (restore_data[idx].width == sizeof(u32)) + regs[idx] = sdhci_readl(host, restore_data[idx].reg); + else if (restore_data[idx].width == sizeof(u16)) + regs[idx] = sdhci_readw(host, restore_data[idx].reg); + else if (restore_data[idx].width == sizeof(u8)) + regs[idx] = sdhci_readb(host, restore_data[idx].reg); + } + + reset_control_assert(priv->rst); + reset_control_deassert(priv->rst); + + for (idx = 0; idx < ARRAY_SIZE(restore_data); idx++) { + if (restore_data[idx].width == sizeof(u32)) + sdhci_writel(host, regs[idx], restore_data[idx].reg); + else if (restore_data[idx].width == sizeof(u16)) + sdhci_writew(host, regs[idx], restore_data[idx].reg); + else if (restore_data[idx].width == sizeof(u8)) + sdhci_writeb(host, regs[idx], restore_data[idx].reg); + } + + return sdhci_execute_tuning(mmc, opcode); +} + +static const struct sdhci_ops sdhci_ma35_ops = { + .set_clock = ma35_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = sdhci_reset, + .adma_write_desc = ma35_adma_write_desc, + .voltage_switch = ma35_voltage_switch, +}; + +static const struct sdhci_pltfm_data sdhci_ma35_pdata = { + .ops = &sdhci_ma35_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_BROKEN_DDR50 | + SDHCI_QUIRK2_ACMD23_BROKEN, +}; + +static int ma35_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct ma35_priv *priv; + int err; + u32 extra, ctl; + + host = sdhci_pltfm_init(pdev, &sdhci_ma35_pdata, sizeof(struct ma35_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + /* Extra adma table cnt for cross 128M boundary handling. */ + extra = DIV_ROUND_UP_ULL(dma_get_required_mask(dev), SZ_128M); + extra = min(extra, SDHCI_MAX_SEGS); + + host->adma_table_cnt += extra; + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); + + pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(pltfm_host->clk)) { + err = dev_err_probe(dev, PTR_ERR(pltfm_host->clk), "failed to get clk\n"); + goto err_sdhci; + } + + err = mmc_of_parse(host->mmc); + if (err) + goto err_sdhci; + + priv->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(priv->rst)) { + err = dev_err_probe(dev, PTR_ERR(priv->rst), "failed to get reset control\n"); + goto err_sdhci; + } + + sdhci_get_of_property(pdev); + + priv->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(priv->pinctrl)) { + priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default"); + priv->pins_uhs = pinctrl_lookup_state(priv->pinctrl, "state_uhs"); + pinctrl_select_state(priv->pinctrl, priv->pins_default); + } + + if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)) { + struct regmap *regmap; + u32 reg; + + regmap = syscon_regmap_lookup_by_phandle(dev_of_node(dev), "nuvoton,sys"); + if (!IS_ERR(regmap)) { + /* Enable SDHCI voltage stable for 1.8V */ + regmap_read(regmap, MA35_SYS_MISCFCR0, ®); + reg |= BIT(17); + regmap_write(regmap, MA35_SYS_MISCFCR0, reg); + } + + host->mmc_host_ops.start_signal_voltage_switch = + ma35_start_signal_voltage_switch; + } + + host->mmc_host_ops.execute_tuning = ma35_execute_tuning; + + err = sdhci_add_host(host); + if (err) + goto err_sdhci; + + /* + * Split data into chunks of 16 or 8 bytes for transmission. + * Each chunk transfer is guaranteed to be uninterrupted on the bus. + * This likely corresponds to the AHB bus DMA burst size. + */ + ctl = sdhci_readw(host, MA35_SDHCI_MBIUCTL); + ctl &= ~MA35_SDHCI_INCR_MSK; + ctl |= MA35_SDHCI_INCR16 | MA35_SDHCI_INCR8; + sdhci_writew(host, ctl, MA35_SDHCI_MBIUCTL); + + return 0; + +err_sdhci: + sdhci_pltfm_free(pdev); + return err; +} + +static void ma35_disable_card_clk(struct sdhci_host *host) +{ + u16 ctrl; + + ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (ctrl & SDHCI_CLOCK_CARD_EN) { + ctrl &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); + } +} + +static void ma35_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + + sdhci_remove_host(host, 0); + ma35_disable_card_clk(host); + sdhci_pltfm_free(pdev); +} + +static const struct of_device_id sdhci_ma35_dt_ids[] = { + { .compatible = "nuvoton,ma35d1-sdhci" }, + {} +}; + +static struct platform_driver sdhci_ma35_driver = { + .driver = { + .name = "sdhci-ma35", + .of_match_table = sdhci_ma35_dt_ids, + }, + .probe = ma35_probe, + .remove_new = ma35_remove, +}; +module_platform_driver(sdhci_ma35_driver); + +MODULE_DESCRIPTION("SDHCI platform driver for Nuvoton MA35"); +MODULE_AUTHOR("Shan-Chun Hung "); +MODULE_LICENSE("GPL"); From 3af5a1e7be84e0a5d6635dbedb13bd1ce4161414 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Sun, 14 Jul 2024 08:55:11 -0700 Subject: [PATCH 03/41] mmc: sdhci-pxav2: Remove unnecessary null pointer check There is no need to check for a null mrq->cmd in pxav1_request_done. mmc_request_done already assumes it's not null, and it's always called in this path by every SDHCI driver. This was caught by Smatch. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/9ddaef2a-05bb-4fe7-98c5-da40a0813027@stanley.mountain/ Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240714155510.48880-1-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index b75cbea88b40..7b957f6d5588 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -126,7 +126,7 @@ static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq) struct sdhci_pxav2_host *pxav2_host; /* If this is an SDIO command, perform errata workaround for silicon bug */ - if (mrq->cmd && !mrq->cmd->error && + if (!mrq->cmd->error && (mrq->cmd->opcode == SD_IO_RW_DIRECT || mrq->cmd->opcode == SD_IO_RW_EXTENDED)) { /* Reset data port */ From 24a9ea1c0fdc2cab2dda08e3370880b7d9a3359e Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 24 Jul 2024 19:21:17 +0100 Subject: [PATCH 04/41] dt-bindings: mmc: renesas,sdhi: Document RZ/V2H(P) support The SD/MMC block on the RZ/V2H(P) ("R9A09G057") SoC is similar to that of the R-Car Gen3, but it has some differences: - HS400 is not supported. - It has additional SD_STATUS register to control voltage, power enable and reset. - It supports fixed address mode. To accommodate these differences, a SoC-specific 'renesas,sdhi-r9a09g057' compatible string is added. Signed-off-by: Lad Prabhakar Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240724182119.652080-2-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 3d0e61e59856..1155b1d79df5 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -18,6 +18,7 @@ properties: - renesas,sdhi-r7s9210 # SH-Mobile AG5 - renesas,sdhi-r8a73a4 # R-Mobile APE6 - renesas,sdhi-r8a7740 # R-Mobile A1 + - renesas,sdhi-r9a09g057 # RZ/V2H(P) - renesas,sdhi-sh73a0 # R-Mobile APE6 - items: - enum: @@ -66,6 +67,7 @@ properties: - renesas,sdhi-r9a07g054 # RZ/V2L - renesas,sdhi-r9a08g045 # RZ/G3S - renesas,sdhi-r9a09g011 # RZ/V2M + - renesas,sdhi-r9a09g057 # RZ/V2H(P) - const: renesas,rzg2l-sdhi reg: From a091f510af0b55617b6abf1989837fb8fe86257a Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 24 Jul 2024 19:21:18 +0100 Subject: [PATCH 05/41] mmc: tmio: Use MMC core APIs to control the vqmmc regulator Use the mmc_regulator_enable_vqmmc() and mmc_regulator_disable_vqmmc() APIs to enable/disable the vqmmc regulator. Signed-off-by: Lad Prabhakar Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Tested-by: Claudiu Beznea # on RZ/G3S Link: https://lore.kernel.org/r/20240724182119.652080-3-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index b359876cc33d..45a474ccab1c 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -895,8 +895,8 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) * It seems, VccQ should be switched on after Vcc, this is also what the * omap_hsmmc.c driver does. */ - if (!IS_ERR(mmc->supply.vqmmc) && !ret) { - ret = regulator_enable(mmc->supply.vqmmc); + if (!ret) { + ret = mmc_regulator_enable_vqmmc(mmc); usleep_range(200, 300); } @@ -909,8 +909,7 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host) { struct mmc_host *mmc = host->mmc; - if (!IS_ERR(mmc->supply.vqmmc)) - regulator_disable(mmc->supply.vqmmc); + mmc_regulator_disable_vqmmc(mmc); if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); From b6db8f1fb415afd4f7773668c8576ad591ed32eb Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Wed, 24 Jul 2024 19:21:19 +0100 Subject: [PATCH 06/41] mmc: renesas_sdhi: Add RZ/V2H(P) compatible string The SD/MMC block on the RZ/V2H(P) ("R9A09G057") SoC is similar to that of the R-Car Gen3, but it has some differences: - HS400 is not supported. - It has additional SD_STATUS register to control voltage, power enable and reset. - It supports fixed address mode. To accommodate these differences, a SoC-specific 'renesas,sdhi-r9a09g057' compatible string is added. Note for RZ/V2H(P), we are using the `of_rzg2l_compatible` OF data as it already handles no HS400 and fixed address mode support. Since the SDxIOVS and SDxPWEN pins can always be used as GPIO pins on the RZ/V2H(P) SoC, no driver changes are done to control the SD_STATUS register. Signed-off-by: Lad Prabhakar Link: https://lore.kernel.org/r/20240724182119.652080-4-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index caf1d2e23343..1dcaa050f264 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -285,6 +285,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, { .compatible = "renesas,sdhi-r9a09g011", .data = &of_rzg2l_compatible, }, + { .compatible = "renesas,sdhi-r9a09g057", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rzg2l-sdhi", .data = &of_rzg2l_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, From 977849b2d46da3ac3e57e46791f2be3eb48df0b6 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:17:21 +0800 Subject: [PATCH 07/41] mmc: sdhci-of-dwcmshc: add common bulk optional clocks support In addition to the required core clock and optional bus clock, the soc will expand its own clocks, so the bulk clock mechanism is abstracted. Note, I call the bulk clocks as "other clocks" due to the bus clock has been called as "optional". Signed-off-by: Chen Wang Tested-by: Drew Fustini # TH1520 Tested-by: Inochi Amaoto # Duo and Huashan Pi Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/e57e8c51da81f176b49608269a884f840903e78e.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 90 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 42 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index e79aa4b3b6c3..35401616fb2e 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -108,7 +108,6 @@ #define DLL_LOCK_WO_TMOUT(x) \ ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) -#define RK35xx_MAX_CLKS 3 /* PHY register area pointer */ #define DWC_MSHC_PTR_PHY_R 0x300 @@ -199,23 +198,54 @@ enum dwcmshc_rk_type { }; struct rk35xx_priv { - /* Rockchip specified optional clocks */ - struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS]; struct reset_control *reset; enum dwcmshc_rk_type devtype; u8 txclk_tapnum; }; +#define DWCMSHC_MAX_OTHER_CLKS 3 + struct dwcmshc_priv { struct clk *bus_clk; int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */ int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */ + int num_other_clks; + struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS]; + void *priv; /* pointer to SoC private stuff */ u16 delay_line; u16 flags; }; +static int dwcmshc_get_enable_other_clks(struct device *dev, + struct dwcmshc_priv *priv, + int num_clks, + const char * const clk_ids[]) +{ + int err; + + if (num_clks > DWCMSHC_MAX_OTHER_CLKS) + return -EINVAL; + + for (int i = 0; i < num_clks; i++) + priv->other_clks[i].id = clk_ids[i]; + + err = devm_clk_bulk_get_optional(dev, num_clks, priv->other_clks); + if (err) { + dev_err(dev, "failed to get clocks %d\n", err); + return err; + } + + err = clk_bulk_prepare_enable(num_clks, priv->other_clks); + if (err) + dev_err(dev, "failed to enable clocks %d\n", err); + + priv->num_other_clks = num_clks; + + return err; +} + /* * If DMA addr spans 128MB boundary, we split the DMA transfer into two * so that each DMA transfer doesn't exceed the boundary. @@ -1036,8 +1066,9 @@ dsbl_cqe_caps: static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { - int err; + static const char * const clk_ids[] = {"axi", "block", "timer"}; struct rk35xx_priv *priv = dwc_priv->priv; + int err; priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); if (IS_ERR(priv->reset)) { @@ -1046,21 +1077,10 @@ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc return err; } - priv->rockchip_clks[0].id = "axi"; - priv->rockchip_clks[1].id = "block"; - priv->rockchip_clks[2].id = "timer"; - err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS, - priv->rockchip_clks); - if (err) { - dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err); + err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (err) return err; - } - - err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks); - if (err) { - dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err); - return err; - } if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum", &priv->txclk_tapnum)) @@ -1280,9 +1300,7 @@ err_rpm: err_clk: clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); - if (rk_priv) - clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, - rk_priv->rockchip_clks); + clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); free_pltfm: sdhci_pltfm_free(pdev); return err; @@ -1304,7 +1322,6 @@ static void dwcmshc_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk35xx_priv *rk_priv = priv->priv; pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1316,9 +1333,7 @@ static void dwcmshc_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); - if (rk_priv) - clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, - rk_priv->rockchip_clks); + clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); sdhci_pltfm_free(pdev); } @@ -1328,7 +1343,6 @@ static int dwcmshc_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk35xx_priv *rk_priv = priv->priv; int ret; pm_runtime_resume(dev); @@ -1347,9 +1361,7 @@ static int dwcmshc_suspend(struct device *dev) if (!IS_ERR(priv->bus_clk)) clk_disable_unprepare(priv->bus_clk); - if (rk_priv) - clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, - rk_priv->rockchip_clks); + clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); return ret; } @@ -1359,7 +1371,6 @@ static int dwcmshc_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk35xx_priv *rk_priv = priv->priv; int ret; ret = clk_prepare_enable(pltfm_host->clk); @@ -1372,29 +1383,24 @@ static int dwcmshc_resume(struct device *dev) goto disable_clk; } - if (rk_priv) { - ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, - rk_priv->rockchip_clks); - if (ret) - goto disable_bus_clk; - } + ret = clk_bulk_prepare_enable(priv->num_other_clks, priv->other_clks); + if (ret) + goto disable_bus_clk; ret = sdhci_resume_host(host); if (ret) - goto disable_rockchip_clks; + goto disable_other_clks; if (host->mmc->caps2 & MMC_CAP2_CQE) { ret = cqhci_resume(host->mmc); if (ret) - goto disable_rockchip_clks; + goto disable_other_clks; } return 0; -disable_rockchip_clks: - if (rk_priv) - clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, - rk_priv->rockchip_clks); +disable_other_clks: + clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); disable_bus_clk: if (!IS_ERR(priv->bus_clk)) clk_disable_unprepare(priv->bus_clk); From 2b857745498fdb341b684aa399b05dfffec35501 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:17:40 +0800 Subject: [PATCH 08/41] mmc: sdhci-of-dwcmshc: move two rk35xx functions This patch just move dwcmshc_rk35xx_init() and dwcmshc_rk35xx_postinit() to put the functions of rk35xx together as much as possible. This change is an intermediate process before further modification. Signed-off-by: Chen Wang Tested-by: Drew Fustini # TH1520 Tested-by: Inochi Amaoto # Duo and Huashan Pi Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/54204702d5febd3e867eb3544c36919fe4140a88.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 90 ++++++++++++++--------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 35401616fb2e..a002636d51fd 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -711,6 +711,51 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) sdhci_reset(host, mask); } +static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + static const char * const clk_ids[] = {"axi", "block", "timer"}; + struct rk35xx_priv *priv = dwc_priv->priv; + int err; + + priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); + if (IS_ERR(priv->reset)) { + err = PTR_ERR(priv->reset); + dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err); + return err; + } + + err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (err) + return err; + + if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum", + &priv->txclk_tapnum)) + priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; + + /* Disable cmd conflict check */ + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + /* Reset previous settings */ + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); + + return 0; +} + +static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + /* + * Don't support highspeed bus mode with low clk speed as we + * cannot use DLL for this condition. + */ + if (host->mmc->f_max <= 52000000) { + dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n", + host->mmc->f_max); + host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400); + host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR); + } +} + static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1064,51 +1109,6 @@ dsbl_cqe_caps: host->mmc->caps2 &= ~(MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD); } -static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) -{ - static const char * const clk_ids[] = {"axi", "block", "timer"}; - struct rk35xx_priv *priv = dwc_priv->priv; - int err; - - priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); - if (IS_ERR(priv->reset)) { - err = PTR_ERR(priv->reset); - dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err); - return err; - } - - err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, - ARRAY_SIZE(clk_ids), clk_ids); - if (err) - return err; - - if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum", - &priv->txclk_tapnum)) - priv->txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; - - /* Disable cmd conflict check */ - sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); - /* Reset previous settings */ - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); - - return 0; -} - -static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) -{ - /* - * Don't support highspeed bus mode with low clk speed as we - * cannot use DLL for this condition. - */ - if (host->mmc->f_max <= 52000000) { - dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n", - host->mmc->f_max); - host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400); - host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR); - } -} - static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { { .compatible = "rockchip,rk3588-dwcmshc", From 76610189b281cad900dafb9320161168e5761c14 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:17:59 +0800 Subject: [PATCH 09/41] mmc: sdhci-of-dwcmshc: factor out code for th1520_init() Different socs have initialization operations in the probe process, which are summarized as functions. This patch first factor out init function for th1520. Signed-off-by: Chen Wang Reviewed-by: Drew Fustini Tested-by: Drew Fustini # TH1520 Tested-by: Inochi Amaoto # Duo and Huashan Pi Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/23c6a81052a6dd3660d60348731229d60a209b32.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 51 +++++++++++++++++------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a002636d51fd..b272ec2ab232 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -830,6 +830,35 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask) } } +static int th1520_init(struct device *dev, + struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + dwc_priv->delay_line = PHY_SDCLKDL_DC_DEFAULT; + + if (device_property_read_bool(dev, "mmc-ddr-1_8v") || + device_property_read_bool(dev, "mmc-hs200-1_8v") || + device_property_read_bool(dev, "mmc-hs400-1_8v")) + dwc_priv->flags |= FLAG_IO_FIXED_1V8; + else + dwc_priv->flags &= ~FLAG_IO_FIXED_1V8; + + /* + * start_signal_voltage_switch() will try 3.3V first + * then 1.8V. Use SDHCI_SIGNALING_180 rather than + * SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V + * in sdhci_start_signal_voltage_switch(). + */ + if (dwc_priv->flags & FLAG_IO_FIXED_1V8) { + host->flags &= ~SDHCI_SIGNALING_330; + host->flags |= SDHCI_SIGNALING_180; + } + + sdhci_enable_v4_mode(host); + + return 0; +} + static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1231,27 +1260,7 @@ static int dwcmshc_probe(struct platform_device *pdev) } if (pltfm_data == &sdhci_dwcmshc_th1520_pdata) { - priv->delay_line = PHY_SDCLKDL_DC_DEFAULT; - - if (device_property_read_bool(dev, "mmc-ddr-1_8v") || - device_property_read_bool(dev, "mmc-hs200-1_8v") || - device_property_read_bool(dev, "mmc-hs400-1_8v")) - priv->flags |= FLAG_IO_FIXED_1V8; - else - priv->flags &= ~FLAG_IO_FIXED_1V8; - - /* - * start_signal_voltage_switch() will try 3.3V first - * then 1.8V. Use SDHCI_SIGNALING_180 rather than - * SDHCI_SIGNALING_330 to avoid setting voltage to 3.3V - * in sdhci_start_signal_voltage_switch(). - */ - if (priv->flags & FLAG_IO_FIXED_1V8) { - host->flags &= ~SDHCI_SIGNALING_330; - host->flags |= SDHCI_SIGNALING_180; - } - - sdhci_enable_v4_mode(host); + th1520_init(dev, host, priv); } #ifdef CONFIG_ACPI From 9676a7ef2cf5feb757fb08a712d0f06dcb824d24 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:18:19 +0800 Subject: [PATCH 10/41] mmc: sdhci-of-dwcmshc: factor out code into dwcmshc_rk35xx_init Continue factor out code fron probe into dwcmshc_rk35xx_init. Signed-off-by: Chen Wang Tested-by: Drew Fustini # TH1520 Tested-by: Inochi Amaoto # Duo and Huashan Pi Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/4f1f2fa403ce7f0b4d79afb7d7e8a1690cde5d6c.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index b272ec2ab232..55fba5ef37ba 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -711,12 +711,22 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) sdhci_reset(host, mask); } -static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) { static const char * const clk_ids[] = {"axi", "block", "timer"}; - struct rk35xx_priv *priv = dwc_priv->priv; + struct rk35xx_priv *priv; int err; + priv = devm_kzalloc(dev, sizeof(struct rk35xx_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc")) + priv->devtype = DWCMSHC_RK3588; + else + priv->devtype = DWCMSHC_RK3568; + priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); if (IS_ERR(priv->reset)) { err = PTR_ERR(priv->reset); @@ -739,6 +749,8 @@ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_STRBIN); + dwc_priv->priv = priv; + return 0; } @@ -1184,7 +1196,6 @@ static int dwcmshc_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; struct dwcmshc_priv *priv; - struct rk35xx_priv *rk_priv = NULL; const struct sdhci_pltfm_data *pltfm_data; int err; u32 extra, caps; @@ -1241,20 +1252,7 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) { - rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); - if (!rk_priv) { - err = -ENOMEM; - goto err_clk; - } - - if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc")) - rk_priv->devtype = DWCMSHC_RK3588; - else - rk_priv->devtype = DWCMSHC_RK3568; - - priv->priv = rk_priv; - - err = dwcmshc_rk35xx_init(host, priv); + err = dwcmshc_rk35xx_init(dev, host, priv); if (err) goto err_clk; } @@ -1290,7 +1288,7 @@ static int dwcmshc_probe(struct platform_device *pdev) dwcmshc_cqhci_init(host, pdev); } - if (rk_priv) + if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) dwcmshc_rk35xx_postinit(host, priv); err = __sdhci_add_host(host); From a2e34ac156a0ff7bc0b22ea21a92b411636b7b50 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:18:43 +0800 Subject: [PATCH 11/41] mmc: sdhci-of-dwcmshc: add dwcmshc_pltfm_data Abstract dwcmshc_pltfm_data to hold the sdhci_pltfm_data plus some comoon operations of soc such as init/postinit. Signed-off-by: Chen Wang Tested-by: Drew Fustini # TH1520 Tested-by: Inochi Amaoto # Duo and Huashan Pi Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/cb2c68c594286e9588c53acb76163e60c140c02b.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 81 +++++++++++++++++------------ 1 file changed, 48 insertions(+), 33 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 55fba5ef37ba..16f420994519 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -218,6 +218,12 @@ struct dwcmshc_priv { u16 flags; }; +struct dwcmshc_pltfm_data { + const struct sdhci_pltfm_data pdata; + int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); + void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); +}; + static int dwcmshc_get_enable_other_clks(struct device *dev, struct dwcmshc_priv *priv, int num_clks, @@ -1048,39 +1054,52 @@ static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = { .platform_execute_tuning = cv18xx_sdhci_execute_tuning, }; -static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { - .ops = &sdhci_dwcmshc_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, }; #ifdef CONFIG_ACPI -static const struct sdhci_pltfm_data sdhci_dwcmshc_bf3_pdata = { - .ops = &sdhci_dwcmshc_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_ACMD23_BROKEN, +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_ACMD23_BROKEN, + }, }; #endif -static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { - .ops = &sdhci_dwcmshc_rk35xx_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, }; -static const struct sdhci_pltfm_data sdhci_dwcmshc_th1520_pdata = { - .ops = &sdhci_dwcmshc_th1520_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_th1520_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, + .init = th1520_init, }; -static const struct sdhci_pltfm_data sdhci_dwcmshc_cv18xx_pdata = { - .ops = &sdhci_dwcmshc_cv18xx_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_cv18xx_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_cv18xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, }; static const struct cqhci_host_ops dwcmshc_cqhci_ops = { @@ -1196,7 +1215,7 @@ static int dwcmshc_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; struct dwcmshc_priv *priv; - const struct sdhci_pltfm_data *pltfm_data; + const struct dwcmshc_pltfm_data *pltfm_data; int err; u32 extra, caps; @@ -1206,7 +1225,7 @@ static int dwcmshc_probe(struct platform_device *pdev) return -ENODEV; } - host = sdhci_pltfm_init(pdev, pltfm_data, + host = sdhci_pltfm_init(pdev, &pltfm_data->pdata, sizeof(struct dwcmshc_priv)); if (IS_ERR(host)) return PTR_ERR(host); @@ -1251,16 +1270,12 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning; - if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) { - err = dwcmshc_rk35xx_init(dev, host, priv); + if (pltfm_data->init) { + err = pltfm_data->init(&pdev->dev, host, priv); if (err) goto err_clk; } - if (pltfm_data == &sdhci_dwcmshc_th1520_pdata) { - th1520_init(dev, host, priv); - } - #ifdef CONFIG_ACPI if (pltfm_data == &sdhci_dwcmshc_bf3_pdata) sdhci_enable_v4_mode(host); @@ -1288,8 +1303,8 @@ static int dwcmshc_probe(struct platform_device *pdev) dwcmshc_cqhci_init(host, pdev); } - if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) - dwcmshc_rk35xx_postinit(host, priv); + if (pltfm_data->postinit) + pltfm_data->postinit(host, priv); err = __sdhci_add_host(host); if (err) From fc7b91683edbba4eab6c454f89e41aa56ea614e2 Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:19:04 +0800 Subject: [PATCH 12/41] dt-bindings: mmc: sdhci-of-dwcmhsc: Add Sophgo SG2042 support SG2042 use Synopsys dwcnshc IP for SD/eMMC controllers. SG2042 defines 3 clocks for SD/eMMC controllers. - EMMC_100M/SD_100M for cclk(Card clocks in DWC_mshc), so reuse existing "core". - AXI_EMMC/AXI_SD for aclk/hclk(Bus interface clocks in DWC_mshc) and blck(Core Base Clock in DWC_mshc), these 3 clocks share one source, so reuse existing "bus". - 100K_EMMC/100K_SD for cqetmclk(Timer clocks in DWC_mshc), so reuse existing "timer" which was added for rockchip specified. Signed-off-by: Chen Wang Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/9ca450097e5389a38bcd7d8ddf863766df4cea10.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 60 +++++++++++++------ 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 4d3031d9965f..80d50178d2e3 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -10,9 +10,6 @@ maintainers: - Ulf Hansson - Jisheng Zhang -allOf: - - $ref: mmc-controller.yaml# - properties: compatible: enum: @@ -21,6 +18,7 @@ properties: - snps,dwcmshc-sdhci - sophgo,cv1800b-dwcmshc - sophgo,sg2002-dwcmshc + - sophgo,sg2042-dwcmshc - thead,th1520-dwcmshc reg: @@ -31,22 +29,11 @@ properties: clocks: minItems: 1 - items: - - description: core clock - - description: bus clock for optional - - description: axi clock for rockchip specified - - description: block clock for rockchip specified - - description: timer clock for rockchip specified - + maxItems: 5 clock-names: minItems: 1 - items: - - const: core - - const: bus - - const: axi - - const: block - - const: timer + maxItems: 5 resets: maxItems: 5 @@ -63,7 +50,6 @@ properties: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 - required: - compatible - reg @@ -71,6 +57,46 @@ required: - clocks - clock-names +allOf: + - $ref: mmc-controller.yaml# + + - if: + properties: + compatible: + contains: + const: sophgo,sg2042-dwcmshc + + then: + properties: + clocks: + items: + - description: core clock + - description: bus clock + - description: timer clock + clock-names: + items: + - const: core + - const: bus + - const: timer + else: + properties: + clocks: + minItems: 1 + items: + - description: core clock + - description: bus clock for optional + - description: axi clock for rockchip specified + - description: block clock for rockchip specified + - description: timer clock for rockchip specified + clock-names: + minItems: 1 + items: + - const: core + - const: bus + - const: axi + - const: block + - const: timer + unevaluatedProperties: false examples: From 7af1a8f0965158b608baf11c53882f10a57376fd Mon Sep 17 00:00:00 2001 From: Chen Wang Date: Mon, 5 Aug 2024 17:19:24 +0800 Subject: [PATCH 13/41] mmc: sdhci-of-dwcmshc: Add support for Sophgo SG2042 Add support for the mmc controller of Sophgo SG2042. SG2042 uses Synopsys PHY the same as TH1520 so we reuse the tuning logic from TH1520. Besides this, this patch implement some SG2042 specific work, such as clocks and reset ops. Signed-off-by: Chen Wang Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/eb21847528a6487af54bb80f1ce94adff289cdb0.1722847198.git.unicorn_wang@outlook.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 125 ++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 16f420994519..ba8960d8b2d4 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -113,12 +113,15 @@ #define DWC_MSHC_PTR_PHY_R 0x300 /* PHY general configuration */ -#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00) -#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */ -#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */ -#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */ -#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */ -#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */ +#define PHY_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x00) +#define PHY_CNFG_RSTN_DEASSERT 0x1 /* Deassert PHY reset */ +#define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */ +#define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */ +#define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */ +#define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */ +#define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */ +#define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */ +#define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */ /* PHY command/response pad settings */ #define PHY_CMDPAD_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x04) @@ -147,10 +150,12 @@ #define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */ #define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */ #define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */ +#define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */ /* PHY CLK delay line settings */ #define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d) -#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */ +#define PHY_SDCLKDL_CNFG_EXTDLY_EN BIT(0) +#define PHY_SDCLKDL_CNFG_UPDATE BIT(4) /* set before writing to SDCLKDL_DC */ /* PHY CLK delay line delay code */ #define PHY_SDCLKDL_DC_R (DWC_MSHC_PTR_PHY_R + 0x1e) @@ -158,10 +163,14 @@ #define PHY_SDCLKDL_DC_DEFAULT 0x32 /* default delay code */ #define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */ +#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20) +#define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1) + /* PHY drift_cclk_rx delay line configuration setting */ #define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21) #define PHY_ATDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */ #define PHY_ATDL_CNFG_INPSEL 0x3 /* delay line input source */ +#define PHY_ATDL_CNFG_INPSEL_SG2042 0x2 /* delay line input source for SG2042 */ /* PHY DLL control settings */ #define PHY_DLL_CTRL_R (DWC_MSHC_PTR_PHY_R + 0x24) @@ -1013,6 +1022,85 @@ static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) return ret; } +static inline void sg2042_sdhci_phy_init(struct sdhci_host *host) +{ + u32 val; + + /* Asset phy reset & set tx drive strength */ + val = sdhci_readl(host, PHY_CNFG_R); + val &= ~PHY_CNFG_RSTN_DEASSERT; + val |= FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1); + val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_SG2042); + val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_SG2042); + sdhci_writel(host, val, PHY_CNFG_R); + + /* Configure phy pads */ + val = PHY_PAD_RXSEL_3V3; + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); + + val = PHY_PAD_RXSEL_3V3; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + val = PHY_PAD_RXSEL_3V3; + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + + /* Configure delay line */ + /* Enable fixed delay */ + sdhci_writeb(host, PHY_SDCLKDL_CNFG_EXTDLY_EN, PHY_SDCLKDL_CNFG_R); + /* + * Set delay line. + * Its recommended that bit UPDATE_DC[4] is 1 when SDCLKDL_DC is being written. + * Ensure UPDATE_DC[4] is '0' when not updating code. + */ + val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R); + val |= PHY_SDCLKDL_CNFG_UPDATE; + sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R); + /* Add 10 * 70ps = 0.7ns for output delay */ + sdhci_writeb(host, 10, PHY_SDCLKDL_DC_R); + val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R); + val &= ~(PHY_SDCLKDL_CNFG_UPDATE); + sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R); + + /* Set SMPLDL_CNFG, Bypass */ + sdhci_writeb(host, PHY_SMPLDL_CNFG_BYPASS_EN, PHY_SMPLDL_CNFG_R); + + /* Set ATDL_CNFG, tuning clk not use for init */ + val = FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL_SG2042); + sdhci_writeb(host, val, PHY_ATDL_CNFG_R); + + /* Deasset phy reset */ + val = sdhci_readl(host, PHY_CNFG_R); + val |= PHY_CNFG_RSTN_DEASSERT; + sdhci_writel(host, val, PHY_CNFG_R); +} + +static void sg2042_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + if (mask & SDHCI_RESET_ALL) + sg2042_sdhci_phy_init(host); +} + +static int sg2042_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + static const char * const clk_ids[] = {"timer"}; + + return dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -1054,6 +1142,16 @@ static const struct sdhci_ops sdhci_dwcmshc_cv18xx_ops = { .platform_execute_tuning = cv18xx_sdhci_execute_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = sg2042_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .platform_execute_tuning = th1520_execute_tuning, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { .pdata = { .ops = &sdhci_dwcmshc_ops, @@ -1102,6 +1200,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_cv18xx_pdata = { }, }; +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_sg2042_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, + .init = sg2042_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .enable = dwcmshc_sdhci_cqe_enable, .disable = sdhci_cqe_disable, @@ -1194,6 +1301,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "thead,th1520-dwcmshc", .data = &sdhci_dwcmshc_th1520_pdata, }, + { + .compatible = "sophgo,sg2042-dwcmshc", + .data = &sdhci_dwcmshc_sg2042_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); From fcd56ecaadc88b26fefebf3fb6a1787ac1d059e2 Mon Sep 17 00:00:00 2001 From: Lad Prabhakar Date: Mon, 5 Aug 2024 22:12:57 +0100 Subject: [PATCH 14/41] dt-bindings: mmc: renesas,sdhi: Remove duplicate compatible and add clock checks Remove the duplicate compatible entry `renesas,sdhi-r9a09g057` and add a restriction for clocks and clock-names for the RZ/V2H(P) SoC, which has four clocks similar to the RZ/G2L SoC. Reported-by: Geert Uytterhoeven Fixes: 32842af74abc ("dt-bindings: mmc: renesas,sdhi: Document RZ/V2H(P) support") Signed-off-by: Lad Prabhakar Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240805211257.61099-1-prabhakar.mahadev-lad.rj@bp.renesas.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 1155b1d79df5..92622d65f12f 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -67,7 +67,6 @@ properties: - renesas,sdhi-r9a07g054 # RZ/V2L - renesas,sdhi-r9a08g045 # RZ/G3S - renesas,sdhi-r9a09g011 # RZ/V2M - - renesas,sdhi-r9a09g057 # RZ/V2H(P) - const: renesas,rzg2l-sdhi reg: @@ -120,7 +119,9 @@ allOf: properties: compatible: contains: - const: renesas,rzg2l-sdhi + enum: + - renesas,sdhi-r9a09g057 + - renesas,rzg2l-sdhi then: properties: clocks: From e634d9873bb13f6a91262099f545af8faf456fd0 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Sun, 18 Aug 2024 16:23:01 +0200 Subject: [PATCH 15/41] mmc: mtk-sd: Improve data type in msdc_timeout_cal() The local variable clk_ns uses at most 32 bits and can be a u32. Replace the 64-by-32 do_div() division with a standard divison. Since do_div() casts the divisor to u32 anyway, changing the data type of clk_ns to u32 also removes the following Coccinelle/coccicheck warning reported by do_div.cocci: WARNING: do_div() does a 64-by-32 division, please consider using div64_u64 instead Use min_t(u32,,) to simplify the code and improve its readability. Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20240818142300.64156-2-thorsten.blum@toblux.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index e386f78e3267..89018b6c97b9 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -795,14 +795,13 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data) static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks) { struct mmc_host *mmc = mmc_from_priv(host); - u64 timeout, clk_ns; - u32 mode = 0; + u64 timeout; + u32 clk_ns, mode = 0; if (mmc->actual_clock == 0) { timeout = 0; } else { - clk_ns = 1000000000ULL; - do_div(clk_ns, mmc->actual_clock); + clk_ns = 1000000000U / mmc->actual_clock; timeout = ns + clk_ns - 1; do_div(timeout, clk_ns); timeout += clks; @@ -831,7 +830,7 @@ static void msdc_set_timeout(struct msdc_host *host, u64 ns, u64 clks) timeout = msdc_timeout_cal(host, ns, clks); sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, - (u32)(timeout > 255 ? 255 : timeout)); + min_t(u32, timeout, 255)); } static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks) @@ -840,7 +839,7 @@ static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks) timeout = msdc_timeout_cal(host, ns, clks); sdr_set_field(host->base + SDC_CFG, SDC_CFG_WRDTOC, - (u32)(timeout > 8191 ? 8191 : timeout)); + min_t(u32, timeout, 8191)); } static void msdc_gate_clock(struct msdc_host *host) From 1645e815cb5c10ee7df736408d2afc9b7bcfd606 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 18 Aug 2024 19:29:23 +0200 Subject: [PATCH 16/41] dt-bindings: mmc: renesas,sdhi: add top-level constraints Properties with variable number of items per each device are expected to have widest constraints in top-level "properties:" block and further customized (narrowed) in "if:then:". Add missing top-level constraints for clocks. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240818172923.121867-1-krzysztof.kozlowski@linaro.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 92622d65f12f..af378b9ff3f4 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -76,9 +76,13 @@ properties: minItems: 1 maxItems: 3 - clocks: true + clocks: + minItems: 1 + maxItems: 4 - clock-names: true + clock-names: + minItems: 1 + maxItems: 4 dmas: minItems: 4 From 1e9046e3a154608f63ce79edcb01e6afd6b10c7c Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 14 Aug 2024 17:35:55 +0200 Subject: [PATCH 17/41] rpmb: add Replay Protected Memory Block (RPMB) subsystem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A number of storage technologies support a specialised hardware partition designed to be resistant to replay attacks. The underlying HW protocols differ but the operations are common. The RPMB partition cannot be accessed via standard block layer, but by a set of specific RPMB commands. Such a partition provides authenticated and replay protected access, hence suitable as a secure storage. The initial aim of this patch is to provide a simple RPMB driver interface which can be accessed by the optee driver to facilitate early RPMB access to OP-TEE OS (secure OS) during the boot time. A TEE device driver can claim the RPMB interface, for example, via rpmb_interface_register() or rpmb_dev_find_device(). The RPMB driver provides a callback to route RPMB frames to the RPMB device accessible via rpmb_route_frames(). The detailed operation of implementing the access is left to the TEE device driver itself. Signed-off-by: Tomas Winkler Signed-off-by: Alex BennĂ©e Signed-off-by: Shyam Saini Signed-off-by: Jens Wiklander Reviewed-by: Linus Walleij Tested-by: Manuel Traut Reviewed-by: Ulf Hansson Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240814153558.708365-2-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- MAINTAINERS | 7 ++ drivers/misc/Kconfig | 10 ++ drivers/misc/Makefile | 1 + drivers/misc/rpmb-core.c | 233 +++++++++++++++++++++++++++++++++++++++ include/linux/rpmb.h | 123 +++++++++++++++++++++ 5 files changed, 374 insertions(+) create mode 100644 drivers/misc/rpmb-core.c create mode 100644 include/linux/rpmb.h diff --git a/MAINTAINERS b/MAINTAINERS index 878dcd23b331..ec8c7ccb0a92 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19861,6 +19861,13 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/allwinner,sun8i-a83t-de2-rotate.yaml F: drivers/media/platform/sunxi/sun8i-rotate/ +RPMB SUBSYSTEM +M: Jens Wiklander +L: linux-kernel@vger.kernel.org +S: Supported +F: drivers/misc/rpmb-core.c +F: include/linux/rpmb.h + RPMSG TTY DRIVER M: Arnaud Pouliquen L: linux-remoteproc@vger.kernel.org diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 41c54051347a..3fe7e2a9bd29 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -104,6 +104,16 @@ config PHANTOM If you choose to build module, its name will be phantom. If unsure, say N here. +config RPMB + tristate "RPMB partition interface" + depends on MMC + help + Unified RPMB unit interface for RPMB capable devices such as eMMC and + UFS. Provides interface for in-kernel security controllers to access + RPMB unit. + + If unsure, select N. + config TIFM_CORE tristate "TI Flash Media interface support" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c2f990862d2b..a9f94525e181 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_LKDTM) += lkdtm/ obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o +obj-$(CONFIG_RPMB) += rpmb-core.o obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_QCOM_FASTRPC) += fastrpc.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c new file mode 100644 index 000000000000..c8888267c222 --- /dev/null +++ b/drivers/misc/rpmb-core.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(c) 2015 - 2019 Intel Corporation. All rights reserved. + * Copyright(c) 2021 - 2024 Linaro Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_IDA(rpmb_ida); +static DEFINE_MUTEX(rpmb_mutex); + +/** + * rpmb_dev_get() - increase rpmb device ref counter + * @rdev: rpmb device + */ +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + if (rdev) + get_device(&rdev->dev); + return rdev; +} +EXPORT_SYMBOL_GPL(rpmb_dev_get); + +/** + * rpmb_dev_put() - decrease rpmb device ref counter + * @rdev: rpmb device + */ +void rpmb_dev_put(struct rpmb_dev *rdev) +{ + if (rdev) + put_device(&rdev->dev); +} +EXPORT_SYMBOL_GPL(rpmb_dev_put); + +/** + * rpmb_route_frames() - route rpmb frames to rpmb device + * @rdev: rpmb device + * @req: rpmb request frames + * @req_len: length of rpmb request frames in bytes + * @rsp: rpmb response frames + * @rsp_len: length of rpmb response frames in bytes + * + * Returns: < 0 on failure + */ +int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, + unsigned int req_len, u8 *rsp, unsigned int rsp_len) +{ + if (!req || !req_len || !rsp || !rsp_len) + return -EINVAL; + + return rdev->descr.route_frames(rdev->dev.parent, req, req_len, + rsp, rsp_len); +} +EXPORT_SYMBOL_GPL(rpmb_route_frames); + +static void rpmb_dev_release(struct device *dev) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + mutex_lock(&rpmb_mutex); + ida_simple_remove(&rpmb_ida, rdev->id); + mutex_unlock(&rpmb_mutex); + kfree(rdev->descr.dev_id); + kfree(rdev); +} + +static struct class rpmb_class = { + .name = "rpmb", + .dev_release = rpmb_dev_release, +}; + +/** + * rpmb_dev_find_device() - return first matching rpmb device + * @start: rpmb device to begin with + * @data: data for the match function + * @match: the matching function + * + * Iterate over registered RPMB devices, and call @match() for each passing + * it the RPMB device and @data. + * + * The return value of @match() is checked for each call. If it returns + * anything other 0, break and return the found RPMB device. + * + * It's the callers responsibility to call rpmb_dev_put() on the returned + * device, when it's done with it. + * + * Returns: a matching rpmb device or NULL on failure + */ +struct rpmb_dev *rpmb_dev_find_device(const void *data, + const struct rpmb_dev *start, + int (*match)(struct device *dev, + const void *data)) +{ + struct device *dev; + const struct device *start_dev = NULL; + + if (start) + start_dev = &start->dev; + dev = class_find_device(&rpmb_class, start_dev, data, match); + + return dev ? to_rpmb_dev(dev) : NULL; +} +EXPORT_SYMBOL_GPL(rpmb_dev_find_device); + +int rpmb_interface_register(struct class_interface *intf) +{ + intf->class = &rpmb_class; + + return class_interface_register(intf); +} +EXPORT_SYMBOL_GPL(rpmb_interface_register); + +void rpmb_interface_unregister(struct class_interface *intf) +{ + class_interface_unregister(intf); +} +EXPORT_SYMBOL_GPL(rpmb_interface_unregister); + +/** + * rpmb_dev_unregister() - unregister RPMB partition from the RPMB subsystem + * @rdev: the rpmb device to unregister + * + * This function should be called from the release function of the + * underlying device used when the RPMB device was registered. + * + * Returns: < 0 on failure + */ +int rpmb_dev_unregister(struct rpmb_dev *rdev) +{ + if (!rdev) + return -EINVAL; + + device_del(&rdev->dev); + + rpmb_dev_put(rdev); + + return 0; +} +EXPORT_SYMBOL_GPL(rpmb_dev_unregister); + +/** + * rpmb_dev_register - register RPMB partition with the RPMB subsystem + * @dev: storage device of the rpmb device + * @descr: RPMB device description + * + * While registering the RPMB partition extract needed device information + * while needed resources are available. + * + * Returns: a pointer to a 'struct rpmb_dev' or an ERR_PTR on failure + */ +struct rpmb_dev *rpmb_dev_register(struct device *dev, + struct rpmb_descr *descr) +{ + struct rpmb_dev *rdev; + int ret; + + if (!dev || !descr || !descr->route_frames || !descr->dev_id || + !descr->dev_id_len) + return ERR_PTR(-EINVAL); + + rdev = kzalloc(sizeof(*rdev), GFP_KERNEL); + if (!rdev) + return ERR_PTR(-ENOMEM); + rdev->descr = *descr; + rdev->descr.dev_id = kmemdup(descr->dev_id, descr->dev_id_len, + GFP_KERNEL); + if (!rdev->descr.dev_id) { + ret = -ENOMEM; + goto err_free_rdev; + } + + mutex_lock(&rpmb_mutex); + ret = ida_simple_get(&rpmb_ida, 0, 0, GFP_KERNEL); + mutex_unlock(&rpmb_mutex); + if (ret < 0) + goto err_free_dev_id; + rdev->id = ret; + + dev_set_name(&rdev->dev, "rpmb%d", rdev->id); + rdev->dev.class = &rpmb_class; + rdev->dev.parent = dev; + + ret = device_register(&rdev->dev); + if (ret) + goto err_id_remove; + + dev_dbg(&rdev->dev, "registered device\n"); + + return rdev; + +err_id_remove: + mutex_lock(&rpmb_mutex); + ida_simple_remove(&rpmb_ida, rdev->id); + mutex_unlock(&rpmb_mutex); +err_free_dev_id: + kfree(rdev->descr.dev_id); +err_free_rdev: + kfree(rdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(rpmb_dev_register); + +static int __init rpmb_init(void) +{ + int ret; + + ret = class_register(&rpmb_class); + if (ret) { + pr_err("couldn't create class\n"); + return ret; + } + ida_init(&rpmb_ida); + return 0; +} + +static void __exit rpmb_exit(void) +{ + ida_destroy(&rpmb_ida); + class_unregister(&rpmb_class); +} + +subsys_initcall(rpmb_init); +module_exit(rpmb_exit); + +MODULE_AUTHOR("Jens Wiklander "); +MODULE_DESCRIPTION("RPMB class"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/rpmb.h b/include/linux/rpmb.h new file mode 100644 index 000000000000..cccda73eea4d --- /dev/null +++ b/include/linux/rpmb.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2015-2019 Intel Corp. All rights reserved + * Copyright (C) 2021-2022 Linaro Ltd + */ +#ifndef __RPMB_H__ +#define __RPMB_H__ + +#include +#include + +/** + * enum rpmb_type - type of underlying storage technology + * + * @RPMB_TYPE_EMMC : emmc (JESD84-B50.1) + * @RPMB_TYPE_UFS : UFS (JESD220) + * @RPMB_TYPE_NVME : NVM Express + */ +enum rpmb_type { + RPMB_TYPE_EMMC, + RPMB_TYPE_UFS, + RPMB_TYPE_NVME, +}; + +/** + * struct rpmb_descr - RPMB description provided by the underlying block device + * + * @type : block device type + * @route_frames : routes frames to and from the RPMB device + * @dev_id : unique device identifier read from the hardware + * @dev_id_len : length of unique device identifier + * @reliable_wr_count: number of sectors that can be written in one access + * @capacity : capacity of the device in units of 128K + * + * @dev_id is intended to be used as input when deriving the authenticaion key. + */ +struct rpmb_descr { + enum rpmb_type type; + int (*route_frames)(struct device *dev, u8 *req, unsigned int req_len, + u8 *resp, unsigned int resp_len); + u8 *dev_id; + size_t dev_id_len; + u16 reliable_wr_count; + u16 capacity; +}; + +/** + * struct rpmb_dev - device which can support RPMB partition + * + * @dev : device + * @id : device_id + * @list_node : linked list node + * @descr : RPMB description + */ +struct rpmb_dev { + struct device dev; + int id; + struct list_head list_node; + struct rpmb_descr descr; +}; + +#define to_rpmb_dev(x) container_of((x), struct rpmb_dev, dev) + +#if IS_ENABLED(CONFIG_RPMB) +struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev); +void rpmb_dev_put(struct rpmb_dev *rdev); +struct rpmb_dev *rpmb_dev_find_device(const void *data, + const struct rpmb_dev *start, + int (*match)(struct device *dev, + const void *data)); +int rpmb_interface_register(struct class_interface *intf); +void rpmb_interface_unregister(struct class_interface *intf); +struct rpmb_dev *rpmb_dev_register(struct device *dev, + struct rpmb_descr *descr); +int rpmb_dev_unregister(struct rpmb_dev *rdev); + +int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, + unsigned int req_len, u8 *resp, unsigned int resp_len); + +#else +static inline struct rpmb_dev *rpmb_dev_get(struct rpmb_dev *rdev) +{ + return NULL; +} + +static inline void rpmb_dev_put(struct rpmb_dev *rdev) { } + +static inline struct rpmb_dev * +rpmb_dev_find_device(const void *data, const struct rpmb_dev *start, + int (*match)(struct device *dev, const void *data)) +{ + return NULL; +} + +static inline int rpmb_interface_register(struct class_interface *intf) +{ + return -EOPNOTSUPP; +} + +static inline void rpmb_interface_unregister(struct class_interface *intf) +{ +} + +static inline struct rpmb_dev * +rpmb_dev_register(struct device *dev, struct rpmb_descr *descr) +{ + return NULL; +} + +static inline int rpmb_dev_unregister(struct rpmb_dev *dev) +{ + return 0; +} + +static inline int rpmb_route_frames(struct rpmb_dev *rdev, u8 *req, + unsigned int req_len, u8 *resp, + unsigned int resp_len) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_RPMB */ + +#endif /* __RPMB_H__ */ From 7852028a35f03e04c9cdc92fd26894cca4a8a4de Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 14 Aug 2024 17:35:56 +0200 Subject: [PATCH 18/41] mmc: block: register RPMB partition with the RPMB subsystem Register eMMC RPMB partition with the RPMB subsystem and provide an implementation for the RPMB access operations abstracting the actual multi step process. Add a callback to extract the needed device information at registration to avoid accessing the struct mmc_card at a later stage as we're not holding a reference counter for this struct. Taking the needed reference to md->disk in mmc_blk_alloc_rpmb_part() instead of in mmc_rpmb_chrdev_open(). This is needed by the route_frames() function pointer in struct rpmb_ops. Signed-off-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Jens Wiklander Tested-by: Manuel Traut Reviewed-by: Linus Walleij Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20240814153558.708365-3-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 242 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 240 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 2c9963248fcb..cc7318089cf2 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include #include +#include #include #include @@ -76,6 +78,48 @@ MODULE_ALIAS("mmc:block"); #define MMC_EXTRACT_INDEX_FROM_ARG(x) ((x & 0x00FF0000) >> 16) #define MMC_EXTRACT_VALUE_FROM_ARG(x) ((x & 0x0000FF00) >> 8) +/** + * struct rpmb_frame - rpmb frame as defined by eMMC 5.1 (JESD84-B51) + * + * @stuff : stuff bytes + * @key_mac : The authentication key or the message authentication + * code (MAC) depending on the request/response type. + * The MAC will be delivered in the last (or the only) + * block of data. + * @data : Data to be written or read by signed access. + * @nonce : Random number generated by the host for the requests + * and copied to the response by the RPMB engine. + * @write_counter: Counter value for the total amount of the successful + * authenticated data write requests made by the host. + * @addr : Address of the data to be programmed to or read + * from the RPMB. Address is the serial number of + * the accessed block (half sector 256B). + * @block_count : Number of blocks (half sectors, 256B) requested to be + * read/programmed. + * @result : Includes information about the status of the write counter + * (valid, expired) and result of the access made to the RPMB. + * @req_resp : Defines the type of request and response to/from the memory. + * + * The stuff bytes and big-endian properties are modeled to fit to the spec. + */ +struct rpmb_frame { + u8 stuff[196]; + u8 key_mac[32]; + u8 data[256]; + u8 nonce[16]; + __be32 write_counter; + __be16 addr; + __be16 block_count; + __be16 result; + __be16 req_resp; +} __packed; + +#define RPMB_PROGRAM_KEY 0x1 /* Program RPMB Authentication Key */ +#define RPMB_GET_WRITE_COUNTER 0x2 /* Read RPMB write counter */ +#define RPMB_WRITE_DATA 0x3 /* Write data to RPMB partition */ +#define RPMB_READ_DATA 0x4 /* Read data from RPMB partition */ +#define RPMB_RESULT_READ 0x5 /* Read result request (Internal) */ + static DEFINE_MUTEX(block_mutex); /* @@ -155,6 +199,7 @@ static const struct bus_type mmc_rpmb_bus_type = { * @id: unique device ID number * @part_index: partition index (0 on first) * @md: parent MMC block device + * @rdev: registered RPMB device * @node: list item, so we can put this device on a list */ struct mmc_rpmb_data { @@ -163,6 +208,7 @@ struct mmc_rpmb_data { int id; unsigned int part_index; struct mmc_blk_data *md; + struct rpmb_dev *rdev; struct list_head node; }; @@ -2670,7 +2716,6 @@ static int mmc_rpmb_chrdev_open(struct inode *inode, struct file *filp) get_device(&rpmb->dev); filp->private_data = rpmb; - mmc_blk_get(rpmb->md->disk); return nonseekable_open(inode, filp); } @@ -2680,7 +2725,6 @@ static int mmc_rpmb_chrdev_release(struct inode *inode, struct file *filp) struct mmc_rpmb_data *rpmb = container_of(inode->i_cdev, struct mmc_rpmb_data, chrdev); - mmc_blk_put(rpmb->md); put_device(&rpmb->dev); return 0; @@ -2701,10 +2745,165 @@ static void mmc_blk_rpmb_device_release(struct device *dev) { struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + rpmb_dev_unregister(rpmb->rdev); + mmc_blk_put(rpmb->md); ida_free(&mmc_rpmb_ida, rpmb->id); kfree(rpmb); } +static void free_idata(struct mmc_blk_ioc_data **idata, unsigned int cmd_count) +{ + unsigned int n; + + for (n = 0; n < cmd_count; n++) + kfree(idata[n]); + kfree(idata); +} + +static struct mmc_blk_ioc_data **alloc_idata(struct mmc_rpmb_data *rpmb, + unsigned int cmd_count) +{ + struct mmc_blk_ioc_data **idata; + unsigned int n; + + idata = kcalloc(cmd_count, sizeof(*idata), GFP_KERNEL); + if (!idata) + return NULL; + + for (n = 0; n < cmd_count; n++) { + idata[n] = kcalloc(1, sizeof(**idata), GFP_KERNEL); + if (!idata[n]) { + free_idata(idata, n); + return NULL; + } + idata[n]->rpmb = rpmb; + } + + return idata; +} + +static void set_idata(struct mmc_blk_ioc_data *idata, u32 opcode, + int write_flag, u8 *buf, unsigned int buf_bytes) +{ + /* + * The size of an RPMB frame must match what's expected by the + * hardware. + */ + BUILD_BUG_ON(sizeof(struct rpmb_frame) != 512); + + idata->ic.opcode = opcode; + idata->ic.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + idata->ic.write_flag = write_flag; + idata->ic.blksz = sizeof(struct rpmb_frame); + idata->ic.blocks = buf_bytes / idata->ic.blksz; + idata->buf = buf; + idata->buf_bytes = buf_bytes; +} + +static int mmc_route_rpmb_frames(struct device *dev, u8 *req, + unsigned int req_len, u8 *resp, + unsigned int resp_len) +{ + struct rpmb_frame *frm = (struct rpmb_frame *)req; + struct mmc_rpmb_data *rpmb = dev_get_drvdata(dev); + struct mmc_blk_data *md = rpmb->md; + struct mmc_blk_ioc_data **idata; + struct mmc_queue_req *mq_rq; + unsigned int cmd_count; + struct request *rq; + u16 req_type; + bool write; + int ret; + + if (IS_ERR(md->queue.card)) + return PTR_ERR(md->queue.card); + + if (req_len < sizeof(*frm)) + return -EINVAL; + + req_type = be16_to_cpu(frm->req_resp); + switch (req_type) { + case RPMB_PROGRAM_KEY: + if (req_len != sizeof(struct rpmb_frame) || + resp_len != sizeof(struct rpmb_frame)) + return -EINVAL; + write = true; + break; + case RPMB_GET_WRITE_COUNTER: + if (req_len != sizeof(struct rpmb_frame) || + resp_len != sizeof(struct rpmb_frame)) + return -EINVAL; + write = false; + break; + case RPMB_WRITE_DATA: + if (req_len % sizeof(struct rpmb_frame) || + resp_len != sizeof(struct rpmb_frame)) + return -EINVAL; + write = true; + break; + case RPMB_READ_DATA: + if (req_len != sizeof(struct rpmb_frame) || + resp_len % sizeof(struct rpmb_frame)) + return -EINVAL; + write = false; + break; + default: + return -EINVAL; + } + + if (write) + cmd_count = 3; + else + cmd_count = 2; + + idata = alloc_idata(rpmb, cmd_count); + if (!idata) + return -ENOMEM; + + if (write) { + struct rpmb_frame *frm = (struct rpmb_frame *)resp; + + /* Send write request frame(s) */ + set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK, + 1 | MMC_CMD23_ARG_REL_WR, req, req_len); + + /* Send result request frame */ + memset(frm, 0, sizeof(*frm)); + frm->req_resp = cpu_to_be16(RPMB_RESULT_READ); + set_idata(idata[1], MMC_WRITE_MULTIPLE_BLOCK, 1, resp, + resp_len); + + /* Read response frame */ + set_idata(idata[2], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len); + } else { + /* Send write request frame(s) */ + set_idata(idata[0], MMC_WRITE_MULTIPLE_BLOCK, 1, req, req_len); + + /* Read response frame */ + set_idata(idata[1], MMC_READ_MULTIPLE_BLOCK, 0, resp, resp_len); + } + + rq = blk_mq_alloc_request(md->queue.queue, REQ_OP_DRV_OUT, 0); + if (IS_ERR(rq)) { + ret = PTR_ERR(rq); + goto out; + } + + mq_rq = req_to_mmc_queue_req(rq); + mq_rq->drv_op = MMC_DRV_OP_IOCTL_RPMB; + mq_rq->drv_op_result = -EIO; + mq_rq->drv_op_data = idata; + mq_rq->ioc_count = cmd_count; + blk_execute_rq(rq, false); + ret = req_to_mmc_queue_req(rq)->drv_op_result; + + blk_mq_free_request(rq); + +out: + free_idata(idata, cmd_count); + return ret; +} + static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, struct mmc_blk_data *md, unsigned int part_index, @@ -2739,6 +2938,7 @@ static int mmc_blk_alloc_rpmb_part(struct mmc_card *card, rpmb->dev.release = mmc_blk_rpmb_device_release; device_initialize(&rpmb->dev); dev_set_drvdata(&rpmb->dev, rpmb); + mmc_blk_get(md->disk); rpmb->md = md; cdev_init(&rpmb->chrdev, &mmc_rpmb_fileops); @@ -3000,6 +3200,42 @@ static void mmc_blk_remove_debugfs(struct mmc_card *card, #endif /* CONFIG_DEBUG_FS */ +static void mmc_blk_rpmb_add(struct mmc_card *card) +{ + struct mmc_blk_data *md = dev_get_drvdata(&card->dev); + struct mmc_rpmb_data *rpmb; + struct rpmb_dev *rdev; + unsigned int n; + u32 cid[4]; + struct rpmb_descr descr = { + .type = RPMB_TYPE_EMMC, + .route_frames = mmc_route_rpmb_frames, + .reliable_wr_count = card->ext_csd.enhanced_rpmb_supported ? + 2 : 32, + .capacity = card->ext_csd.raw_rpmb_size_mult, + .dev_id = (void *)cid, + .dev_id_len = sizeof(cid), + }; + + /* + * Provice CID as an octet array. The CID needs to be interpreted + * when used as input to derive the RPMB key since some fields + * will change due to firmware updates. + */ + for (n = 0; n < 4; n++) + cid[n] = be32_to_cpu((__force __be32)card->raw_cid[n]); + + list_for_each_entry(rpmb, &md->rpmbs, node) { + rdev = rpmb_dev_register(&rpmb->dev, &descr); + if (IS_ERR(rdev)) { + pr_warn("%s: could not register RPMB device\n", + dev_name(&rpmb->dev)); + continue; + } + rpmb->rdev = rdev; + } +} + static int mmc_blk_probe(struct mmc_card *card) { struct mmc_blk_data *md; @@ -3045,6 +3281,8 @@ static int mmc_blk_probe(struct mmc_card *card) pm_runtime_enable(&card->dev); } + mmc_blk_rpmb_add(card); + return 0; out: From c30b855e814d9094e369a19fbd86c9bb5badc154 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 14 Aug 2024 17:35:57 +0200 Subject: [PATCH 19/41] tee: add tee_device_set_dev_groups() Add tee_device_set_dev_groups() to TEE drivers to supply driver specific attribute groups. The class specific attributes are from now on added via the tee_class, which currently only consist of implementation_id. Signed-off-by: Jens Wiklander Reviewed-by: Sumit Garg Link: https://lore.kernel.org/r/20240814153558.708365-4-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- drivers/tee/tee_core.c | 19 +++++++++++++------ include/linux/tee_core.h | 12 ++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d52e879b204e..d113679b1e2d 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -40,10 +40,7 @@ static const uuid_t tee_client_uuid_ns = UUID_INIT(0x58ac9ca0, 0x2086, 0x4683, static DECLARE_BITMAP(dev_mask, TEE_NUM_DEVICES); static DEFINE_SPINLOCK(driver_lock); -static const struct class tee_class = { - .name = "tee", -}; - +static const struct class tee_class; static dev_t tee_devt; struct tee_context *teedev_open(struct tee_device *teedev) @@ -965,6 +962,13 @@ err: } EXPORT_SYMBOL_GPL(tee_device_alloc); +void tee_device_set_dev_groups(struct tee_device *teedev, + const struct attribute_group **dev_groups) +{ + teedev->dev.groups = dev_groups; +} +EXPORT_SYMBOL_GPL(tee_device_set_dev_groups); + static ssize_t implementation_id_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -983,6 +987,11 @@ static struct attribute *tee_dev_attrs[] = { ATTRIBUTE_GROUPS(tee_dev); +static const struct class tee_class = { + .name = "tee", + .dev_groups = tee_dev_groups, +}; + /** * tee_device_register() - Registers a TEE device * @teedev: Device to register @@ -1001,8 +1010,6 @@ int tee_device_register(struct tee_device *teedev) return -EINVAL; } - teedev->dev.groups = tee_dev_groups; - rc = cdev_device_add(&teedev->cdev, &teedev->dev); if (rc) { dev_err(&teedev->dev, diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index efd16ed52315..a38494d6b5f4 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -154,6 +154,18 @@ int tee_device_register(struct tee_device *teedev); */ void tee_device_unregister(struct tee_device *teedev); +/** + * tee_device_set_dev_groups() - Set device attribute groups + * @teedev: Device to register + * @dev_groups: Attribute groups + * + * Assigns the provided @dev_groups to the @teedev to be registered later + * with tee_device_register(). Calling this function is optional, but if + * it's called it must be called before tee_device_register(). + */ +void tee_device_set_dev_groups(struct tee_device *teedev, + const struct attribute_group **dev_groups); + /** * tee_session_calc_client_uuid() - Calculates client UUID for session * @uuid: Resulting UUID From f0c8431568eedee7705c92f5c341bdc4567e3ad5 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Wed, 14 Aug 2024 17:35:58 +0200 Subject: [PATCH 20/41] optee: probe RPMB device using RPMB subsystem Adds support in the OP-TEE drivers (both SMC and FF-A ABIs) to probe and use an RPMB device via the RPMB subsystem instead of passing the RPMB frames via tee-supplicant in user space. A fallback mechanism is kept to route RPMB frames via tee-supplicant if the RPMB subsystem isn't available. The OP-TEE RPC ABI is extended to support iterating over all RPMB devices until one is found with the expected RPMB key already programmed. Signed-off-by: Jens Wiklander Tested-by: Manuel Traut Reviewed-by: Sumit Garg Link: https://lore.kernel.org/r/20240814153558.708365-5-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- Documentation/ABI/testing/sysfs-class-tee | 15 ++ MAINTAINERS | 1 + drivers/tee/optee/core.c | 96 +++++++++++- drivers/tee/optee/device.c | 7 + drivers/tee/optee/ffa_abi.c | 14 ++ drivers/tee/optee/optee_ffa.h | 2 + drivers/tee/optee/optee_private.h | 26 +++- drivers/tee/optee/optee_rpc_cmd.h | 35 +++++ drivers/tee/optee/optee_smc.h | 2 + drivers/tee/optee/rpc.c | 177 ++++++++++++++++++++++ drivers/tee/optee/smc_abi.c | 14 ++ 11 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-class-tee diff --git a/Documentation/ABI/testing/sysfs-class-tee b/Documentation/ABI/testing/sysfs-class-tee new file mode 100644 index 000000000000..c9144d16003e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-tee @@ -0,0 +1,15 @@ +What: /sys/class/tee/tee{,priv}X/rpmb_routing_model +Date: May 2024 +KernelVersion: 6.10 +Contact: op-tee@lists.trustedfirmware.org +Description: + RPMB frames can be routed to the RPMB device via the + user-space daemon tee-supplicant or the RPMB subsystem + in the kernel. The value "user" means that the driver + will route the RPMB frames via user space. Conversely, + "kernel" means that the frames are routed via the RPMB + subsystem without assistance from tee-supplicant. It + should be assumed that RPMB frames are routed via user + space if the variable is absent. The primary purpose + of this variable is to let systemd know whether + tee-supplicant is needed in the early boot with initramfs. diff --git a/MAINTAINERS b/MAINTAINERS index ec8c7ccb0a92..cfeb80fbc15f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22458,6 +22458,7 @@ M: Jens Wiklander R: Sumit Garg L: op-tee@lists.trustedfirmware.org S: Maintained +F: Documentation/ABI/testing/sysfs-class-tee F: Documentation/driver-api/tee.rst F: Documentation/tee/ F: Documentation/userspace-api/tee.rst diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 39e688d4e974..c75fddc83576 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -10,17 +10,85 @@ #include #include #include +#include #include #include #include #include #include "optee_private.h" +struct blocking_notifier_head optee_rpmb_intf_added = + BLOCKING_NOTIFIER_INIT(optee_rpmb_intf_added); + +static int rpmb_add_dev(struct device *dev) +{ + blocking_notifier_call_chain(&optee_rpmb_intf_added, 0, + to_rpmb_dev(dev)); + + return 0; +} + +static struct class_interface rpmb_class_intf = { + .add_dev = rpmb_add_dev, +}; + +void optee_bus_scan_rpmb(struct work_struct *work) +{ + struct optee *optee = container_of(work, struct optee, + rpmb_scan_bus_work); + int ret; + + if (!optee->rpmb_scan_bus_done) { + ret = optee_enumerate_devices(PTA_CMD_GET_DEVICES_RPMB); + optee->rpmb_scan_bus_done = !ret; + if (ret && ret != -ENODEV) + pr_info("Scanning for RPMB device: ret %d\n", ret); + } +} + +int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action, + void *data) +{ + struct optee *optee = container_of(intf, struct optee, rpmb_intf); + + schedule_work(&optee->rpmb_scan_bus_work); + + return 0; +} + static void optee_bus_scan(struct work_struct *work) { WARN_ON(optee_enumerate_devices(PTA_CMD_GET_DEVICES_SUPP)); } +static ssize_t rpmb_routing_model_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct optee *optee = dev_get_drvdata(dev); + const char *s; + + if (optee->in_kernel_rpmb_routing) + s = "kernel"; + else + s = "user"; + + return scnprintf(buf, PAGE_SIZE, "%s\n", s); +} +static DEVICE_ATTR_RO(rpmb_routing_model); + +static struct attribute *optee_dev_attrs[] = { + &dev_attr_rpmb_routing_model.attr, + NULL +}; + +ATTRIBUTE_GROUPS(optee_dev); + +void optee_set_dev_group(struct optee *optee) +{ + tee_device_set_dev_groups(optee->teedev, optee_dev_groups); + tee_device_set_dev_groups(optee->supp_teedev, optee_dev_groups); +} + int optee_open(struct tee_context *ctx, bool cap_memref_null) { struct optee_context_data *ctxdata; @@ -97,6 +165,9 @@ void optee_release_supp(struct tee_context *ctx) void optee_remove_common(struct optee *optee) { + blocking_notifier_chain_unregister(&optee_rpmb_intf_added, + &optee->rpmb_intf); + cancel_work_sync(&optee->rpmb_scan_bus_work); /* Unregister OP-TEE specific client devices on TEE bus */ optee_unregister_devices(); @@ -113,13 +184,18 @@ void optee_remove_common(struct optee *optee) tee_shm_pool_free(optee->pool); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); + rpmb_dev_put(optee->rpmb_dev); + mutex_destroy(&optee->rpmb_dev_mutex); } static int smc_abi_rc; static int ffa_abi_rc; +static bool intf_is_regged; static int __init optee_core_init(void) { + int rc; + /* * The kernel may have crashed at the same time that all available * secure world threads were suspended and we cannot reschedule the @@ -130,18 +206,36 @@ static int __init optee_core_init(void) if (is_kdump_kernel()) return -ENODEV; + if (IS_REACHABLE(CONFIG_RPMB)) { + rc = rpmb_interface_register(&rpmb_class_intf); + if (rc) + return rc; + intf_is_regged = true; + } + smc_abi_rc = optee_smc_abi_register(); ffa_abi_rc = optee_ffa_abi_register(); /* If both failed there's no point with this module */ - if (smc_abi_rc && ffa_abi_rc) + if (smc_abi_rc && ffa_abi_rc) { + if (IS_REACHABLE(CONFIG_RPMB)) { + rpmb_interface_unregister(&rpmb_class_intf); + intf_is_regged = false; + } return smc_abi_rc; + } + return 0; } module_init(optee_core_init); static void __exit optee_core_exit(void) { + if (IS_REACHABLE(CONFIG_RPMB) && intf_is_regged) { + rpmb_interface_unregister(&rpmb_class_intf); + intf_is_regged = false; + } + if (!smc_abi_rc) optee_smc_abi_unregister(); if (!ffa_abi_rc) diff --git a/drivers/tee/optee/device.c b/drivers/tee/optee/device.c index d296c70ddfdc..950b4661d5df 100644 --- a/drivers/tee/optee/device.c +++ b/drivers/tee/optee/device.c @@ -43,6 +43,13 @@ static int get_devices(struct tee_context *ctx, u32 session, ret = tee_client_invoke_func(ctx, &inv_arg, param); if ((ret < 0) || ((inv_arg.ret != TEEC_SUCCESS) && (inv_arg.ret != TEEC_ERROR_SHORT_BUFFER))) { + /* + * TEE_ERROR_STORAGE_NOT_AVAILABLE is returned when getting + * the list of device TAs that depends on RPMB but a usable + * RPMB device isn't found. + */ + if (inv_arg.ret == TEE_ERROR_STORAGE_NOT_AVAILABLE) + return -ENODEV; pr_err("PTA_CMD_GET_DEVICES invoke function err: %x\n", inv_arg.ret); return -EINVAL; diff --git a/drivers/tee/optee/ffa_abi.c b/drivers/tee/optee/ffa_abi.c index 3e73efa51bba..f3af5666bb11 100644 --- a/drivers/tee/optee/ffa_abi.c +++ b/drivers/tee/optee/ffa_abi.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -909,6 +910,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) optee->ffa.bottom_half_value = U32_MAX; optee->rpc_param_count = rpc_param_count; + if (IS_REACHABLE(CONFIG_RPMB) && + (sec_caps & OPTEE_FFA_SEC_CAP_RPMB_PROBE)) + optee->in_kernel_rpmb_routing = true; + teedev = tee_device_alloc(&optee_ffa_clnt_desc, NULL, optee->pool, optee); if (IS_ERR(teedev)) { @@ -925,6 +930,8 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) } optee->supp_teedev = teedev; + optee_set_dev_group(optee); + rc = tee_device_register(optee->teedev); if (rc) goto err_unreg_supp_teedev; @@ -940,6 +947,7 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) optee_cq_init(&optee->call_queue, 0); optee_supp_init(&optee->supp); optee_shm_arg_cache_init(optee, arg_cache_flags); + mutex_init(&optee->rpmb_dev_mutex); ffa_dev_set_drvdata(ffa_dev, optee); ctx = teedev_open(optee->teedev); if (IS_ERR(ctx)) { @@ -961,6 +969,10 @@ static int optee_ffa_probe(struct ffa_device *ffa_dev) if (rc) goto err_unregister_devices; + INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb); + optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev; + blocking_notifier_chain_register(&optee_rpmb_intf_added, + &optee->rpmb_intf); pr_info("initialized driver\n"); return 0; @@ -974,6 +986,8 @@ err_close_ctx: teedev_close_context(ctx); err_rhashtable_free: rhashtable_free_and_destroy(&optee->ffa.global_ids, rh_free_fn, NULL); + rpmb_dev_put(optee->rpmb_dev); + mutex_destroy(&optee->rpmb_dev_mutex); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); mutex_destroy(&optee->ffa.mutex); diff --git a/drivers/tee/optee/optee_ffa.h b/drivers/tee/optee/optee_ffa.h index 5db779dc00de..257735ae5b56 100644 --- a/drivers/tee/optee/optee_ffa.h +++ b/drivers/tee/optee/optee_ffa.h @@ -92,6 +92,8 @@ #define OPTEE_FFA_SEC_CAP_ARG_OFFSET BIT(0) /* OP-TEE supports asynchronous notification via FF-A */ #define OPTEE_FFA_SEC_CAP_ASYNC_NOTIF BIT(1) +/* OP-TEE supports probing for RPMB device if needed */ +#define OPTEE_FFA_SEC_CAP_RPMB_PROBE BIT(2) #define OPTEE_FFA_EXCHANGE_CAPABILITIES OPTEE_FFA_BLOCKING_CALL(2) diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index 424898cdc4e9..dc0f355ef72a 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -7,7 +7,9 @@ #define OPTEE_PRIVATE_H #include +#include #include +#include #include #include #include @@ -20,6 +22,7 @@ /* Some Global Platform error codes used in this driver */ #define TEEC_SUCCESS 0x00000000 #define TEEC_ERROR_BAD_PARAMETERS 0xFFFF0006 +#define TEEC_ERROR_ITEM_NOT_FOUND 0xFFFF0008 #define TEEC_ERROR_NOT_SUPPORTED 0xFFFF000A #define TEEC_ERROR_COMMUNICATION 0xFFFF000E #define TEEC_ERROR_OUT_OF_MEMORY 0xFFFF000C @@ -28,6 +31,7 @@ /* API Return Codes are from the GP TEE Internal Core API Specification */ #define TEE_ERROR_TIMEOUT 0xFFFF3001 +#define TEE_ERROR_STORAGE_NOT_AVAILABLE 0xF0100003 #define TEEC_ORIGIN_COMMS 0x00000002 @@ -200,6 +204,12 @@ struct optee_ops { * @notif: notification synchronization struct * @supp: supplicant synchronization struct for RPC to supplicant * @pool: shared memory pool + * @mutex: mutex protecting @rpmb_dev + * @rpmb_dev: current RPMB device or NULL + * @rpmb_scan_bus_done flag if device registation of RPMB dependent devices + * was already done + * @rpmb_scan_bus_work workq to for an RPMB device and to scan optee bus + * and register RPMB dependent optee drivers * @rpc_param_count: If > 0 number of RPC parameters to make room for * @scan_bus_done flag if device registation was already done. * @scan_bus_work workq to scan optee bus and register optee drivers @@ -218,9 +228,16 @@ struct optee { struct optee_notif notif; struct optee_supp supp; struct tee_shm_pool *pool; + /* Protects rpmb_dev pointer */ + struct mutex rpmb_dev_mutex; + struct rpmb_dev *rpmb_dev; + struct notifier_block rpmb_intf; unsigned int rpc_param_count; - bool scan_bus_done; + bool scan_bus_done; + bool rpmb_scan_bus_done; + bool in_kernel_rpmb_routing; struct work_struct scan_bus_work; + struct work_struct rpmb_scan_bus_work; }; struct optee_session { @@ -253,6 +270,8 @@ struct optee_call_ctx { size_t num_entries; }; +extern struct blocking_notifier_head optee_rpmb_intf_added; + int optee_notif_init(struct optee *optee, u_int max_key); void optee_notif_uninit(struct optee *optee); int optee_notif_wait(struct optee *optee, u_int key, u32 timeout); @@ -283,9 +302,14 @@ int optee_cancel_req(struct tee_context *ctx, u32 cancel_id, u32 session); #define PTA_CMD_GET_DEVICES 0x0 #define PTA_CMD_GET_DEVICES_SUPP 0x1 +#define PTA_CMD_GET_DEVICES_RPMB 0x2 int optee_enumerate_devices(u32 func); void optee_unregister_devices(void); +void optee_bus_scan_rpmb(struct work_struct *work); +int optee_rpmb_intf_rdev(struct notifier_block *intf, unsigned long action, + void *data); +void optee_set_dev_group(struct optee *optee); void optee_remove_common(struct optee *optee); int optee_open(struct tee_context *ctx, bool cap_memref_null); void optee_release(struct tee_context *ctx); diff --git a/drivers/tee/optee/optee_rpc_cmd.h b/drivers/tee/optee/optee_rpc_cmd.h index 4576751b490c..87a59cc03480 100644 --- a/drivers/tee/optee/optee_rpc_cmd.h +++ b/drivers/tee/optee/optee_rpc_cmd.h @@ -104,4 +104,39 @@ /* I2C master control flags */ #define OPTEE_RPC_I2C_FLAGS_TEN_BIT BIT(0) +/* + * Reset RPMB probing + * + * Releases an eventually already used RPMB devices and starts over searching + * for RPMB devices. Returns the kind of shared memory to use in subsequent + * OPTEE_RPC_CMD_RPMB_PROBE_NEXT and OPTEE_RPC_CMD_RPMB calls. + * + * [out] value[0].a OPTEE_RPC_SHM_TYPE_*, the parameter for + * OPTEE_RPC_CMD_SHM_ALLOC + */ +#define OPTEE_RPC_CMD_RPMB_PROBE_RESET 22 + +/* + * Probe next RPMB device + * + * [out] value[0].a Type of RPMB device, OPTEE_RPC_RPMB_* + * [out] value[0].b EXT CSD-slice 168 "RPMB Size" + * [out] value[0].c EXT CSD-slice 222 "Reliable Write Sector Count" + * [out] memref[1] Buffer with the raw CID + */ +#define OPTEE_RPC_CMD_RPMB_PROBE_NEXT 23 + +/* Type of RPMB device */ +#define OPTEE_RPC_RPMB_EMMC 0 +#define OPTEE_RPC_RPMB_UFS 1 +#define OPTEE_RPC_RPMB_NVME 2 + +/* + * Replay Protected Memory Block access + * + * [in] memref[0] Frames to device + * [out] memref[1] Frames from device + */ +#define OPTEE_RPC_CMD_RPMB_FRAMES 24 + #endif /*__OPTEE_RPC_CMD_H*/ diff --git a/drivers/tee/optee/optee_smc.h b/drivers/tee/optee/optee_smc.h index 7d9fa426505b..879426300821 100644 --- a/drivers/tee/optee/optee_smc.h +++ b/drivers/tee/optee/optee_smc.h @@ -278,6 +278,8 @@ struct optee_smc_get_shm_config_result { #define OPTEE_SMC_SEC_CAP_ASYNC_NOTIF BIT(5) /* Secure world supports pre-allocating RPC arg struct */ #define OPTEE_SMC_SEC_CAP_RPC_ARG BIT(6) +/* Secure world supports probing for RPMB device if needed */ +#define OPTEE_SMC_SEC_CAP_RPMB_PROBE BIT(7) #define OPTEE_SMC_FUNCID_EXCHANGE_CAPABILITIES 9 #define OPTEE_SMC_EXCHANGE_CAPABILITIES \ diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index 5de4504665be..a4b49fd1d46d 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include "optee_private.h" @@ -261,6 +262,154 @@ void optee_rpc_cmd_free_suppl(struct tee_context *ctx, struct tee_shm *shm) optee_supp_thrd_req(ctx, OPTEE_RPC_CMD_SHM_FREE, 1, ¶m); } +static void handle_rpc_func_rpmb_probe_reset(struct tee_context *ctx, + struct optee *optee, + struct optee_msg_arg *arg) +{ + struct tee_param params[1]; + + if (arg->num_params != ARRAY_SIZE(params) || + optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params) || + params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + params[0].u.value.a = OPTEE_RPC_SHM_TYPE_KERNEL; + params[0].u.value.b = 0; + params[0].u.value.c = 0; + if (optee->ops->to_msg_param(optee, arg->params, + arg->num_params, params)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + mutex_lock(&optee->rpmb_dev_mutex); + rpmb_dev_put(optee->rpmb_dev); + optee->rpmb_dev = NULL; + mutex_unlock(&optee->rpmb_dev_mutex); + + arg->ret = TEEC_SUCCESS; +} + +static int rpmb_type_to_rpc_type(enum rpmb_type rtype) +{ + switch (rtype) { + case RPMB_TYPE_EMMC: + return OPTEE_RPC_RPMB_EMMC; + case RPMB_TYPE_UFS: + return OPTEE_RPC_RPMB_UFS; + case RPMB_TYPE_NVME: + return OPTEE_RPC_RPMB_NVME; + default: + return -1; + } +} + +static int rpc_rpmb_match(struct device *dev, const void *data) +{ + struct rpmb_dev *rdev = to_rpmb_dev(dev); + + return rpmb_type_to_rpc_type(rdev->descr.type) >= 0; +} + +static void handle_rpc_func_rpmb_probe_next(struct tee_context *ctx, + struct optee *optee, + struct optee_msg_arg *arg) +{ + struct rpmb_dev *rdev; + struct tee_param params[2]; + void *buf; + + if (arg->num_params != ARRAY_SIZE(params) || + optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params) || + params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT || + params[1].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + buf = tee_shm_get_va(params[1].u.memref.shm, + params[1].u.memref.shm_offs); + if (!buf) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + mutex_lock(&optee->rpmb_dev_mutex); + rdev = rpmb_dev_find_device(NULL, optee->rpmb_dev, rpc_rpmb_match); + rpmb_dev_put(optee->rpmb_dev); + optee->rpmb_dev = rdev; + mutex_unlock(&optee->rpmb_dev_mutex); + + if (!rdev) { + arg->ret = TEEC_ERROR_ITEM_NOT_FOUND; + return; + } + + if (params[1].u.memref.size < rdev->descr.dev_id_len) { + arg->ret = TEEC_ERROR_SHORT_BUFFER; + return; + } + memcpy(buf, rdev->descr.dev_id, rdev->descr.dev_id_len); + params[1].u.memref.size = rdev->descr.dev_id_len; + params[0].u.value.a = rpmb_type_to_rpc_type(rdev->descr.type); + params[0].u.value.b = rdev->descr.capacity; + params[0].u.value.c = rdev->descr.reliable_wr_count; + if (optee->ops->to_msg_param(optee, arg->params, + arg->num_params, params)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + return; + } + + arg->ret = TEEC_SUCCESS; +} + +static void handle_rpc_func_rpmb_frames(struct tee_context *ctx, + struct optee *optee, + struct optee_msg_arg *arg) +{ + struct tee_param params[2]; + struct rpmb_dev *rdev; + void *p0, *p1; + + mutex_lock(&optee->rpmb_dev_mutex); + rdev = rpmb_dev_get(optee->rpmb_dev); + mutex_unlock(&optee->rpmb_dev_mutex); + if (!rdev) { + arg->ret = TEEC_ERROR_ITEM_NOT_FOUND; + return; + } + + if (arg->num_params != ARRAY_SIZE(params) || + optee->ops->from_msg_param(optee, params, arg->num_params, + arg->params) || + params[0].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT || + params[1].attr != TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + + p0 = tee_shm_get_va(params[0].u.memref.shm, + params[0].u.memref.shm_offs); + p1 = tee_shm_get_va(params[1].u.memref.shm, + params[1].u.memref.shm_offs); + if (rpmb_route_frames(rdev, p0, params[0].u.memref.size, p1, + params[1].u.memref.size)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + if (optee->ops->to_msg_param(optee, arg->params, + arg->num_params, params)) { + arg->ret = TEEC_ERROR_BAD_PARAMETERS; + goto out; + } + arg->ret = TEEC_SUCCESS; +out: + rpmb_dev_put(rdev); +} + void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, struct optee_msg_arg *arg) { @@ -277,6 +426,34 @@ void optee_rpc_cmd(struct tee_context *ctx, struct optee *optee, case OPTEE_RPC_CMD_I2C_TRANSFER: handle_rpc_func_cmd_i2c_transfer(ctx, arg); break; + /* + * optee->in_kernel_rpmb_routing true means that OP-TEE supports + * in-kernel RPMB routing _and_ that the RPMB subsystem is + * reachable. This is reported to user space with + * rpmb_routing_model=kernel in sysfs. + * + * rpmb_routing_model=kernel is also a promise to user space that + * RPMB access will not require supplicant support, hence the + * checks below. + */ + case OPTEE_RPC_CMD_RPMB_PROBE_RESET: + if (optee->in_kernel_rpmb_routing) + handle_rpc_func_rpmb_probe_reset(ctx, optee, arg); + else + handle_rpc_supp_cmd(ctx, optee, arg); + break; + case OPTEE_RPC_CMD_RPMB_PROBE_NEXT: + if (optee->in_kernel_rpmb_routing) + handle_rpc_func_rpmb_probe_next(ctx, optee, arg); + else + handle_rpc_supp_cmd(ctx, optee, arg); + break; + case OPTEE_RPC_CMD_RPMB_FRAMES: + if (optee->in_kernel_rpmb_routing) + handle_rpc_func_rpmb_frames(ctx, optee, arg); + else + handle_rpc_supp_cmd(ctx, optee, arg); + break; default: handle_rpc_supp_cmd(ctx, optee, arg); } diff --git a/drivers/tee/optee/smc_abi.c b/drivers/tee/optee/smc_abi.c index 844285d4f03c..e9456e3e74cc 100644 --- a/drivers/tee/optee/smc_abi.c +++ b/drivers/tee/optee/smc_abi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -1685,6 +1686,10 @@ static int optee_probe(struct platform_device *pdev) optee->smc.sec_caps = sec_caps; optee->rpc_param_count = rpc_param_count; + if (IS_REACHABLE(CONFIG_RPMB) && + (sec_caps & OPTEE_SMC_SEC_CAP_RPMB_PROBE)) + optee->in_kernel_rpmb_routing = true; + teedev = tee_device_alloc(&optee_clnt_desc, NULL, pool, optee); if (IS_ERR(teedev)) { rc = PTR_ERR(teedev); @@ -1699,6 +1704,8 @@ static int optee_probe(struct platform_device *pdev) } optee->supp_teedev = teedev; + optee_set_dev_group(optee); + rc = tee_device_register(optee->teedev); if (rc) goto err_unreg_supp_teedev; @@ -1712,6 +1719,7 @@ static int optee_probe(struct platform_device *pdev) optee->smc.memremaped_shm = memremaped_shm; optee->pool = pool; optee_shm_arg_cache_init(optee, arg_cache_flags); + mutex_init(&optee->rpmb_dev_mutex); platform_set_drvdata(pdev, optee); ctx = teedev_open(optee->teedev); @@ -1766,6 +1774,10 @@ static int optee_probe(struct platform_device *pdev) if (rc) goto err_disable_shm_cache; + INIT_WORK(&optee->rpmb_scan_bus_work, optee_bus_scan_rpmb); + optee->rpmb_intf.notifier_call = optee_rpmb_intf_rdev; + blocking_notifier_chain_register(&optee_rpmb_intf_added, + &optee->rpmb_intf); pr_info("initialized driver\n"); return 0; @@ -1779,6 +1791,8 @@ err_notif_uninit: err_close_ctx: teedev_close_context(ctx); err_supp_uninit: + rpmb_dev_put(optee->rpmb_dev); + mutex_destroy(&optee->rpmb_dev_mutex); optee_shm_arg_cache_uninit(optee); optee_supp_uninit(&optee->supp); mutex_destroy(&optee->call_queue.mutex); From d3596c73011d1b0e9b43ae12f1e0f491d6bb96eb Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 21 Aug 2024 15:24:06 +0200 Subject: [PATCH 21/41] mmc: core: Remove struct mmc_context_info The 'mmc_context_info' structure is unused. It has been introduced in: - commit 2220eedfd7ae ("mmc: fix async request mechanism for sequential read scenarios") in 2013-02 and its usages have been removed in: - commit 126b62700386 ("mmc: core: Remove code no longer needed after the switch to blk-mq") - commit 0fbfd1251830 ("mmc: block: Remove code no longer needed after the switch to blk-mq") in 2017-12. Now remove this unused structure. Signed-off-by: Christophe JAILLET Reviewed-by: Vladimir Zapolskiy Link: https://lore.kernel.org/r/232106a8a6a374dee25feea9b94498361568c10b.1724246389.git.christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- include/linux/mmc/host.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 88c6a76042ee..f85df7d045ca 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -291,20 +291,6 @@ struct mmc_slot { void *handler_priv; }; -/** - * mmc_context_info - synchronization details for mmc context - * @is_done_rcv wake up reason was done request - * @is_new_req wake up reason was new request - * @is_waiting_last_req mmc context waiting for single running request - * @wait wait queue - */ -struct mmc_context_info { - bool is_done_rcv; - bool is_new_req; - bool is_waiting_last_req; - wait_queue_head_t wait; -}; - struct regulator; struct mmc_pwrseq; From f5e1638bf3c785600e2ab53e5532007af5296581 Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Sat, 24 Aug 2024 01:59:17 +0300 Subject: [PATCH 22/41] mmc: core: remove left-over data structure declarations The last users of 'enum mmc_blk_status' and 'struct mmc_async_req' were removed by commit 126b62700386 ("mmc: core: Remove code no longer needed after the switch to blk-mq") in 2017, remove these two left-over data structures. Signed-off-by: Vladimir Zapolskiy Link: https://lore.kernel.org/r/20240823225917.2826156-1-vladimir.zapolskiy@linaro.org Signed-off-by: Ulf Hansson --- include/linux/mmc/core.h | 12 ------------ include/linux/mmc/host.h | 10 ---------- 2 files changed, 22 deletions(-) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 2c7928a50907..f0ac2e469b32 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -11,18 +11,6 @@ struct mmc_data; struct mmc_request; -enum mmc_blk_status { - MMC_BLK_SUCCESS = 0, - MMC_BLK_PARTIAL, - MMC_BLK_CMD_ERR, - MMC_BLK_RETRY, - MMC_BLK_ABORT, - MMC_BLK_DATA_ERR, - MMC_BLK_ECC_ERR, - MMC_BLK_NOMEDIUM, - MMC_BLK_NEW_REQUEST, -}; - struct mmc_command { u32 opcode; u32 arg; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f85df7d045ca..ac01cd1622ef 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -264,16 +264,6 @@ struct mmc_cqe_ops { void (*cqe_recovery_finish)(struct mmc_host *host); }; -struct mmc_async_req { - /* active mmc request */ - struct mmc_request *mrq; - /* - * Check error status of completed mmc request. - * Returns 0 if success otherwise non zero. - */ - enum mmc_blk_status (*err_check)(struct mmc_card *, struct mmc_async_req *); -}; - /** * struct mmc_slot - MMC slot functions * From 7eb42da6ab4a963677e3f692c4debdcc781cf4c9 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 27 Aug 2024 12:40:16 -0400 Subject: [PATCH 23/41] mmc: sdhci-of-dwcmshc: Add hw_reset() support for BlueField-3 SoC The eMMC RST_N register is implemented as secure register on the BlueField-3 SoC and controlled by TF-A. This commit adds the hw_reset() support which sends an SMC call to TF-A for the eMMC HW reset. Reviewed-by: David Thompson Signed-off-by: Liming Sun Link: https://lore.kernel.org/r/20240827164016.237617-1-limings@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index ba8960d8b2d4..8999b97263af 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -8,6 +8,7 @@ */ #include +#include #include #include #include @@ -201,6 +202,9 @@ SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_DMA) +/* SMC call for BlueField-3 eMMC RST_N */ +#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 + enum dwcmshc_rk_type { DWCMSHC_RK3568, DWCMSHC_RK3588, @@ -1111,6 +1115,29 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = { .irq = dwcmshc_cqe_irq_handler, }; +#ifdef CONFIG_ACPI +static void dwcmshc_bf3_hw_reset(struct sdhci_host *host) +{ + struct arm_smccc_res res = { 0 }; + + arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0, &res); + + if (res.a0) + pr_err("%s: RST_N failed.\n", mmc_hostname(host->mmc)); +} + +static const struct sdhci_ops sdhci_dwcmshc_bf3_ops = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, + .hw_reset = dwcmshc_bf3_hw_reset, +}; +#endif + static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { .set_clock = dwcmshc_rk3568_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -1163,7 +1190,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { #ifdef CONFIG_ACPI static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = { .pdata = { - .ops = &sdhci_dwcmshc_ops, + .ops = &sdhci_dwcmshc_bf3_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_ACMD23_BROKEN, From ee601384079cf683b4750a2ea9edbe653e444ad8 Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Wed, 28 Aug 2024 15:24:53 +0000 Subject: [PATCH 24/41] dt-bindings: mmc: Add support for rk3576 dw-mshc Add the compatible string for rockchip,rk3576-dw-mshc in its own new block, for devices that have internal phase settings instead of external clocks. Signed-off-by: Detlev Casanova Acked-by: Krzysztof Kozlowski Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/010201919996f687-08c1988a-f588-46fa-ad82-023068c316ba-000000@eu-west-1.amazonses.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index 211cd0b0bc5f..06df1269f247 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -43,6 +43,8 @@ properties: - rockchip,rv1108-dw-mshc - rockchip,rv1126-dw-mshc - const: rockchip,rk3288-dw-mshc + # for Rockchip RK3576 with phase tuning inside the controller + - const: rockchip,rk3576-dw-mshc reg: maxItems: 1 From 59903441f5e49d46478fefcccec41e2ba896b740 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 28 Aug 2024 15:24:55 +0000 Subject: [PATCH 25/41] mmc: dw_mmc-rockchip: Add internal phase support Some Rockchip devices put the phase settings into the dw_mmc controller. When the feature is present, the ciu-drive and ciu-sample clocks are not used and the phase configuration is done directly through the mmc controller. Signed-off-by: Shawn Lin Signed-off-by: Detlev Casanova Acked-by: Shawn Lin Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/010201919996fdae-8a9f843e-00a8-4131-98bf-a9da4ed04bfd-000000@eu-west-1.amazonses.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 171 +++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index b07190ba4b7a..75e9ac4bcd08 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -15,7 +15,17 @@ #include "dw_mmc.h" #include "dw_mmc-pltfm.h" -#define RK3288_CLKGEN_DIV 2 +#define RK3288_CLKGEN_DIV 2 +#define SDMMC_TIMING_CON0 0x130 +#define SDMMC_TIMING_CON1 0x134 +#define ROCKCHIP_MMC_DELAY_SEL BIT(10) +#define ROCKCHIP_MMC_DEGREE_MASK 0x3 +#define ROCKCHIP_MMC_DEGREE_OFFSET 1 +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) +#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 }; @@ -24,8 +34,143 @@ struct dw_mci_rockchip_priv_data { struct clk *sample_clk; int default_sample_phase; int num_phases; + bool internal_phase; }; +/* + * Each fine delay is between 44ps-77ps. Assume each fine delay is 60ps to + * simplify calculations. So 45degs could be anywhere between 33deg and 57.8deg. + */ +static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample) +{ + unsigned long rate = clk_get_rate(host->ciu_clk); + u32 raw_value; + u16 degrees; + u32 delay_num = 0; + + /* Constant signal, no measurable phase shift */ + if (!rate) + return 0; + + if (sample) + raw_value = mci_readl(host, TIMING_CON1); + else + raw_value = mci_readl(host, TIMING_CON0); + + raw_value >>= ROCKCHIP_MMC_DEGREE_OFFSET; + degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90; + + if (raw_value & ROCKCHIP_MMC_DELAY_SEL) { + /* degrees/delaynum * 1000000 */ + unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) * + 36 * (rate / 10000); + + delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK); + delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET; + degrees += DIV_ROUND_CLOSEST(delay_num * factor, 1000000); + } + + return degrees % 360; +} + +static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample) +{ + struct dw_mci_rockchip_priv_data *priv = host->priv; + struct clk *clock = sample ? priv->sample_clk : priv->drv_clk; + + if (priv->internal_phase) + return rockchip_mmc_get_internal_phase(host, sample); + else + return clk_get_phase(clock); +} + +static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees) +{ + unsigned long rate = clk_get_rate(host->ciu_clk); + u8 nineties, remainder; + u8 delay_num; + u32 raw_value; + u32 delay; + + /* + * The below calculation is based on the output clock from + * MMC host to the card, which expects the phase clock inherits + * the clock rate from its parent, namely the output clock + * provider of MMC host. However, things may go wrong if + * (1) It is orphan. + * (2) It is assigned to the wrong parent. + * + * This check help debug the case (1), which seems to be the + * most likely problem we often face and which makes it difficult + * for people to debug unstable mmc tuning results. + */ + if (!rate) { + dev_err(host->dev, "%s: invalid clk rate\n", __func__); + return -EINVAL; + } + + nineties = degrees / 90; + remainder = (degrees % 90); + + /* + * Due to the inexact nature of the "fine" delay, we might + * actually go non-monotonic. We don't go _too_ monotonic + * though, so we should be OK. Here are options of how we may + * work: + * + * Ideally we end up with: + * 1.0, 2.0, ..., 69.0, 70.0, ..., 89.0, 90.0 + * + * On one extreme (if delay is actually 44ps): + * .73, 1.5, ..., 50.6, 51.3, ..., 65.3, 90.0 + * The other (if delay is actually 77ps): + * 1.3, 2.6, ..., 88.6. 89.8, ..., 114.0, 90 + * + * It's possible we might make a delay that is up to 25 + * degrees off from what we think we're making. That's OK + * though because we should be REALLY far from any bad range. + */ + + /* + * Convert to delay; do a little extra work to make sure we + * don't overflow 32-bit / 64-bit numbers. + */ + delay = 10000000; /* PSECS_PER_SEC / 10000 / 10 */ + delay *= remainder; + delay = DIV_ROUND_CLOSEST(delay, + (rate / 1000) * 36 * + (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10)); + + delay_num = (u8) min_t(u32, delay, 255); + + raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0; + raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET; + raw_value |= nineties; + + if (sample) + mci_writel(host, TIMING_CON1, HIWORD_UPDATE(raw_value, 0x07ff, 1)); + else + mci_writel(host, TIMING_CON0, HIWORD_UPDATE(raw_value, 0x07ff, 1)); + + dev_dbg(host->dev, "set %s_phase(%d) delay_nums=%u actual_degrees=%d\n", + sample ? "sample" : "drv", degrees, delay_num, + rockchip_mmc_get_phase(host, sample) + ); + + return 0; +} + +static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees) +{ + struct dw_mci_rockchip_priv_data *priv = host->priv; + struct clk *clock = sample ? priv->sample_clk : priv->drv_clk; + + if (priv->internal_phase) + return rockchip_mmc_set_internal_phase(host, sample, degrees); + else + return clk_set_phase(clock, degrees); +} + static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_rockchip_priv_data *priv = host->priv; @@ -64,7 +209,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) /* Make sure we use phases which we can enumerate with */ if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS) - clk_set_phase(priv->sample_clk, priv->default_sample_phase); + rockchip_mmc_set_phase(host, true, priv->default_sample_phase); /* * Set the drive phase offset based on speed mode to achieve hold times. @@ -127,7 +272,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) break; } - clk_set_phase(priv->drv_clk, phase); + rockchip_mmc_set_phase(host, false, phase); } } @@ -151,6 +296,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) int longest_range_len = -1; int longest_range = -1; int middle_phase; + int phase; if (IS_ERR(priv->sample_clk)) { dev_err(host->dev, "Tuning clock (sample_clk) not defined.\n"); @@ -164,8 +310,10 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) /* Try each phase and extract good ranges */ for (i = 0; i < priv->num_phases; ) { - clk_set_phase(priv->sample_clk, - TUNING_ITERATION_TO_PHASE(i, priv->num_phases)); + rockchip_mmc_set_phase(host, true, + TUNING_ITERATION_TO_PHASE( + i, + priv->num_phases)); v = !mmc_send_tuning(mmc, opcode, NULL); @@ -211,7 +359,8 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) } if (ranges[0].start == 0 && ranges[0].end == priv->num_phases - 1) { - clk_set_phase(priv->sample_clk, priv->default_sample_phase); + rockchip_mmc_set_phase(host, true, priv->default_sample_phase); + dev_info(host->dev, "All phases work, using default phase %d.", priv->default_sample_phase); goto free; @@ -248,12 +397,10 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) middle_phase = ranges[longest_range].start + longest_range_len / 2; middle_phase %= priv->num_phases; - dev_info(host->dev, "Successfully tuned phase to %d\n", - TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases)); + phase = TUNING_ITERATION_TO_PHASE(middle_phase, priv->num_phases); + dev_info(host->dev, "Successfully tuned phase to %d\n", phase); - clk_set_phase(priv->sample_clk, - TUNING_ITERATION_TO_PHASE(middle_phase, - priv->num_phases)); + rockchip_mmc_set_phase(host, true, phase); free: kfree(ranges); @@ -287,6 +434,8 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host) host->priv = priv; + priv->internal_phase = false; + return 0; } From 73abb1f16e28d5a41d0abea779a3f0b75cf8823e Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Wed, 28 Aug 2024 15:24:56 +0000 Subject: [PATCH 26/41] mmc: dw_mmc-rockchip: Add support for rk3576 SoCs On rk3576 the tunable clocks are inside the controller itself, removing the need for the "ciu-drive" and "ciu-sample" clocks. That makes it a new type of controller that has its own dt_parse function. Signed-off-by: Detlev Casanova Reviewed-by: Heiko Stuebner Link: https://lore.kernel.org/r/010201919997044d-c3a008d1-afbc-462f-a928-fc1ece785bdb-000000@eu-west-1.amazonses.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 48 ++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 75e9ac4bcd08..f96260fd143b 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -407,7 +407,7 @@ free: return ret; } -static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +static int dw_mci_common_parse_dt(struct dw_mci *host) { struct device_node *np = host->dev->of_node; struct dw_mci_rockchip_priv_data *priv; @@ -417,13 +417,29 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host) return -ENOMEM; if (of_property_read_u32(np, "rockchip,desired-num-phases", - &priv->num_phases)) + &priv->num_phases)) priv->num_phases = 360; if (of_property_read_u32(np, "rockchip,default-sample-phase", - &priv->default_sample_phase)) + &priv->default_sample_phase)) priv->default_sample_phase = 0; + host->priv = priv; + + return 0; +} + +static int dw_mci_rk3288_parse_dt(struct dw_mci *host) +{ + struct dw_mci_rockchip_priv_data *priv; + int err; + + err = dw_mci_common_parse_dt(host); + if (err) + return err; + + priv = host->priv; + priv->drv_clk = devm_clk_get(host->dev, "ciu-drive"); if (IS_ERR(priv->drv_clk)) dev_dbg(host->dev, "ciu-drive not available\n"); @@ -432,13 +448,25 @@ static int dw_mci_rk3288_parse_dt(struct dw_mci *host) if (IS_ERR(priv->sample_clk)) dev_dbg(host->dev, "ciu-sample not available\n"); - host->priv = priv; - priv->internal_phase = false; return 0; } +static int dw_mci_rk3576_parse_dt(struct dw_mci *host) +{ + struct dw_mci_rockchip_priv_data *priv; + int err = dw_mci_common_parse_dt(host); + if (err) + return err; + + priv = host->priv; + + priv->internal_phase = true; + + return 0; +} + static int dw_mci_rockchip_init(struct dw_mci *host) { int ret, i; @@ -480,11 +508,21 @@ static const struct dw_mci_drv_data rk3288_drv_data = { .init = dw_mci_rockchip_init, }; +static const struct dw_mci_drv_data rk3576_drv_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_rk3288_set_ios, + .execute_tuning = dw_mci_rk3288_execute_tuning, + .parse_dt = dw_mci_rk3576_parse_dt, + .init = dw_mci_rockchip_init, +}; + static const struct of_device_id dw_mci_rockchip_match[] = { { .compatible = "rockchip,rk2928-dw-mshc", .data = &rk2928_drv_data }, { .compatible = "rockchip,rk3288-dw-mshc", .data = &rk3288_drv_data }, + { .compatible = "rockchip,rk3576-dw-mshc", + .data = &rk3576_drv_data }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match); From 4c0a6a0ac902dbf184ae7be1f1fce225cc0b7380 Mon Sep 17 00:00:00 2001 From: Chanwoo Lee Date: Thu, 29 Aug 2024 11:47:09 +0900 Subject: [PATCH 27/41] mmc: core: Replace the argument of mmc_sd_switch() with defines Replace with already defined values for readability. While at it, let's also change the mode-parameter from an int to bool, as the only used values are 0 or 1. Signed-off-by: Chanwoo Lee Link: https://lore.kernel.org/r/20240829024709.402285-1-cw9316.lee@samsung.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 13 ++++++++----- drivers/mmc/core/sd_ops.c | 3 +-- include/linux/mmc/host.h | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index ee37ad14e79e..d7318c2647da 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -346,7 +346,7 @@ static int mmc_read_switch(struct mmc_card *card) * The argument does not matter, as the support bits do not * change with the arguments. */ - err = mmc_sd_switch(card, 0, 0, 0, status); + err = mmc_sd_switch(card, SD_SWITCH_CHECK, 0, 0, status); if (err) { /* * If the host or the card can't do the switch, @@ -402,7 +402,8 @@ int mmc_sd_switch_hs(struct mmc_card *card) if (!status) return -ENOMEM; - err = mmc_sd_switch(card, 1, 0, HIGH_SPEED_BUS_SPEED, status); + err = mmc_sd_switch(card, SD_SWITCH_SET, 0, + HIGH_SPEED_BUS_SPEED, status); if (err) goto out; @@ -434,7 +435,8 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) card_drv_type, &drv_type); if (drive_strength) { - err = mmc_sd_switch(card, 1, 2, drive_strength, status); + err = mmc_sd_switch(card, SD_SWITCH_SET, 2, + drive_strength, status); if (err) return err; if ((status[15] & 0xF) != drive_strength) { @@ -514,7 +516,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status) return 0; } - err = mmc_sd_switch(card, 1, 0, card->sd_bus_speed, status); + err = mmc_sd_switch(card, SD_SWITCH_SET, 0, card->sd_bus_speed, status); if (err) return err; @@ -605,7 +607,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status) current_limit = SD_SET_CURRENT_LIMIT_200; if (current_limit != SD_SET_CURRENT_NO_CHANGE) { - err = mmc_sd_switch(card, 1, 3, current_limit, status); + err = mmc_sd_switch(card, SD_SWITCH_SET, 3, + current_limit, status); if (err) return err; diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 8b9b34286ef3..f93c392040ae 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -336,14 +336,13 @@ int mmc_app_send_scr(struct mmc_card *card) return 0; } -int mmc_sd_switch(struct mmc_card *card, int mode, int group, +int mmc_sd_switch(struct mmc_card *card, bool mode, int group, u8 value, u8 *resp) { u32 cmd_args; /* NOTE: caller guarantees resp is heap-allocated */ - mode = !!mode; value &= 0xF; cmd_args = mode << 31 | 0x00FFFFFF; cmd_args &= ~(0xF << (group * 4)); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ac01cd1622ef..6a31ed02d3ff 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -648,7 +648,8 @@ static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host, host->err_stats[stat] += 1; } -int mmc_sd_switch(struct mmc_card *card, int mode, int group, u8 value, u8 *resp); +int mmc_sd_switch(struct mmc_card *card, bool mode, int group, + u8 value, u8 *resp); int mmc_send_status(struct mmc_card *card, u32 *status); int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode); From 03117a4934d6ea3cc9c84baf80c05322485a2b5a Mon Sep 17 00:00:00 2001 From: Seunghwan Baek Date: Thu, 29 Aug 2024 15:18:23 +0900 Subject: [PATCH 28/41] mmc: cqhci: Make use of cqhci_halted() routine Make use of cqhci_halted() in couple places to avoid open-coding. Signed-off-by: Seunghwan Baek Reviewed-by: Ritesh Harjani Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240829061823.3718-3-sh8267.baek@samsung.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/cqhci-core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index a02da26a1efd..178277d90c31 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -33,6 +33,11 @@ struct cqhci_slot { #define CQHCI_HOST_OTHER BIT(4) }; +static bool cqhci_halted(struct cqhci_host *cq_host) +{ + return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT; +} + static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag) { return cq_host->desc_base + (tag * cq_host->slot_sz); @@ -282,7 +287,7 @@ static void __cqhci_enable(struct cqhci_host *cq_host) cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) + if (cqhci_halted(cq_host)) cqhci_writel(cq_host, 0, CQHCI_CTL); mmc->cqe_on = true; @@ -617,7 +622,7 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) cqhci_writel(cq_host, 0, CQHCI_CTL); mmc->cqe_on = true; pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); - if (cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT) { + if (cqhci_halted(cq_host)) { pr_err("%s: cqhci: CQE failed to exit halt state\n", mmc_hostname(mmc)); } @@ -953,11 +958,6 @@ static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout) return ret; } -static bool cqhci_halted(struct cqhci_host *cq_host) -{ - return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT; -} - static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout) { struct cqhci_host *cq_host = mmc->cqe_private; From d2253bfa830e907eddad13d9de99d7b6df8585a1 Mon Sep 17 00:00:00 2001 From: Riyan Dhiman Date: Sun, 1 Sep 2024 23:03:09 +0530 Subject: [PATCH 29/41] mmc: core: Calculate size from pointer Calculate the size from pointer instead of struct to adhere to linux kernel coding style. Issue reported by checkpatch. This commit has no functional changes. Signed-off-by: Riyan Dhiman Link: https://lore.kernel.org/r/20240901173309.7124-1-riyandhiman14@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index cc7318089cf2..2ff053f2be11 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2530,7 +2530,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, return ERR_PTR(devidx); } - md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + md = kzalloc(sizeof(*md), GFP_KERNEL); if (!md) { ret = -ENOMEM; goto out; From 6f25e5deca7739a27462ef26fb5c07cfb926c4e4 Mon Sep 17 00:00:00 2001 From: Riyan Dhiman Date: Sun, 1 Sep 2024 23:52:44 +0530 Subject: [PATCH 30/41] mmc: core: Convert simple_stroul to kstroul simple_strtoul() is obsolete and lacks proper error handling, making it unsafe for converting strings to unsigned long values. Replace it with kstrtoul(), which provides robust error checking and better safety. This change improves the reliability of the string-to-integer conversion and aligns with current kernel coding standards. Error handling is added to catch conversion failures, returning -EINVAL when input is invalid. Issue reported by checkpatch: - WARNING: simple_strtoul is obsolete, use kstrtoul instead Signed-off-by: Riyan Dhiman Link: https://lore.kernel.org/r/20240901182244.45543-1-riyandhiman14@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 2ff053f2be11..f58bea534004 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -353,10 +353,10 @@ static ssize_t force_ro_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int ret; - char *end; struct mmc_blk_data *md = mmc_blk_get(dev_to_disk(dev)); - unsigned long set = simple_strtoul(buf, &end, 0); - if (end == buf) { + unsigned long set; + + if (kstrtoul(buf, 0, &set)) { ret = -EINVAL; goto out; } From 1c97ea115f89d096ec403f0827cc01671e3daba3 Mon Sep 17 00:00:00 2001 From: Dharma Balasubiramani Date: Mon, 2 Sep 2024 16:27:09 +0530 Subject: [PATCH 31/41] dt-bindings: mmc: sdhci-atmel: Convert to json schema Convert sdhci-atmel documentation to yaml format. The new file will inherit from sdhci-common.yaml. Note: Add microchip,sama7g5-sdhci to compatible list as we already use it in the DT. Signed-off-by: Dharma Balasubiramani Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240902-atmel-sdhci-v4-1-96912fab6b2d@microchip.com Signed-off-by: Ulf Hansson --- .../bindings/mmc/atmel,sama5d2-sdhci.yaml | 92 +++++++++++++++++++ .../devicetree/bindings/mmc/sdhci-atmel.txt | 35 ------- 2 files changed, 92 insertions(+), 35 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/atmel,sama5d2-sdhci.yaml delete mode 100644 Documentation/devicetree/bindings/mmc/sdhci-atmel.txt diff --git a/Documentation/devicetree/bindings/mmc/atmel,sama5d2-sdhci.yaml b/Documentation/devicetree/bindings/mmc/atmel,sama5d2-sdhci.yaml new file mode 100644 index 000000000000..8c8ade88e8fe --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/atmel,sama5d2-sdhci.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/atmel,sama5d2-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Atmel SDHCI controller + +maintainers: + - Aubin Constans + - Nicolas Ferre + +description: + Bindings for the SDHCI controller found in Atmel/Microchip SoCs. + +properties: + compatible: + oneOf: + - enum: + - atmel,sama5d2-sdhci + - microchip,sam9x60-sdhci + - items: + - enum: + - microchip,sam9x7-sdhci + - microchip,sama7g5-sdhci + - const: microchip,sam9x60-sdhci + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: hclock + - description: multclk + - description: baseclk + minItems: 2 + + clock-names: + items: + - const: hclock + - const: multclk + - const: baseclk + minItems: 2 + + microchip,sdcal-inverted: + type: boolean + description: + When present, polarity on the SDCAL SoC pin is inverted. The default + polarity for this signal is described in the datasheet. For instance on + SAMA5D2, the pin is usually tied to the GND with a resistor and a + capacitor (see "SDMMC I/O Calibration" chapter). + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - $ref: sdhci-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - atmel,sama5d2-sdhci + then: + properties: + clocks: + minItems: 3 + clock-names: + minItems: 3 + +unevaluatedProperties: false + +examples: + - | + #include + #include + mmc@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"; + assigned-clocks = <&sdmmc0_gclk>; + assigned-clock-rates = <480000000>; + }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt b/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt deleted file mode 100644 index a9fb0a91245f..000000000000 --- a/Documentation/devicetree/bindings/mmc/sdhci-atmel.txt +++ /dev/null @@ -1,35 +0,0 @@ -* 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" or "microchip,sam9x60-sdhci" - or "microchip,sam9x7-sdhci", "microchip,sam9x60-sdhci". -- clocks: Phandlers to the clocks. -- clock-names: Must be "hclock", "multclk", "baseclk" for - "atmel,sama5d2-sdhci". - Must be "hclock", "multclk" for "microchip,sam9x60-sdhci". - Must be "hclock", "multclk" for "microchip,sam9x7-sdhci". - -Optional properties: -- assigned-clocks: The same with "multclk". -- assigned-clock-rates The rate of "multclk" in order to not rely on the - gck configuration set by previous components. -- microchip,sdcal-inverted: when present, polarity on the SDCAL SoC pin is - inverted. The default polarity for this signal is described in the datasheet. - For instance on SAMA5D2, the pin is usually tied to the GND with a resistor - and a capacitor (see "SDMMC I/O Calibration" chapter). - -Example: - -mmc0: 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"; - assigned-clocks = <&sdmmc0_gclk>; - assigned-clock-rates = <480000000>; -}; From 38fb699795f0f24f1dcbe175537ec9c2ba500fa4 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Mon, 2 Sep 2024 15:33:31 +0300 Subject: [PATCH 32/41] mmc: core Convert UNSTUFF_BITS macro to inline function The UNSTUFF_BITS macro, which is defined in both drivers/mmc/core/mmc.c and drivers/mmc/core/sd.c, has been converted to an inline function to improve readability, maintainability, and type safety. Signed-off-by: Avri Altman Link: https://lore.kernel.org/r/20240902123331.3566447-1-avri.altman@wdc.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 106 ++++++++++++++------------------ drivers/mmc/core/mmc_ops.h | 14 +++++ drivers/mmc/core/sd.c | 120 ++++++++++++++++--------------------- 3 files changed, 113 insertions(+), 127 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 5b2f7c285461..6a23be214543 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -51,20 +51,6 @@ static const unsigned int taac_mant[] = { 35, 40, 45, 50, 55, 60, 70, 80, }; -#define UNSTUFF_BITS(resp,start,size) \ - ({ \ - const int __size = size; \ - const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ - const int __off = 3 - ((start) / 32); \ - const int __shft = (start) & 31; \ - u32 __res; \ - \ - __res = resp[__off] >> __shft; \ - if (__size + __shft > 32) \ - __res |= resp[__off-1] << ((32 - __shft) % 32); \ - __res & __mask; \ - }) - /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ @@ -85,36 +71,36 @@ static int mmc_decode_cid(struct mmc_card *card) switch (card->csd.mmca_vsn) { case 0: /* MMC v1.0 - v1.2 */ case 1: /* MMC v1.4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); - card->cid.serial = UNSTUFF_BITS(resp, 16, 24); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + card->cid.manfid = unstuff_bits(resp, 104, 24); + card->cid.prod_name[0] = unstuff_bits(resp, 96, 8); + card->cid.prod_name[1] = unstuff_bits(resp, 88, 8); + card->cid.prod_name[2] = unstuff_bits(resp, 80, 8); + card->cid.prod_name[3] = unstuff_bits(resp, 72, 8); + card->cid.prod_name[4] = unstuff_bits(resp, 64, 8); + card->cid.prod_name[5] = unstuff_bits(resp, 56, 8); + card->cid.prod_name[6] = unstuff_bits(resp, 48, 8); + card->cid.hwrev = unstuff_bits(resp, 44, 4); + card->cid.fwrev = unstuff_bits(resp, 40, 4); + card->cid.serial = unstuff_bits(resp, 16, 24); + card->cid.month = unstuff_bits(resp, 12, 4); + card->cid.year = unstuff_bits(resp, 8, 4) + 1997; break; case 2: /* MMC v2.0 - v2.2 */ case 3: /* MMC v3.1 - v3.3 */ case 4: /* MMC v4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.prv = UNSTUFF_BITS(resp, 48, 8); - card->cid.serial = UNSTUFF_BITS(resp, 16, 32); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + card->cid.manfid = unstuff_bits(resp, 120, 8); + card->cid.oemid = unstuff_bits(resp, 104, 16); + card->cid.prod_name[0] = unstuff_bits(resp, 96, 8); + card->cid.prod_name[1] = unstuff_bits(resp, 88, 8); + card->cid.prod_name[2] = unstuff_bits(resp, 80, 8); + card->cid.prod_name[3] = unstuff_bits(resp, 72, 8); + card->cid.prod_name[4] = unstuff_bits(resp, 64, 8); + card->cid.prod_name[5] = unstuff_bits(resp, 56, 8); + card->cid.prv = unstuff_bits(resp, 48, 8); + card->cid.serial = unstuff_bits(resp, 16, 32); + card->cid.month = unstuff_bits(resp, 12, 4); + card->cid.year = unstuff_bits(resp, 8, 4) + 1997; break; default: @@ -161,43 +147,43 @@ static int mmc_decode_csd(struct mmc_card *card) * v1.2 has extra information in bits 15, 11 and 10. * We also support eMMC v4.4 & v4.41. */ - csd->structure = UNSTUFF_BITS(resp, 126, 2); + csd->structure = unstuff_bits(resp, 126, 2); if (csd->structure == 0) { pr_err("%s: unrecognised CSD structure version %d\n", mmc_hostname(card->host), csd->structure); return -EINVAL; } - csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); + csd->mmca_vsn = unstuff_bits(resp, 122, 4); + m = unstuff_bits(resp, 115, 4); + e = unstuff_bits(resp, 112, 3); csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; - csd->taac_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + csd->taac_clks = unstuff_bits(resp, 104, 8) * 100; - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); + m = unstuff_bits(resp, 99, 4); + e = unstuff_bits(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + csd->cmdclass = unstuff_bits(resp, 84, 12); - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); + e = unstuff_bits(resp, 47, 3); + m = unstuff_bits(resp, 62, 12); csd->capacity = (1 + m) << (e + 2); - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + csd->read_blkbits = unstuff_bits(resp, 80, 4); + csd->read_partial = unstuff_bits(resp, 79, 1); + csd->write_misalign = unstuff_bits(resp, 78, 1); + csd->read_misalign = unstuff_bits(resp, 77, 1); + csd->dsr_imp = unstuff_bits(resp, 76, 1); + csd->r2w_factor = unstuff_bits(resp, 26, 3); + csd->write_blkbits = unstuff_bits(resp, 22, 4); + csd->write_partial = unstuff_bits(resp, 21, 1); if (csd->write_blkbits >= 9) { - a = UNSTUFF_BITS(resp, 42, 5); - b = UNSTUFF_BITS(resp, 37, 5); + a = unstuff_bits(resp, 42, 5); + b = unstuff_bits(resp, 37, 5); csd->erase_size = (a + 1) * (b + 1); csd->erase_size <<= csd->write_blkbits - 9; - csd->wp_grp_size = UNSTUFF_BITS(resp, 32, 5); + csd->wp_grp_size = unstuff_bits(resp, 32, 5); } return 0; diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 92d4194c7893..06017110e1b0 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -56,5 +56,19 @@ int mmc_cmdq_enable(struct mmc_card *card); int mmc_cmdq_disable(struct mmc_card *card); int mmc_sanitize(struct mmc_card *card, unsigned int timeout_ms); +static inline u32 unstuff_bits(const u32 *resp, int start, int size) +{ + const int __size = size; + const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; + const int __off = 3 - (start / 32); + const int __shft = start & 31; + u32 __res = resp[__off] >> __shft; + + if (__size + __shft > 32) + __res |= resp[__off - 1] << ((32 - __shft) % 32); + + return __res & __mask; +} + #endif diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index d7318c2647da..12fe282bea77 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -56,20 +56,6 @@ static const unsigned int sd_au_size[] = { SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512, }; -#define UNSTUFF_BITS(resp,start,size) \ - ({ \ - const int __size = size; \ - const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ - const int __off = 3 - ((start) / 32); \ - const int __shft = (start) & 31; \ - u32 __res; \ - \ - __res = resp[__off] >> __shft; \ - if (__size + __shft > 32) \ - __res |= resp[__off-1] << ((32 - __shft) % 32); \ - __res & __mask; \ - }) - #define SD_POWEROFF_NOTIFY_TIMEOUT_MS 1000 #define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000 @@ -95,18 +81,18 @@ void mmc_decode_cid(struct mmc_card *card) * SD doesn't currently have a version field so we will * have to assume we can parse this. */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); - card->cid.serial = UNSTUFF_BITS(resp, 24, 32); - card->cid.year = UNSTUFF_BITS(resp, 12, 8); - card->cid.month = UNSTUFF_BITS(resp, 8, 4); + card->cid.manfid = unstuff_bits(resp, 120, 8); + card->cid.oemid = unstuff_bits(resp, 104, 16); + card->cid.prod_name[0] = unstuff_bits(resp, 96, 8); + card->cid.prod_name[1] = unstuff_bits(resp, 88, 8); + card->cid.prod_name[2] = unstuff_bits(resp, 80, 8); + card->cid.prod_name[3] = unstuff_bits(resp, 72, 8); + card->cid.prod_name[4] = unstuff_bits(resp, 64, 8); + card->cid.hwrev = unstuff_bits(resp, 60, 4); + card->cid.fwrev = unstuff_bits(resp, 56, 4); + card->cid.serial = unstuff_bits(resp, 24, 32); + card->cid.year = unstuff_bits(resp, 12, 8); + card->cid.month = unstuff_bits(resp, 8, 4); card->cid.year += 2000; /* SD cards year offset */ } @@ -120,41 +106,41 @@ static int mmc_decode_csd(struct mmc_card *card) unsigned int e, m, csd_struct; u32 *resp = card->raw_csd; - csd_struct = UNSTUFF_BITS(resp, 126, 2); + csd_struct = unstuff_bits(resp, 126, 2); switch (csd_struct) { case 0: - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); + m = unstuff_bits(resp, 115, 4); + e = unstuff_bits(resp, 112, 3); csd->taac_ns = (taac_exp[e] * taac_mant[m] + 9) / 10; - csd->taac_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + csd->taac_clks = unstuff_bits(resp, 104, 8) * 100; - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); + m = unstuff_bits(resp, 99, 4); + e = unstuff_bits(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + csd->cmdclass = unstuff_bits(resp, 84, 12); - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); + e = unstuff_bits(resp, 47, 3); + m = unstuff_bits(resp, 62, 12); csd->capacity = (1 + m) << (e + 2); - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + csd->read_blkbits = unstuff_bits(resp, 80, 4); + csd->read_partial = unstuff_bits(resp, 79, 1); + csd->write_misalign = unstuff_bits(resp, 78, 1); + csd->read_misalign = unstuff_bits(resp, 77, 1); + csd->dsr_imp = unstuff_bits(resp, 76, 1); + csd->r2w_factor = unstuff_bits(resp, 26, 3); + csd->write_blkbits = unstuff_bits(resp, 22, 4); + csd->write_partial = unstuff_bits(resp, 21, 1); - if (UNSTUFF_BITS(resp, 46, 1)) { + if (unstuff_bits(resp, 46, 1)) { csd->erase_size = 1; } else if (csd->write_blkbits >= 9) { - csd->erase_size = UNSTUFF_BITS(resp, 39, 7) + 1; + csd->erase_size = unstuff_bits(resp, 39, 7) + 1; csd->erase_size <<= csd->write_blkbits - 9; } - if (UNSTUFF_BITS(resp, 13, 1)) + if (unstuff_bits(resp, 13, 1)) mmc_card_set_readonly(card); break; case 1: @@ -169,17 +155,17 @@ static int mmc_decode_csd(struct mmc_card *card) csd->taac_ns = 0; /* Unused */ csd->taac_clks = 0; /* Unused */ - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); + m = unstuff_bits(resp, 99, 4); + e = unstuff_bits(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - csd->c_size = UNSTUFF_BITS(resp, 48, 22); + csd->cmdclass = unstuff_bits(resp, 84, 12); + csd->c_size = unstuff_bits(resp, 48, 22); /* SDXC cards have a minimum C_SIZE of 0x00FFFF */ if (csd->c_size >= 0xFFFF) mmc_card_set_ext_capacity(card); - m = UNSTUFF_BITS(resp, 48, 22); + m = unstuff_bits(resp, 48, 22); csd->capacity = (1 + m) << 10; csd->read_blkbits = 9; @@ -191,7 +177,7 @@ static int mmc_decode_csd(struct mmc_card *card) csd->write_partial = 0; csd->erase_size = 1; - if (UNSTUFF_BITS(resp, 13, 1)) + if (unstuff_bits(resp, 13, 1)) mmc_card_set_readonly(card); break; default: @@ -217,33 +203,33 @@ static int mmc_decode_scr(struct mmc_card *card) resp[3] = card->raw_scr[1]; resp[2] = card->raw_scr[0]; - scr_struct = UNSTUFF_BITS(resp, 60, 4); + scr_struct = unstuff_bits(resp, 60, 4); if (scr_struct != 0) { pr_err("%s: unrecognised SCR structure version %d\n", mmc_hostname(card->host), scr_struct); return -EINVAL; } - scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); - scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + scr->sda_vsn = unstuff_bits(resp, 56, 4); + scr->bus_widths = unstuff_bits(resp, 48, 4); if (scr->sda_vsn == SCR_SPEC_VER_2) /* Check if Physical Layer Spec v3.0 is supported */ - scr->sda_spec3 = UNSTUFF_BITS(resp, 47, 1); + scr->sda_spec3 = unstuff_bits(resp, 47, 1); if (scr->sda_spec3) { - scr->sda_spec4 = UNSTUFF_BITS(resp, 42, 1); - scr->sda_specx = UNSTUFF_BITS(resp, 38, 4); + scr->sda_spec4 = unstuff_bits(resp, 42, 1); + scr->sda_specx = unstuff_bits(resp, 38, 4); } - if (UNSTUFF_BITS(resp, 55, 1)) + if (unstuff_bits(resp, 55, 1)) card->erased_byte = 0xFF; else card->erased_byte = 0x0; if (scr->sda_spec4) - scr->cmds = UNSTUFF_BITS(resp, 32, 4); + scr->cmds = unstuff_bits(resp, 32, 4); else if (scr->sda_spec3) - scr->cmds = UNSTUFF_BITS(resp, 32, 2); + scr->cmds = unstuff_bits(resp, 32, 2); /* SD Spec says: any SD Card shall set at least bits 0 and 2 */ if (!(scr->bus_widths & SD_SCR_BUS_WIDTH_1) || @@ -289,17 +275,17 @@ static int mmc_read_ssr(struct mmc_card *card) kfree(raw_ssr); /* - * UNSTUFF_BITS only works with four u32s so we have to offset the + * unstuff_bits only works with four u32s so we have to offset the * bitfield positions accordingly. */ - au = UNSTUFF_BITS(card->raw_ssr, 428 - 384, 4); + au = unstuff_bits(card->raw_ssr, 428 - 384, 4); if (au) { if (au <= 9 || card->scr.sda_spec3) { card->ssr.au = sd_au_size[au]; - es = UNSTUFF_BITS(card->raw_ssr, 408 - 384, 16); - et = UNSTUFF_BITS(card->raw_ssr, 402 - 384, 6); + es = unstuff_bits(card->raw_ssr, 408 - 384, 16); + et = unstuff_bits(card->raw_ssr, 402 - 384, 6); if (es && et) { - eo = UNSTUFF_BITS(card->raw_ssr, 400 - 384, 2); + eo = unstuff_bits(card->raw_ssr, 400 - 384, 2); card->ssr.erase_timeout = (et * 1000) / es; card->ssr.erase_offset = eo * 1000; } @@ -313,7 +299,7 @@ static int mmc_read_ssr(struct mmc_card *card) * starting SD5.1 discard is supported if DISCARD_SUPPORT (b313) is set */ resp[3] = card->raw_ssr[6]; - discard_support = UNSTUFF_BITS(resp, 313 - 288, 1); + discard_support = unstuff_bits(resp, 313 - 288, 1); card->erase_arg = (card->scr.sda_specx && discard_support) ? SD_DISCARD_ARG : SD_ERASE_ARG; From b6c41df38c24f42482b5c5cd05220b8eb32ff6ff Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 2 Sep 2024 17:12:30 +0200 Subject: [PATCH 33/41] mmc: block: add RPMB dependency Prevent build error when CONFIG_RPMB=m and CONFIG_MMC_BLOCK=y by adding a dependency to CONFIG_RPMB for CONFIG_MMC_BLOCK block so the RPMB subsystem always is reachable if configured. This means that CONFIG_MMC_BLOCK automatically becomes compiled as a module if CONFIG_RPMB is compiled as a module. If CONFIG_RPMB isn't configured or is configured as built-in, CONFIG_MMC_BLOCK will remain unchanged. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202409021448.RSvcBPzt-lkp@intel.com/ Fixes: 7852028a35f0 ("mmc: block: register RPMB partition with the RPMB subsystem") Signed-off-by: Jens Wiklander Acked-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240902151231.3705204-1-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index bf4e29ef023c..14d2ecbb04d3 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE config MMC_BLOCK tristate "MMC block device driver" depends on BLOCK + depends on RPMB || !RPMB imply IOSCHED_BFQ default y help From edd3183c5c5f9fe7aede49a41678556bc8bf618f Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 2 Sep 2024 17:12:31 +0200 Subject: [PATCH 34/41] optee: add RPMB dependency Prevent build error when CONFIG_RPMB=m and CONFIG_OPTEE=y by adding a dependency to CONFIG_RPMB for CONFIG_OPTEE so the RPMB subsystem always is reachable if configured. This means that CONFIG_OPTEE automatically becomes compiled as a module if CONFIG_RPMB is compiled as a module. If CONFIG_RPMB isn't configured or is configured as built-in, CONFIG_OPTEE will remain unchanged. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202409021448.RSvcBPzt-lkp@intel.com/ Fixes: f0c8431568ee ("optee: probe RPMB device using RPMB subsystem") Signed-off-by: Jens Wiklander Link: https://lore.kernel.org/r/20240902151231.3705204-2-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- drivers/tee/optee/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/tee/optee/Kconfig b/drivers/tee/optee/Kconfig index 976928641aa6..7bb7990d0b07 100644 --- a/drivers/tee/optee/Kconfig +++ b/drivers/tee/optee/Kconfig @@ -4,6 +4,7 @@ config OPTEE tristate "OP-TEE" depends on HAVE_ARM_SMCCC depends on MMU + depends on RPMB || !RPMB help This implements the OP-TEE Trusted Execution Environment (TEE) driver. From 5854809b51f4e3868202d9b45e628133d76b48cd Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Mon, 2 Sep 2024 12:58:03 +0200 Subject: [PATCH 35/41] rpmb: fix error path in rpmb_dev_register() Until this patch was rpmb_dev_register() always freeing rdev in the error path. However, past device_register() it must not do that since the memory is now managed by the device even if it failed to register properly. So fix this by doing a put_device() before returning the error code. Fixes the smatch warning: drivers/misc/rpmb-core.c:204 rpmb_dev_register() warn: freeing device managed memory (leak): 'rdev' Fixes: 1e9046e3a154 ("rpmb: add Replay Protected Memory Block (RPMB) subsystem") Reported-by: Dan Carpenter Signed-off-by: Jens Wiklander Link: https://lore.kernel.org/r/20240902105803.2885544-1-jens.wiklander@linaro.org Signed-off-by: Ulf Hansson --- drivers/misc/rpmb-core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/misc/rpmb-core.c b/drivers/misc/rpmb-core.c index c8888267c222..bc68cde1a8bf 100644 --- a/drivers/misc/rpmb-core.c +++ b/drivers/misc/rpmb-core.c @@ -187,17 +187,15 @@ struct rpmb_dev *rpmb_dev_register(struct device *dev, rdev->dev.parent = dev; ret = device_register(&rdev->dev); - if (ret) - goto err_id_remove; + if (ret) { + put_device(&rdev->dev); + return ERR_PTR(ret); + } dev_dbg(&rdev->dev, "registered device\n"); return rdev; -err_id_remove: - mutex_lock(&rpmb_mutex); - ida_simple_remove(&rpmb_ida, rdev->id); - mutex_unlock(&rpmb_mutex); err_free_dev_id: kfree(rdev->descr.dev_id); err_free_rdev: From 819e4b3723bf191e0887207f7d456f4b928a87f0 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Tue, 3 Sep 2024 16:38:55 +0300 Subject: [PATCH 36/41] Documentation: mmc: Add mmc-test doc Add missing documentation for mmc_test. Reviewed-by: Christian Loehle Signed-off-by: Avri Altman Link: https://lore.kernel.org/r/20240903133855.3589845-1-avri.altman@wdc.com Signed-off-by: Ulf Hansson --- Documentation/driver-api/mmc/index.rst | 1 + Documentation/driver-api/mmc/mmc-test.rst | 299 ++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 Documentation/driver-api/mmc/mmc-test.rst diff --git a/Documentation/driver-api/mmc/index.rst b/Documentation/driver-api/mmc/index.rst index 7339736ac774..8188863e5959 100644 --- a/Documentation/driver-api/mmc/index.rst +++ b/Documentation/driver-api/mmc/index.rst @@ -10,4 +10,5 @@ MMC/SD/SDIO card support mmc-dev-attrs mmc-dev-parts mmc-async-req + mmc-test mmc-tools diff --git a/Documentation/driver-api/mmc/mmc-test.rst b/Documentation/driver-api/mmc/mmc-test.rst new file mode 100644 index 000000000000..1fe33eb43742 --- /dev/null +++ b/Documentation/driver-api/mmc/mmc-test.rst @@ -0,0 +1,299 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================== +MMC Test Framework +======================== + +Overview +======== + +The `mmc_test` framework is designed to test the performance and reliability of host controller drivers and all devices handled by the MMC subsystem. This includes not only MMC devices but also SD cards and other devices supported by the subsystem. + +The framework provides a variety of tests to evaluate different aspects of the host controller and device interactions, such as read and write performance, data integrity, and error handling. These tests help ensure that the host controller drivers and devices operate correctly under various conditions. + +The `mmc_test` framework is particularly useful for: + +- Verifying the functionality and performance of MMC and SD host controller drivers. +- Ensuring compatibility and reliability of MMC and SD devices. +- Identifying and diagnosing issues in the MMC subsystem. + +The results of the tests are logged in the kernel log, providing detailed information about the test outcomes and any encountered issues. + +Note: whatever is on your card will be overwritten by these tests. + +Initialization +============== + +To use the ``mmc_test`` framework, follow these steps: + +1. **Enable the MMC Test Framework**: + + Ensure that the ``CONFIG_MMC_TEST`` kernel configuration option is enabled. This can be done by configuring the kernel: + + .. code-block:: none + + make menuconfig + + Navigate to: + + Device Drivers ---> + <*> MMC/SD/SDIO card support ---> + [*] MMC host test driver + + Alternatively, you can enable it directly in the kernel configuration file: + + .. code-block:: none + + echo "CONFIG_MMC_TEST=y" >> .config + + Rebuild and install the kernel if necessary. + +2. **Load the MMC Test Module**: + + If the ``mmc_test`` framework is built as a module, you need to load it using ``modprobe``: + + .. code-block:: none + + modprobe mmc_test + +Binding the MMC Card for Testing +================================ + +To enable MMC testing, you need to unbind the MMC card from the ``mmcblk`` driver and bind it to the ``mmc_test`` driver. This allows the ``mmc_test`` framework to take control of the MMC card for testing purposes. + +1. Identify the MMC card: + + .. code-block:: sh + + ls /sys/bus/mmc/devices/ + + This will list the MMC devices, such as ``mmc0:0001``. + +2. Unbind the MMC card from the ``mmcblk`` driver: + + .. code-block:: sh + + echo 'mmc0:0001' > /sys/bus/mmc/drivers/mmcblk/unbind + +3. Bind the MMC card to the ``mmc_test`` driver: + + .. code-block:: sh + + echo 'mmc0:0001' > /sys/bus/mmc/drivers/mmc_test/bind + +After binding, you should see a line in the kernel log indicating that the card has been claimed for testing: + +.. code-block:: none + + mmc_test mmc0:0001: Card claimed for testing. + + +Usage - Debugfs Entries +======================= + +Once the ``mmc_test`` framework is enabled, you can interact with the following debugfs entries located in ``/sys/kernel/debug/mmc0/mmc0:0001``: + +1. **test**: + + This file is used to run specific tests. Write the test number to this file to execute a test. + + .. code-block:: sh + + echo > /sys/kernel/debug/mmc0/mmc0:0001/test + + The test result is indicated in the kernel log info. You can view the kernel log using the `dmesg` command or by checking the log file in `/var/log/`. + + .. code-block:: sh + + dmesg | grep mmc0 + + Example: + + To run test number 4 (Basic read with data verification): + + .. code-block:: sh + + echo 4 > /sys/kernel/debug/mmc0/mmc0:0001/test + + Check the kernel log for the result: + + .. code-block:: sh + + dmesg | grep mmc0 + +2. **testlist**: + + This file lists all available tests. You can read this file to see the list of tests and their corresponding numbers. + + .. code-block:: sh + + cat /sys/kernel/debug/mmc0/mmc0:0001/testlist + + The available tests are listed in the table below: + ++------+--------------------------------+---------------------------------------------+ +| Test | Test Name | Test Description | ++======+================================+=============================================+ +| 0 | Run all tests | Runs all available tests | ++------+--------------------------------+---------------------------------------------+ +| 1 | Basic write | Performs a basic write operation of a | +| | | single 512-Byte block to the MMC card | +| | | without data verification. | ++------+--------------------------------+---------------------------------------------+ +| 2 | Basic read | Same for read | ++------+--------------------------------+---------------------------------------------+ +| 3 | Basic write | Performs a basic write operation of a | +| | (with data verification) | single 512-Byte block to the MMC card | +| | | with data verification by reading back | +| | | the written data and comparing it. | ++------+--------------------------------+---------------------------------------------+ +| 4 | Basic read | Same for read | +| | (with data verification) | | ++------+--------------------------------+---------------------------------------------+ +| 5 | Multi-block write | Performs a multi-block write operation of | +| | | 8 blocks (each 512 bytes) to the MMC card. | ++------+--------------------------------+---------------------------------------------+ +| 6 | Multi-block read | Same for read | ++------+--------------------------------+---------------------------------------------+ +| 7 | Power of two block writes | Performs write operations with block sizes | +| | | that are powers of two, starting from 1 | +| | | byte up to 256 bytes, to the MMC card. | ++------+--------------------------------+---------------------------------------------+ +| 8 | Power of two block reads | Same for read | ++------+--------------------------------+---------------------------------------------+ +| 9 | Weird sized block writes | Performs write operations with varying | +| | | block sizes starting from 3 bytes and | +| | | increasing by 7 bytes each iteration, up | +| | | to 511 bytes, to the MMC card. | ++------+--------------------------------+---------------------------------------------+ +| 10 | Weird sized block reads | same for read | ++------+--------------------------------+---------------------------------------------+ +| 11 | Badly aligned write | Performs write operations with buffers | +| | | starting at different alignments (0 to 7 | +| | | bytes offset) to test how the MMC card | +| | | handles unaligned data transfers. | ++------+--------------------------------+---------------------------------------------+ +| 12 | Badly aligned read | same for read | ++------+--------------------------------+---------------------------------------------+ +| 13 | Badly aligned multi-block write| same for multi-write | ++------+--------------------------------+---------------------------------------------+ +| 14 | Badly aligned multi-block read | same for multi-read | ++------+--------------------------------+---------------------------------------------+ +| 15 | Proper xfer_size at write | intentionally create a broken transfer by | +| | (Start failure) | modifying the MMC request in a way that it | +| | | will not perform as expected, e.g. use | +| | | MMC_WRITE_BLOCK for a multi-block transfer | ++------+--------------------------------+---------------------------------------------+ +| 16 | Proper xfer_size at read | same for read | +| | (Start failure) | | ++------+--------------------------------+---------------------------------------------+ +| 17 | Proper xfer_size at write | same for 2 blocks | +| | (Midway failure) | | ++------+--------------------------------+---------------------------------------------+ +| 18 | Proper xfer_size at read | same for read | +| | (Midway failure) | | ++------+--------------------------------+---------------------------------------------+ +| 19 | Highmem write | use a high memory page | ++------+--------------------------------+---------------------------------------------+ +| 20 | Highmem read | same for read | ++------+--------------------------------+---------------------------------------------+ +| 21 | Multi-block highmem write | same for multi-write | ++------+--------------------------------+---------------------------------------------+ +| 22 | Multi-block highmem read | same for mult-read | ++------+--------------------------------+---------------------------------------------+ +| 23 | Best-case read performance | Performs 512K sequential read (non sg) | ++------+--------------------------------+---------------------------------------------+ +| 24 | Best-case write performance | same for write | ++------+--------------------------------+---------------------------------------------+ +| 25 | Best-case read performance | Same using sg | +| | (Into scattered pages) | | ++------+--------------------------------+---------------------------------------------+ +| 26 | Best-case write performance | same for write | +| | (From scattered pages) | | ++------+--------------------------------+---------------------------------------------+ +| 27 | Single read performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 28 | Single write performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 29 | Single trim performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 30 | Consecutive read performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 31 | Consecutive write performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 32 | Consecutive trim performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 33 | Random read performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 34 | Random write performance | By transfer size | ++------+--------------------------------+---------------------------------------------+ +| 35 | Large sequential read | Into scattered pages | ++------+--------------------------------+---------------------------------------------+ +| 36 | Large sequential write | From scattered pages | ++------+--------------------------------+---------------------------------------------+ +| 37 | Write performance | With blocking req 4k to 4MB | ++------+--------------------------------+---------------------------------------------+ +| 38 | Write performance | With non-blocking req 4k to 4MB | ++------+--------------------------------+---------------------------------------------+ +| 39 | Read performance | With blocking req 4k to 4MB | ++------+--------------------------------+---------------------------------------------+ +| 40 | Read performance | With non-blocking req 4k to 4MB | ++------+--------------------------------+---------------------------------------------+ +| 41 | Write performance | Blocking req 1 to 512 sg elems | ++------+--------------------------------+---------------------------------------------+ +| 42 | Write performance | Non-blocking req 1 to 512 sg elems | ++------+--------------------------------+---------------------------------------------+ +| 43 | Read performance | Blocking req 1 to 512 sg elems | ++------+--------------------------------+---------------------------------------------+ +| 44 | Read performance | Non-blocking req 1 to 512 sg elems | ++------+--------------------------------+---------------------------------------------+ +| 45 | Reset test | | ++------+--------------------------------+---------------------------------------------+ +| 46 | Commands during read | No Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ +| 47 | Commands during write | No Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ +| 48 | Commands during read | Use Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ +| 49 | Commands during write | Use Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ +| 50 | Commands during non-blocking | Read - use Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ +| 51 | Commands during non-blocking | Write - use Set Block Count (CMD23) | ++------+--------------------------------+---------------------------------------------+ + +Test Results +============ + +The results of the tests are logged in the kernel log. Each test logs the start, end, and result of the test. The possible results are: + +- **OK**: The test completed successfully. +- **FAILED**: The test failed. +- **UNSUPPORTED (by host)**: The test is unsupported by the host. +- **UNSUPPORTED (by card)**: The test is unsupported by the card. +- **ERROR**: An error occurred during the test. + +Example Kernel Log Output +========================= + +When running a test, you will see log entries similar to the following in the kernel log: + +.. code-block:: none + + [ 1234.567890] mmc0: Starting tests of card mmc0:0001... + [ 1234.567891] mmc0: Test case 4. Basic read (with data verification)... + [ 1234.567892] mmc0: Result: OK + [ 1234.567893] mmc0: Tests completed. + +In this example, test case 4 (Basic read with data verification) was executed, and the result was OK. + + +Contributing +============ + +Contributions to the `mmc_test` framework are welcome. Please follow the standard Linux kernel contribution guidelines and submit patches to the appropriate maintainers. + +Contact +======= + +For more information or to report issues, please contact the MMC subsystem maintainers. From 7e856617a1f34fc9714eaf562b78fcffacda4e3e Mon Sep 17 00:00:00 2001 From: Detlev Casanova Date: Wed, 4 Sep 2024 16:30:58 -0400 Subject: [PATCH 37/41] dt-bindings: mmc: Add support for rk3576 eMMC The device is compatible with rk3588, so add an entry for the 2 compatibles together. The rk3576 device has a power-domain that needs to be on for the eMMC to be used. Add it as a requirement. Signed-off-by: Detlev Casanova Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240904203154.253655-2-detlev.casanova@collabora.com Signed-off-by: Ulf Hansson --- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 80d50178d2e3..c3d5e0230af1 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -12,14 +12,18 @@ maintainers: properties: compatible: - enum: - - rockchip,rk3568-dwcmshc - - rockchip,rk3588-dwcmshc - - snps,dwcmshc-sdhci - - sophgo,cv1800b-dwcmshc - - sophgo,sg2002-dwcmshc - - sophgo,sg2042-dwcmshc - - thead,th1520-dwcmshc + oneOf: + - items: + - const: rockchip,rk3576-dwcmshc + - const: rockchip,rk3588-dwcmshc + - enum: + - rockchip,rk3568-dwcmshc + - rockchip,rk3588-dwcmshc + - snps,dwcmshc-sdhci + - sophgo,cv1800b-dwcmshc + - sophgo,sg2002-dwcmshc + - sophgo,sg2042-dwcmshc + - thead,th1520-dwcmshc reg: maxItems: 1 @@ -35,6 +39,9 @@ properties: minItems: 1 maxItems: 5 + power-domains: + maxItems: 1 + resets: maxItems: 5 @@ -97,6 +104,20 @@ allOf: - const: block - const: timer + - if: + properties: + compatible: + contains: + const: rockchip,rk3576-dwcmshc + + then: + required: + - power-domains + + else: + properties: + power-domains: false + unevaluatedProperties: false examples: From 901d16e462963cb0d824144be7448505b15ff4d6 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Wed, 4 Sep 2024 18:25:11 -0500 Subject: [PATCH 38/41] mmc: sdhci_am654: Add retry tuning Add retry tuning up to 10 times if we fail to find a failing region or no passing itapdly. This is necessary since some eMMC has been observed to never find a failing itapdly on the first couple of tuning iterations, but eventually does. Keep count of current tuning iteration using tuning_loop. It has been observed that the tuning algorithm does not need to loop more than 10 times before finding a failing itapdly. Signed-off-by: Judith Mendez Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240904232512.830778-2-jm@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 47 +++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 64e10f7c9faa..8eb6ce9f3b17 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -86,6 +86,7 @@ #define CLOCK_TOO_SLOW_HZ 50000000 #define SDHCI_AM654_AUTOSUSPEND_DELAY -1 +#define RETRY_TUNING_MAX 10 /* Command Queue Host Controller Interface Base address */ #define SDHCI_AM654_CQE_BASE_ADDR 0x200 @@ -151,6 +152,7 @@ struct sdhci_am654_data { u32 flags; u32 quirks; bool dll_enable; + u32 tuning_loop; #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) }; @@ -443,22 +445,23 @@ static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask) #define ITAPDLY_LENGTH 32 #define ITAPDLY_LAST_INDEX (ITAPDLY_LENGTH - 1) -static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window +static int sdhci_am654_calculate_itap(struct sdhci_host *host, struct window *fail_window, u8 num_fails, bool circular_buffer) { u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0; u8 first_fail_start = 0, last_fail_end = 0; - struct device *dev = mmc_dev(host->mmc); struct window pass_window = {0, 0, 0}; int prev_fail_end = -1; u8 i; - if (!num_fails) - return ITAPDLY_LAST_INDEX >> 1; + if (!num_fails) { + /* Retry tuning */ + return -1; + } if (fail_window->length == ITAPDLY_LENGTH) { - dev_err(dev, "No passing ITAPDLY, return 0\n"); - return 0; + /* Retry tuning */ + return -1; } first_fail_start = fail_window->start; @@ -494,8 +497,8 @@ static u32 sdhci_am654_calculate_itap(struct sdhci_host *host, struct window return (itap > ITAPDLY_LAST_INDEX) ? ITAPDLY_LAST_INDEX >> 1 : itap; } -static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, - u32 opcode) +static int sdhci_am654_do_tuning(struct sdhci_host *host, + u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); @@ -532,13 +535,30 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, if (fail_window[fail_index].length != 0) fail_index++; - itap = sdhci_am654_calculate_itap(host, fail_window, fail_index, - sdhci_am654->dll_enable); + return sdhci_am654_calculate_itap(host, fail_window, fail_index, + sdhci_am654->dll_enable); +} - sdhci_am654_write_itapdly(sdhci_am654, itap, sdhci_am654->itap_del_ena[timing]); +static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, + u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + unsigned char timing = host->mmc->ios.timing; + int itapdly; + do { + itapdly = sdhci_am654_do_tuning(host, opcode); + if (itapdly >= 0) + break; + } while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX); + + if (itapdly < 0) + return -1; + + sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]); /* Save ITAPDLY */ - sdhci_am654->itap_del_sel[timing] = itap; + sdhci_am654->itap_del_sel[timing] = itapdly; return 0; } @@ -742,6 +762,9 @@ static int sdhci_am654_init(struct sdhci_host *host) regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK, TUNINGFORSDR50_MASK); + /* Use to re-execute tuning */ + sdhci_am654->tuning_loop = 0; + ret = sdhci_setup_host(host); if (ret) return ret; From cf6444ba528f043398b112ac36e041a4d8685cb1 Mon Sep 17 00:00:00 2001 From: Judith Mendez Date: Wed, 4 Sep 2024 18:25:12 -0500 Subject: [PATCH 39/41] mmc: sdhci_am654: Add prints to tuning algorithm Add debug prints to tuning algorithm for debugging. Also add error print if we fail tuning. Signed-off-by: Judith Mendez Link: https://lore.kernel.org/r/20240904232512.830778-3-jm@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 8eb6ce9f3b17..0aa3c40ea6ed 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -450,17 +450,20 @@ static int sdhci_am654_calculate_itap(struct sdhci_host *host, struct window { u8 itap = 0, start_fail = 0, end_fail = 0, pass_length = 0; u8 first_fail_start = 0, last_fail_end = 0; + struct device *dev = mmc_dev(host->mmc); struct window pass_window = {0, 0, 0}; int prev_fail_end = -1; u8 i; if (!num_fails) { /* Retry tuning */ + dev_dbg(dev, "No failing region found, retry tuning\n"); return -1; } if (fail_window->length == ITAPDLY_LENGTH) { /* Retry tuning */ + dev_dbg(dev, "No passing itapdly, retry tuning\n"); return -1; } @@ -504,6 +507,7 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host, struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); unsigned char timing = host->mmc->ios.timing; struct window fail_window[ITAPDLY_LENGTH]; + struct device *dev = mmc_dev(host->mmc); u8 curr_pass, itap; u8 fail_index = 0; u8 prev_pass = 1; @@ -524,6 +528,7 @@ static int sdhci_am654_do_tuning(struct sdhci_host *host, if (!curr_pass) { fail_window[fail_index].end = itap; fail_window[fail_index].length++; + dev_dbg(dev, "Failed itapdly=%d\n", itap); } if (curr_pass && !prev_pass) @@ -545,6 +550,7 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); unsigned char timing = host->mmc->ios.timing; + struct device *dev = mmc_dev(host->mmc); int itapdly; do { @@ -553,9 +559,12 @@ static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host, break; } while (++sdhci_am654->tuning_loop < RETRY_TUNING_MAX); - if (itapdly < 0) + if (itapdly < 0) { + dev_err(dev, "Failed to find itapdly, fail tuning\n"); return -1; + } + dev_dbg(dev, "Passed tuning, final itapdly=%d\n", itapdly); sdhci_am654_write_itapdly(sdhci_am654, itapdly, sdhci_am654->itap_del_ena[timing]); /* Save ITAPDLY */ sdhci_am654->itap_del_sel[timing] = itapdly; From 73048a832587520c6222bacbe1bc7121409d8861 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 5 Sep 2024 16:17:06 +0300 Subject: [PATCH 40/41] optee: Fix a NULL vs IS_ERR() check The tee_shm_get_va() function never returns NULL, it returns error pointers. Update the check to match. Fixes: f0c8431568ee ("optee: probe RPMB device using RPMB subsystem") Signed-off-by: Dan Carpenter Reviewed-by: Jens Wiklander Link: https://lore.kernel.org/r/f8c12aed-b5d1-4522-bf95-622b8569706d@stanley.mountain Signed-off-by: Ulf Hansson --- drivers/tee/optee/rpc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/optee/rpc.c b/drivers/tee/optee/rpc.c index a4b49fd1d46d..ebbbd42b0e3e 100644 --- a/drivers/tee/optee/rpc.c +++ b/drivers/tee/optee/rpc.c @@ -332,7 +332,7 @@ static void handle_rpc_func_rpmb_probe_next(struct tee_context *ctx, } buf = tee_shm_get_va(params[1].u.memref.shm, params[1].u.memref.shm_offs); - if (!buf) { + if (IS_ERR(buf)) { arg->ret = TEEC_ERROR_BAD_PARAMETERS; return; } From cd3689b6772fbc1a4513934a5204fd2fa5b4426b Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 11 Sep 2024 11:09:09 +0200 Subject: [PATCH 41/41] mmc: core: Use dev_err_probe for deferred regulators In case vmmc or vqmmc regulator is not available yet, use dev_err_probe in order to set a deferred probe reason. This is a helpful hint in /sys/kernel/debug/devices_deferred Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20240911090910.3060749-1-alexander.stein@ew.tq-group.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/regulator.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/regulator.c b/drivers/mmc/core/regulator.c index 005247a49e51..01747ab1024e 100644 --- a/drivers/mmc/core/regulator.c +++ b/drivers/mmc/core/regulator.c @@ -255,7 +255,9 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) if (IS_ERR(mmc->supply.vmmc)) { if (PTR_ERR(mmc->supply.vmmc) == -EPROBE_DEFER) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "vmmc regulator not available\n"); + dev_dbg(dev, "No vmmc regulator found\n"); } else { ret = mmc_regulator_get_ocrmask(mmc->supply.vmmc); @@ -267,7 +269,9 @@ int mmc_regulator_get_supply(struct mmc_host *mmc) if (IS_ERR(mmc->supply.vqmmc)) { if (PTR_ERR(mmc->supply.vqmmc) == -EPROBE_DEFER) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "vqmmc regulator not available\n"); + dev_dbg(dev, "No vqmmc regulator found\n"); }