diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-13 13:41:26 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-12-13 13:41:26 -0800 |
commit | 71946a25f357a51dcce849367501d7fb04c0465b (patch) | |
tree | a9d09be0ea5ac152679a2730cfdc074e3679788f | |
parent | 90b12f423d3c8a89424c7bdde18e1923dfd0941e (diff) | |
parent | ff874dbc4f868af128b412a9bd92637103cf11d7 (diff) | |
download | linux-71946a25f357a51dcce849367501d7fb04c0465b.tar.gz linux-71946a25f357a51dcce849367501d7fb04c0465b.tar.bz2 linux-71946a25f357a51dcce849367501d7fb04c0465b.zip |
Merge tag 'mmc-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC and MEMSTICK updates from Ulf Hansson:
"MMC core:
- A few minor improvements and cleanups
MMC host:
- Remove some redundant calls to local_irq_{save,restore}()
- Replace kmap_atomic() with kmap_local_page()
- Take return values from mmc_add_host() into account
- dw_mmc-pltfm: Add support to configure clk-phase for socfpga
- hsq: Minimize latency by using a fifo to dispatch requests
- litex_mmc: Fixup corner case for polling mode
- mtk-sd: Add inline crypto engine clock control
- mtk-sd: Add support for the mediatek MT7986 variant
- renesas_sdhi: Improve reset from HS400 mode
- renesas_sdhi: Take DMA end interrupts into account
- sdhci: Avoid unnecessary update of clock
- sdhci: Fix an SD tuning issue
- sdhci-brcmst: Add Kamal Dasu as maintainer for the Broadcom driver
- sdhci-esdhc-imx: Improve tuning logic
- sdhci-esdhc-imx: Improve support for the imxrt1050 variant
- sdhci_f_sdh30: Add support for non-removable media
- sdhci_f_sdh30: Add support for the Socionext F_SDH30_E51 variant
- sdhci_f_sdh30: Add reset control support
- sdhci-msm: Add support for the Qcom SM8550/SM8350/SM6375 variants
- sdhci-msm: Add support for the Qcom MSM8976 variant
- sdhci-of-arasan: Add support for dynamic configuration
- sdhci-of-esdhc: Limit the clock frequency to confirm to spec
- sdhci-pci: Enable asynchronous probe
- sdhci-sprd: Improve card detection
- sdhci-tegra: Improve reset support
- sdhci-tegra: Add support to program MC stream ID
- sunplus-mmc: Add new mmc driver for the Sunplus SP7021 controller
- vub300: Fix warning splat for SDIO irq
MEMSTICK core:
- memstick: A few minor improvements and cleanups
CLK/IOMMU:
- clk: socfpga: Drop redundant support for clk-phase for the SD/MMC clk
- iommu: Add tegra specific helper to get stream_id"
* tag 'mmc-v6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (108 commits)
mmc: sdhci-sprd: Disable CLK_AUTO when the clock is less than 400K
mmc: sdhci-of-esdhc: Modify mismatched function name
memstick/mspro_block: Convert to use sysfs_emit()/sysfs_emit_at() APIs
mmc: sdhci-tegra: Issue CMD and DAT resets together
mmc: sdhci-tegra: Add support to program MC stream ID
mmc: sdhci-tegra: Separate Tegra194 and Tegra234 SoC data
mmc: sdhci-tegra: Sort includes alphabetically
iommu/tegra: Add tegra_dev_iommu_get_stream_id() helper
iommu: Add note about struct iommu_fwspec usage
mmc: sdhci-brcmstb: Resolve "unused" warnings with CONFIG_OF=n
dt-bindings: mmc: sdhci-msm: allow dma-coherent
dt-bindings: mmc: sdhci-msm: drop properties mentioned in common MMC
dt-bindings: mmc: sdhci-msm: cleanup style
dt-bindings: mmc: sdhci-am654: cleanup style
dt-bindings: mmc: sdhci: document sdhci-caps and sdhci-caps-mask
mmc: vub300: fix warning - do not call blocking ops when !TASK_RUNNING
MAINTAINERS: Update maintainer for SDHCI Broadcom BRCMSTB driver
mmc: sdhci-of-esdhc: limit the SDHC clock frequency
mmc: sdhci: Remove unneeded semicolon
mmc: core: Normalize the error handling branch in sd_read_ext_regs()
...
82 files changed, 2117 insertions, 728 deletions
diff --git a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml index 1e69a5a42439..1c96da04f0e5 100644 --- a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml +++ b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml @@ -95,7 +95,9 @@ properties: PIO (polled I/O) interrupt and occurs when the FIFO needs to be emptied as part of a bulk read from the card. Some variants have these two interrupts wired into the same line (logic OR) and in that case - only one interrupt may be provided. + only one interrupt may be provided. The interrupt-names property is + not used due to inconsistency of existing DTs regarding its content. + deprecated: false minItems: 1 maxItems: 2 diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index 0e7833478869..dc6256f04b42 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -50,11 +50,11 @@ properties: - const: fsl,imx8mm-usdhc - items: - enum: + - fsl,imx8dxl-usdhc - fsl,imx8qm-usdhc - const: fsl,imx8qxp-usdhc - items: - enum: - - fsl,imx8dxl-usdhc - fsl,imx8mm-usdhc - fsl,imx8mn-usdhc - fsl,imx8mp-usdhc @@ -71,6 +71,7 @@ properties: deprecated: true - items: - enum: + - fsl,imx8dxl-usdhc - fsl,imx8qm-usdhc - const: fsl,imx8qxp-usdhc - const: fsl,imx7d-usdhc diff --git a/Documentation/devicetree/bindings/mmc/fujitsu,sdhci-fujitsu.yaml b/Documentation/devicetree/bindings/mmc/fujitsu,sdhci-fujitsu.yaml new file mode 100644 index 000000000000..73d747e917f3 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/fujitsu,sdhci-fujitsu.yaml @@ -0,0 +1,57 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/fujitsu,sdhci-fujitsu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Fujitsu/Socionext SDHCI controller (F_SDH30) + +maintainers: + - Kunihiko Hayashi <hayashi.kunihiko@socionext.com> + +allOf: + - $ref: mmc-controller.yaml# + +properties: + compatible: + enum: + - fujitsu,mb86s70-sdhci-3.0 + - socionext,f-sdh30-e51-mmc + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: iface + - const: core + + resets: + maxItems: 1 + + fujitsu,cmd-dat-delay-select: + type: boolean + description: | + Indicating that this host requires the CMD_DAT_DELAY control to be enabled + +unevaluatedProperties: false + +required: + - compatible + - reg + - clocks + - clock-names + +examples: + - | + sdhci1: mmc@36600000 { + compatible = "fujitsu,mb86s70-sdhci-3.0"; + reg = <0x36600000 0x1000>; + bus-width = <4>; + vqmmc-supply = <&vccq_sdhci1>; + clocks = <&clock 2 2 0>, <&clock 2 3 0>; + clock-names = "iface", "core"; + }; diff --git a/Documentation/devicetree/bindings/mmc/mmc-controller.yaml b/Documentation/devicetree/bindings/mmc/mmc-controller.yaml index 802e3ca8be4d..e82c00368088 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-controller.yaml +++ b/Documentation/devicetree/bindings/mmc/mmc-controller.yaml @@ -293,7 +293,6 @@ properties: description: SDIO only. Preserves card power during a suspend/resume cycle. - # Deprecated: enable-sdio-wakeup wakeup-source: $ref: /schemas/types.yaml#/definitions/flag description: diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml index d8e1e2e9adf2..8ed94a12a03b 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml @@ -10,9 +10,6 @@ maintainers: - Chaotian Jing <chaotian.jing@mediatek.com> - Wenbin Mei <wenbin.mei@mediatek.com> -allOf: - - $ref: mmc-controller.yaml# - properties: compatible: oneOf: @@ -23,6 +20,7 @@ properties: - mediatek,mt6795-mmc - mediatek,mt7620-mmc - mediatek,mt7622-mmc + - mediatek,mt7986-mmc - mediatek,mt8135-mmc - mediatek,mt8173-mmc - mediatek,mt8183-mmc @@ -48,27 +46,11 @@ properties: description: Should contain phandle for the clock feeding the MMC controller. minItems: 2 - items: - - description: source clock (required). - - description: HCLK which used for host (required). - - description: independent source clock gate (required for MT2712). - - description: bus clock used for internal register access (required for MT2712 MSDC0/3). - - description: msdc subsys clock gate (required for MT8192). - - description: peripheral bus clock gate (required for MT8192). - - description: AXI bus clock gate (required for MT8192). - - description: AHB bus clock gate (required for MT8192). + maxItems: 7 clock-names: minItems: 2 - items: - - const: source - - const: hclk - - const: source_cg - - const: bus_clk - - const: sys_cg - - const: pclk_cg - - const: axi_cg - - const: ahb_cg + maxItems: 7 interrupts: description: @@ -190,15 +172,144 @@ required: - vmmc-supply - vqmmc-supply -if: - properties: - compatible: - contains: - const: mediatek,mt8183-mmc -then: - properties: - reg: - minItems: 2 +allOf: + - $ref: mmc-controller.yaml# + - if: + properties: + compatible: + enum: + - mediatek,mt2701-mmc + - mediatek,mt6779-mmc + - mediatek,mt6795-mmc + - mediatek,mt7620-mmc + - mediatek,mt7622-mmc + - mediatek,mt7623-mmc + - mediatek,mt8135-mmc + - mediatek,mt8173-mmc + - mediatek,mt8183-mmc + - mediatek,mt8186-mmc + - mediatek,mt8188-mmc + - mediatek,mt8195-mmc + - mediatek,mt8516-mmc + then: + properties: + clocks: + minItems: 2 + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + clock-names: + minItems: 2 + items: + - const: source + - const: hclk + - const: source_cg + + - if: + properties: + compatible: + contains: + const: mediatek,mt2712-mmc + then: + properties: + clocks: + minItems: 3 + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + - description: bus clock used for internal register access (required for MSDC0/3). + clock-names: + minItems: 3 + items: + - const: source + - const: hclk + - const: source_cg + - const: bus_clk + + - if: + properties: + compatible: + contains: + const: mediatek,mt8183-mmc + then: + properties: + reg: + minItems: 2 + + - if: + properties: + compatible: + contains: + enum: + - mediatek,mt7986-mmc + then: + properties: + clocks: + minItems: 3 + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + - description: bus clock used for internal register access (required for MSDC0/3). + - description: msdc subsys clock gate + clock-names: + minItems: 3 + items: + - const: source + - const: hclk + - const: source_cg + - const: bus_clk + - const: sys_cg + + - if: + properties: + compatible: + enum: + - mediatek,mt8186-mmc + - mediatek,mt8188-mmc + - mediatek,mt8195-mmc + then: + properties: + clocks: + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + - description: crypto clock used for data encrypt/decrypt (optional) + clock-names: + items: + - const: source + - const: hclk + - const: source_cg + - const: crypto + + - if: + properties: + compatible: + contains: + const: mediatek,mt8192-mmc + then: + properties: + clocks: + items: + - description: source clock + - description: HCLK which used for host + - description: independent source clock gate + - description: msdc subsys clock gate + - description: peripheral bus clock gate + - description: AXI bus clock gate + - description: AHB bus clock gate + clock-names: + items: + - const: source + - const: hclk + - const: source_cg + - const: sys_cg + - const: pclk_cg + - const: axi_cg + - const: ahb_cg unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 0424b06cb655..7bfb10c62566 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -64,6 +64,7 @@ properties: - enum: - renesas,sdhi-r8a779a0 # R-Car V3U - renesas,sdhi-r8a779f0 # R-Car S4-8 + - renesas,sdhi-r8a779g0 # R-Car V4H - const: renesas,rcar-gen4-sdhi # R-Car Gen4 reg: diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index 95f59a5e3576..c7e14b7dba9e 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -71,6 +71,9 @@ properties: to control the clock phases, "ciu-sample" is required for tuning high speed modes. + power-domains: + maxItems: 1 + rockchip,default-sample-phase: $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml index ea9121fb188d..676a74695389 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ %YAML 1.2 --- -$id: "http://devicetree.org/schemas/mmc/sdhci-am654.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/mmc/sdhci-am654.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TI AM654 MMC Controller @@ -11,17 +11,18 @@ maintainers: - Ulf Hansson <ulf.hansson@linaro.org> allOf: - - $ref: mmc-controller.yaml# + - $ref: sdhci-common.yaml# properties: compatible: oneOf: - - const: ti,am654-sdhci-5.1 - - const: ti,j721e-sdhci-8bit - - const: ti,j721e-sdhci-4bit - - const: ti,am64-sdhci-8bit - - const: ti,am64-sdhci-4bit - - const: ti,am62-sdhci + - enum: + - ti,am62-sdhci + - ti,am64-sdhci-4bit + - ti,am64-sdhci-8bit + - ti,am654-sdhci-5.1 + - ti,j721e-sdhci-4bit + - ti,j721e-sdhci-8bit - items: - const: ti,j7200-sdhci-8bit - const: ti,j721e-sdhci-8bit @@ -49,8 +50,6 @@ properties: - const: clk_ahb - const: clk_xin - sdhci-caps-mask: true - dma-coherent: type: boolean @@ -61,67 +60,67 @@ properties: ti,otap-del-sel-legacy: description: Output tap delay for SD/MMC legacy timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-mmc-hs: description: Output tap delay for MMC high speed timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-sd-hs: description: Output tap delay for SD high speed timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-sdr12: description: Output tap delay for SD UHS SDR12 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-sdr25: description: Output tap delay for SD UHS SDR25 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-sdr50: description: Output tap delay for SD UHS SDR50 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-sdr104: description: Output tap delay for SD UHS SDR104 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-ddr50: description: Output tap delay for SD UHS DDR50 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-ddr52: description: Output tap delay for eMMC DDR52 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-hs200: description: Output tap delay for eMMC HS200 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,otap-del-sel-hs400: description: Output tap delay for eMMC HS400 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf @@ -131,49 +130,55 @@ properties: ti,itap-del-sel-legacy: description: Input tap delay for SD/MMC legacy timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,itap-del-sel-mmc-hs: description: Input tap delay for MMC high speed timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,itap-del-sel-sd-hs: description: Input tap delay for SD high speed timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,itap-del-sel-sdr12: description: Input tap delay for SD UHS SDR12 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,itap-del-sel-sdr25: description: Input tap delay for SD UHS SDR25 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 0x1f + + ti,itap-del-sel-ddr50: + description: Input tap delay for MMC DDR50 timing + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,itap-del-sel-ddr52: description: Input tap delay for MMC DDR52 timing - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0x1f ti,trm-icp: description: DLL trim select - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 minimum: 0 maximum: 0xf ti,driver-strength-ohm: description: DLL drive strength in ohms - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 enum: - 33 - 40 @@ -183,11 +188,11 @@ properties: ti,strobe-sel: description: strobe select delay for HS400 speed mode. - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 ti,clkbuf-sel: description: Clock Delay Buffer Select - $ref: "/schemas/types.yaml#/definitions/uint32" + $ref: /schemas/types.yaml#/definitions/uint32 ti,fails-without-test-cd: $ref: /schemas/types.yaml#/definitions/flag diff --git a/Documentation/devicetree/bindings/mmc/sdhci-common.yaml b/Documentation/devicetree/bindings/mmc/sdhci-common.yaml new file mode 100644 index 000000000000..1664615187c8 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-common.yaml @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/sdhci-common.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SDHCI Controller Common Properties + +maintainers: + - Adrian Hunter <adrian.hunter@intel.com> + +description: + Common properties present on Secure Digital Host Controller Interface (SDHCI) + devices. + +properties: + sdhci-caps: + $ref: /schemas/types.yaml#/definitions/uint64 + description: + Additionally present SDHCI capabilities - values for SDHCI_CAPABILITIES + and SDHCI_CAPABILITIES_1 registers. + + sdhci-caps-mask: + $ref: /schemas/types.yaml#/definitions/uint64 + description: + Masked SDHCI capabilities to remove from SDHCI_CAPABILITIES and + SDHCI_CAPABILITIES_1 registers. + +allOf: + - $ref: mmc-controller.yaml# + +additionalProperties: true diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt deleted file mode 100644 index 3ee9263adf73..000000000000 --- a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt +++ /dev/null @@ -1,32 +0,0 @@ -* Fujitsu SDHCI controller - -This file documents differences between the core properties in mmc.txt -and the properties used by the sdhci_f_sdh30 driver. - -Required properties: -- compatible: "fujitsu,mb86s70-sdhci-3.0" -- clocks: Must contain an entry for each entry in clock-names. It is a - list of phandles and clock-specifier pairs. - See ../clocks/clock-bindings.txt for details. -- clock-names: Should contain the following two entries: - "iface" - clock used for sdhci interface - "core" - core clock for sdhci controller - -Optional properties: -- vqmmc-supply: phandle to the regulator device tree node, mentioned - as the VCCQ/VDD_IO supply in the eMMC/SD specs. -- fujitsu,cmd-dat-delay-select: boolean property indicating that this host - requires the CMD_DAT_DELAY control to be enabled. - -Example: - - sdhci1: mmc@36600000 { - compatible = "fujitsu,mb86s70-sdhci-3.0"; - reg = <0 0x36600000 0x1000>; - interrupts = <0 172 0x4>, - <0 173 0x4>; - bus-width = <4>; - vqmmc-supply = <&vccq_sdhci1>; - clocks = <&clock 2 2 0>, <&clock 2 3 0>; - clock-names = "iface", "core"; - }; diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index a96f143479c7..6b89238f0565 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -1,9 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) - %YAML 1.2 --- -$id: "http://devicetree.org/schemas/mmc/sdhci-msm.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/mmc/sdhci-msm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Qualcomm SDHCI controller (sdhci-msm) @@ -26,6 +25,7 @@ properties: - qcom,msm8226-sdhci - qcom,msm8953-sdhci - qcom,msm8974-sdhci + - qcom,msm8976-sdhci - qcom,msm8916-sdhci - qcom,msm8992-sdhci - qcom,msm8994-sdhci @@ -45,9 +45,12 @@ properties: - qcom,sm6115-sdhci - qcom,sm6125-sdhci - qcom,sm6350-sdhci + - qcom,sm6375-sdhci - qcom,sm8150-sdhci - qcom,sm8250-sdhci + - qcom,sm8350-sdhci - qcom,sm8450-sdhci + - qcom,sm8550-sdhci - const: qcom,sdhci-msm-v5 # for sdcc version 5.0 reg: @@ -80,6 +83,8 @@ properties: - const: cal - const: sleep + dma-coherent: true + interrupts: maxItems: 2 @@ -133,16 +138,6 @@ properties: description: A phandle to sdhci power domain node maxItems: 1 - mmc-ddr-1_8v: true - - mmc-hs200-1_8v: true - - mmc-hs400-1_8v: true - - bus-width: true - - max-frequency: true - operating-points-v2: true patternProperties: @@ -165,7 +160,7 @@ required: - interrupts allOf: - - $ref: mmc-controller.yaml# + - $ref: sdhci-common.yaml# - if: properties: diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 71f8e726d641..51ba44cad842 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -45,6 +45,17 @@ properties: - const: block - const: timer + resets: + maxItems: 5 + + reset-names: + items: + - const: core + - const: bus + - const: axi + - const: block + - const: timer + rockchip,txclk-tapnum: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 diff --git a/Documentation/devicetree/bindings/mmc/sunplus,mmc.yaml b/Documentation/devicetree/bindings/mmc/sunplus,mmc.yaml new file mode 100644 index 000000000000..23aa8e6b2d70 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sunplus,mmc.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) Sunplus Ltd. Co. 2021 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/sunplus,mmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Sunplus MMC Controller + +maintainers: + - Tony Huang <tonyhuang.sunplus@gmail.com> + - Li-hao Kuo <lhjeff911@gmail.com> + +allOf: + - $ref: "mmc-controller.yaml" + +properties: + compatible: + enum: + - sunplus,sp7021-mmc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - resets + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/interrupt-controller/arm-gic.h> + mmc0: mmc@9c003b00 { + compatible = "sunplus,sp7021-mmc"; + reg = <0x9c003b00 0x180>; + interrupts = <20 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkc 0x4e>; + resets = <&rstc 0x3e>; + bus-width = <8>; + max-frequency = <52000000>; + non-removable; + disable-wp; + cap-mmc-highspeed; + mmc-ddr-3_3v; + no-sdio; + no-sd; + }; diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml index ae6d6fca79e2..e1f5f26f3f1c 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml @@ -6,9 +6,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Synopsys Designware Mobile Storage Host Controller Binding -allOf: - - $ref: "synopsys-dw-mshc-common.yaml#" - maintainers: - Ulf Hansson <ulf.hansson@linaro.org> @@ -38,6 +35,35 @@ properties: - const: biu - const: ciu + altr,sysmgr-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to the sysmgr node + - description: register offset that controls the SDMMC clock phase + - description: register shift for the smplsel(drive in) setting + description: + This property is optional. Contains the phandle to System Manager block + that contains the SDMMC clock-phase control register. The first value is + the pointer to the sysmgr, the 2nd value is the register offset for the + SDMMC clock phase register, and the 3rd value is the bit shift for the + smplsel(drive in) setting. + +allOf: + - $ref: synopsys-dw-mshc-common.yaml# + + - if: + properties: + compatible: + contains: + const: altr,socfpga-dw-mshc + then: + properties: + altr,sysmgr-syscon: true + else: + properties: + altr,sysmgr-syscon: false + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/power/wakeup-source.txt b/Documentation/devicetree/bindings/power/wakeup-source.txt index cfd74659fbed..697333a56d5e 100644 --- a/Documentation/devicetree/bindings/power/wakeup-source.txt +++ b/Documentation/devicetree/bindings/power/wakeup-source.txt @@ -17,15 +17,14 @@ interrupt. List of legacy properties and respective binding document --------------------------------------------------------- -1. "enable-sdio-wakeup" Documentation/devicetree/bindings/mmc/mmc.txt -2. "gpio-key,wakeup" Documentation/devicetree/bindings/input/gpio-keys{,-polled}.txt -3. "has-tpo" Documentation/devicetree/bindings/rtc/rtc-opal.txt -4. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt +1. "gpio-key,wakeup" Documentation/devicetree/bindings/input/gpio-keys{,-polled}.txt +2. "has-tpo" Documentation/devicetree/bindings/rtc/rtc-opal.txt +3. "linux,wakeup" Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt Documentation/devicetree/bindings/mfd/tc3589x.txt Documentation/devicetree/bindings/input/touchscreen/ads7846.txt -5. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt -6. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt -7. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt +4. "linux,keypad-wakeup" Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt +5. "linux,input-wakeup" Documentation/devicetree/bindings/input/samsung-keypad.txt +6. "nvidia,wakeup-source" Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt Examples -------- diff --git a/MAINTAINERS b/MAINTAINERS index bfffbe8c35e2..cedfeb9ab5a3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18612,6 +18612,7 @@ K: \bsecure_computing K: \bTIF_SECCOMP\b SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) Broadcom BRCMSTB DRIVER +M: Kamal Dasu <kdasu.kdev@gmail.com> M: Al Cooper <alcooperx@gmail.com> R: Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com> L: linux-mmc@vger.kernel.org @@ -18622,6 +18623,7 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER M: Adrian Hunter <adrian.hunter@intel.com> L: linux-mmc@vger.kernel.org S: Supported +F: Documentation/devicetree/bindings/mmc/sdhci-common.yaml F: drivers/mmc/host/sdhci* SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) MICROCHIP DRIVER @@ -19884,6 +19886,13 @@ W: https://sunplus.atlassian.net/wiki/spaces/doc/overview F: Documentation/devicetree/bindings/net/sunplus,sp7021-emac.yaml F: drivers/net/ethernet/sunplus/ +SUNPLUS MMC DRIVER +M: Tony Huang <tonyhuang.sunplus@gmail.com> +M: Li-hao Kuo <lhjeff911@gmail.com> +S: Maintained +F: Documentation/devicetree/bindings/mmc/sunplus,mmc.yaml +F: drivers/mmc/host/sunplus-mmc.c + SUNPLUS OCOTP DRIVER M: Vincent Shih <vincent.sunplus@gmail.com> S: Maintained diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c index 738c53391e39..7cdf2f07c79b 100644 --- a/drivers/clk/socfpga/clk-gate-a10.c +++ b/drivers/clk/socfpga/clk-gate-a10.c @@ -35,59 +35,7 @@ static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, return parent_rate / div; } -static int socfpga_clk_prepare(struct clk_hw *hwclk) -{ - struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); - int i; - u32 hs_timing; - u32 clk_phase[2]; - - if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { - for (i = 0; i < ARRAY_SIZE(clk_phase); i++) { - switch (socfpgaclk->clk_phase[i]) { - case 0: - clk_phase[i] = 0; - break; - case 45: - clk_phase[i] = 1; - break; - case 90: - clk_phase[i] = 2; - break; - case 135: - clk_phase[i] = 3; - break; - case 180: - clk_phase[i] = 4; - break; - case 225: - clk_phase[i] = 5; - break; - case 270: - clk_phase[i] = 6; - break; - case 315: - clk_phase[i] = 7; - break; - default: - clk_phase[i] = 0; - break; - } - } - - hs_timing = SYSMGR_SDMMC_CTRL_SET_AS10(clk_phase[0], clk_phase[1]); - if (!IS_ERR(socfpgaclk->sys_mgr_base_addr)) - regmap_write(socfpgaclk->sys_mgr_base_addr, - SYSMGR_SDMMCGRP_CTRL_OFFSET, hs_timing); - else - pr_err("%s: cannot set clk_phase because sys_mgr_base_addr is not available!\n", - __func__); - } - return 0; -} - static struct clk_ops gateclk_ops = { - .prepare = socfpga_clk_prepare, .recalc_rate = socfpga_gate_clk_recalc_rate, }; @@ -96,7 +44,6 @@ static void __init __socfpga_gate_init(struct device_node *node, { u32 clk_gate[2]; u32 div_reg[3]; - u32 clk_phase[2]; u32 fixed_div; struct clk_hw *hw_clk; struct socfpga_gate_clk *socfpga_clk; @@ -136,21 +83,6 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->div_reg = NULL; } - rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); - if (!rc) { - socfpga_clk->clk_phase[0] = clk_phase[0]; - socfpga_clk->clk_phase[1] = clk_phase[1]; - - socfpga_clk->sys_mgr_base_addr = - syscon_regmap_lookup_by_compatible("altr,sys-mgr"); - if (IS_ERR(socfpga_clk->sys_mgr_base_addr)) { - pr_err("%s: failed to find altr,sys-mgr regmap!\n", - __func__); - kfree(socfpga_clk); - return; - } - } - of_property_read_string(node, "clock-output-names", &clk_name); init.name = clk_name; diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index 53d6e3ec4309..3e347b9e9eff 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -108,61 +108,7 @@ static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, return parent_rate / div; } -static int socfpga_clk_prepare(struct clk_hw *hwclk) -{ - struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); - struct regmap *sys_mgr_base_addr; - int i; - u32 hs_timing; - u32 clk_phase[2]; - - if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { - sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); - if (IS_ERR(sys_mgr_base_addr)) { - pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__); - return -EINVAL; - } - - for (i = 0; i < 2; i++) { - switch (socfpgaclk->clk_phase[i]) { - case 0: - clk_phase[i] = 0; - break; - case 45: - clk_phase[i] = 1; - break; - case 90: - clk_phase[i] = 2; - break; - case 135: - clk_phase[i] = 3; - break; - case 180: - clk_phase[i] = 4; - break; - case 225: - clk_phase[i] = 5; - break; - case 270: - clk_phase[i] = 6; - break; - case 315: - clk_phase[i] = 7; - break; - default: - clk_phase[i] = 0; - break; - } - } - hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); - regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET, - hs_timing); - } - return 0; -} - static struct clk_ops gateclk_ops = { - .prepare = socfpga_clk_prepare, .recalc_rate = socfpga_clk_recalc_rate, .get_parent = socfpga_clk_get_parent, .set_parent = socfpga_clk_set_parent, @@ -172,7 +118,6 @@ void __init socfpga_gate_init(struct device_node *node) { u32 clk_gate[2]; u32 div_reg[3]; - u32 clk_phase[2]; u32 fixed_div; struct clk_hw *hw_clk; struct socfpga_gate_clk *socfpga_clk; @@ -218,12 +163,6 @@ void __init socfpga_gate_init(struct device_node *node) socfpga_clk->div_reg = NULL; } - rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); - if (!rc) { - socfpga_clk->clk_phase[0] = clk_phase[0]; - socfpga_clk->clk_phase[1] = clk_phase[1]; - } - of_property_read_string(node, "clock-output-names", &clk_name); init.name = clk_name; diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d80115fbdd6a..9a2fb2dde5b8 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -50,7 +50,6 @@ struct socfpga_gate_clk { u32 width; /* only valid if div_reg != 0 */ u32 shift; /* only valid if div_reg != 0 */ u32 bypass_shift; /* only valid if bypass_reg != 0 */ - u32 clk_phase[2]; }; struct socfpga_periph_clk { diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index ba8414519515..04115cd92433 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -2116,6 +2116,11 @@ static int msb_init_disk(struct memstick_dev *card) dbg("Set total disk size to %lu sectors", capacity); msb->io_queue = alloc_ordered_workqueue("ms_block", WQ_MEM_RECLAIM); + if (!msb->io_queue) { + rc = -ENOMEM; + goto out_cleanup_disk; + } + INIT_WORK(&msb->io_work, msb_io_work); sg_init_table(msb->prealloc_sg, MS_BLOCK_MAX_SEGS+1); @@ -2125,10 +2130,12 @@ static int msb_init_disk(struct memstick_dev *card) msb_start(card); rc = device_add_disk(&card->dev, msb->disk, NULL); if (rc) - goto out_cleanup_disk; + goto out_destroy_workqueue; dbg("Disk added"); return 0; +out_destroy_workqueue: + destroy_workqueue(msb->io_queue); out_cleanup_disk: put_disk(msb->disk); out_free_tag_set: diff --git a/drivers/memstick/core/mspro_block.c b/drivers/memstick/core/mspro_block.c index 61cf75d4a01e..5a69ed33999b 100644 --- a/drivers/memstick/core/mspro_block.c +++ b/drivers/memstick/core/mspro_block.c @@ -260,8 +260,8 @@ static ssize_t mspro_block_attr_show_default(struct device *dev, buffer[rc++] = '\n'; } - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "%02x ", - ((unsigned char *)s_attr->data)[cnt]); + rc += sysfs_emit_at(buffer, rc, "%02x ", + ((unsigned char *)s_attr->data)[cnt]); } return rc; } @@ -290,61 +290,43 @@ static ssize_t mspro_block_attr_show_sysinfo(struct device *dev, date_tz_f *= 15; } - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "class: %x\n", - x_sys->class); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block size: %x\n", - be16_to_cpu(x_sys->block_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "block count: %x\n", - be16_to_cpu(x_sys->block_count)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "user block count: %x\n", - be16_to_cpu(x_sys->user_block_count)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "page size: %x\n", - be16_to_cpu(x_sys->page_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly date: " - "GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u\n", - date_tz, date_tz_f, - be16_to_cpup((__be16 *)&x_sys->assembly_date[1]), - x_sys->assembly_date[3], x_sys->assembly_date[4], - x_sys->assembly_date[5], x_sys->assembly_date[6], - x_sys->assembly_date[7]); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "serial number: %x\n", - be32_to_cpu(x_sys->serial_number)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "assembly maker code: %x\n", - x_sys->assembly_maker_code); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "assembly model code: " - "%02x%02x%02x\n", x_sys->assembly_model_code[0], - x_sys->assembly_model_code[1], - x_sys->assembly_model_code[2]); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory maker code: %x\n", - be16_to_cpu(x_sys->memory_maker_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "memory model code: %x\n", - be16_to_cpu(x_sys->memory_model_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vcc: %x\n", - x_sys->vcc); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "vpp: %x\n", - x_sys->vpp); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller number: %x\n", - be16_to_cpu(x_sys->controller_number)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "controller function: %x\n", - be16_to_cpu(x_sys->controller_function)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n", - be16_to_cpu(x_sys->start_sector)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "unit size: %x\n", - be16_to_cpu(x_sys->unit_size)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sub class: %x\n", - x_sys->ms_sub_class); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "interface type: %x\n", - x_sys->interface_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "controller code: %x\n", - be16_to_cpu(x_sys->controller_code)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "format type: %x\n", - x_sys->format_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "device type: %x\n", - x_sys->device_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "mspro id: %s\n", - x_sys->mspro_id); + rc += sysfs_emit_at(buffer, rc, "class: %x\n", x_sys->class); + rc += sysfs_emit_at(buffer, rc, "block size: %x\n", be16_to_cpu(x_sys->block_size)); + rc += sysfs_emit_at(buffer, rc, "block count: %x\n", be16_to_cpu(x_sys->block_count)); + rc += sysfs_emit_at(buffer, rc, "user block count: %x\n", + be16_to_cpu(x_sys->user_block_count)); + rc += sysfs_emit_at(buffer, rc, "page size: %x\n", be16_to_cpu(x_sys->page_size)); + rc += sysfs_emit_at(buffer, rc, "assembly date: GMT%+d:%d %04u-%02u-%02u %02u:%02u:%02u\n", + date_tz, date_tz_f, + be16_to_cpup((__be16 *)&x_sys->assembly_date[1]), + x_sys->assembly_date[3], x_sys->assembly_date[4], + x_sys->assembly_date[5], x_sys->assembly_date[6], + x_sys->assembly_date[7]); + rc += sysfs_emit_at(buffer, rc, "serial number: %x\n", be32_to_cpu(x_sys->serial_number)); + rc += sysfs_emit_at(buffer, rc, "assembly maker code: %x\n", x_sys->assembly_maker_code); + rc += sysfs_emit_at(buffer, rc, "assembly model code: %02x%02x%02x\n", + x_sys->assembly_model_code[0], + x_sys->assembly_model_code[1], + x_sys->assembly_model_code[2]); + rc += sysfs_emit_at(buffer, rc, "memory maker code: %x\n", + be16_to_cpu(x_sys->memory_maker_code)); + rc += sysfs_emit_at(buffer, rc, "memory model code: %x\n", + be16_to_cpu(x_sys->memory_model_code)); + rc += sysfs_emit_at(buffer, rc, "vcc: %x\n", x_sys->vcc); + rc += sysfs_emit_at(buffer, rc, "vpp: %x\n", x_sys->vpp); + rc += sysfs_emit_at(buffer, rc, "controller number: %x\n", + be16_to_cpu(x_sys->controller_number)); + rc += sysfs_emit_at(buffer, rc, "controller function: %x\n", + be16_to_cpu(x_sys->controller_function)); + rc += sysfs_emit_at(buffer, rc, "start sector: %x\n", be16_to_cpu(x_sys->start_sector)); + rc += sysfs_emit_at(buffer, rc, "unit size: %x\n", be16_to_cpu(x_sys->unit_size)); + rc += sysfs_emit_at(buffer, rc, "sub class: %x\n", x_sys->ms_sub_class); + rc += sysfs_emit_at(buffer, rc, "interface type: %x\n", x_sys->interface_type); + rc += sysfs_emit_at(buffer, rc, "controller code: %x\n", + be16_to_cpu(x_sys->controller_code)); + rc += sysfs_emit_at(buffer, rc, "format type: %x\n", x_sys->format_type); + rc += sysfs_emit_at(buffer, rc, "device type: %x\n", x_sys->device_type); + rc += sysfs_emit_at(buffer, rc, "mspro id: %s\n", x_sys->mspro_id); return rc; } @@ -356,7 +338,7 @@ static ssize_t mspro_block_attr_show_modelname(struct device *dev, struct mspro_sys_attr, dev_attr); - return scnprintf(buffer, PAGE_SIZE, "%s", (char *)s_attr->data); + return sysfs_emit(buffer, "%s", (char *)s_attr->data); } static ssize_t mspro_block_attr_show_mbr(struct device *dev, @@ -369,27 +351,17 @@ static ssize_t mspro_block_attr_show_mbr(struct device *dev, struct mspro_mbr *x_mbr = x_attr->data; ssize_t rc = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "boot partition: %x\n", - x_mbr->boot_partition); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start head: %x\n", - x_mbr->start_head); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sector: %x\n", - x_mbr->start_sector); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cylinder: %x\n", - x_mbr->start_cylinder); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "partition type: %x\n", - x_mbr->partition_type); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end head: %x\n", - x_mbr->end_head); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end sector: %x\n", - x_mbr->end_sector); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "end cylinder: %x\n", - x_mbr->end_cylinder); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start sectors: %x\n", - x_mbr->start_sectors); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, - "sectors per partition: %x\n", - x_mbr->sectors_per_partition); + rc += sysfs_emit_at(buffer, rc, "boot partition: %x\n", x_mbr->boot_partition); + rc += sysfs_emit_at(buffer, rc, "start head: %x\n", x_mbr->start_head); + rc += sysfs_emit_at(buffer, rc, "start sector: %x\n", x_mbr->start_sector); + rc += sysfs_emit_at(buffer, rc, "start cylinder: %x\n", x_mbr->start_cylinder); + rc += sysfs_emit_at(buffer, rc, "partition type: %x\n", x_mbr->partition_type); + rc += sysfs_emit_at(buffer, rc, "end head: %x\n", x_mbr->end_head); + rc += sysfs_emit_at(buffer, rc, "end sector: %x\n", x_mbr->end_sector); + rc += sysfs_emit_at(buffer, rc, "end cylinder: %x\n", x_mbr->end_cylinder); + rc += sysfs_emit_at(buffer, rc, "start sectors: %x\n", x_mbr->start_sectors); + rc += sysfs_emit_at(buffer, rc, "sectors per partition: %x\n", + x_mbr->sectors_per_partition); return rc; } @@ -409,22 +381,19 @@ static ssize_t mspro_block_attr_show_specfile(struct device *dev, memcpy(ext, x_spfile->ext, 3); ext[3] = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "name: %s\n", name); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "ext: %s\n", ext); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "attribute: %x\n", - x_spfile->attr); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "time: %d:%d:%d\n", - x_spfile->time >> 11, - (x_spfile->time >> 5) & 0x3f, - (x_spfile->time & 0x1f) * 2); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "date: %d-%d-%d\n", - (x_spfile->date >> 9) + 1980, - (x_spfile->date >> 5) & 0xf, - x_spfile->date & 0x1f); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "start cluster: %x\n", - x_spfile->cluster); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "size: %x\n", - x_spfile->size); + rc += sysfs_emit_at(buffer, rc, "name: %s\n", name); + rc += sysfs_emit_at(buffer, rc, "ext: %s\n", ext); + rc += sysfs_emit_at(buffer, rc, "attribute: %x\n", x_spfile->attr); + rc += sysfs_emit_at(buffer, rc, "time: %d:%d:%d\n", + x_spfile->time >> 11, + (x_spfile->time >> 5) & 0x3f, + (x_spfile->time & 0x1f) * 2); + rc += sysfs_emit_at(buffer, rc, "date: %d-%d-%d\n", + (x_spfile->date >> 9) + 1980, + (x_spfile->date >> 5) & 0xf, + x_spfile->date & 0x1f); + rc += sysfs_emit_at(buffer, rc, "start cluster: %x\n", x_spfile->cluster); + rc += sysfs_emit_at(buffer, rc, "size: %x\n", x_spfile->size); return rc; } @@ -438,16 +407,14 @@ static ssize_t mspro_block_attr_show_devinfo(struct device *dev, struct mspro_devinfo *x_devinfo = x_attr->data; ssize_t rc = 0; - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "cylinders: %x\n", - be16_to_cpu(x_devinfo->cylinders)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "heads: %x\n", - be16_to_cpu(x_devinfo->heads)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per track: %x\n", - be16_to_cpu(x_devinfo->bytes_per_track)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "bytes per sector: %x\n", - be16_to_cpu(x_devinfo->bytes_per_sector)); - rc += scnprintf(buffer + rc, PAGE_SIZE - rc, "sectors per track: %x\n", - be16_to_cpu(x_devinfo->sectors_per_track)); + rc += sysfs_emit_at(buffer, rc, "cylinders: %x\n", be16_to_cpu(x_devinfo->cylinders)); + rc += sysfs_emit_at(buffer, rc, "heads: %x\n", be16_to_cpu(x_devinfo->heads)); + rc += sysfs_emit_at(buffer, rc, "bytes per track: %x\n", + be16_to_cpu(x_devinfo->bytes_per_track)); + rc += sysfs_emit_at(buffer, rc, "bytes per sector: %x\n", + be16_to_cpu(x_devinfo->bytes_per_sector)); + rc += sysfs_emit_at(buffer, rc, "sectors per track: %x\n", + be16_to_cpu(x_devinfo->sectors_per_track)); return rc; } diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index db6d8a099910..20da7ed43e6d 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -514,19 +514,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, if (idata->ic.data_timeout_ns) data.timeout_ns = idata->ic.data_timeout_ns; - if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) { - /* - * Pretend this is a data transfer and rely on the - * host driver to compute timeout. When all host - * drivers support cmd.cmd_timeout for R1B, this - * can be changed to: - * - * mrq.data = NULL; - * cmd.cmd_timeout = idata->ic.cmd_timeout_ms; - */ - data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000; - } - mrq.data = &data; } diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index d8762fa3d5cd..36679f4e9acc 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -359,9 +359,7 @@ int mmc_add_card(struct mmc_card *card) uhs_bus_speed_mode, type, card->rca); } -#ifdef CONFIG_DEBUG_FS mmc_add_card_debugfs(card); -#endif card->dev.of_node = mmc_of_find_child_device(card->host, 0); device_enable_async_suspend(&card->dev); @@ -383,9 +381,7 @@ void mmc_remove_card(struct mmc_card *card) { struct mmc_host *host = card->host; -#ifdef CONFIG_DEBUG_FS mmc_remove_card_debugfs(card); -#endif if (mmc_card_present(card)) { if (mmc_host_is_spi(card->host)) { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index f0d19356ad76..426c7f66b349 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -56,7 +56,7 @@ static const unsigned freqs[] = { 400000, 300000, 200000, 100000 }; /* * Enabling software CRCs on the data blocks can be a significant (30%) * performance cost, and for other reasons may not always be desired. - * So we allow it it to be disabled. + * So we allow it to be disabled. */ bool use_spi_crc = 1; module_param(use_spi_crc, bool, 0); @@ -142,8 +142,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) int err = cmd->error; /* Flag re-tuning needed on CRC errors */ - if (cmd->opcode != MMC_SEND_TUNING_BLOCK && - cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 && + if (!mmc_op_tuning(cmd->opcode) && !host->retune_crc_disable && (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || (mrq->data && mrq->data->error == -EILSEQ) || @@ -527,7 +526,7 @@ EXPORT_SYMBOL(mmc_cqe_post_req); * mmc_cqe_recovery - Recover from CQE errors. * @host: MMC host to recover * - * Recovery consists of stopping CQE, stopping eMMC, discarding the queue in + * Recovery consists of stopping CQE, stopping eMMC, discarding the queue * in eMMC, and discarding the queue in CQE. CQE must call * mmc_cqe_request_done() on all requests. An error is returned if the eMMC * fails to discard its queue. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index f5f3f623ea49..37091a6589ed 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -86,11 +86,26 @@ int mmc_attach_sdio(struct mmc_host *host); extern bool use_spi_crc; /* Debugfs information for hosts and cards */ +#ifdef CONFIG_DEBUG_FS void mmc_add_host_debugfs(struct mmc_host *host); void mmc_remove_host_debugfs(struct mmc_host *host); void mmc_add_card_debugfs(struct mmc_card *card); void mmc_remove_card_debugfs(struct mmc_card *card); +#else +static inline void mmc_add_host_debugfs(struct mmc_host *host) +{ +} +static inline void mmc_remove_host_debugfs(struct mmc_host *host) +{ +} +static inline void mmc_add_card_debugfs(struct mmc_card *card) +{ +} +static inline void mmc_remove_card_debugfs(struct mmc_card *card) +{ +} +#endif int mmc_execute_tuning(struct mmc_card *card); int mmc_hs200_to_hs400(struct mmc_card *card); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index b89dca1f15e9..d17eda753b7e 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -269,7 +269,7 @@ EXPORT_SYMBOL(mmc_of_parse_clk_phase); * @host: host whose properties should be parsed. * * To keep the rest of the MMC subsystem unaware of whether DT has been - * used to to instantiate and configure this host instance or not, we + * used to instantiate and configure this host instance or not, we * parse the properties and set respective generic mmc-host flags and * parameters. */ @@ -629,9 +629,7 @@ int mmc_add_host(struct mmc_host *host) led_trigger_register_simple(dev_name(&host->class_dev), &host->led); -#ifdef CONFIG_DEBUG_FS mmc_add_host_debugfs(host); -#endif mmc_start_host(host); return 0; @@ -651,9 +649,7 @@ void mmc_remove_host(struct mmc_host *host) { mmc_stop_host(host); -#ifdef CONFIG_DEBUG_FS mmc_remove_host_debugfs(host); -#endif device_del(&host->class_dev); diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 155ce2bdfe62..156d34b2ed4d 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -932,7 +932,6 @@ static int mmc_test_transfer(struct mmc_test_card *test, unsigned blocks, unsigned blksz, int write) { int ret, i; - unsigned long flags; if (write) { for (i = 0; i < blocks * blksz; i++) @@ -940,9 +939,7 @@ static int mmc_test_transfer(struct mmc_test_card *test, } else { memset(test->scratch, 0, BUFFER_SIZE); } - local_irq_save(flags); sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); - local_irq_restore(flags); ret = mmc_test_set_blksize(test, blksz); if (ret) @@ -987,9 +984,7 @@ static int mmc_test_transfer(struct mmc_test_card *test, return RESULT_FAIL; } } else { - local_irq_save(flags); sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE); - local_irq_restore(flags); for (i = 0; i < blocks * blksz; i++) { if (test->scratch[i] != (u8)i) return RESULT_FAIL; diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c index ef675f364bf0..2374669b588a 100644 --- a/drivers/mmc/core/pwrseq.c +++ b/drivers/mmc/core/pwrseq.c @@ -29,7 +29,7 @@ int mmc_pwrseq_alloc(struct mmc_host *host) mutex_lock(&pwrseq_list_mutex); list_for_each_entry(p, &pwrseq_list, pwrseq_node) { - if (p->dev->of_node == np) { + if (device_match_of_node(p->dev, np)) { if (!try_module_get(p->owner)) dev_err(host->parent, "increasing module refcount failed\n"); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 3662bf5320ce..72b664ed90cf 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1259,7 +1259,7 @@ static int sd_read_ext_regs(struct mmc_card *card) */ err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf); if (err) { - pr_warn("%s: error %d reading general info of SD ext reg\n", + pr_err("%s: error %d reading general info of SD ext reg\n", mmc_hostname(card->host), err); goto out; } @@ -1273,7 +1273,12 @@ static int sd_read_ext_regs(struct mmc_card *card) /* Number of extensions to be find. */ num_ext = gen_info_buf[4]; - /* We support revision 0, but limit it to 512 bytes for simplicity. */ + /* + * We only support revision 0 and limit it to 512 bytes for simplicity. + * No matter what, let's return zero to allow us to continue using the + * card, even if we can't support the features from the SD function + * extensions registers. + */ if (rev != 0 || len > 512) { pr_warn("%s: non-supported SD ext reg layout\n", mmc_hostname(card->host)); @@ -1288,7 +1293,7 @@ static int sd_read_ext_regs(struct mmc_card *card) for (i = 0; i < num_ext; i++) { err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr); if (err) { - pr_warn("%s: error %d parsing SD ext reg\n", + pr_err("%s: error %d parsing SD ext reg\n", mmc_hostname(card->host), err); goto out; } diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index fb1062a6394c..5e19a961c34d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -14,6 +14,15 @@ config MMC_DEBUG added host drivers please don't invent their private macro for debugging. +config MMC_SUNPLUS + tristate "Sunplus SP7021 MMC Controller" + depends on ARCH_SUNPLUS || COMPILE_TEST + help + If you say yes here, you will get support for eMMC host interface + on Sunplus SoCs. + + If unsure, say N + config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" depends on ARM_AMBA @@ -1040,10 +1049,10 @@ config MMC_SDHCI_MICROCHIP_PIC32 config MMC_SDHCI_BRCMSTB tristate "Broadcom SDIO/SD/MMC support" - depends on ARCH_BRCMSTB || BMIPS_GENERIC + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST depends on MMC_SDHCI_PLTFM select MMC_CQHCI - default y + default ARCH_BRCMSTB || BMIPS_GENERIC help This selects support for the SDIO/SD/MMC Host Controller on Broadcom STB SoCs. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 4e4ceb32c4b4..ba0c6d0cd85d 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o +obj-$(CONFIG_MMC_SUNPLUS) += sunplus-mmc.o obj-$(CONFIG_MMC_CQHCI) += cqhci.o cqhci-y += cqhci-core.o cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c index bfb8efeb7eb8..d01df01d4b4d 100644 --- a/drivers/mmc/host/alcor.c +++ b/drivers/mmc/host/alcor.c @@ -1114,7 +1114,10 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) alcor_hw_init(host); dev_set_drvdata(&pdev->dev, host); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto free_host; + return 0; free_host: diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 91d52ba7a39f..bb9bbf1c927b 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2222,6 +2222,7 @@ static int atmci_init_slot(struct atmel_mci *host, { struct mmc_host *mmc; struct atmel_mci_slot *slot; + int ret; mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev); if (!mmc) @@ -2305,11 +2306,13 @@ static int atmci_init_slot(struct atmel_mci *host, host->slot[id] = slot; mmc_regulator_get_supply(mmc); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) { + mmc_free_host(mmc); + return ret; + } if (gpio_is_valid(slot->detect_pin)) { - int ret; - timer_setup(&slot->detect_timer, atmci_detect_change, 0); ret = request_irq(gpio_to_irq(slot->detect_pin), diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index c88b039dc9fb..82dd0ae40305 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -388,7 +388,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host) /* This is the pointer to the data buffer */ sg = &data->sg[host->pio.index]; - sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset; + sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset; /* This is the space left inside the buffer */ sg_len = data->sg[host->pio.index].length - host->pio.offset; @@ -409,7 +409,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host) __raw_writel((unsigned long)val, HOST_TXPORT(host)); wmb(); /* drain writebuffer */ } - kunmap_atomic(sg_ptr); + kunmap_local(sg_ptr); host->pio.len -= count; host->pio.offset += count; @@ -446,7 +446,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) if (host->pio.index < host->dma.len) { sg = &data->sg[host->pio.index]; - sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset; + sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset; /* This is the space left inside the buffer */ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; @@ -488,7 +488,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) sg_ptr[count] = (unsigned char)(val & 0xFF); } if (sg_ptr) - kunmap_atomic(sg_ptr); + kunmap_local(sg_ptr); host->pio.len -= count; host->pio.offset += count; diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 641ab4f42125..8648f7e63ca1 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -327,7 +327,6 @@ static void bcm2835_dma_complete(void *param) static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read) { - unsigned long flags; size_t blksize; unsigned long wait_max; @@ -335,8 +334,6 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read) wait_max = jiffies + msecs_to_jiffies(500); - local_irq_save(flags); - while (blksize) { int copy_words; u32 hsts = 0; @@ -421,8 +418,6 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read) } sg_miter_stop(&host->sg_miter); - - local_irq_restore(flags); } static void bcm2835_transfer_pio(struct bcm2835_host *host) @@ -1068,7 +1063,6 @@ static void bcm2835_dma_complete_work(struct work_struct *work) } if (host->drain_words) { - unsigned long flags; void *page; u32 *buf; @@ -1076,8 +1070,7 @@ static void bcm2835_dma_complete_work(struct work_struct *work) host->drain_page += host->drain_offset >> PAGE_SHIFT; host->drain_offset &= ~PAGE_MASK; } - local_irq_save(flags); - page = kmap_atomic(host->drain_page); + page = kmap_local_page(host->drain_page); buf = page + host->drain_offset; while (host->drain_words) { @@ -1088,8 +1081,7 @@ static void bcm2835_dma_complete_work(struct work_struct *work) host->drain_words--; } - kunmap_atomic(page); - local_irq_restore(flags); + kunmap_local(page); } bcm2835_finish_data(host); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 9901208be797..13e55cff8237 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -17,10 +17,16 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/of.h> +#include <linux/mfd/altera-sysmgr.h> +#include <linux/regmap.h> #include "dw_mmc.h" #include "dw_mmc-pltfm.h" +#define SOCFPGA_DW_MMC_CLK_PHASE_STEP 45 +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel, reg_shift) \ + ((((smplsel) & 0x7) << reg_shift) | (((drvsel) & 0x7) << 0)) + int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data) { @@ -62,9 +68,42 @@ const struct dev_pm_ops dw_mci_pltfm_pmops = { }; 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 regmap *sys_mgr_base_addr; + u32 clk_phase[2] = {0}, reg_offset, reg_shift; + int i, rc, hs_timing; + + rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0); + if (rc < 0) + return 0; + + sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); + if (IS_ERR(sys_mgr_base_addr)) { + dev_warn(host->dev, "clk-phase-sd-hs was specified, but failed to find altr,sys-mgr regmap!\n"); + return 0; + } + + 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; + + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift); + regmap_write(sys_mgr_base_addr, reg_offset, hs_timing); + + return 0; +} + +static const struct dw_mci_drv_data socfpga_drv_data = { + .init = dw_mci_socfpga_priv_init, +}; + static const struct of_device_id dw_mci_pltfm_match[] = { { .compatible = "snps,dw-mshc", }, - { .compatible = "altr,socfpga-dw-mshc", }, + { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data, }, { .compatible = "img,pistachio-dw-mshc", }, {}, }; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6ef410053037..829af2c98a44 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -334,8 +334,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) cmdr == MMC_READ_MULTIPLE_BLOCK || cmdr == MMC_WRITE_BLOCK || cmdr == MMC_WRITE_MULTIPLE_BLOCK || - cmdr == MMC_SEND_TUNING_BLOCK || - cmdr == MMC_SEND_TUNING_BLOCK_HS200 || + mmc_op_tuning(cmdr) || cmdr == MMC_GEN_CMD) { stop->opcode = MMC_STOP_TRANSMISSION; stop->arg = 0; @@ -1363,7 +1362,7 @@ static void __dw_mci_start_request(struct dw_mci *host, * is just about to roll over. * * We do this whole thing under spinlock and only if the - * command hasn't already completed (indicating the the irq + * command hasn't already completed (indicating the irq * already ran so we don't want the timeout). */ spin_lock_irqsave(&host->irq_lock, irqflags); diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index 6ba0d63b8c07..39c6707fdfdb 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -502,6 +502,7 @@ static int litex_mmc_irq_init(struct platform_device *pdev, use_polling: host->mmc->caps |= MMC_CAP_NEEDS_POLL; + host->irq = 0; return 0; } diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index df05e60bed9a..6e5ea0213b47 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1335,7 +1335,9 @@ static int meson_mmc_probe(struct platform_device *pdev) } mmc->ops = &meson_mmc_ops; - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto err_free_irq; return 0; diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c index 9d35453e7371..424dc7b07858 100644 --- a/drivers/mmc/host/mmc_hsq.c +++ b/drivers/mmc/host/mmc_hsq.c @@ -13,9 +13,6 @@ #include "mmc_hsq.h" -#define HSQ_NUM_SLOTS 64 -#define HSQ_INVALID_TAG HSQ_NUM_SLOTS - static void mmc_hsq_retry_handler(struct work_struct *work) { struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work); @@ -73,7 +70,6 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq) static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains) { - struct hsq_slot *slot; int tag; /* @@ -82,29 +78,12 @@ static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains) */ if (!remains) { hsq->next_tag = HSQ_INVALID_TAG; + hsq->tail_tag = HSQ_INVALID_TAG; return; } - /* - * Increasing the next tag and check if the corresponding request is - * available, if yes, then we found a candidate request. - */ - if (++hsq->next_tag != HSQ_INVALID_TAG) { - slot = &hsq->slot[hsq->next_tag]; - if (slot->mrq) - return; - } - - /* Othersie we should iterate all slots to find a available tag. */ - for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) { - slot = &hsq->slot[tag]; - if (slot->mrq) - break; - } - - if (tag == HSQ_NUM_SLOTS) - tag = HSQ_INVALID_TAG; - + tag = hsq->tag_slot[hsq->next_tag]; + hsq->tag_slot[hsq->next_tag] = HSQ_INVALID_TAG; hsq->next_tag = tag; } @@ -233,8 +212,14 @@ static int mmc_hsq_request(struct mmc_host *mmc, struct mmc_request *mrq) * Set the next tag as current request tag if no available * next tag. */ - if (hsq->next_tag == HSQ_INVALID_TAG) + if (hsq->next_tag == HSQ_INVALID_TAG) { hsq->next_tag = tag; + hsq->tail_tag = tag; + hsq->tag_slot[hsq->tail_tag] = HSQ_INVALID_TAG; + } else { + hsq->tag_slot[hsq->tail_tag] = tag; + hsq->tail_tag = tag; + } hsq->qcnt++; @@ -339,8 +324,10 @@ static const struct mmc_cqe_ops mmc_hsq_ops = { int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) { + int i; hsq->num_slots = HSQ_NUM_SLOTS; hsq->next_tag = HSQ_INVALID_TAG; + hsq->tail_tag = HSQ_INVALID_TAG; hsq->slot = devm_kcalloc(mmc_dev(mmc), hsq->num_slots, sizeof(struct hsq_slot), GFP_KERNEL); @@ -351,6 +338,9 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc) hsq->mmc->cqe_private = hsq; mmc->cqe_ops = &mmc_hsq_ops; + for (i = 0; i < HSQ_NUM_SLOTS; i++) + hsq->tag_slot[i] = HSQ_INVALID_TAG; + INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler); spin_lock_init(&hsq->lock); init_waitqueue_head(&hsq->wait_queue); diff --git a/drivers/mmc/host/mmc_hsq.h b/drivers/mmc/host/mmc_hsq.h index ffdd9cd172c3..1808024fc6c5 100644 --- a/drivers/mmc/host/mmc_hsq.h +++ b/drivers/mmc/host/mmc_hsq.h @@ -2,6 +2,9 @@ #ifndef LINUX_MMC_HSQ_H #define LINUX_MMC_HSQ_H +#define HSQ_NUM_SLOTS 64 +#define HSQ_INVALID_TAG HSQ_NUM_SLOTS + struct hsq_slot { struct mmc_request *mrq; }; @@ -17,6 +20,8 @@ struct mmc_hsq { int next_tag; int num_slots; int qcnt; + int tail_tag; + int tag_slot[HSQ_NUM_SLOTS]; bool enabled; bool waiting_for_idle; diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 012aa85489d8..b9e5dfe74e5c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2256,7 +2256,9 @@ static int mmci_probe(struct amba_device *dev, pm_runtime_set_autosuspend_delay(&dev->dev, 50); pm_runtime_use_autosuspend(&dev->dev); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto clk_disable; pm_runtime_put(&dev->dev); return 0; diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index dfc3ffd5b1f8..52ed30f2d9f4 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -665,7 +665,9 @@ static int moxart_probe(struct platform_device *pdev) goto out; dev_set_drvdata(dev, mmc); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto out; dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width); diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 26bc59b5a7cc..edade0e54a0c 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -452,6 +452,7 @@ struct msdc_host { struct clk *bus_clk; /* bus clock which used to access register */ struct clk *src_clk_cg; /* msdc source clock control gate */ struct clk *sys_clk_cg; /* msdc subsys clock control gate */ + struct clk *crypto_clk; /* msdc crypto clock control gate */ struct clk_bulk_data bulk_clks[MSDC_NR_CLOCKS]; u32 mclk; /* mmc subsystem clock frequency */ u32 src_clk_freq; /* source clock frequency */ @@ -552,6 +553,19 @@ static const struct mtk_mmc_compatible mt7622_compat = { .support_64g = false, }; +static const struct mtk_mmc_compatible mt7986_compat = { + .clk_div_bits = 12, + .recheck_sdio_irq = true, + .hs400_tune = false, + .pad_tune_reg = MSDC_PAD_TUNE0, + .async_fifo = true, + .data_tune = true, + .busy_check = true, + .stop_clk_fix = true, + .enhance_rx = true, + .support_64g = true, +}; + static const struct mtk_mmc_compatible mt8135_compat = { .clk_div_bits = 8, .recheck_sdio_irq = true, @@ -609,6 +623,7 @@ static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt6795-mmc", .data = &mt6795_compat}, { .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat}, { .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat}, + { .compatible = "mediatek,mt7986-mmc", .data = &mt7986_compat}, { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat}, { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat}, { .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat}, @@ -735,7 +750,7 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, else bd[j].bd_info &= ~BDMA_DESC_EOL; - /* checksume need to clear first */ + /* checksum need to clear first */ bd[j].bd_info &= ~BDMA_DESC_CHECKSUM; bd[j].bd_info |= msdc_dma_calcs((u8 *)(&bd[j]), 16) << 8; } @@ -826,6 +841,7 @@ static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks) static void msdc_gate_clock(struct msdc_host *host) { clk_bulk_disable_unprepare(MSDC_NR_CLOCKS, host->bulk_clks); + clk_disable_unprepare(host->crypto_clk); clk_disable_unprepare(host->src_clk_cg); clk_disable_unprepare(host->src_clk); clk_disable_unprepare(host->bus_clk); @@ -841,6 +857,7 @@ static int msdc_ungate_clock(struct msdc_host *host) clk_prepare_enable(host->bus_clk); clk_prepare_enable(host->src_clk); clk_prepare_enable(host->src_clk_cg); + clk_prepare_enable(host->crypto_clk); ret = clk_bulk_prepare_enable(MSDC_NR_CLOCKS, host->bulk_clks); if (ret) { dev_err(host->dev, "Cannot enable pclk/axi/ahb clock gates\n"); @@ -1207,12 +1224,10 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, if (!sbc_error && !(events & MSDC_INT_CMDRDY)) { if (events & MSDC_INT_CMDTMO || - (cmd->opcode != MMC_SEND_TUNING_BLOCK && - cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 && - !host->hs400_tuning)) + (!mmc_op_tuning(cmd->opcode) && !host->hs400_tuning)) /* * should not clear fifo/interrupt as the tune data - * may have alreay come when cmd19/cmd21 gets response + * may have already come when cmd19/cmd21 gets response * CRC error. */ msdc_reset_hw(host); @@ -1303,9 +1318,7 @@ static void msdc_cmd_next(struct msdc_host *host, { if ((cmd->error && !(cmd->error == -EILSEQ && - (cmd->opcode == MMC_SEND_TUNING_BLOCK || - cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 || - host->hs400_tuning))) || + (mmc_op_tuning(cmd->opcode) || host->hs400_tuning))) || (mrq->sbc && mrq->sbc->error)) msdc_request_done(host, mrq); else if (cmd == mrq->sbc) @@ -2656,6 +2669,15 @@ static int msdc_drv_probe(struct platform_device *pdev) goto host_free; } + /* only eMMC has crypto property */ + if (!(mmc->caps2 & MMC_CAP2_NO_MMC)) { + host->crypto_clk = devm_clk_get_optional(&pdev->dev, "crypto"); + if (IS_ERR(host->crypto_clk)) + host->crypto_clk = NULL; + else + mmc->caps2 |= MMC_CAP2_CRYPTO; + } + host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { ret = -EINVAL; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 2cf0413407ea..668f865f3efb 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1143,7 +1143,9 @@ static int mxcmci_probe(struct platform_device *pdev) timer_setup(&host->watchdog, mxcmci_watchdog, 0); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto out_free_dma; return 0; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index fca30add563e..4bd744755205 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1946,7 +1946,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (!ret) mmc->caps |= MMC_CAP_SDIO_IRQ; - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto err_irq; if (mmc_pdata(host)->name != NULL) { ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index e4003f6058eb..2a988f942b6c 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -763,7 +763,12 @@ static int pxamci_probe(struct platform_device *pdev) dev_warn(dev, "gpio_ro and get_ro() both defined\n"); } - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) { + if (host->pdata && host->pdata->exit) + host->pdata->exit(dev, mmc); + goto out; + } return 0; diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index c4abfee1ebae..68da3da9e2e5 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -38,12 +38,15 @@ struct renesas_sdhi_of_data { #define SDHI_CALIB_TABLE_MAX 32 +#define sdhi_has_quirk(p, q) ((p)->quirks && (p)->quirks->q) + struct renesas_sdhi_quirks { bool hs400_disabled; bool hs400_4taps; bool fixed_addr_mode; bool dma_one_rx_only; bool manual_tap_correction; + bool old_info1_layout; u32 hs400_bad_taps; const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX]; }; @@ -53,12 +56,17 @@ struct renesas_sdhi_of_data_with_quirks { const struct renesas_sdhi_quirks *quirks; }; -struct tmio_mmc_dma { +/* We want both end_flags to be set before we mark DMA as finished */ +#define SDHI_DMA_END_FLAG_DMA 0 +#define SDHI_DMA_END_FLAG_ACCESS 1 + +struct renesas_sdhi_dma { + unsigned long end_flags; enum dma_slave_buswidth dma_buswidth; bool (*filter)(struct dma_chan *chan, void *arg); void (*enable)(struct tmio_mmc_host *host, bool enable); - struct completion dma_dataend; - struct tasklet_struct dma_complete; + struct completion dma_dataend; + struct tasklet_struct dma_complete; }; struct renesas_sdhi { @@ -66,7 +74,7 @@ struct renesas_sdhi { struct clk *clkh; struct clk *clk_cd; struct tmio_mmc_data mmc_data; - struct tmio_mmc_dma dma_priv; + struct renesas_sdhi_dma dma_priv; const struct renesas_sdhi_quirks *quirks; struct pinctrl *pinctrl; struct pinctrl_state *pins_default, *pins_uhs; diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index b970699743e0..345934e4f59e 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -141,7 +141,7 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host, if (priv->clkh) { /* HS400 with 4TAP needs different clock settings */ - bool use_4tap = priv->quirks && priv->quirks->hs400_4taps; + bool use_4tap = sdhi_has_quirk(priv, hs400_4taps); bool need_slow_clkh = host->mmc->ios.timing == MMC_TIMING_MMC_HS400; clkh_shift = use_4tap && need_slow_clkh ? 1 : 2; ref_clk = priv->clkh; @@ -383,7 +383,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) struct tmio_mmc_host *host = mmc_priv(mmc); struct renesas_sdhi *priv = host_to_priv(host); u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0; - bool use_4tap = priv->quirks && priv->quirks->hs400_4taps; + bool use_4tap = sdhi_has_quirk(priv, hs400_4taps); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN & sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL)); @@ -395,7 +395,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos_hs400); - if (priv->quirks && priv->quirks->manual_tap_correction) + if (sdhi_has_quirk(priv, manual_tap_correction)) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); @@ -546,7 +546,7 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host, SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2)); - if (priv->adjust_hs400_calib_table) + if (sdhi_has_quirk(priv, hs400_calib_table) || sdhi_has_quirk(priv, hs400_bad_taps)) renesas_sdhi_adjust_hs400_mode_disable(host); sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN | @@ -732,7 +732,7 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); /* Change TAP position according to correction status */ - if (priv->quirks && priv->quirks->manual_tap_correction && + if (sdhi_has_quirk(priv, manual_tap_correction) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0; /* @@ -796,7 +796,7 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, struct mmc_request *mrq) { struct renesas_sdhi *priv = host_to_priv(host); - bool use_4tap = priv->quirks && priv->quirks->hs400_4taps; + bool use_4tap = sdhi_has_quirk(priv, hs400_4taps); bool ret = false; /* @@ -908,7 +908,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, { struct tmio_mmc_data *mmd = pdev->dev.platform_data; struct tmio_mmc_data *mmc_data; - struct tmio_mmc_dma *dma_priv; + struct renesas_sdhi_dma *dma_priv; struct tmio_mmc_host *host; struct renesas_sdhi *priv; int num_irqs, irq, ret, i; @@ -990,7 +990,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->multi_io_quirk = renesas_sdhi_multi_io_quirk; host->dma_ops = dma_ops; - if (quirks && quirks->hs400_disabled) + if (sdhi_has_quirk(priv, hs400_disabled)) host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); /* For some SoC, we disable internal WP. GPIO may override this */ @@ -1018,7 +1018,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, dma_priv->filter = shdma_chan_filter; dma_priv->enable = renesas_sdhi_enable_dma; - mmc_data->alignment_shift = 1; /* 2-byte alignment */ mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED; /* @@ -1056,7 +1055,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (ver == SDHI_VER_GEN2_SDR50) mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY; - if (ver == SDHI_VER_GEN3_SDMMC && quirks && quirks->hs400_calib_table) { + if (ver == SDHI_VER_GEN3_SDMMC && sdhi_has_quirk(priv, hs400_calib_table)) { host->fixup_request = renesas_sdhi_fixup_request; priv->adjust_hs400_calib_table = *( res->start == SDHI_GEN3_MMC0_ADDR ? @@ -1068,13 +1067,15 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (ver >= SDHI_VER_GEN3_SD) host->get_timeout_cycles = renesas_sdhi_gen3_get_cycles; + /* Check for SCC so we can reset it if needed */ + if (of_data && of_data->scc_offset && ver >= SDHI_VER_GEN2_SDR104) + priv->scc_ctl = host->ctl + of_data->scc_offset; + /* Enable tuning iff we have an SCC and a supported mode */ - if (of_data && of_data->scc_offset && - (host->mmc->caps & MMC_CAP_UHS_SDR104 || - host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR | - MMC_CAP2_HS400_1_8V))) { + if (priv->scc_ctl && (host->mmc->caps & MMC_CAP_UHS_SDR104 || + host->mmc->caps2 & MMC_CAP2_HSX00_1_8V)) { const struct renesas_sdhi_scc *taps = of_data->taps; - bool use_4tap = quirks && quirks->hs400_4taps; + bool use_4tap = sdhi_has_quirk(priv, hs400_4taps); bool hit = false; for (i = 0; i < of_data->taps_num; i++) { @@ -1092,7 +1093,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (!hit) dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); - priv->scc_ctl = host->ctl + of_data->scc_offset; host->check_retune = renesas_sdhi_check_scc_error; host->ops.execute_tuning = renesas_sdhi_execute_tuning; host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 42937596c4c4..29f562115c66 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -47,9 +47,9 @@ #define RST_RESERVED_BITS GENMASK_ULL(31, 0) /* DM_CM_INFO1 and DM_CM_INFO1_MASK */ -#define INFO1_CLEAR 0 #define INFO1_MASK_CLEAR GENMASK_ULL(31, 0) -#define INFO1_DTRANEND1 BIT(17) +#define INFO1_DTRANEND1 BIT(20) +#define INFO1_DTRANEND1_OLD BIT(17) #define INFO1_DTRANEND0 BIT(16) /* DM_CM_INFO2 and DM_CM_INFO2_MASK */ @@ -165,6 +165,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400_one_rx = { .hs400_disabled = true, .hs400_4taps = true, .dma_one_rx_only = true, + .old_info1_layout = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_4tap = { @@ -280,23 +281,17 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match); static void -renesas_sdhi_internal_dmac_dm_write(struct tmio_mmc_host *host, - int addr, u64 val) -{ - writeq(val, host->ctl + addr); -} - -static void renesas_sdhi_internal_dmac_enable_dma(struct tmio_mmc_host *host, bool enable) { struct renesas_sdhi *priv = host_to_priv(host); + u32 dma_irqs = INFO1_DTRANEND0 | + (sdhi_has_quirk(priv, old_info1_layout) ? + INFO1_DTRANEND1_OLD : INFO1_DTRANEND1); if (!host->chan_tx || !host->chan_rx) return; - if (!enable) - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1, - INFO1_CLEAR); + writel(enable ? ~dma_irqs : INFO1_MASK_CLEAR, host->ctl + DM_CM_INFO1_MASK); if (priv->dma_priv.enable) priv->dma_priv.enable(host, enable); @@ -309,22 +304,44 @@ renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host) renesas_sdhi_internal_dmac_enable_dma(host, false); - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST, - RST_RESERVED_BITS & ~val); - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST, - RST_RESERVED_BITS | val); + writel(RST_RESERVED_BITS & ~val, host->ctl + DM_CM_RST); + writel(RST_RESERVED_BITS | val, host->ctl + DM_CM_RST); clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags); renesas_sdhi_internal_dmac_enable_dma(host, true); } +static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host) +{ + struct renesas_sdhi *priv = host_to_priv(host); + struct renesas_sdhi_dma *dma_priv = &priv->dma_priv; + + u32 dma_irqs = INFO1_DTRANEND0 | + (sdhi_has_quirk(priv, old_info1_layout) ? + INFO1_DTRANEND1_OLD : INFO1_DTRANEND1); + u32 status = readl(host->ctl + DM_CM_INFO1); + + if (status & dma_irqs) { + writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1); + set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags); + if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags)) + tasklet_schedule(&dma_priv->dma_complete); + } + + return status & dma_irqs; +} + static void renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) { struct renesas_sdhi *priv = host_to_priv(host); + struct renesas_sdhi_dma *dma_priv = &priv->dma_priv; - tasklet_schedule(&priv->dma_priv.dma_complete); + set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags); + if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) || + host->data->error) + tasklet_schedule(&dma_priv->dma_complete); } /* @@ -379,7 +396,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, struct scatterlist *sg = host->sg_ptr; u32 dtran_mode = DTRAN_MODE_BUS_WIDTH; - if (!(priv->quirks && priv->quirks->fixed_addr_mode)) + if (!sdhi_has_quirk(priv, fixed_addr_mode)) dtran_mode |= DTRAN_MODE_ADDR_MODE; if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED)) @@ -387,20 +404,19 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; - if (priv->quirks && priv->quirks->dma_one_rx_only && + if (sdhi_has_quirk(priv, dma_one_rx_only) && test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags)) goto force_pio_with_unmap; } else { dtran_mode |= DTRAN_MODE_CH_NUM_CH0; } + priv->dma_priv.end_flags = 0; renesas_sdhi_internal_dmac_enable_dma(host, true); /* set dma parameters */ - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE, - dtran_mode); - renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR, - sg_dma_address(sg)); + writel(dtran_mode, host->ctl + DM_CM_DTRAN_MODE); + writel(sg_dma_address(sg), host->ctl + DM_DTRAN_ADDR); host->dma_on = true; @@ -416,12 +432,19 @@ force_pio: static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg) { struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg; + struct renesas_sdhi *priv = host_to_priv(host); tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); - /* start the DMAC */ - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_CTRL, - DTRAN_CTRL_DM_START); + if (!host->cmd->error) { + /* start the DMAC */ + writel(DTRAN_CTRL_DM_START, host->ctl + DM_CM_DTRAN_CTRL); + } else { + /* on CMD errors, simulate DMA end immediately */ + set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags); + if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags)) + tasklet_schedule(&priv->dma_priv.dma_complete); + } } static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host) @@ -501,11 +524,11 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host, { struct renesas_sdhi *priv = host_to_priv(host); - /* Disable DMAC interrupts, we don't use them */ - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1_MASK, - INFO1_MASK_CLEAR); - renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO2_MASK, - INFO2_MASK_CLEAR); + /* Disable DMAC interrupts initially */ + writel(INFO1_MASK_CLEAR, host->ctl + DM_CM_INFO1_MASK); + writel(INFO2_MASK_CLEAR, host->ctl + DM_CM_INFO2_MASK); + writel(0, host->ctl + DM_CM_INFO1); + writel(0, host->ctl + DM_CM_INFO2); /* Each value is set to non-zero to assume "enabling" each DMA */ host->chan_rx = host->chan_tx = (void *)0xdeadbeaf; @@ -537,6 +560,7 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = { .abort = renesas_sdhi_internal_dmac_abort_dma, .dataend = renesas_sdhi_internal_dmac_dataend_dma, .end = renesas_sdhi_internal_dmac_end_dma, + .dma_irq = renesas_sdhi_internal_dmac_dma_irq, }; static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev) diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 99e3426df702..b559ad38b667 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -160,7 +160,7 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host) dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << host->pdata->alignment_shift) - 1; + unsigned int align = 1; /* 2-byte alignment */ for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -232,7 +232,7 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host) dma_cookie_t cookie; int ret, i; bool aligned = true, multiple = true; - unsigned int align = (1 << host->pdata->alignment_shift) - 1; + unsigned int align = 1; /* 2-byte alignment */ for_each_sg(sg, sg_tmp, host->sg_len, i) { if (sg_tmp->offset & align) @@ -254,12 +254,11 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host) /* The only sg element can be unaligned, use our bounce buffer then */ if (!aligned) { - unsigned long flags; - void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags); + void *sg_vaddr = kmap_local_page(sg_page(sg)); sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length); - memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length); - tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr); + memcpy(host->bounce_buf, sg_vaddr + sg->offset, host->bounce_sg.length); + kunmap_local(sg_vaddr); host->sg_ptr = &host->bounce_sg; sg = host->sg_ptr; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index e1580f78c6b2..8098726dcc0b 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1474,6 +1474,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) struct realtek_pci_sdmmc *host; struct rtsx_pcr *pcr; struct pcr_handle *handle = pdev->dev.platform_data; + int ret; if (!handle) return -ENXIO; @@ -1511,7 +1512,13 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) { + pm_runtime_dont_use_autosuspend(&pdev->dev); + pm_runtime_disable(&pdev->dev); + mmc_free_host(mmc); + return ret; + } return 0; } diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 5798aee06653..2c650cd58693 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1329,6 +1329,7 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) #ifdef RTSX_USB_USE_LEDS_CLASS int err; #endif + int ret; ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent)); if (!ucr) @@ -1365,7 +1366,15 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) INIT_WORK(&host->led_work, rtsx_usb_update_led); #endif - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) { +#ifdef RTSX_USB_USE_LEDS_CLASS + led_classdev_unregister(&host->led); +#endif + mmc_free_host(mmc); + pm_runtime_disable(&pdev->dev); + return ret; + } return 0; } diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 4cca4c90769b..8f0e639236b1 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -648,10 +648,10 @@ static int sdhci_acpi_emmc_amd_probe_slot(struct platform_device *pdev, * in reading a garbage value and using the wrong presets. * * Since HS400 and HS200 presets must be identical, we could - * instead use the the SDR104 preset register. + * instead use the SDR104 preset register. * * If the above issues are resolved we could remove this quirk for - * firmware that that has valid presets (i.e., SDR12 <= 12 MHz). + * firmware that has valid presets (i.e., SDR12 <= 12 MHz). */ host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 61a12f2f7f03..6a93a54fe067 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -168,7 +168,7 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host, /* * JEDEC and SD spec specify supplying 74 continuous clocks to * device after power up. With minimum bus (100KHz) that - * that translates to 740us + * translates to 740us */ if (power_mode != MMC_POWER_OFF) udelay(740); diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 55d8bd232695..f2cf3d70db79 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -179,7 +179,7 @@ static const struct brcmstb_match_priv match_priv_7216 = { .ops = &sdhci_brcmstb_ops_7216, }; -static const struct of_device_id sdhci_brcm_of_match[] = { +static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ffeb5759830f..89ef0c80ac37 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -307,7 +307,8 @@ static struct esdhc_soc_data usdhc_imx7ulp_data = { | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static struct esdhc_soc_data usdhc_imxrt1050_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_HS200 | ESDHC_FLAG_ERR004536, + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, }; static struct esdhc_soc_data usdhc_imx8qxp_data = { @@ -1012,6 +1013,44 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width) SDHCI_HOST_CONTROL); } +static void esdhc_reset_tuning(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + u32 ctrl; + int ret; + + /* Reset the tuning circuit */ + if (esdhc_is_usdhc(imx_data)) { + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { + ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL); + ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); + writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { + ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); + ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE; + writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS); + /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */ + ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS, + ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "Warning! clear execute tuning bit failed\n"); + /* + * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the + * usdhc IP internal logic flag execute_tuning_with_clr_buf, which + * will finally make sure the normal data transfer logic correct. + */ + ctrl = readl(host->ioaddr + SDHCI_INT_STATUS); + ctrl |= SDHCI_INT_DATA_AVAIL; + writel(ctrl, host->ioaddr + SDHCI_INT_STATUS); + } + } +} + static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); @@ -1023,6 +1062,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) if (host->timing == MMC_TIMING_UHS_DDR50) return 0; + /* + * Reset tuning circuit logic. If not, the previous tuning result + * will impact current tuning, make current tuning can't set the + * correct delay cell. + */ + esdhc_reset_tuning(host); return sdhci_execute_tuning(mmc, opcode); } @@ -1196,44 +1241,6 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host) "warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v); } -static void esdhc_reset_tuning(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - u32 ctrl; - int ret; - - /* Reset the tuning circuit */ - if (esdhc_is_usdhc(imx_data)) { - if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { - ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL); - ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL; - writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); - writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); - } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { - ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); - ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE; - writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS); - /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */ - ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS, - ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50); - if (ret == -ETIMEDOUT) - dev_warn(mmc_dev(host->mmc), - "Warning! clear execute tuning bit failed\n"); - /* - * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the - * usdhc IP internal logic flag execute_tuning_with_clr_buf, which - * will finally make sure the normal data transfer logic correct. - */ - ctrl = readl(host->ioaddr + SDHCI_INT_STATUS); - ctrl |= SDHCI_INT_DATA_AVAIL; - writel(ctrl, host->ioaddr + SDHCI_INT_STATUS); - } - } -} - static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) { u32 m; @@ -1454,7 +1461,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) /* * On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card - * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the + * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let * the 1st linux configure power/clock for the 2nd Linux. * * When the 2nd Linux is booting into rootfs stage, we let the 1st Linux diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3a091a387ecb..4ac8651d0b29 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -19,6 +19,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/reset.h> +#include "sdhci-cqhci.h" #include "sdhci-pltfm.h" #include "cqhci.h" @@ -2218,8 +2219,7 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) if (!msm_host->use_cdr) break; if ((msm_host->transfer_mode & SDHCI_TRNS_READ) && - SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK_HS200 && - SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK) + !mmc_op_tuning(SDHCI_GET_CMD(val))) sdhci_msm_set_cdr(host, true); else sdhci_msm_set_cdr(host, false); @@ -2304,13 +2304,6 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } -static void sdhci_msm_reset(struct sdhci_host *host, u8 mask) -{ - if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL)) - cqhci_deactivate(host->mmc); - sdhci_reset(host, mask); -} - static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host) { int ret; @@ -2450,7 +2443,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = { MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); static const struct sdhci_ops sdhci_msm_ops = { - .reset = sdhci_msm_reset, + .reset = sdhci_and_cqhci_reset, .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index cfb891430174..89c431a34c43 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -21,6 +21,7 @@ #include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/regmap.h> +#include <linux/reset.h> #include <linux/of.h> #include <linux/firmware/xlnx-zynqmp.h> @@ -1522,6 +1523,65 @@ static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan, return 0; } +static int sdhci_zynqmp_set_dynamic_config(struct device *dev, + struct sdhci_arasan_data *sdhci_arasan) +{ + struct sdhci_host *host = sdhci_arasan->host; + struct clk_hw *hw = &sdhci_arasan->clk_data.sdcardclk_hw; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + const char *clk_name = clk_hw_get_name(hw); + u32 mhz, node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1; + struct reset_control *rstc; + int ret; + + /* Obtain SDHC reset control */ + rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(rstc)) { + dev_err(dev, "Cannot get SDHC reset.\n"); + return PTR_ERR(rstc); + } + + ret = reset_control_assert(rstc); + if (ret) + return ret; + + ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_FIXED, 0); + if (ret) + return ret; + + ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_EMMC_SEL, + !!(host->mmc->caps & MMC_CAP_NONREMOVABLE)); + if (ret) + return ret; + + mhz = DIV_ROUND_CLOSEST_ULL(clk_get_rate(pltfm_host->clk), 1000000); + if (mhz > 100 && mhz <= 200) + mhz = 200; + else if (mhz > 50 && mhz <= 100) + mhz = 100; + else if (mhz > 25 && mhz <= 50) + mhz = 50; + else + mhz = 25; + + ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz); + if (ret) + return ret; + + ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_8BIT, + !!(host->mmc->caps & MMC_CAP_8_BIT_DATA)); + if (ret) + return ret; + + ret = reset_control_deassert(rstc); + if (ret) + return ret; + + usleep_range(1000, 1500); + + return 0; +} + static int sdhci_arasan_add_host(struct sdhci_arasan_data *sdhci_arasan) { struct sdhci_host *host = sdhci_arasan->host; @@ -1686,6 +1746,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev) goto unreg_clk; } + if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) { + ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_SD_CONFIG); + if (!ret) { + ret = sdhci_zynqmp_set_dynamic_config(dev, sdhci_arasan); + if (ret) + goto unreg_clk; + } + } + sdhci_arasan->phy = ERR_PTR(-ENODEV); if (of_device_is_compatible(np, "arasan,sdhci-5.1")) { sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan"); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index e0266638381d..4712adac7f7c 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -42,6 +42,12 @@ static const struct esdhc_clk_fixup ls1021a_esdhc_clk = { .max_clk[MMC_TIMING_SD_HS] = 46500000, }; +static const struct esdhc_clk_fixup ls1043a_esdhc_clk = { + .sd_dflt_max_clk = 25000000, + .max_clk[MMC_TIMING_UHS_SDR104] = 116700000, + .max_clk[MMC_TIMING_MMC_HS200] = 116700000, +}; + static const struct esdhc_clk_fixup ls1046a_esdhc_clk = { .sd_dflt_max_clk = 25000000, .max_clk[MMC_TIMING_UHS_SDR104] = 167000000, @@ -63,6 +69,7 @@ static const struct esdhc_clk_fixup p1010_esdhc_clk = { static const struct of_device_id sdhci_esdhc_of_match[] = { { .compatible = "fsl,ls1021a-esdhc", .data = &ls1021a_esdhc_clk}, + { .compatible = "fsl,ls1043a-esdhc", .data = &ls1043a_esdhc_clk}, { .compatible = "fsl,ls1046a-esdhc", .data = &ls1046a_esdhc_clk}, { .compatible = "fsl,ls1012a-esdhc", .data = &ls1012a_esdhc_clk}, { .compatible = "fsl,p1010-esdhc", .data = &p1010_esdhc_clk}, @@ -91,7 +98,7 @@ struct sdhci_esdhc { }; /** - * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register + * esdhc_readl_fixup - Fixup the value read from incompatible eSDHC register * to make it compatible with SD spec. * * @host: pointer to sdhci_host @@ -216,7 +223,7 @@ static u8 esdhc_readb_fixup(struct sdhci_host *host, } /** - * esdhc_write*_fixup - Fixup the SD spec register value so that it could be + * esdhc_writel_fixup - Fixup the SD spec register value so that it could be * written into eSDHC register. * * @host: pointer to sdhci_host diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 033be559a730..8ed9256b83da 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -370,7 +370,7 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) /* * Stage 1: Search for a maximum pass window ignoring any - * any single point failures. If the tuning value ends up + * single point failures. If the tuning value ends up * near it, move away from it in stage 2 below */ while (phase_delay <= MAX_PHASE_DELAY) { diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 28dc65023fa9..c359f867df0a 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -38,6 +38,7 @@ #include "cqhci.h" #include "sdhci.h" +#include "sdhci-cqhci.h" #include "sdhci-pci.h" static void sdhci_pci_hw_reset(struct sdhci_host *host); @@ -234,14 +235,6 @@ static void sdhci_pci_dumpregs(struct mmc_host *mmc) sdhci_dumpregs(mmc_priv(mmc)); } -static void sdhci_cqhci_reset(struct sdhci_host *host, u8 mask) -{ - if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) && - host->mmc->cqe_private) - cqhci_deactivate(host->mmc); - sdhci_reset(host, mask); -} - /*****************************************************************************\ * * * Hardware specific quirk handling * @@ -703,7 +696,7 @@ static const struct sdhci_ops sdhci_intel_glk_ops = { .set_power = sdhci_intel_set_power, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_cqhci_reset, + .reset = sdhci_and_cqhci_reset, .set_uhs_signaling = sdhci_intel_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, .irq = sdhci_cqhci_irq, @@ -2283,7 +2276,8 @@ static struct pci_driver sdhci_driver = { .probe = sdhci_pci_probe, .remove = sdhci_pci_remove, .driver = { - .pm = &sdhci_pci_pm_ops + .pm = &sdhci_pci_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index 4d509f656188..633a8ee8f8c5 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -15,6 +15,7 @@ #include <linux/of.h> #include <linux/iopoll.h> #include "sdhci.h" +#include "sdhci-cqhci.h" #include "sdhci-pci.h" #include "cqhci.h" @@ -922,14 +923,6 @@ cleanup: return ret; } -static void sdhci_gl9763e_reset(struct sdhci_host *host, u8 mask) -{ - if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) && - host->mmc->cqe_private) - cqhci_deactivate(host->mmc); - sdhci_reset(host, mask); -} - static void gli_set_gl9763e(struct sdhci_pci_slot *slot) { struct pci_dev *pdev = slot->chip->pdev; @@ -1136,7 +1129,7 @@ static const struct sdhci_ops sdhci_gl9763e_ops = { .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_gl9763e_reset, + .reset = sdhci_and_cqhci_reset, .set_uhs_signaling = sdhci_set_gl9763e_signaling, .voltage_switch = sdhci_gli_voltage_switch, .irq = sdhci_gl9763e_cqhci_irq, diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index bca1d095b759..98cadff47b2b 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -326,8 +326,7 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) (host->timing != MMC_TIMING_UHS_SDR50)) return sdhci_execute_tuning(mmc, opcode); - if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) && - (opcode != MMC_SEND_TUNING_BLOCK))) + if (WARN_ON(!mmc_op_tuning(opcode))) return -EINVAL; /* Force power mode enter L0 */ diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index bec3f9e3cd3f..525f979e2a97 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -228,13 +228,15 @@ static inline void _sdhci_sprd_set_clock(struct sdhci_host *host, div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8); sdhci_enable_clk(host, div); - /* enable auto gate sdhc_enable_auto_gate */ - val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); - mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | - SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN; - if (mask != (val & mask)) { - val |= mask; - sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI); + /* Enable CLK_AUTO when the clock is greater than 400K. */ + if (clk > 400000) { + val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI); + mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | + SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN; + if (mask != (val & mask)) { + val |= mask; + sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI); + } } } diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index c71000a07656..bff084f178c9 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -3,28 +3,30 @@ * Copyright (C) 2010 Google, Inc. */ +#include <linux/bitfield.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/module.h> +#include <linux/gpio/consumer.h> #include <linux/init.h> -#include <linux/iopoll.h> -#include <linux/platform_device.h> -#include <linux/clk.h> #include <linux/io.h> -#include <linux/of.h> +#include <linux/iommu.h> +#include <linux/iopoll.h> +#include <linux/ktime.h> +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/module.h> #include <linux/of_device.h> +#include <linux/of.h> #include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> -#include <linux/mmc/card.h> -#include <linux/mmc/host.h> -#include <linux/mmc/mmc.h> -#include <linux/mmc/slot-gpio.h> -#include <linux/gpio/consumer.h> -#include <linux/ktime.h> #include <soc/tegra/common.h> @@ -95,6 +97,8 @@ #define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec #define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31) +#define SDHCI_TEGRA_CIF2AXI_CTRL_0 0x1fc + #define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0) #define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1) #define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2) @@ -122,6 +126,7 @@ #define NVQUIRK_HAS_TMCLK BIT(10) #define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11) +#define NVQUIRK_PROGRAM_STREAMID BIT(12) /* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */ #define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000 @@ -178,6 +183,7 @@ struct sdhci_tegra { bool enable_hwcq; unsigned long curr_clk_rate; u8 tuned_tap_delay; + u32 stream_id; }; static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) @@ -268,13 +274,9 @@ static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg) { bool is_tuning_cmd = 0; bool clk_enabled; - u8 cmd; - if (reg == SDHCI_COMMAND) { - cmd = SDHCI_GET_CMD(val); - is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK || - cmd == MMC_SEND_TUNING_BLOCK_HS200; - } + if (reg == SDHCI_COMMAND) + is_tuning_cmd = mmc_op_tuning(SDHCI_GET_CMD(val)); if (is_tuning_cmd) clk_enabled = tegra_sdhci_configure_card_clk(host, 0); @@ -1526,7 +1528,8 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = { SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER, .ops = &tegra186_sdhci_ops, }; @@ -1557,7 +1560,22 @@ static const struct sdhci_tegra_soc_data soc_data_tegra194 = { .max_tap_delay = 139, }; +static const struct sdhci_tegra_soc_data soc_data_tegra234 = { + .pdata = &sdhci_tegra186_pdata, + .dma_mask = DMA_BIT_MASK(39), + .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL | + NVQUIRK_HAS_PADCALIB | + NVQUIRK_DIS_CARD_CLK_CONFIG_TAP | + NVQUIRK_ENABLE_SDR50 | + NVQUIRK_ENABLE_SDR104 | + NVQUIRK_PROGRAM_STREAMID | + NVQUIRK_HAS_TMCLK, + .min_tap_delay = 95, + .max_tap_delay = 111, +}; + static const struct of_device_id sdhci_tegra_dt_match[] = { + { .compatible = "nvidia,tegra234-sdhci", .data = &soc_data_tegra234 }, { .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 }, { .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 }, { .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 }, @@ -1617,6 +1635,19 @@ cleanup: return ret; } +/* Program MC streamID for DMA transfers */ +static void sdhci_tegra_program_stream_id(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host); + + if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID) { + tegra_sdhci_writel(host, FIELD_PREP(GENMASK(15, 8), tegra_host->stream_id) | + FIELD_PREP(GENMASK(7, 0), tegra_host->stream_id), + SDHCI_TEGRA_CIF2AXI_CTRL_0); + } +} + static int sdhci_tegra_probe(struct platform_device *pdev) { const struct sdhci_tegra_soc_data *soc_data; @@ -1677,6 +1708,12 @@ static int sdhci_tegra_probe(struct platform_device *pdev) tegra_sdhci_parse_dt(host); + if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID && + !tegra_dev_iommu_get_stream_id(&pdev->dev, &tegra_host->stream_id)) { + dev_warn(mmc_dev(host->mmc), "missing IOMMU stream ID\n"); + tegra_host->stream_id = 0x7f; + } + tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power", GPIOD_OUT_HIGH); if (IS_ERR(tegra_host->power_gpio)) { @@ -1762,6 +1799,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (rc) goto err_add_host; + sdhci_tegra_program_stream_id(host); + return 0; err_add_host: @@ -1858,6 +1897,8 @@ static int sdhci_tegra_resume(struct device *dev) if (ret) return ret; + sdhci_tegra_program_stream_id(host); + ret = sdhci_resume_host(host); if (ret) goto disable_clk; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c7ad32a75b57..f3af1bd0f7b9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -270,6 +270,11 @@ enum sdhci_reset_reason { static void sdhci_reset_for_reason(struct sdhci_host *host, enum sdhci_reset_reason reason) { + if (host->quirks2 & SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) { + sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + return; + } + switch (reason) { case SDHCI_RESET_FOR_INIT: sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); @@ -526,7 +531,6 @@ static inline bool sdhci_has_requests(struct sdhci_host *host) static void sdhci_read_block_pio(struct sdhci_host *host) { - unsigned long flags; size_t blksize, len, chunk; u32 scratch; u8 *buf; @@ -536,8 +540,6 @@ static void sdhci_read_block_pio(struct sdhci_host *host) blksize = host->data->blksz; chunk = 0; - local_irq_save(flags); - while (blksize) { BUG_ON(!sg_miter_next(&host->sg_miter)); @@ -564,13 +566,10 @@ static void sdhci_read_block_pio(struct sdhci_host *host) } sg_miter_stop(&host->sg_miter); - - local_irq_restore(flags); } static void sdhci_write_block_pio(struct sdhci_host *host) { - unsigned long flags; size_t blksize, len, chunk; u32 scratch; u8 *buf; @@ -581,8 +580,6 @@ static void sdhci_write_block_pio(struct sdhci_host *host) chunk = 0; scratch = 0; - local_irq_save(flags); - while (blksize) { BUG_ON(!sg_miter_next(&host->sg_miter)); @@ -609,8 +606,6 @@ static void sdhci_write_block_pio(struct sdhci_host *host) } sg_miter_stop(&host->sg_miter); - - local_irq_restore(flags); } static void sdhci_transfer_pio(struct sdhci_host *host) @@ -706,16 +701,14 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host, return sg_count; } -static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags) +static char *sdhci_kmap_atomic(struct scatterlist *sg) { - local_irq_save(*flags); - return kmap_atomic(sg_page(sg)) + sg->offset; + return kmap_local_page(sg_page(sg)) + sg->offset; } -static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) +static void sdhci_kunmap_atomic(void *buffer) { - kunmap_atomic(buffer); - local_irq_restore(*flags); + kunmap_local(buffer); } void sdhci_adma_write_desc(struct sdhci_host *host, void **desc, @@ -757,7 +750,6 @@ static void sdhci_adma_table_pre(struct sdhci_host *host, struct mmc_data *data, int sg_count) { struct scatterlist *sg; - unsigned long flags; dma_addr_t addr, align_addr; void *desc, *align; char *buffer; @@ -789,9 +781,9 @@ static void sdhci_adma_table_pre(struct sdhci_host *host, SDHCI_ADMA2_MASK; if (offset) { if (data->flags & MMC_DATA_WRITE) { - buffer = sdhci_kmap_atomic(sg, &flags); + buffer = sdhci_kmap_atomic(sg); memcpy(align, buffer, offset); - sdhci_kunmap_atomic(buffer, &flags); + sdhci_kunmap_atomic(buffer); } /* tran, valid */ @@ -852,7 +844,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host, int i, size; void *align; char *buffer; - unsigned long flags; if (data->flags & MMC_DATA_READ) { bool has_unaligned = false; @@ -875,9 +866,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host, size = SDHCI_ADMA2_ALIGN - (sg_dma_address(sg) & SDHCI_ADMA2_MASK); - buffer = sdhci_kmap_atomic(sg, &flags); + buffer = sdhci_kmap_atomic(sg); memcpy(buffer, align, size); - sdhci_kunmap_atomic(buffer, &flags); + sdhci_kunmap_atomic(buffer); align += SDHCI_ADMA2_ALIGN; } @@ -1466,7 +1457,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (host->quirks2 & SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) { /* must not clear SDHCI_TRANSFER_MODE when tuning */ - if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) + if (!mmc_op_tuning(cmd->opcode)) sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE); } else { /* clear Auto CMD settings for no data CMDs */ @@ -1707,8 +1698,7 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) flags |= SDHCI_CMD_INDEX; /* CMD19 is special in that the Data Present Select should be set */ - if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK || - cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) + if (cmd->data || mmc_op_tuning(cmd->opcode)) flags |= SDHCI_CMD_DATA; timeout = jiffies; @@ -2304,7 +2294,7 @@ static bool sdhci_timing_has_preset(unsigned char timing) case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: return true; - }; + } return false; } @@ -2418,8 +2408,21 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->version >= SDHCI_SPEC_300) { u16 clk, ctrl_2; + /* + * According to SDHCI Spec v3.00, if the Preset Value + * Enable in the Host Control 2 register is set, we + * need to reset SD Clock Enable before changing High + * Speed Enable to avoid generating clock glitches. + */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (clk & SDHCI_CLOCK_CARD_EN) { + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + } + + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + if (!host->preset_enabled) { - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * We only need to set Driver Strength if the * preset value enable is not set. @@ -2442,30 +2445,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); host->drv_type = ios->drv_type; - } else { - /* - * According to SDHC Spec v3.00, if the Preset Value - * Enable in the Host Control 2 register is set, we - * need to reset SD Clock Enable before changing High - * Speed Enable to avoid generating clock gliches. - */ - - /* Reset SD Clock Enable */ - clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk &= ~SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); - - /* Re-enable SD Clock */ - host->ops->set_clock(host, host->clock); } - /* Reset SD Clock Enable */ - clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - clk &= ~SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); - host->ops->set_uhs_signaling(host, ios->timing); host->timing = ios->timing; @@ -3388,8 +3369,6 @@ static void sdhci_adma_show_error(struct sdhci_host *host) static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { - u32 command; - /* * CMD19 generates _only_ Buffer Read Ready interrupt if * use sdhci_send_tuning. @@ -3398,9 +3377,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * SDHCI_INT_DATA_AVAIL always there, stuck in irq storm. */ if (intmask & SDHCI_INT_DATA_AVAIL && !host->data) { - command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); - if (command == MMC_SEND_TUNING_BLOCK || - command == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)))) { host->tuning_done = 1; wake_up(&host->buf_ready_int); return; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 87a3aaa07438..605eaee805f7 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -345,7 +345,7 @@ struct sdhci_adma2_64_desc { */ #define SDHCI_MAX_SEGS 128 -/* Allow for a a command request and a data request at the same time */ +/* Allow for a command request and a data request at the same time */ #define SDHCI_MAX_MRQS 2 /* @@ -478,6 +478,8 @@ struct sdhci_host { * block count. */ #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18) +/* Issue CMD and DATA reset together */ +#define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1<<19) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index c2333c7acac9..7ef828942df3 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -836,7 +836,7 @@ static int sdhci_am654_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) { - dev_err(dev, "parsing dt failed (%d)\n", ret); + dev_err_probe(dev, ret, "parsing dt failed\n"); goto pm_runtime_put; } diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index 3f5977979cf2..a202a69a4b08 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -5,6 +5,7 @@ * Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd * Vincent Yang <vincent.yang@tw.fujitsu.com> * Copyright (C) 2015 Linaro Ltd Andy Green <andy.green@linaro.org> + * Copyright (C) 2019 Socionext Inc. */ #include <linux/acpi.h> @@ -14,6 +15,7 @@ #include <linux/of.h> #include <linux/property.h> #include <linux/clk.h> +#include <linux/reset.h> #include "sdhci-pltfm.h" #include "sdhci_f_sdh30.h" @@ -21,6 +23,7 @@ struct f_sdhost_priv { struct clk *clk_iface; struct clk *clk; + struct reset_control *rst; u32 vendor_hs200; struct device *dev; bool enable_cmd_dat_delay; @@ -74,6 +77,13 @@ static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) ctl |= F_SDH30_CMD_DAT_DELAY; sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL); } + + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) && + !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { + ctl = sdhci_readl(host, F_SDH30_TEST); + ctl |= F_SDH30_FORCE_CARD_INSERT; + sdhci_writel(host, ctl, F_SDH30_TEST); + } } static const struct sdhci_ops sdhci_f_sdh30_ops = { @@ -150,6 +160,16 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) ret = clk_prepare_enable(priv->clk); if (ret) goto err_clk; + + priv->rst = devm_reset_control_get_optional_shared(dev, NULL); + if (IS_ERR(priv->rst)) { + ret = PTR_ERR(priv->rst); + goto err_rst; + } + + ret = reset_control_deassert(priv->rst); + if (ret) + goto err_rst; } /* init vendor specific regs */ @@ -168,6 +188,9 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) if (reg & SDHCI_CAN_DO_8BIT) priv->vendor_hs200 = F_SDH30_EMMC_HS200; + if (!(reg & SDHCI_TIMEOUT_CLK_MASK)) + host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; + ret = sdhci_add_host(host); if (ret) goto err_add_host; @@ -175,6 +198,8 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) return 0; err_add_host: + reset_control_assert(priv->rst); +err_rst: clk_disable_unprepare(priv->clk); err_clk: clk_disable_unprepare(priv->clk_iface); @@ -191,8 +216,9 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev) sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - clk_disable_unprepare(priv->clk_iface); + reset_control_assert(priv->rst); clk_disable_unprepare(priv->clk); + clk_disable_unprepare(priv->clk_iface); sdhci_free_host(host); platform_set_drvdata(pdev, NULL); @@ -203,6 +229,7 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id f_sdh30_dt_ids[] = { { .compatible = "fujitsu,mb86s70-sdhci-3.0" }, + { .compatible = "socionext,f-sdh30-e51-mmc" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids); @@ -232,5 +259,5 @@ module_platform_driver(sdhci_f_sdh30_driver); MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver"); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD."); +MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD., Socionext Inc."); MODULE_ALIAS("platform:f_sdh30"); diff --git a/drivers/mmc/host/sdhci_f_sdh30.h b/drivers/mmc/host/sdhci_f_sdh30.h index fc1ad28f7ca9..7c3c66291d42 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.h +++ b/drivers/mmc/host/sdhci_f_sdh30.h @@ -29,4 +29,7 @@ #define F_SDH30_CMD_DAT_DELAY BIT(9) #define F_SDH30_EMMC_HS200 BIT(24) +#define F_SDH30_TEST 0x158 +#define F_SDH30_FORCE_CARD_INSERT BIT(6) + #define F_SDH30_MIN_CLOCK 400000 diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c new file mode 100644 index 000000000000..db5e0dcdfa7f --- /dev/null +++ b/drivers/mmc/host/sunplus-mmc.c @@ -0,0 +1,1000 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Sunplus Inc. + * Author: Tony Huang <tonyhuang.sunplus@gmail.com> + * Author: Li-hao Kuo <lhjeff911@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/mmc/core.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#define SPMMC_MIN_CLK 400000 +#define SPMMC_MAX_CLK 52000000 +#define SPMMC_MAX_BLK_COUNT 65536 +#define SPMMC_MAX_TUNABLE_DLY 7 +#define SPMMC_TIMEOUT_US 500000 +#define SPMMC_POLL_DELAY_US 10 + +#define SPMMC_CARD_MEDIATYPE_SRCDST_REG 0x0000 +#define SPMMC_MEDIA_TYPE GENMASK(2, 0) +#define SPMMC_DMA_SOURCE GENMASK(6, 4) +#define SPMMC_DMA_DESTINATION GENMASK(10, 8) +#define SPMMC_MEDIA_NONE 0 +#define SPMMC_MEDIA_SD 6 +#define SPMMC_MEDIA_MS 7 + +#define SPMMC_SDRAM_SECTOR_0_SIZE_REG 0x0008 +#define SPMMC_DMA_BASE_ADDR_REG 0x000C +#define SPMMC_HW_DMA_CTRL_REG 0x0010 +#define SPMMC_HW_DMA_RST BIT(9) +#define SPMMC_DMAIDLE BIT(10) + +#define SPMMC_MAX_DMA_MEMORY_SECTORS 8 + +#define SPMMC_SDRAM_SECTOR_1_ADDR_REG 0x0018 +#define SPMMC_SDRAM_SECTOR_1_LENG_REG 0x001C +#define SPMMC_SDRAM_SECTOR_2_ADDR_REG 0x0020 +#define SPMMC_SDRAM_SECTOR_2_LENG_REG 0x0024 +#define SPMMC_SDRAM_SECTOR_3_ADDR_REG 0x0028 +#define SPMMC_SDRAM_SECTOR_3_LENG_REG 0x002C +#define SPMMC_SDRAM_SECTOR_4_ADDR_REG 0x0030 +#define SPMMC_SDRAM_SECTOR_4_LENG_REG 0x0034 +#define SPMMC_SDRAM_SECTOR_5_ADDR_REG 0x0038 +#define SPMMC_SDRAM_SECTOR_5_LENG_REG 0x003C +#define SPMMC_SDRAM_SECTOR_6_ADDR_REG 0x0040 +#define SPMMC_SDRAM_SECTOR_6_LENG_REG 0x0044 +#define SPMMC_SDRAM_SECTOR_7_ADDR_REG 0x0048 +#define SPMMC_SDRAM_SECTOR_7_LENG_REG 0x004C + +#define SPMMC_SD_INT_REG 0x0088 +#define SPMMC_SDINT_SDCMPEN BIT(0) +#define SPMMC_SDINT_SDCMP BIT(1) +#define SPMMC_SDINT_SDCMPCLR BIT(2) +#define SPMMC_SDINT_SDIOEN BIT(3) +#define SPMMC_SDINT_SDIO BIT(4) +#define SPMMC_SDINT_SDIOCLR BIT(5) + +#define SPMMC_SD_PAGE_NUM_REG 0x008C + +#define SPMMC_SD_CONFIG0_REG 0x0090 +#define SPMMC_SD_PIO_MODE BIT(0) +#define SPMMC_SD_DDR_MODE BIT(1) +#define SPMMC_SD_LEN_MODE BIT(2) +#define SPMMC_SD_TRANS_MODE GENMASK(5, 4) +#define SPMMC_SD_AUTO_RESPONSE BIT(6) +#define SPMMC_SD_CMD_DUMMY BIT(7) +#define SPMMC_SD_RSP_CHK_EN BIT(8) +#define SPMMC_SDIO_MODE BIT(9) +#define SPMMC_SD_MMC_MODE BIT(10) +#define SPMMC_SD_DATA_WD BIT(11) +#define SPMMC_RX4_EN BIT(14) +#define SPMMC_SD_RSP_TYPE BIT(15) +#define SPMMC_MMC8_EN BIT(18) +#define SPMMC_CLOCK_DIVISION GENMASK(31, 20) + +#define SPMMC_SDIO_CTRL_REG 0x0094 +#define SPMMC_INT_MULTI_TRIG BIT(6) + +#define SPMMC_SD_RST_REG 0x0098 +#define SPMMC_SD_CTRL_REG 0x009C +#define SPMMC_NEW_COMMAND_TRIGGER BIT(0) +#define SPMMC_DUMMY_CLOCK_TRIGGER BIT(1) + +#define SPMMC_SD_STATUS_REG 0x00A0 +#define SPMMC_SDSTATUS_DUMMY_READY BIT(0) +#define SPMMC_SDSTATUS_RSP_BUF_FULL BIT(1) +#define SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY BIT(2) +#define SPMMC_SDSTATUS_RX_DATA_BUF_FULL BIT(3) +#define SPMMC_SDSTATUS_CMD_PIN_STATUS BIT(4) +#define SPMMC_SDSTATUS_DAT0_PIN_STATUS BIT(5) +#define SPMMC_SDSTATUS_RSP_TIMEOUT BIT(6) +#define SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT BIT(7) +#define SPMMC_SDSTATUS_STB_TIMEOUT BIT(8) +#define SPMMC_SDSTATUS_RSP_CRC7_ERROR BIT(9) +#define SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR BIT(10) +#define SPMMC_SDSTATUS_RDATA_CRC16_ERROR BIT(11) +#define SPMMC_SDSTATUS_SUSPEND_STATE_READY BIT(12) +#define SPMMC_SDSTATUS_BUSY_CYCLE BIT(13) +#define SPMMC_SDSTATUS_DAT1_PIN_STATUS BIT(14) +#define SPMMC_SDSTATUS_SD_SENSE_STATUS BIT(15) +#define SPMMC_SDSTATUS_BOOT_ACK_TIMEOUT BIT(16) +#define SPMMC_SDSTATUS_BOOT_DATA_TIMEOUT BIT(17) +#define SPMMC_SDSTATUS_BOOT_ACK_ERROR BIT(18) + +#define SPMMC_SD_STATE_REG 0x00A4 +#define SPMMC_CRCTOKEN_CHECK_RESULT GENMASK(6, 4) +#define SPMMC_SDSTATE_ERROR BIT(13) +#define SPMMC_SDSTATE_FINISH BIT(14) + +#define SPMMC_SD_HW_STATE_REG 0x00A8 +#define SPMMC_SD_BLOCKSIZE_REG 0x00AC + +#define SPMMC_SD_CONFIG1_REG 0x00B0 +#define SPMMC_TX_DUMMY_NUM GENMASK(8, 0) +#define SPMMC_SD_HIGH_SPEED_EN BIT(31) + +#define SPMMC_SD_TIMING_CONFIG0_REG 0x00B4 +#define SPMMC_SD_CLOCK_DELAY GENMASK(2, 0) +#define SPMMC_SD_WRITE_DATA_DELAY GENMASK(6, 4) +#define SPMMC_SD_WRITE_COMMAND_DELAY GENMASK(10, 8) +#define SPMMC_SD_READ_RESPONSE_DELAY GENMASK(14, 12) +#define SPMMC_SD_READ_DATA_DELAY GENMASK(18, 16) +#define SPMMC_SD_READ_CRC_DELAY GENMASK(22, 20) + +#define SPMMC_SD_PIODATATX_REG 0x00BC +#define SPMMC_SD_PIODATARX_REG 0x00C0 +#define SPMMC_SD_CMDBUF0_3_REG 0x00C4 +#define SPMMC_SD_CMDBUF4_REG 0x00C8 +#define SPMMC_SD_RSPBUF0_3_REG 0x00CC +#define SPMMC_SD_RSPBUF4_5_REG 0x00D0 + +#define SPMMC_MAX_RETRIES (8 * 8) + +struct spmmc_tuning_info { + int enable_tuning; + int need_tuning; + int retried; /* how many times has been retried */ + u32 rd_crc_dly:3; + u32 rd_dat_dly:3; + u32 rd_rsp_dly:3; + u32 wr_cmd_dly:3; + u32 wr_dat_dly:3; + u32 clk_dly:3; +}; + +#define SPMMC_DMA_MODE 0 +#define SPMMC_PIO_MODE 1 + +struct spmmc_host { + void __iomem *base; + struct clk *clk; + struct reset_control *rstc; + struct mmc_host *mmc; + struct mmc_request *mrq; /* current mrq */ + int irq; + int dmapio_mode; + struct spmmc_tuning_info tuning_info; + int dma_int_threshold; + int dma_use_int; +}; + +static inline int spmmc_wait_finish(struct spmmc_host *host) +{ + u32 state; + + return readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, state, + (state & SPMMC_SDSTATE_FINISH), + SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US); +} + +static inline int spmmc_wait_sdstatus(struct spmmc_host *host, unsigned int status_bit) +{ + u32 status; + + return readl_poll_timeout(host->base + SPMMC_SD_STATUS_REG, status, + (status & status_bit), + SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US); +} + +#define spmmc_wait_rspbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RSP_BUF_FULL) +#define spmmc_wait_rxbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RX_DATA_BUF_FULL) +#define spmmc_wait_txbuf_empty(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY) + +static void spmmc_get_rsp(struct spmmc_host *host, struct mmc_command *cmd) +{ + u32 value0_3, value4_5; + + if (!(cmd->flags & MMC_RSP_PRESENT)) + return; + if (cmd->flags & MMC_RSP_136) { + if (spmmc_wait_rspbuf_full(host)) + return; + value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); + value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; + cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8); + cmd->resp[1] = value4_5 << 24; + value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); + value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; + cmd->resp[1] |= value0_3 >> 8; + cmd->resp[2] = value0_3 << 24; + cmd->resp[2] |= value4_5 << 8; + value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); + value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; + cmd->resp[2] |= value0_3 >> 24; + cmd->resp[3] = value0_3 << 8; + cmd->resp[3] |= value4_5 >> 8; + } else { + if (spmmc_wait_rspbuf_full(host)) + return; + value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG); + value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff; + cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8); + cmd->resp[1] = value4_5 << 24; + } +} + +static void spmmc_set_bus_clk(struct spmmc_host *host, int clk) +{ + unsigned int clkdiv; + int f_min = host->mmc->f_min; + int f_max = host->mmc->f_max; + u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); + + if (clk < f_min) + clk = f_min; + if (clk > f_max) + clk = f_max; + + clkdiv = (clk_get_rate(host->clk) + clk) / clk - 1; + if (clkdiv > 0xfff) + clkdiv = 0xfff; + value &= ~SPMMC_CLOCK_DIVISION; + value |= FIELD_PREP(SPMMC_CLOCK_DIVISION, clkdiv); + writel(value, host->base + SPMMC_SD_CONFIG0_REG); +} + +static void spmmc_set_bus_timing(struct spmmc_host *host, unsigned int timing) +{ + u32 value = readl(host->base + SPMMC_SD_CONFIG1_REG); + int clkdiv = FIELD_GET(SPMMC_CLOCK_DIVISION, readl(host->base + SPMMC_SD_CONFIG0_REG)); + int delay = clkdiv / 2 < 7 ? clkdiv / 2 : 7; + int hs_en = 1, ddr_enabled = 0; + + switch (timing) { + case MMC_TIMING_LEGACY: + hs_en = 0; + break; + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + case MMC_TIMING_UHS_SDR50: + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_MMC_HS200: + hs_en = 1; + break; + case MMC_TIMING_UHS_DDR50: + ddr_enabled = 1; + break; + case MMC_TIMING_MMC_DDR52: + ddr_enabled = 1; + break; + default: + hs_en = 0; + break; + } + + if (hs_en) { + value |= SPMMC_SD_HIGH_SPEED_EN; + writel(value, host->base + SPMMC_SD_CONFIG1_REG); + value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); + value &= ~SPMMC_SD_WRITE_DATA_DELAY; + value |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, delay); + value &= ~SPMMC_SD_WRITE_COMMAND_DELAY; + value |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, delay); + writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); + } else { + value &= ~SPMMC_SD_HIGH_SPEED_EN; + writel(value, host->base + SPMMC_SD_CONFIG1_REG); + } + if (ddr_enabled) { + value = readl(host->base + SPMMC_SD_CONFIG0_REG); + value |= SPMMC_SD_DDR_MODE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); + } else { + value = readl(host->base + SPMMC_SD_CONFIG0_REG); + value &= ~SPMMC_SD_DDR_MODE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); + } +} + +static void spmmc_set_bus_width(struct spmmc_host *host, int width) +{ + u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); + + switch (width) { + case MMC_BUS_WIDTH_8: + value &= ~SPMMC_SD_DATA_WD; + value |= SPMMC_MMC8_EN; + break; + case MMC_BUS_WIDTH_4: + value |= SPMMC_SD_DATA_WD; + value &= ~SPMMC_MMC8_EN; + break; + default: + value &= ~SPMMC_SD_DATA_WD; + value &= ~SPMMC_MMC8_EN; + break; + } + writel(value, host->base + SPMMC_SD_CONFIG0_REG); +} + +/* + * select the working mode of controller: sd/sdio/emmc + */ +static void spmmc_set_sdmmc_mode(struct spmmc_host *host) +{ + u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG); + + value |= SPMMC_SD_MMC_MODE; + value &= ~SPMMC_SDIO_MODE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); +} + +static void spmmc_sw_reset(struct spmmc_host *host) +{ + u32 value; + + /* + * Must reset dma operation first, or it will + * be stuck on sd_state == 0x1c00 because of + * a controller software reset bug + */ + value = readl(host->base + SPMMC_HW_DMA_CTRL_REG); + value |= SPMMC_DMAIDLE; + writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); + value &= ~SPMMC_DMAIDLE; + writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); + value = readl(host->base + SPMMC_HW_DMA_CTRL_REG); + value |= SPMMC_HW_DMA_RST; + writel(value, host->base + SPMMC_HW_DMA_CTRL_REG); + writel(0x7, host->base + SPMMC_SD_RST_REG); + readl_poll_timeout_atomic(host->base + SPMMC_SD_HW_STATE_REG, value, + !(value & BIT(6)), 1, SPMMC_TIMEOUT_US); +} + +static void spmmc_prepare_cmd(struct spmmc_host *host, struct mmc_command *cmd) +{ + u32 value; + + /* add start bit, according to spec, command format */ + value = ((cmd->opcode | 0x40) << 24) | (cmd->arg >> 8); + writel(value, host->base + SPMMC_SD_CMDBUF0_3_REG); + writeb(cmd->arg & 0xff, host->base + SPMMC_SD_CMDBUF4_REG); + + /* disable interrupt if needed */ + value = readl(host->base + SPMMC_SD_INT_REG); + value |= SPMMC_SDINT_SDCMPCLR; + value &= ~SPMMC_SDINT_SDCMPEN; + writel(value, host->base + SPMMC_SD_INT_REG); + + value = readl(host->base + SPMMC_SD_CONFIG0_REG); + value &= ~SPMMC_SD_TRANS_MODE; + value |= SPMMC_SD_CMD_DUMMY; + if (cmd->flags & MMC_RSP_PRESENT) { + value |= SPMMC_SD_AUTO_RESPONSE; + } else { + value &= ~SPMMC_SD_AUTO_RESPONSE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); + + return; + } + /* + * Currently, host is not capable of checking R2's CRC7, + * thus, enable crc7 check only for 48 bit response commands + */ + if (cmd->flags & MMC_RSP_CRC && !(cmd->flags & MMC_RSP_136)) + value |= SPMMC_SD_RSP_CHK_EN; + else + value &= ~SPMMC_SD_RSP_CHK_EN; + + if (cmd->flags & MMC_RSP_136) + value |= SPMMC_SD_RSP_TYPE; + else + value &= ~SPMMC_SD_RSP_TYPE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); +} + +static void spmmc_prepare_data(struct spmmc_host *host, struct mmc_data *data) +{ + u32 value, srcdst; + + writel(data->blocks - 1, host->base + SPMMC_SD_PAGE_NUM_REG); + writel(data->blksz - 1, host->base + SPMMC_SD_BLOCKSIZE_REG); + value = readl(host->base + SPMMC_SD_CONFIG0_REG); + if (data->flags & MMC_DATA_READ) { + value &= ~SPMMC_SD_TRANS_MODE; + value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 2); + value &= ~SPMMC_SD_AUTO_RESPONSE; + value &= ~SPMMC_SD_CMD_DUMMY; + srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); + srcdst &= ~SPMMC_DMA_SOURCE; + srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x2); + srcdst &= ~SPMMC_DMA_DESTINATION; + srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x1); + writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); + } else { + value &= ~SPMMC_SD_TRANS_MODE; + value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 1); + srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); + srcdst &= ~SPMMC_DMA_SOURCE; + srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x1); + srcdst &= ~SPMMC_DMA_DESTINATION; + srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x2); + writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); + } + + value |= SPMMC_SD_LEN_MODE; + if (host->dmapio_mode == SPMMC_DMA_MODE) { + struct scatterlist *sg; + dma_addr_t dma_addr; + unsigned int dma_size; + int i, count = 1; + + count = dma_map_sg(host->mmc->parent, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (!count || count > SPMMC_MAX_DMA_MEMORY_SECTORS) { + data->error = -EINVAL; + + return; + } + for_each_sg(data->sg, sg, count, i) { + dma_addr = sg_dma_address(sg); + dma_size = sg_dma_len(sg) / data->blksz - 1; + if (i == 0) { + writel(dma_addr, host->base + SPMMC_DMA_BASE_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_0_SIZE_REG); + } else if (i == 1) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_1_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_1_LENG_REG); + } else if (i == 2) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_2_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_2_LENG_REG); + } else if (i == 3) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_3_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_3_LENG_REG); + } else if (i == 4) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_4_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_4_LENG_REG); + } else if (i == 5) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_5_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_5_LENG_REG); + } else if (i == 6) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_6_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_6_LENG_REG); + } else if (i == 7) { + writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_7_ADDR_REG); + writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_7_LENG_REG); + } + } + value &= ~SPMMC_SD_PIO_MODE; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); + /* enable interrupt if needed */ + if (data->blksz * data->blocks > host->dma_int_threshold) { + host->dma_use_int = 1; + value = readl(host->base + SPMMC_SD_INT_REG); + value &= ~SPMMC_SDINT_SDCMPEN; + value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 1); /* sdcmpen */ + writel(value, host->base + SPMMC_SD_INT_REG); + } + } else { + value |= SPMMC_SD_PIO_MODE; + value |= SPMMC_RX4_EN; + writel(value, host->base + SPMMC_SD_CONFIG0_REG); + } +} + +static inline void spmmc_trigger_transaction(struct spmmc_host *host) +{ + u32 value = readl(host->base + SPMMC_SD_CTRL_REG); + + value |= SPMMC_NEW_COMMAND_TRIGGER; + writel(value, host->base + SPMMC_SD_CTRL_REG); +} + +static void spmmc_send_stop_cmd(struct spmmc_host *host) +{ + struct mmc_command stop = {}; + u32 value; + + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B; + spmmc_prepare_cmd(host, &stop); + value = readl(host->base + SPMMC_SD_INT_REG); + value &= ~SPMMC_SDINT_SDCMPEN; + value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 0); + writel(value, host->base + SPMMC_SD_INT_REG); + spmmc_trigger_transaction(host); + readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, value, + (value & SPMMC_SDSTATE_FINISH), 1, SPMMC_TIMEOUT_US); +} + +static int spmmc_check_error(struct spmmc_host *host, struct mmc_request *mrq) +{ + int ret = 0; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + + u32 value = readl(host->base + SPMMC_SD_STATE_REG); + u32 crc_token = FIELD_GET(SPMMC_CRCTOKEN_CHECK_RESULT, value); + + if (value & SPMMC_SDSTATE_ERROR) { + u32 timing_cfg0 = 0; + + value = readl(host->base + SPMMC_SD_STATUS_REG); + + if (host->tuning_info.enable_tuning) { + timing_cfg0 = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); + host->tuning_info.rd_crc_dly = FIELD_GET(SPMMC_SD_READ_CRC_DELAY, + timing_cfg0); + host->tuning_info.rd_dat_dly = FIELD_GET(SPMMC_SD_READ_DATA_DELAY, + timing_cfg0); + host->tuning_info.rd_rsp_dly = FIELD_GET(SPMMC_SD_READ_RESPONSE_DELAY, + timing_cfg0); + host->tuning_info.wr_cmd_dly = FIELD_GET(SPMMC_SD_WRITE_COMMAND_DELAY, + timing_cfg0); + host->tuning_info.wr_dat_dly = FIELD_GET(SPMMC_SD_WRITE_DATA_DELAY, + timing_cfg0); + } + + if (value & SPMMC_SDSTATUS_RSP_TIMEOUT) { + ret = -ETIMEDOUT; + host->tuning_info.wr_cmd_dly++; + } else if (value & SPMMC_SDSTATUS_RSP_CRC7_ERROR) { + ret = -EILSEQ; + host->tuning_info.rd_rsp_dly++; + } else if (data) { + if ((value & SPMMC_SDSTATUS_STB_TIMEOUT)) { + ret = -ETIMEDOUT; + host->tuning_info.rd_dat_dly++; + } else if (value & SPMMC_SDSTATUS_RDATA_CRC16_ERROR) { + ret = -EILSEQ; + host->tuning_info.rd_dat_dly++; + } else if (value & SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT) { + ret = -ETIMEDOUT; + host->tuning_info.rd_crc_dly++; + } else if (value & SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR) { + ret = -EILSEQ; + if (crc_token == 0x5) + host->tuning_info.wr_dat_dly++; + else + host->tuning_info.rd_crc_dly++; + } + } + cmd->error = ret; + if (data) { + data->error = ret; + data->bytes_xfered = 0; + } + if (!host->tuning_info.need_tuning && host->tuning_info.enable_tuning) + cmd->retries = SPMMC_MAX_RETRIES; + spmmc_sw_reset(host); + + if (host->tuning_info.enable_tuning) { + timing_cfg0 &= ~SPMMC_SD_READ_CRC_DELAY; + timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, + host->tuning_info.rd_crc_dly); + timing_cfg0 &= ~SPMMC_SD_READ_DATA_DELAY; + timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, + host->tuning_info.rd_dat_dly); + timing_cfg0 &= ~SPMMC_SD_READ_RESPONSE_DELAY; + timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, + host->tuning_info.rd_rsp_dly); + timing_cfg0 &= ~SPMMC_SD_WRITE_COMMAND_DELAY; + timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, + host->tuning_info.wr_cmd_dly); + timing_cfg0 &= ~SPMMC_SD_WRITE_DATA_DELAY; + timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, + host->tuning_info.wr_dat_dly); + writel(timing_cfg0, host->base + SPMMC_SD_TIMING_CONFIG0_REG); + } + } else if (data) { + data->error = 0; + data->bytes_xfered = data->blocks * data->blksz; + } + host->tuning_info.need_tuning = ret; + + return ret; +} + +/* + * the strategy is: + * 1. if several continuous delays are acceptable, we choose a middle one; + * 2. otherwise, we choose the first one. + */ +static inline int spmmc_find_best_delay(u8 candidate_dly) +{ + int f, w, value; + + if (!candidate_dly) + return 0; + f = ffs(candidate_dly) - 1; + w = hweight8(candidate_dly); + value = ((1 << w) - 1) << f; + if (0xff == (value & ~candidate_dly)) + return (f + w / 2); + else + return (f); +} + +static void spmmc_xfer_data_pio(struct spmmc_host *host, struct mmc_data *data) +{ + u32 *buf; + int data_left = data->blocks * data->blksz; + int consumed, remain; + + struct sg_mapping_iter sg_miter; + unsigned int flags = 0; + + if (data->flags & MMC_DATA_WRITE) + flags |= SG_MITER_FROM_SG; + else + flags |= SG_MITER_TO_SG; + sg_miter_start(&sg_miter, data->sg, data->sg_len, flags); + while (data_left > 0) { + consumed = 0; + if (!sg_miter_next(&sg_miter)) + break; + buf = sg_miter.addr; + remain = sg_miter.length; + do { + if (data->flags & MMC_DATA_WRITE) { + if (spmmc_wait_txbuf_empty(host)) + goto done; + writel(*buf, host->base + SPMMC_SD_PIODATATX_REG); + } else { + if (spmmc_wait_rxbuf_full(host)) + goto done; + *buf = readl(host->base + SPMMC_SD_PIODATARX_REG); + } + buf++; + /* tx/rx 4 bytes one time in pio mode */ + consumed += 4; + remain -= 4; + } while (remain); + sg_miter.consumed = consumed; + data_left -= consumed; + } +done: + sg_miter_stop(&sg_miter); +} + +static void spmmc_controller_init(struct spmmc_host *host) +{ + u32 value; + int ret = reset_control_assert(host->rstc); + + if (!ret) { + usleep_range(1000, 1250); + ret = reset_control_deassert(host->rstc); + } + + value = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); + value &= ~SPMMC_MEDIA_TYPE; + value |= FIELD_PREP(SPMMC_MEDIA_TYPE, SPMMC_MEDIA_SD); + writel(value, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG); +} + +/* + * 1. unmap scatterlist if needed; + * 2. get response & check error conditions; + * 3. notify mmc layer the request is done + */ +static void spmmc_finish_request(struct spmmc_host *host, struct mmc_request *mrq) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + if (!mrq) + return; + + cmd = mrq->cmd; + data = mrq->data; + + if (data && SPMMC_DMA_MODE == host->dmapio_mode) { + dma_unmap_sg(host->mmc->parent, data->sg, data->sg_len, mmc_get_dma_dir(data)); + host->dma_use_int = 0; + } + + spmmc_get_rsp(host, cmd); + spmmc_check_error(host, mrq); + if (mrq->stop) + spmmc_send_stop_cmd(host); + + host->mrq = NULL; + mmc_request_done(host->mmc, mrq); +} + +/* Interrupt Service Routine */ +static irqreturn_t spmmc_irq(int irq, void *dev_id) +{ + struct spmmc_host *host = dev_id; + u32 value = readl(host->base + SPMMC_SD_INT_REG); + + if ((value & SPMMC_SDINT_SDCMP) && (value & SPMMC_SDINT_SDCMPEN)) { + value &= ~SPMMC_SDINT_SDCMPEN; + value |= SPMMC_SDINT_SDCMPCLR; + writel(value, host->base + SPMMC_SD_INT_REG); + return IRQ_WAKE_THREAD; + } + return IRQ_HANDLED; +} + +static void spmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct spmmc_host *host = mmc_priv(mmc); + struct mmc_data *data; + struct mmc_command *cmd; + + host->mrq = mrq; + data = mrq->data; + cmd = mrq->cmd; + + spmmc_prepare_cmd(host, cmd); + /* we need manually read response R2. */ + if (cmd->flags & MMC_RSP_136) { + spmmc_trigger_transaction(host); + spmmc_get_rsp(host, cmd); + spmmc_wait_finish(host); + spmmc_check_error(host, mrq); + host->mrq = NULL; + mmc_request_done(host->mmc, mrq); + } else { + if (data) + spmmc_prepare_data(host, data); + + if (host->dmapio_mode == SPMMC_PIO_MODE && data) { + u32 value; + /* pio data transfer do not use interrupt */ + value = readl(host->base + SPMMC_SD_INT_REG); + value &= ~SPMMC_SDINT_SDCMPEN; + writel(value, host->base + SPMMC_SD_INT_REG); + spmmc_trigger_transaction(host); + spmmc_xfer_data_pio(host, data); + spmmc_wait_finish(host); + spmmc_finish_request(host, mrq); + } else { + if (host->dma_use_int) { + spmmc_trigger_transaction(host); + } else { + spmmc_trigger_transaction(host); + spmmc_wait_finish(host); + spmmc_finish_request(host, mrq); + } + } + } +} + +static void spmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct spmmc_host *host = (struct spmmc_host *)mmc_priv(mmc); + + spmmc_set_bus_clk(host, ios->clock); + spmmc_set_bus_timing(host, ios->timing); + spmmc_set_bus_width(host, ios->bus_width); + /* ensure mode is correct, because we might have hw reset the controller */ + spmmc_set_sdmmc_mode(host); +} + +/* + * Return values for the get_cd callback should be: + * 0 for a absent card + * 1 for a present card + * -ENOSYS when not supported (equal to NULL callback) + * or a negative errno value when something bad happened + */ +static int spmmc_get_cd(struct mmc_host *mmc) +{ + int ret = 0; + + if (mmc_can_gpio_cd(mmc)) + ret = mmc_gpio_get_cd(mmc); + + if (ret < 0) + ret = 0; + + return ret; +} + +static int spmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct spmmc_host *host = mmc_priv(mmc); + u8 smpl_dly = 0, candidate_dly = 0; + u32 value; + + host->tuning_info.enable_tuning = 0; + do { + value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); + value &= ~SPMMC_SD_READ_RESPONSE_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly); + value &= ~SPMMC_SD_READ_DATA_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly); + value &= ~SPMMC_SD_READ_CRC_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly); + writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); + + if (!mmc_send_tuning(mmc, opcode, NULL)) { + candidate_dly |= (1 << smpl_dly); + break; + } + } while (smpl_dly++ <= SPMMC_MAX_TUNABLE_DLY); + host->tuning_info.enable_tuning = 1; + + if (candidate_dly) { + smpl_dly = spmmc_find_best_delay(candidate_dly); + value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); + value &= ~SPMMC_SD_READ_RESPONSE_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly); + value &= ~SPMMC_SD_READ_DATA_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly); + value &= ~SPMMC_SD_READ_CRC_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly); + writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); + return 0; + } + + return -EIO; +} + +static const struct mmc_host_ops spmmc_ops = { + .request = spmmc_request, + .set_ios = spmmc_set_ios, + .get_cd = spmmc_get_cd, + .execute_tuning = spmmc_execute_tuning, +}; + +static irqreturn_t spmmc_func_finish_req(int irq, void *dev_id) +{ + struct spmmc_host *host = dev_id; + + spmmc_finish_request(host, host->mrq); + + return IRQ_HANDLED; +} + +static int spmmc_drv_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct resource *res; + struct spmmc_host *host; + int ret = 0; + + mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto probe_free_host; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dmapio_mode = SPMMC_DMA_MODE; + host->dma_int_threshold = 1024; + + host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(host->base)) + return PTR_ERR(host->base); + + host->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "clk get fail\n"); + + host->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); + if (IS_ERR(host->rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->rstc), "rst get fail\n"); + + host->irq = platform_get_irq(pdev, 0); + if (host->irq <= 0) + return host->irq; + + ret = devm_request_threaded_irq(&pdev->dev, host->irq, + spmmc_irq, spmmc_func_finish_req, IRQF_SHARED, + NULL, host); + if (ret) + return ret; + + ret = clk_prepare_enable(host->clk); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to enable clk\n"); + + ret = mmc_of_parse(mmc); + if (ret) + goto probe_free_host; + + mmc->ops = &spmmc_ops; + mmc->f_min = SPMMC_MIN_CLK; + if (mmc->f_max > SPMMC_MAX_CLK) + mmc->f_max = SPMMC_MAX_CLK; + + ret = mmc_regulator_get_supply(mmc); + if (ret) + goto probe_free_host; + + if (!mmc->ocr_avail) + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->max_seg_size = SPMMC_MAX_BLK_COUNT * 512; + mmc->max_segs = SPMMC_MAX_DMA_MEMORY_SECTORS; + mmc->max_req_size = SPMMC_MAX_BLK_COUNT * 512; + mmc->max_blk_size = 512; + mmc->max_blk_count = SPMMC_MAX_BLK_COUNT; + + dev_set_drvdata(&pdev->dev, host); + spmmc_controller_init(host); + spmmc_set_sdmmc_mode(host); + host->tuning_info.enable_tuning = 1; + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + mmc_add_host(mmc); + + return ret; + +probe_free_host: + if (mmc) + mmc_free_host(mmc); + + return ret; +} + +static int spmmc_drv_remove(struct platform_device *dev) +{ + struct spmmc_host *host = platform_get_drvdata(dev); + + mmc_remove_host(host->mmc); + pm_runtime_get_sync(&dev->dev); + clk_disable_unprepare(host->clk); + pm_runtime_put_noidle(&dev->dev); + pm_runtime_disable(&dev->dev); + platform_set_drvdata(dev, NULL); + mmc_free_host(host->mmc); + + return 0; +} + +static int spmmc_pm_runtime_suspend(struct device *dev) +{ + struct spmmc_host *host; + + host = dev_get_drvdata(dev); + clk_disable_unprepare(host->clk); + + return 0; +} + +static int spmmc_pm_runtime_resume(struct device *dev) +{ + struct spmmc_host *host; + + host = dev_get_drvdata(dev); + + return clk_prepare_enable(host->clk); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(spmmc_pm_ops, spmmc_pm_runtime_suspend, + spmmc_pm_runtime_resume, NULL); + +static const struct of_device_id spmmc_of_table[] = { + { + .compatible = "sunplus,sp7021-mmc", + }, + {/* sentinel */} +}; +MODULE_DEVICE_TABLE(of, spmmc_of_table); + +static struct platform_driver spmmc_driver = { + .probe = spmmc_drv_probe, + .remove = spmmc_drv_remove, + .driver = { + .name = "spmmc", + .pm = pm_ptr(&spmmc_pm_ops), + .of_match_table = spmmc_of_table, + }, +}; +module_platform_driver(spmmc_driver); + +MODULE_AUTHOR("Tony Huang <tonyhuang.sunplus@gmail.com>"); +MODULE_AUTHOR("Li-hao Kuo <lhjeff911@gmail.com>"); +MODULE_DESCRIPTION("Sunplus MMC controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 63917070b1a7..b5a2f2f25ad9 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -116,7 +116,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, unsigned char *buf; unsigned int pos = 0, val; - buf = kmap_atomic(pg) + off; + buf = kmap_local_page(pg) + off; if (host->cmd_flags & DATA_CARRY) { buf[pos++] = host->bounce_buf_data[0]; host->cmd_flags &= ~DATA_CARRY; @@ -132,7 +132,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, } buf[pos++] = (val >> 8) & 0xff; } - kunmap_atomic(buf - off); + kunmap_local(buf - off); } static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, @@ -142,7 +142,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, unsigned char *buf; unsigned int pos = 0, val; - buf = kmap_atomic(pg) + off; + buf = kmap_local_page(pg) + off; if (host->cmd_flags & DATA_CARRY) { val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); writel(val, sock->addr + SOCK_MMCSD_DATA); @@ -159,7 +159,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, val |= (buf[pos++] << 8) & 0xff00; writel(val, sock->addr + SOCK_MMCSD_DATA); } - kunmap_atomic(buf - off); + kunmap_local(buf - off); } static void tifm_sd_transfer_data(struct tifm_sd *host) @@ -210,13 +210,13 @@ static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, struct page *src, unsigned int src_off, unsigned int count) { - unsigned char *src_buf = kmap_atomic(src) + src_off; - unsigned char *dst_buf = kmap_atomic(dst) + dst_off; + unsigned char *src_buf = kmap_local_page(src) + src_off; + unsigned char *dst_buf = kmap_local_page(dst) + dst_off; memcpy(dst_buf, src_buf, count); - kunmap_atomic(dst_buf - dst_off); - kunmap_atomic(src_buf - src_off); + kunmap_local(dst_buf - dst_off); + kunmap_local(src_buf - src_off); } static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) @@ -264,16 +264,13 @@ static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; unsigned int dma_len, dma_blk_cnt, dma_off; struct scatterlist *sg = NULL; - unsigned long flags; if (host->sg_pos == host->sg_len) return 1; if (host->cmd_flags & DATA_CARRY) { host->cmd_flags &= ~DATA_CARRY; - local_irq_save(flags); tifm_sd_bounce_block(host, r_data); - local_irq_restore(flags); if (host->sg_pos == host->sg_len) return 1; } @@ -300,11 +297,9 @@ static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) if (dma_blk_cnt) sg = &r_data->sg[host->sg_pos]; else if (dma_len) { - if (r_data->flags & MMC_DATA_WRITE) { - local_irq_save(flags); + if (r_data->flags & MMC_DATA_WRITE) tifm_sd_bounce_block(host, r_data); - local_irq_restore(flags); - } else + else host->cmd_flags |= DATA_CARRY; sg = &host->bounce_buf; @@ -506,7 +501,6 @@ static void tifm_sd_card_event(struct tifm_dev *sock) unsigned int host_status = 0; int cmd_error = 0; struct mmc_command *cmd = NULL; - unsigned long flags; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); @@ -570,9 +564,7 @@ static void tifm_sd_card_event(struct tifm_dev *sock) if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF | TIFM_MMCSD_BRS)) { - local_irq_save(flags); tifm_sd_transfer_data(host); - local_irq_restore(flags); host_status &= ~TIFM_MMCSD_AE; } } diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 501613c74406..de56e6534aea 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -128,6 +128,7 @@ struct tmio_mmc_dma_ops { /* optional */ void (*end)(struct tmio_mmc_host *host); /* held host->lock */ + bool (*dma_irq)(struct tmio_mmc_host *host); }; struct tmio_mmc_host { @@ -204,20 +205,6 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i); irqreturn_t tmio_mmc_irq(int irq, void *devid); -static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, - unsigned long *flags) -{ - local_irq_save(*flags); - return kmap_atomic(sg_page(sg)) + sg->offset; -} - -static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg, - unsigned long *flags, void *virt) -{ - kunmap_atomic(virt - sg->offset); - local_irq_restore(*flags); -} - #ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 437048bb8027..e24c3d284515 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -412,7 +412,6 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) void *sg_virt; unsigned short *buf; unsigned int count; - unsigned long flags; if (host->dma_on) { pr_err("PIO IRQ in DMA mode!\n"); @@ -422,8 +421,8 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) return; } - sg_virt = tmio_mmc_kmap_atomic(host->sg_ptr, &flags); - buf = (unsigned short *)(sg_virt + host->sg_off); + sg_virt = kmap_local_page(sg_page(host->sg_ptr)); + buf = (unsigned short *)(sg_virt + host->sg_ptr->offset + host->sg_off); count = host->sg_ptr->length - host->sg_off; if (count > data->blksz) @@ -437,7 +436,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) host->sg_off += count; - tmio_mmc_kunmap_atomic(host->sg_ptr, &flags, sg_virt); + kunmap_local(sg_virt); if (host->sg_off == host->sg_ptr->length) tmio_mmc_next_sg(host); @@ -446,11 +445,11 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host) static void tmio_mmc_check_bounce_buffer(struct tmio_mmc_host *host) { if (host->sg_ptr == &host->bounce_sg) { - unsigned long flags; - void *sg_vaddr = tmio_mmc_kmap_atomic(host->sg_orig, &flags); + void *sg_virt = kmap_local_page(sg_page(host->sg_orig)); - memcpy(sg_vaddr, host->bounce_buf, host->bounce_sg.length); - tmio_mmc_kunmap_atomic(host->sg_orig, &flags, sg_vaddr); + memcpy(sg_virt + host->sg_orig->offset, host->bounce_buf, + host->bounce_sg.length); + kunmap_local(sg_virt); } } @@ -670,6 +669,9 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host, int ireg, return true; } + if (host->dma_ops && host->dma_ops->dma_irq && host->dma_ops->dma_irq(host)) + return true; + return false; } diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index 8d037c2071ab..497791ffada6 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -651,7 +651,9 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto unmap; - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto free_irq; base = pci_resource_start(pdev, 0); dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq); @@ -660,6 +662,8 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; +free_irq: + free_irq(pdev->irq, host); unmap: pci_iounmap(pdev, host->ioaddr); release: diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 88662a90ed96..a2b0d9461665 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1151,7 +1151,9 @@ static int via_sd_probe(struct pci_dev *pcidev, pcidev->subsystem_device == 0x3891) sdhost->quirks = VIA_CRDR_QUIRK_300MS_PWRDELAY; - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto unmap; return 0; diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 97beece62fec..72f65f32abbc 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2049,6 +2049,7 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) return; kref_get(&vub300->kref); if (enable) { + set_current_state(TASK_RUNNING); mutex_lock(&vub300->irq_mutex); if (vub300->irqs_queued) { vub300->irqs_queued -= 1; @@ -2064,6 +2065,7 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) vub300_queue_poll_work(vub300, 0); } mutex_unlock(&vub300->irq_mutex); + set_current_state(TASK_INTERRUPTIBLE); } else { vub300->irq_enabled = 0; } @@ -2299,14 +2301,14 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (retval < 0) { - goto error4; + goto error5; } else if (sizeof(vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; vub300->read_only = (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; } else { - goto error4; + goto error5; } usb_set_intfdata(interface, vub300); INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); @@ -2329,8 +2331,13 @@ static int vub300_probe(struct usb_interface *interface, "USB vub300 remote SDIO host controller[%d]" "connected with no SD/SDIO card inserted\n", interface_to_InterfaceNumber(interface)); - mmc_add_host(mmc); + retval = mmc_add_host(mmc); + if (retval) + goto error6; + return 0; +error6: + del_timer_sync(&vub300->inactivity_timer); error5: mmc_free_host(mmc); /* diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 67ecd342fe5f..521af9251f33 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -267,7 +267,7 @@ static inline int wbsd_next_sg(struct wbsd_host *host) static inline char *wbsd_map_sg(struct wbsd_host *host) { - return kmap_atomic(sg_page(host->cur_sg)) + host->cur_sg->offset; + return kmap_local_page(sg_page(host->cur_sg)) + host->cur_sg->offset; } static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) @@ -439,7 +439,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { - kunmap_atomic(buffer); + kunmap_local(buffer); /* * Get next entry. Check if last. */ @@ -451,7 +451,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) } } } - kunmap_atomic(buffer); + kunmap_local(buffer); /* * This is a very dirty hack to solve a @@ -505,7 +505,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { - kunmap_atomic(buffer); + kunmap_local(buffer); /* * Get next entry. Check if last. */ @@ -517,7 +517,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) } } } - kunmap_atomic(buffer); + kunmap_local(buffer); /* * The controller stops sending interrupts for @@ -1698,7 +1698,17 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma, */ wbsd_init_device(host); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) { + if (!pnp) + wbsd_chip_poweroff(host); + + wbsd_release_resources(host); + wbsd_free_mmc(dev); + + mmc_free_host(mmc); + return ret; + } pr_info("%s: W83L51xD", mmc_hostname(mmc)); if (host->chip_id != 0) diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 9b5c503e3a3f..9aa3027ca25e 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -856,11 +856,15 @@ static int wmt_mci_probe(struct platform_device *pdev) /* configure the controller to a known 'ready' state */ wmt_reset_hardware(mmc); - mmc_add_host(mmc); + ret = mmc_add_host(mmc); + if (ret) + goto fail7; dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); return 0; +fail7: + clk_disable_unprepare(priv->clk_sdmmc); fail6: clk_put(priv->clk_sdmmc); fail5_and_a_half: diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 3c9da1f8979e..6f53ad74fa0d 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -605,6 +605,10 @@ struct iommu_group *fsl_mc_device_group(struct device *dev); * @flags: IOMMU_FWSPEC_* flags * @num_ids: number of associated device IDs * @ids: IDs which this device may present to the IOMMU + * + * Note that the IDs (and any other information, really) stored in this structure should be + * considered private to the IOMMU device driver and are not to be used directly by IOMMU + * consumers. */ struct iommu_fwspec { const struct iommu_ops *ops; @@ -1099,4 +1103,25 @@ static inline void iommu_dma_compose_msi_msg(struct msi_desc *desc, struct msi_m #endif /* CONFIG_IOMMU_DMA */ +/* + * Newer generations of Tegra SoCs require devices' stream IDs to be directly programmed into + * some registers. These are always paired with a Tegra SMMU or ARM SMMU, for which the contents + * of the struct iommu_fwspec are known. Use this helper to formalize access to these internals. + */ +#define TEGRA_STREAM_ID_BYPASS 0x7f + +static inline bool tegra_dev_iommu_get_stream_id(struct device *dev, u32 *stream_id) +{ +#ifdef CONFIG_IOMMU_API + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + + if (fwspec && fwspec->num_ids == 1) { + *stream_id = fwspec->ids[0] & 0xffff; + return true; + } +#endif + + return false; +} + #endif /* __LINUX_IOMMU_H */ diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index 27264fe4b3b9..e8bf90281ba0 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -102,7 +102,6 @@ struct tmio_mmc_data { unsigned long capabilities2; unsigned long flags; u32 ocr_mask; /* available voltages */ - int alignment_shift; dma_addr_t dma_rx_offset; unsigned int max_blk_count; unsigned short max_segs; |