Commit da15efe1 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mmc-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Extend slot-gpio to be used for host specific card detect interrupts
   - Align to common busy polling behaviour for mmc ioctls
   - Suggest the BFQ I/O scheduler to be built along with MMC/SD support
   - Add devm_mmc_alloc_host() to enable further cleanups in host drivers

  MMC host:
   - atmel-mci: Fix race condition when stopping/starting a command
   - dw_mmc-starfive: Add new driver to support the StarFive JH7110 variant
   - dw_mmc-rockchip: Add support for the RK3588 variant
   - jz4740: Add support for the vqmmc power supply
   - meson-gx: Convert the DT bindings to the dt-schema
   - meson-gx: Enable the platform interrupt to be used for card detect
   - moxart: Set the supported maximum request/block/segment sizes
   - renesas,sdhi: Add support for the RZ/V2M variants
   - sdhci: Rework code to drop SDHCI_QUIRK_MISSING_CAPS
   - sdhci-esdhc-imx: Improve tuning logic support
   - sdhci-msm: Add support for the IPQ5332 and the IPQ9574 variants
   - sdhci-of-dwcmshc: Add the missing device table IDs for acpi
   - sdhci-of-dwcmshc: Improve clock support for the Rockchip variant
   - sdhci-of-dwcmshc: Enable support of V4 host for the BlueField-3 variant
   - sdhci-pxav2: Add support for the PXA168 V1 variant
   - sdhci-pxav2: Add support for SDIO IRQs for the PXA168 V1 variant
   - uniphier-sd: Add support for SD UHS-I speed modes"

* tag 'mmc-v6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (59 commits)
  mmc: meson-gx: Use devm_platform_get_and_ioremap_resource()
  mmc: meson-gx: constify member data of struct meson_host
  mmc: meson-gx: use devm_clk_get_enabled() for core clock
  mmc: core: fix return value check in devm_mmc_alloc_host()
  dt-bindings: mmc: meson-gx: fix interrupt binding
  mmc: meson-gx: support platform interrupt as card detect interrupt
  dt-bindings: mmc: meson-gx: support specifying cd interrupt
  mmc: core: support setting card detect interrupt from drivers
  mmc: starfive: Add sdio/emmc driver support
  dt-bindings: mmc: Add StarFive MMC module
  dt-bindings: mmc: sdhci-msm: Allow 1 icc path
  dt-bindings: mmc: rockchip-dw-mshc: Add RK3588 compatible string
  mmc: core: Align to common busy polling behaviour for mmc ioctls
  dt-bindings: mmc: Add resets property to cadence SDHCI binding
  mmc: meson-gx: remove meson_mmc_get_cd
  mmc: moxart: set maximum request/block/segment sizes
  mmc: sdhci-brcmstb: Use devm_platform_get_and_ioremap_resource()
  mmc: sdhci-of-dwcmshc: add the missing device table IDs for acpi
  mmc: sdhci-of-dwcmshc: Update DLL and pre-change delay for rockchip platform
  mmc: jz4740: Add support for vqmmc power supply
  ...
