diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:15:25 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-04-15 14:15:25 -0700 |
| commit | 4ddd4f0651a710f33dfbb9dadd94f2bb0aa31aa8 (patch) | |
| tree | e1d5912e626588751347c425a54a3936fdf3f6e1 | |
| parent | b9962335d4c6dee152e95dce9f0dd32048735a6d (diff) | |
| parent | 52957cdad30f8011da1f4ef1338ba0339ca4c158 (diff) | |
Merge tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Add NXP vendor and IW61x device IDs for WiFi chips over SDIO
- Add quirk for incorrect manufacturing date
- Add support for manufacturing date beyond 2025
- Optimize support for secure erase/trim for some Kingston eMMCs
- Remove support for the legacy "enable-sdio-wakeup" DT property
- Use single block writes in the retry path
MMC host:
- dw_mmc:
- A great amount of cleanups/simplifications to improve the code
- Add clk_phase_map support
- Remove mshc DT alias support
- dw_mmc-rockchip:
- Fix runtime PM support for internal phase
- Add support for the RV1103B variant
- loongson2:
- Add support for the Loongson-2K0300 SD/SDIO/eMMC controller
- mtk-sd:
- Add support for the MT8189 variant
- renesas_sdhi_core:
- Add support for selecting an optional mux
- rtsx_pci_sdmmc:
- Simplify voltage switch handling
- sdhci:
- Stop advertising the driver in dmesg
- sdhci-esdhc-imx:
- Add 1-bit bus width support
- Add support for the NXP S32N79 variant
- sdhci-msm:
- Add support for the IPQ5210 and IPQ9650 variants
- Add support for wrapped keys
- Enable ICE for CQE-capable controllers with non-CQE cards
- sdhci-of-arasan:
- Add support for the Axiado AX3000 variant
- sdhci-of-aspeed:
- Add support for the AST2700 variant
- sdhci-of-bst:
- Add driver for the Black Sesame Technologies C1200 controller
- sdhci-of-dwcmshc:
- Add support for the Canaan K230 variant
- Add support for the HPE GSC variant
- Prevent clock glitches to avoid malfunction
- sdhci-of-k1:
- Add support for the K3 variant
mux core/consumers:
- core:
- Add helper functions for getting optional and selected mux-state
- i2c-omap:
- Convert to devm_mux_state_get_optional_selected()
- phy-renesas:
- Convert to devm_mux_state_get_optional_selected()
- phy-can-transceiver:
- Convert to devm_mux_state_get_optional()"
* tag 'mmc-v7.1' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (131 commits)
mmc: sdhci-msm: Fix the wrapped key handling
mmc: sdhci-of-dwcmshc: Disable clock before DLL configuration
mmc: core: Simplify with scoped for each OF child loop
mmc: core: Optimize size of struct mmc_queue_req
mmc: vub300: clean up module init
mmc: vub300: rename probe error labels
mmc: dw_mmc: Remove dw_mci_start_request wrapper and rename core function
mmc: dw_mmc: Inline dw_mci_queue_request() into dw_mci_request()
mmc: block: Use MQRQ_XFER_SINGLE_BLOCK for both read and write recovery
mmc: mmc_test: Replace hard-coded values with macros and consolidate test parameters
mmc: block: Convert to use DEFINE_SIMPLE_DEV_PM_OPS()
mmc: core: Replace the hard-coded shift value 9 with SECTOR_SHIFT
mmc: sdhci-dwcmshc: Refactor Rockchip platform data for controller revisions
mmc: core: Switch to use pm_ptr() for mmc_host_class_dev_pm_ops
mmc: core: Remove legacy 'enable-sdio-wakeup' DT property support
mmc: mmc_test: use kzalloc_flex
mmc: mtk-sd: disable new_tx/rx and modify related settings for mt8189
dt-bindings: mmc: hisilicon,hi3660-dw-mshc: Convert to DT schema
dt-bindings: mmc: sdhci-msm: add IPQ9650 compatible
mmc: block: use single block write in retry
...
76 files changed, 2564 insertions, 1415 deletions
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml index 57646575a13f..976f36de2091 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml @@ -19,6 +19,10 @@ allOf: properties: compatible: oneOf: + - items: + - enum: + - amlogic,t7-mmc + - const: amlogic,meson-axg-mmc - const: amlogic,meson-axg-mmc - items: - const: amlogic,meson-gx-mmc diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml index 8e79de97b242..f343fb78e114 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml @@ -106,6 +106,9 @@ properties: description: For this device it is strongly suggested to include arasan,soc-ctl-syscon. + - items: + - const: axiado,ax3000-sdhci-5.1-emmc # Axiado AX3000 eMMC controller + - const: arasan,sdhci-5.1 reg: maxItems: 1 @@ -121,6 +124,8 @@ properties: - const: clk_ahb - const: gate + dma-coherent: true + interrupts: minItems: 1 maxItems: 2 diff --git a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml index f90fd73904a2..8d62be4355a0 100644 --- a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml +++ b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml @@ -11,7 +11,7 @@ maintainers: - Ulf Hansson <ulf.hansson@linaro.org> description: - The ARM PrimeCells MMCI PL180 and PL181 provides an interface for + The ARM PrimeCell MMCI PL180 and PL181 provides an interface for reading and writing to MultiMedia and SD cards alike. Over the years vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO host controllers with very similar characteristics. diff --git a/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml index d24950ccea95..e4a9c2810893 100644 --- a/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml @@ -22,10 +22,15 @@ description: |+ properties: compatible: - enum: - - aspeed,ast2400-sd-controller - - aspeed,ast2500-sd-controller - - aspeed,ast2600-sd-controller + oneOf: + - enum: + - aspeed,ast2400-sd-controller + - aspeed,ast2500-sd-controller + - aspeed,ast2600-sd-controller + - items: + - const: aspeed,ast2700-sd-controller + - const: aspeed,ast2600-sd-controller + reg: maxItems: 1 description: Common configuration registers @@ -38,6 +43,9 @@ properties: maxItems: 1 description: The SD/SDIO controller clock gate + resets: + maxItems: 1 + patternProperties: "^sdhci@[0-9a-f]+$": type: object @@ -46,10 +54,15 @@ patternProperties: properties: compatible: - enum: - - aspeed,ast2400-sdhci - - aspeed,ast2500-sdhci - - aspeed,ast2600-sdhci + oneOf: + - enum: + - aspeed,ast2400-sdhci + - aspeed,ast2500-sdhci + - aspeed,ast2600-sdhci + - items: + - const: aspeed,ast2700-sdhci + - const: aspeed,ast2600-sdhci + reg: maxItems: 1 description: The SDHCI registers @@ -78,6 +91,18 @@ required: - ranges - clocks +if: + properties: + compatible: + contains: + const: aspeed,ast2700-sd-controller +then: + required: + - resets +else: + properties: + resets: false + examples: - | #include <dt-bindings/clock/aspeed-clock.h> diff --git a/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml index 2f63f2cdeb71..65bb2f66f8cf 100644 --- a/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml @@ -26,9 +26,14 @@ properties: reg: minItems: 1 + dma-coherent: true + interrupts: maxItems: 1 + iommus: + maxItems: 1 + clocks: maxItems: 1 description: diff --git a/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml b/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml new file mode 100644 index 000000000000..8358bb70c333 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/bst,c1200-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Black Sesame Technologies DWCMSHC SDHCI Controller + +maintainers: + - Ge Gordon <gordon.ge@bst.ai> + +allOf: + - $ref: sdhci-common.yaml# + +properties: + compatible: + const: bst,c1200-sdhci + + reg: + items: + - description: Core SDHCI registers + - description: CRM registers + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + memory-region: + maxItems: 1 + + dma-coherent: true + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + #include <dt-bindings/interrupt-controller/irq.h> + + bus { + #address-cells = <2>; + #size-cells = <2>; + + mmc@22200000 { + compatible = "bst,c1200-sdhci"; + reg = <0x0 0x22200000 0x0 0x1000>, + <0x0 0x23006000 0x0 0x1000>; + interrupts = <GIC_SPI 144 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk_mmc>; + clock-names = "core"; + memory-region = <&mmc0_reserved>; + max-frequency = <200000000>; + bus-width = <8>; + non-removable; + dma-coherent; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml index ac75d694611a..6c7317d13aa6 100644 --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml @@ -134,8 +134,6 @@ allOf: items: - description: Host controller registers - description: Elba byte-lane enable register for writes - required: - - resets else: properties: reg: diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index b98a84f93277..014b049baeb6 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -35,6 +35,7 @@ properties: - fsl,imx8mm-usdhc - fsl,imxrt1050-usdhc - nxp,s32g2-usdhc + - nxp,s32n79-usdhc - items: - const: fsl,imx50-esdhc - const: fsl,imx53-esdhc diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml new file mode 100644 index 000000000000..296bd776488e --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/hisilicon,hi3660-dw-mshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon specific extensions to the Synopsys Designware Mobile Storage Host Controller + +maintainers: + - Zhangfei Gao <zhangfei.gao@linaro.org> + +description: + The Synopsys designware mobile storage host controller is used to interface + a SoC with storage medium such as eMMC or SD/MMC cards. This file documents + differences between the core Synopsys dw mshc controller properties described + by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific + extensions to the Synopsys Designware Mobile Storage Host Controller. + +allOf: + - $ref: /schemas/mmc/synopsys-dw-mshc-common.yaml# + +properties: + compatible: + oneOf: + - enum: + - hisilicon,hi3660-dw-mshc + - hisilicon,hi4511-dw-mshc + - hisilicon,hi6220-dw-mshc + - items: + - const: hisilicon,hi3670-dw-mshc + - const: hisilicon,hi3660-dw-mshc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: card interface unit clock + - description: bus interface unit clock + + clock-names: + items: + - const: ciu + - const: biu + + hisilicon,peripheral-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle of syscon used to control peripheral. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/clock/hi3620-clock.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + mmc@fcd03000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd03000 0x1000>; + interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>; + clock-names = "ciu", "biu"; + vmmc-supply = <&ldo12>; + fifo-depth = <0x100>; + pinctrl-names = "default"; + pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; + bus-width = <4>; + disable-wp; + cd-gpios = <&gpio10 3 GPIO_ACTIVE_HIGH>; + cap-mmc-highspeed; + cap-sd-highspeed; + }; + + - | + #include <dt-bindings/clock/hi6220-clock.h> + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + + soc { + #address-cells = <2>; + #size-cells = <2>; + + mmc@f723e000 { + compatible = "hisilicon,hi6220-dw-mshc"; + reg = <0x0 0xf723e000 0x0 0x1000>; + interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clock_sys HI6220_MMC1_CIUCLK>, + <&clock_sys HI6220_MMC1_CLK>; + clock-names = "ciu", "biu"; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + card-detect-delay = <200>; + hisilicon,peripheral-syscon = <&ao_ctrl>; + cd-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>; + pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; + vqmmc-supply = <&ldo7>; + vmmc-supply = <&ldo10>; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt deleted file mode 100644 index 36c4bea675d5..000000000000 --- a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt +++ /dev/null @@ -1,73 +0,0 @@ -* Hisilicon specific extensions to the Synopsys Designware Mobile - Storage Host Controller - -Read synopsys-dw-mshc.txt for more details - -The Synopsys designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core Synopsys dw mshc controller properties described -by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific -extensions to the Synopsys Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be one of the following. - - "hisilicon,hi3660-dw-mshc": for controllers with hi3660 specific extensions. - - "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc": for controllers - with hi3670 specific extensions. - - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions. - - "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions. - -Optional Properties: -- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. - -Example: - - /* for Hi3620 */ - - /* SoC portion */ - dwmmc_0: dwmmc0@fcd03000 { - compatible = "hisilicon,hi4511-dw-mshc"; - reg = <0xfcd03000 0x1000>; - interrupts = <0 16 4>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>; - clock-names = "ciu", "biu"; - }; - - /* Board portion */ - dwmmc0@fcd03000 { - vmmc-supply = <&ldo12>; - fifo-depth = <0x100>; - pinctrl-names = "default"; - pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; - bus-width = <4>; - disable-wp; - cd-gpios = <&gpio10 3 0>; - cap-mmc-highspeed; - cap-sd-highspeed; - }; - - /* for Hi6220 */ - - dwmmc_1: dwmmc1@f723e000 { - compatible = "hisilicon,hi6220-dw-mshc"; - bus-width = <0x4>; - disable-wp; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - card-detect-delay = <200>; - hisilicon,peripheral-syscon = <&ao_ctrl>; - reg = <0x0 0xf723e000 0x0 0x1000>; - interrupts = <0x0 0x49 0x4>; - clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>; - clock-names = "ciu", "biu"; - cd-gpios = <&gpio1 0 1>; - pinctrl-names = "default", "idle"; - pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>; - pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; - vqmmc-supply = <&ldo7>; - vmmc-supply = <&ldo10>; - }; diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml index c142421bc723..b3e8d3f13592 100644 --- a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml @@ -22,6 +22,7 @@ allOf: properties: compatible: enum: + - loongson,ls2k0300-mmc - loongson,ls2k0500-mmc - loongson,ls2k1000-mmc - loongson,ls2k2000-mmc diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml index 6dd26ad31491..eb3755bdfdf7 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml @@ -25,6 +25,7 @@ properties: - mediatek,mt8135-mmc - mediatek,mt8173-mmc - mediatek,mt8183-mmc + - mediatek,mt8189-mmc - mediatek,mt8196-mmc - mediatek,mt8516-mmc - items: @@ -192,6 +193,7 @@ allOf: - mediatek,mt8183-mmc - mediatek,mt8186-mmc - mediatek,mt8188-mmc + - mediatek,mt8189-mmc - mediatek,mt8195-mmc - mediatek,mt8196-mmc - mediatek,mt8516-mmc @@ -240,6 +242,7 @@ allOf: - mediatek,mt7986-mmc - mediatek,mt7988-mmc - mediatek,mt8183-mmc + - mediatek,mt8189-mmc - mediatek,mt8196-mmc then: properties: diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index c754ea71f51f..64fac0d11329 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -106,6 +106,11 @@ properties: iommus: maxItems: 1 + mux-states: + description: + mux controller node to route the SD/SDIO/eMMC signals from SoC to cards. + maxItems: 1 + power-domains: maxItems: 1 @@ -275,6 +280,7 @@ examples: max-frequency = <195000000>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; resets = <&cpg 314>; + mux-states = <&mux 0>; }; sdhi1: mmc@ee120000 { diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index acb9fb9a92cd..4965bb518c54 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -47,6 +47,10 @@ properties: - rockchip,rv1126-dw-mshc - const: rockchip,rk3288-dw-mshc # for Rockchip RK3576 with phase tuning inside the controller + - items: + - enum: + - rockchip,rv1103b-dw-mshc + - const: rockchip,rk3576-dw-mshc - const: rockchip,rk3576-dw-mshc reg: diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index 938be8228d66..695a95e8f35d 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -38,10 +38,12 @@ properties: - items: - enum: - qcom,ipq5018-sdhci + - qcom,ipq5210-sdhci - qcom,ipq5332-sdhci - qcom,ipq5424-sdhci - qcom,ipq6018-sdhci - qcom,ipq9574-sdhci + - qcom,ipq9650-sdhci - qcom,kaanapali-sdhci - qcom,milos-sdhci - qcom,qcm2290-sdhci diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 7e7c55dc2440..cd823a3ef213 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -23,6 +23,9 @@ properties: - const: sophgo,sg2044-dwcmshc - const: sophgo,sg2042-dwcmshc - enum: + - canaan,k230-emmc + - canaan,k230-sdio + - hpe,gsc-dwcmshc - rockchip,rk3568-dwcmshc - rockchip,rk3588-dwcmshc - snps,dwcmshc-sdhci @@ -50,11 +53,18 @@ properties: maxItems: 1 resets: + minItems: 4 maxItems: 5 reset-names: + minItems: 4 maxItems: 5 + canaan,usb-phy: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the Canaan K230 USB PHY node required for + k230-emmc/sdio. + rockchip,txclk-tapnum: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 @@ -77,6 +87,17 @@ properties: description: Specifies the drive impedance in Ohm. enum: [33, 40, 50, 66, 100] + hpe,gxp-sysreg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to HPE GXP SoC system register block (syscon) + - description: offset of the MSHCCS register within the syscon block + description: + Phandle to the HPE GXP SoC system register block (syscon) and + offset of the MSHCCS register used to configure clock + synchronisation for HS200 tuning. + required: - compatible - reg @@ -91,6 +112,47 @@ allOf: properties: compatible: contains: + enum: + - canaan,k230-emmc + - canaan,k230-sdio + then: + properties: + clocks: + minItems: 5 + clock-names: + items: + - const: core + - const: bus + - const: axi + - const: block + - const: timer + required: + - canaan,usb-phy + + - if: + properties: + compatible: + contains: + const: hpe,gsc-dwcmshc + + then: + properties: + clocks: + items: + - description: core clock + clock-names: + items: + - const: core + required: + - hpe,gxp-sysreg + else: + properties: + hpe,gxp-sysreg: false + + - if: + properties: + compatible: + contains: const: sophgo,sg2042-dwcmshc then: @@ -146,6 +208,7 @@ allOf: else: properties: resets: + minItems: 5 maxItems: 5 reset-names: items: diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml index 13d9382058fb..9a055d963a7f 100644 --- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: spacemit,k1-sdhci + enum: + - spacemit,k1-sdhci + - spacemit,k3-sdhci reg: maxItems: 1 @@ -32,6 +34,16 @@ properties: - const: core - const: io + resets: + items: + - description: axi reset, connect to AXI bus, shared by all controllers + - description: sdh reset, connect to individual controller separately + + reset-names: + items: + - const: axi + - const: sdh + required: - compatible - reg diff --git a/MAINTAINERS b/MAINTAINERS index d32c39c2d5e6..17b03d555a0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2640,7 +2640,9 @@ R: BST Linux Kernel Upstream Group <bst-upstream@bstai.top> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/arm/bst.yaml +F: Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml F: arch/arm64/boot/dts/bst/ +F: drivers/mmc/host/sdhci-of-bst.c ARM/CALXEDA HIGHBANK ARCHITECTURE M: Andre Przywara <andre.przywara@arm.com> diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts index ed84ab92fb19..c6056a85ce80 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts @@ -20,8 +20,8 @@ compatible = "hisilicon,hi3660-hikey960", "hisilicon,hi3660"; aliases { - mshc1 = &dwmmc1; - mshc2 = &dwmmc2; + mmc1 = &dwmmc1; + mmc2 = &dwmmc2; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; diff --git a/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts b/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts index 7c32f5fd5cc5..65764b38d2e6 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts @@ -19,8 +19,8 @@ compatible = "hisilicon,hi3670-hikey970", "hisilicon,hi3670"; aliases { - mshc1 = &dwmmc1; - mshc2 = &dwmmc2; + mmc1 = &dwmmc1; + mmc2 = &dwmmc2; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index d9f590f0c384..f02d294db42a 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev) (1000 * omap->speed / 8); } - if (of_property_present(node, "mux-states")) { - struct mux_state *mux_state; - - mux_state = devm_mux_state_get(&pdev->dev, NULL); - if (IS_ERR(mux_state)) { - r = PTR_ERR(mux_state); - dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r); - goto err_put_pm; - } - omap->mux_state = mux_state; - r = mux_state_select(omap->mux_state); - if (r) { - dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r); - goto err_put_pm; - } + omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(omap->mux_state)) { + r = PTR_ERR(omap->mux_state); + goto err_put_pm; } /* reset ASAP, clearing any IRQs */ r = omap_i2c_init(omap); if (r) - goto err_mux_state_deselect; + goto err_put_pm; if (omap->rev < OMAP_I2C_OMAP1_REV_2) r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr, @@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev) err_unuse_clocks: omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); -err_mux_state_deselect: - if (omap->mux_state) - mux_state_deselect(omap->mux_state); err_put_pm: pm_runtime_put_sync(omap->dev); err_disable_pm: diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 05ee76cb0a08..0274e8d07660 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1401,6 +1401,9 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, rq_data_dir(req) == WRITE && (md->flags & MMC_BLK_REL_WR); + if (mqrq->flags & MQRQ_XFER_SINGLE_BLOCK) + recovery_mode = 1; + memset(brq, 0, sizeof(struct mmc_blk_request)); mmc_crypto_prepare_req(mqrq); @@ -1455,7 +1458,7 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, * sectors can be read successfully. */ if (recovery_mode) - brq->data.blocks = queue_physical_block_size(mq->queue) >> 9; + brq->data.blocks = queue_physical_block_size(mq->queue) >> SECTOR_SHIFT; /* * Some controllers have HW issues while operating @@ -1540,10 +1543,12 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req) err = 0; if (err) { - if (mqrq->retries++ < MMC_CQE_RETRIES) + if (mqrq->retries++ < MMC_CQE_RETRIES) { + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); - else + } else { blk_mq_end_request(req, BLK_STS_IOERR); + } } else if (mrq->data) { if (blk_update_request(req, BLK_STS_OK, mrq->data->bytes_xfered)) blk_mq_requeue_request(req, true); @@ -1776,63 +1781,6 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req) return err; } -#define MMC_READ_SINGLE_RETRIES 2 - -/* Single (native) sector read during recovery */ -static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) -{ - struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); - struct mmc_request *mrq = &mqrq->brq.mrq; - struct mmc_card *card = mq->card; - struct mmc_host *host = card->host; - blk_status_t error = BLK_STS_OK; - size_t bytes_per_read = queue_physical_block_size(mq->queue); - - do { - u32 status; - int err; - int retries = 0; - - while (retries++ <= MMC_READ_SINGLE_RETRIES) { - mmc_blk_rw_rq_prep(mqrq, card, 1, mq); - - mmc_wait_for_req(host, mrq); - - err = mmc_send_status(card, &status); - if (err) - goto error_exit; - - if (!mmc_host_is_spi(host) && - !mmc_ready_for_data(status)) { - err = mmc_blk_fix_state(card, req); - if (err) - goto error_exit; - } - - if (!mrq->cmd->error) - break; - } - - if (mrq->cmd->error || - mrq->data->error || - (!mmc_host_is_spi(host) && - (mrq->cmd->resp[0] & CMD_ERRORS || status & CMD_ERRORS))) - error = BLK_STS_IOERR; - else - error = BLK_STS_OK; - - } while (blk_update_request(req, error, bytes_per_read)); - - return; - -error_exit: - mrq->data->bytes_xfered = 0; - blk_update_request(req, BLK_STS_IOERR, bytes_per_read); - /* Let it try the remaining request again */ - if (mqrq->retries > MMC_MAX_RETRIES - 1) - mqrq->retries = MMC_MAX_RETRIES - 1; -} - static inline bool mmc_blk_oor_valid(struct mmc_blk_request *brq) { return !!brq->mrq.sbc; @@ -1968,13 +1916,6 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) mqrq->retries = MMC_MAX_RETRIES - MMC_DATA_RETRIES; return; } - - if (rq_data_dir(req) == READ && brq->data.blocks > - queue_physical_block_size(mq->queue) >> 9) { - /* Read one (native) sector at a time */ - mmc_blk_read_single(mq, req); - return; - } } static inline bool mmc_blk_rq_error(struct mmc_blk_request *brq) @@ -2085,6 +2026,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req) } else if (!blk_rq_bytes(req)) { __blk_mq_end_request(req, BLK_STS_IOERR); } else if (mqrq->retries++ < MMC_MAX_RETRIES) { + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); } else { if (mmc_card_removed(mq->card)) @@ -3017,14 +2959,14 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) */ ret = mmc_blk_alloc_rpmb_part(card, md, card->part[idx].part_cfg, - card->part[idx].size >> 9, + card->part[idx].size >> SECTOR_SHIFT, card->part[idx].name); if (ret) return ret; } else if (card->part[idx].size) { ret = mmc_blk_alloc_part(card, md, card->part[idx].part_cfg, - card->part[idx].size >> 9, + card->part[idx].size >> SECTOR_SHIFT, card->part[idx].force_ro, card->part[idx].name, card->part[idx].area_type); @@ -3354,7 +3296,6 @@ static void mmc_blk_shutdown(struct mmc_card *card) _mmc_blk_suspend(card); } -#ifdef CONFIG_PM_SLEEP static int mmc_blk_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); @@ -3380,14 +3321,13 @@ static int mmc_blk_resume(struct device *dev) } return 0; } -#endif -static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); static struct mmc_driver mmc_driver = { .drv = { .name = "mmcblk", - .pm = &mmc_blk_pm_ops, + .pm = pm_sleep_ptr(&mmc_blk_pm_ops), }, .probe = mmc_blk_probe, .remove = mmc_blk_remove, diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 1200951bab08..a7c364d0030a 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -89,6 +89,7 @@ struct mmc_fixup { #define CID_MANFID_MICRON 0x13 #define CID_MANFID_SAMSUNG 0x15 #define CID_MANFID_APACER 0x27 +#define CID_MANFID_SANDISK_MMC 0x45 #define CID_MANFID_SWISSBIT 0x5D #define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_HYNIX 0x90 @@ -305,4 +306,14 @@ static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c) return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING; } +static inline int mmc_card_broken_mdt(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_BROKEN_MDT; +} + +static inline int mmc_card_fixed_secure_erase_trim_time(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME; +} + #endif diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 860378bea557..29e80e5f928e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -97,7 +97,8 @@ static void mmc_should_fail_request(struct mmc_host *host, return; data->error = data_errors[get_random_u32_below(ARRAY_SIZE(data_errors))]; - data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> 9) << 9; + data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> SECTOR_SHIFT) + << SECTOR_SHIFT; } #else /* CONFIG_FAIL_MMC_REQUEST */ diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 88c95dbfd9cf..b7ce3137d452 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -33,7 +33,6 @@ static DEFINE_IDA(mmc_host_ida); -#ifdef CONFIG_PM_SLEEP static int mmc_host_class_prepare(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -60,15 +59,10 @@ static void mmc_host_class_complete(struct device *dev) } static const struct dev_pm_ops mmc_host_class_dev_pm_ops = { - .prepare = mmc_host_class_prepare, - .complete = mmc_host_class_complete, + .prepare = pm_sleep_ptr(mmc_host_class_prepare), + .complete = pm_sleep_ptr(mmc_host_class_complete), }; -#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops) -#else -#define MMC_HOST_CLASS_DEV_PM_OPS NULL -#endif - static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -90,7 +84,7 @@ static const struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, .shutdown_pre = mmc_host_classdev_shutdown, - .pm = MMC_HOST_CLASS_DEV_PM_OPS, + .pm = pm_ptr(&mmc_host_class_dev_pm_ops), }; int mmc_register_host_class(void) @@ -379,8 +373,7 @@ int mmc_of_parse(struct mmc_host *host) host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND; if (device_property_read_bool(dev, "keep-power-in-suspend")) host->pm_caps |= MMC_PM_KEEP_POWER; - if (device_property_read_bool(dev, "wakeup-source") || - device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ + if (device_property_read_bool(dev, "wakeup-source")) host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; if (device_property_read_bool(dev, "mmc-ddr-3_3v")) host->caps |= MMC_CAP_3_3V_DDR; @@ -624,12 +617,24 @@ static int mmc_validate_host_caps(struct mmc_host *host) return -EINVAL; } + /* UHS/DDR/HS200 modes require at least 4-bit bus */ + if (!(caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) && + ((caps & (MMC_CAP_UHS | MMC_CAP_DDR)) || (caps2 & MMC_CAP2_HS200))) { + dev_warn(dev, "drop UHS/DDR/HS200 support since 1-bit bus only\n"); + caps &= ~(MMC_CAP_UHS | MMC_CAP_DDR); + caps2 &= ~MMC_CAP2_HS200; + } + + /* HS400 and HS400ES modes require 8-bit bus */ if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) && !(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) { dev_warn(dev, "drop HS400 support since no 8-bit bus\n"); - host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400; + caps2 &= ~(MMC_CAP2_HS400_ES | MMC_CAP2_HS400); } + host->caps = caps; + host->caps2 = caps2; + return 0; } diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index 5941d68ff989..6bce5a4798e2 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -56,11 +56,7 @@ static inline int mmc_host_can_access_boot(struct mmc_host *host) static inline int mmc_host_can_uhs(struct mmc_host *host) { - return host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50) && - host->caps & MMC_CAP_4_BIT_DATA; + return host->caps & MMC_CAP_UHS; } static inline bool mmc_card_hs200(struct mmc_card *card) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7c86efb1044a..8846550a8892 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -671,7 +671,19 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.enhanced_rpmb_supported = (card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR); + + if (card->ext_csd.rev >= 9) { + /* Adjust production date as per JEDEC JESD84-B51B September 2025 */ + if (card->cid.year < 2023) + card->cid.year += 16; + } else { + /* Handle vendors with broken MDT reporting */ + if (mmc_card_broken_mdt(card) && card->cid.year >= 2010 && + card->cid.year <= 2012) + card->cid.year += 16; + } } + out: return err; } diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 43fdb67d5407..ab38e4c45a8d 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -37,7 +37,7 @@ * Limit the test area size to the maximum MMC HC erase group size. Note that * the maximum SD allocation unit size is just 4MiB. */ -#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024) +#define TEST_AREA_MAX_SIZE SZ_128M /** * struct mmc_test_pages - pages allocated by 'alloc_pages()'. @@ -51,12 +51,12 @@ struct mmc_test_pages { /** * struct mmc_test_mem - allocated memory. - * @arr: array of allocations * @cnt: number of allocations + * @arr: array of allocations */ struct mmc_test_mem { - struct mmc_test_pages *arr; unsigned int cnt; + struct mmc_test_pages arr[] __counted_by(cnt); }; /** @@ -135,21 +135,22 @@ struct mmc_test_dbgfs_file { * struct mmc_test_card - test information. * @card: card under test * @scratch: transfer buffer - * @buffer: transfer buffer * @highmem: buffer for highmem tests * @area: information for performance tests * @gr: pointer to results of current testcase + * @buffer: transfer buffer */ struct mmc_test_card { struct mmc_card *card; u8 scratch[BUFFER_SIZE]; - u8 *buffer; #ifdef CONFIG_HIGHMEM struct page *highmem; #endif struct mmc_test_area area; struct mmc_test_general_result *gr; + + u8 buffer[]; }; enum mmc_test_prep_media { @@ -168,6 +169,11 @@ struct mmc_test_multiple_rw { enum mmc_test_prep_media prepare; }; +static unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, + 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; + +static unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, + 1 << 7, 1 << 8, 1 << 9}; /*******************************************************************/ /* General helper functions */ /*******************************************************************/ @@ -315,7 +321,6 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem) while (mem->cnt--) __free_pages(mem->arr[mem->cnt].page, mem->arr[mem->cnt].order); - kfree(mem->arr); kfree(mem); } @@ -348,14 +353,10 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, if (max_segs > max_page_cnt) max_segs = max_page_cnt; - mem = kzalloc_obj(*mem); + mem = kzalloc_flex(*mem, arr, max_segs); if (!mem) return NULL; - mem->arr = kzalloc_objs(*mem->arr, max_segs); - if (!mem->arr) - goto out_free; - while (max_page_cnt) { struct page *page; unsigned int order; @@ -506,7 +507,7 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec64 *ts) uint64_t ns; ns = timespec64_to_ns(ts); - bytes *= 1000000000; + bytes *= NSEC_PER_SEC; while (ns > UINT_MAX) { bytes >>= 1; @@ -552,7 +553,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test, static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, struct timespec64 *ts1, struct timespec64 *ts2) { - unsigned int rate, iops, sectors = bytes >> 9; + unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT; struct timespec64 ts; ts = timespec64_sub(*ts2, *ts1); @@ -577,7 +578,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes, unsigned int count, struct timespec64 *ts1, struct timespec64 *ts2) { - unsigned int rate, iops, sectors = bytes >> 9; + unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT; uint64_t tot = bytes * count; struct timespec64 ts; @@ -1378,7 +1379,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz, int err; unsigned int sg_len = 0; - t->blocks = sz >> 9; + t->blocks = sz >> SECTOR_SHIFT; if (max_scatter) { err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg, @@ -1461,7 +1462,7 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, else for (i = 0; i < count && ret == 0; i++) { ret = mmc_test_area_transfer(test, dev_addr, write); - dev_addr += sz >> 9; + dev_addr += sz >> SECTOR_SHIFT; } if (ret) @@ -1504,7 +1505,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test) if (!mmc_card_can_erase(test->card)) return 0; - return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9, + return mmc_erase(test->card, t->dev_addr, t->max_sz >> SECTOR_SHIFT, MMC_ERASE_ARG); } @@ -1532,7 +1533,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test) static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) { struct mmc_test_area *t = &test->area; - unsigned long min_sz = 64 * 1024, sz; + unsigned long min_sz = SZ_64K, sz; int ret; ret = mmc_test_set_blksize(test, 512); @@ -1540,9 +1541,9 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) return ret; /* Make the test area size about 4MiB */ - sz = (unsigned long)test->card->pref_erase << 9; + sz = (unsigned long)test->card->pref_erase << SECTOR_SHIFT; t->max_sz = sz; - while (t->max_sz < 4 * 1024 * 1024) + while (t->max_sz < SZ_4M) t->max_sz += sz; while (t->max_sz > TEST_AREA_MAX_SIZE && t->max_sz > sz) t->max_sz -= sz; @@ -1552,8 +1553,8 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) t->max_seg_sz -= t->max_seg_sz % 512; t->max_tfr = t->max_sz; - if (t->max_tfr >> 9 > test->card->host->max_blk_count) - t->max_tfr = test->card->host->max_blk_count << 9; + if (t->max_tfr >> SECTOR_SHIFT > test->card->host->max_blk_count) + t->max_tfr = test->card->host->max_blk_count << SECTOR_SHIFT; if (t->max_tfr > test->card->host->max_req_size) t->max_tfr = test->card->host->max_req_size; if (t->max_tfr / t->max_seg_sz > t->max_segs) @@ -1583,7 +1584,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) } t->dev_addr = mmc_test_capacity(test->card) / 2; - t->dev_addr -= t->dev_addr % (t->max_sz >> 9); + t->dev_addr -= t->dev_addr % (t->max_sz >> SECTOR_SHIFT); if (erase) { ret = mmc_test_area_erase(test); @@ -1688,7 +1689,7 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test) int ret; for (sz = 512; sz < t->max_tfr; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1); if (ret) return ret; @@ -1712,7 +1713,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test) if (ret) return ret; for (sz = 512; sz < t->max_tfr; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1); if (ret) return ret; @@ -1743,9 +1744,9 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test) return RESULT_UNSUP_HOST; for (sz = 512; sz < t->max_sz; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ktime_get_ts64(&ts1); - ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; ktime_get_ts64(&ts2); @@ -1753,7 +1754,7 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test) } dev_addr = t->dev_addr; ktime_get_ts64(&ts1); - ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; ktime_get_ts64(&ts2); @@ -1775,7 +1776,7 @@ static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz) ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1817,7 +1818,7 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz) ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1870,11 +1871,11 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test) dev_addr = t->dev_addr; ktime_get_ts64(&ts1); for (i = 0; i < cnt; i++) { - ret = mmc_erase(test->card, dev_addr, sz >> 9, + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1901,7 +1902,7 @@ static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print, struct timespec64 ts1, ts2, ts; int ret; - ssz = sz >> 9; + ssz = sz >> SECTOR_SHIFT; rnd_addr = mmc_test_capacity(test->card) / 4; range1 = rnd_addr / test->card->pref_erase; @@ -2017,10 +2018,10 @@ static int mmc_test_seq_perf(struct mmc_test_card *test, int write, sz = max_tfr; } - ssz = sz >> 9; + ssz = sz >> SECTOR_SHIFT; dev_addr = mmc_test_capacity(test->card) / 4; - if (tot_sz > dev_addr << 9) - tot_sz = dev_addr << 9; + if (tot_sz > dev_addr << SECTOR_SHIFT) + tot_sz = dev_addr << SECTOR_SHIFT; cnt = tot_sz / sz; dev_addr &= 0xffff0000; /* Round to 64MiB boundary */ @@ -2044,17 +2045,17 @@ static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write) int ret, i; for (i = 0; i < 10; i++) { - ret = mmc_test_seq_perf(test, write, 10 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 10 * SZ_1M, 1); if (ret) return ret; } for (i = 0; i < 5; i++) { - ret = mmc_test_seq_perf(test, write, 100 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 100 * SZ_1M, 1); if (ret) return ret; } for (i = 0; i < 3; i++) { - ret = mmc_test_seq_perf(test, write, 1000 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 1000 * SZ_1M, 1); if (ret) return ret; } @@ -2157,7 +2158,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test, int i; for (i = 0 ; i < rw->len && ret == 0; i++) { - ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size, + ret = mmc_test_rw_multiple(test, rw, SZ_512K, rw->size, rw->sg_len[i]); if (ret) break; @@ -2170,8 +2171,6 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test, */ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2189,8 +2188,6 @@ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2208,8 +2205,6 @@ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2227,8 +2222,6 @@ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2246,8 +2239,6 @@ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2265,8 +2256,6 @@ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2284,8 +2273,6 @@ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2303,8 +2290,6 @@ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2456,7 +2441,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test, if (ret) goto out_free; - if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr) + if (repeat_cmd && (t->blocks + 1) << SECTOR_SHIFT > t->max_tfr) pr_info("%s: %d commands completed during transfer of %u blocks\n", mmc_hostname(test->card->host), count, t->blocks); @@ -3099,7 +3084,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, if (ret) return ret; - test = kzalloc_obj(*test); + test = kzalloc_flex(*test, buffer, BUFFER_SIZE); if (!test) return -ENOMEM; @@ -3111,7 +3096,6 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, test->card = card; - test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); #ifdef CONFIG_HIGHMEM test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER); if (!test->highmem) { @@ -3120,17 +3104,14 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, } #endif - if (test->buffer) { - mutex_lock(&mmc_test_lock); - mmc_test_run(test, testcase); - mutex_unlock(&mmc_test_lock); - } + mutex_lock(&mmc_test_lock); + mmc_test_run(test, testcase); + mutex_unlock(&mmc_test_lock); #ifdef CONFIG_HIGHMEM __free_pages(test->highmem, BUFFER_ORDER); free_test_buffer: #endif - kfree(test->buffer); kfree(test); return count; diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 13000fc57e2e..39fcb662c43f 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -184,8 +184,13 @@ static void mmc_queue_setup_discard(struct mmc_card *card, return; lim->max_hw_discard_sectors = max_discard; - if (mmc_card_can_secure_erase_trim(card)) - lim->max_secure_erase_sectors = max_discard; + if (mmc_card_can_secure_erase_trim(card)) { + if (mmc_card_fixed_secure_erase_trim_time(card)) + lim->max_secure_erase_sectors = UINT_MAX >> card->erase_shift; + else + lim->max_secure_erase_sectors = max_discard; + } + if (mmc_card_can_trim(card) && card->erased_byte == 0) lim->max_write_zeroes_sectors = max_discard; diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index 1498840a4ea0..dd7211e3a6d5 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -61,14 +61,17 @@ enum mmc_drv_op { MMC_DRV_OP_GET_EXT_CSD, }; +#define MQRQ_XFER_SINGLE_BLOCK BIT(0) + struct mmc_queue_req { struct mmc_blk_request brq; struct scatterlist *sg; enum mmc_drv_op drv_op; int drv_op_result; void *drv_op_data; - unsigned int ioc_count; - int retries; + u8 ioc_count; + u8 retries; + u8 flags; }; struct mmc_queue { diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index c417ed34c057..940549d3b95d 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -153,6 +153,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, MMC_QUIRK_TRIM_BROKEN), + /* + * On Some Kingston eMMCs, secure erase/trim time is independent + * of erase size, fixed at approximately 2 seconds. + */ + MMC_FIXUP("IY2964", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), + MMC_FIXUP("IB2932", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), + END_FIXUP }; @@ -170,6 +179,9 @@ static const struct mmc_fixup __maybe_unused mmc_ext_csd_fixups[] = { MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_NUMONYX, 0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6), + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_MMC, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BROKEN_MDT), + END_FIXUP }; @@ -213,14 +225,9 @@ static const struct mmc_fixup __maybe_unused sdio_card_init_methods[] = { static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card, const char *compatible) { - struct device_node *np; - - for_each_child_of_node(mmc_dev(card->host)->of_node, np) { - if (of_device_is_compatible(np, compatible)) { - of_node_put(np); + for_each_child_of_node_scoped(mmc_dev(card->host)->of_node, np) + if (of_device_is_compatible(np, compatible)) return true; - } - } return false; } diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index b774bf51981d..12716ec0e35d 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -163,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz) if (blksz > func->card->host->max_blk_size) return -EINVAL; - if (blksz == 0) { - blksz = min(func->max_blksize, func->card->host->max_blk_size); - blksz = min(blksz, 512u); - } + if (blksz == 0) + blksz = min3(func->max_blksize, func->card->host->max_blk_size, 512u); ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6d79cc9a79e2..4f060d3e5636 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -429,6 +429,20 @@ config MMC_SDHCI_BCM_KONA If you have a controller with this interface, say Y or M here. +config MMC_SDHCI_BST + tristate "SDHCI support for Black Sesame Technologies BST C1200 controller" + depends on ARCH_BST || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + for Black Sesame Technologies BST C1200 SoC. The controller is + based on Synopsys DesignWare Cores Mobile Storage Controller but + requires platform-specific workarounds for hardware limitations. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_F_SDH30 tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" depends on MMC_SDHCI_PLTFM @@ -1044,7 +1058,7 @@ config MMC_MTK config MMC_SDHCI_MICROCHIP_PIC32 tristate "Microchip PIC32MZDA SDHCI support" - depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM + depends on MMC_SDHCI && MMC_SDHCI_PLTFM && (PIC32MZDA || COMPILE_TEST) help This selects the Secure Digital Host Controller Interface (SDHCI) for PIC32MZDA platform. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 5057fea8afb6..ee412e6b84d6 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +obj-$(CONFIG_MMC_SDHCI_BST) += sdhci-of-bst.o sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fdf6926ea468..3b4928f5b9b2 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -629,14 +629,13 @@ static int atmci_of_init(struct atmel_mci *host) { struct device *dev = host->dev; struct device_node *np = dev->of_node; - struct device_node *cnp; u32 slot_id; int err; if (!np) return dev_err_probe(dev, -EINVAL, "device node not found\n"); - for_each_child_of_node(np, cnp) { + for_each_child_of_node_scoped(np, cnp) { if (of_property_read_u32(cnp, "reg", &slot_id)) { dev_warn(dev, "reg property is missing for %pOF\n", cnp); continue; @@ -645,7 +644,6 @@ static int atmci_of_init(struct atmel_mci *host) if (slot_id >= ATMCI_MAX_NR_SLOTS) { dev_warn(dev, "can't have more than %d slots\n", ATMCI_MAX_NR_SLOTS); - of_node_put(cnp); break; } @@ -658,10 +656,8 @@ static int atmci_of_init(struct atmel_mci *host) "cd", GPIOD_IN, "cd-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].detect_pin = NULL; } @@ -673,10 +669,8 @@ static int atmci_of_init(struct atmel_mci *host) "wp", GPIOD_IN, "wp-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].wp_pin = NULL; } } diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 0592f356b1e5..8a0daddd9200 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -148,7 +148,7 @@ static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host) static int octeon_mmc_probe(struct platform_device *pdev) { - struct device_node *cn, *node = pdev->dev.of_node; + struct device_node *node = pdev->dev.of_node; struct cvm_mmc_host *host; void __iomem *base; int mmc_irq[9]; @@ -268,7 +268,7 @@ static int octeon_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); i = 0; - for_each_child_of_node(node, cn) { + for_each_child_of_node_scoped(node, cn) { host->slot_pdev[i] = of_platform_device_create(cn, NULL, &pdev->dev); if (!host->slot_pdev[i]) { @@ -279,7 +279,6 @@ static int octeon_mmc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Error populating slots\n"); octeon_mmc_set_shared_power(host, 0); - of_node_put(cn); goto error; } i++; diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 9a55db0e657c..37a88f2a0c86 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -905,9 +905,7 @@ static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock) { struct mmc_host *mmc = slot->mmc; - clock = min(clock, mmc->f_max); - clock = max(clock, mmc->f_min); - slot->clock = clock; + slot->clock = clamp(clock, mmc->f_min, mmc->f_max); } static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c index 3cf526ab0387..81dc072d0e3f 100644 --- a/drivers/mmc/host/dw_mmc-bluefield.c +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = { .name = "dwmmc_bluefield", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_bluefield_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 384609671a9a..261344d3a8cf 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -185,8 +185,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot) - set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags); + if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel)) + set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags); } static int dw_mci_exynos_runtime_resume(struct device *dev) @@ -530,11 +530,10 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_exynos_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; u8 start_smpl, smpl, candidates = 0; s8 found; int ret = 0; diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index 0ccfae1b2dbe..4b723ed3259c 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -57,11 +57,9 @@ static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) clk_set_phase(priv->drive_clk, 135); } -static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, - u32 opcode) +static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct hi3798cv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1; int err, prev_err = -1; @@ -72,7 +70,7 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, clk_set_phase(priv->sample_clk, degrees[i]); mci_writel(host, RINTSTS, ALL_INT_CLR); - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); if (!err) found = 1; diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c index 5791a975a944..fda417888be9 100644 --- a/drivers/mmc/host/dw_mmc-hi3798mv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -30,13 +30,12 @@ struct dw_mci_hi3798mv200_priv { struct clk *drive_clk; struct regmap *crg_reg; u32 sap_dll_offset; - struct mmc_clk_phase_map phase_map; }; static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_hi3798mv200_priv *priv = host->priv; - struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing]; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; u32 val; val = mci_readl(host, ENABLE_SHIFT); @@ -74,25 +73,24 @@ static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } -static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, +static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct dw_mci_hi3798mv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1, mid; int err, prev_err = -1; @@ -101,7 +99,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, int i; int ret; - ret = dw_mci_hi3798mv200_enable_tuning(slot); + ret = dw_mci_hi3798mv200_enable_tuning(host); if (ret < 0) return ret; @@ -115,7 +113,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, * * Treat edge(flip) found as an error too. */ - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); regval = mci_readl(host, TUNING_CTRL); if (err || (regval & SDMMC_TUNING_FIND_EDGE)) err = 1; @@ -136,7 +134,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, } tuning_out: - ret = dw_mci_hi3798mv200_disable_tuning(slot); + ret = dw_mci_hi3798mv200_disable_tuning(host); if (ret < 0) return ret; @@ -159,9 +157,9 @@ tuning_out: * We don't care what timing we are tuning for, * simply use the same phase for all timing needs tuning. */ - priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; clk_set_phase(priv->sample_clk, degrees[mid]); dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n", @@ -186,8 +184,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host) if (!priv) return -ENOMEM; - mmc_of_parse_clk_phase(host->dev, &priv->phase_map); - priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample"); if (IS_ERR(priv->sample_clk)) return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk), diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index ad6aa1aea549..59b802c9f2b8 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -53,7 +53,6 @@ #define USE_DLY_MAX_SMPL (14) struct k3_priv { - int ctrl_id; u32 cur_speed; struct regmap *reg; }; @@ -127,26 +126,17 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host) if (IS_ERR(priv->reg)) priv->reg = NULL; - priv->ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (priv->ctrl_id < 0) - priv->ctrl_id = 0; - - if (priv->ctrl_id >= TIMING_MODE) - return -EINVAL; - host->priv = priv; return 0; } -static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +static int dw_mci_hi6220_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; int min_uv, max_uv; int ret; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) @@ -199,7 +189,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) host->bus_hz = clk_get_rate(host->biu_clk); } -static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi6220_execute_tuning(struct dw_mci *host, u32 opcode) { return 0; } @@ -213,7 +203,7 @@ static const struct dw_mci_drv_data hi6220_data = { .execute_tuning = dw_mci_hi6220_execute_tuning, }; -static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, +static int dw_mci_hs_set_timing(struct dw_mci *host, int timing, int smpl_phase) { u32 drv_phase; @@ -222,10 +212,10 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, u32 enable_shift = 0; u32 reg_value; int ctrl_id; - struct k3_priv *priv; - priv = host->priv; - ctrl_id = priv->ctrl_id; + ctrl_id = host->mmc->index; + if (ctrl_id >= TIMING_MODE) + return -EINVAL; drv_phase = hs_timing_cfg[ctrl_id][timing].drv_phase; smpl_dly = hs_timing_cfg[ctrl_id][timing].smpl_dly; @@ -262,6 +252,8 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, /* We should delay 1ms wait for timing setting finished. */ usleep_range(1000, 2000); + + return 0; } static int dw_mci_hi3660_init(struct dw_mci *host) @@ -269,10 +261,9 @@ static int dw_mci_hi3660_init(struct dw_mci *host) mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(SDCARD_RD_THRESHOLD, SDMMC_CARD_RD_THR_EN)); - dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); host->bus_hz /= (GENCLK_DIV + 1); - return 0; + return dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); } static int dw_mci_set_sel18(struct dw_mci *host, bool set) @@ -364,11 +355,10 @@ static int dw_mci_get_best_clksmpl(unsigned int sample_flag) return middle_range; } -static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi3660_execute_tuning(struct dw_mci *host, u32 opcode) { int i = 0; - struct dw_mci *host = slot->host; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int smpl_phase = 0; u32 tuning_sample_flag = 0; int best_clksmpl = 0; @@ -398,21 +388,19 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) return 0; } -static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc, +static int dw_mci_hi3660_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - int ret = 0; - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; + int ret = 0; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) return 0; - if (priv->ctrl_id == DWMMC_SDIO_ID) + if (mmc->index == DWMMC_SDIO_ID) return 0; if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) @@ -460,11 +448,6 @@ static int dw_mci_k3_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } -static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) -}; - static struct platform_driver dw_mci_k3_pltfm_driver = { .probe = dw_mci_k3_probe, .remove = dw_mci_pltfm_remove, @@ -472,7 +455,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = { .name = "dwmmc_k3", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_k3_match, - .pm = pm_ptr(&dw_mci_k3_dev_pm_ops), + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 092cc99175af..a2e1b4d75a5e 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -10,13 +10,14 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/pci.h> +#include <linux/pci-epf.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include "dw_mmc.h" +#include "dw_mmc-pltfm.h" -#define PCI_BAR_NO 2 #define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 #define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 /* Defining the Capabilities */ @@ -24,11 +25,8 @@ MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\ MMC_CAP_SDIO_IRQ) -static struct dw_mci_board pci_board_data = { - .caps = DW_MCI_CAPABILITIES, - .bus_hz = 33 * 1000 * 1000, - .detect_delay_ms = 200, - .fifo_depth = 32, +static const struct dw_mci_drv_data pci_drv_data = { + .common_caps = DW_MCI_CAPABILITIES, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -41,20 +39,20 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, if (ret) return ret; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = pdev->irq; host->irq_flags = IRQF_SHARED; - host->dev = &pdev->dev; - host->pdata = &pci_board_data; + host->fifo_depth = 32; + host->detect_delay_ms = 200; + host->bus_hz = 33 * 1000 * 1000; + host->drv_data = &pci_drv_data; - ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); - if (ret) - return ret; - - host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO]; + host->regs = pcim_iomap_region(pdev, BAR_2, pci_name(pdev)); + if (IS_ERR(host->regs)) + return PTR_ERR(host->regs); pci_set_master(pdev); @@ -74,11 +72,6 @@ static void dw_mci_pci_remove(struct pci_dev *pdev) dw_mci_remove(host); } -static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) -}; - static const struct pci_device_id dw_mci_pci_id[] = { { PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) }, {} @@ -91,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = { .probe = dw_mci_pci_probe, .remove = dw_mci_pci_remove, .driver = { - .pm = pm_ptr(&dw_mci_pci_dev_pm_ops), + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index de820ffd2133..cf38bb118dc2 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -33,18 +33,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev, struct dw_mci *host; struct resource *regs; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return host->irq; host->drv_data = drv_data; - host->dev = &pdev->dev; host->irq_flags = 0; - host->pdata = pdev->dev.platform_data; host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(host->regs)) @@ -58,24 +56,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); -const struct dev_pm_ops dw_mci_pltfm_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; -EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); - static int dw_mci_socfpga_priv_init(struct dw_mci *host) { struct device_node *np = host->dev->of_node; + struct mmc_clk_phase phase; struct regmap *sys_mgr_base_addr; - u32 clk_phase[2] = {0}, reg_offset, reg_shift; - int i, rc, hs_timing; + u32 reg_offset, reg_shift; + int hs_timing; - rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0); - if (rc < 0) + phase = host->phase_map.phase[MMC_TIMING_SD_HS]; + if (!phase.valid) return 0; sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); @@ -87,10 +77,10 @@ static int dw_mci_socfpga_priv_init(struct dw_mci *host) of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset); of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift); - for (i = 0; i < ARRAY_SIZE(clk_phase); i++) - clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; + phase.in_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; + phase.out_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; - hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift); + hs_timing = SYSMGR_SDMMC_CTRL_SET(phase.in_deg, phase.out_deg, reg_shift); regmap_write(sys_mgr_base_addr, reg_offset, hs_timing); return 0; @@ -136,7 +126,7 @@ static struct platform_driver dw_mci_pltfm_driver = { .name = "dw_mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_pltfm_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 64cf7522a3d4..ef1b05d484c3 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -11,6 +11,6 @@ extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); extern void dw_mci_pltfm_remove(struct platform_device *pdev); -extern const struct dev_pm_ops dw_mci_pltfm_pmops; +extern const struct dev_pm_ops dw_mci_pmops; #endif /* _DW_MMC_PLTFM_H_ */ diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index ac069d0c42b2..c6eece4ec3fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -179,7 +179,8 @@ static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int 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; - int ret; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; + int ret, sample_phase, drv_phase; unsigned int cclkin; u32 bus_hz; @@ -213,8 +214,15 @@ 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) - rockchip_mmc_set_phase(host, true, priv->default_sample_phase); + if (!IS_ERR(priv->sample_clk)) { + /* Keep backward compatibility */ + if (ios->timing <= MMC_TIMING_SD_HS) { + sample_phase = phase.valid ? phase.in_deg : priv->default_sample_phase; + rockchip_mmc_set_phase(host, true, sample_phase); + } else if (phase.valid) { + rockchip_mmc_set_phase(host, true, phase.in_deg); + } + } /* * Set the drive phase offset based on speed mode to achieve hold times. @@ -243,15 +251,13 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * same results, for instance). */ if (!IS_ERR(priv->drv_clk)) { - int phase; - /* * In almost all cases a 90 degree phase offset will provide * sufficient hold times across all valid input clock rates * assuming delay_o is not absurd for a given SoC. We'll use * that as a default. */ - phase = 90; + drv_phase = 90; switch (ios->timing) { case MMC_TIMING_MMC_DDR52: @@ -261,7 +267,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * to get the same timings. */ if (ios->bus_width == MMC_BUS_WIDTH_8) - phase = 180; + drv_phase = 180; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: @@ -273,22 +279,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * SoCs measured this seems to be OK, but it doesn't * hurt to give margin here, so we use 180. */ - phase = 180; + drv_phase = 180; break; } - rockchip_mmc_set_phase(host, false, phase); + /* Use out phase from phase map first */ + if (phase.valid) + drv_phase = phase.out_deg; + rockchip_mmc_set_phase(host, false, drv_phase); } } #define TUNING_ITERATION_TO_PHASE(i, num_phases) \ (DIV_ROUND_UP((i) * 360, num_phases)) -static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_rockchip_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ret = 0; int i; bool v, prev_v = 0, first_v; @@ -476,8 +484,8 @@ static int dw_mci_rockchip_init(struct dw_mci *host) struct dw_mci_rockchip_priv_data *priv = host->priv; int ret, i; - /* It is slot 8 on Rockchip SoCs */ - host->sdio_id0 = 8; + /* SDIO irq is the 8th on Rockchip SoCs */ + host->sdio_irq = 8; if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) { host->bus_hz /= RK3288_CLKGEN_DIV; diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c index 34964b0dab21..11472a641cbe 100644 --- a/drivers/mmc/host/dw_mmc-starfive.c +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -53,11 +53,10 @@ static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase mdelay(1); } -static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, +static int dw_mci_starfive_execute_tuning(struct dw_mci *host, u32 opcode) { static const int grade = MAX_DELAY_CHAIN; - struct dw_mci *host = slot->host; int smpl_phase, smpl_raise = -1, smpl_fall = -1; int ret; @@ -65,7 +64,7 @@ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, dw_mci_starfive_set_sample_phase(host, smpl_phase); mci_writel(host, RINTSTS, ALL_INT_CLR); - ret = mmc_send_tuning(slot->mmc, opcode, NULL); + ret = mmc_send_tuning(host->mmc, opcode, NULL); if (!ret && smpl_raise < 0) { smpl_raise = smpl_phase; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 07eb9f23b9d6..20193ee7b73e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -7,35 +7,28 @@ * Copyright (C) 2009, 2010 Imagination Technologies Ltd. */ -#include <linux/blkdev.h> +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/iopoll.h> -#include <linux/ioport.h> -#include <linux/ktime.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/prandom.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/stat.h> -#include <linux/delay.h> #include <linux/irq.h> +#include <linux/ktime.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> -#include <linux/bitops.h> -#include <linux/regulator/consumer.h> -#include <linux/of.h> #include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include "dw_mmc.h" @@ -47,8 +40,6 @@ SDMMC_INT_RESP_ERR | SDMMC_INT_HLE) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ DW_MCI_CMD_ERROR_FLAGS) -#define DW_MCI_SEND_STATUS 1 -#define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ @@ -107,15 +98,15 @@ struct idmac_desc { #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { - struct dw_mci_slot *slot = s->private; + struct dw_mci *host = s->private; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_command *stop; struct mmc_data *data; /* Make sure we get a consistent snapshot */ - spin_lock_bh(&slot->host->lock); - mrq = slot->mrq; + spin_lock_bh(&host->lock); + mrq = host->mrq; if (mrq) { cmd = mrq->cmd; @@ -140,7 +131,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v) stop->resp[2], stop->error); } - spin_unlock_bh(&slot->host->lock); + spin_unlock_bh(&host->lock); return 0; } @@ -165,10 +156,9 @@ static int dw_mci_regs_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(dw_mci_regs); -static void dw_mci_init_debugfs(struct dw_mci_slot *slot) +static void dw_mci_init_debugfs(struct dw_mci *host) { - struct mmc_host *mmc = slot->mmc; - struct dw_mci *host = slot->host; + struct mmc_host *mmc = host->mmc; struct dentry *root; root = mmc->debugfs_root; @@ -176,7 +166,7 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot) return; debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops); - debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops); + debugfs_create_file("req", 0400, root, host, &dw_mci_req_fops); debugfs_create_u32("state", 0400, root, &host->state); debugfs_create_xul("pending_events", 0400, root, &host->pending_events); @@ -231,9 +221,8 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) } } -static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +static void mci_send_cmd(struct dw_mci *host, u32 cmd, u32 arg) { - struct dw_mci *host = slot->host; unsigned int cmd_status = 0; mci_writel(host, CMDARG, arg); @@ -244,15 +233,14 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) if (readl_poll_timeout_atomic(host->regs + SDMMC_CMD, cmd_status, !(cmd_status & SDMMC_CMD_START), 1, 500 * USEC_PER_MSEC)) - dev_err(&slot->mmc->class_dev, + dev_err(&host->mmc->class_dev, "Timeout sending command (cmd %#x arg %#x status %#x)\n", cmd, arg, cmd_status); } static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); u32 cmdr; cmd->error = -EINPROGRESS; @@ -274,8 +262,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_VOLT_SWITCH; /* Change state to continue to handle CMD11 weirdness */ - WARN_ON(slot->host->state != STATE_SENDING_CMD); - slot->host->state = STATE_SENDING_CMD11; + WARN_ON(host->state != STATE_SENDING_CMD); + host->state = STATE_SENDING_CMD11; /* * We need to disable low power mode (automatic clock stop) @@ -289,9 +277,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) * until the voltage change is all done. */ clk_en_a = mci_readl(host, CLKENA); - clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); + clk_en_a &= ~SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } @@ -311,7 +299,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_DAT_WR; } - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -350,7 +338,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) cmdr = stop->opcode | SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -480,7 +468,7 @@ static void dw_mci_dmac_complete_dma(void *arg) if ((host->use_dma == TRANS_MODE_EDMAC) && data && (data->flags & MMC_DATA_READ)) /* Invalidate cache after read */ - dma_sync_sg_for_cpu(mmc_dev(host->slot->mmc), + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); @@ -575,23 +563,26 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } -static inline int dw_mci_prepare_desc64(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) +static inline int dw_mci_prepare_desc(struct dw_mci *host, struct mmc_data *data, + unsigned int sg_len, bool is_64bit) { unsigned int desc_len; - struct idmac_desc_64addr *desc_first, *desc_last, *desc; - u32 val; - int i; + struct idmac_desc *desc_first, *desc_last, *desc; + struct idmac_desc_64addr *desc64_first, *desc64_last, *desc64; + u32 val, des0; + int i, err; - desc_first = desc_last = desc = host->sg_cpu; + if (is_64bit) + desc64_first = desc64_last = desc64 = host->sg_cpu; + else + desc_first = desc_last = desc = host->sg_cpu; for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); u64 mem_addr = sg_dma_address(&data->sg[i]); - for ( ; length ; desc++) { + while (length > 0) { desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? length : DW_MCI_DESC_DATA_LENGTH; @@ -603,113 +594,60 @@ static inline int dw_mci_prepare_desc64(struct dw_mci *host, * isn't still owned by IDMAC as IDMAC's write * ops and CPU's read ops are asynchronous. */ - if (readl_poll_timeout_atomic(&desc->des0, val, - !(val & IDMAC_DES0_OWN), - 10, 100 * USEC_PER_MSEC)) + if (is_64bit) + err = readl_poll_timeout_atomic(&desc64->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + else + err = readl_poll_timeout_atomic(&desc->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + if (err) goto err_own_bit; - /* - * Set the OWN bit and disable interrupts - * for this descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; - - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; - - /* Update physical address for the next desc */ - mem_addr += desc_len; - - /* Save pointer to the last descriptor */ - desc_last = desc; - } - } - - /* Set first descriptor */ - desc_first->des0 |= IDMAC_DES0_FD; - - /* Set last descriptor */ - desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc_last->des0 |= IDMAC_DES0_LD; - - return 0; -err_own_bit: - /* restore the descriptor chain as it's polluted */ - dev_dbg(host->dev, "descriptor is still owned by IDMAC.\n"); - memset(host->sg_cpu, 0, DESC_RING_BUF_SZ); - dw_mci_idmac_init(host); - return -EINVAL; -} - - -static inline int dw_mci_prepare_desc32(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) -{ - unsigned int desc_len; - struct idmac_desc *desc_first, *desc_last, *desc; - u32 val; - int i; - - desc_first = desc_last = desc = host->sg_cpu; - - for (i = 0; i < sg_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); - - u32 mem_addr = sg_dma_address(&data->sg[i]); - - for ( ; length ; desc++) { - desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? - length : DW_MCI_DESC_DATA_LENGTH; - - length -= desc_len; - - /* - * Wait for the former clear OWN bit operation - * of IDMAC to make sure that this descriptor - * isn't still owned by IDMAC as IDMAC's write - * ops and CPU's read ops are asynchronous. - */ - if (readl_poll_timeout_atomic(&desc->des0, val, - IDMAC_OWN_CLR64(val), - 10, - 100 * USEC_PER_MSEC)) - goto err_own_bit; + des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + if (is_64bit) + desc64->des0 = des0; + else + desc->des0 = cpu_to_le32(des0); /* - * Set the OWN bit and disable interrupts - * for this descriptor + * 1. Set OWN bit and disable interrupts for this descriptor + * 2. Set Buffer length + * Set physical address to DMA to/from */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | - IDMAC_DES0_CH); - - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + if (is_64bit) { + desc64->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc64, desc_len); + desc64->des4 = mem_addr & 0xffffffff; + desc64->des5 = mem_addr >> 32; + } else { + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); + desc->des2 = cpu_to_le32(mem_addr); + } /* Update physical address for the next desc */ mem_addr += desc_len; /* Save pointer to the last descriptor */ - desc_last = desc; + if (is_64bit) { + desc64_last = desc64; + desc64++; + } else { + desc_last = desc; + desc++; + } } } - /* Set first descriptor */ - desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); - - /* Set last descriptor */ - desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | - IDMAC_DES0_DIC)); - desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + /* Set the first descriptor and the last descriptor */ + if (is_64bit) { + desc64_first->des0 |= IDMAC_DES0_FD; + desc64_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc64_last->des0 |= IDMAC_DES0_LD; + } else { + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + } return 0; err_own_bit: @@ -725,11 +663,7 @@ static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) u32 temp; int ret; - if (host->dma_64bit_address == 1) - ret = dw_mci_prepare_desc64(host, host->data, sg_len); - else - ret = dw_mci_prepare_desc32(host, host->data, sg_len); - + ret = dw_mci_prepare_desc(host, host->data, sg_len, host->dma_64bit_address); if (ret) goto out; @@ -823,7 +757,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, /* Flush cache before write */ if (host->data->flags & MMC_DATA_WRITE) - dma_sync_sg_for_device(mmc_dev(host->slot->mmc), sgl, + dma_sync_sg_for_device(mmc_dev(host->mmc), sgl, sg_elems, DMA_TO_DEVICE); dma_async_issue_pending(host->dms->ch); @@ -913,16 +847,16 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, static void dw_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; /* This data might be unmapped at this time */ data->host_cookie = COOKIE_UNMAPPED; - if (dw_mci_pre_dma_transfer(slot->host, mrq->data, + if (dw_mci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED) < 0) data->host_cookie = COOKIE_UNMAPPED; } @@ -931,14 +865,14 @@ static void dw_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, int err) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; if (data->host_cookie != COOKIE_UNMAPPED) - dma_unmap_sg(slot->host->dev, + dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); @@ -947,43 +881,21 @@ static void dw_mci_post_req(struct mmc_host *mmc, static int dw_mci_get_cd(struct mmc_host *mmc) { - int present; - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); int gpio_cd = mmc_gpio_get_cd(mmc); - /* Use platform get_cd function, else try onboard card detect */ - if (((mmc->caps & MMC_CAP_NEEDS_POLL) - || !mmc_card_is_removable(mmc))) { - present = 1; - - if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { - if (mmc->caps & MMC_CAP_NEEDS_POLL) { - dev_info(&mmc->class_dev, - "card is polling.\n"); - } else { - dev_info(&mmc->class_dev, - "card is non-removable.\n"); - } - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - } + if (mmc->caps & MMC_CAP_NEEDS_POLL) + return 1; - return present; - } else if (gpio_cd >= 0) - present = gpio_cd; - else - present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) - == 0 ? 1 : 0; + if (!mmc_card_is_removable(mmc)) + return 1; - spin_lock_bh(&host->lock); - if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags)) - dev_dbg(&mmc->class_dev, "card is present\n"); - else if (!present && - !test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags)) - dev_dbg(&mmc->class_dev, "card is not present\n"); - spin_unlock_bh(&host->lock); + /* Try slot gpio detection */ + if (gpio_cd >= 0) + return !!gpio_cd; - return present; + /* Host native card detect */ + return !(mci_readl(host, CDETECT) & BIT(0)); } static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) @@ -1150,9 +1062,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->data = data; if (data->flags & MMC_DATA_READ) - host->dir_status = DW_MCI_RECV_STATUS; + host->dir_status = MMC_DATA_READ; else - host->dir_status = DW_MCI_SEND_STATUS; + host->dir_status = MMC_DATA_WRITE; dw_mci_ctrl_thld(host, data); @@ -1200,10 +1112,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } } -static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) +static void dw_mci_setup_bus(struct dw_mci *host, bool force_clkinit) { - struct dw_mci *host = slot->host; - unsigned int clock = slot->clock; + unsigned int clock = host->clock; u32 div; u32 clk_en_a; u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; @@ -1212,11 +1123,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; - slot->mmc->actual_clock = 0; + host->mmc->actual_clock = 0; if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -1228,14 +1139,14 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - if ((clock != slot->__clk_old && - !test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) || + if ((clock != host->clk_old && + !test_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags)) || force_clkinit) { /* Silent the verbose log if calling from PM context */ if (!force_clkinit) - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", - slot->id, host->bus_hz, clock, + dev_info(&host->mmc->class_dev, + "Bus speed = %dHz (req %dHz, actual %dHZ div = %d)\n", + host->bus_hz, clock, div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); @@ -1243,9 +1154,9 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) * If card is polling, display the message only * one time at boot time. */ - if (slot->mmc->caps & MMC_CAP_NEEDS_POLL && - slot->mmc->f_min == clock) - set_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags); + if (host->mmc->caps & MMC_CAP_NEEDS_POLL && + host->mmc->f_min == clock) + set_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags); } /* disable clock */ @@ -1253,33 +1164,33 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* set clock to desired speed */ mci_writel(host, CLKDIV, div); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ - clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) - clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; + clk_en_a = SDMMC_CLKEN_ENABLE; + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags)) + clk_en_a |= SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* keep the last clock value that was requested from core */ - slot->__clk_old = clock; - slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->clk_old = clock; + host->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : host->bus_hz; } host->current_speed = clock; - /* Set the current slot bus width */ - mci_writel(host, CTYPE, (slot->ctype << slot->id)); + /* Set the current bus width */ + mci_writel(host, CTYPE, host->ctype); } static void dw_mci_set_data_timeout(struct dw_mci *host, @@ -1313,15 +1224,13 @@ static void dw_mci_set_data_timeout(struct dw_mci *host, timeout_ns, tmout >> 8); } -static void __dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot, - struct mmc_command *cmd) +static void dw_mci_start_request(struct dw_mci *host, struct mmc_command *cmd) { struct mmc_request *mrq; struct mmc_data *data; u32 cmdflags; - mrq = slot->mrq; + mrq = host->mrq; host->mrq = mrq; @@ -1338,10 +1247,10 @@ static void __dw_mci_start_request(struct dw_mci *host, mci_writel(host, BLKSIZ, data->blksz); } - cmdflags = dw_mci_prepare_command(slot->mmc, cmd); + cmdflags = dw_mci_prepare_command(host->mmc, cmd); /* this is the first command, send the initialization clock */ - if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags)) + if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &host->flags)) cmdflags |= SDMMC_CMD_INIT; if (data) { @@ -1374,27 +1283,32 @@ static void __dw_mci_start_request(struct dw_mci *host, host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } -static void dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot) +static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { - struct mmc_request *mrq = slot->mrq; + struct dw_mci *host = mmc_priv(mmc); struct mmc_command *cmd; - cmd = mrq->sbc ? mrq->sbc : mrq->cmd; - __dw_mci_start_request(host, slot, cmd); -} + WARN_ON(host->mrq); -/* must be called with host->lock held */ -static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, - struct mmc_request *mrq) -{ - dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", - host->state); + /* + * The check for card presence and queueing of the request must be + * atomic, otherwise the card could be removed in between and the + * request wouldn't fail until another card was inserted. + */ + if (!dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } - slot->mrq = mrq; + spin_lock_bh(&host->lock); + + dev_vdbg(&host->mmc->class_dev, "request: state=%d\n", + host->state); + host->mrq = mrq; if (host->state == STATE_WAITING_CMD11_DONE) { - dev_warn(&slot->mmc->class_dev, + dev_warn(&host->mmc->class_dev, "Voltage change didn't complete\n"); /* * this case isn't expected to happen, so we can @@ -1406,168 +1320,119 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); - } else { - list_add_tail(&slot->queue_node, &host->queue); - } -} - -static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; - - WARN_ON(slot->mrq); - - /* - * The check for card presence and queueing of the request must be - * atomic, otherwise the card could be removed in between and the - * request wouldn't fail until another card was inserted. - */ - - if (!dw_mci_get_cd(mmc)) { - mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - return; + cmd = mrq->sbc ? mrq->sbc : mrq->cmd; + dw_mci_start_request(host, cmd); } - spin_lock_bh(&host->lock); - - dw_mci_queue_request(host, slot, mrq); - spin_unlock_bh(&host->lock); } static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - const struct dw_mci_drv_data *drv_data = slot->host->drv_data; + struct dw_mci *host = mmc_priv(mmc); + const struct dw_mci_drv_data *drv_data = host->drv_data; u32 regs; int ret; switch (ios->bus_width) { case MMC_BUS_WIDTH_4: - slot->ctype = SDMMC_CTYPE_4BIT; + host->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: - slot->ctype = SDMMC_CTYPE_8BIT; + host->ctype = SDMMC_CTYPE_8BIT; break; default: /* set default 1 bit mode */ - slot->ctype = SDMMC_CTYPE_1BIT; + host->ctype = SDMMC_CTYPE_1BIT; } - regs = mci_readl(slot->host, UHS_REG); + regs = mci_readl(host, UHS_REG); /* DDR mode set */ if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_HS400) - regs |= ((0x1 << slot->id) << 16); + regs |= BIT(16); else - regs &= ~((0x1 << slot->id) << 16); + regs &= ~BIT(16); - mci_writel(slot->host, UHS_REG, regs); - slot->host->timing = ios->timing; + mci_writel(host, UHS_REG, regs); + host->timing = ios->timing; /* * Use mirror of ios->clock to prevent race with mmc * core ios update when finding the minimum. */ - slot->clock = ios->clock; + host->clock = ios->clock; if (drv_data && drv_data->set_ios) - drv_data->set_ios(slot->host, ios); + drv_data->set_ios(host, ios); switch (ios->power_mode) { case MMC_POWER_UP: - if (!IS_ERR(mmc->supply.vmmc)) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, - ios->vdd); - if (ret) { - dev_err(slot->host->dev, - "failed to enable vmmc regulator\n"); - /*return, if failed turn on vmmc*/ - return; - } + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(host->dev, "failed to enable vmmc regulator\n"); + return; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); + set_bit(DW_MMC_CARD_NEED_INIT, &host->flags); + regs = mci_readl(host, PWREN); + regs |= BIT(0); + mci_writel(host, PWREN, regs); break; case MMC_POWER_ON: - if (!slot->host->vqmmc_enabled) { - if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); - if (ret < 0) - dev_err(slot->host->dev, - "failed to enable vqmmc\n"); - else - slot->host->vqmmc_enabled = true; - - } else { - /* Keep track so we don't reset again */ - slot->host->vqmmc_enabled = true; - } - - /* Reset our state machine after powering on */ - dw_mci_ctrl_reset(slot->host, - SDMMC_CTRL_ALL_RESET_FLAGS); - } - + mmc_regulator_enable_vqmmc(mmc); /* Adjust clock / bus width after power is up */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); break; case MMC_POWER_OFF: /* Turn clock off before power goes down */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) - regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; + mmc_regulator_disable_vqmmc(mmc); - regs = mci_readl(slot->host, PWREN); - regs &= ~(1 << slot->id); - mci_writel(slot->host, PWREN, regs); + regs = mci_readl(host, PWREN); + regs &= ~BIT(0); + mci_writel(host, PWREN, regs); + /* Reset our state machine after powering off */ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); break; default: break; } - if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) - slot->host->state = STATE_IDLE; + if (host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + host->state = STATE_IDLE; } static int dw_mci_card_busy(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); u32 status; /* * Check the busy bit which is low when DAT[3:0] * (the data lines) are 0000 */ - status = mci_readl(slot->host, STATUS); + status = mci_readl(host, STATUS); return !!(status & SDMMC_STATUS_BUSY); } static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; - u32 v18 = SDMMC_UHS_18V << slot->id; + u32 v18 = SDMMC_UHS_18V; int ret; if (drv_data && drv_data->switch_voltage) - return drv_data->switch_voltage(mmc, ios); + return drv_data->switch_voltage(host, ios); /* * Program the voltage. Note that some instances of dw_mmc may use @@ -1597,7 +1462,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ @@ -1605,7 +1470,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = gpio_ro; else read_only = - mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; + mci_readl(host, WRTPRT) & BIT(0) ? 1 : 0; dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); @@ -1615,8 +1480,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) static void dw_mci_hw_reset(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int reset; @@ -1639,18 +1503,17 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) * tRSTH >= 1us: RST_n high period */ reset = mci_readl(host, RST_N); - reset &= ~(SDMMC_RST_HWACTIVE << slot->id); + reset &= ~SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(1, 2); - reset |= SDMMC_RST_HWACTIVE << slot->id; + reset |= SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(200, 300); } -static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) +static void dw_mci_prepare_sdio_irq(struct dw_mci *host, bool prepare) { - struct dw_mci *host = slot->host; - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR; u32 clk_en_a_old; u32 clk_en_a; @@ -1662,23 +1525,22 @@ static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) clk_en_a_old = mci_readl(host, CLKENA); if (prepare) { - set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + set_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old & ~clken_low_pwr; } else { - clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old | clken_low_pwr; } if (clk_en_a != clk_en_a_old) { mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } } -static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) +static void __dw_mci_enable_sdio_irq(struct dw_mci *host, int enb) { - struct dw_mci *host = slot->host; unsigned long irqflags; u32 int_mask; @@ -1687,9 +1549,9 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); if (enb) - int_mask |= SDMMC_INT_SDIO(slot->sdio_id); + int_mask |= SDMMC_INT_SDIO(host->sdio_irq); else - int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); + int_mask &= ~SDMMC_INT_SDIO(host->sdio_irq); mci_writel(host, INTMASK, int_mask); spin_unlock_irqrestore(&host->irq_lock, irqflags); @@ -1697,11 +1559,10 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); - dw_mci_prepare_sdio_irq(slot, enb); - __dw_mci_enable_sdio_irq(slot, enb); + dw_mci_prepare_sdio_irq(host, enb); + __dw_mci_enable_sdio_irq(host, enb); /* Avoid runtime suspending the device when SDIO IRQ is enabled */ if (enb) @@ -1712,28 +1573,26 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) static void dw_mci_ack_sdio_irq(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); - __dw_mci_enable_sdio_irq(slot, 1); + __dw_mci_enable_sdio_irq(host, 1); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int err = -EINVAL; if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot, opcode); + err = drv_data->execute_tuning(host, opcode); return err; } static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; if (drv_data && drv_data->prepare_hs400_tuning) @@ -1804,7 +1663,7 @@ static bool dw_mci_reset(struct dw_mci *host) ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0); + mci_send_cmd(host, SDMMC_CMD_UPD_CLK, 0); return ret; } @@ -1895,29 +1754,16 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { - struct dw_mci_slot *slot; - struct mmc_host *prev_mmc = host->slot->mmc; + struct mmc_host *prev_mmc = host->mmc; WARN_ON(host->cmd || host->data); - host->slot->mrq = NULL; host->mrq = NULL; - if (!list_empty(&host->queue)) { - slot = list_entry(host->queue.next, - struct dw_mci_slot, queue_node); - list_del(&slot->queue_node); - dev_vdbg(host->dev, "list not empty: %s is next\n", - mmc_hostname(slot->mmc)); - host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); - } else { - dev_vdbg(host->dev, "list empty\n"); - if (host->state == STATE_SENDING_CMD11) - host->state = STATE_WAITING_CMD11_DONE; - else - host->state = STATE_IDLE; - } + if (host->state == STATE_SENDING_CMD11) + host->state = STATE_WAITING_CMD11_DONE; + else + host->state = STATE_IDLE; spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); @@ -1968,7 +1814,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->error = -EILSEQ; } else if (status & SDMMC_INT_EBE) { if (host->dir_status == - DW_MCI_SEND_STATUS) { + MMC_DATA_WRITE) { /* * No data CRC status was returned. * The number of bytes transferred @@ -1977,7 +1823,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->bytes_xfered = 0; data->error = -ETIMEDOUT; } else if (host->dir_status == - DW_MCI_RECV_STATUS) { + MMC_DATA_READ) { data->error = -EILSEQ; } } else { @@ -2095,8 +1941,7 @@ static void dw_mci_work_func(struct work_struct *t) set_bit(EVENT_CMD_COMPLETE, &host->completed_events); err = dw_mci_command_complete(host, cmd); if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, host->slot, - mrq->cmd); + dw_mci_start_request(host, mrq->cmd); goto unlock; } @@ -2123,7 +1968,7 @@ static void dw_mci_work_func(struct work_struct *t) * avoids races and keeps things simple. */ if (err != -ETIMEDOUT && - host->dir_status == DW_MCI_RECV_STATUS) { + host->dir_status == MMC_DATA_READ) { state = STATE_SENDING_DATA; continue; } @@ -2167,7 +2012,7 @@ static void dw_mci_work_func(struct work_struct *t) * If all data-related interrupts don't come * within the given time in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2207,7 +2052,7 @@ static void dw_mci_work_func(struct work_struct *t) * interrupt doesn't come within the given time. * in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2803,17 +2648,14 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) static void dw_mci_handle_cd(struct dw_mci *host) { - struct dw_mci_slot *slot = host->slot; - - mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); + mmc_detect_change(host->mmc, + msecs_to_jiffies(host->detect_delay_ms)); } static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; u32 pending; - struct dw_mci_slot *slot = host->slot; pending = mci_readl(host, MINTSTS); /* read-only mask reg */ @@ -2878,7 +2720,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (!host->data_status) host->data_status = pending; smp_wmb(); /* drain writebuffer */ - if (host->dir_status == DW_MCI_RECV_STATUS) { + if (host->dir_status == MMC_DATA_READ) { if (host->sg != NULL) dw_mci_read_data_pio(host, true); } @@ -2890,13 +2732,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_RXDR) { mci_writel(host, RINTSTS, SDMMC_INT_RXDR); - if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) + if (host->dir_status == MMC_DATA_READ && host->sg) dw_mci_read_data_pio(host, false); } if (pending & SDMMC_INT_TXDR) { mci_writel(host, RINTSTS, SDMMC_INT_TXDR); - if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) + if (host->dir_status == MMC_DATA_WRITE && host->sg) dw_mci_write_data_pio(host); } @@ -2914,11 +2756,11 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_handle_cd(host); } - if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + if (pending & SDMMC_INT_SDIO(host->sdio_irq)) { mci_writel(host, RINTSTS, - SDMMC_INT_SDIO(slot->sdio_id)); - __dw_mci_enable_sdio_irq(slot, 0); - sdio_signal_irq(slot->mmc); + SDMMC_INT_SDIO(host->sdio_irq)); + __dw_mci_enable_sdio_irq(host, 0); + sdio_signal_irq(host->mmc); } } @@ -2950,29 +2792,19 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) +static int dw_mci_init_host_caps(struct dw_mci *host) { - struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ctrl_id; - if (host->pdata->caps) - mmc->caps = host->pdata->caps; - - if (host->pdata->pm_caps) - mmc->pm_caps = host->pdata->pm_caps; - if (drv_data) mmc->caps |= drv_data->common_caps; - if (host->dev->of_node) { - ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (ctrl_id < 0) - ctrl_id = 0; - } else { + if (host->dev->of_node) + ctrl_id = mmc->index; + else ctrl_id = to_platform_device(host->dev)->id; - } if (drv_data && drv_data->caps) { if (ctrl_id >= drv_data->num_caps) { @@ -2983,9 +2815,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) mmc->caps |= drv_data->caps[ctrl_id]; } - if (host->pdata->caps2) - mmc->caps2 = host->pdata->caps2; - /* if host has set a minimum_freq, we should respect it */ if (host->minimum_speed) mmc->f_min = host->minimum_speed; @@ -3002,23 +2831,11 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) return 0; } -static int dw_mci_init_slot(struct dw_mci *host) +static int dw_mci_init_host(struct dw_mci *host) { - struct mmc_host *mmc; - struct dw_mci_slot *slot; + struct mmc_host *mmc = host->mmc; int ret; - mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot)); - if (!mmc) - return -ENOMEM; - - slot = mmc_priv(mmc); - slot->id = 0; - slot->sdio_id = host->sdio_id0 + slot->id; - slot->mmc = mmc; - slot->host = host; - host->slot = slot; - mmc->ops = &dw_mci_ops; /*if there are external regulators, get them*/ @@ -3033,7 +2850,9 @@ static int dw_mci_init_slot(struct dw_mci *host) if (ret) return ret; - ret = dw_mci_init_slot_caps(slot); + mmc_of_parse_clk_phase(host->dev, &host->phase_map); + + ret = dw_mci_init_host_caps(host); if (ret) return ret; @@ -3061,6 +2880,11 @@ static int dw_mci_init_slot(struct dw_mci *host) mmc->max_seg_size = mmc->max_req_size; } + if (mmc->caps & MMC_CAP_NEEDS_POLL) + dev_info(&mmc->class_dev, "card is polling.\n"); + else if (!mmc_card_is_removable(mmc)) + dev_info(&mmc->class_dev, "card is non-removable.\n"); + dw_mci_get_cd(mmc); ret = mmc_add_host(mmc); @@ -3068,17 +2892,16 @@ static int dw_mci_init_slot(struct dw_mci *host) return ret; #if defined(CONFIG_DEBUG_FS) - dw_mci_init_debugfs(slot); + dw_mci_init_debugfs(host); #endif return 0; } -static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) +static void dw_mci_cleanup_host(struct dw_mci *host) { /* Debugfs stuff is cleaned up by mmc core */ - mmc_remove_host(slot->mmc); - slot->host->slot = NULL; + mmc_remove_host(host->mmc); } static void dw_mci_init_dma(struct dw_mci *host) @@ -3291,54 +3114,43 @@ exit: spin_unlock_irqrestore(&host->irq_lock, irqflags); } -#ifdef CONFIG_OF -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) +static int dw_mci_parse_dt(struct dw_mci *host) { - struct dw_mci_board *pdata; struct device *dev = host->dev; const struct dw_mci_drv_data *drv_data = host->drv_data; int ret; u32 clock_frequency; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - /* find reset controller when exist */ - pdata->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); - if (IS_ERR(pdata->rstc)) - return ERR_CAST(pdata->rstc); + host->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); + if (IS_ERR(host->rstc)) + return PTR_ERR(host->rstc); - if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) + if (!host->fifo_depth && device_property_read_u32(dev, "fifo-depth", &host->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); - device_property_read_u32(dev, "card-detect-delay", - &pdata->detect_delay_ms); + if (!host->detect_delay_ms) + device_property_read_u32(dev, "card-detect-delay", + &host->detect_delay_ms); - device_property_read_u32(dev, "data-addr", &host->data_addr_override); + if (!host->data_addr_override) + device_property_read_u32(dev, "data-addr", &host->data_addr_override); if (device_property_present(dev, "fifo-watermark-aligned")) host->wm_aligned = true; - if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency)) - pdata->bus_hz = clock_frequency; + if (!host->bus_hz && !device_property_read_u32(dev, "clock-frequency", &clock_frequency)) + host->bus_hz = clock_frequency; if (drv_data && drv_data->parse_dt) { ret = drv_data->parse_dt(host); if (ret) - return ERR_PTR(ret); + return ret; } - return pdata; -} - -#else /* CONFIG_OF */ -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) -{ - return ERR_PTR(-EINVAL); + return 0; } -#endif /* CONFIG_OF */ static void dw_mci_enable_cd(struct dw_mci *host) { @@ -3346,13 +3158,13 @@ static void dw_mci_enable_cd(struct dw_mci *host) u32 temp; /* - * No need for CD if all slots have a non-error GPIO + * No need for CD if host has a non-error GPIO * as well as broken card detection is found. */ - if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL) + if (host->mmc->caps & MMC_CAP_NEEDS_POLL) return; - if (mmc_gpio_get_cd(host->slot->mmc) < 0) { + if (mmc_gpio_get_cd(host->mmc) < 0) { spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; @@ -3361,18 +3173,32 @@ static void dw_mci_enable_cd(struct dw_mci *host) } } +struct dw_mci *dw_mci_alloc_host(struct device *dev) +{ + struct mmc_host *mmc; + struct dw_mci *host; + + mmc = devm_mmc_alloc_host(dev, sizeof(struct dw_mci)); + if (!mmc) + return ERR_PTR(-ENOMEM); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dev = dev; + + return host; +} +EXPORT_SYMBOL(dw_mci_alloc_host); + int dw_mci_probe(struct dw_mci *host) { const struct dw_mci_drv_data *drv_data = host->drv_data; int width, i, ret = 0; u32 fifo_size; - if (!host->pdata) { - host->pdata = dw_mci_parse_dt(host); - if (IS_ERR(host->pdata)) - return dev_err_probe(host->dev, PTR_ERR(host->pdata), - "platform data not available\n"); - } + ret = dw_mci_parse_dt(host); + if (ret) + return dev_err_probe(host->dev, ret, "parse dt failed\n"); host->biu_clk = devm_clk_get(host->dev, "biu"); if (IS_ERR(host->biu_clk)) { @@ -3395,8 +3221,6 @@ int dw_mci_probe(struct dw_mci *host) ret = PTR_ERR(host->ciu_clk); if (ret == -EPROBE_DEFER) goto err_clk_biu; - - host->bus_hz = host->pdata->bus_hz; } else { ret = clk_prepare_enable(host->ciu_clk); if (ret) { @@ -3404,12 +3228,12 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_biu; } - if (host->pdata->bus_hz) { - ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); + if (host->bus_hz) { + ret = clk_set_rate(host->ciu_clk, host->bus_hz); if (ret) dev_warn(host->dev, "Unable to set bus rate to %uHz\n", - host->pdata->bus_hz); + host->bus_hz); } host->bus_hz = clk_get_rate(host->ciu_clk); } @@ -3421,10 +3245,10 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - if (host->pdata->rstc) { - reset_control_assert(host->pdata->rstc); + if (host->rstc) { + reset_control_assert(host->rstc); usleep_range(10, 50); - reset_control_deassert(host->pdata->rstc); + reset_control_deassert(host->rstc); } if (drv_data && drv_data->init) { @@ -3442,7 +3266,6 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); - INIT_LIST_HEAD(&host->queue); dw_mci_init_fault(host); @@ -3483,7 +3306,6 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - host->dma_ops = host->pdata->dma_ops; dw_mci_init_dma(host); /* Clear the interrupts for the host controller */ @@ -3497,7 +3319,7 @@ int dw_mci_probe(struct dw_mci *host) * FIFO threshold settings RxMark = fifo_size / 2 - 1, * Tx Mark = fifo_size / 2 DMA Size = 8 */ - if (!host->pdata->fifo_depth) { + if (!host->fifo_depth) { /* * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may * have been overwritten by the bootloader, just like we're @@ -3507,7 +3329,7 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = mci_readl(host, FIFOTH); fifo_size = 1 + ((fifo_size >> 16) & 0xfff); } else { - fifo_size = host->pdata->fifo_depth; + fifo_size = host->fifo_depth; } host->fifo_depth = fifo_size; host->fifoth_val = @@ -3552,14 +3374,13 @@ int dw_mci_probe(struct dw_mci *host) "DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n", host->irq, width, fifo_size); - /* We need at least one slot to succeed */ - ret = dw_mci_init_slot(host); + ret = dw_mci_init_host(host); if (ret) { - dev_dbg(host->dev, "slot %d init failed\n", i); + dev_dbg(host->dev, "host init failed\n"); goto err_dmaunmap; } - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; @@ -3568,7 +3389,7 @@ err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); err_clk_ciu: clk_disable_unprepare(host->ciu_clk); @@ -3582,9 +3403,8 @@ EXPORT_SYMBOL(dw_mci_probe); void dw_mci_remove(struct dw_mci *host) { - dev_dbg(host->dev, "remove slot\n"); - if (host->slot) - dw_mci_cleanup_slot(host->slot); + dev_dbg(host->dev, "remove host\n"); + dw_mci_cleanup_host(host); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ @@ -3596,16 +3416,13 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->biu_clk); } EXPORT_SYMBOL(dw_mci_remove); - - -#ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); @@ -3615,9 +3432,8 @@ int dw_mci_runtime_suspend(struct device *dev) clk_disable_unprepare(host->ciu_clk); - if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return 0; @@ -3629,9 +3445,8 @@ int dw_mci_runtime_resume(struct device *dev) int ret = 0; struct dw_mci *host = dev_get_drvdata(dev); - if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) { + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) { ret = clk_prepare_enable(host->biu_clk); if (ret) return ret; @@ -3647,8 +3462,11 @@ int dw_mci_runtime_resume(struct device *dev) goto err; } - if (host->use_dma && host->dma_ops->init) - host->dma_ops->init(host); + if (host->use_dma && host->dma_ops->init) { + ret = host->dma_ops->init(host); + if (ret) + return ret; + } /* * Restore the initial value at FIFOTH register @@ -3667,44 +3485,35 @@ int dw_mci_runtime_resume(struct device *dev) mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - if (host->slot && host->slot->mmc->pm_flags & MMC_PM_KEEP_POWER) - dw_mci_set_ios(host->slot->mmc, &host->slot->mmc->ios); + if (host->mmc->pm_flags & MMC_PM_KEEP_POWER) + dw_mci_set_ios(host->mmc, &host->mmc->ios); /* Force setup bus to guarantee available clock output */ - dw_mci_setup_bus(host->slot, true); + dw_mci_setup_bus(host, true); /* Re-enable SDIO interrupts. */ - if (sdio_irq_claimed(host->slot->mmc)) - __dw_mci_enable_sdio_irq(host->slot, 1); + if (sdio_irq_claimed(host->mmc)) + __dw_mci_enable_sdio_irq(host, 1); - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; err: - if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return ret; } EXPORT_SYMBOL(dw_mci_runtime_resume); -#endif /* CONFIG_PM */ - -static int __init dw_mci_init(void) -{ - pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); - return 0; -} - -static void __exit dw_mci_exit(void) -{ -} -module_init(dw_mci_init); -module_exit(dw_mci_exit); +const struct dev_pm_ops dw_mci_pmops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(dw_mci_pmops); MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 648b4a5641bf..42e58be74ce0 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -43,8 +43,6 @@ enum dw_mci_cookie { COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */ }; -struct mmc_data; - enum { TRANS_MODE_PIO = 0, TRANS_MODE_IDMAC, @@ -57,14 +55,14 @@ struct dw_mci_dma_slave { }; /** - * struct dw_mci - MMC controller state shared between all slots + * struct dw_mci - MMC controller state * @lock: Spinlock protecting the queue and associated data. * @irq_lock: Spinlock protecting the INTMASK setting. * @regs: Pointer to MMIO registers. * @fifo_reg: Pointer to MMIO registers for data FIFO * @sg: Scatterlist entry currently being processed by PIO code, if any. * @sg_miter: PIO mapping scatterlist iterator. - * @mrq: The request currently being processed on @slot, + * @mrq: The request currently being processed on @host, * or NULL if the controller is idle. * @cmd: The command currently being sent to the card, or NULL. * @data: The data currently being transferred, or NULL if no data @@ -78,7 +76,7 @@ struct dw_mci_dma_slave { * @dma_64bit_address: Whether DMA supports 64-bit address mode or not. * @sg_dma: Bus address of DMA buffer. * @sg_cpu: Virtual address of DMA buffer. - * @dma_ops: Pointer to platform-specific DMA callbacks. + * @dma_ops: Pointer to DMA callbacks. * @cmd_status: Snapshot of SR taken upon completion of the current * @ring_size: Buffer size for idma descriptors. * command. Only valid when EVENT_CMD_COMPLETE is pending. @@ -96,7 +94,6 @@ struct dw_mci_dma_slave { * @completed_events: Bitmask of events which the state machine has * processed. * @state: BH work state. - * @queue: List of slots waiting for access to the controller. * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus * rate and timeout calculations. * @current_speed: Configured rate of the controller. @@ -104,12 +101,10 @@ struct dw_mci_dma_slave { * @fifoth_val: The value of FIFOTH register. * @verid: Denote Version ID. * @dev: Device associated with the MMC controller. - * @pdata: Platform data associated with the MMC controller. * @drv_data: Driver specific data for identified variant of the controller * @priv: Implementation defined private data. * @biu_clk: Pointer to bus interface unit clock instance. * @ciu_clk: Pointer to card interface unit clock instance. - * @slot: Slots sharing this MMC controller. * @fifo_depth: depth of FIFO. * @data_addr_override: override fifo reg offset with this value. * @wm_aligned: force fifo watermark equal with data length in PIO mode. @@ -121,23 +116,28 @@ struct dw_mci_dma_slave { * @push_data: Pointer to FIFO push function. * @pull_data: Pointer to FIFO pull function. * @quirks: Set of quirks that apply to specific versions of the IP. - * @vqmmc_enabled: Status of vqmmc, should be true or false. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. - * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @sdio_irq: SDIO interrupt bit in interrupt registers. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. + * @mmc: The mmc_host representing this dw_mci. + * @flags: Random state bits associated with the host. + * @ctype: Card type for this host. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @clk_old: The last clock value that was requested from core. + * @pdev: platform_device registered + * @rstc: Reset controller for this host. + * @detect_delay_ms: Delay in mS before detecting cards after interrupt. + * @phase_map: The map for recording in and out phases for each timing * * Locking * ======= * - * @lock is a softirq-safe spinlock protecting @queue as well as - * @slot, @mrq and @state. These must always be updated + * @lock is a softirq-safe spinlock protecting as well as + * @mrq and @state. These must always be updated * at the same time while holding @lock. - * The @mrq field of struct dw_mci_slot is also protected by @lock, - * and must always be written at the same time as the slot is added to - * @queue. * * @irq_lock is an irq-safe spinlock protecting the INTMASK register * to allow the interrupt handler to modify it directly. Held for only long @@ -199,7 +199,6 @@ struct dw_mci { unsigned long pending_events; unsigned long completed_events; enum dw_mci_state state; - struct list_head queue; u32 bus_hz; u32 current_speed; @@ -207,7 +206,6 @@ struct dw_mci { u32 fifoth_val; u16 verid; struct device *dev; - struct dw_mci_board *pdata; const struct dw_mci_drv_data *drv_data; void *priv; struct clk *biu_clk; @@ -228,11 +226,10 @@ struct dw_mci { void (*pull_data)(struct dw_mci *host, void *buf, int cnt); u32 quirks; - bool vqmmc_enabled; unsigned long irq_flags; /* IRQ flags */ int irq; - int sdio_id0; + int sdio_irq; struct timer_list cmd11_timer; struct timer_list cto_timer; @@ -242,6 +239,19 @@ struct dw_mci { struct fault_attr fail_data_crc; struct hrtimer fault_timer; #endif + struct mmc_host *mmc; + unsigned long flags; +#define DW_MMC_CARD_NEED_INIT 0 +#define DW_MMC_CARD_NO_LOW_PWR 1 +#define DW_MMC_CARD_NO_USE_HOLD 2 +#define DW_MMC_CARD_NEEDS_POLL 3 + u32 ctype; + unsigned int clock; + unsigned int clk_old; + struct platform_device *pdev; + struct reset_control *rstc; + u32 detect_delay_ms; + struct mmc_clk_phase_map phase_map; }; /* DMA ops for Internal/External DMAC interface */ @@ -255,30 +265,6 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); }; -struct dma_pdata; - -/* Board platform data */ -struct dw_mci_board { - unsigned int bus_hz; /* Clock speed at the cclk_in pad */ - - u32 caps; /* Capabilities */ - u32 caps2; /* More capabilities */ - u32 pm_caps; /* PM capabilities */ - /* - * Override fifo depth. If 0, autodetect it from the FIFOTH register, - * but note that this may not be reliable after a bootloader has used - * it. - */ - unsigned int fifo_depth; - - /* delay in mS before detecting cards after interrupt */ - u32 detect_delay_ms; - - struct reset_control *rstc; - struct dw_mci_dma_ops *dma_ops; - struct dma_pdata *data; -}; - /* Support for longer data read timeout */ #define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0) /* Force 32-bit access to the FIFO */ @@ -396,7 +382,6 @@ struct dw_mci_board { #define SDMMC_INT_CMD_DONE BIT(2) #define SDMMC_INT_RESP_ERR BIT(1) #define SDMMC_INT_CD BIT(0) -#define SDMMC_INT_ERROR 0xbfc2 /* Command register defines */ #define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_USE_HOLD_REG BIT(29) @@ -505,84 +490,17 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) #define mci_writel(dev, reg, value) \ writel_relaxed((value), (dev)->regs + SDMMC_##reg) -/* 16-bit FIFO access macros */ -#define mci_readw(dev, reg) \ - readw_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writew(dev, reg, value) \ - writew_relaxed((value), (dev)->regs + SDMMC_##reg) - -/* 64-bit FIFO access macros */ -#ifdef readq -#define mci_readq(dev, reg) \ - readq_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writeq(dev, reg, value) \ - writeq_relaxed((value), (dev)->regs + SDMMC_##reg) -#else -/* - * Dummy readq implementation for architectures that don't define it. - * - * We would assume that none of these architectures would configure - * the IP block with a 64bit FIFO width, so this code will never be - * executed on those machines. Defining these macros here keeps the - * rest of the code free from ifdefs. - */ -#define mci_readq(dev, reg) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)) -#define mci_writeq(dev, reg, value) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) - +#ifndef readq #define __raw_writeq(__value, __reg) \ (*(volatile u64 __force *)(__reg) = (__value)) #define __raw_readq(__reg) (*(volatile u64 __force *)(__reg)) #endif +extern struct dw_mci *dw_mci_alloc_host(struct device *device); extern int dw_mci_probe(struct dw_mci *host); extern void dw_mci_remove(struct dw_mci *host); -#ifdef CONFIG_PM extern int dw_mci_runtime_suspend(struct device *device); extern int dw_mci_runtime_resume(struct device *device); -#else -static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; } -static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; } -#endif - -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @ctype: Card type for this slot. - * @mrq: mmc_request currently being processed or waiting to be - * processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of - * &struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @__clk_old: The last clock value that was requested from core. - * Keeping track of this helps us to avoid spamming the console. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @sdio_id: Number of this slot in the SDIO interrupt registers. - */ -struct dw_mci_slot { - struct mmc_host *mmc; - struct dw_mci *host; - - u32 ctype; - - struct mmc_request *mrq; - struct list_head queue_node; - - unsigned int clock; - unsigned int __clk_old; - - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 -#define DW_MMC_CARD_NO_LOW_PWR 2 -#define DW_MMC_CARD_NO_USE_HOLD 3 -#define DW_MMC_CARD_NEEDS_POLL 4 - int id; - int sdio_id; -}; /** * dw_mci driver data - dw-mshc implementation specific driver data. @@ -609,10 +527,10 @@ struct dw_mci_drv_data { int (*init)(struct dw_mci *host); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode); + int (*execute_tuning)(struct dw_mci *host, u32 opcode); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); - int (*switch_voltage)(struct mmc_host *mmc, + int (*switch_voltage)(struct dw_mci *host, struct mmc_ios *ios); void (*set_data_timeout)(struct dw_mci *host, unsigned int timeout_ns); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 6a0d0250d47b..6a3c26b7c82d 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1052,7 +1052,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); /* Default if no match is JZ4740 */ - host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev); + host->version = (unsigned long)device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); if (ret) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index da3daab5f3d6..f553e92fd9e5 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -189,6 +189,12 @@ #define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000 #define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500 +/* + * Due to a hardware design flaw, the Loongson-2K0300 may fail to recognize the + * CMD48 (SD_READ_EXTR_SINGLE) interrupt. + */ +#define LOONGSON2_MMC_CMD48_QUIRK BIT(0) + /* Loongson-2K1000 SDIO2 DMA routing register */ #define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15) #define LS2K1000_DMA0_CONF 0x0 @@ -245,6 +251,7 @@ struct loongson2_mmc_host { }; struct loongson2_mmc_pdata { + u32 flags; const struct regmap_config *regmap_config; void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd); void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd); @@ -568,6 +575,12 @@ static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct loongson2_mmc_host *host = mmc_priv(mmc); + if ((host->pdata->flags & LOONGSON2_MMC_CMD48_QUIRK) && + mrq->cmd->opcode == SD_READ_EXTR_SINGLE) { + mmc_request_done(mmc, mrq); + return; + } + host->cmd_is_stop = 0; host->mrq = mrq; loongson2_mmc_send_request(mmc); @@ -703,14 +716,6 @@ static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host, return 0; } -static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { - .regmap_config = &ls2k0500_mmc_regmap_config, - .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, - .setting_dma = ls2k0500_mmc_set_external_dma, - .prepare_dma = loongson2_mmc_prepare_external_dma, - .release_dma = loongson2_mmc_release_external_dma, -}; - static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, struct platform_device *pdev) { @@ -735,14 +740,6 @@ static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, return 0; } -static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { - .regmap_config = &ls2k0500_mmc_regmap_config, - .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, - .setting_dma = ls2k1000_mmc_set_external_dma, - .prepare_dma = loongson2_mmc_prepare_external_dma, - .release_dma = loongson2_mmc_release_external_dma, -}; - static const struct regmap_config ls2k2000_mmc_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -846,7 +843,6 @@ static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host, if (!host->sg_cpu) return -ENOMEM; - memset(host->sg_cpu, 0, PAGE_SIZE); return 0; } @@ -856,7 +852,36 @@ static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host, dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); } +static struct loongson2_mmc_pdata ls2k0300_mmc_pdata = { + .flags = LOONGSON2_MMC_CMD48_QUIRK, + .regmap_config = &ls2k2000_mmc_regmap_config, + .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, + .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, + .setting_dma = ls2k2000_mmc_set_internal_dma, + .prepare_dma = loongson2_mmc_prepare_internal_dma, + .release_dma = loongson2_mmc_release_internal_dma, +}; + +static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { + .flags = 0, + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k0500_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { + .flags = 0, + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k1000_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = { + .flags = 0, .regmap_config = &ls2k2000_mmc_regmap_config, .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, @@ -985,6 +1010,7 @@ static void loongson2_mmc_remove(struct platform_device *pdev) } static const struct of_device_id loongson2_mmc_of_ids[] = { + { .compatible = "loongson,ls2k0300-mmc", .data = &ls2k0300_mmc_pdata }, { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata }, { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata }, { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata }, diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index daed659f63f6..b2680cc054bd 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -203,9 +203,10 @@ #define SDC_CFG_DTOC GENMASK(31, 24) /* RW */ /* SDC_STS mask */ -#define SDC_STS_SDCBUSY BIT(0) /* RW */ -#define SDC_STS_CMDBUSY BIT(1) /* RW */ -#define SDC_STS_SWR_COMPL BIT(31) /* RW */ +#define SDC_STS_SDCBUSY BIT(0) /* RW */ +#define SDC_STS_CMDBUSY BIT(1) /* RW */ +#define SDC_STS_SPM_RESOURCE_RELEASE BIT(3) /* RW */ +#define SDC_STS_SWR_COMPL BIT(31) /* RW */ /* SDC_ADV_CFG0 mask */ #define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */ @@ -448,6 +449,7 @@ struct mtk_mmc_compatible { bool use_internal_cd; bool support_new_tx; bool support_new_rx; + bool support_spm_res_release; }; struct msdc_tune_para { @@ -673,6 +675,25 @@ static const struct mtk_mmc_compatible mt8516_compat = { .stop_dly_sel = 3, }; +static const struct mtk_mmc_compatible mt8189_compat = { + .clk_div_bits = 12, + .recheck_sdio_irq = false, + .hs400_tune = false, + .needs_top_base = true, + .pad_tune_reg = MSDC_PAD_TUNE0, + .async_fifo = true, + .data_tune = false, + .busy_check = true, + .stop_clk_fix = true, + .stop_dly_sel = 3, + .pop_en_cnt = 8, + .enhance_rx = true, + .support_64g = true, + .support_new_tx = false, + .support_new_rx = false, + .support_spm_res_release = true, +}; + static const struct mtk_mmc_compatible mt8196_compat = { .clk_div_bits = 12, .recheck_sdio_irq = false, @@ -703,6 +724,7 @@ static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat}, { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat}, { .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat}, + { .compatible = "mediatek,mt8189-mmc", .data = &mt8189_compat}, { .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat}, { .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat}, @@ -3296,6 +3318,10 @@ static int msdc_runtime_suspend(struct device *dev) __msdc_enable_sdio_irq(host, 0); } + + if (host->dev_comp->support_spm_res_release) + sdr_set_bits(host->base + SDC_STS, SDC_STS_SPM_RESOURCE_RELEASE); + msdc_gate_clock(host); return 0; } diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 2a310a145785..f9ec78d699f4 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -26,6 +26,7 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/slot-gpio.h> #include <linux/module.h> +#include <linux/mux/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl-state.h> #include <linux/platform_data/tmio.h> @@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, struct regulator_dev *rdev; struct renesas_sdhi_dma *dma_priv; struct device *dev = &pdev->dev; + struct mux_state *mux_state; struct tmio_mmc_host *host; struct renesas_sdhi *priv; int num_irqs, irq, ret, i; @@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, "state_uhs"); } + mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(mux_state)) + return PTR_ERR(mux_state); + host = tmio_mmc_host_alloc(pdev, mmc_data); if (IS_ERR(host)) return PTR_ERR(host); diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 543ad1d0ed1c..9215600f03a2 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -456,19 +456,15 @@ static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) of_device_get_match_data(&pdev->dev), NULL); } -static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, - tmio_mmc_host_runtime_resume, - NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(renesas_sdhi_sys_dmac_dev_pm_ops, + tmio_mmc_host_runtime_suspend, + tmio_mmc_host_runtime_resume, NULL); static struct platform_driver renesas_sys_dmac_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, + .pm = pm_ptr(&renesas_sdhi_sys_dmac_dev_pm_ops), .of_match_table = renesas_sdhi_sys_dmac_of_match, }, .probe = renesas_sdhi_sys_dmac_probe, diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 4db3328f46df..8dfbc62f165b 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1181,79 +1181,6 @@ static int sdmmc_get_cd(struct mmc_host *mmc) return cd; } -static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat; - - /* Reference to Signal Voltage Switch Sequence in SD spec. - * Wait for a period of time so that the card can drive SD_CMD and - * SD_DAT[3:0] to low after sending back CMD11 response. - */ - mdelay(1); - - /* SD_CMD, SD_DAT[3:0] should be driven to low by card; - * If either one of SD_CMD,SD_DAT[3:0] is not low, - * abort the voltage switch sequence; - */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) - return -EINVAL; - - /* Stop toggle SD clock */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - 0xFF, SD_CLK_FORCE_STOP); - if (err < 0) - return err; - - return 0; -} - -static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat, mask, val; - - /* Wait 1.8V output of voltage regulator in card stable */ - msleep(50); - - /* Toggle SD clock again */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN); - if (err < 0) - return err; - - /* Wait for a period of time so that the card can drive - * SD_DAT[3:0] to high at 1.8V - */ - msleep(20); - - /* SD_CMD, SD_DAT[3:0] should be pulled high by host */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - if ((stat & mask) != val) { - dev_dbg(sdmmc_dev(host), - "%s: SD_BUS_STAT = 0x%x\n", __func__, stat); - rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); - rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0); - return -EINVAL; - } - - return 0; -} - static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -1281,7 +1208,9 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) voltage = OUTPUT_1V8; if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_1(host); + /* Stop toggle SD clock */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_FORCE_STOP); if (err < 0) goto out; } @@ -1290,16 +1219,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (err < 0) goto out; - if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_2(host); - if (err < 0) - goto out; - } - out: /* Stop toggle SD clock in idle */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + if (err < 0) + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); mutex_unlock(&pcr->pcr_mutex); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a7a5df673b0f..18ecddd6df6f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -216,6 +216,8 @@ #define ESDHC_FLAG_DUMMY_PAD BIT(19) #define ESDHC_AUTO_TUNING_WINDOW 3 +/* 100ms timeout for data inhibit */ +#define ESDHC_DATA_INHIBIT_WAIT_US 100000 enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ @@ -321,6 +323,14 @@ static struct esdhc_soc_data usdhc_s32g2_data = { .quirks = SDHCI_QUIRK_NO_LED, }; +static struct esdhc_soc_data usdhc_s32n79_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_SKIP_ERR004536, + .quirks = SDHCI_QUIRK_NO_LED, +}; + static struct esdhc_soc_data usdhc_imx7ulp_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 @@ -408,6 +418,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, }, { .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, }, { .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, }, + { .compatible = "nxp,s32n79-usdhc", .data = &usdhc_s32n79_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -1453,6 +1464,22 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) static void esdhc_reset(struct sdhci_host *host, u8 mask) { + u32 present_state; + int ret; + + /* + * For data or full reset, ensure any active data transfer completes + * before resetting to avoid system hang. + */ + if (mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) { + ret = readl_poll_timeout_atomic(host->ioaddr + ESDHC_PRSSTAT, present_state, + !(present_state & SDHCI_DATA_INHIBIT), 2, + ESDHC_DATA_INHIBIT_WAIT_US); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "timeout waiting for data transfer completion\n"); + } + sdhci_and_cqhci_reset(host, mask); sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); @@ -1795,8 +1822,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "fsl,strobe-dll-delay-target", &boarddata->strobe_dll_delay_target); - if (of_property_read_bool(np, "no-1-8-v")) - host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; @@ -1815,9 +1840,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (ret) return ret; - /* HS400/HS400ES require 8 bit bus */ - if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA)) - host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + sdhci_get_property(pdev); if (mmc_gpio_get_cd(host->mmc) >= 0) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3b85233131b3..633462c0be5f 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -157,6 +157,17 @@ #define CQHCI_VENDOR_CFG1 0xA00 #define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) +/* non command queue crypto enable register*/ +#define NONCQ_CRYPTO_PARM 0x70 +#define NONCQ_CRYPTO_DUN 0x74 + +#define DISABLE_CRYPTO BIT(15) +#define CRYPTO_GENERAL_ENABLE BIT(1) +#define HC_VENDOR_SPECIFIC_FUNC4 0x260 + +#define ICE_HCI_PARAM_CCI GENMASK(7, 0) +#define ICE_HCI_PARAM_CE GENMASK(8, 8) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -300,6 +311,7 @@ struct sdhci_msm_host { u32 dll_config; u32 ddr_config; bool vqmmc_enabled; + bool non_cqe_ice_init_done; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -1914,11 +1926,6 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, if (IS_ERR_OR_NULL(ice)) return PTR_ERR_OR_ZERO(ice); - if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) { - dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n"); - return 0; - } - msm_host->ice = ice; /* Initialize the blk_crypto_profile */ @@ -1932,7 +1939,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, profile->ll_ops = sdhci_msm_crypto_ops; profile->max_dun_bytes_supported = 4; - profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW; + profile->key_types_supported = qcom_ice_get_supported_key_type(ice); profile->dev = dev; /* @@ -2012,9 +2019,111 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile, return qcom_ice_evict_key(msm_host->ice, slot); } +static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile, + const u8 *eph_key, size_t eph_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_derive_sw_secret(msm_host->ice, eph_key, eph_key_size, + sw_secret); +} + +static int sdhci_msm_ice_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_import_key(msm_host->ice, raw_key, raw_key_size, lt_key); +} + +static int sdhci_msm_ice_generate_key(struct blk_crypto_profile *profile, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_generate_key(msm_host->ice, lt_key); +} + +static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile, + const u8 *lt_key, size_t lt_key_size, + u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key); +} + +static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + u32 config; + + config = sdhci_readl(host, HC_VENDOR_SPECIFIC_FUNC4); + config &= ~DISABLE_CRYPTO; + sdhci_writel(host, config, HC_VENDOR_SPECIFIC_FUNC4); + config = cqhci_readl(cq_host, CQHCI_CFG); + config |= CRYPTO_GENERAL_ENABLE; + cqhci_writel(cq_host, config, CQHCI_CFG); +} + +static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + unsigned int crypto_params = 0; + int key_index; + + if (mrq->crypto_ctx) { + if (!msm_host->non_cqe_ice_init_done) { + sdhci_msm_non_cqe_ice_init(host); + msm_host->non_cqe_ice_init_done = true; + } + + key_index = mrq->crypto_key_slot; + crypto_params = FIELD_PREP(ICE_HCI_PARAM_CE, 1) | + FIELD_PREP(ICE_HCI_PARAM_CCI, key_index); + + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + cqhci_writel(cq_host, lower_32_bits(mrq->crypto_ctx->bc_dun[0]), + NONCQ_CRYPTO_DUN); + } else { + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + } + + /* Ensure crypto configuration is written before proceeding */ + wmb(); +} + +/* + * Handle non-CQE MMC requests with ICE crypto support. + * Configures ICE registers before passing the request to + * the standard SDHCI handler. + */ +static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct sdhci_host *host = mmc_priv(mmc); + + /* Only need to handle non-CQE crypto requests in this path */ + if (mmc->caps2 & MMC_CAP2_CRYPTO) + sdhci_msm_ice_cfg(host, mrq); + + sdhci_request(mmc, mrq); +} + static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = { .keyslot_program = sdhci_msm_ice_keyslot_program, .keyslot_evict = sdhci_msm_ice_keyslot_evict, + .derive_sw_secret = sdhci_msm_ice_derive_sw_secret, + .import_key = sdhci_msm_ice_import_key, + .generate_key = sdhci_msm_ice_generate_key, + .prepare_key = sdhci_msm_ice_prepare_key, }; #else /* CONFIG_MMC_CRYPTO */ @@ -2762,6 +2871,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; +#ifdef CONFIG_MMC_CRYPTO + host->mmc_host_ops.request = sdhci_msm_request; +#endif /* Set the timeout value to max possible */ host->max_timeout_count = 0xF; diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index ab7f0ffe7b4f..785d3acb18c5 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -152,8 +152,7 @@ struct sdhci_arasan_clk_ops { * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @sampleclk_hw: Struct for the clock we might provide to a PHY. * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw. - * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes - * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes + * @phase_map: Struct for mmc_clk_phase_map provided. * @set_clk_delays: Function pointer for setting Clock Delays * @clk_of_data: Platform specific runtime clock data storage pointer */ @@ -162,8 +161,7 @@ struct sdhci_arasan_clk_data { struct clk *sdcardclk; struct clk_hw sampleclk_hw; struct clk *sampleclk; - int clk_phase_in[MMC_TIMING_MMC_HS400 + 1]; - int clk_phase_out[MMC_TIMING_MMC_HS400 + 1]; + struct mmc_clk_phase_map phase_map; void (*set_clk_delays)(struct sdhci_host *host); void *clk_of_data; }; @@ -1249,36 +1247,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci_host *host) struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; clk_set_phase(clk_data->sampleclk, - clk_data->clk_phase_in[host->timing]); + clk_data->phase_map.phase[host->timing].in_deg); clk_set_phase(clk_data->sdcardclk, - clk_data->clk_phase_out[host->timing]); -} - -static void arasan_dt_read_clk_phase(struct device *dev, - struct sdhci_arasan_clk_data *clk_data, - unsigned int timing, const char *prop) -{ - struct device_node *np = dev->of_node; - - u32 clk_phase[2] = {0}; - int ret; - - /* - * Read Tap Delay values from DT, if the DT does not contain the - * Tap Values then use the pre-defined values. - */ - ret = of_property_read_variable_u32_array(np, prop, &clk_phase[0], - 2, 0); - if (ret < 0) { - dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n", - prop, clk_data->clk_phase_in[timing], - clk_data->clk_phase_out[timing]); - return; - } - - /* The values read are Input and Output Clock Delays in order */ - clk_data->clk_phase_in[timing] = clk_phase[0]; - clk_data->clk_phase_out[timing] = clk_phase[1]; + clk_data->phase_map.phase[host->timing].out_deg); } /** @@ -1315,8 +1286,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, } for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i]; - clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = zynqmp_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = zynqmp_oclk_phase[i]; } } @@ -1327,8 +1298,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_oclk_phase[i]; } } if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) { @@ -1338,32 +1309,12 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_NET_EMMC_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_net_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_net_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_net_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_net_oclk_phase[i]; } } - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY, - "clk-phase-legacy"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS, - "clk-phase-mmc-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS, - "clk-phase-sd-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12, - "clk-phase-uhs-sdr12"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25, - "clk-phase-uhs-sdr25"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50, - "clk-phase-uhs-sdr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104, - "clk-phase-uhs-sdr104"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50, - "clk-phase-uhs-ddr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52, - "clk-phase-mmc-ddr52"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200, - "clk-phase-mmc-hs200"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400, - "clk-phase-mmc-hs400"); + + mmc_of_parse_clk_phase(dev, &clk_data->phase_map); } static const struct sdhci_pltfm_data sdhci_arasan_pdata = { @@ -1512,6 +1463,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = { .clk_ops = &arasan_clk_ops, }; +static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CQE, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = { + .pdata = &sdhci_arasan_axiado_pdata, + .clk_ops = &arasan_clk_ops, +}; + static const struct of_device_id sdhci_arasan_of_match[] = { /* SoC-specific compatible strings w/ soc_ctl_map */ { @@ -1538,6 +1500,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, + { + .compatible = "axiado,ax3000-sdhci-5.1-emmc", + .data = &sdhci_arasan_axiado_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index ca97b01996b1..f5d973783cbe 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/reset.h> #include <linux/spinlock.h> #include "sdhci-pltfm.h" @@ -519,7 +520,8 @@ static struct platform_driver aspeed_sdhci_driver = { static int aspeed_sdc_probe(struct platform_device *pdev) { - struct device_node *parent, *child; + struct reset_control *reset; + struct device_node *parent; struct aspeed_sdc *sdc; int ret; @@ -529,6 +531,10 @@ static int aspeed_sdc_probe(struct platform_device *pdev) spin_lock_init(&sdc->lock); + reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(reset), "unable to acquire reset\n"); + sdc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sdc->clk)) return PTR_ERR(sdc->clk); @@ -548,12 +554,11 @@ static int aspeed_sdc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, sdc); parent = pdev->dev.of_node; - for_each_available_child_of_node(parent, child) { + for_each_available_child_of_node_scoped(parent, child) { struct platform_device *cpdev; cpdev = of_platform_device_create(child, NULL, &pdev->dev); if (!cpdev) { - of_node_put(child); ret = -ENODEV; goto err_clk; } diff --git a/drivers/mmc/host/sdhci-of-bst.c b/drivers/mmc/host/sdhci-of-bst.c new file mode 100644 index 000000000000..f8d3df715e1a --- /dev/null +++ b/drivers/mmc/host/sdhci-of-bst.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SDHCI driver for Black Sesame Technologies C1200 controller + * + * Copyright (c) 2025 Black Sesame Technologies + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include "sdhci.h" +#include "sdhci-pltfm.h" + +/* SDHCI register extensions */ +#define SDHCI_CLOCK_PLL_EN 0x0008 +#define SDHCI_VENDOR_PTR_R 0xE8 + +/* BST-specific tuning parameters */ +#define BST_TUNING_COUNT 0x20 + +/* Synopsys vendor specific registers */ +#define SDHC_EMMC_CTRL_R_OFFSET 0x2C +#define MBIU_CTRL 0x510 + +/* MBIU burst control bits */ +#define BURST_INCR16_EN BIT(3) +#define BURST_INCR8_EN BIT(2) +#define BURST_INCR4_EN BIT(1) +#define BURST_EN (BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN) +#define MBIU_BURST_MASK GENMASK(3, 0) + +/* CRM (Clock/Reset/Management) register offsets */ +#define SDEMMC_CRM_BCLK_DIV_CTRL 0x08 +#define SDEMMC_CRM_TIMER_DIV_CTRL 0x0C +#define SDEMMC_CRM_RX_CLK_CTRL 0x14 +#define SDEMMC_CRM_VOL_CTRL 0x1C +#define REG_WR_PROTECT 0x88 +#define DELAY_CHAIN_SEL 0x94 + +/* CRM register values and bit definitions */ +#define REG_WR_PROTECT_KEY 0x1234abcd +#define BST_VOL_STABLE_ON BIT(7) +#define BST_TIMER_DIV_MASK GENMASK(7, 0) +#define BST_TIMER_DIV_VAL 0x20 +#define BST_TIMER_LOAD_BIT BIT(8) +#define BST_BCLK_EN_BIT BIT(10) +#define BST_RX_UPDATE_BIT BIT(11) +#define BST_EMMC_CTRL_RST_N BIT(2) /* eMMC card reset control */ + +/* Clock frequency limits */ +#define BST_DEFAULT_MAX_FREQ 200000000UL /* 200 MHz */ +#define BST_DEFAULT_MIN_FREQ 400000UL /* 400 kHz */ + +/* Clock control bit definitions */ +#define BST_CLOCK_DIV_MASK GENMASK(7, 0) +#define BST_CLOCK_DIV_SHIFT 8 +#define BST_BCLK_DIV_MASK GENMASK(9, 0) + +/* Clock frequency thresholds */ +#define BST_CLOCK_THRESHOLD_LOW 1500 + +/* Clock stability polling parameters */ +#define BST_CLK_STABLE_POLL_US 1000 /* Poll interval in microseconds */ +#define BST_CLK_STABLE_TIMEOUT_US 20000 /* Timeout for internal clock stabilization (us) */ + +struct sdhci_bst_priv { + void __iomem *crm_reg_base; +}; + +union sdhci_bst_rx_ctrl { + struct { + u32 rx_revert:1, + rx_clk_sel_sec:1, + rx_clk_div:4, + rx_clk_phase_inner:2, + rx_clk_sel_first:1, + rx_clk_phase_out:2, + rx_clk_en:1, + res0:20; + }; + u32 reg; +}; + +static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + return readl(priv->crm_reg_base + offset); +} + +static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + writel(value, priv->crm_reg_base + offset); +} + +static int sdhci_bst_wait_int_clk(struct sdhci_host *host) +{ + u16 clk; + + if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE), + BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false, + host, SDHCI_CLOCK_CONTROL)) + return -EBUSY; + return 0; +} + +static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MAX_FREQ; +} + +static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MIN_FREQ; +} + +static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk) +{ + struct sdhci_pltfm_host *pltfm_host; + unsigned int div; + u32 val; + union sdhci_bst_rx_ctrl rx_reg; + + pltfm_host = sdhci_priv(host); + + /* Calculate clock divider based on target frequency */ + if (clk == 0) { + div = 0; + } else if (clk < BST_DEFAULT_MIN_FREQ) { + /* Below minimum: use max divider to get closest to min freq */ + div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ; + } else if (clk <= BST_DEFAULT_MAX_FREQ) { + /* Normal range: calculate divider directly */ + div = BST_DEFAULT_MAX_FREQ / clk; + } else { + /* Above maximum: no division needed */ + div = 1; + } + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk &= ~SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_DIV_MASK; + val |= BST_TIMER_DIV_VAL; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val |= BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + rx_reg.reg = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + + rx_reg.rx_revert = 0; + rx_reg.rx_clk_sel_sec = 1; + rx_reg.rx_clk_div = 4; + rx_reg.rx_clk_phase_inner = 2; + rx_reg.rx_clk_sel_first = 0; + rx_reg.rx_clk_phase_out = 2; + + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val |= BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Disable clock first */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Setup clock divider */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_DIV_MASK; + val |= div; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Enable clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val |= BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* RMW the clock divider bits to avoid clobbering other fields */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~(BST_CLOCK_DIV_MASK << BST_CLOCK_DIV_SHIFT); + clk |= (div & BST_CLOCK_DIV_MASK) << BST_CLOCK_DIV_SHIFT; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_bst_set_clock(struct sdhci_host *host, unsigned int clock) +{ + /* Turn off card/internal/PLL clocks when clock==0 to avoid idle power */ + u32 clk_reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + if (!clock) { + clk_reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN); + sdhci_writew(host, clk_reg, SDHCI_CLOCK_CONTROL); + return; + } + sdhci_bst_enable_clk(host, clock); +} + +/* + * sdhci_bst_reset - Reset the SDHCI host controller with special + * handling for eMMC card reset control. + */ +static void sdhci_bst_reset(struct sdhci_host *host, u8 mask) +{ + u16 vendor_ptr, emmc_ctrl_reg; + u32 reg; + + if (host->mmc->caps2 & MMC_CAP2_NO_SD) { + vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R); + emmc_ctrl_reg = vendor_ptr + SDHC_EMMC_CTRL_R_OFFSET; + + reg = sdhci_readw(host, emmc_ctrl_reg); + reg &= ~BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + sdhci_reset(host, mask); + usleep_range(10, 20); + reg = sdhci_readw(host, emmc_ctrl_reg); + reg |= BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + } else { + sdhci_reset(host, mask); + } +} + +/* Set timeout control register to maximum value (0xE) */ +static void sdhci_bst_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) +{ + sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL); +} + +/* + * sdhci_bst_set_power - Set power mode and voltage, also configures + * MBIU burst mode control based on power state. + */ +static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + u32 reg; + u32 val; + + sdhci_set_power(host, mode, vdd); + + if (mode == MMC_POWER_OFF) { + /* Disable MBIU burst mode */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~BURST_EN; /* Clear all burst enable bits */ + sdhci_writew(host, reg, MBIU_CTRL); + + /* Disable CRM BCLK */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Disable RX clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Turn off voltage stable power */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_VOL_CTRL); + val &= ~BST_VOL_STABLE_ON; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, val); + } else { + /* Configure burst mode only when powered on */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~MBIU_BURST_MASK; /* Clear burst related bits */ + reg |= BURST_EN; /* Enable burst mode for better bandwidth */ + sdhci_writew(host, reg, MBIU_CTRL); + } +} + +/* + * sdhci_bst_execute_tuning - Execute tuning procedure by trying different + * delay chain values and selecting the optimal one. + */ +static int sdhci_bst_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host; + int ret = 0, error; + int first_start = -1, first_end = -1, best = 0; + int second_start = -1, second_end = -1, has_failure = 0; + int i; + + pltfm_host = sdhci_priv(host); + + for (i = 0; i < BST_TUNING_COUNT; i++) { + /* Protected write */ + sdhci_bst_crm_write(pltfm_host, REG_WR_PROTECT, REG_WR_PROTECT_KEY); + /* Write tuning value */ + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << i) - 1); + + /* Wait for internal clock stable before tuning */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + ret = mmc_send_tuning(host->mmc, opcode, &error); + if (ret != 0) { + has_failure = 1; + } else { + if (has_failure == 0) { + if (first_start == -1) + first_start = i; + first_end = i; + } else { + if (second_start == -1) + second_start = i; + second_end = i; + } + } + } + + /* Calculate best tuning value */ + if (first_end - first_start >= second_end - second_start) + best = ((first_end - first_start) >> 1) + first_start; + else + best = ((second_end - second_start) >> 1) + second_start; + + if (best < 0) + best = 0; + + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << best) - 1); + /* Confirm internal clock stable after setting best tuning value */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + return 0; +} + +/* Enable voltage stable power for voltage switch */ +static void sdhci_bst_voltage_switch(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + /* Enable voltage stable power */ + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, BST_VOL_STABLE_ON); +} + +static const struct sdhci_ops sdhci_bst_ops = { + .set_clock = sdhci_bst_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_min_clock = sdhci_bst_get_min_clock, + .get_max_clock = sdhci_bst_get_max_clock, + .reset = sdhci_bst_reset, + .set_power = sdhci_bst_set_power, + .set_timeout = sdhci_bst_set_timeout, + .platform_execute_tuning = sdhci_bst_execute_tuning, + .voltage_switch = sdhci_bst_voltage_switch, +}; + +static const struct sdhci_pltfm_data sdhci_bst_pdata = { + .ops = &sdhci_bst_ops, + .quirks = SDHCI_QUIRK_BROKEN_ADMA | + SDHCI_QUIRK_DELAY_AFTER_POWER | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT, + .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 | + SDHCI_QUIRK2_TUNING_WORK_AROUND | + SDHCI_QUIRK2_ACMD23_BROKEN, +}; + +static void sdhci_bst_free_bounce_buffer(struct sdhci_host *host) +{ + if (host->bounce_buffer) { + dma_free_coherent(mmc_dev(host->mmc), host->bounce_buffer_size, + host->bounce_buffer, host->bounce_addr); + host->bounce_buffer = NULL; + } + of_reserved_mem_device_release(mmc_dev(host->mmc)); +} + +static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + unsigned int bounce_size; + int ret; + + /* Fixed SRAM bounce size to 32KB: verified config under 32-bit DMA addressing limit */ + bounce_size = SZ_32K; + + ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0); + if (ret) { + dev_err(mmc_dev(mmc), "Failed to initialize reserved memory\n"); + return ret; + } + + host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size, + &host->bounce_addr, GFP_KERNEL); + if (!host->bounce_buffer) { + of_reserved_mem_device_release(mmc_dev(mmc)); + return -ENOMEM; + } + + host->bounce_buffer_size = bounce_size; + + return 0; +} + +static int sdhci_bst_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct sdhci_bst_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_bst_pdata, sizeof(struct sdhci_bst_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); /* Get platform private data */ + + err = mmc_of_parse(host->mmc); + if (err) + return err; + + sdhci_get_of_property(pdev); + + /* Get CRM registers from the second reg entry */ + priv->crm_reg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->crm_reg_base)) { + err = PTR_ERR(priv->crm_reg_base); + return err; + } + + /* + * Silicon constraints for BST C1200: + * - System RAM base is 0x800000000 (above 32-bit addressable range) + * - The eMMC controller DMA engine is limited to 32-bit addressing + * - SMMU cannot be used on this path due to hardware design flaws + * - These are fixed in silicon and cannot be changed in software + * + * Bus/controller mapping: + * - No registers are available to reprogram the address mapping + * - The 32-bit DMA limit is a hard constraint of the controller IP + * + * Given these constraints, an SRAM-based bounce buffer in the 32-bit + * address space is required to enable eMMC DMA on this platform. + */ + err = sdhci_bst_alloc_bounce_buffer(host); + if (err) { + dev_err(&pdev->dev, "Failed to allocate bounce buffer: %d\n", err); + return err; + } + + err = sdhci_add_host(host); + if (err) + goto err_free_bounce_buffer; + + return 0; + +err_free_bounce_buffer: + sdhci_bst_free_bounce_buffer(host); + + return err; +} + +static void sdhci_bst_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + + sdhci_bst_free_bounce_buffer(host); + sdhci_pltfm_remove(pdev); +} + +static const struct of_device_id sdhci_bst_ids[] = { + { .compatible = "bst,c1200-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_bst_ids); + +static struct platform_driver sdhci_bst_driver = { + .driver = { + .name = "sdhci-bst", + .of_match_table = sdhci_bst_ids, + }, + .probe = sdhci_bst_probe, + .remove = sdhci_bst_remove, +}; +module_platform_driver(sdhci_bst_driver); + +MODULE_DESCRIPTION("Black Sesame Technologies SDHCI driver (BST)"); +MODULE_AUTHOR("Black Sesame Technologies Co., Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 2b75a36c096b..0b2158a7e409 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -40,7 +40,10 @@ #define DWCMSHC_AREA1_MASK GENMASK(11, 0) /* Offset inside the vendor area 1 */ #define DWCMSHC_HOST_CTRL3 0x8 +#define DWCMSHC_HOST_CTRL3_CMD_CONFLICT BIT(0) #define DWCMSHC_EMMC_CONTROL 0x2c +/* HPE GSC SoC MSHCCS register */ +#define HPE_GSC_MSHCCS_SCGSYNCDIS BIT(18) #define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 @@ -128,9 +131,11 @@ #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_k230 0x09 /* PMOS TX drive strength for k230 */ #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_k230 0x08 /* NMOS TX drive strength for k230 */ #define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */ /* PHY command/response pad settings */ @@ -153,14 +158,21 @@ #define PHY_PAD_RXSEL_3V3 0x2 /* Receiver type select for 3.3V */ #define PHY_PAD_WEAKPULL_MASK GENMASK(4, 3) /* bits [4:3] */ +#define PHY_PAD_WEAKPULL_DISABLED 0x0 /* Weak pull up and pull down disabled */ #define PHY_PAD_WEAKPULL_PULLUP 0x1 /* Weak pull up enabled */ #define PHY_PAD_WEAKPULL_PULLDOWN 0x2 /* Weak pull down enabled */ #define PHY_PAD_TXSLEW_CTRL_P_MASK GENMASK(8, 5) /* bits [8:5] */ #define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */ +#define PHY_PAD_TXSLEW_CTRL_P_k230 0x2 /* Slew control for P-Type pad TX for k230 */ #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 */ +#define PHY_PAD_TXSLEW_CTRL_N_k230 0x2 /* Slew control for N-Type pad TX for k230 */ + +/* PHY Common DelayLine config settings */ +#define PHY_COMMDL_CNFG (DWC_MSHC_PTR_PHY_R + 0x1c) +#define PHY_COMMDL_CNFG_DLSTEP_SEL BIT(0) /* DelayLine outputs on PAD enabled */ /* PHY CLK delay line settings */ #define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d) @@ -174,7 +186,10 @@ #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_EXTDLY_EN BIT(0) #define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1) +#define PHY_SMPLDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */ +#define PHY_SMPLDL_CNFG_INPSEL 0x3 /* delay line input source */ /* PHY drift_cclk_rx delay line configuration setting */ #define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21) @@ -224,9 +239,20 @@ SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_DMA) +#define to_pltfm_data(priv, name) \ + container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata) + /* SMC call for BlueField-3 eMMC RST_N */ #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 +/* Canaan specific Registers */ +#define SD0_CTRL 0x00 +#define SD0_HOST_REG_VOL_STABLE BIT(4) +#define SD0_CARD_WRITE_PROT BIT(6) +#define SD1_CTRL 0x08 +#define SD1_HOST_REG_VOL_STABLE BIT(0) +#define SD1_CARD_WRITE_PROT BIT(2) + /* Eswin specific Registers */ #define EIC7700_CARD_CLK_STABLE BIT(28) #define EIC7700_INT_BCLK_STABLE BIT(16) @@ -252,14 +278,8 @@ #define PHY_DELAY_CODE_EMMC 0x17 #define PHY_DELAY_CODE_SD 0x55 -enum dwcmshc_rk_type { - DWCMSHC_RK3568, - DWCMSHC_RK3588, -}; - struct rk35xx_priv { struct reset_control *reset; - enum dwcmshc_rk_type devtype; u8 txclk_tapnum; }; @@ -268,6 +288,11 @@ struct eic7700_priv { unsigned int drive_impedance; }; +struct k230_priv { + /* Canaan k230 specific */ + struct regmap *hi_sys_regmap; +}; + #define DWCMSHC_MAX_OTHER_CLKS 3 struct dwcmshc_priv { @@ -278,6 +303,7 @@ struct dwcmshc_priv { int num_other_clks; struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS]; + const struct dwcmshc_pltfm_data *dwcmshc_pdata; void *priv; /* pointer to SoC private stuff */ u16 delay_line; u16 flags; @@ -290,6 +316,24 @@ struct dwcmshc_pltfm_data { void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); }; +struct k230_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + bool is_emmc; + u32 ctrl_reg; + u32 vol_stable_bit; + u32 write_prot_bit; +}; + +struct rockchip_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + /* + * The controller hardware has two known revisions documented internally: + * - Revision 0: Exclusively used by RK3566 and RK3568 SoCs. + * - Revision 1: Implemented in all other Rockchip SoCs, including RK3576, RK3588, etc. + */ + int revision; +}; + static void dwcmshc_enable_card_clk(struct sdhci_host *host) { u16 ctrl; @@ -709,6 +753,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + const struct rockchip_pltfm_data *rockchip_pdata = to_pltfm_data(dwc_priv, rockchip); u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; u32 extra, reg; int err; @@ -738,12 +783,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock extra |= BIT(4); sdhci_writel(host, extra, reg); + /* Disable clock while config DLL */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + if (clock <= 52000000) { if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { dev_err(mmc_dev(host->mmc), "Can't reduce the clock below 52MHz in HS200/HS400 mode"); - return; + goto enable_clk; } /* @@ -763,7 +811,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_DELAY_NUM_SEL | DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); - return; + goto enable_clk; } /* Reset DLL */ @@ -776,7 +824,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock * we must set it in higher speed mode. */ extra = DWCMSHC_EMMC_DLL_DLYENA; - if (priv->devtype == DWCMSHC_RK3568) + if (rockchip_pdata->revision == 0) extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); @@ -790,7 +838,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock 500 * USEC_PER_MSEC); if (err) { dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); - return; + goto enable_clk; } extra = 0x1 << 16 | /* tune clock stop en */ @@ -802,7 +850,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock host->mmc->ios.timing == MMC_TIMING_MMC_HS400) txclk_tapnum = priv->txclk_tapnum; - if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + if (rockchip_pdata->revision == 1 && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES; extra = DLL_CMDOUT_SRC_CLK_NEG | @@ -823,6 +871,16 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_TAPNUM_DEFAULT | DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); + +enable_clk: + /* + * The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional + * on Rockchip's SDHCI implementation. Instead, the clock frequency is fully + * controlled via external clk provider by calling clk_set_rate(). Consequently, + * passing 0 to sdhci_enable_clk() only re-enables the already-configured clock, + * which matches the hardware's actual behavior. + */ + sdhci_enable_clk(host, 0); } static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) @@ -858,11 +916,6 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, 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); @@ -1245,6 +1298,126 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host, ARRAY_SIZE(clk_ids), clk_ids); } +/* + * HPE GSC-specific vendor configuration: disable command conflict check + * and program Auto-Tuning Control register. + */ +static void dwcmshc_hpe_vendor_specific(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 atctrl; + u8 extra; + + extra = sdhci_readb(host, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + extra &= ~DWCMSHC_HOST_CTRL3_CMD_CONFLICT; + sdhci_writeb(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + + atctrl = AT_CTRL_AT_EN | AT_CTRL_SWIN_TH_EN | AT_CTRL_TUNE_CLK_STOP_EN | + FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, 3) | + FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY) | + FIELD_PREP(AT_CTRL_SWIN_TH_VAL_MASK, 2); + sdhci_writel(host, atctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); +} + +static void dwcmshc_hpe_set_emmc(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u16 ctrl; + + ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + ctrl |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); +} + +static void dwcmshc_hpe_reset(struct sdhci_host *host, u8 mask) +{ + dwcmshc_reset(host, mask); + dwcmshc_hpe_vendor_specific(host); + dwcmshc_hpe_set_emmc(host); +} + +static void dwcmshc_hpe_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + dwcmshc_set_uhs_signaling(host, timing); + dwcmshc_hpe_set_emmc(host); +} + +/* + * HPE GSC eMMC controller clock setup. + * + * The GSC SoC wires the freq_sel field of SDHCI_CLOCK_CONTROL directly to a + * clock mux rather than a divider. Force freq_sel = 1 when running at + * 200 MHz (HS200) so the mux selects the correct clock source. + */ +static void dwcmshc_hpe_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + + if (host->mmc->actual_clock == 200000000) + clk |= (1 << SDHCI_DIVIDER_SHIFT); + + sdhci_enable_clk(host, clk); +} + +/* + * HPE GSC eMMC controller init. + * + * The GSC SoC requires configuring MSHCCS. Bit 18 (SCGSyncDis) disables clock + * synchronisation for phase-select values going to the HS200 RX delay lines, + * allowing the card clock to be stopped while the delay selection settles and + * the phase shift is applied. This must be used together with the ATCTRL + * settings programmed in dwcmshc_hpe_vendor_specific(): + * AT_CTRL_R.TUNE_CLK_STOP_EN = 0x1 + * AT_CTRL_R.POST_CHANGE_DLY = 0x3 + * AT_CTRL_R.PRE_CHANGE_DLY = 0x3 + * + * The DTS node provides a syscon phandle ('hpe,gxp-sysreg') with the + * MSHCCS register offset as an argument. + */ +static int dwcmshc_hpe_gsc_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + unsigned int reg_offset; + struct regmap *soc_ctrl; + int ret; + + /* Disable cmd conflict check and configure auto-tuning */ + dwcmshc_hpe_vendor_specific(host); + + /* Look up the GXP sysreg syscon and MSHCCS offset */ + soc_ctrl = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "hpe,gxp-sysreg", + 1, ®_offset); + if (IS_ERR(soc_ctrl)) { + dev_err(dev, "failed to get hpe,gxp-sysreg syscon\n"); + return PTR_ERR(soc_ctrl); + } + + /* Set SCGSyncDis (bit 18) to disable sync on HS200 RX delay lines */ + ret = regmap_update_bits(soc_ctrl, reg_offset, + HPE_GSC_MSHCCS_SCGSYNCDIS, + HPE_GSC_MSHCCS_SCGSYNCDIS); + if (ret) { + dev_err(dev, "failed to set SCGSyncDis in MSHCCS\n"); + return ret; + } + + sdhci_enable_v4_mode(host); + + return 0; +} + static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1656,6 +1829,181 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm return 0; } +static void dwcmshc_k230_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + sdhci_set_clock(host, clock); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + /* + * It is necessary to enable SDHCI_PROG_CLOCK_MODE. This is a + * vendor-specific quirk. If this is not done, the eMMC will be + * unable to read or write. + */ + clk |= SDHCI_PROG_CLOCK_MODE; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_k230_config_phy_delay(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 val; + + sdhci_writeb(host, PHY_COMMDL_CNFG_DLSTEP_SEL, PHY_COMMDL_CNFG); + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); + sdhci_writeb(host, PHY_SDCLKDL_DC_INITIAL, PHY_SDCLKDL_DC_R); + + val = PHY_SMPLDL_CNFG_EXTDLY_EN; + val |= FIELD_PREP(PHY_SMPLDL_CNFG_INPSEL_MASK, PHY_SMPLDL_CNFG_INPSEL); + sdhci_writeb(host, val, PHY_SMPLDL_CNFG_R); + + sdhci_writeb(host, FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL), + PHY_ATDL_CNFG_R); + + val = sdhci_readl(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val |= AT_CTRL_TUNE_CLK_STOP_EN; + val |= FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, AT_CTRL_PRE_CHANGE_DLY); + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY); + sdhci_writel(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_AT_STAT); +} + +static int dwcmshc_k230_phy_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 rxsel; + u32 val; + u32 reg; + int ret; + + /* reset phy */ + sdhci_writew(host, 0, PHY_CNFG_R); + + /* Disable the clock */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + rxsel = dwc_priv->flags & FLAG_IO_FIXED_1V8 ? PHY_PAD_RXSEL_1V8 : PHY_PAD_RXSEL_3V3; + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + + 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 = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + val = rxsel; + 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_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + + sdhci_k230_config_phy_delay(host); + + /* Wait max 150 ms */ + ret = read_poll_timeout(sdhci_readl, reg, + (reg & FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1)), + 10, 150000, false, host, PHY_CNFG_R); + if (ret) { + dev_err(mmc_dev(host->mmc), "READ PHY PWRGOOD timeout!\n"); + return -ETIMEDOUT; + } + + reg = FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_k230) | + FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_k230); + sdhci_writel(host, reg, PHY_CNFG_R); + + /* de-assert the phy */ + reg |= PHY_CNFG_RSTN_DEASSERT; + sdhci_writel(host, reg, PHY_CNFG_R); + + return 0; +} + +static void dwcmshc_k230_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + u8 emmc_ctrl; + + dwcmshc_reset(host, mask); + + if (mask != SDHCI_RESET_ALL) + return; + + emmc_ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + sdhci_writeb(host, emmc_ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + + if (k230_pdata->is_emmc) + dwcmshc_k230_phy_init(host); + else + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); +} + +static int dwcmshc_k230_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + static const char * const clk_ids[] = {"block", "timer", "axi"}; + struct device_node *usb_phy_node; + struct k230_priv *k230_priv; + u32 data; + int ret; + + k230_priv = devm_kzalloc(dev, sizeof(struct k230_priv), GFP_KERNEL); + if (!k230_priv) + return -ENOMEM; + + dwc_priv->priv = k230_priv; + + usb_phy_node = of_parse_phandle(dev->of_node, "canaan,usb-phy", 0); + if (!usb_phy_node) + return dev_err_probe(dev, -ENODEV, "Failed to find canaan,usb-phy phandle\n"); + + k230_priv->hi_sys_regmap = device_node_to_regmap(usb_phy_node); + of_node_put(usb_phy_node); + + if (IS_ERR(k230_priv->hi_sys_regmap)) + return dev_err_probe(dev, PTR_ERR(k230_priv->hi_sys_regmap), + "Failed to get k230-usb-phy regmap\n"); + + ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (ret) + return dev_err_probe(dev, ret, "Failed to get/enable k230 mmc other clocks\n"); + + if (k230_pdata->is_emmc) { + host->flags &= ~SDHCI_SIGNALING_330; + dwc_priv->flags |= FLAG_IO_FIXED_1V8; + } else { + host->mmc->caps |= MMC_CAP_SD_HIGHSPEED; + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + + ret = regmap_read(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, &data); + if (ret) + return dev_err_probe(dev, ret, "Failed to read control reg 0x%x\n", + k230_pdata->ctrl_reg); + + data |= k230_pdata->write_prot_bit | k230_pdata->vol_stable_bit; + ret = regmap_write(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to write control reg 0x%x\n", + k230_pdata->ctrl_reg); + + return 0; +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -1743,6 +2091,15 @@ static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = { .platform_execute_tuning = sdhci_eic7700_executing_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_k230_ops = { + .set_clock = dwcmshc_k230_sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = dwcmshc_k230_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { .pdata = { .ops = &sdhci_dwcmshc_ops, @@ -1771,30 +2128,52 @@ static const struct cqhci_host_ops rk35xx_cqhci_ops = { .set_tran_desc = dwcmshc_set_tran_desc, }; -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, +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3568_pdata = { + .dwcmshc_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, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, }, - .cqhci_host_ops = &rk35xx_cqhci_ops, - .init = dwcmshc_rk35xx_init, - .postinit = dwcmshc_rk35xx_postinit, + .revision = 0, }; -static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_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, +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3576_pdata = { + .dwcmshc_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, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk3576_postinit, }, - .cqhci_host_ops = &rk35xx_cqhci_ops, - .init = dwcmshc_rk35xx_init, - .postinit = dwcmshc_rk3576_postinit, + .revision = 1, +}; + +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3588_pdata = { + .dwcmshc_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, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, + }, + .revision = 1, }; static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = { @@ -1834,6 +2213,55 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { .init = eic7700_init, }; +static const struct k230_pltfm_data k230_emmc_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = true, + .ctrl_reg = SD0_CTRL, + .vol_stable_bit = SD0_HOST_REG_VOL_STABLE, + .write_prot_bit = SD0_CARD_WRITE_PROT, +}; + +static const struct k230_pltfm_data k230_sdio_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = false, + .ctrl_reg = SD1_CTRL, + .vol_stable_bit = SD1_HOST_REG_VOL_STABLE, + .write_prot_bit = SD1_CARD_WRITE_PROT, +}; + +static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = { + .set_clock = dwcmshc_hpe_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_hpe_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = dwcmshc_hpe_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, +}; + +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_hpe_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, + .init = dwcmshc_hpe_gsc_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .enable = dwcmshc_sdhci_cqe_enable, .disable = sdhci_cqe_disable, @@ -1907,8 +2335,16 @@ dsbl_cqe_caps: static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { { + .compatible = "canaan,k230-emmc", + .data = &k230_emmc_data.dwcmshc_pdata, + }, + { + .compatible = "canaan,k230-sdio", + .data = &k230_sdio_data.dwcmshc_pdata, + }, + { .compatible = "rockchip,rk3588-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3588_pdata, }, { .compatible = "rockchip,rk3576-dwcmshc", @@ -1916,7 +2352,7 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { }, { .compatible = "rockchip,rk3568-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3568_pdata, }, { .compatible = "snps,dwcmshc-sdhci", @@ -1942,6 +2378,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "eswin,eic7700-dwcmshc", .data = &sdhci_dwcmshc_eic7700_pdata, }, + { + .compatible = "hpe,gsc-dwcmshc", + .data = &sdhci_dwcmshc_hpe_gsc_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); @@ -1988,6 +2428,7 @@ static int dwcmshc_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); priv = sdhci_pltfm_priv(pltfm_host); + priv->dwcmshc_pdata = pltfm_data; if (dev->of_node) { pltfm_host->clk = devm_clk_get(dev, "core"); diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 0cc97e23a2f9..455656f9842d 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/reset.h> #include <linux/platform_device.h> #include "sdhci.h" @@ -223,6 +224,21 @@ static inline int spacemit_sdhci_get_clocks(struct device *dev, return 0; } +static inline int spacemit_sdhci_get_resets(struct device *dev) +{ + struct reset_control *rst; + + rst = devm_reset_control_get_optional_shared_deasserted(dev, "axi"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, "sdh"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + return 0; +} + static const struct sdhci_ops spacemit_sdhci_ops = { .get_max_clock = spacemit_sdhci_clk_get_max_clock, .reset = spacemit_sdhci_reset, @@ -243,8 +259,20 @@ static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; +static const struct sdhci_pltfm_data spacemit_sdhci_k3_pdata = { + .ops = &spacemit_sdhci_ops, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + static const struct of_device_id spacemit_sdhci_of_match[] = { - { .compatible = "spacemit,k1-sdhci" }, + { .compatible = "spacemit,k1-sdhci", .data = &spacemit_sdhci_k1_pdata }, + { .compatible = "spacemit,k3-sdhci", .data = &spacemit_sdhci_k3_pdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); @@ -255,10 +283,13 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) struct spacemit_sdhci_host *sdhst; struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; + const struct sdhci_pltfm_data *data; struct mmc_host_ops *mops; int ret; - host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst)); + data = of_device_get_match_data(&pdev->dev); + + host = sdhci_pltfm_init(pdev, data, sizeof(*sdhst)); if (IS_ERR(host)) return PTR_ERR(host); @@ -284,6 +315,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) if (ret) goto err_pltfm; + ret = spacemit_sdhci_get_resets(dev); + if (ret) + goto err_pltfm; + ret = sdhci_add_host(host); if (ret) goto err_pltfm; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 47a0a738862b..c347fac24515 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -20,7 +20,6 @@ #include <linux/scatterlist.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/gpio.h> #include <linux/gpio/machine.h> #include <linux/pm_runtime.h> #include <linux/pm_qos.h> diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index 2cc632e91fe4..6899cf6c65e7 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Support of SDHCI platform devices for Microchip PIC32. * @@ -5,10 +6,6 @@ * Andrei Pistirica, Paul Thacker * * Inspired by sdhci-pltfm.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 7f6ac636f040..933fafe0a0ef 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -95,13 +95,6 @@ void sdhci_get_property(struct platform_device *pdev) sdhci_get_compatibility(pdev); device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock); - - if (device_property_present(dev, "keep-power-in-suspend")) - host->mmc->pm_caps |= MMC_PM_KEEP_POWER; - - if (device_property_read_bool(dev, "wakeup-source") || - device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ - host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; } EXPORT_SYMBOL_GPL(sdhci_get_property); @@ -215,19 +208,6 @@ const struct dev_pm_ops sdhci_pltfm_pmops = { }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); -static int __init sdhci_pltfm_drv_init(void) -{ - pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n"); - - return 0; -} -module_init(sdhci_pltfm_drv_init); - -static void __exit sdhci_pltfm_drv_exit(void) -{ -} -module_exit(sdhci_pltfm_drv_exit); - MODULE_DESCRIPTION("SDHCI platform and OF driver helper"); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c index c459a08d01da..41e49c6cc751 100644 --- a/drivers/mmc/host/sdhci-uhs2.c +++ b/drivers/mmc/host/sdhci-uhs2.c @@ -1126,7 +1126,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id) /*****************************************************************************\ * * - * Driver init/exit * + * Driver init * * * \*****************************************************************************/ @@ -1138,17 +1138,6 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host) return 0; } -static int __init sdhci_uhs2_mod_init(void) -{ - return 0; -} -module_init(sdhci_uhs2_mod_init); - -static void __exit sdhci_uhs2_mod_exit(void) -{ -} -module_exit(sdhci_uhs2_mod_exit); - /*****************************************************************************\ * * Device allocation/registration * diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index fec9329e1edb..605be55f8d2d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4193,6 +4193,12 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) unsigned int bounce_size; int ret; + /* Drivers may have already allocated the buffer */ + if (host->bounce_buffer) { + bounce_size = host->bounce_buffer_size; + max_blocks = bounce_size / 512; + goto out; + } /* * Cap the bounce buffer at 64KB. Using a bigger bounce buffer * has diminishing returns, this is probably because SD/MMC @@ -4241,6 +4247,7 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) host->bounce_buffer_size = bounce_size; +out: /* Lie about this since we're bouncing */ mmc->max_segs = max_blocks; mmc->max_seg_size = bounce_size; @@ -5004,22 +5011,6 @@ EXPORT_SYMBOL_GPL(sdhci_remove_host); * * \*****************************************************************************/ -static int __init sdhci_drv_init(void) -{ - pr_info(DRIVER_NAME - ": Secure Digital Host Controller Interface driver\n"); - pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - - return 0; -} - -static void __exit sdhci_drv_exit(void) -{ -} - -module_init(sdhci_drv_init); -module_exit(sdhci_drv_exit); - module_param(debug_quirks, uint, 0444); module_param(debug_quirks2, uint, 0444); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 2cd69c9e9571..c1f7d5b37911 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -193,9 +193,7 @@ static void tifm_sd_transfer_data(struct tifm_sd *host) pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); - p_cnt = PAGE_SIZE - p_off; - p_cnt = min(p_cnt, cnt); - p_cnt = min(p_cnt, t_size); + p_cnt = min3(PAGE_SIZE - p_off, cnt, t_size); if (r_data->flags & MMC_DATA_READ) tifm_sd_read_fifo(host, pg, p_off, p_cnt); diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 3c9df27f9fa7..6c3cb2f1c9d3 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2107,19 +2107,19 @@ static int vub300_probe(struct usb_interface *interface, command_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_out_urb) { retval = -ENOMEM; - goto error0; + goto err_put_udev; } command_res_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_res_urb) { retval = -ENOMEM; - goto error1; + goto err_free_out_urb; } /* this also allocates memory for our VUB300 mmc host device */ mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); - goto error4; + goto err_free_res_urb; } /* MMC core transfer sizes tunable parameters */ mmc->caps = 0; @@ -2336,10 +2336,11 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto error6; + goto err_delete_timer; return 0; -error6: + +err_delete_timer: timer_delete_sync(&vub300->inactivity_timer); err_free_host: mmc_free_host(mmc); @@ -2347,12 +2348,13 @@ err_free_host: * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb: usb_free_urb(command_res_urb); -error1: +err_free_out_urb: usb_free_urb(command_out_urb); -error0: +err_put_udev: usb_put_dev(udev); + return retval; } @@ -2427,37 +2429,36 @@ static int __init vub300_init(void) pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X", firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout); + cmndworkqueue = create_singlethread_workqueue("kvub300c"); - if (!cmndworkqueue) { - pr_err("not enough memory for the REQUEST workqueue"); - result = -ENOMEM; - goto out1; - } + if (!cmndworkqueue) + return -ENOMEM; + pollworkqueue = create_singlethread_workqueue("kvub300p"); if (!pollworkqueue) { - pr_err("not enough memory for the IRQPOLL workqueue"); result = -ENOMEM; - goto out2; + goto err_destroy_cmdwq; } + deadworkqueue = create_singlethread_workqueue("kvub300d"); if (!deadworkqueue) { - pr_err("not enough memory for the EXPIRED workqueue"); result = -ENOMEM; - goto out3; + goto err_destroy_pollwq; } + result = usb_register(&vub300_driver); - if (result) { - pr_err("usb_register failed. Error number %d", result); - goto out4; - } + if (result) + goto err_destroy_deadwq; + return 0; -out4: + +err_destroy_deadwq: destroy_workqueue(deadworkqueue); -out3: +err_destroy_pollwq: destroy_workqueue(pollworkqueue); -out2: +err_destroy_cmdwq: destroy_workqueue(cmndworkqueue); -out1: + return result; } diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index c68132e38138..6d17dfa25dad 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -4,10 +4,21 @@ # config MULTIPLEXER - tristate + bool + +config MUX_CORE + bool "Generic Multiplexer Support" + select MULTIPLEXER + help + This framework is designed to abstract multiplexer handling for + devices via various GPIO-, MMIO/Regmap or specific multiplexer + controller chips. + + If unsure, say no. + +if MULTIPLEXER menu "Multiplexer drivers" - depends on MULTIPLEXER config MUX_ADG792A tristate "Analog Devices ADG792A/ADG792G Multiplexers" @@ -60,3 +71,5 @@ config MUX_MMIO be called mux-mmio. endmenu + +endif # MULTIPLEXER diff --git a/drivers/mux/core.c b/drivers/mux/core.c index f09ee8782e3d..23538de2c91b 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -46,6 +46,16 @@ static const struct class mux_class = { .name = "mux", }; +/** + * struct devm_mux_state_state - Tracks managed resources for mux-state objects. + * @mstate: Pointer to a mux state. + * @exit: An optional callback to execute before free. + */ +struct devm_mux_state_state { + struct mux_state *mstate; + int (*exit)(struct mux_state *mstate); +}; + static DEFINE_IDA(mux_ida); static int __init mux_init(void) @@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) return dev ? to_mux_chip(dev) : NULL; } -/* +/** * mux_get() - Get the mux-control for a device. * @dev: The device that needs a mux-control. * @mux_name: The name identifying the mux-control. * @state: Pointer to where the requested state is returned, or NULL when * the required multiplexer states are handled by other means. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ static struct mux_control *mux_get(struct device *dev, const char *mux_name, - unsigned int *state) + unsigned int *state, bool optional) { struct device_node *np = dev->of_node; struct of_phandle_args args; @@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, else index = of_property_match_string(np, "mux-control-names", mux_name); - if (index < 0) { + if (index < 0 && optional) { + return NULL; + } else if (index < 0) { dev_err(dev, "mux controller '%s' not found\n", mux_name); return ERR_PTR(index); @@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, "mux-controls", "#mux-control-cells", index, &args); if (ret) { + if (optional && ret == -ENOENT) + return NULL; + dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n", - np, state ? "state" : "control", mux_name ?: "", index); + np, state ? "state" : "control", + mux_name ?: "", index); return ERR_PTR(ret); } @@ -617,11 +635,30 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, */ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) { - return mux_get(dev, mux_name, NULL); + struct mux_control *mux = mux_get(dev, mux_name, NULL, false); + + if (!mux) + return ERR_PTR(-ENOENT); + + return mux; } EXPORT_SYMBOL_GPL(mux_control_get); /** + * mux_control_get_optional() - Get the optional mux-control for a device. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + */ +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name) +{ + return mux_get(dev, mux_name, NULL, true); +} +EXPORT_SYMBOL_GPL(mux_control_get_optional); + +/** * mux_control_put() - Put away the mux-control for good. * @mux: The mux-control to put away. * @@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_mux_control_get); -/* +/** * mux_state_get() - Get the mux-state for a device. * @dev: The device that needs a mux-state. * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ -static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) +static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional) { struct mux_state *mstate; @@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) if (!mstate) return ERR_PTR(-ENOMEM); - mstate->mux = mux_get(dev, mux_name, &mstate->state); + mstate->mux = mux_get(dev, mux_name, &mstate->state, optional); if (IS_ERR(mstate->mux)) { int err = PTR_ERR(mstate->mux); kfree(mstate); return ERR_PTR(err); + } else if (!mstate->mux) { + kfree(mstate); + return optional ? NULL : ERR_PTR(-ENOENT); } return mstate; @@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate) static void devm_mux_state_release(struct device *dev, void *res) { - struct mux_state *mstate = *(struct mux_state **)res; + struct devm_mux_state_state *devm_state = res; + + if (devm_state->exit) + devm_state->exit(devm_state->mstate); + + mux_state_put(devm_state->mstate); +} + +/** + * __devm_mux_state_get() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @init: Optional function pointer for mux-state object initialisation. + * @exit: Optional function pointer for mux-state object cleanup on release. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. + */ +static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name, + bool optional, + int (*init)(struct mux_state *mstate), + int (*exit)(struct mux_state *mstate)) +{ + struct devm_mux_state_state *devm_state; + struct mux_state *mstate; + int ret; + + mstate = mux_state_get(dev, mux_name, optional); + if (IS_ERR(mstate)) + return ERR_CAST(mstate); + else if (optional && !mstate) + return NULL; + else if (!mstate) + return ERR_PTR(-ENOENT); + + devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL); + if (!devm_state) { + ret = -ENOMEM; + goto err_devres_alloc; + } + + if (init) { + ret = init(mstate); + if (ret) + goto err_mux_state_init; + } + + devm_state->mstate = mstate; + devm_state->exit = exit; + devres_add(dev, devm_state); + return mstate; + +err_mux_state_init: + devres_free(devm_state); +err_devres_alloc: mux_state_put(mstate); + return ERR_PTR(ret); } /** @@ -722,28 +821,69 @@ static void devm_mux_state_release(struct device *dev, void *res) * @mux_name: The name identifying the mux-control. * * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The mux-state will automatically be freed on release. */ -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name) +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) { - struct mux_state **ptr, *mstate; - - ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + return __devm_mux_state_get(dev, mux_name, false, NULL, NULL); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get); - mstate = mux_state_get(dev, mux_name); - if (IS_ERR(mstate)) { - devres_free(ptr); - return mstate; - } +/** + * devm_mux_state_get_optional() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The mux-state will automatically be freed on release. + */ +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, NULL, NULL); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional); - *ptr = mstate; - devres_add(dev, ptr); +/** + * devm_mux_state_get_selected() - Get the mux-state for a device, with + * resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_selected); - return mstate; +/** + * devm_mux_state_get_optional_selected() - Get the optional mux-state for + * a device, with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect); } -EXPORT_SYMBOL_GPL(devm_mux_state_get); +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected); /* * Using subsys_initcall instead of module_init here to try to ensure - for diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 330356706ad7..2b52e47f247a 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = { }; MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - static struct phy *can_transceiver_phy_xlate(struct device *dev, const struct of_phandle_args *args) { diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index cfc2a8d9028d..79e820e2fe55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable); } -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - -static void rcar_gen3_phy_mux_state_deselect(void *data) -{ - mux_state_deselect(data); -} - static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - mux_state = devm_mux_state_get_optional(dev, NULL); + mux_state = devm_mux_state_get_optional_selected(dev, NULL); if (IS_ERR(mux_state)) - return PTR_ERR(mux_state); - if (mux_state) { - ret = mux_state_select(mux_state); - if (ret) - return dev_err_probe(dev, ret, "Failed to select USB mux\n"); - - ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect, - mux_state); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register USB mux state deselect\n"); - } + return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n"); if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) { ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index e9e964c20e53..9dc4750296af 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -329,6 +329,8 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ #define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */ +#define MMC_QUIRK_BROKEN_MDT (1<<19) /* Wrong manufacturing year */ +#define MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME (1<<20) /* Secure erase/trim time is fixed regardless of size */ bool written_flag; /* Indicates eMMC has been written since power on */ bool reenable_cmdq; /* Re-enable Command Queue */ diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index dce89c110691..0685dd717e85 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -117,6 +117,9 @@ #define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296 #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347 +#define SDIO_VENDOR_ID_NXP 0x0471 +#define SDIO_DEVICE_ID_NXP_IW61X 0x0205 + #define SDIO_VENDOR_ID_REALTEK 0x024c #define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723 #define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821 diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index 2e25c838f831..a961861a503b 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -16,6 +16,8 @@ struct device; struct mux_control; struct mux_state; +#if IS_ENABLED(CONFIG_MULTIPLEXER) + unsigned int mux_control_states(struct mux_control *mux); int __must_check mux_control_select_delay(struct mux_control *mux, unsigned int state, @@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux); int mux_state_deselect(struct mux_state *mstate); struct mux_control *mux_control_get(struct device *dev, const char *mux_name); +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name); void mux_control_put(struct mux_control *mux); -struct mux_control *devm_mux_control_get(struct device *dev, - const char *mux_name); -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name); +struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name); + +#else + +static inline unsigned int mux_control_states(struct mux_control *mux) +{ + return 0; +} +static inline int __must_check mux_control_select_delay(struct mux_control *mux, + unsigned int state, unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_control_try_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_try_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_try_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int mux_control_deselect(struct mux_control *mux) +{ + return -EOPNOTSUPP; +} +static inline int mux_state_deselect(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_control *mux_control_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline void mux_control_put(struct mux_control *mux) {} + +static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline struct mux_state *devm_mux_state_get_selected(struct device *dev, + const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return NULL; +} + +#endif /* CONFIG_MULTIPLEXER */ #endif /* _LINUX_MUX_CONSUMER_H */ |
