mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-01-01 10:45:49 +00:00
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 -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmP8wf8XHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCmcwQ//cx6R8Faf71W2a/ta1rqkCK5y AmZT47sT3DkXI5exQLyR1k+9Fed7CjayaGp/j4Vy6ylYFBJmxwT9IwKytOr/pRD7 EmuI1uxMePg/XqcrByMYSxDKccWapLbdiRyIyZrEn/BVoL38KuJq87SVyhLHWiQ2 Hubb5lbS5pgnpUjD+10ZlFeTbYbVYkoYiRHUAiJU1mSTId6yxqdVpGpIR4zwgt8C oEhfdWvKrbemBJW2vFzvplHC4aTzipxxiwiTlkPKXPvJwIsblD4lUFn2TeO7SjkJ hnsjF6J1hLcZTgHyChQ2PY/eldmsKDe8EAUVohj/3j5eDVCj4buebxWAiuUJAO1x ulNLl5wjYRqpWXFSIJZyu32VtRH2UDUzQSDoSDxkx/9Txu7OEd7HIr0juxD9D7lw gMO8QLXALth+W++pTpb0sIVBc9FyvdN0ZDqf4wpi3KRvyJwWziyAsqWxrQFTnw3+ mISJ/AZTaLljtEhrOVdYHFvFV3lQ6jm4dXLx3ZhMf5wQpKgokDB5zIwTO5bglVq5 mBz6CIbTLzi99089i++yYB9GO6mxZPWNc4bBMvwPuFxQI86hi8gWM2PF4dU+xGQu 4LjWlqbnoY2ZnWK3D4DGaVRvr+5A3Slf1AsAJd2BR8oivdBt+Owim+CvlVvALa5d xaDqiP7WUAeAFWLFOx0= =CF/8 -----END PGP SIGNATURE----- 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 ...
This commit is contained in:
commit
da15efe1a8
@ -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>
|
||||
|
@ -0,0 +1,76 @@
|
||||
# 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>;
|
||||
};
|
@ -1,39 +0,0 @@
|
||||
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>
|
||||
|
@ -1,8 +1,8 @@
|
||||
# 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:
|
||||
|
@ -1,8 +1,8 @@
|
||||
# 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
|
||||
|
||||
|
@ -0,0 +1,77 @@
|
||||
# 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 {
|
||||
|
186
drivers/mmc/host/dw_mmc-starfive.c
Normal file
186
drivers/mmc/host/dw_mmc-starfive.c
Normal file
@ -0,0 +1,186 @@
|
||||
// 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);
|
||||
|
Loading…
Reference in New Issue
Block a user