parents 6ffb575b 571f2351
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Allwinner A10 MMC Controller
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
maintainers:
- Chen-Yu Tsai <wens@csie.org>
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/amlogic,meson-gx-mmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic SD / eMMC controller for S905/GXBB family SoCs
description:
The MMC 5.1 compliant host controller on Amlogic provides the
interface for SD, eMMC and SDIO devices
maintainers:
- Neil Armstrong <neil.armstrong@linaro.org>
allOf:
- $ref: mmc-controller.yaml#
properties:
compatible:
oneOf:
- const: amlogic,meson-axg-mmc
- items:
- const: amlogic,meson-gx-mmc
- const: amlogic,meson-gxbb-mmc
reg:
maxItems: 1
interrupts:
minItems: 1
items:
- description: mmc controller instance
- description: card detect
clocks:
maxItems: 3
clock-names:
items:
- const: core
- const: clkin0
- const: clkin1
resets:
maxItems: 1
amlogic,dram-access-quirk:
type: boolean
description:
set when controller's internal DMA engine cannot access the DRAM memory,
like on the G12A dedicated SDIO controller.
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- resets
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
mmc@70000 {
compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
reg = <0x70000 0x2000>;
interrupts = <GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
clocks = <&clk_mmc>, <&xtal>, <&clk_div>;
clock-names = "core", "clkin0", "clkin1";
pinctrl-0 = <&emm_pins>;
resets = <&reset_mmc>;
};
Amlogic SD / eMMC controller for S905/GXBB family SoCs
The MMC 5.1 compliant host controller on Amlogic provides the
interface for SD, eMMC and SDIO devices.
This file documents the properties in addition to those available in
the MMC core bindings, documented by mmc.txt.
Required properties:
- compatible : contains one of:
- "amlogic,meson-gx-mmc"
- "amlogic,meson-gxbb-mmc"
- "amlogic,meson-gxl-mmc"
- "amlogic,meson-gxm-mmc"
- "amlogic,meson-axg-mmc"
- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
- clock-names: Should contain the following:
"core" - Main peripheral bus clock
"clkin0" - Parent clock of internal mux
"clkin1" - Other parent clock of internal mux
The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the
clock rate requested by the MMC core.
- resets : phandle of the internal reset line
Optional properties:
- amlogic,dram-access-quirk: set when controller's internal DMA engine cannot access the
DRAM memory, like on the G12A dedicated SDIO controller.
Example:
sd_emmc_a: mmc@70000 {
compatible = "amlogic,meson-gxbb-mmc";
reg = <0x0 0x70000 0x0 0x2000>;
interrupts = < GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
clock-names = "core", "clkin0", "clkin1";
pinctrl-0 = <&emmc_pins>;
resets = <&reset RESET_SD_EMMC_A>;
};
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Amlogic Meson SDHC controller
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
maintainers:
- Martin Blumenstingl <martin.blumenstingl@googlemail.com>
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/mmc/arasan,sdhci.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
$id: http://devicetree.org/schemas/mmc/arasan,sdhci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Arasan SDHCI Controller
......@@ -10,7 +10,7 @@ maintainers:
- Adrian Hunter <adrian.hunter@intel.com>
allOf:
- $ref: "mmc-controller.yaml#"
- $ref: mmc-controller.yaml#
- if:
properties:
compatible:
......
......@@ -29,6 +29,9 @@ properties:
clocks:
maxItems: 1
resets:
maxItems: 1
# PHY DLL input delays:
# They are used to delay the data valid window, and align the window to
# sampling clock. The delay starts from 5ns (for delay parameter equal to 0)
......@@ -36,43 +39,43 @@ properties:
cdns,phy-input-delay-sd-highspeed:
description: Value of the delay in the input path for SD high-speed timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-legacy:
description: Value of the delay in the input path for legacy timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-sd-uhs-sdr12:
description: Value of the delay in the input path for SD UHS SDR12 timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-sd-uhs-sdr25:
description: Value of the delay in the input path for SD UHS SDR25 timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-sd-uhs-sdr50:
description: Value of the delay in the input path for SD UHS SDR50 timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-sd-uhs-ddr50:
description: Value of the delay in the input path for SD UHS DDR50 timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
cdns,phy-input-delay-mmc-highspeed:
description: Value of the delay in the input path for MMC high-speed timing
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
......@@ -83,7 +86,7 @@ properties:
# Each delay property represents the fraction of the clock period.
# The approximate delay value will be
# (<delay property value>/128)*sdmclk_clock_period.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x1f
......@@ -91,7 +94,7 @@ properties:
description: |
Value of the delay introduced on the sdclk output for all modes except
HS200, HS400 and HS400_ES.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x7f
......@@ -99,7 +102,7 @@ properties:
description: |
Value of the delay introduced on the sdclk output for HS200, HS400 and
HS400_ES speed modes.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x7f
......@@ -107,7 +110,7 @@ properties:
description: |
Value of the delay introduced on the dat_strobe input used in
HS400 / HS400_ES speed modes.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 0x7f
......
......@@ -10,7 +10,7 @@ maintainers:
- Shawn Guo <shawnguo@kernel.org>
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
description: |
The Enhanced Secure Digital Host Controller on Freescale i.MX family
......@@ -29,14 +29,23 @@ properties:
- fsl,imx53-esdhc
- fsl,imx6q-usdhc
- fsl,imx6sl-usdhc
- fsl,imx6sll-usdhc
- fsl,imx6sx-usdhc
- fsl,imx6ull-usdhc
- fsl,imx7d-usdhc
- fsl,imx7ulp-usdhc
- fsl,imx8mm-usdhc
- fsl,imxrt1050-usdhc
- nxp,s32g2-usdhc
- items:
- const: fsl,imx50-esdhc
- const: fsl,imx53-esdhc
- items:
- enum:
- fsl,imx6sll-usdhc
- fsl,imx6ull-usdhc
- const: fsl,imx6sx-usdhc
- items:
- const: fsl,imx7d-usdhc
- const: fsl,imx6sl-usdhc
- items:
- enum:
- fsl,imx8mq-usdhc
......@@ -98,12 +107,12 @@ properties:
Specify the number of delay cells for override mode.
This is used to set the clock delay for DLL(Delay Line) on override mode
to select a proper data sampling window in case the clock quality is not good
due to signal path is too long on the board. Please refer to eSDHC/uSDHC
because the signal path is too long on the board. Please refer to eSDHC/uSDHC
chapter, DLL (Delay Line) section in RM for details.
default: 0
voltage-ranges:
$ref: '/schemas/types.yaml#/definitions/uint32-matrix'
$ref: /schemas/types.yaml#/definitions/uint32-matrix
description: |
Specify the voltage range in case there are software transparent level
shifters on the outputs of the controller. Two cells are required, first
......@@ -127,7 +136,7 @@ properties:
Specify the increasing delay cell steps in tuning procedure.
The uSDHC use one delay cell as default increasing step to do tuning process.
This property allows user to change the tuning step to more than one delay
cells which is useful for some special boards or cards when the default
cell which is useful for some special boards or cards when the default
tuning step can't find the proper delay window within limited tuning retries.
default: 0
......
......@@ -10,7 +10,7 @@ maintainers:
- Markus Pargmann <mpa@pengutronix.de>
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
properties:
compatible:
......
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip Sparx5 Mobile Storage Host Controller
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
maintainers:
- Lars Povlsen <lars.povlsen@microchip.com>
......@@ -35,7 +35,7 @@ properties:
microchip,clock-delay:
description: Delay clock to card to meet setup time requirements.
Each step increase by 1.25ns.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 15
......
......@@ -41,7 +41,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
sdhci0_pwrseq {
pwrseq {
compatible = "mmc-pwrseq-emmc";
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
};
......
......@@ -35,7 +35,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
wifi_pwrseq: wifi_pwrseq {
pwrseq {
compatible = "mmc-pwrseq-sd8787";
powerdown-gpios = <&twl_gpio 0 GPIO_ACTIVE_LOW>;
reset-gpios = <&twl_gpio 1 GPIO_ACTIVE_LOW>;
......
......@@ -55,7 +55,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
sdhci0_pwrseq {
pwrseq {
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
clocks = <&clk_32768_ck>;
......
......@@ -10,7 +10,7 @@ maintainers:
- Ulf Hansson <ulf.hansson@linaro.org>
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
- $ref: /schemas/spi/spi-peripheral-props.yaml
description: |
......
......@@ -17,7 +17,7 @@ description: |
and the properties used by the mxsmmc driver.
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
properties:
compatible:
......
......@@ -99,53 +99,53 @@ properties:
The DQS trim values are only used on controllers which support HS400
timing. Only SDMMC4 on Tegra210 and Tegra186 supports HS400.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,default-trim:
description: Specify the default outbound clock trimmer value.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,dqs-trim:
description: Specify DQS trim value for HS400 timing.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-1v8:
description: Specify drive strength calibration offsets for 1.8 V
signaling modes.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-1v8-timeout:
description: Specify drive strength used as a fallback in case the
automatic calibration times out on a 1.8 V signaling mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-3v3:
description: Specify drive strength calibration offsets for 3.3 V
signaling modes.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-3v3-timeout:
description: Specify drive strength used as a fallback in case the
automatic calibration times out on a 3.3 V signaling mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-sdr104:
description: Specify drive strength calibration offsets for SDR104 mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-down-offset-hs400:
description: Specify drive strength calibration offsets for HS400 mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-1v8:
description: Specify drive strength calibration offsets for 1.8 V
signaling modes.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-1v8-timeout:
description: Specify drive strength used as a fallback in case the
automatic calibration times out on a 1.8 V signaling mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-3v3:
description: Specify drive strength calibration offsets for 3.3 V
......@@ -157,25 +157,25 @@ properties:
refer to the reference manual of the SoC for correct values. The SDR104
and HS400 timing specific values are used in corresponding modes if
specified.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-3v3-timeout:
description: Specify drive strength used as a fallback in case the
automatic calibration times out on a 3.3 V signaling mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-sdr104:
description: Specify drive strength calibration offsets for SDR104 mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,pad-autocal-pull-up-offset-hs400:
description: Specify drive strength calibration offsets for HS400 mode.
$ref: "/schemas/types.yaml#/definitions/uint32"
$ref: /schemas/types.yaml#/definitions/uint32
nvidia,only-1-8v:
description: The presence of this property indicates that the controller
operates at a 1.8 V fixed I/O voltage.
$ref: "/schemas/types.yaml#/definitions/flag"
$ref: /schemas/types.yaml#/definitions/flag
required:
- compatible
......@@ -186,7 +186,7 @@ required:
- reset-names
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
- if:
properties:
compatible:
......
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Actions Semi Owl SoCs SD/MMC/SDIO controller
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
maintainers:
- Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
......
......@@ -10,7 +10,7 @@ maintainers:
- Wolfram Sang <wsa+renesas@sang-engineering.com>
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
properties:
compatible:
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/mmc/renesas,sdhi.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
$id: http://devicetree.org/schemas/mmc/renesas,sdhi.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas SDHI SD/MMC controller
......@@ -59,6 +59,7 @@ properties:
- renesas,sdhi-r9a07g043 # RZ/G2UL
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
- renesas,sdhi-r9a07g054 # RZ/V2L
- renesas,sdhi-r9a09g011 # RZ/V2M
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
- items:
- enum:
......@@ -111,7 +112,7 @@ properties:
max-frequency: true
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
- if:
properties:
......@@ -121,6 +122,7 @@ allOf:
- renesas,sdhi-r9a07g043
- renesas,sdhi-r9a07g044
- renesas,sdhi-r9a07g054
- renesas,sdhi-r9a09g011
then:
properties:
clocks:
......
......@@ -14,7 +14,7 @@ description:
file and the Rockchip specific extensions.
allOf:
- $ref: "synopsys-dw-mshc-common.yaml#"
- $ref: synopsys-dw-mshc-common.yaml#
maintainers:
- Heiko Stuebner <heiko@sntech.de>
......@@ -39,6 +39,7 @@ properties:
- rockchip,rk3368-dw-mshc
- rockchip,rk3399-dw-mshc
- rockchip,rk3568-dw-mshc
- rockchip,rk3588-dw-mshc
- rockchip,rv1108-dw-mshc
- rockchip,rv1126-dw-mshc
- const: rockchip,rk3288-dw-mshc
......
......@@ -112,7 +112,7 @@ required:
- samsung,dw-mshc-sdr-timing
allOf:
- $ref: "synopsys-dw-mshc-common.yaml#"
- $ref: synopsys-dw-mshc-common.yaml#
- if:
properties:
compatible:
......
......@@ -34,6 +34,8 @@ properties:
- const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
- items:
- enum:
- qcom,ipq5332-sdhci
- qcom,ipq9574-sdhci
- qcom,qcs404-sdhci
- qcom,sc7180-sdhci
- qcom,sc7280-sdhci
......@@ -125,11 +127,13 @@ properties:
phandle to apps_smmu node with sid mask.
interconnects:
minItems: 1
items:
- description: data path, sdhc to ddr
- description: config path, cpu to sdhc
interconnect-names:
minItems: 1
items:
- const: sdhc-ddr
- const: cpu-sdhc
......
......@@ -4,7 +4,7 @@
$id: http://devicetree.org/schemas/mmc/sdhci-pxa.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Marvell PXA SDHCI v2/v3
title: Marvell PXA SDHCI v1/v2/v3
maintainers:
- Ulf Hansson <ulf.hansson@linaro.org>
......@@ -34,6 +34,7 @@ allOf:
properties:
compatible:
enum:
- mrvl,pxav1-mmc
- mrvl,pxav2-mmc
- mrvl,pxav3-mmc
- marvell,armada-380-sdhci
......@@ -61,6 +62,22 @@ properties:
- const: io
- const: core
pinctrl-names:
description:
Optional for supporting PXA168 SDIO IRQ errata to switch CMD pin between
SDIO CMD and GPIO mode.
items:
- const: default
- const: state_cmd_gpio
pinctrl-0:
description:
Should contain default pinctrl.
pinctrl-1:
description:
Should switch CMD pin to GPIO mode as a high output.
mrvl,clk-delay-cycles:
description: Specify a number of cycles to delay for tuning.
$ref: /schemas/types.yaml#/definitions/uint32
......
......@@ -55,6 +55,16 @@ properties:
minItems: 1
maxItems: 3
socionext,syscon-uhs-mode:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: phandle to syscon that configures UHS mode
- description: ID of SD instance
description:
A phandle to syscon with one argument that configures UHS mode.
The argument is the ID of SD instance.
allOf:
- $ref: mmc-controller.yaml
......
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/starfive,jh7110-mmc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: StarFive Designware Mobile Storage Host Controller
description:
StarFive uses the Synopsys designware mobile storage host controller
to interface a SoC with storage medium such as eMMC or SD/MMC cards.
allOf:
- $ref: synopsys-dw-mshc-common.yaml#
maintainers:
- William Qiu <william.qiu@starfivetech.com>
properties:
compatible:
const: starfive,jh7110-mmc
reg:
maxItems: 1
clocks:
items:
- description: biu clock
- description: ciu clock
clock-names:
items:
- const: biu
- const: ciu
interrupts:
maxItems: 1
starfive,sysreg:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: phandle to System Register Controller syscon node
- description: offset of SYS_SYSCONSAIF__SYSCFG register for MMC controller
- description: shift of SYS_SYSCONSAIF__SYSCFG register for MMC controller
- description: mask of SYS_SYSCONSAIF__SYSCFG register for MMC controller
description:
Should be four parameters, the phandle to System Register Controller
syscon node and the offset/shift/mask of SYS_SYSCONSAIF__SYSCFG register
for MMC controller.
required:
- compatible
- reg
- clocks
- clock-names
- interrupts
- starfive,sysreg
unevaluatedProperties: false
examples:
- |
mmc@16010000 {
compatible = "starfive,jh7110-mmc";
reg = <0x16010000 0x10000>;
clocks = <&syscrg 91>,
<&syscrg 93>;
clock-names = "biu","ciu";
resets = <&syscrg 64>;
reset-names = "reset";
interrupts = <74>;
fifo-depth = <32>;
fifo-watermark-aligned;
data-addr = <0>;
starfive,sysreg = <&sys_syscon 0x14 0x1a 0x7c000000>;
};
......@@ -12,7 +12,7 @@ maintainers:
- Li-hao Kuo <lhjeff911@gmail.com>
allOf:
- $ref: "mmc-controller.yaml"
- $ref: mmc-controller.yaml
properties:
compatible:
......
......@@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
title: Synopsys Designware Mobile Storage Host Controller Common Properties
allOf:
- $ref: "mmc-controller.yaml#"
- $ref: mmc-controller.yaml#
maintainers:
- Ulf Hansson <ulf.hansson@linaro.org>
......
......@@ -19924,6 +19924,12 @@ F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml
F: drivers/clk/starfive/clk-starfive-jh7100*
F: include/dt-bindings/clock/starfive-jh7100*.h
STARFIVE JH7110 MMC/SD/SDIO DRIVER
M: William Qiu <william.qiu@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/mmc/starfive*
F: drivers/mmc/host/dw_mmc-starfive.c
STARFIVE JH71X0 PINCTRL DRIVERS
M: Emil Renner Berthing <kernel@esmil.dk>
M: Jianlong Huang <jianlong.huang@starfivetech.com>
......
......@@ -20,6 +20,7 @@ config MEMSTICK_UNSAFE_RESUME
config MSPRO_BLOCK
tristate "MemoryStick Pro block device driver"
depends on BLOCK
imply IOSCHED_BFQ
help
Say Y here to enable the MemoryStick Pro block device driver
support. This provides a block device driver, which you can use
......@@ -29,6 +30,7 @@ config MSPRO_BLOCK
config MS_BLOCK
tristate "MemoryStick Standard device driver"
depends on BLOCK
imply IOSCHED_BFQ
help
Say Y here to enable the MemoryStick Standard device driver
support. This provides a block device driver, which you can use
......
......@@ -15,7 +15,7 @@ config PWRSEQ_EMMC
config PWRSEQ_SD8787
tristate "HW reset support for SD8787 BT + Wifi module"
depends on OF && (MWIFIEX || BT_MRVL_SDIO || LIBERTAS_SDIO || WILC1000_SDIO)
depends on OF && (MWIFIEX != n || BT_MRVL_SDIO != n || LIBERTAS_SDIO != n || WILC1000_SDIO != n)
help
This selects hardware reset support for the SD8787 BT + Wifi
module. By default this option is set to n.
......@@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE
config MMC_BLOCK
tristate "MMC block device driver"
depends on BLOCK
imply IOSCHED_BFQ
default y
help
Say Y here to enable the MMC block device driver support.
......
......@@ -470,6 +470,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_data data = {};
struct mmc_request mrq = {};
struct scatterlist sg;
bool r1b_resp, use_r1b_resp = false;
unsigned int busy_timeout_ms;
int err;
unsigned int target_part;
......@@ -545,6 +547,13 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
(cmd.opcode == MMC_SWITCH))
return mmc_sanitize(card, idata->ic.cmd_timeout_ms);
/* If it's an R1B response we need some more preparations. */
busy_timeout_ms = idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS;
r1b_resp = (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B;
if (r1b_resp)
use_r1b_resp = mmc_prepare_busy_cmd(card->host, &cmd,
busy_timeout_ms);
mmc_wait_for_req(card->host, &mrq);
memcpy(&idata->ic.response, cmd.resp, sizeof(cmd.resp));
......@@ -596,14 +605,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
if (idata->ic.postsleep_min_us)
usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us);
if (idata->rpmb || (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
/*
* Ensure RPMB/R1B command has completed by polling CMD13 "Send Status". Here we
* allow to override the default timeout value if a custom timeout is specified.
*/
err = mmc_poll_for_busy(card, idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS,
false, MMC_BUSY_IO);
}
/* No need to poll when using HW busy detection. */
if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
return 0;
/* Ensure RPMB/R1B command has completed by polling with CMD13. */
if (idata->rpmb || r1b_resp)
err = mmc_poll_for_busy(card, busy_timeout_ms, false,
MMC_BUSY_IO);
return err;
}
......
......@@ -588,6 +588,32 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
EXPORT_SYMBOL(mmc_alloc_host);
static void devm_mmc_host_release(struct device *dev, void *res)
{
mmc_free_host(*(struct mmc_host **)res);
}
struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra)
{
struct mmc_host **dr, *host;
dr = devres_alloc(devm_mmc_host_release, sizeof(*dr), GFP_KERNEL);
if (!dr)
return NULL;
host = mmc_alloc_host(extra, dev);
if (!host) {
devres_free(dr);
return NULL;
}
*dr = host;
devres_add(dev, dr);
return host;
}
EXPORT_SYMBOL(devm_mmc_alloc_host);
static int mmc_validate_host_caps(struct mmc_host *host)
{
struct device *dev = host->parent;
......
......@@ -575,6 +575,7 @@ bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd,
cmd->busy_timeout = timeout_ms;
return true;
}
EXPORT_SYMBOL_GPL(mmc_prepare_busy_cmd);
/**
* __mmc_switch - modify EXT_CSD register
......
......@@ -119,14 +119,14 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev)
pwrseq->ext_clk = devm_clk_get(dev, "ext_clock");
if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT)
return PTR_ERR(pwrseq->ext_clk);
return dev_err_probe(dev, PTR_ERR(pwrseq->ext_clk), "external clock not ready\n");
pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset",
GPIOD_OUT_HIGH);
if (IS_ERR(pwrseq->reset_gpios) &&
PTR_ERR(pwrseq->reset_gpios) != -ENOENT &&
PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) {
return PTR_ERR(pwrseq->reset_gpios);
return dev_err_probe(dev, PTR_ERR(pwrseq->reset_gpios), "reset GPIOs not ready\n");
}
device_property_read_u32(dev, "post-power-on-delay-ms",
......
......@@ -766,7 +766,7 @@ EXPORT_SYMBOL_GPL(sdio_retune_crc_disable);
* sdio_retune_crc_enable - re-enable retuning on CRC errors
* @func: SDIO function attached to host
*
* This is the compement to sdio_retune_crc_disable().
* This is the complement to sdio_retune_crc_disable().
*/
void sdio_retune_crc_enable(struct sdio_func *func)
{
......
......@@ -23,6 +23,7 @@ struct mmc_gpio {
char *ro_label;
char *cd_label;
u32 cd_debounce_delay_ms;
int cd_irq;
};
static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id)
......@@ -53,12 +54,24 @@ int mmc_gpio_alloc(struct mmc_host *host)
ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname);
if (!ctx->ro_label)
return -ENOMEM;
ctx->cd_irq = -EINVAL;
host->slot.handler_priv = ctx;
host->slot.cd_irq = -EINVAL;
return 0;
}
void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
if (!ctx || irq < 0)
return;
ctx->cd_irq = irq;
}
EXPORT_SYMBOL(mmc_gpio_set_cd_irq);
int mmc_gpio_get_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
......@@ -98,7 +111,9 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host)
* Do not use IRQ if the platform prefers to poll, e.g., because that
* IRQ number is already used by another unit and cannot be shared.
*/
if (!(host->caps & MMC_CAP_NEEDS_POLL))
if (ctx->cd_irq >= 0)
irq = ctx->cd_irq;
else if (!(host->caps & MMC_CAP_NEEDS_POLL))
irq = gpiod_to_irq(ctx->cd_gpio);
if (irq >= 0) {
......
......@@ -348,6 +348,7 @@ config MMC_SDHCI_PXAV2
depends on MMC_SDHCI_PLTFM
depends on ARCH_MMP || COMPILE_TEST
default CPU_PXA910
select MMC_SDHCI_IO_ACCESSORS
help
This selects the Marvell(R) PXAV2 SD Host Controller.
If you have a PXA9XX platform with SD Host Controller
......@@ -816,6 +817,16 @@ config MMC_DW_ROCKCHIP
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
config MMC_DW_STARFIVE
tristate "StarFive specific extensions for Synopsys DW Memory Card Interface"
depends on SOC_STARFIVE
depends on MMC_DW
select MMC_DW_PLTFM
help
This selects support for StarFive JH7110 SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on StarFive JH7110 SoC.
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
......
......@@ -54,6 +54,7 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
obj-$(CONFIG_MMC_DW_STARFIVE) += dw_mmc-starfive.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
......
......@@ -1817,7 +1817,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY);
state = STATE_WAITING_NOTBUSY;
} else if (host->mrq->stop) {
atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY);
atmci_send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
} else {
......@@ -1850,8 +1849,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t)
* command to send.
*/
if (host->mrq->stop) {
atmci_writel(host, ATMCI_IER,
ATMCI_CMDRDY);
atmci_send_stop_cmd(host, data);
state = STATE_SENDING_STOP;
} else {
......
// SPDX-License-Identifier: GPL-2.0
/*
* StarFive Designware Mobile Storage Host Controller Driver
*
* Copyright (c) 2022 StarFive Technology Co., Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/mfd/syscon.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
#define ALL_INT_CLR 0x1ffff
#define MAX_DELAY_CHAIN 32
struct starfive_priv {
struct device *dev;
struct regmap *reg_syscon;
u32 syscon_offset;
u32 syscon_shift;
u32 syscon_mask;
};
static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
int ret;
unsigned int clock;
if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) {
clock = (ios->clock > 50000000 && ios->clock <= 52000000) ? 100000000 : ios->clock;
ret = clk_set_rate(host->ciu_clk, clock);
if (ret)
dev_dbg(host->dev, "Use an external frequency divider %uHz\n", ios->clock);
host->bus_hz = clk_get_rate(host->ciu_clk);
} else {
dev_dbg(host->dev, "Using the internal divider\n");
}
}
static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot,
u32 opcode)
{
static const int grade = MAX_DELAY_CHAIN;
struct dw_mci *host = slot->host;
struct starfive_priv *priv = host->priv;
int rise_point = -1, fall_point = -1;
int err, prev_err;
int i;
bool found = 0;
u32 regval;
/*
* Use grade as the max delay chain, and use the rise_point and
* fall_point to ensure the best sampling point of a data input
* signals.
*/
for (i = 0; i < grade; i++) {
regval = i << priv->syscon_shift;
err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
priv->syscon_mask, regval);
if (err)
return err;
mci_writel(host, RINTSTS, ALL_INT_CLR);
err = mmc_send_tuning(slot->mmc, opcode, NULL);
if (!err)
found = 1;
if (i > 0) {
if (err && !prev_err)
fall_point = i - 1;
if (!err && prev_err)
rise_point = i;
}
if (rise_point != -1 && fall_point != -1)
goto tuning_out;
prev_err = err;
err = 0;
}
tuning_out:
if (found) {
if (rise_point == -1)
rise_point = 0;
if (fall_point == -1)
fall_point = grade - 1;
if (fall_point < rise_point) {
if ((rise_point + fall_point) >
(grade - 1))
i = fall_point / 2;
else
i = (rise_point + grade - 1) / 2;
} else {
i = (rise_point + fall_point) / 2;
}
regval = i << priv->syscon_shift;
err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset,
priv->syscon_mask, regval);
if (err)
return err;
mci_writel(host, RINTSTS, ALL_INT_CLR);
dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i);
} else {
dev_err(host->dev, "No valid delay chain! use default\n");
err = -EINVAL;
}
mci_writel(host, RINTSTS, ALL_INT_CLR);
return err;
}
static int dw_mci_starfive_parse_dt(struct dw_mci *host)
{
struct of_phandle_args args;
struct starfive_priv *priv;
int ret;
priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
ret = of_parse_phandle_with_fixed_args(host->dev->of_node,
"starfive,sysreg", 3, 0, &args);
if (ret) {
dev_err(host->dev, "Failed to parse starfive,sysreg\n");
return -EINVAL;
}
priv->reg_syscon = syscon_node_to_regmap(args.np);
of_node_put(args.np);
if (IS_ERR(priv->reg_syscon))
return PTR_ERR(priv->reg_syscon);
priv->syscon_offset = args.args[0];
priv->syscon_shift = args.args[1];
priv->syscon_mask = args.args[2];
host->priv = priv;
return 0;
}
static const struct dw_mci_drv_data starfive_data = {
.common_caps = MMC_CAP_CMD23,
.set_ios = dw_mci_starfive_set_ios,
.parse_dt = dw_mci_starfive_parse_dt,
.execute_tuning = dw_mci_starfive_execute_tuning,
};
static const struct of_device_id dw_mci_starfive_match[] = {
{ .compatible = "starfive,jh7110-mmc",
.data = &starfive_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_starfive_match);
static int dw_mci_starfive_probe(struct platform_device *pdev)
{
return dw_mci_pltfm_register(pdev, &starfive_data);
}
static struct platform_driver dw_mci_starfive_driver = {
.probe = dw_mci_starfive_probe,
.remove = dw_mci_pltfm_remove,
.driver = {
.name = "dwmmc_starfive",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
.of_match_table = dw_mci_starfive_match,
},
};
module_platform_driver(dw_mci_starfive_driver);
MODULE_DESCRIPTION("StarFive JH7110 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:dwmmc_starfive");
......@@ -21,6 +21,7 @@
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/scatterlist.h>
#include <asm/cacheflush.h>
......@@ -158,6 +159,8 @@ struct jz4740_mmc_host {
struct mmc_request *req;
struct mmc_command *cmd;
bool vqmmc_enabled;
unsigned long waiting;
uint32_t cmdat;
......@@ -935,6 +938,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct jz4740_mmc_host *host = mmc_priv(mmc);
int ret;
if (ios->clock)
jz4740_mmc_set_clock_rate(host, ios->clock);
......@@ -947,12 +952,25 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_prepare_enable(host->clk);
break;
case MMC_POWER_ON:
if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
ret = regulator_enable(mmc->supply.vqmmc);
if (ret)
dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n");
else
host->vqmmc_enabled = true;
}
break;
default:
case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
regulator_disable(mmc->supply.vqmmc);
host->vqmmc_enabled = false;
}
clk_disable_unprepare(host->clk);
break;
default:
break;
}
switch (ios->bus_width) {
......@@ -978,6 +996,23 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable);
}
static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
int ret;
/* vqmmc regulator is available */
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
return ret < 0 ? ret : 0;
}
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
return 0;
return -EINVAL;
}
static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request,
.pre_req = jz4740_mmc_pre_request,
......@@ -986,6 +1021,7 @@ static const struct mmc_host_ops jz4740_mmc_ops = {
.get_ro = mmc_gpio_get_ro,
.get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
.start_signal_voltage_switch = jz4740_voltage_switch,
};
static const struct of_device_id jz4740_mmc_of_match[] = {
......
......@@ -150,12 +150,11 @@ struct sd_emmc_desc {
struct meson_host {
struct device *dev;
struct meson_mmc_data *data;
const struct meson_mmc_data *data;
struct mmc_host *mmc;
struct mmc_command *cmd;
void __iomem *regs;
struct clk *core_clk;
struct clk *mux_clk;
struct clk *mmc_clk;
unsigned long req_rate;
......@@ -1083,20 +1082,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
return IRQ_HANDLED;
}
/*
* NOTE: we only need this until the GPIO/pinctrl driver can handle
* interrupts. For now, the MMC core will use this for polling.
*/
static int meson_mmc_get_cd(struct mmc_host *mmc)
{
int status = mmc_gpio_get_cd(mmc);
if (status == -ENOSYS)
return 1; /* assume present */
return status;
}
static void meson_mmc_cfg_init(struct meson_host *host)
{
u32 cfg = 0;
......@@ -1165,7 +1150,7 @@ static void meson_mmc_ack_sdio_irq(struct mmc_host *mmc)
static const struct mmc_host_ops meson_mmc_ops = {
.request = meson_mmc_request,
.set_ios = meson_mmc_set_ios,
.get_cd = meson_mmc_get_cd,
.get_cd = mmc_gpio_get_cd,
.pre_req = meson_mmc_pre_req,
.post_req = meson_mmc_post_req,
.execute_tuning = meson_mmc_resampling_tuning,
......@@ -1180,9 +1165,10 @@ static int meson_mmc_probe(struct platform_device *pdev)
struct resource *res;
struct meson_host *host;
struct mmc_host *mmc;
int ret;
struct clk *core_clk;
int cd_irq, ret;
mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev);
mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host));
if (!mmc)
return -ENOMEM;
host = mmc_priv(mmc);
......@@ -1198,51 +1184,39 @@ static int meson_mmc_probe(struct platform_device *pdev)
host->vqmmc_enabled = false;
ret = mmc_regulator_get_supply(mmc);
if (ret)
goto free_host;
return ret;
ret = mmc_of_parse(mmc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
goto free_host;
}
if (ret)
return dev_err_probe(&pdev->dev, ret, "error parsing DT\n");
mmc->caps |= MMC_CAP_CMD23;
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
host->data = (struct meson_mmc_data *)
of_device_get_match_data(&pdev->dev);
if (!host->data) {
ret = -EINVAL;
goto free_host;
}
host->data = of_device_get_match_data(&pdev->dev);
if (!host->data)
return -EINVAL;
ret = device_reset_optional(&pdev->dev);
if (ret) {
dev_err_probe(&pdev->dev, ret, "device reset failed\n");
goto free_host;
}
if (ret)
return dev_err_probe(&pdev->dev, ret, "device reset failed\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->regs)) {
ret = PTR_ERR(host->regs);
goto free_host;
}
host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(host->regs))
return PTR_ERR(host->regs);
host->irq = platform_get_irq(pdev, 0);
if (host->irq <= 0) {
ret = -EINVAL;
goto free_host;
}
if (host->irq <= 0)
return -EINVAL;
cd_irq = platform_get_irq_optional(pdev, 1);
mmc_gpio_set_cd_irq(mmc, cd_irq);
host->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(host->pinctrl)) {
ret = PTR_ERR(host->pinctrl);
goto free_host;
}
if (IS_ERR(host->pinctrl))
return PTR_ERR(host->pinctrl);
host->pins_clk_gate = pinctrl_lookup_state(host->pinctrl,
"clk-gate");
......@@ -1252,19 +1226,13 @@ static int meson_mmc_probe(struct platform_device *pdev)
host->pins_clk_gate = NULL;
}
host->core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(host->core_clk)) {
ret = PTR_ERR(host->core_clk);
goto free_host;
}
ret = clk_prepare_enable(host->core_clk);
if (ret)
goto free_host;
core_clk = devm_clk_get_enabled(&pdev->dev, "core");
if (IS_ERR(core_clk))
return PTR_ERR(core_clk);
ret = meson_mmc_clk_init(host);
if (ret)
goto err_core_clk;
return ret;
/* set config to sane default */
meson_mmc_cfg_init(host);
......@@ -1348,10 +1316,6 @@ static int meson_mmc_probe(struct platform_device *pdev)
free_irq(host->irq, host);
err_init_clk:
clk_disable_unprepare(host->mmc_clk);
err_core_clk:
clk_disable_unprepare(host->core_clk);
free_host:
mmc_free_host(mmc);
return ret;
}
......@@ -1366,9 +1330,7 @@ static int meson_mmc_remove(struct platform_device *pdev)
free_irq(host->irq, host);
clk_disable_unprepare(host->mmc_clk);
clk_disable_unprepare(host->core_clk);
mmc_free_host(host->mmc);
return 0;
}
......
......@@ -611,6 +611,9 @@ static int moxart_probe(struct platform_device *pdev)
mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2);
mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2);
mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */
mmc->max_blk_size = 2048; /* Max. block length in REG_DATA_CONTROL */
mmc->max_req_size = DATA_LEN_MASK; /* bits 0-23 in REG_DATA_LENGTH */
mmc->max_blk_count = mmc->max_req_size / 512;
if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) {
if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER ||
......@@ -628,6 +631,8 @@ static int moxart_probe(struct platform_device *pdev)
}
dev_dbg(dev, "PIO mode transfer enabled\n");
host->have_dma = false;
mmc->max_seg_size = mmc->max_req_size;
} else {
dev_dbg(dev, "DMA channels found (%p,%p)\n",
host->dma_chan_tx, host->dma_chan_rx);
......@@ -646,6 +651,10 @@ static int moxart_probe(struct platform_device *pdev)
cfg.src_addr = host->reg_phys + REG_DATA_WINDOW;
cfg.dst_addr = 0;
dmaengine_slave_config(host->dma_chan_rx, &cfg);
mmc->max_seg_size = min3(mmc->max_req_size,
dma_get_max_seg_size(host->dma_chan_rx->device->dev),
dma_get_max_seg_size(host->dma_chan_tx->device->dev));
}
if (readl(host->base + REG_BUS_WIDTH) & BUS_WIDTH_4_SUPPORT)
......
......@@ -210,6 +210,11 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
.manual_tap_correction = true,
};
static const struct renesas_sdhi_quirks sdhi_quirks_r9a09g011 = {
.fixed_addr_mode = true,
.hs400_disabled = true,
};
/*
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
* So, we want to treat them equally and only have a match for ES1.2 to enforce
......@@ -251,6 +256,11 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = {
.quirks = &sdhi_quirks_r8a77990,
};
static const struct renesas_sdhi_of_data_with_quirks of_r9a09g011_compatible = {
.of_data = &of_data_rcar_gen3,
.quirks = &sdhi_quirks_r9a09g011,
};
static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = {
.of_data = &of_data_rcar_gen3,
};
......@@ -274,6 +284,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
{ .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, },
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
{ .compatible = "renesas,sdhi-r9a09g011", .data = &of_r9a09g011_compatible, },
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },
{},
......
......@@ -255,7 +255,6 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
struct sdhci_brcmstb_priv *priv;
u32 actual_clock_mhz;
struct sdhci_host *host;
struct resource *iomem;
struct clk *clk;
struct clk *base_clk = NULL;
int res;
......@@ -291,8 +290,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
}
/* Map in the non-standard CFG registers */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL);
if (IS_ERR(priv->cfg_regs)) {
res = PTR_ERR(priv->cfg_regs);
goto err;
......@@ -324,13 +322,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
* will allow these modes to be specified by device tree
* properties through mmc_of_parse().
*/
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
sdhci_read_caps(host);
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_NO_64BIT)
host->caps &= ~SDHCI_CAN_64BIT;
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT)
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
......
......@@ -338,6 +338,16 @@ struct pltfm_imx_data {
struct clk *clk_ahb;
struct clk *clk_per;
unsigned int actual_clock;
/*
* USDHC has one limition, require the SDIO device a different
* register setting. Driver has to recognize card type during
* the card init, but at this stage, mmc_host->card is not
* available. So involve this field to save the card type
* during card init through usdhc_init_card().
*/
unsigned int init_card_type;
enum {
NO_CMD_PENDING, /* no multiblock command pending */
MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */
......@@ -430,9 +440,12 @@ static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host)
}
/* Enable the auto tuning circuit to check the CMD line and BUS line */
static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
static inline void usdhc_auto_tuning_mode_sel_and_en(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 buswidth, auto_tune_buswidth;
u32 reg;
buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL));
......@@ -448,9 +461,27 @@ static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host)
break;
}
/*
* For USDHC, auto tuning circuit can not handle the async sdio
* device interrupt correctly. When sdio device use 4 data lines,
* async sdio interrupt will use the shared DAT[1], if enable auto
* tuning circuit check these 4 data lines, include the DAT[1],
* this circuit will detect this interrupt, take this as a data on
* DAT[1], and adjust the delay cell wrongly.
* This is the hardware design limitation, to avoid this, for sdio
* device, config the auto tuning circuit only check DAT[0] and CMD
* line.
*/
if (imx_data->init_card_type == MMC_TYPE_SDIO)
auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN;
esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK,
auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN,
ESDHC_VEND_SPEC2);
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
......@@ -682,14 +713,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
} else {
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
}
if (val & SDHCI_CTRL_EXEC_TUNING) {
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
usdhc_auto_tuning_mode_sel(host);
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
......@@ -1023,13 +1051,15 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
/* Reset the tuning circuit */
if (esdhc_is_usdhc(imx_data)) {
ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN;
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) {
writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
......@@ -1052,9 +1082,19 @@ static void esdhc_reset_tuning(struct sdhci_host *host)
}
}
static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
imx_data->init_card_type = card->type;
}
static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
int err;
/*
* i.MX uSDHC internally already uses a fixed optimized timing for
......@@ -1069,7 +1109,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
* correct delay cell.
*/
esdhc_reset_tuning(host);
return sdhci_execute_tuning(mmc, opcode);
err = sdhci_execute_tuning(mmc, opcode);
/* If tuning done, enable auto tuning */
if (!err && !host->tuning_err)
usdhc_auto_tuning_mode_sel_and_en(host);
return err;
}
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
......@@ -1103,11 +1148,8 @@ static void esdhc_post_tuning(struct sdhci_host *host)
{
u32 reg;
usdhc_auto_tuning_mode_sel(host);
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg &= ~ESDHC_MIX_CTRL_EXE_TUNE;
reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN;
writel(reg, host->ioaddr + ESDHC_MIX_CTRL);
}
......@@ -1674,6 +1716,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
* to replace the standard one in sdhci_ops.
*/
host->mmc_host_ops.execute_tuning = usdhc_execute_tuning;
/*
* Link usdhc specific mmc_host_ops init card function,
* to distinguish the card type.
*/
host->mmc_host_ops.init_card = usdhc_init_card;
}
err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data);
......
......@@ -18,6 +18,7 @@ struct sdhci_iproc_data {
u32 caps;
u32 caps1;
u32 mmc_caps;
bool missing_caps;
};
struct sdhci_iproc_host {
......@@ -251,7 +252,6 @@ static const struct sdhci_iproc_data iproc_data = {
static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_NO_HISPD_BIT,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_iproc_32only_ops,
......@@ -266,6 +266,7 @@ static const struct sdhci_iproc_data bcm2835_data = {
.caps1 = SDHCI_DRIVER_TYPE_A |
SDHCI_DRIVER_TYPE_C,
.mmc_caps = 0x00000000,
.missing_caps = true,
};
static const struct sdhci_ops sdhci_iproc_bcm2711_ops = {
......@@ -295,8 +296,7 @@ static const struct sdhci_iproc_data bcm2711_data = {
};
static const struct sdhci_pltfm_data sdhci_bcm7211a0_pltfm_data = {
.quirks = SDHCI_QUIRK_MISSING_CAPS |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
.quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
SDHCI_QUIRK_BROKEN_DMA |
SDHCI_QUIRK_BROKEN_ADMA,
.ops = &sdhci_iproc_ops,
......@@ -315,6 +315,7 @@ static const struct sdhci_iproc_data bcm7211a0_data = {
SDHCI_CAN_DO_HISPD,
.caps1 = SDHCI_DRIVER_TYPE_C |
SDHCI_DRIVER_TYPE_D,
.missing_caps = true,
};
static const struct of_device_id sdhci_iproc_of_match[] = {
......@@ -397,9 +398,10 @@ static int sdhci_iproc_probe(struct platform_device *pdev)
}
}
if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) {
host->caps = iproc_host->data->caps;
host->caps1 = iproc_host->data->caps1;
if (iproc_host->data->missing_caps) {
__sdhci_read_caps(host, NULL,
&iproc_host->data->caps,
&iproc_host->data->caps1);
}
ret = sdhci_add_host(host);
......
......@@ -48,6 +48,7 @@
#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29
#define DWCMSHC_EMMC_DLL_START_POINT 16
#define DWCMSHC_EMMC_DLL_INC 8
#define DWCMSHC_EMMC_DLL_BYPASS BIT(24)
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
......@@ -60,6 +61,7 @@
#define DLL_RXCLK_NO_INVERTER 1
#define DLL_RXCLK_INVERTER 0
#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
#define DLL_RXCLK_ORI_GATE BIT(31)
#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
......@@ -234,9 +236,12 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_writel(host, extra, reg);
if (clock <= 52000000) {
/* Disable DLL and reset both of sample and drive clock */
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
/*
* Disable DLL and reset both of sample and drive clock.
* The bypass bit and start bit need to be set if DLL is not locked.
*/
sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL);
sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK);
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
/*
......@@ -279,7 +284,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
}
extra = 0x1 << 16 | /* tune clock stop en */
0x2 << 17 | /* pre-change delay */
0x3 << 17 | /* pre-change delay */
0x3 << 19; /* post-change delay */
sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
......@@ -446,6 +451,7 @@ static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = {
},
{}
};
MODULE_DEVICE_TABLE(acpi, sdhci_dwcmshc_acpi_ids);
#endif
static int dwcmshc_probe(struct platform_device *pdev)
......@@ -528,6 +534,11 @@ static int dwcmshc_probe(struct platform_device *pdev)
goto err_clk;
}
#ifdef CONFIG_ACPI
if (pltfm_data == &sdhci_dwcmshc_bf3_pdata)
sdhci_enable_v4_mode(host);
#endif
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
err = sdhci_setup_host(host);
......
......@@ -251,13 +251,16 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->caps =
u32 caps =
FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
SDHCI_TIMEOUT_CLK_UNIT |
SDHCI_CAN_VDD_330 |
SDHCI_CAN_DO_HISPD |
SDHCI_CAN_DO_SDMA;
u32 caps1 = 0;
__sdhci_read_caps(slot->host, NULL, &caps, &caps1);
return 0;
}
......@@ -286,8 +289,7 @@ static const struct sdhci_pci_fixes sdhci_ricoh_mmc = {
#endif
.quirks = SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_CLOCK_BEFORE_RESET |
SDHCI_QUIRK_NO_CARD_NO_RESET |
SDHCI_QUIRK_MISSING_CAPS
SDHCI_QUIRK_NO_CARD_NO_RESET,
};
static void ene_714_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
......
......@@ -20,6 +20,9 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/mmc.h>
#include <linux/pinctrl/consumer.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
......@@ -41,6 +44,13 @@
#define MMC_CARD 0x1000
#define MMC_WIDTH 0x0100
struct sdhci_pxav2_host {
struct mmc_request *sdio_mrq;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default;
struct pinctrl_state *pins_cmd_gpio;
};
static void pxav2_reset(struct sdhci_host *host, u8 mask)
{
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
......@@ -80,6 +90,71 @@ static void pxav2_reset(struct sdhci_host *host, u8 mask)
}
}
static u16 pxav1_readw(struct sdhci_host *host, int reg)
{
/* Workaround for data abort exception on SDH2 and SDH4 on PXA168 */
if (reg == SDHCI_HOST_VERSION)
return readl(host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16;
return readw(host->ioaddr + reg);
}
static u32 pxav1_irq(struct sdhci_host *host, u32 intmask)
{
struct sdhci_pxav2_host *pxav2_host = sdhci_pltfm_priv(sdhci_priv(host));
struct mmc_request *sdio_mrq;
if (pxav2_host->sdio_mrq && (intmask & SDHCI_INT_CMD_MASK)) {
/* The dummy CMD0 for the SDIO workaround just completed */
sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS);
intmask &= ~SDHCI_INT_CMD_MASK;
/* Restore MMC function to CMD pin */
if (pxav2_host->pinctrl && pxav2_host->pins_default)
pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_default);
sdio_mrq = pxav2_host->sdio_mrq;
pxav2_host->sdio_mrq = NULL;
mmc_request_done(host->mmc, sdio_mrq);
}
return intmask;
}
static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq)
{
u16 tmp;
struct sdhci_pxav2_host *pxav2_host;
/* If this is an SDIO command, perform errata workaround for silicon bug */
if (mrq->cmd && !mrq->cmd->error &&
(mrq->cmd->opcode == SD_IO_RW_DIRECT ||
mrq->cmd->opcode == SD_IO_RW_EXTENDED)) {
/* Reset data port */
tmp = readw(host->ioaddr + SDHCI_TIMEOUT_CONTROL);
tmp |= 0x400;
writew(tmp, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
/* Clock is now stopped, so restart it by sending a dummy CMD0 */
pxav2_host = sdhci_pltfm_priv(sdhci_priv(host));
pxav2_host->sdio_mrq = mrq;
/* Set CMD as high output rather than MMC function while we do CMD0 */
if (pxav2_host->pinctrl && pxav2_host->pins_cmd_gpio)
pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_cmd_gpio);
sdhci_writel(host, 0, SDHCI_ARGUMENT);
sdhci_writew(host, 0, SDHCI_TRANSFER_MODE);
sdhci_writew(host, SDHCI_MAKE_CMD(MMC_GO_IDLE_STATE, SDHCI_CMD_RESP_NONE),
SDHCI_COMMAND);
/* Don't finish this request until the dummy CMD0 finishes */
return;
}
mmc_request_done(host->mmc, mrq);
}
static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
......@@ -101,6 +176,27 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width)
writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
}
struct sdhci_pxa_variant {
const struct sdhci_ops *ops;
unsigned int extra_quirks;
};
static const struct sdhci_ops pxav1_sdhci_ops = {
.read_w = pxav1_readw,
.set_clock = sdhci_set_clock,
.irq = pxav1_irq,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.set_bus_width = pxav2_mmc_set_bus_width,
.reset = pxav2_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.request_done = pxav1_request_done,
};
static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = {
.ops = &pxav1_sdhci_ops,
.extra_quirks = SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_32BIT_DMA_SIZE,
};
static const struct sdhci_ops pxav2_sdhci_ops = {
.set_clock = sdhci_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
......@@ -109,11 +205,14 @@ static const struct sdhci_ops pxav2_sdhci_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
static const struct sdhci_pxa_variant pxav2_variant = {
.ops = &pxav2_sdhci_ops,
};
#ifdef CONFIG_OF
static const struct of_device_id sdhci_pxav2_of_match[] = {
{
.compatible = "mrvl,pxav2-mmc",
},
{ .compatible = "mrvl,pxav1-mmc", .data = &pxav1_variant, },
{ .compatible = "mrvl,pxav2-mmc", .data = &pxav2_variant, },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match);
......@@ -155,40 +254,53 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
{
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
struct sdhci_pxav2_host *pxav2_host;
struct device *dev = &pdev->dev;
struct sdhci_host *host = NULL;
const struct of_device_id *match;
const struct sdhci_pxa_variant *variant;
int ret;
struct clk *clk;
struct clk *clk, *clk_core;
host = sdhci_pltfm_init(pdev, NULL, 0);
host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host));
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
pxav2_host = sdhci_pltfm_priv(pltfm_host);
clk = devm_clk_get(dev, "PXA-SDHCLK");
clk = devm_clk_get(dev, "io");
if (IS_ERR(clk) && PTR_ERR(clk) != -EPROBE_DEFER)
clk = devm_clk_get(dev, NULL);
if (IS_ERR(clk)) {
dev_err(dev, "failed to get io clock\n");
ret = PTR_ERR(clk);
dev_err_probe(dev, ret, "failed to get io clock\n");
goto free;
}
pltfm_host->clk = clk;
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable io clock\n");
dev_err(dev, "failed to enable io clock\n");
goto free;
}
clk_core = devm_clk_get_optional_enabled(dev, "core");
if (IS_ERR(clk_core)) {
ret = PTR_ERR(clk_core);
dev_err_probe(dev, ret, "failed to enable core clock\n");
goto disable_clk;
}
host->quirks = SDHCI_QUIRK_BROKEN_ADMA
| SDHCI_QUIRK_BROKEN_TIMEOUT_VAL
| SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev);
if (match) {
variant = of_device_get_match_data(dev);
if (variant)
pdata = pxav2_get_mmc_pdata(dev);
}
else
variant = &pxav2_variant;
if (pdata) {
if (pdata->flags & PXA_FLAG_CARD_PERMANENT) {
/* on-chip device */
......@@ -208,7 +320,23 @@ static int sdhci_pxav2_probe(struct platform_device *pdev)
host->mmc->pm_caps |= pdata->pm_caps;
}
host->ops = &pxav2_sdhci_ops;
host->quirks |= variant->extra_quirks;
host->ops = variant->ops;
/* Set up optional pinctrl for PXA168 SDIO IRQ fix */
pxav2_host->pinctrl = devm_pinctrl_get(dev);
if (!IS_ERR(pxav2_host->pinctrl)) {
pxav2_host->pins_cmd_gpio = pinctrl_lookup_state(pxav2_host->pinctrl,
"state_cmd_gpio");
if (IS_ERR(pxav2_host->pins_cmd_gpio))
pxav2_host->pins_cmd_gpio = NULL;
pxav2_host->pins_default = pinctrl_lookup_state(pxav2_host->pinctrl,
"default");
if (IS_ERR(pxav2_host->pins_default))
pxav2_host->pins_default = NULL;
} else {
pxav2_host->pinctrl = NULL;
}
ret = sdhci_add_host(host);
if (ret)
......
......@@ -124,10 +124,8 @@ static int armada_38x_quirks(struct platform_device *pdev,
struct resource *res;
host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN;
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
sdhci_read_caps(host);
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"conf-sdio3");
......
......@@ -553,8 +553,7 @@ static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host,
static const struct sdhci_pltfm_data sdhci_sprd_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS,
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
SDHCI_QUIRK2_USE_32BIT_BLK_CNT |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
......@@ -671,8 +670,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
* will allow these modes to be specified only by device
* tree properties through mmc_of_parse().
*/
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
sdhci_read_caps(host);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
......
......@@ -4121,9 +4121,6 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver,
v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
return;
if (caps) {
host->caps = *caps;
} else {
......
......@@ -423,8 +423,6 @@ struct sdhci_host {
#define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25)
/* Controller cannot support End Attribute in NOP ADMA descriptor */
#define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26)
/* Controller is missing device caps. Use caps provided by host */
#define SDHCI_QUIRK_MISSING_CAPS (1<<27)
/* Controller uses Auto CMD12 command to stop the transfer */
#define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28)
/* Controller doesn't have HISPD bit field in HI-SPEED SD card */
......
......@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/tmio.h>
#include <linux/mmc/host.h>
#include <linux/module.h>
......@@ -15,6 +16,7 @@
#include <linux/of_device.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include "tmio_mmc.h"
......@@ -48,6 +50,12 @@
#define UNIPHIER_SD_DMA_ADDR_L 0x440
#define UNIPHIER_SD_DMA_ADDR_H 0x444
/* SD control */
#define UNIPHIER_SDCTRL_CHOFFSET 0x200
#define UNIPHIER_SDCTRL_MODE 0x30
#define UNIPHIER_SDCTRL_MODE_UHS1MOD BIT(15)
#define UNIPHIER_SDCTRL_MODE_SDRSEL BIT(14)
/*
* IP is extended to support various features: built-in DMA engine,
* 1/1024 divisor, etc.
......@@ -66,6 +74,8 @@ struct uniphier_sd_priv {
struct reset_control *rst_hw;
struct dma_chan *chan;
enum dma_data_direction dma_dir;
struct regmap *sdctrl_regmap;
u32 sdctrl_ch;
unsigned long clk_rate;
unsigned long caps;
};
......@@ -420,6 +430,42 @@ static void uniphier_sd_hw_reset(struct mmc_host *mmc)
usleep_range(300, 1000);
}
static void uniphier_sd_speed_switch(struct tmio_mmc_host *host)
{
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
unsigned int offset;
u32 val = 0;
if (!(host->mmc->caps & MMC_CAP_UHS))
return;
if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR50 ||
host->mmc->ios.timing == MMC_TIMING_UHS_SDR104)
val = UNIPHIER_SDCTRL_MODE_SDRSEL;
offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch
+ UNIPHIER_SDCTRL_MODE;
regmap_write_bits(priv->sdctrl_regmap, offset,
UNIPHIER_SDCTRL_MODE_SDRSEL, val);
}
static void uniphier_sd_uhs_enable(struct tmio_mmc_host *host, bool uhs_en)
{
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
unsigned int offset;
u32 val;
if (!(host->mmc->caps & MMC_CAP_UHS))
return;
val = (uhs_en) ? UNIPHIER_SDCTRL_MODE_UHS1MOD : 0;
offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch
+ UNIPHIER_SDCTRL_MODE;
regmap_write_bits(priv->sdctrl_regmap, offset,
UNIPHIER_SDCTRL_MODE_UHS1MOD, val);
}
static void uniphier_sd_set_clock(struct tmio_mmc_host *host,
unsigned int clock)
{
......@@ -433,6 +479,8 @@ static void uniphier_sd_set_clock(struct tmio_mmc_host *host,
tmp &= ~CLK_CTL_SCLKEN;
writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1));
uniphier_sd_speed_switch(host);
if (clock == 0)
return;
......@@ -500,14 +548,17 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
struct pinctrl_state *pinstate = NULL;
u32 val, tmp;
bool uhs_en;
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
val = UNIPHIER_SD_VOLT_330;
uhs_en = false;
break;
case MMC_SIGNAL_VOLTAGE_180:
val = UNIPHIER_SD_VOLT_180;
pinstate = priv->pinstate_uhs;
uhs_en = true;
break;
default:
return -ENOTSUPP;
......@@ -523,12 +574,19 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
else
pinctrl_select_default_state(mmc_dev(mmc));
uniphier_sd_uhs_enable(host, uhs_en);
return 0;
}
static int uniphier_sd_uhs_init(struct tmio_mmc_host *host,
struct uniphier_sd_priv *priv)
static int uniphier_sd_uhs_init(struct tmio_mmc_host *host)
{
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
struct device *dev = &host->pdev->dev;
struct device_node *np = dev->of_node;
struct of_phandle_args args;
int ret;
priv->pinctrl = devm_pinctrl_get(mmc_dev(host->mmc));
if (IS_ERR(priv->pinctrl))
return PTR_ERR(priv->pinctrl);
......@@ -537,8 +595,20 @@ static int uniphier_sd_uhs_init(struct tmio_mmc_host *host,
if (IS_ERR(priv->pinstate_uhs))
return PTR_ERR(priv->pinstate_uhs);
host->ops.start_signal_voltage_switch =
uniphier_sd_start_signal_voltage_switch;
ret = of_parse_phandle_with_fixed_args(np,
"socionext,syscon-uhs-mode",
1, 0, &args);
if (ret) {
dev_err(dev, "Can't get syscon-uhs-mode property\n");
return ret;
}
priv->sdctrl_regmap = syscon_node_to_regmap(args.np);
of_node_put(args.np);
if (IS_ERR(priv->sdctrl_regmap)) {
dev_err(dev, "Can't map syscon-uhs-mode\n");
return PTR_ERR(priv->sdctrl_regmap);
}
priv->sdctrl_ch = args.args[0];
return 0;
}
......@@ -601,12 +671,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
}
if (host->mmc->caps & MMC_CAP_UHS) {
ret = uniphier_sd_uhs_init(host, priv);
ret = uniphier_sd_uhs_init(host);
if (ret) {
dev_warn(dev,
"failed to setup UHS (error %d). Disabling UHS.",
ret);
host->mmc->caps &= ~MMC_CAP_UHS;
} else {
host->ops.start_signal_voltage_switch =
uniphier_sd_start_signal_voltage_switch;
}
}
......
......@@ -527,6 +527,7 @@ struct mmc_host {
struct device_node;
struct mmc_host *mmc_alloc_host(int extra, struct device *);
struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra);
int mmc_add_host(struct mmc_host *);
void mmc_remove_host(struct mmc_host *);
void mmc_free_host(struct mmc_host *);
......
......@@ -15,6 +15,7 @@ struct mmc_host;
int mmc_gpio_get_ro(struct mmc_host *host);
int mmc_gpio_get_cd(struct mmc_host *host);
void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq);
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment