MMC core:
- Export SDIO revision and info strings to userspace - Add support for specifying mmc/mmcblk index via mmc aliases in DT MMC host: - Enable support for async probe for all mmc host drivers - Enable compile testing of multiple host drivers - dw_mmc: Enable the Synopsys DesignWare driver for RISCV and CSKY - mtk-sd: Fixup support for CQHCI - owl-mmc: Add support for the actions,s700-mmc variant - renesas_sdhi: Fix regression (temporary) for re-insertion of SD cards - renesas_sdhi: Add support for the r8a774e1 variant - renesas_sdhi/tmio: Improvements for tunings - renesas_sdhi/tmio: Rework support for reset of controller - sdhci-acpi: Fix HS400 tuning for devices with invalid presets on AMDI0040 - sdhci_am654: Improve support for tunings - sdhci_am654: Add support for input tap delays - sdhci_am654: Add workaround for card detect debounce timer - sdhci-am654: Add support for the TI's J7200 variants - sdhci-esdhc-imx: Fix support for manual tuning - sdhci-iproc: Enable support for eMMC DDR 3.3V for bcm2711 - sdhci-msm: Fix stability issues with HS400 for sc7180 - sdhci-of-sparx5: Add Sparx5 SoC eMMC driver - sdhci-of-esdhc: Fixup reference clock source selection - sdhci-pci: Add LTR support for some Intel BYT controllers - sdhci-pci-gli: Add CQHCI Support for GL9763E -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAl+EKAsXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjClKMw/+L8auxMmo8r5bEXb6b7/anJIq HhnsQIG+bd/OT2zVIXcDF6estBAd4g748MImGKhMLLvPls5Mt9FP04rkdQPFEDPJ 0L6f1GduN2agTbqofphgBhjZnYN916vNZWFjr0yLF/TTosKQJCv7Vv/pfjYlVE4g k4sIOcFytj03FVs9D/QbEf2wKomX1e5AsLCWeoJvV/6ncTJrEGqqr+4Ty45Q6jiz apm9t5KezlkQTUZ0w1sWuKuhsCHcRROM5iHJUAsbVMpix3TpFe3//kMY0l+uGcvo JZwJux06gwMNT3dkRgAfWnzTpdEvaVvuYnKkYOWtNbUAkX6f6ua1iGQmB1a1gqsw Af02FKQJG1V9vKCodvkULdB3C2dhE+S7nsfE7PgnvQS6Lo3PBFSToHD5npSblK1b flec6B8AeWjUtniuNzMHf8/ucNqVQ2fKUW1kFgMeoSwFWJlbXpjkMANzPYUTMjWI WhiYaxmGFn8kpDcksDCIVND6ARZ6pILpQF+Ykl5GKlmWiSA9iCDyYHY6mcEY/20s 8OUaIrL5VoA/fNO0fRyToeJtMOQVeZo7qxAd76IDmupeYDK6Tv5P3VJ+drvNXR0h K9DvPn5sGJLm6TX1b6LNC+79m41CvgyreOcT6BBssbAkyQkhSqmzcdXssCgHYII7 SbAn/AOWG2ujef3jOB8= =mg10 -----END PGP SIGNATURE----- Merge tag 'mmc-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Export SDIO revision and info strings to userspace - Add support for specifying mmc/mmcblk index via mmc aliases in DT MMC host: - Enable support for async probe for all mmc host drivers - Enable compile testing of multiple host drivers - dw_mmc: Enable the Synopsys DesignWare driver for RISCV and CSKY - mtk-sd: Fixup support for CQHCI - owl-mmc: Add support for the actions,s700-mmc variant - renesas_sdhi: Fix regression (temporary) for re-insertion of SD cards - renesas_sdhi: Add support for the r8a774e1 variant - renesas_sdhi/tmio: Improvements for tunings - renesas_sdhi/tmio: Rework support for reset of controller - sdhci-acpi: Fix HS400 tuning for devices with invalid presets on AMDI0040 - sdhci_am654: Improve support for tunings - sdhci_am654: Add support for input tap delays - sdhci_am654: Add workaround for card detect debounce timer - sdhci-am654: Add support for the TI's J7200 variants - sdhci-esdhc-imx: Fix support for manual tuning - sdhci-iproc: Enable support for eMMC DDR 3.3V for bcm2711 - sdhci-msm: Fix stability issues with HS400 for sc7180 - sdhci-of-sparx5: Add Sparx5 SoC eMMC driver - sdhci-of-esdhc: Fixup reference clock source selection - sdhci-pci: Add LTR support for some Intel BYT controllers - sdhci-pci-gli: Add CQHCI Support for GL9763E" * tag 'mmc-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (91 commits) mmc: sdhci_am654: Fix module autoload mmc: renesas_sdhi: workaround a regression when reinserting SD cards mmc: sdhci-pci-gli: Add CQHCI Support for GL9763E mmc: sdhci-acpi: AMDI0040: Set SDHCI_QUIRK2_PRESET_VALUE_BROKEN mmc: sdhci_am654: Enable tuning for SDR50 mmc: sdhci_am654: Add support for software tuning mmc: sdhci_am654: Add support for input tap delay mmc: sdhci_am654: Fix hard coded otap delay array size dt-bindings: mmc: sdhci-am654: Add documentation for input tap delay dt-bindings: mmc: sdhci-am654: Convert sdhci-am654 controller documentation to json schema mmc: sdhci-of-esdhc: fix reference clock source selection mmc: host: fix depends for MMC_MESON_GX w/ COMPILE_TEST mmc: sdhci-s3c: hide forward declaration of of_device_id behind CONFIG_OF mmc: sdhci: fix indentation mistakes mmc: moxart: remove unneeded check for drvdata mmc: renesas_sdhi: drop local flag for tuning mmc: rtsx_usb_sdmmc: simplify the return expression of sd_change_phase() mmc: core: document mmc_hw_reset() mmc: mediatek: Drop pointer to mmc_host from msdc_host dt-bindings: mmc: owl: add compatible string actions,s700-mmc ...
This commit is contained in:
Коммит
647412daeb
|
@ -62,7 +62,7 @@ examples:
|
||||||
};
|
};
|
||||||
|
|
||||||
mmc@5b010000 {
|
mmc@5b010000 {
|
||||||
compatible = "fsl,imx8qxp-usdhc";
|
compatible = "fsl,imx8qxp-usdhc", "fsl,imx7d-usdhc";
|
||||||
interrupts = <GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>;
|
interrupts = <GIC_SPI 232 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
reg = <0x5b010000 0x10000>;
|
reg = <0x5b010000 0x10000>;
|
||||||
clocks = <&conn_lpcg IMX_CONN_LPCG_SDHC0_IPG_CLK>,
|
clocks = <&conn_lpcg IMX_CONN_LPCG_SDHC0_IPG_CLK>,
|
||||||
|
|
|
@ -21,23 +21,26 @@ description: |
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
enum:
|
oneOf:
|
||||||
- fsl,imx25-esdhc
|
- enum:
|
||||||
- fsl,imx35-esdhc
|
- fsl,imx25-esdhc
|
||||||
- fsl,imx51-esdhc
|
- fsl,imx35-esdhc
|
||||||
- fsl,imx53-esdhc
|
- fsl,imx51-esdhc
|
||||||
- fsl,imx6q-usdhc
|
- fsl,imx53-esdhc
|
||||||
- fsl,imx6sl-usdhc
|
- fsl,imx6q-usdhc
|
||||||
- fsl,imx6sx-usdhc
|
- fsl,imx6sl-usdhc
|
||||||
- fsl,imx6ull-usdhc
|
- fsl,imx6sx-usdhc
|
||||||
- fsl,imx7d-usdhc
|
- fsl,imx6ull-usdhc
|
||||||
- fsl,imx7ulp-usdhc
|
- fsl,imx7d-usdhc
|
||||||
- fsl,imx8mq-usdhc
|
- fsl,imx7ulp-usdhc
|
||||||
- fsl,imx8mm-usdhc
|
- items:
|
||||||
- fsl,imx8mn-usdhc
|
- enum:
|
||||||
- fsl,imx8mp-usdhc
|
- fsl,imx8mm-usdhc
|
||||||
- fsl,imx8qm-usdhc
|
- fsl,imx8mn-usdhc
|
||||||
- fsl,imx8qxp-usdhc
|
- fsl,imx8mp-usdhc
|
||||||
|
- fsl,imx8mq-usdhc
|
||||||
|
- fsl,imx8qxp-usdhc
|
||||||
|
- const: fsl,imx7d-usdhc
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mmc/microchip,dw-sparx5-sdhci.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Microchip Sparx5 Mobile Storage Host Controller Binding
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: "mmc-controller.yaml"
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Lars Povlsen <lars.povlsen@microchip.com>
|
||||||
|
|
||||||
|
# Everything else is described in the common file
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: microchip,dw-sparx5-sdhci
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
description:
|
||||||
|
Handle to "core" clock for the sdhci controller.
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: core
|
||||||
|
|
||||||
|
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"
|
||||||
|
minimum: 1
|
||||||
|
maximum: 15
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
#include <dt-bindings/clock/microchip,sparx5.h>
|
||||||
|
sdhci0: mmc@600800000 {
|
||||||
|
compatible = "microchip,dw-sparx5-sdhci";
|
||||||
|
reg = <0x00800000 0x1000>;
|
||||||
|
pinctrl-0 = <&emmc_pins>;
|
||||||
|
pinctrl-names = "default";
|
||||||
|
clocks = <&clks CLK_ID_AUX1>;
|
||||||
|
clock-names = "core";
|
||||||
|
assigned-clocks = <&clks CLK_ID_AUX1>;
|
||||||
|
assigned-clock-rates = <800000000>;
|
||||||
|
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
bus-width = <8>;
|
||||||
|
microchip,clock-delay = <10>;
|
||||||
|
};
|
|
@ -14,6 +14,10 @@ description: |
|
||||||
that requires the respective functionality should implement them using
|
that requires the respective functionality should implement them using
|
||||||
these definitions.
|
these definitions.
|
||||||
|
|
||||||
|
It is possible to assign a fixed index mmcN to an MMC host controller
|
||||||
|
(and the corresponding mmcblkN devices) by defining an alias in the
|
||||||
|
/aliases device tree node.
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
$nodename:
|
$nodename:
|
||||||
pattern: "^mmc(@.*)?$"
|
pattern: "^mmc(@.*)?$"
|
||||||
|
|
|
@ -20,6 +20,8 @@ properties:
|
||||||
|
|
||||||
reset-gpios:
|
reset-gpios:
|
||||||
minItems: 1
|
minItems: 1
|
||||||
|
# Put some limit to avoid false warnings
|
||||||
|
maxItems: 32
|
||||||
description:
|
description:
|
||||||
contains a list of GPIO specifiers. The reset GPIOs are asserted
|
contains a list of GPIO specifiers. The reset GPIOs are asserted
|
||||||
at initialization and prior we start the power up procedure of the card.
|
at initialization and prior we start the power up procedure of the card.
|
||||||
|
|
|
@ -14,7 +14,11 @@ maintainers:
|
||||||
|
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
const: actions,owl-mmc
|
oneOf:
|
||||||
|
- const: actions,owl-mmc
|
||||||
|
- items:
|
||||||
|
- const: actions,s700-mmc
|
||||||
|
- const: actions,owl-mmc
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
|
@ -50,6 +50,7 @@ properties:
|
||||||
- renesas,sdhi-r8a774a1 # RZ/G2M
|
- renesas,sdhi-r8a774a1 # RZ/G2M
|
||||||
- renesas,sdhi-r8a774b1 # RZ/G2N
|
- renesas,sdhi-r8a774b1 # RZ/G2N
|
||||||
- renesas,sdhi-r8a774c0 # RZ/G2E
|
- renesas,sdhi-r8a774c0 # RZ/G2E
|
||||||
|
- renesas,sdhi-r8a774e1 # RZ/G2H
|
||||||
- renesas,sdhi-r8a7795 # R-Car H3
|
- renesas,sdhi-r8a7795 # R-Car H3
|
||||||
- renesas,sdhi-r8a7796 # R-Car M3-W
|
- renesas,sdhi-r8a7796 # R-Car M3-W
|
||||||
- renesas,sdhi-r8a77961 # R-Car M3-W+
|
- renesas,sdhi-r8a77961 # R-Car M3-W+
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
Device Tree Bindings for the SDHCI Controllers present on TI's AM654 SOCs
|
|
||||||
|
|
||||||
The bindings follow the mmc[1], clock[2] and interrupt[3] bindings.
|
|
||||||
Only deviations are documented here.
|
|
||||||
|
|
||||||
[1] Documentation/devicetree/bindings/mmc/mmc.txt
|
|
||||||
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
|
|
||||||
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
|
||||||
|
|
||||||
Required Properties:
|
|
||||||
- compatible: should be one of:
|
|
||||||
"ti,am654-sdhci-5.1": SDHCI on AM654 device.
|
|
||||||
"ti,j721e-sdhci-8bit": 8 bit SDHCI on J721E device.
|
|
||||||
"ti,j721e-sdhci-4bit": 4 bit SDHCI on J721E device.
|
|
||||||
- reg: Must be two entries.
|
|
||||||
- The first should be the sdhci register space
|
|
||||||
- The second should the subsystem/phy register space
|
|
||||||
- clocks: Handles to the clock inputs.
|
|
||||||
- clock-names: Tuple including "clk_xin" and "clk_ahb"
|
|
||||||
- interrupts: Interrupt specifiers
|
|
||||||
Output tap delay for each speed mode:
|
|
||||||
- ti,otap-del-sel-legacy
|
|
||||||
- ti,otap-del-sel-mmc-hs
|
|
||||||
- ti,otap-del-sel-sd-hs
|
|
||||||
- ti,otap-del-sel-sdr12
|
|
||||||
- ti,otap-del-sel-sdr25
|
|
||||||
- ti,otap-del-sel-sdr50
|
|
||||||
- ti,otap-del-sel-sdr104
|
|
||||||
- ti,otap-del-sel-ddr50
|
|
||||||
- ti,otap-del-sel-ddr52
|
|
||||||
- ti,otap-del-sel-hs200
|
|
||||||
- ti,otap-del-sel-hs400
|
|
||||||
These bindings must be provided otherwise the driver will disable the
|
|
||||||
corresponding speed mode (i.e. all nodes must provide at least -legacy)
|
|
||||||
|
|
||||||
Optional Properties (Required for ti,am654-sdhci-5.1 and ti,j721e-sdhci-8bit):
|
|
||||||
- ti,trm-icp: DLL trim select
|
|
||||||
- ti,driver-strength-ohm: driver strength in ohms.
|
|
||||||
Valid values are 33, 40, 50, 66 and 100 ohms.
|
|
||||||
Optional Properties:
|
|
||||||
- ti,strobe-sel: strobe select delay for HS400 speed mode. Default value: 0x0.
|
|
||||||
- ti,clkbuf-sel: Clock Delay Buffer Select
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
sdhci0: sdhci@4f80000 {
|
|
||||||
compatible = "ti,am654-sdhci-5.1";
|
|
||||||
reg = <0x0 0x4f80000 0x0 0x260>, <0x0 0x4f90000 0x0 0x134>;
|
|
||||||
power-domains = <&k3_pds 47>;
|
|
||||||
clocks = <&k3_clks 47 0>, <&k3_clks 47 1>;
|
|
||||||
clock-names = "clk_ahb", "clk_xin";
|
|
||||||
interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
|
|
||||||
sdhci-caps-mask = <0x80000007 0x0>;
|
|
||||||
mmc-ddr-1_8v;
|
|
||||||
ti,otap-del-sel-legacy = <0x0>;
|
|
||||||
ti,otap-del-sel-mmc-hs = <0x0>;
|
|
||||||
ti,otap-del-sel-ddr52 = <0x5>;
|
|
||||||
ti,otap-del-sel-hs200 = <0x5>;
|
|
||||||
ti,otap-del-sel-hs400 = <0x0>;
|
|
||||||
ti,trm-icp = <0x8>;
|
|
||||||
};
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
# Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: "http://devicetree.org/schemas/mmc/sdhci-am654.yaml#"
|
||||||
|
$schema : "http://devicetree.org/meta-schemas/core.yaml#"
|
||||||
|
|
||||||
|
title: TI AM654 MMC Controller
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: mmc-controller.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- ti,am654-sdhci-5.1
|
||||||
|
- ti,j721e-sdhci-8bit
|
||||||
|
- ti,j721e-sdhci-4bit
|
||||||
|
- ti,j7200-sdhci-8bit
|
||||||
|
- ti,j721e-sdhci-4bit
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
power-domains:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 2
|
||||||
|
description: Handles to input clocks
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
minItems: 1
|
||||||
|
maxItems: 2
|
||||||
|
items:
|
||||||
|
- const: clk_ahb
|
||||||
|
- const: clk_xin
|
||||||
|
|
||||||
|
# PHY output tap delays:
|
||||||
|
# Used to delay the data valid window and align it to the sampling clock.
|
||||||
|
# Binding needs to be provided for each supported speed mode otherwise the
|
||||||
|
# corresponding mode will be disabled.
|
||||||
|
|
||||||
|
ti,otap-del-sel-legacy:
|
||||||
|
description: Output tap delay for SD/MMC legacy timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-mmc-hs:
|
||||||
|
description: Output tap delay for MMC high speed timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-sd-hs:
|
||||||
|
description: Output tap delay for SD high speed timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-sdr12:
|
||||||
|
description: Output tap delay for SD UHS SDR12 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-sdr25:
|
||||||
|
description: Output tap delay for SD UHS SDR25 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-sdr50:
|
||||||
|
description: Output tap delay for SD UHS SDR50 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-sdr104:
|
||||||
|
description: Output tap delay for SD UHS SDR104 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-ddr50:
|
||||||
|
description: Output tap delay for SD UHS DDR50 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-ddr52:
|
||||||
|
description: Output tap delay for eMMC DDR52 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-hs200:
|
||||||
|
description: Output tap delay for eMMC HS200 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,otap-del-sel-hs400:
|
||||||
|
description: Output tap delay for eMMC HS400 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
# PHY input tap delays:
|
||||||
|
# Used to delay the data valid window and align it to the sampling clock for
|
||||||
|
# modes that don't support tuning
|
||||||
|
|
||||||
|
ti,itap-del-sel-legacy:
|
||||||
|
description: Input tap delay for SD/MMC legacy timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,itap-del-sel-mmc-hs:
|
||||||
|
description: Input tap delay for MMC high speed timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,itap-del-sel-sd-hs:
|
||||||
|
description: Input tap delay for SD high speed timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,itap-del-sel-sdr12:
|
||||||
|
description: Input tap delay for SD UHS SDR12 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,itap-del-sel-sdr25:
|
||||||
|
description: Input tap delay for SD UHS SDR25 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,itap-del-sel-ddr52:
|
||||||
|
description: Input tap delay for MMC DDR52 timing
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0x1f
|
||||||
|
|
||||||
|
ti,trm-icp:
|
||||||
|
description: DLL trim select
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
minimum: 0
|
||||||
|
maximum: 0xf
|
||||||
|
|
||||||
|
ti,driver-strength-ohm:
|
||||||
|
description: DLL drive strength in ohms
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
oneOf:
|
||||||
|
- enum:
|
||||||
|
- 33
|
||||||
|
- 40
|
||||||
|
- 50
|
||||||
|
- 66
|
||||||
|
- 100
|
||||||
|
|
||||||
|
ti,strobe-sel:
|
||||||
|
description: strobe select delay for HS400 speed mode.
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
|
||||||
|
ti,clkbuf-sel:
|
||||||
|
description: Clock Delay Buffer Select
|
||||||
|
$ref: "/schemas/types.yaml#/definitions/uint32"
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
- ti,otap-del-sel-legacy
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
|
||||||
|
bus {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
|
mmc0: mmc@4f80000 {
|
||||||
|
compatible = "ti,am654-sdhci-5.1";
|
||||||
|
reg = <0x0 0x4f80000 0x0 0x260>, <0x0 0x4f90000 0x0 0x134>;
|
||||||
|
power-domains = <&k3_pds 47>;
|
||||||
|
clocks = <&k3_clks 47 0>, <&k3_clks 47 1>;
|
||||||
|
clock-names = "clk_ahb", "clk_xin";
|
||||||
|
interrupts = <GIC_SPI 136 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
sdhci-caps-mask = <0x80000007 0x0>;
|
||||||
|
mmc-ddr-1_8v;
|
||||||
|
ti,otap-del-sel-legacy = <0x0>;
|
||||||
|
ti,otap-del-sel-mmc-hs = <0x0>;
|
||||||
|
ti,otap-del-sel-ddr52 = <0x5>;
|
||||||
|
ti,otap-del-sel-hs200 = <0x5>;
|
||||||
|
ti,otap-del-sel-hs400 = <0x0>;
|
||||||
|
ti,itap-del-sel-legacy = <0x10>;
|
||||||
|
ti,itap-del-sel-mmc-hs = <0xa>;
|
||||||
|
ti,itap-del-sel-ddr52 = <0x3>;
|
||||||
|
ti,trm-icp = <0x8>;
|
||||||
|
};
|
||||||
|
};
|
|
@ -1223,7 +1223,7 @@ static int msb_read_boot_blocks(struct msb_data *msb)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (be16_to_cpu(page->header.block_id) != MS_BLOCK_BOOT_ID) {
|
if (be16_to_cpu(page->header.block_id) != MS_BLOCK_BOOT_ID) {
|
||||||
dbg("the pba at %d doesn' contain boot block ID", pba);
|
dbg("the pba at %d doesn't contain boot block ID", pba);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
{
|
{
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
const char *type;
|
const char *type;
|
||||||
|
unsigned int i;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
switch (card->type) {
|
switch (card->type) {
|
||||||
|
@ -98,6 +99,17 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
card->cis.vendor, card->cis.device);
|
card->cis.vendor, card->cis.device);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
retval = add_uevent_var(env, "SDIO_REVISION=%u.%u",
|
||||||
|
card->major_rev, card->minor_rev);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
for (i = 0; i < card->num_info; i++) {
|
||||||
|
retval = add_uevent_var(env, "SDIO_INFO%u=%s", i+1, card->info[i]);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -2063,6 +2063,16 @@ static void mmc_hw_reset_for_init(struct mmc_host *host)
|
||||||
host->ops->hw_reset(host);
|
host->ops->hw_reset(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mmc_hw_reset - reset the card in hardware
|
||||||
|
* @host: MMC host to which the card is attached
|
||||||
|
*
|
||||||
|
* Hard reset the card. This function is only for upper layers, like the
|
||||||
|
* block layer or card drivers. You cannot use it in host drivers (struct
|
||||||
|
* mmc_card might be gone then).
|
||||||
|
*
|
||||||
|
* Return: 0 on success, -errno on failure
|
||||||
|
*/
|
||||||
int mmc_hw_reset(struct mmc_host *host)
|
int mmc_hw_reset(struct mmc_host *host)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
|
@ -376,6 +376,20 @@ int mmc_of_parse_voltage(struct device_node *np, u32 *mask)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
EXPORT_SYMBOL(mmc_of_parse_voltage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mmc_first_nonreserved_index() - get the first index that is not reserved
|
||||||
|
*/
|
||||||
|
static int mmc_first_nonreserved_index(void)
|
||||||
|
{
|
||||||
|
int max;
|
||||||
|
|
||||||
|
max = of_alias_get_highest_id("mmc");
|
||||||
|
if (max < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return max + 1;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mmc_alloc_host - initialise the per-host structure.
|
* mmc_alloc_host - initialise the per-host structure.
|
||||||
* @extra: sizeof private data structure
|
* @extra: sizeof private data structure
|
||||||
|
@ -387,6 +401,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
struct mmc_host *host;
|
struct mmc_host *host;
|
||||||
|
int alias_id, min_idx, max_idx;
|
||||||
|
|
||||||
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
|
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
|
||||||
if (!host)
|
if (!host)
|
||||||
|
@ -395,7 +410,16 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
|
||||||
/* scanning will be enabled when we're ready */
|
/* scanning will be enabled when we're ready */
|
||||||
host->rescan_disable = 1;
|
host->rescan_disable = 1;
|
||||||
|
|
||||||
err = ida_simple_get(&mmc_host_ida, 0, 0, GFP_KERNEL);
|
alias_id = of_alias_get_id(dev->of_node, "mmc");
|
||||||
|
if (alias_id >= 0) {
|
||||||
|
min_idx = alias_id;
|
||||||
|
max_idx = alias_id + 1;
|
||||||
|
} else {
|
||||||
|
min_idx = mmc_first_nonreserved_index();
|
||||||
|
max_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ida_simple_get(&mmc_host_ida, min_idx, max_idx, GFP_KERNEL);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
kfree(host);
|
kfree(host);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -1168,13 +1168,13 @@ static int mmc_select_hs400(struct mmc_card *card)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set host controller to HS timing */
|
|
||||||
mmc_set_timing(card->host, MMC_TIMING_MMC_HS);
|
|
||||||
|
|
||||||
/* Prepare host to downgrade to HS timing */
|
/* Prepare host to downgrade to HS timing */
|
||||||
if (host->ops->hs400_downgrade)
|
if (host->ops->hs400_downgrade)
|
||||||
host->ops->hs400_downgrade(host);
|
host->ops->hs400_downgrade(host);
|
||||||
|
|
||||||
|
/* Set host controller to HS timing */
|
||||||
|
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
||||||
|
|
||||||
/* Reduce frequency to HS frequency */
|
/* Reduce frequency to HS frequency */
|
||||||
max_dtr = card->ext_csd.hs_max_dtr;
|
max_dtr = card->ext_csd.hs_max_dtr;
|
||||||
mmc_set_clock(host, max_dtr);
|
mmc_set_clock(host, max_dtr);
|
||||||
|
@ -1253,6 +1253,9 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
|
||||||
|
if (host->ops->hs400_downgrade)
|
||||||
|
host->ops->hs400_downgrade(host);
|
||||||
|
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
|
||||||
|
|
||||||
err = mmc_switch_status(card, true);
|
err = mmc_switch_status(card, true);
|
||||||
|
@ -1268,9 +1271,6 @@ int mmc_hs400_to_hs200(struct mmc_card *card)
|
||||||
|
|
||||||
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
mmc_set_timing(host, MMC_TIMING_MMC_HS);
|
||||||
|
|
||||||
if (host->ops->hs400_downgrade)
|
|
||||||
host->ops->hs400_downgrade(host);
|
|
||||||
|
|
||||||
err = mmc_switch_status(card, true);
|
err = mmc_switch_status(card, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
|
@ -1763,13 +1763,17 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
if (mmc_card_hs200(card)) {
|
if (mmc_card_hs200(card)) {
|
||||||
|
host->doing_init_tune = 1;
|
||||||
|
|
||||||
err = mmc_hs200_tuning(card);
|
err = mmc_hs200_tuning(card);
|
||||||
|
if (!err)
|
||||||
|
err = mmc_select_hs400(card);
|
||||||
|
|
||||||
|
host->doing_init_tune = 0;
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
goto free_card;
|
goto free_card;
|
||||||
|
|
||||||
err = mmc_select_hs400(card);
|
|
||||||
if (err)
|
|
||||||
goto free_card;
|
|
||||||
} else if (!mmc_card_hs400es(card)) {
|
} else if (!mmc_card_hs400es(card)) {
|
||||||
/* Select the desired bus width optionally */
|
/* Select the desired bus width optionally */
|
||||||
err = mmc_select_bus_width(card);
|
err = mmc_select_bus_width(card);
|
||||||
|
|
|
@ -2669,22 +2669,22 @@ static const struct mmc_test_case mmc_test_cases[] = {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "Correct xfer_size at write (start failure)",
|
.name = "Proper xfer_size at write (start failure)",
|
||||||
.run = mmc_test_xfersize_write,
|
.run = mmc_test_xfersize_write,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "Correct xfer_size at read (start failure)",
|
.name = "Proper xfer_size at read (start failure)",
|
||||||
.run = mmc_test_xfersize_read,
|
.run = mmc_test_xfersize_read,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "Correct xfer_size at write (midway failure)",
|
.name = "Proper xfer_size at write (midway failure)",
|
||||||
.run = mmc_test_multi_xfersize_write,
|
.run = mmc_test_multi_xfersize_write,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "Correct xfer_size at read (midway failure)",
|
.name = "Proper xfer_size at read (midway failure)",
|
||||||
.run = mmc_test_multi_xfersize_read,
|
.run = mmc_test_multi_xfersize_read,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -709,10 +709,34 @@ static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
||||||
|
|
||||||
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
||||||
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
||||||
|
MMC_DEV_ATTR(revision, "%u.%u\n", card->major_rev, card->minor_rev);
|
||||||
|
|
||||||
|
#define sdio_info_attr(num) \
|
||||||
|
static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||||
|
{ \
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev); \
|
||||||
|
\
|
||||||
|
if (num > card->num_info) \
|
||||||
|
return -ENODATA; \
|
||||||
|
if (!card->info[num-1][0]) \
|
||||||
|
return 0; \
|
||||||
|
return sprintf(buf, "%s\n", card->info[num-1]); \
|
||||||
|
} \
|
||||||
|
static DEVICE_ATTR_RO(info##num)
|
||||||
|
|
||||||
|
sdio_info_attr(1);
|
||||||
|
sdio_info_attr(2);
|
||||||
|
sdio_info_attr(3);
|
||||||
|
sdio_info_attr(4);
|
||||||
|
|
||||||
static struct attribute *sd_std_attrs[] = {
|
static struct attribute *sd_std_attrs[] = {
|
||||||
&dev_attr_vendor.attr,
|
&dev_attr_vendor.attr,
|
||||||
&dev_attr_device.attr,
|
&dev_attr_device.attr,
|
||||||
|
&dev_attr_revision.attr,
|
||||||
|
&dev_attr_info1.attr,
|
||||||
|
&dev_attr_info2.attr,
|
||||||
|
&dev_attr_info3.attr,
|
||||||
|
&dev_attr_info4.attr,
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
&dev_attr_csd.attr,
|
&dev_attr_csd.attr,
|
||||||
&dev_attr_scr.attr,
|
&dev_attr_scr.attr,
|
||||||
|
@ -735,12 +759,18 @@ static struct attribute *sd_std_attrs[] = {
|
||||||
static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
|
static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
struct device *dev = container_of(kobj, struct device, kobj);
|
struct device *dev = kobj_to_dev(kobj);
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||||
|
|
||||||
/* CIS vendor and device ids are available only for Combo cards */
|
/* CIS vendor and device ids, revision and info string are available only for Combo cards */
|
||||||
if ((attr == &dev_attr_vendor.attr || attr == &dev_attr_device.attr) &&
|
if ((attr == &dev_attr_vendor.attr ||
|
||||||
card->type != MMC_TYPE_SD_COMBO)
|
attr == &dev_attr_device.attr ||
|
||||||
|
attr == &dev_attr_revision.attr ||
|
||||||
|
attr == &dev_attr_info1.attr ||
|
||||||
|
attr == &dev_attr_info2.attr ||
|
||||||
|
attr == &dev_attr_info3.attr ||
|
||||||
|
attr == &dev_attr_info4.attr
|
||||||
|
) && card->type != MMC_TYPE_SD_COMBO)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return attr->mode;
|
return attr->mode;
|
||||||
|
|
|
@ -29,12 +29,36 @@
|
||||||
|
|
||||||
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
||||||
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
||||||
|
MMC_DEV_ATTR(revision, "%u.%u\n", card->major_rev, card->minor_rev);
|
||||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
||||||
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
||||||
|
|
||||||
|
#define sdio_info_attr(num) \
|
||||||
|
static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||||
|
{ \
|
||||||
|
struct mmc_card *card = mmc_dev_to_card(dev); \
|
||||||
|
\
|
||||||
|
if (num > card->num_info) \
|
||||||
|
return -ENODATA; \
|
||||||
|
if (!card->info[num-1][0]) \
|
||||||
|
return 0; \
|
||||||
|
return sprintf(buf, "%s\n", card->info[num-1]); \
|
||||||
|
} \
|
||||||
|
static DEVICE_ATTR_RO(info##num)
|
||||||
|
|
||||||
|
sdio_info_attr(1);
|
||||||
|
sdio_info_attr(2);
|
||||||
|
sdio_info_attr(3);
|
||||||
|
sdio_info_attr(4);
|
||||||
|
|
||||||
static struct attribute *sdio_std_attrs[] = {
|
static struct attribute *sdio_std_attrs[] = {
|
||||||
&dev_attr_vendor.attr,
|
&dev_attr_vendor.attr,
|
||||||
&dev_attr_device.attr,
|
&dev_attr_device.attr,
|
||||||
|
&dev_attr_revision.attr,
|
||||||
|
&dev_attr_info1.attr,
|
||||||
|
&dev_attr_info2.attr,
|
||||||
|
&dev_attr_info3.attr,
|
||||||
|
&dev_attr_info4.attr,
|
||||||
&dev_attr_ocr.attr,
|
&dev_attr_ocr.attr,
|
||||||
&dev_attr_rca.attr,
|
&dev_attr_rca.attr,
|
||||||
NULL,
|
NULL,
|
||||||
|
|
|
@ -28,34 +28,50 @@
|
||||||
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
|
#define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
|
||||||
|
|
||||||
/* show configuration fields */
|
/* show configuration fields */
|
||||||
#define sdio_config_attr(field, format_string) \
|
#define sdio_config_attr(field, format_string, args...) \
|
||||||
static ssize_t \
|
static ssize_t \
|
||||||
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||||
{ \
|
{ \
|
||||||
struct sdio_func *func; \
|
struct sdio_func *func; \
|
||||||
\
|
\
|
||||||
func = dev_to_sdio_func (dev); \
|
func = dev_to_sdio_func (dev); \
|
||||||
return sprintf (buf, format_string, func->field); \
|
return sprintf(buf, format_string, args); \
|
||||||
} \
|
} \
|
||||||
static DEVICE_ATTR_RO(field)
|
static DEVICE_ATTR_RO(field)
|
||||||
|
|
||||||
sdio_config_attr(class, "0x%02x\n");
|
sdio_config_attr(class, "0x%02x\n", func->class);
|
||||||
sdio_config_attr(vendor, "0x%04x\n");
|
sdio_config_attr(vendor, "0x%04x\n", func->vendor);
|
||||||
sdio_config_attr(device, "0x%04x\n");
|
sdio_config_attr(device, "0x%04x\n", func->device);
|
||||||
|
sdio_config_attr(revision, "%u.%u\n", func->major_rev, func->minor_rev);
|
||||||
|
sdio_config_attr(modalias, "sdio:c%02Xv%04Xd%04X\n", func->class, func->vendor, func->device);
|
||||||
|
|
||||||
static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
|
#define sdio_info_attr(num) \
|
||||||
{
|
static ssize_t info##num##_show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||||
struct sdio_func *func = dev_to_sdio_func (dev);
|
{ \
|
||||||
|
struct sdio_func *func = dev_to_sdio_func(dev); \
|
||||||
|
\
|
||||||
|
if (num > func->num_info) \
|
||||||
|
return -ENODATA; \
|
||||||
|
if (!func->info[num-1][0]) \
|
||||||
|
return 0; \
|
||||||
|
return sprintf(buf, "%s\n", func->info[num-1]); \
|
||||||
|
} \
|
||||||
|
static DEVICE_ATTR_RO(info##num)
|
||||||
|
|
||||||
return sprintf(buf, "sdio:c%02Xv%04Xd%04X\n",
|
sdio_info_attr(1);
|
||||||
func->class, func->vendor, func->device);
|
sdio_info_attr(2);
|
||||||
}
|
sdio_info_attr(3);
|
||||||
static DEVICE_ATTR_RO(modalias);
|
sdio_info_attr(4);
|
||||||
|
|
||||||
static struct attribute *sdio_dev_attrs[] = {
|
static struct attribute *sdio_dev_attrs[] = {
|
||||||
&dev_attr_class.attr,
|
&dev_attr_class.attr,
|
||||||
&dev_attr_vendor.attr,
|
&dev_attr_vendor.attr,
|
||||||
&dev_attr_device.attr,
|
&dev_attr_device.attr,
|
||||||
|
&dev_attr_revision.attr,
|
||||||
|
&dev_attr_info1.attr,
|
||||||
|
&dev_attr_info2.attr,
|
||||||
|
&dev_attr_info3.attr,
|
||||||
|
&dev_attr_info4.attr,
|
||||||
&dev_attr_modalias.attr,
|
&dev_attr_modalias.attr,
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
@ -106,6 +122,7 @@ static int
|
||||||
sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
{
|
{
|
||||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
if (add_uevent_var(env,
|
if (add_uevent_var(env,
|
||||||
"SDIO_CLASS=%02X", func->class))
|
"SDIO_CLASS=%02X", func->class))
|
||||||
|
@ -115,6 +132,15 @@ sdio_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
"SDIO_ID=%04X:%04X", func->vendor, func->device))
|
"SDIO_ID=%04X:%04X", func->vendor, func->device))
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (add_uevent_var(env,
|
||||||
|
"SDIO_REVISION=%u.%u", func->major_rev, func->minor_rev))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < func->num_info; i++) {
|
||||||
|
if (add_uevent_var(env, "SDIO_INFO%u=%s", i+1, func->info[i]))
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
if (add_uevent_var(env,
|
if (add_uevent_var(env,
|
||||||
"MODALIAS=sdio:c%02Xv%04Xd%04X",
|
"MODALIAS=sdio:c%02Xv%04Xd%04X",
|
||||||
func->class, func->vendor, func->device))
|
func->class, func->vendor, func->device))
|
||||||
|
|
|
@ -23,9 +23,16 @@
|
||||||
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
|
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
|
||||||
const unsigned char *buf, unsigned size)
|
const unsigned char *buf, unsigned size)
|
||||||
{
|
{
|
||||||
|
u8 major_rev, minor_rev;
|
||||||
unsigned i, nr_strings;
|
unsigned i, nr_strings;
|
||||||
char **buffer, *string;
|
char **buffer, *string;
|
||||||
|
|
||||||
|
if (size < 2)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
major_rev = buf[0];
|
||||||
|
minor_rev = buf[1];
|
||||||
|
|
||||||
/* Find all null-terminated (including zero length) strings in
|
/* Find all null-terminated (including zero length) strings in
|
||||||
the TPLLV1_INFO field. Trailing garbage is ignored. */
|
the TPLLV1_INFO field. Trailing garbage is ignored. */
|
||||||
buf += 2;
|
buf += 2;
|
||||||
|
@ -57,9 +64,13 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (func) {
|
if (func) {
|
||||||
|
func->major_rev = major_rev;
|
||||||
|
func->minor_rev = minor_rev;
|
||||||
func->num_info = nr_strings;
|
func->num_info = nr_strings;
|
||||||
func->info = (const char**)buffer;
|
func->info = (const char**)buffer;
|
||||||
} else {
|
} else {
|
||||||
|
card->major_rev = major_rev;
|
||||||
|
card->minor_rev = minor_rev;
|
||||||
card->num_info = nr_strings;
|
card->num_info = nr_strings;
|
||||||
card->info = (const char**)buffer;
|
card->info = (const char**)buffer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ config MMC_SDHCI_OF_AT91
|
||||||
config MMC_SDHCI_OF_ESDHC
|
config MMC_SDHCI_OF_ESDHC
|
||||||
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
tristate "SDHCI OF support for the Freescale eSDHC controller"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE
|
depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
select FSL_GUTS
|
select FSL_GUTS
|
||||||
help
|
help
|
||||||
|
@ -213,6 +213,18 @@ config MMC_SDHCI_OF_DWCMSHC
|
||||||
If you have a controller with this interface, say Y or M here.
|
If you have a controller with this interface, say Y or M here.
|
||||||
If unsure, say N.
|
If unsure, say N.
|
||||||
|
|
||||||
|
config MMC_SDHCI_OF_SPARX5
|
||||||
|
tristate "SDHCI OF support for the MCHP Sparx5 SoC"
|
||||||
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
depends on ARCH_SPARX5 || COMPILE_TEST
|
||||||
|
help
|
||||||
|
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||||
|
found in the MCHP Sparx5 SoC.
|
||||||
|
|
||||||
|
If you have a Sparx5 SoC with this interface, say Y or M here.
|
||||||
|
|
||||||
|
If unsure, say N.
|
||||||
|
|
||||||
config MMC_SDHCI_CADENCE
|
config MMC_SDHCI_CADENCE
|
||||||
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
|
tristate "SDHCI support for the Cadence SD/SDIO/eMMC controller"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
|
@ -226,7 +238,7 @@ config MMC_SDHCI_CADENCE
|
||||||
|
|
||||||
config MMC_SDHCI_CNS3XXX
|
config MMC_SDHCI_CNS3XXX
|
||||||
tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
|
tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
|
||||||
depends on ARCH_CNS3XXX
|
depends on ARCH_CNS3XXX || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
help
|
help
|
||||||
This selects the SDHCI support for CNS3xxx System-on-Chip devices.
|
This selects the SDHCI support for CNS3xxx System-on-Chip devices.
|
||||||
|
@ -250,7 +262,7 @@ config MMC_SDHCI_ESDHC_MCF
|
||||||
|
|
||||||
config MMC_SDHCI_ESDHC_IMX
|
config MMC_SDHCI_ESDHC_IMX
|
||||||
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
select MMC_CQHCI
|
select MMC_CQHCI
|
||||||
|
@ -264,7 +276,7 @@ config MMC_SDHCI_ESDHC_IMX
|
||||||
|
|
||||||
config MMC_SDHCI_DOVE
|
config MMC_SDHCI_DOVE
|
||||||
tristate "SDHCI support on Marvell's Dove SoC"
|
tristate "SDHCI support on Marvell's Dove SoC"
|
||||||
depends on ARCH_DOVE || MACH_DOVE
|
depends on ARCH_DOVE || MACH_DOVE || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
|
@ -277,7 +289,7 @@ config MMC_SDHCI_DOVE
|
||||||
|
|
||||||
config MMC_SDHCI_TEGRA
|
config MMC_SDHCI_TEGRA
|
||||||
tristate "SDHCI platform support for the Tegra SD/MMC Controller"
|
tristate "SDHCI platform support for the Tegra SD/MMC Controller"
|
||||||
depends on ARCH_TEGRA
|
depends on ARCH_TEGRA || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
select MMC_CQHCI
|
select MMC_CQHCI
|
||||||
|
@ -289,7 +301,8 @@ config MMC_SDHCI_TEGRA
|
||||||
|
|
||||||
config MMC_SDHCI_S3C
|
config MMC_SDHCI_S3C
|
||||||
tristate "SDHCI support on Samsung S3C SoC"
|
tristate "SDHCI support on Samsung S3C SoC"
|
||||||
depends on MMC_SDHCI && PLAT_SAMSUNG
|
depends on MMC_SDHCI
|
||||||
|
depends on PLAT_SAMSUNG || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||||
often referrered to as the HSMMC block in some of the Samsung S3C
|
often referrered to as the HSMMC block in some of the Samsung S3C
|
||||||
|
@ -301,7 +314,7 @@ config MMC_SDHCI_S3C
|
||||||
|
|
||||||
config MMC_SDHCI_SIRF
|
config MMC_SDHCI_SIRF
|
||||||
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs"
|
||||||
depends on ARCH_SIRF
|
depends on ARCH_SIRF || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
|
@ -339,7 +352,8 @@ config MMC_SDHCI_PXAV2
|
||||||
|
|
||||||
config MMC_SDHCI_SPEAR
|
config MMC_SDHCI_SPEAR
|
||||||
tristate "SDHCI support on ST SPEAr platform"
|
tristate "SDHCI support on ST SPEAr platform"
|
||||||
depends on MMC_SDHCI && PLAT_SPEAR
|
depends on MMC_SDHCI
|
||||||
|
depends on PLAT_SPEAR || COMPILE_TEST
|
||||||
depends on OF
|
depends on OF
|
||||||
help
|
help
|
||||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||||
|
@ -362,7 +376,7 @@ config MMC_SDHCI_S3C_DMA
|
||||||
|
|
||||||
config MMC_SDHCI_BCM_KONA
|
config MMC_SDHCI_BCM_KONA
|
||||||
tristate "SDHCI support on Broadcom KONA platform"
|
tristate "SDHCI support on Broadcom KONA platform"
|
||||||
depends on ARCH_BCM_MOBILE
|
depends on ARCH_BCM_MOBILE || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
help
|
help
|
||||||
This selects the Broadcom Kona Secure Digital Host Controller
|
This selects the Broadcom Kona Secure Digital Host Controller
|
||||||
|
@ -410,7 +424,8 @@ config MMC_SDHCI_IPROC
|
||||||
|
|
||||||
config MMC_MESON_GX
|
config MMC_MESON_GX
|
||||||
tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support"
|
tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support"
|
||||||
depends on ARCH_MESON && MMC
|
depends on ARCH_MESON|| COMPILE_TEST
|
||||||
|
depends on COMMON_CLK
|
||||||
help
|
help
|
||||||
This selects support for the Amlogic SD/MMC Host Controller
|
This selects support for the Amlogic SD/MMC Host Controller
|
||||||
found on the S905/GX*/AXG family of SoCs. This controller is
|
found on the S905/GX*/AXG family of SoCs. This controller is
|
||||||
|
@ -446,7 +461,7 @@ config MMC_MESON_MX_SDIO
|
||||||
|
|
||||||
config MMC_MOXART
|
config MMC_MOXART
|
||||||
tristate "MOXART SD/MMC Host Controller support"
|
tristate "MOXART SD/MMC Host Controller support"
|
||||||
depends on ARCH_MOXART && MMC
|
depends on ARCH_MOXART || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects support for the MOXART SD/MMC Host Controller.
|
This selects support for the MOXART SD/MMC Host Controller.
|
||||||
MOXA provides one multi-functional card reader which can
|
MOXA provides one multi-functional card reader which can
|
||||||
|
@ -455,7 +470,7 @@ config MMC_MOXART
|
||||||
|
|
||||||
config MMC_SDHCI_ST
|
config MMC_SDHCI_ST
|
||||||
tristate "SDHCI support on STMicroelectronics SoC"
|
tristate "SDHCI support on STMicroelectronics SoC"
|
||||||
depends on ARCH_STI || FSP2
|
depends on ARCH_STI || FSP2 || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
help
|
help
|
||||||
|
@ -525,7 +540,7 @@ config MMC_ATMELMCI
|
||||||
|
|
||||||
config MMC_SDHCI_MSM
|
config MMC_SDHCI_MSM
|
||||||
tristate "Qualcomm SDHCI Controller Support"
|
tristate "Qualcomm SDHCI Controller Support"
|
||||||
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
|
depends on ARCH_QCOM || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
select MMC_CQHCI
|
select MMC_CQHCI
|
||||||
|
@ -575,7 +590,7 @@ config MMC_TIFM_SD
|
||||||
|
|
||||||
config MMC_MVSDIO
|
config MMC_MVSDIO
|
||||||
tristate "Marvell MMC/SD/SDIO host driver"
|
tristate "Marvell MMC/SD/SDIO host driver"
|
||||||
depends on PLAT_ORION
|
depends on PLAT_ORION || (COMPILE_TEST && ARM)
|
||||||
depends on OF
|
depends on OF
|
||||||
help
|
help
|
||||||
This selects the Marvell SDIO host driver.
|
This selects the Marvell SDIO host driver.
|
||||||
|
@ -587,7 +602,7 @@ config MMC_MVSDIO
|
||||||
|
|
||||||
config MMC_DAVINCI
|
config MMC_DAVINCI
|
||||||
tristate "TI DAVINCI Multimedia Card Interface support"
|
tristate "TI DAVINCI Multimedia Card Interface support"
|
||||||
depends on ARCH_DAVINCI
|
depends on ARCH_DAVINCI || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects the TI DAVINCI Multimedia card Interface.
|
This selects the TI DAVINCI Multimedia card Interface.
|
||||||
If you have an DAVINCI board with a Multimedia Card slot,
|
If you have an DAVINCI board with a Multimedia Card slot,
|
||||||
|
@ -669,7 +684,7 @@ config MMC_SDRICOH_CS
|
||||||
|
|
||||||
config MMC_SDHCI_SPRD
|
config MMC_SDHCI_SPRD
|
||||||
tristate "Spreadtrum SDIO host Controller"
|
tristate "Spreadtrum SDIO host Controller"
|
||||||
depends on ARCH_SPRD
|
depends on ARCH_SPRD || COMPILE_TEST
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
select MMC_SDHCI_IO_ACCESSORS
|
select MMC_SDHCI_IO_ACCESSORS
|
||||||
select MMC_HSQ
|
select MMC_HSQ
|
||||||
|
@ -686,7 +701,7 @@ config MMC_TMIO_CORE
|
||||||
|
|
||||||
config MMC_TMIO
|
config MMC_TMIO
|
||||||
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
|
tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
|
||||||
depends on MFD_TMIO || MFD_ASIC3
|
depends on MFD_TMIO || MFD_ASIC3 || COMPILE_TEST
|
||||||
select MMC_TMIO_CORE
|
select MMC_TMIO_CORE
|
||||||
help
|
help
|
||||||
This provides support for the SD/MMC cell found in TC6393XB,
|
This provides support for the SD/MMC cell found in TC6393XB,
|
||||||
|
@ -777,7 +792,7 @@ config MMC_CAVIUM_THUNDERX
|
||||||
|
|
||||||
config MMC_DW
|
config MMC_DW
|
||||||
tristate "Synopsys DesignWare Memory Card Interface"
|
tristate "Synopsys DesignWare Memory Card Interface"
|
||||||
depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST
|
depends on ARC || ARM || ARM64 || MIPS || RISCV || CSKY || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects support for the Synopsys DesignWare Mobile Storage IP
|
This selects support for the Synopsys DesignWare Mobile Storage IP
|
||||||
block, this provides host support for SD and MMC interfaces, in both
|
block, this provides host support for SD and MMC interfaces, in both
|
||||||
|
@ -959,7 +974,7 @@ config MMC_REALTEK_USB
|
||||||
|
|
||||||
config MMC_SUNXI
|
config MMC_SUNXI
|
||||||
tristate "Allwinner sunxi SD/MMC Host Controller support"
|
tristate "Allwinner sunxi SD/MMC Host Controller support"
|
||||||
depends on ARCH_SUNXI
|
depends on ARCH_SUNXI || COMPILE_TEST
|
||||||
help
|
help
|
||||||
This selects support for the SD/MMC Host Controller on
|
This selects support for the SD/MMC Host Controller on
|
||||||
Allwinner sunxi SoCs.
|
Allwinner sunxi SoCs.
|
||||||
|
|
|
@ -94,6 +94,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
|
obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o
|
||||||
|
obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
obj-$(CONFIG_MMC_SDHCI_IPROC) += sdhci-iproc.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
|
||||||
|
|
|
@ -1178,6 +1178,7 @@ static struct platform_driver alcor_pci_sdmmc_driver = {
|
||||||
.id_table = alcor_pci_sdmmc_ids,
|
.id_table = alcor_pci_sdmmc_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME_ALCOR_PCI_SDMMC,
|
.name = DRV_NAME_ALCOR_PCI_SDMMC,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &alcor_mmc_pm_ops
|
.pm = &alcor_mmc_pm_ops
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -537,6 +537,7 @@ static struct platform_driver goldfish_mmc_driver = {
|
||||||
.remove = goldfish_mmc_remove,
|
.remove = goldfish_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2668,6 +2668,7 @@ static struct platform_driver atmci_driver = {
|
||||||
.remove = atmci_remove,
|
.remove = atmci_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "atmel_mci",
|
.name = "atmel_mci",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(atmci_dt_ids),
|
.of_match_table = of_match_ptr(atmci_dt_ids),
|
||||||
.pm = &atmci_dev_pm_ops,
|
.pm = &atmci_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1189,6 +1189,7 @@ static struct platform_driver au1xmmc_driver = {
|
||||||
.resume = au1xmmc_resume,
|
.resume = au1xmmc_resume,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1406,9 +1406,7 @@ static int bcm2835_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
clk = devm_clk_get(dev, NULL);
|
clk = devm_clk_get(dev, NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
ret = PTR_ERR(clk);
|
ret = dev_err_probe(dev, PTR_ERR(clk), "could not get clk\n");
|
||||||
if (ret != -EPROBE_DEFER)
|
|
||||||
dev_err(dev, "could not get clk: %d\n", ret);
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1476,6 +1474,7 @@ static struct platform_driver bcm2835_driver = {
|
||||||
.remove = bcm2835_remove,
|
.remove = bcm2835_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhost-bcm2835",
|
.name = "sdhost-bcm2835",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = bcm2835_match,
|
.of_match_table = bcm2835_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -327,6 +327,7 @@ static struct platform_driver octeon_mmc_driver = {
|
||||||
.remove = octeon_mmc_remove,
|
.remove = octeon_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = KBUILD_MODNAME,
|
.name = KBUILD_MODNAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = octeon_mmc_match,
|
.of_match_table = octeon_mmc_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -376,6 +376,9 @@ static void cqhci_off(struct mmc_host *mmc)
|
||||||
else
|
else
|
||||||
pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc));
|
pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc));
|
||||||
|
|
||||||
|
if (cq_host->ops->post_disable)
|
||||||
|
cq_host->ops->post_disable(mmc);
|
||||||
|
|
||||||
mmc->cqe_on = false;
|
mmc->cqe_on = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,6 +583,9 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
__cqhci_enable(cq_host);
|
__cqhci_enable(cq_host);
|
||||||
|
|
||||||
if (!mmc->cqe_on) {
|
if (!mmc->cqe_on) {
|
||||||
|
if (cq_host->ops->pre_enable)
|
||||||
|
cq_host->ops->pre_enable(mmc);
|
||||||
|
|
||||||
cqhci_writel(cq_host, 0, CQHCI_CTL);
|
cqhci_writel(cq_host, 0, CQHCI_CTL);
|
||||||
mmc->cqe_on = true;
|
mmc->cqe_on = true;
|
||||||
pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc));
|
pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc));
|
||||||
|
|
|
@ -206,6 +206,8 @@ struct cqhci_host_ops {
|
||||||
void (*disable)(struct mmc_host *mmc, bool recovery);
|
void (*disable)(struct mmc_host *mmc, bool recovery);
|
||||||
void (*update_dcmd_desc)(struct mmc_host *mmc, struct mmc_request *mrq,
|
void (*update_dcmd_desc)(struct mmc_host *mmc, struct mmc_request *mrq,
|
||||||
u64 *data);
|
u64 *data);
|
||||||
|
void (*pre_enable)(struct mmc_host *mmc);
|
||||||
|
void (*post_disable)(struct mmc_host *mmc);
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
|
static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg)
|
||||||
|
|
|
@ -996,7 +996,7 @@ static irqreturn_t mmc_davinci_irq(int irq, void *dev_id)
|
||||||
|
|
||||||
if (qstatus & MMCST0_RSPDNE) {
|
if (qstatus & MMCST0_RSPDNE) {
|
||||||
/* End of command phase */
|
/* End of command phase */
|
||||||
end_command = (int) host->cmd;
|
end_command = host->cmd ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end_command)
|
if (end_command)
|
||||||
|
@ -1240,9 +1240,8 @@ static int davinci_mmcsd_probe(struct platform_device *pdev)
|
||||||
pdev->id_entry = match->data;
|
pdev->id_entry = match->data;
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret != -EPROBE_DEFER)
|
dev_err_probe(&pdev->dev, ret,
|
||||||
dev_err(&pdev->dev,
|
"could not parse of data\n");
|
||||||
"could not parse of data: %d\n", ret);
|
|
||||||
goto parse_fail;
|
goto parse_fail;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1396,6 +1395,7 @@ static const struct dev_pm_ops davinci_mmcsd_pm = {
|
||||||
static struct platform_driver davinci_mmcsd_driver = {
|
static struct platform_driver davinci_mmcsd_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "davinci_mmc",
|
.name = "davinci_mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = davinci_mmcsd_pm_ops,
|
.pm = davinci_mmcsd_pm_ops,
|
||||||
.of_match_table = davinci_mmc_dt_ids,
|
.of_match_table = davinci_mmc_dt_ids,
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,6 +55,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = {
|
||||||
.remove = dw_mci_pltfm_remove,
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_bluefield",
|
.name = "dwmmc_bluefield",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_bluefield_match,
|
.of_match_table = dw_mci_bluefield_match,
|
||||||
.pm = &dw_mci_pltfm_pmops,
|
.pm = &dw_mci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -592,6 +592,7 @@ static struct platform_driver dw_mci_exynos_pltfm_driver = {
|
||||||
.remove = dw_mci_exynos_remove,
|
.remove = dw_mci_exynos_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_exynos",
|
.name = "dwmmc_exynos",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_exynos_match,
|
.of_match_table = dw_mci_exynos_match,
|
||||||
.pm = &dw_mci_exynos_pmops,
|
.pm = &dw_mci_exynos_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -200,6 +200,7 @@ static struct platform_driver dw_mci_hi3798cv200_driver = {
|
||||||
.remove = dw_mci_hi3798cv200_remove,
|
.remove = dw_mci_hi3798cv200_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_hi3798cv200",
|
.name = "dwmmc_hi3798cv200",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_hi3798cv200_match,
|
.of_match_table = dw_mci_hi3798cv200_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -473,6 +473,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
|
||||||
.remove = dw_mci_pltfm_remove,
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_k3",
|
.name = "dwmmc_k3",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_k3_match,
|
.of_match_table = dw_mci_k3_match,
|
||||||
.pm = &dw_mci_k3_dev_pm_ops,
|
.pm = &dw_mci_k3_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -98,6 +98,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
|
||||||
.remove = dw_mci_pltfm_remove,
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dw_mmc",
|
.name = "dw_mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_pltfm_match,
|
.of_match_table = dw_mci_pltfm_match,
|
||||||
.pm = &dw_mci_pltfm_pmops,
|
.pm = &dw_mci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -383,6 +383,7 @@ static struct platform_driver dw_mci_rockchip_pltfm_driver = {
|
||||||
.remove = dw_mci_rockchip_remove,
|
.remove = dw_mci_rockchip_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_rockchip",
|
.name = "dwmmc_rockchip",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_rockchip_match,
|
.of_match_table = dw_mci_rockchip_match,
|
||||||
.pm = &dw_mci_rockchip_dev_pm_ops,
|
.pm = &dw_mci_rockchip_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -155,7 +155,6 @@ static int dw_mci_zx_parse_dt(struct dw_mci *host)
|
||||||
struct device_node *node;
|
struct device_node *node;
|
||||||
struct dw_mci_zx_priv_data *priv;
|
struct dw_mci_zx_priv_data *priv;
|
||||||
struct regmap *sysc_base;
|
struct regmap *sysc_base;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* syscon is needed only by emmc */
|
/* syscon is needed only by emmc */
|
||||||
node = of_parse_phandle(np, "zte,aon-syscon", 0);
|
node = of_parse_phandle(np, "zte,aon-syscon", 0);
|
||||||
|
@ -163,13 +162,9 @@ static int dw_mci_zx_parse_dt(struct dw_mci *host)
|
||||||
sysc_base = syscon_node_to_regmap(node);
|
sysc_base = syscon_node_to_regmap(node);
|
||||||
of_node_put(node);
|
of_node_put(node);
|
||||||
|
|
||||||
if (IS_ERR(sysc_base)) {
|
if (IS_ERR(sysc_base))
|
||||||
ret = PTR_ERR(sysc_base);
|
return dev_err_probe(host->dev, PTR_ERR(sysc_base),
|
||||||
if (ret != -EPROBE_DEFER)
|
"Can't get syscon\n");
|
||||||
dev_err(host->dev, "Can't get syscon: %d\n",
|
|
||||||
ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -227,6 +222,7 @@ static struct platform_driver dw_mci_zx_pltfm_driver = {
|
||||||
.remove = dw_mci_pltfm_remove,
|
.remove = dw_mci_pltfm_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "dwmmc_zx",
|
.name = "dwmmc_zx",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = dw_mci_zx_match,
|
.of_match_table = dw_mci_zx_match,
|
||||||
.pm = &dw_mci_zx_dev_pm_ops,
|
.pm = &dw_mci_zx_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -3161,12 +3161,9 @@ int dw_mci_probe(struct dw_mci *host)
|
||||||
|
|
||||||
if (!host->pdata) {
|
if (!host->pdata) {
|
||||||
host->pdata = dw_mci_parse_dt(host);
|
host->pdata = dw_mci_parse_dt(host);
|
||||||
if (PTR_ERR(host->pdata) == -EPROBE_DEFER) {
|
if (IS_ERR(host->pdata))
|
||||||
return -EPROBE_DEFER;
|
return dev_err_probe(host->dev, PTR_ERR(host->pdata),
|
||||||
} else if (IS_ERR(host->pdata)) {
|
"platform data not available\n");
|
||||||
dev_err(host->dev, "platform data not available\n");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
host->biu_clk = devm_clk_get(host->dev, "biu");
|
host->biu_clk = devm_clk_get(host->dev, "biu");
|
||||||
|
|
|
@ -991,9 +991,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret != -EPROBE_DEFER)
|
dev_err_probe(&pdev->dev, ret, "could not parse device properties\n");
|
||||||
dev_err(&pdev->dev,
|
|
||||||
"could not parse device properties: %d\n", ret);
|
|
||||||
goto err_free_host;
|
goto err_free_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1126,6 +1124,7 @@ static struct platform_driver jz4740_mmc_driver = {
|
||||||
.remove = jz4740_mmc_remove,
|
.remove = jz4740_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "jz4740-mmc",
|
.name = "jz4740-mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(jz4740_mmc_of_match),
|
.of_match_table = of_match_ptr(jz4740_mmc_of_match),
|
||||||
.pm = pm_ptr(&jz4740_mmc_pm_ops),
|
.pm = pm_ptr(&jz4740_mmc_pm_ops),
|
||||||
},
|
},
|
||||||
|
|
|
@ -426,11 +426,9 @@ static int meson_mmc_clk_init(struct meson_host *host)
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "clkin%d", i);
|
snprintf(name, sizeof(name), "clkin%d", i);
|
||||||
clk = devm_clk_get(host->dev, name);
|
clk = devm_clk_get(host->dev, name);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
if (clk != ERR_PTR(-EPROBE_DEFER))
|
return dev_err_probe(host->dev, PTR_ERR(clk),
|
||||||
dev_err(host->dev, "Missing clock %s\n", name);
|
"Missing clock %s\n", name);
|
||||||
return PTR_ERR(clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
mux_parent_names[i] = __clk_get_name(clk);
|
mux_parent_names[i] = __clk_get_name(clk);
|
||||||
}
|
}
|
||||||
|
@ -521,7 +519,7 @@ static int meson_mmc_resampling_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
val |= ADJUST_ADJ_EN;
|
val |= ADJUST_ADJ_EN;
|
||||||
writel(val, host->regs + host->data->adjust);
|
writel(val, host->regs + host->data->adjust);
|
||||||
|
|
||||||
if (mmc->doing_retune)
|
if (mmc_doing_retune(mmc))
|
||||||
dly = FIELD_GET(ADJUST_ADJ_DELAY_MASK, val) + 1;
|
dly = FIELD_GET(ADJUST_ADJ_DELAY_MASK, val) + 1;
|
||||||
else
|
else
|
||||||
dly = 0;
|
dly = 0;
|
||||||
|
@ -1077,12 +1075,8 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = device_reset_optional(&pdev->dev);
|
ret = device_reset_optional(&pdev->dev);
|
||||||
if (ret) {
|
if (ret)
|
||||||
if (ret != -EPROBE_DEFER)
|
return dev_err_probe(&pdev->dev, ret, "device reset failed\n");
|
||||||
dev_err(&pdev->dev, "device reset failed: %d\n", ret);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
host->regs = devm_ioremap_resource(&pdev->dev, res);
|
host->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
@ -1270,6 +1264,7 @@ static struct platform_driver meson_mmc_driver = {
|
||||||
.remove = meson_mmc_remove,
|
.remove = meson_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(meson_mmc_of_match),
|
.of_match_table = of_match_ptr(meson_mmc_of_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -903,6 +903,7 @@ static struct platform_driver meson_mx_sdhc_driver = {
|
||||||
.remove = meson_mx_sdhc_remove,
|
.remove = meson_mx_sdhc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "meson-mx-sdhc",
|
.name = "meson-mx-sdhc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(meson_mx_sdhc_of_match),
|
.of_match_table = of_match_ptr(meson_mx_sdhc_of_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -755,6 +755,7 @@ static struct platform_driver meson_mx_mmc_driver = {
|
||||||
.remove = meson_mx_mmc_remove,
|
.remove = meson_mx_mmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "meson-mx-sdio",
|
.name = "meson-mx-sdio",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(meson_mx_mmc_of_match),
|
.of_match_table = of_match_ptr(meson_mx_mmc_of_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -882,9 +882,9 @@ mmc_spi_data_do(struct mmc_spi_host *host, struct mmc_command *cmd,
|
||||||
else
|
else
|
||||||
clock_rate = spi->max_speed_hz;
|
clock_rate = spi->max_speed_hz;
|
||||||
|
|
||||||
timeout = data->timeout_ns +
|
timeout = data->timeout_ns / 1000 +
|
||||||
data->timeout_clks * 1000000 / clock_rate;
|
data->timeout_clks * 1000000 / clock_rate;
|
||||||
timeout = usecs_to_jiffies((unsigned int)(timeout / 1000)) + 1;
|
timeout = usecs_to_jiffies((unsigned int)timeout) + 1;
|
||||||
|
|
||||||
/* Handle scatterlist segments one at a time, with synch for
|
/* Handle scatterlist segments one at a time, with synch for
|
||||||
* each 512-byte block
|
* each 512-byte block
|
||||||
|
|
|
@ -689,19 +689,18 @@ static int moxart_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_set_drvdata(&pdev->dev, NULL);
|
dev_set_drvdata(&pdev->dev, NULL);
|
||||||
|
|
||||||
if (mmc) {
|
if (!IS_ERR(host->dma_chan_tx))
|
||||||
if (!IS_ERR(host->dma_chan_tx))
|
dma_release_channel(host->dma_chan_tx);
|
||||||
dma_release_channel(host->dma_chan_tx);
|
if (!IS_ERR(host->dma_chan_rx))
|
||||||
if (!IS_ERR(host->dma_chan_rx))
|
dma_release_channel(host->dma_chan_rx);
|
||||||
dma_release_channel(host->dma_chan_rx);
|
mmc_remove_host(mmc);
|
||||||
mmc_remove_host(mmc);
|
mmc_free_host(mmc);
|
||||||
mmc_free_host(mmc);
|
|
||||||
|
writel(0, host->base + REG_INTERRUPT_MASK);
|
||||||
|
writel(0, host->base + REG_POWER_CONTROL);
|
||||||
|
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
||||||
|
host->base + REG_CLOCK_CONTROL);
|
||||||
|
|
||||||
writel(0, host->base + REG_INTERRUPT_MASK);
|
|
||||||
writel(0, host->base + REG_POWER_CONTROL);
|
|
||||||
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
|
||||||
host->base + REG_CLOCK_CONTROL);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -717,6 +716,7 @@ static struct platform_driver moxart_mmc_driver = {
|
||||||
.remove = moxart_remove,
|
.remove = moxart_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mmc-moxart",
|
.name = "mmc-moxart",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = moxart_mmc_match,
|
.of_match_table = moxart_mmc_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -397,7 +397,6 @@ struct msdc_delay_phase {
|
||||||
struct msdc_host {
|
struct msdc_host {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
const struct mtk_mmc_compatible *dev_comp;
|
const struct mtk_mmc_compatible *dev_comp;
|
||||||
struct mmc_host *mmc; /* mmc structure */
|
|
||||||
int cmd_rsp;
|
int cmd_rsp;
|
||||||
|
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
@ -734,14 +733,15 @@ static void msdc_unprepare_data(struct msdc_host *host, struct mmc_request *mrq)
|
||||||
|
|
||||||
static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks)
|
static u64 msdc_timeout_cal(struct msdc_host *host, u64 ns, u64 clks)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
u64 timeout, clk_ns;
|
u64 timeout, clk_ns;
|
||||||
u32 mode = 0;
|
u32 mode = 0;
|
||||||
|
|
||||||
if (host->mmc->actual_clock == 0) {
|
if (mmc->actual_clock == 0) {
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
} else {
|
} else {
|
||||||
clk_ns = 1000000000ULL;
|
clk_ns = 1000000000ULL;
|
||||||
do_div(clk_ns, host->mmc->actual_clock);
|
do_div(clk_ns, mmc->actual_clock);
|
||||||
timeout = ns + clk_ns - 1;
|
timeout = ns + clk_ns - 1;
|
||||||
do_div(timeout, clk_ns);
|
do_div(timeout, clk_ns);
|
||||||
timeout += clks;
|
timeout += clks;
|
||||||
|
@ -802,6 +802,7 @@ static void msdc_ungate_clock(struct msdc_host *host)
|
||||||
|
|
||||||
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
u32 mode;
|
u32 mode;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u32 div;
|
u32 div;
|
||||||
|
@ -811,7 +812,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||||
if (!hz) {
|
if (!hz) {
|
||||||
dev_dbg(host->dev, "set mclk to 0\n");
|
dev_dbg(host->dev, "set mclk to 0\n");
|
||||||
host->mclk = 0;
|
host->mclk = 0;
|
||||||
host->mmc->actual_clock = 0;
|
mmc->actual_clock = 0;
|
||||||
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
|
sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -890,7 +891,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||||
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
|
while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB))
|
||||||
cpu_relax();
|
cpu_relax();
|
||||||
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
|
sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN);
|
||||||
host->mmc->actual_clock = sclk;
|
mmc->actual_clock = sclk;
|
||||||
host->mclk = hz;
|
host->mclk = hz;
|
||||||
host->timing = timing;
|
host->timing = timing;
|
||||||
/* need because clk changed. */
|
/* need because clk changed. */
|
||||||
|
@ -901,7 +902,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||||
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
|
* mmc_select_hs400() will drop to 50Mhz and High speed mode,
|
||||||
* tune result of hs200/200Mhz is not suitable for 50Mhz
|
* tune result of hs200/200Mhz is not suitable for 50Mhz
|
||||||
*/
|
*/
|
||||||
if (host->mmc->actual_clock <= 52000000) {
|
if (mmc->actual_clock <= 52000000) {
|
||||||
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
|
writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
|
||||||
if (host->top_base) {
|
if (host->top_base) {
|
||||||
writel(host->def_tune_para.emmc_top_control,
|
writel(host->def_tune_para.emmc_top_control,
|
||||||
|
@ -932,7 +933,7 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
|
||||||
sdr_set_field(host->base + tune_reg,
|
sdr_set_field(host->base + tune_reg,
|
||||||
MSDC_PAD_TUNE_CMDRRDLY,
|
MSDC_PAD_TUNE_CMDRRDLY,
|
||||||
host->hs400_cmd_int_delay);
|
host->hs400_cmd_int_delay);
|
||||||
dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->mmc->actual_clock,
|
dev_dbg(host->dev, "sclk: %d, timing: %d\n", mmc->actual_clock,
|
||||||
timing);
|
timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -967,6 +968,7 @@ static inline u32 msdc_cmd_find_resp(struct msdc_host *host,
|
||||||
static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
||||||
struct mmc_request *mrq, struct mmc_command *cmd)
|
struct mmc_request *mrq, struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
/* rawcmd :
|
/* rawcmd :
|
||||||
* vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
|
* vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 |
|
||||||
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
|
* stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode
|
||||||
|
@ -993,7 +995,7 @@ static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host,
|
||||||
struct mmc_data *data = cmd->data;
|
struct mmc_data *data = cmd->data;
|
||||||
|
|
||||||
if (mmc_op_multi(opcode)) {
|
if (mmc_op_multi(opcode)) {
|
||||||
if (mmc_card_mmc(host->mmc->card) && mrq->sbc &&
|
if (mmc_card_mmc(mmc->card) && mrq->sbc &&
|
||||||
!(mrq->sbc->arg & 0xFFFF0000))
|
!(mrq->sbc->arg & 0xFFFF0000))
|
||||||
rawcmd |= 0x2 << 28; /* AutoCMD23 */
|
rawcmd |= 0x2 << 28; /* AutoCMD23 */
|
||||||
}
|
}
|
||||||
|
@ -1070,9 +1072,10 @@ static int msdc_auto_cmd_done(struct msdc_host *host, int events,
|
||||||
*/
|
*/
|
||||||
static void msdc_recheck_sdio_irq(struct msdc_host *host)
|
static void msdc_recheck_sdio_irq(struct msdc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
u32 reg_int, reg_inten, reg_ps;
|
u32 reg_int, reg_inten, reg_ps;
|
||||||
|
|
||||||
if (host->mmc->caps & MMC_CAP_SDIO_IRQ) {
|
if (mmc->caps & MMC_CAP_SDIO_IRQ) {
|
||||||
reg_inten = readl(host->base + MSDC_INTEN);
|
reg_inten = readl(host->base + MSDC_INTEN);
|
||||||
if (reg_inten & MSDC_INTEN_SDIOIRQ) {
|
if (reg_inten & MSDC_INTEN_SDIOIRQ) {
|
||||||
reg_int = readl(host->base + MSDC_INT);
|
reg_int = readl(host->base + MSDC_INT);
|
||||||
|
@ -1080,7 +1083,7 @@ static void msdc_recheck_sdio_irq(struct msdc_host *host)
|
||||||
if (!(reg_int & MSDC_INT_SDIOIRQ ||
|
if (!(reg_int & MSDC_INT_SDIOIRQ ||
|
||||||
reg_ps & MSDC_PS_DATA1)) {
|
reg_ps & MSDC_PS_DATA1)) {
|
||||||
__msdc_enable_sdio_irq(host, 0);
|
__msdc_enable_sdio_irq(host, 0);
|
||||||
sdio_signal_irq(host->mmc);
|
sdio_signal_irq(mmc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1113,7 +1116,7 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq)
|
||||||
msdc_unprepare_data(host, mrq);
|
msdc_unprepare_data(host, mrq);
|
||||||
if (host->error)
|
if (host->error)
|
||||||
msdc_reset_hw(host);
|
msdc_reset_hw(host);
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(mmc_from_priv(host), mrq);
|
||||||
if (host->dev_comp->recheck_sdio_irq)
|
if (host->dev_comp->recheck_sdio_irq)
|
||||||
msdc_recheck_sdio_irq(host);
|
msdc_recheck_sdio_irq(host);
|
||||||
}
|
}
|
||||||
|
@ -1500,6 +1503,7 @@ static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||||
|
|
||||||
static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
|
static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
int cmd_err = 0, dat_err = 0;
|
int cmd_err = 0, dat_err = 0;
|
||||||
|
|
||||||
if (intsts & MSDC_INT_RSPCRCERR) {
|
if (intsts & MSDC_INT_RSPCRCERR) {
|
||||||
|
@ -1523,12 +1527,13 @@ static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
|
||||||
cmd_err, dat_err, intsts);
|
cmd_err, dat_err, intsts);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cqhci_irq(host->mmc, 0, cmd_err, dat_err);
|
return cqhci_irq(mmc, 0, cmd_err, dat_err);
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t msdc_irq(int irq, void *dev_id)
|
static irqreturn_t msdc_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct msdc_host *host = (struct msdc_host *) dev_id;
|
struct msdc_host *host = (struct msdc_host *) dev_id;
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -1551,18 +1556,18 @@ static irqreturn_t msdc_irq(int irq, void *dev_id)
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
if ((events & event_mask) & MSDC_INT_SDIOIRQ)
|
if ((events & event_mask) & MSDC_INT_SDIOIRQ)
|
||||||
sdio_signal_irq(host->mmc);
|
sdio_signal_irq(mmc);
|
||||||
|
|
||||||
if ((events & event_mask) & MSDC_INT_CDSC) {
|
if ((events & event_mask) & MSDC_INT_CDSC) {
|
||||||
if (host->internal_cd)
|
if (host->internal_cd)
|
||||||
mmc_detect_change(host->mmc, msecs_to_jiffies(20));
|
mmc_detect_change(mmc, msecs_to_jiffies(20));
|
||||||
events &= ~MSDC_INT_CDSC;
|
events &= ~MSDC_INT_CDSC;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
|
if (!(events & (event_mask & ~MSDC_INT_SDIOIRQ)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if ((host->mmc->caps2 & MMC_CAP2_CQE) &&
|
if ((mmc->caps2 & MMC_CAP2_CQE) &&
|
||||||
(events & MSDC_INT_CMDQ)) {
|
(events & MSDC_INT_CMDQ)) {
|
||||||
msdc_cmdq_irq(host, events);
|
msdc_cmdq_irq(host, events);
|
||||||
/* clear interrupts */
|
/* clear interrupts */
|
||||||
|
@ -2290,6 +2295,26 @@ static void msdc_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void msdc_cqe_pre_enable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = cqhci_readl(cq_host, CQHCI_CFG);
|
||||||
|
reg |= CQHCI_ENABLE;
|
||||||
|
cqhci_writel(cq_host, reg, CQHCI_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void msdc_cqe_post_disable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = cqhci_readl(cq_host, CQHCI_CFG);
|
||||||
|
reg &= ~CQHCI_ENABLE;
|
||||||
|
cqhci_writel(cq_host, reg, CQHCI_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct mmc_host_ops mt_msdc_ops = {
|
static const struct mmc_host_ops mt_msdc_ops = {
|
||||||
.post_req = msdc_post_req,
|
.post_req = msdc_post_req,
|
||||||
.pre_req = msdc_pre_req,
|
.pre_req = msdc_pre_req,
|
||||||
|
@ -2309,6 +2334,8 @@ static const struct mmc_host_ops mt_msdc_ops = {
|
||||||
static const struct cqhci_host_ops msdc_cmdq_ops = {
|
static const struct cqhci_host_ops msdc_cmdq_ops = {
|
||||||
.enable = msdc_cqe_enable,
|
.enable = msdc_cqe_enable,
|
||||||
.disable = msdc_cqe_disable,
|
.disable = msdc_cqe_disable,
|
||||||
|
.pre_enable = msdc_cqe_pre_enable,
|
||||||
|
.post_disable = msdc_cqe_post_disable,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void msdc_of_property_parse(struct platform_device *pdev,
|
static void msdc_of_property_parse(struct platform_device *pdev,
|
||||||
|
@ -2434,7 +2461,6 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
host->dev = &pdev->dev;
|
host->dev = &pdev->dev;
|
||||||
host->dev_comp = of_device_get_match_data(&pdev->dev);
|
host->dev_comp = of_device_get_match_data(&pdev->dev);
|
||||||
host->mmc = mmc;
|
|
||||||
host->src_clk_freq = clk_get_rate(host->src_clk);
|
host->src_clk_freq = clk_get_rate(host->src_clk);
|
||||||
/* Set host parameters to mmc */
|
/* Set host parameters to mmc */
|
||||||
mmc->ops = &mt_msdc_ops;
|
mmc->ops = &mt_msdc_ops;
|
||||||
|
@ -2475,7 +2501,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||||
mmc_dev(mmc)->dma_mask = &host->dma_mask;
|
mmc_dev(mmc)->dma_mask = &host->dma_mask;
|
||||||
|
|
||||||
if (mmc->caps2 & MMC_CAP2_CQE) {
|
if (mmc->caps2 & MMC_CAP2_CQE) {
|
||||||
host->cq_host = devm_kzalloc(host->mmc->parent,
|
host->cq_host = devm_kzalloc(mmc->parent,
|
||||||
sizeof(*host->cq_host),
|
sizeof(*host->cq_host),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!host->cq_host) {
|
if (!host->cq_host) {
|
||||||
|
@ -2560,7 +2586,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
|
||||||
pm_runtime_get_sync(host->dev);
|
pm_runtime_get_sync(host->dev);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, NULL);
|
platform_set_drvdata(pdev, NULL);
|
||||||
mmc_remove_host(host->mmc);
|
mmc_remove_host(mmc);
|
||||||
msdc_deinit_hw(host);
|
msdc_deinit_hw(host);
|
||||||
msdc_gate_clock(host);
|
msdc_gate_clock(host);
|
||||||
|
|
||||||
|
@ -2572,7 +2598,7 @@ static int msdc_drv_remove(struct platform_device *pdev)
|
||||||
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
|
dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc),
|
||||||
host->dma.bd, host->dma.bd_addr);
|
host->dma.bd, host->dma.bd_addr);
|
||||||
|
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(mmc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2607,6 +2633,7 @@ static void msdc_save_reg(struct msdc_host *host)
|
||||||
|
|
||||||
static void msdc_restore_reg(struct msdc_host *host)
|
static void msdc_restore_reg(struct msdc_host *host)
|
||||||
{
|
{
|
||||||
|
struct mmc_host *mmc = mmc_from_priv(host);
|
||||||
u32 tune_reg = host->dev_comp->pad_tune_reg;
|
u32 tune_reg = host->dev_comp->pad_tune_reg;
|
||||||
|
|
||||||
writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
|
writel(host->save_para.msdc_cfg, host->base + MSDC_CFG);
|
||||||
|
@ -2631,7 +2658,7 @@ static void msdc_restore_reg(struct msdc_host *host)
|
||||||
writel(host->save_para.pad_tune, host->base + tune_reg);
|
writel(host->save_para.pad_tune, host->base + tune_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sdio_irq_claimed(host->mmc))
|
if (sdio_irq_claimed(mmc))
|
||||||
__msdc_enable_sdio_irq(host, 1);
|
__msdc_enable_sdio_irq(host, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2667,6 +2694,7 @@ static struct platform_driver mt_msdc_driver = {
|
||||||
.remove = msdc_drv_remove,
|
.remove = msdc_drv_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mtk-msdc",
|
.name = "mtk-msdc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = msdc_of_ids,
|
.of_match_table = msdc_of_ids,
|
||||||
.pm = &msdc_dev_pm_ops,
|
.pm = &msdc_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -824,6 +824,7 @@ static struct platform_driver mvsd_driver = {
|
||||||
.remove = mvsd_remove,
|
.remove = mvsd_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = mvsdio_dt_ids,
|
.of_match_table = mvsdio_dt_ids,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1244,6 +1244,7 @@ static struct platform_driver mxcmci_driver = {
|
||||||
.id_table = mxcmci_devtype,
|
.id_table = mxcmci_devtype,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &mxcmci_pm_ops,
|
.pm = &mxcmci_pm_ops,
|
||||||
.of_match_table = mxcmci_of_match,
|
.of_match_table = mxcmci_of_match,
|
||||||
}
|
}
|
||||||
|
|
|
@ -726,6 +726,7 @@ static struct platform_driver mxs_mmc_driver = {
|
||||||
.id_table = mxs_ssp_ids,
|
.id_table = mxs_ssp_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &mxs_mmc_pm_ops,
|
.pm = &mxs_mmc_pm_ops,
|
||||||
.of_match_table = mxs_mmc_dt_ids,
|
.of_match_table = mxs_mmc_dt_ids,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1504,6 +1504,7 @@ static struct platform_driver mmc_omap_driver = {
|
||||||
.remove = mmc_omap_remove,
|
.remove = mmc_omap_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(mmc_omap_match),
|
.of_match_table = of_match_ptr(mmc_omap_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1114,8 +1114,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Disable the clocks */
|
/* Disable the clocks */
|
||||||
if (host->dbclk)
|
clk_disable_unprepare(host->dbclk);
|
||||||
clk_disable_unprepare(host->dbclk);
|
|
||||||
|
|
||||||
/* Turn the power off */
|
/* Turn the power off */
|
||||||
ret = omap_hsmmc_set_power(host, 0);
|
ret = omap_hsmmc_set_power(host, 0);
|
||||||
|
@ -1123,8 +1122,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)
|
||||||
/* Turn the power ON with given VDD 1.8 or 3.0v */
|
/* Turn the power ON with given VDD 1.8 or 3.0v */
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = omap_hsmmc_set_power(host, 1);
|
ret = omap_hsmmc_set_power(host, 1);
|
||||||
if (host->dbclk)
|
clk_prepare_enable(host->dbclk);
|
||||||
clk_prepare_enable(host->dbclk);
|
|
||||||
|
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -2014,8 +2012,7 @@ err_irq:
|
||||||
pm_runtime_dont_use_autosuspend(host->dev);
|
pm_runtime_dont_use_autosuspend(host->dev);
|
||||||
pm_runtime_put_sync(host->dev);
|
pm_runtime_put_sync(host->dev);
|
||||||
pm_runtime_disable(host->dev);
|
pm_runtime_disable(host->dev);
|
||||||
if (host->dbclk)
|
clk_disable_unprepare(host->dbclk);
|
||||||
clk_disable_unprepare(host->dbclk);
|
|
||||||
err1:
|
err1:
|
||||||
mmc_free_host(mmc);
|
mmc_free_host(mmc);
|
||||||
err:
|
err:
|
||||||
|
@ -2037,8 +2034,7 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
|
||||||
pm_runtime_put_sync(host->dev);
|
pm_runtime_put_sync(host->dev);
|
||||||
pm_runtime_disable(host->dev);
|
pm_runtime_disable(host->dev);
|
||||||
device_init_wakeup(&pdev->dev, false);
|
device_init_wakeup(&pdev->dev, false);
|
||||||
if (host->dbclk)
|
clk_disable_unprepare(host->dbclk);
|
||||||
clk_disable_unprepare(host->dbclk);
|
|
||||||
|
|
||||||
mmc_free_host(host->mmc);
|
mmc_free_host(host->mmc);
|
||||||
|
|
||||||
|
@ -2063,8 +2059,7 @@ static int omap_hsmmc_suspend(struct device *dev)
|
||||||
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
|
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->dbclk)
|
clk_disable_unprepare(host->dbclk);
|
||||||
clk_disable_unprepare(host->dbclk);
|
|
||||||
|
|
||||||
pm_runtime_put_sync(host->dev);
|
pm_runtime_put_sync(host->dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2080,8 +2075,7 @@ static int omap_hsmmc_resume(struct device *dev)
|
||||||
|
|
||||||
pm_runtime_get_sync(host->dev);
|
pm_runtime_get_sync(host->dev);
|
||||||
|
|
||||||
if (host->dbclk)
|
clk_prepare_enable(host->dbclk);
|
||||||
clk_prepare_enable(host->dbclk);
|
|
||||||
|
|
||||||
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
|
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
|
||||||
omap_hsmmc_conf_bus_power(host);
|
omap_hsmmc_conf_bus_power(host);
|
||||||
|
@ -2171,6 +2165,7 @@ static struct platform_driver omap_hsmmc_driver = {
|
||||||
.remove = omap_hsmmc_remove,
|
.remove = omap_hsmmc_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &omap_hsmmc_dev_pm_ops,
|
.pm = &omap_hsmmc_dev_pm_ops,
|
||||||
.of_match_table = of_match_ptr(omap_mmc_of_match),
|
.of_match_table = of_match_ptr(omap_mmc_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -689,6 +689,7 @@ MODULE_DEVICE_TABLE(of, owl_mmc_of_match);
|
||||||
static struct platform_driver owl_mmc_driver = {
|
static struct platform_driver owl_mmc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "owl_mmc",
|
.name = "owl_mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = owl_mmc_of_match,
|
.of_match_table = owl_mmc_of_match,
|
||||||
},
|
},
|
||||||
.probe = owl_mmc_probe,
|
.probe = owl_mmc_probe,
|
||||||
|
|
|
@ -811,6 +811,7 @@ static struct platform_driver pxamci_driver = {
|
||||||
.remove = pxamci_remove,
|
.remove = pxamci_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
|
.of_match_table = of_match_ptr(pxa_mmc_dt_ids),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,10 +33,13 @@ struct renesas_sdhi_of_data {
|
||||||
unsigned short max_segs;
|
unsigned short max_segs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define SDHI_CALIB_TABLE_MAX 32
|
||||||
|
|
||||||
struct renesas_sdhi_quirks {
|
struct renesas_sdhi_quirks {
|
||||||
bool hs400_disabled;
|
bool hs400_disabled;
|
||||||
bool hs400_4taps;
|
bool hs400_4taps;
|
||||||
u32 hs400_bad_taps;
|
u32 hs400_bad_taps;
|
||||||
|
const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tmio_mmc_dma {
|
struct tmio_mmc_dma {
|
||||||
|
@ -58,7 +61,8 @@ struct renesas_sdhi {
|
||||||
void __iomem *scc_ctl;
|
void __iomem *scc_ctl;
|
||||||
u32 scc_tappos;
|
u32 scc_tappos;
|
||||||
u32 scc_tappos_hs400;
|
u32 scc_tappos_hs400;
|
||||||
bool doing_tune;
|
const u8 *adjust_hs400_calib_table;
|
||||||
|
bool needs_adjust_hs400;
|
||||||
|
|
||||||
/* Tuning values: 1 for success, 0 for failure */
|
/* Tuning values: 1 for success, 0 for failure */
|
||||||
DECLARE_BITMAP(taps, BITS_PER_LONG);
|
DECLARE_BITMAP(taps, BITS_PER_LONG);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/mfd/tmio.h>
|
#include <linux/mfd/tmio.h>
|
||||||
#include <linux/sh_dma.h>
|
#include <linux/sh_dma.h>
|
||||||
|
@ -47,6 +48,8 @@
|
||||||
#define SDHI_VER_GEN3_SD 0xcc10
|
#define SDHI_VER_GEN3_SD 0xcc10
|
||||||
#define SDHI_VER_GEN3_SDMMC 0xcd10
|
#define SDHI_VER_GEN3_SDMMC 0xcd10
|
||||||
|
|
||||||
|
#define SDHI_GEN3_MMC0_ADDR 0xee140000
|
||||||
|
|
||||||
static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
||||||
{
|
{
|
||||||
u32 val;
|
u32 val;
|
||||||
|
@ -117,8 +120,12 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
|
||||||
unsigned int freq, diff, best_freq = 0, diff_min = ~0;
|
unsigned int freq, diff, best_freq = 0, diff_min = ~0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* tested only on R-Car Gen2+ currently; may work for others */
|
/*
|
||||||
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2))
|
* We simply return the current rate if a) we are not on a R-Car Gen2+
|
||||||
|
* SoC (may work for others, but untested) or b) if the SCC needs its
|
||||||
|
* clock during tuning, so we don't change the external clock setup.
|
||||||
|
*/
|
||||||
|
if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2) || mmc_doing_tune(host->mmc))
|
||||||
return clk_get_rate(priv->clk);
|
return clk_get_rate(priv->clk);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -247,6 +254,11 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A
|
#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A
|
||||||
#define SH_MOBILE_SDHI_SCC_SMPCMP 0x00C
|
#define SH_MOBILE_SDHI_SCC_SMPCMP 0x00C
|
||||||
#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E
|
#define SH_MOBILE_SDHI_SCC_TMPPORT2 0x00E
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT3 0x014
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT4 0x016
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT5 0x018
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT6 0x01A
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT7 0x01C
|
||||||
|
|
||||||
#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0)
|
#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0)
|
||||||
#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16
|
#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16
|
||||||
|
@ -267,6 +279,40 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
|
||||||
#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4)
|
#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL BIT(4)
|
||||||
#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)
|
#define SH_MOBILE_SDHI_SCC_TMPPORT2_HS400EN BIT(31)
|
||||||
|
|
||||||
|
/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT4 register */
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START BIT(0)
|
||||||
|
|
||||||
|
/* Definitions for values the SH_MOBILE_SDHI_SCC_TMPPORT5 register */
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R BIT(8)
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W (0 << 8)
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_ADR_MASK 0x3F
|
||||||
|
|
||||||
|
/* Definitions for values the SH_MOBILE_SDHI_SCC register */
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT_DISABLE_WP_CODE 0xa5000000
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT_CALIB_CODE_MASK 0x1f
|
||||||
|
#define SH_MOBILE_SDHI_SCC_TMPPORT_MANUAL_MODE BIT(7)
|
||||||
|
|
||||||
|
static const u8 r8a7796_es13_calib_table[2][SDHI_CALIB_TABLE_MAX] = {
|
||||||
|
{ 3, 3, 3, 3, 3, 3, 3, 4, 4, 5, 6, 7, 8, 9, 10, 15,
|
||||||
|
16, 16, 16, 16, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25 },
|
||||||
|
{ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 8, 11,
|
||||||
|
12, 17, 18, 18, 18, 18, 18, 18, 18, 19, 20, 21, 22, 23, 25, 25 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 r8a77965_calib_table[2][SDHI_CALIB_TABLE_MAX] = {
|
||||||
|
{ 1, 2, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 16,
|
||||||
|
17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 26, 27, 28, 29, 30, 31 },
|
||||||
|
{ 2, 3, 4, 4, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17,
|
||||||
|
17, 17, 20, 21, 22, 23, 24, 25, 27, 28, 29, 30, 31, 31, 31, 31 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 r8a77990_calib_table[2][SDHI_CALIB_TABLE_MAX] = {
|
||||||
|
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||||
|
{ 0, 0, 0, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 8, 9, 10,
|
||||||
|
11, 12, 13, 15, 16, 17, 17, 18, 18, 19, 20, 22, 24, 25, 26, 26 }
|
||||||
|
};
|
||||||
|
|
||||||
static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
|
static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
|
||||||
struct renesas_sdhi *priv, int addr)
|
struct renesas_sdhi *priv, int addr)
|
||||||
{
|
{
|
||||||
|
@ -373,6 +419,9 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
||||||
|
|
||||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
|
|
||||||
|
if (priv->adjust_hs400_calib_table)
|
||||||
|
priv->needs_adjust_hs400 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host,
|
static void renesas_sdhi_reset_scc(struct tmio_mmc_host *host,
|
||||||
|
@ -403,6 +452,74 @@ static void renesas_sdhi_disable_scc(struct mmc_host *mmc)
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 sd_scc_tmpport_read32(struct tmio_mmc_host *host,
|
||||||
|
struct renesas_sdhi *priv, u32 addr)
|
||||||
|
{
|
||||||
|
/* read mode */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT5,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_RW_SEL_R |
|
||||||
|
(SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr));
|
||||||
|
|
||||||
|
/* access start and stop */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT4,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START);
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT4, 0);
|
||||||
|
|
||||||
|
return sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT7);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sd_scc_tmpport_write32(struct tmio_mmc_host *host,
|
||||||
|
struct renesas_sdhi *priv, u32 addr, u32 val)
|
||||||
|
{
|
||||||
|
/* write mode */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT5,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_RW_SEL_W |
|
||||||
|
(SH_MOBILE_SDHI_SCC_TMPPORT5_DLL_ADR_MASK & addr));
|
||||||
|
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT6, val);
|
||||||
|
|
||||||
|
/* access start and stop */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT4,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT4_DLL_ACC_START);
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT4, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renesas_sdhi_adjust_hs400_mode_enable(struct tmio_mmc_host *host)
|
||||||
|
{
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
u32 calib_code;
|
||||||
|
|
||||||
|
/* disable write protect */
|
||||||
|
sd_scc_tmpport_write32(host, priv, 0x00,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT_DISABLE_WP_CODE);
|
||||||
|
/* read calibration code and adjust */
|
||||||
|
calib_code = sd_scc_tmpport_read32(host, priv, 0x26);
|
||||||
|
calib_code &= SH_MOBILE_SDHI_SCC_TMPPORT_CALIB_CODE_MASK;
|
||||||
|
|
||||||
|
sd_scc_tmpport_write32(host, priv, 0x22,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT_MANUAL_MODE |
|
||||||
|
priv->adjust_hs400_calib_table[calib_code]);
|
||||||
|
|
||||||
|
/* set offset value to TMPPORT3, hardcoded to OFFSET0 (= 0x3) for now */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT3, 0x3);
|
||||||
|
|
||||||
|
/* adjustment done, clear flag */
|
||||||
|
priv->needs_adjust_hs400 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void renesas_sdhi_adjust_hs400_mode_disable(struct tmio_mmc_host *host)
|
||||||
|
{
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
|
||||||
|
/* disable write protect */
|
||||||
|
sd_scc_tmpport_write32(host, priv, 0x00,
|
||||||
|
SH_MOBILE_SDHI_SCC_TMPPORT_DISABLE_WP_CODE);
|
||||||
|
/* disable manual calibration */
|
||||||
|
sd_scc_tmpport_write32(host, priv, 0x22, 0);
|
||||||
|
/* clear offset value of TMPPORT3 */
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT3, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
|
static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
|
||||||
struct renesas_sdhi *priv)
|
struct renesas_sdhi *priv)
|
||||||
{
|
{
|
||||||
|
@ -420,6 +537,9 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
|
||||||
SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
|
SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
|
||||||
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
|
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
|
||||||
|
|
||||||
|
if (priv->adjust_hs400_calib_table)
|
||||||
|
renesas_sdhi_adjust_hs400_mode_disable(host);
|
||||||
|
|
||||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
}
|
}
|
||||||
|
@ -432,6 +552,37 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void renesas_sdhi_reset(struct tmio_mmc_host *host)
|
||||||
|
{
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
|
||||||
|
renesas_sdhi_reset_scc(host, priv);
|
||||||
|
renesas_sdhi_reset_hs400_mode(host, priv);
|
||||||
|
priv->needs_adjust_hs400 = false;
|
||||||
|
|
||||||
|
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
||||||
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
|
|
||||||
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
|
||||||
|
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
|
||||||
|
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
|
||||||
|
|
||||||
|
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||||
|
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK,
|
||||||
|
TMIO_MASK_INIT_RCAR2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a temporary workaround! This driver used 'hw_reset' wrongly and the
|
||||||
|
* fix for that showed a regression. So, we mimic the old behaviour until the
|
||||||
|
* proper solution is found.
|
||||||
|
*/
|
||||||
|
static void renesas_sdhi_hw_reset(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
|
renesas_sdhi_reset(host);
|
||||||
|
}
|
||||||
|
|
||||||
#define SH_MOBILE_SDHI_MIN_TAP_ROW 3
|
#define SH_MOBILE_SDHI_MIN_TAP_ROW 3
|
||||||
|
|
||||||
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
||||||
|
@ -441,7 +592,6 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
||||||
unsigned int taps_size = priv->tap_num * 2, min_tap_row;
|
unsigned int taps_size = priv->tap_num * 2, min_tap_row;
|
||||||
unsigned long *bitmap;
|
unsigned long *bitmap;
|
||||||
|
|
||||||
priv->doing_tune = false;
|
|
||||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -500,10 +650,11 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
static int renesas_sdhi_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
{
|
{
|
||||||
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct renesas_sdhi *priv = host_to_priv(host);
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
priv->tap_num = renesas_sdhi_init_tuning(host);
|
priv->tap_num = renesas_sdhi_init_tuning(host);
|
||||||
if (!priv->tap_num)
|
if (!priv->tap_num)
|
||||||
|
@ -515,7 +666,6 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
priv->doing_tune = true;
|
|
||||||
bitmap_zero(priv->taps, priv->tap_num * 2);
|
bitmap_zero(priv->taps, priv->tap_num * 2);
|
||||||
bitmap_zero(priv->smpcmp, priv->tap_num * 2);
|
bitmap_zero(priv->smpcmp, priv->tap_num * 2);
|
||||||
|
|
||||||
|
@ -524,14 +674,17 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
||||||
/* Set sampling clock position */
|
/* Set sampling clock position */
|
||||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % priv->tap_num);
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, i % priv->tap_num);
|
||||||
|
|
||||||
if (mmc_send_tuning(host->mmc, opcode, NULL) == 0)
|
if (mmc_send_tuning(mmc, opcode, NULL) == 0)
|
||||||
set_bit(i, priv->taps);
|
set_bit(i, priv->taps);
|
||||||
|
|
||||||
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
|
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
|
||||||
set_bit(i, priv->smpcmp);
|
set_bit(i, priv->smpcmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renesas_sdhi_select_tuning(host);
|
ret = renesas_sdhi_select_tuning(host);
|
||||||
|
if (ret < 0)
|
||||||
|
renesas_sdhi_reset(host);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
|
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
|
||||||
|
@ -621,7 +774,7 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||||
!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && !use_4tap))
|
!(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 && !use_4tap))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (mmc_doing_retune(host->mmc) || priv->doing_tune)
|
if (mmc_doing_tune(host->mmc))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
|
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
|
||||||
|
@ -631,27 +784,6 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||||
return renesas_sdhi_manual_correction(host, use_4tap);
|
return renesas_sdhi_manual_correction(host, use_4tap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void renesas_sdhi_hw_reset(struct tmio_mmc_host *host)
|
|
||||||
{
|
|
||||||
struct renesas_sdhi *priv;
|
|
||||||
|
|
||||||
priv = host_to_priv(host);
|
|
||||||
|
|
||||||
renesas_sdhi_reset_scc(host, priv);
|
|
||||||
renesas_sdhi_reset_hs400_mode(host, priv);
|
|
||||||
|
|
||||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
|
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
|
||||||
|
|
||||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
|
|
||||||
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
|
|
||||||
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
|
|
||||||
|
|
||||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
|
||||||
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK,
|
|
||||||
TMIO_MASK_INIT_RCAR2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
|
static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit)
|
||||||
{
|
{
|
||||||
int timeout = 1000;
|
int timeout = 1000;
|
||||||
|
@ -711,6 +843,13 @@ static int renesas_sdhi_multi_io_quirk(struct mmc_card *card,
|
||||||
return blk_size;
|
return blk_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void renesas_sdhi_fixup_request(struct tmio_mmc_host *host, struct mmc_request *mrq)
|
||||||
|
{
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
|
||||||
|
if (priv->needs_adjust_hs400 && mrq->cmd->opcode == MMC_SEND_STATUS)
|
||||||
|
renesas_sdhi_adjust_hs400_mode_enable(host);
|
||||||
|
}
|
||||||
static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
|
static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
|
||||||
{
|
{
|
||||||
/* Iff regs are 8 byte apart, sdbuf is 64 bit. Otherwise always 32. */
|
/* Iff regs are 8 byte apart, sdbuf is 64 bit. Otherwise always 32. */
|
||||||
|
@ -742,6 +881,21 @@ static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
|
||||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct renesas_sdhi_quirks sdhi_quirks_r8a7796_es13 = {
|
||||||
|
.hs400_4taps = true,
|
||||||
|
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||||
|
.hs400_calib_table = r8a7796_es13_calib_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77965 = {
|
||||||
|
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||||
|
.hs400_calib_table = r8a77965_calib_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
|
||||||
|
.hs400_calib_table = r8a77990_calib_table,
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
|
* 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
|
* So, we want to treat them equally and only have a match for ES1.2 to enforce
|
||||||
|
@ -753,10 +907,11 @@ static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||||
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
||||||
{ .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
|
{ .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
|
||||||
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||||
{ .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap },
|
{ .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_r8a7796_es13 },
|
||||||
{ .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
|
{ .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
|
||||||
{ .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 },
|
{ .soc_id = "r8a77965", .data = &sdhi_quirks_r8a77965 },
|
||||||
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
||||||
|
{ .soc_id = "r8a77990", .data = &sdhi_quirks_r8a77990 },
|
||||||
{ /* Sentinel. */ },
|
{ /* Sentinel. */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -862,11 +1017,11 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
renesas_sdhi_start_signal_voltage_switch;
|
renesas_sdhi_start_signal_voltage_switch;
|
||||||
host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27;
|
host->sdcard_irq_setbit_mask = TMIO_STAT_ALWAYS_SET_27;
|
||||||
|
|
||||||
/* SDR and HS200/400 registers requires HW reset */
|
|
||||||
if (of_data && of_data->scc_offset) {
|
if (of_data && of_data->scc_offset) {
|
||||||
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
priv->scc_ctl = host->ctl + of_data->scc_offset;
|
||||||
|
host->reset = renesas_sdhi_reset;
|
||||||
|
host->ops.hw_reset = renesas_sdhi_hw_reset;
|
||||||
host->mmc->caps |= MMC_CAP_HW_RESET;
|
host->mmc->caps |= MMC_CAP_HW_RESET;
|
||||||
host->hw_reset = renesas_sdhi_hw_reset;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,6 +1070,14 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
if (ver == SDHI_VER_GEN2_SDR50)
|
if (ver == SDHI_VER_GEN2_SDR50)
|
||||||
mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY;
|
mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY;
|
||||||
|
|
||||||
|
if (ver == SDHI_VER_GEN3_SDMMC && quirks && quirks->hs400_calib_table) {
|
||||||
|
host->fixup_request = renesas_sdhi_fixup_request;
|
||||||
|
priv->adjust_hs400_calib_table = *(
|
||||||
|
res->start == SDHI_GEN3_MMC0_ADDR ?
|
||||||
|
quirks->hs400_calib_table :
|
||||||
|
quirks->hs400_calib_table + 1);
|
||||||
|
}
|
||||||
|
|
||||||
ret = tmio_mmc_host_probe(host);
|
ret = tmio_mmc_host_probe(host);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto edisclk;
|
goto edisclk;
|
||||||
|
@ -943,8 +1106,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
if (!hit)
|
if (!hit)
|
||||||
dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n");
|
dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n");
|
||||||
|
|
||||||
host->execute_tuning = renesas_sdhi_execute_tuning;
|
|
||||||
host->check_retune = renesas_sdhi_check_scc_error;
|
host->check_retune = renesas_sdhi_check_scc_error;
|
||||||
|
host->ops.execute_tuning = renesas_sdhi_execute_tuning;
|
||||||
host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning;
|
host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning;
|
||||||
host->ops.hs400_downgrade = renesas_sdhi_disable_scc;
|
host->ops.hs400_downgrade = renesas_sdhi_disable_scc;
|
||||||
host->ops.hs400_complete = renesas_sdhi_hs400_complete;
|
host->ops.hs400_complete = renesas_sdhi_hs400_complete;
|
||||||
|
|
|
@ -336,10 +336,6 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
|
||||||
if (soc)
|
if (soc)
|
||||||
global_flags |= (unsigned long)soc->data;
|
global_flags |= (unsigned long)soc->data;
|
||||||
|
|
||||||
dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
|
|
||||||
if (!dev->dma_parms)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* value is max of SD_SECCNT. Confirmed by HW engineers */
|
/* value is max of SD_SECCNT. Confirmed by HW engineers */
|
||||||
dma_set_max_seg_size(dev, 0xffffffff);
|
dma_set_max_seg_size(dev, 0xffffffff);
|
||||||
|
|
||||||
|
@ -357,6 +353,7 @@ static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
|
||||||
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
|
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "renesas_sdhi_internal_dmac",
|
.name = "renesas_sdhi_internal_dmac",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
|
.pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
|
||||||
.of_match_table = renesas_sdhi_internal_dmac_of_match,
|
.of_match_table = renesas_sdhi_internal_dmac_of_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -463,6 +463,7 @@ static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = {
|
||||||
static struct platform_driver renesas_sys_dmac_sdhi_driver = {
|
static struct platform_driver renesas_sys_dmac_sdhi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sh_mobile_sdhi",
|
.name = "sh_mobile_sdhi",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &renesas_sdhi_sys_dmac_dev_pm_ops,
|
.pm = &renesas_sdhi_sys_dmac_dev_pm_ops,
|
||||||
.of_match_table = renesas_sdhi_sys_dmac_of_match,
|
.of_match_table = renesas_sdhi_sys_dmac_of_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1471,6 +1471,7 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
|
||||||
.id_table = rtsx_pci_sdmmc_ids,
|
.id_table = rtsx_pci_sdmmc_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRV_NAME_RTSX_PCI_SDMMC,
|
.name = DRV_NAME_RTSX_PCI_SDMMC,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
module_platform_driver(rtsx_pci_sdmmc_driver);
|
module_platform_driver(rtsx_pci_sdmmc_driver);
|
||||||
|
|
|
@ -579,7 +579,6 @@ static void sd_normal_rw(struct rtsx_usb_sdmmc *host,
|
||||||
static int sd_change_phase(struct rtsx_usb_sdmmc *host, u8 sample_point, int tx)
|
static int sd_change_phase(struct rtsx_usb_sdmmc *host, u8 sample_point, int tx)
|
||||||
{
|
{
|
||||||
struct rtsx_ucr *ucr = host->ucr;
|
struct rtsx_ucr *ucr = host->ucr;
|
||||||
int err;
|
|
||||||
|
|
||||||
dev_dbg(sdmmc_dev(host), "%s: %s sample_point = %d\n",
|
dev_dbg(sdmmc_dev(host), "%s: %s sample_point = %d\n",
|
||||||
__func__, tx ? "TX" : "RX", sample_point);
|
__func__, tx ? "TX" : "RX", sample_point);
|
||||||
|
@ -601,11 +600,7 @@ static int sd_change_phase(struct rtsx_usb_sdmmc *host, u8 sample_point, int tx)
|
||||||
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, 0);
|
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CLK_DIV, CLK_CHANGE, 0);
|
||||||
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_RST, 0);
|
rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_RST, 0);
|
||||||
|
|
||||||
err = rtsx_usb_send_cmd(ucr, MODE_C, 100);
|
return rtsx_usb_send_cmd(ucr, MODE_C, 100);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 get_phase_point(u32 phase_map, unsigned int idx)
|
static inline u32 get_phase_point(u32 phase_map, unsigned int idx)
|
||||||
|
@ -1458,6 +1453,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
|
||||||
.id_table = rtsx_usb_sdmmc_ids,
|
.id_table = rtsx_usb_sdmmc_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "rtsx_usb_sdmmc",
|
.name = "rtsx_usb_sdmmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &rtsx_usb_sdmmc_dev_pm_ops,
|
.pm = &rtsx_usb_sdmmc_dev_pm_ops,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -150,8 +150,8 @@ static void s3cmci_reset(struct s3cmci_host *host);
|
||||||
|
|
||||||
static void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
|
static void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
|
||||||
{
|
{
|
||||||
u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer, bsize;
|
u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer;
|
||||||
u32 datcon, datcnt, datsta, fsta, imask;
|
u32 datcon, datcnt, datsta, fsta;
|
||||||
|
|
||||||
con = readl(host->base + S3C2410_SDICON);
|
con = readl(host->base + S3C2410_SDICON);
|
||||||
pre = readl(host->base + S3C2410_SDIPRE);
|
pre = readl(host->base + S3C2410_SDIPRE);
|
||||||
|
@ -163,12 +163,10 @@ static void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
|
||||||
r2 = readl(host->base + S3C2410_SDIRSP2);
|
r2 = readl(host->base + S3C2410_SDIRSP2);
|
||||||
r3 = readl(host->base + S3C2410_SDIRSP3);
|
r3 = readl(host->base + S3C2410_SDIRSP3);
|
||||||
timer = readl(host->base + S3C2410_SDITIMER);
|
timer = readl(host->base + S3C2410_SDITIMER);
|
||||||
bsize = readl(host->base + S3C2410_SDIBSIZE);
|
|
||||||
datcon = readl(host->base + S3C2410_SDIDCON);
|
datcon = readl(host->base + S3C2410_SDIDCON);
|
||||||
datcnt = readl(host->base + S3C2410_SDIDCNT);
|
datcnt = readl(host->base + S3C2410_SDIDCNT);
|
||||||
datsta = readl(host->base + S3C2410_SDIDSTA);
|
datsta = readl(host->base + S3C2410_SDIDSTA);
|
||||||
fsta = readl(host->base + S3C2410_SDIFSTA);
|
fsta = readl(host->base + S3C2410_SDIFSTA);
|
||||||
imask = readl(host->base + host->sdiimsk);
|
|
||||||
|
|
||||||
dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
|
dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
|
||||||
prefix, con, pre, timer);
|
prefix, con, pre, timer);
|
||||||
|
@ -396,9 +394,6 @@ static void s3cmci_enable_irq(struct s3cmci_host *host, bool more)
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
|
static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -1379,7 +1374,7 @@ static int s3cmci_state_show(struct seq_file *seq, void *v)
|
||||||
{
|
{
|
||||||
struct s3cmci_host *host = seq->private;
|
struct s3cmci_host *host = seq->private;
|
||||||
|
|
||||||
seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base);
|
seq_printf(seq, "Register base = 0x%p\n", host->base);
|
||||||
seq_printf(seq, "Clock rate = %ld\n", host->clk_rate);
|
seq_printf(seq, "Clock rate = %ld\n", host->clk_rate);
|
||||||
seq_printf(seq, "Prescale = %d\n", host->prescaler);
|
seq_printf(seq, "Prescale = %d\n", host->prescaler);
|
||||||
seq_printf(seq, "is2440 = %d\n", host->is2440);
|
seq_printf(seq, "is2440 = %d\n", host->is2440);
|
||||||
|
@ -1522,7 +1517,7 @@ static int s3cmci_probe_dt(struct s3cmci_host *host)
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
host->is2440 = (int) of_device_get_match_data(&pdev->dev);
|
host->is2440 = (long) of_device_get_match_data(&pdev->dev);
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -1809,6 +1804,7 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
|
||||||
static struct platform_driver s3cmci_driver = {
|
static struct platform_driver s3cmci_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "s3c-sdi",
|
.name = "s3c-sdi",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = s3cmci_dt_match,
|
.of_match_table = s3cmci_dt_match,
|
||||||
},
|
},
|
||||||
.id_table = s3cmci_driver_ids,
|
.id_table = s3cmci_driver_ids,
|
||||||
|
|
|
@ -662,6 +662,43 @@ static int sdhci_acpi_emmc_amd_probe_slot(struct platform_device *pdev,
|
||||||
(host->mmc->caps & MMC_CAP_1_8V_DDR))
|
(host->mmc->caps & MMC_CAP_1_8V_DDR))
|
||||||
host->mmc->caps2 = MMC_CAP2_HS400_1_8V;
|
host->mmc->caps2 = MMC_CAP2_HS400_1_8V;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are two types of presets out in the wild:
|
||||||
|
* 1) Default/broken presets.
|
||||||
|
* These presets have two sets of problems:
|
||||||
|
* a) The clock divisor for SDR12, SDR25, and SDR50 is too small.
|
||||||
|
* This results in clock frequencies that are 2x higher than
|
||||||
|
* acceptable. i.e., SDR12 = 25 MHz, SDR25 = 50 MHz, SDR50 =
|
||||||
|
* 100 MHz.x
|
||||||
|
* b) The HS200 and HS400 driver strengths don't match.
|
||||||
|
* By default, the SDR104 preset register has a driver strength of
|
||||||
|
* A, but the (internal) HS400 preset register has a driver
|
||||||
|
* strength of B. As part of initializing HS400, HS200 tuning
|
||||||
|
* needs to be performed. Having different driver strengths
|
||||||
|
* between tuning and operation is wrong. It results in different
|
||||||
|
* rise/fall times that lead to incorrect sampling.
|
||||||
|
* 2) Firmware with properly initialized presets.
|
||||||
|
* These presets have proper clock divisors. i.e., SDR12 => 12MHz,
|
||||||
|
* SDR25 => 25 MHz, SDR50 => 50 MHz. Additionally the HS200 and
|
||||||
|
* HS400 preset driver strengths match.
|
||||||
|
*
|
||||||
|
* Enabling presets for HS400 doesn't work for the following reasons:
|
||||||
|
* 1) sdhci_set_ios has a hard coded list of timings that are used
|
||||||
|
* to determine if presets should be enabled.
|
||||||
|
* 2) sdhci_get_preset_value is using a non-standard register to
|
||||||
|
* read out HS400 presets. The AMD controller doesn't support this
|
||||||
|
* non-standard register. In fact, it doesn't expose the HS400
|
||||||
|
* preset register anywhere in the SDHCI memory map. This results
|
||||||
|
* in reading a garbage value and using the wrong presets.
|
||||||
|
*
|
||||||
|
* Since HS400 and HS200 presets must be identical, we could
|
||||||
|
* instead use the the SDR104 preset register.
|
||||||
|
*
|
||||||
|
* If the above issues are resolved we could remove this quirk for
|
||||||
|
* firmware that that has valid presets (i.e., SDR12 <= 12 MHz).
|
||||||
|
*/
|
||||||
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
|
|
||||||
host->mmc_host_ops.select_drive_strength = amd_select_drive_strength;
|
host->mmc_host_ops.select_drive_strength = amd_select_drive_strength;
|
||||||
host->mmc_host_ops.set_ios = amd_set_ios;
|
host->mmc_host_ops.set_ios = amd_set_ios;
|
||||||
host->mmc_host_ops.execute_tuning = amd_sdhci_execute_tuning;
|
host->mmc_host_ops.execute_tuning = amd_sdhci_execute_tuning;
|
||||||
|
@ -1027,6 +1064,7 @@ static const struct dev_pm_ops sdhci_acpi_pm_ops = {
|
||||||
static struct platform_driver sdhci_acpi_driver = {
|
static struct platform_driver sdhci_acpi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-acpi",
|
.name = "sdhci-acpi",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.acpi_match_table = sdhci_acpi_ids,
|
.acpi_match_table = sdhci_acpi_ids,
|
||||||
.pm = &sdhci_acpi_pm_ops,
|
.pm = &sdhci_acpi_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -324,6 +324,7 @@ err_pltfm_free:
|
||||||
static struct platform_driver sdhci_bcm_kona_driver = {
|
static struct platform_driver sdhci_bcm_kona_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-kona",
|
.name = "sdhci-kona",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = sdhci_bcm_kona_of_match,
|
.of_match_table = sdhci_bcm_kona_of_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -235,13 +235,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible);
|
dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible);
|
||||||
|
|
||||||
clk = devm_clk_get(&pdev->dev, NULL);
|
clk = devm_clk_get_optional(&pdev->dev, NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk))
|
||||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
|
||||||
return -EPROBE_DEFER;
|
"Failed to get clock from Device Tree\n");
|
||||||
dev_err(&pdev->dev, "Clock not found in Device Tree\n");
|
|
||||||
clk = NULL;
|
|
||||||
}
|
|
||||||
res = clk_prepare_enable(clk);
|
res = clk_prepare_enable(clk);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
@ -328,6 +326,7 @@ MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
|
||||||
static struct platform_driver sdhci_brcmstb_driver = {
|
static struct platform_driver sdhci_brcmstb_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-brcmstb",
|
.name = "sdhci-brcmstb",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
|
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -463,6 +463,7 @@ MODULE_DEVICE_TABLE(of, sdhci_cdns_match);
|
||||||
static struct platform_driver sdhci_cdns_driver = {
|
static struct platform_driver sdhci_cdns_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-cdns",
|
.name = "sdhci-cdns",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_cdns_pm_ops,
|
.pm = &sdhci_cdns_pm_ops,
|
||||||
.of_match_table = sdhci_cdns_match,
|
.of_match_table = sdhci_cdns_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -98,6 +98,7 @@ static int sdhci_cns3xxx_probe(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_cns3xxx_driver = {
|
static struct platform_driver sdhci_cns3xxx_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-cns3xxx",
|
.name = "sdhci-cns3xxx",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
.probe = sdhci_cns3xxx_probe,
|
.probe = sdhci_cns3xxx_probe,
|
||||||
|
|
|
@ -105,6 +105,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
|
||||||
static struct platform_driver sdhci_dove_driver = {
|
static struct platform_driver sdhci_dove_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-dove",
|
.name = "sdhci-dove",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = sdhci_dove_of_match_table,
|
.of_match_table = sdhci_dove_of_match_table,
|
||||||
},
|
},
|
||||||
|
|
|
@ -987,10 +987,20 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
|
||||||
{
|
{
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
u8 sw_rst;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
|
||||||
mdelay(1);
|
mdelay(1);
|
||||||
|
|
||||||
|
/* IC suggest to reset USDHC before every tuning command */
|
||||||
|
esdhc_clrset_le(host, 0xff, SDHCI_RESET_ALL, SDHCI_SOFTWARE_RESET);
|
||||||
|
ret = readb_poll_timeout(host->ioaddr + SDHCI_SOFTWARE_RESET, sw_rst,
|
||||||
|
!(sw_rst & SDHCI_RESET_ALL), 10, 100);
|
||||||
|
if (ret == -ETIMEDOUT)
|
||||||
|
dev_warn(mmc_dev(host->mmc),
|
||||||
|
"warning! RESET_ALL never complete before sending tuning command\n");
|
||||||
|
|
||||||
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
|
||||||
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
|
||||||
ESDHC_MIX_CTRL_FBCLK_SEL;
|
ESDHC_MIX_CTRL_FBCLK_SEL;
|
||||||
|
@ -1367,7 +1377,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
||||||
* response, block the tuning procedure or the first command
|
* response, block the tuning procedure or the first command
|
||||||
* after the whole tuning procedure always can't get any response.
|
* after the whole tuning procedure always can't get any response.
|
||||||
*/
|
*/
|
||||||
tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
|
tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
|
||||||
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||||
/*
|
/*
|
||||||
|
@ -1643,10 +1653,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
||||||
goto disable_ipg_clk;
|
goto disable_ipg_clk;
|
||||||
|
|
||||||
imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
|
imx_data->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||||
if (IS_ERR(imx_data->pinctrl)) {
|
if (IS_ERR(imx_data->pinctrl))
|
||||||
err = PTR_ERR(imx_data->pinctrl);
|
|
||||||
dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
|
dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (esdhc_is_usdhc(imx_data)) {
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
|
@ -1917,6 +1925,7 @@ static const struct dev_pm_ops sdhci_esdhc_pmops = {
|
||||||
static struct platform_driver sdhci_esdhc_imx_driver = {
|
static struct platform_driver sdhci_esdhc_imx_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-esdhc-imx",
|
.name = "sdhci-esdhc-imx",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = imx_esdhc_dt_ids,
|
.of_match_table = imx_esdhc_dt_ids,
|
||||||
.pm = &sdhci_esdhc_pmops,
|
.pm = &sdhci_esdhc_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -509,6 +509,7 @@ static int sdhci_esdhc_mcf_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_esdhc_mcf_driver = {
|
static struct platform_driver sdhci_esdhc_mcf_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-esdhc-mcf",
|
.name = "sdhci-esdhc-mcf",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
},
|
},
|
||||||
.probe = sdhci_esdhc_mcf_probe,
|
.probe = sdhci_esdhc_mcf_probe,
|
||||||
.remove = sdhci_esdhc_mcf_remove,
|
.remove = sdhci_esdhc_mcf_remove,
|
||||||
|
|
|
@ -283,6 +283,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2711_pltfm_data = {
|
||||||
|
|
||||||
static const struct sdhci_iproc_data bcm2711_data = {
|
static const struct sdhci_iproc_data bcm2711_data = {
|
||||||
.pdata = &sdhci_bcm2711_pltfm_data,
|
.pdata = &sdhci_bcm2711_pltfm_data,
|
||||||
|
.mmc_caps = MMC_CAP_3_3V_DDR,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id sdhci_iproc_of_match[] = {
|
static const struct of_device_id sdhci_iproc_of_match[] = {
|
||||||
|
@ -368,6 +369,7 @@ err:
|
||||||
static struct platform_driver sdhci_iproc_driver = {
|
static struct platform_driver sdhci_iproc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-iproc",
|
.name = "sdhci-iproc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_iproc_of_match,
|
.of_match_table = sdhci_iproc_of_match,
|
||||||
.acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids),
|
.acpi_match_table = ACPI_PTR(sdhci_iproc_acpi_ids),
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
|
|
|
@ -333,6 +333,7 @@ static int sdhci_milbeaut_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_milbeaut_driver = {
|
static struct platform_driver sdhci_milbeaut_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-milbeaut",
|
.name = "sdhci-milbeaut",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(mlb_dt_ids),
|
.of_match_table = of_match_ptr(mlb_dt_ids),
|
||||||
},
|
},
|
||||||
.probe = sdhci_milbeaut_probe,
|
.probe = sdhci_milbeaut_probe,
|
||||||
|
|
|
@ -263,7 +263,6 @@ struct sdhci_msm_host {
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct opp_table *opp_table;
|
struct opp_table *opp_table;
|
||||||
bool has_opp_table;
|
|
||||||
bool use_14lpp_dll_reset;
|
bool use_14lpp_dll_reset;
|
||||||
bool tuning_done;
|
bool tuning_done;
|
||||||
bool calibration_done;
|
bool calibration_done;
|
||||||
|
@ -2167,6 +2166,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||||
{.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
|
{.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
|
||||||
|
{.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2301,11 +2301,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
/* OPP table is optional */
|
/* OPP table is optional */
|
||||||
ret = dev_pm_opp_of_add_table(&pdev->dev);
|
ret = dev_pm_opp_of_add_table(&pdev->dev);
|
||||||
if (!ret) {
|
if (ret && ret != -ENODEV) {
|
||||||
msm_host->has_opp_table = true;
|
|
||||||
} else if (ret != -ENODEV) {
|
|
||||||
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
|
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
|
||||||
goto opp_cleanup;
|
goto opp_put_clkname;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vote for maximum clock rate for maximum performance */
|
/* Vote for maximum clock rate for maximum performance */
|
||||||
|
@ -2469,8 +2467,8 @@ clk_disable:
|
||||||
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
||||||
msm_host->bulk_clks);
|
msm_host->bulk_clks);
|
||||||
opp_cleanup:
|
opp_cleanup:
|
||||||
if (msm_host->has_opp_table)
|
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
opp_put_clkname:
|
||||||
dev_pm_opp_put_clkname(msm_host->opp_table);
|
dev_pm_opp_put_clkname(msm_host->opp_table);
|
||||||
bus_clk_disable:
|
bus_clk_disable:
|
||||||
if (!IS_ERR(msm_host->bus_clk))
|
if (!IS_ERR(msm_host->bus_clk))
|
||||||
|
@ -2490,8 +2488,7 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
sdhci_remove_host(host, dead);
|
sdhci_remove_host(host, dead);
|
||||||
|
|
||||||
if (msm_host->has_opp_table)
|
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
|
||||||
dev_pm_opp_put_clkname(msm_host->opp_table);
|
dev_pm_opp_put_clkname(msm_host->opp_table);
|
||||||
pm_runtime_get_sync(&pdev->dev);
|
pm_runtime_get_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
|
@ -2557,6 +2554,7 @@ static struct platform_driver sdhci_msm_driver = {
|
||||||
.name = "sdhci_msm",
|
.name = "sdhci_msm",
|
||||||
.of_match_table = sdhci_msm_dt_match,
|
.of_match_table = sdhci_msm_dt_match,
|
||||||
.pm = &sdhci_msm_pm_ops,
|
.pm = &sdhci_msm_pm_ops,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1543,10 +1543,9 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||||
of_node_put(node);
|
of_node_put(node);
|
||||||
|
|
||||||
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
|
if (IS_ERR(sdhci_arasan->soc_ctl_base)) {
|
||||||
ret = PTR_ERR(sdhci_arasan->soc_ctl_base);
|
ret = dev_err_probe(&pdev->dev,
|
||||||
if (ret != -EPROBE_DEFER)
|
PTR_ERR(sdhci_arasan->soc_ctl_base),
|
||||||
dev_err(&pdev->dev, "Can't get syscon: %d\n",
|
"Can't get syscon\n");
|
||||||
ret);
|
|
||||||
goto err_pltfm_free;
|
goto err_pltfm_free;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1694,6 +1693,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_arasan_driver = {
|
static struct platform_driver sdhci_arasan_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-arasan",
|
.name = "sdhci-arasan",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_arasan_of_match,
|
.of_match_table = sdhci_arasan_of_match,
|
||||||
.pm = &sdhci_arasan_dev_pm_ops,
|
.pm = &sdhci_arasan_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -240,6 +240,7 @@ static const struct of_device_id aspeed_sdhci_of_match[] = {
|
||||||
static struct platform_driver aspeed_sdhci_driver = {
|
static struct platform_driver aspeed_sdhci_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-aspeed",
|
.name = "sdhci-aspeed",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = aspeed_sdhci_of_match,
|
.of_match_table = aspeed_sdhci_of_match,
|
||||||
},
|
},
|
||||||
.probe = aspeed_sdhci_probe,
|
.probe = aspeed_sdhci_probe,
|
||||||
|
@ -318,6 +319,7 @@ MODULE_DEVICE_TABLE(of, aspeed_sdc_of_match);
|
||||||
static struct platform_driver aspeed_sdc_driver = {
|
static struct platform_driver aspeed_sdc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sd-controller-aspeed",
|
.name = "sd-controller-aspeed",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
.of_match_table = aspeed_sdc_of_match,
|
.of_match_table = aspeed_sdc_of_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -465,6 +465,7 @@ static int sdhci_at91_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_at91_driver = {
|
static struct platform_driver sdhci_at91_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-at91",
|
.name = "sdhci-at91",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_at91_dt_match,
|
.of_match_table = sdhci_at91_dt_match,
|
||||||
.pm = &sdhci_at91_dev_pm_ops,
|
.pm = &sdhci_at91_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -214,6 +214,7 @@ MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
|
||||||
static struct platform_driver sdhci_dwcmshc_driver = {
|
static struct platform_driver sdhci_dwcmshc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-dwcmshc",
|
.name = "sdhci-dwcmshc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_dwcmshc_dt_ids,
|
.of_match_table = sdhci_dwcmshc_dt_ids,
|
||||||
.pm = &dwcmshc_pmops,
|
.pm = &dwcmshc_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1360,13 +1360,19 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
||||||
clk_put(clk);
|
clk_put(clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esdhc->peripheral_clock) {
|
esdhc_clock_enable(host, false);
|
||||||
esdhc_clock_enable(host, false);
|
val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||||
val = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
/*
|
||||||
|
* This bit is not able to be reset by SDHCI_RESET_ALL. Need to
|
||||||
|
* initialize it as 1 or 0 once, to override the different value
|
||||||
|
* which may be configured in bootloader.
|
||||||
|
*/
|
||||||
|
if (esdhc->peripheral_clock)
|
||||||
val |= ESDHC_PERIPHERAL_CLK_SEL;
|
val |= ESDHC_PERIPHERAL_CLK_SEL;
|
||||||
sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
else
|
||||||
esdhc_clock_enable(host, true);
|
val &= ~ESDHC_PERIPHERAL_CLK_SEL;
|
||||||
}
|
sdhci_writel(host, val, ESDHC_DMA_SYSCTL);
|
||||||
|
esdhc_clock_enable(host, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
|
static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
|
||||||
|
@ -1468,6 +1474,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_esdhc_driver = {
|
static struct platform_driver sdhci_esdhc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-esdhc",
|
.name = "sdhci-esdhc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_esdhc_of_match,
|
.of_match_table = sdhci_esdhc_of_match,
|
||||||
.pm = &esdhc_of_dev_pm_ops,
|
.pm = &esdhc_of_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,6 +80,7 @@ MODULE_DEVICE_TABLE(of, sdhci_hlwd_of_match);
|
||||||
static struct platform_driver sdhci_hlwd_driver = {
|
static struct platform_driver sdhci_hlwd_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-hlwd",
|
.name = "sdhci-hlwd",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_hlwd_of_match,
|
.of_match_table = sdhci_hlwd_of_match,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* drivers/mmc/host/sdhci-of-sparx5.c
|
||||||
|
*
|
||||||
|
* MCHP Sparx5 SoC Secure Digital Host Controller Interface.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2019 Microchip Inc.
|
||||||
|
*
|
||||||
|
* Author: Lars Povlsen <lars.povlsen@microchip.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
|
|
||||||
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
#define CPU_REGS_GENERAL_CTRL (0x22 * 4)
|
||||||
|
#define MSHC_DLY_CC_MASK GENMASK(16, 13)
|
||||||
|
#define MSHC_DLY_CC_SHIFT 13
|
||||||
|
#define MSHC_DLY_CC_MAX 15
|
||||||
|
|
||||||
|
#define CPU_REGS_PROC_CTRL (0x2C * 4)
|
||||||
|
#define ACP_CACHE_FORCE_ENA BIT(4)
|
||||||
|
#define ACP_AWCACHE BIT(3)
|
||||||
|
#define ACP_ARCACHE BIT(2)
|
||||||
|
#define ACP_CACHE_MASK (ACP_CACHE_FORCE_ENA|ACP_AWCACHE|ACP_ARCACHE)
|
||||||
|
|
||||||
|
#define MSHC2_VERSION 0x500 /* Off 0x140, reg 0x0 */
|
||||||
|
#define MSHC2_TYPE 0x504 /* Off 0x140, reg 0x1 */
|
||||||
|
#define MSHC2_EMMC_CTRL 0x52c /* Off 0x140, reg 0xB */
|
||||||
|
#define MSHC2_EMMC_CTRL_EMMC_RST_N BIT(2)
|
||||||
|
#define MSHC2_EMMC_CTRL_IS_EMMC BIT(0)
|
||||||
|
|
||||||
|
struct sdhci_sparx5_data {
|
||||||
|
struct sdhci_host *host;
|
||||||
|
struct regmap *cpu_ctrl;
|
||||||
|
int delay_clock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define BOUNDARY_OK(addr, len) \
|
||||||
|
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
|
||||||
|
* so that each DMA transfer doesn't exceed the boundary.
|
||||||
|
*/
|
||||||
|
static void sdhci_sparx5_adma_write_desc(struct sdhci_host *host, void **desc,
|
||||||
|
dma_addr_t addr, int len,
|
||||||
|
unsigned int cmd)
|
||||||
|
{
|
||||||
|
int tmplen, offset;
|
||||||
|
|
||||||
|
if (likely(!len || BOUNDARY_OK(addr, len))) {
|
||||||
|
sdhci_adma_write_desc(host, desc, addr, len, cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("%s: write_desc: splitting dma len %d, offset %pad\n",
|
||||||
|
mmc_hostname(host->mmc), len, &addr);
|
||||||
|
|
||||||
|
offset = addr & (SZ_128M - 1);
|
||||||
|
tmplen = SZ_128M - offset;
|
||||||
|
sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
|
||||||
|
|
||||||
|
addr += tmplen;
|
||||||
|
len -= tmplen;
|
||||||
|
sdhci_adma_write_desc(host, desc, addr, len, cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sparx5_set_cacheable(struct sdhci_host *host, u32 value)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
|
||||||
|
pr_debug("%s: Set Cacheable = 0x%x\n", mmc_hostname(host->mmc), value);
|
||||||
|
|
||||||
|
/* Update ACP caching attributes in HW */
|
||||||
|
regmap_update_bits(sdhci_sparx5->cpu_ctrl,
|
||||||
|
CPU_REGS_PROC_CTRL, ACP_CACHE_MASK, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sparx5_set_delay(struct sdhci_host *host, u8 value)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_sparx5_data *sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
|
||||||
|
pr_debug("%s: Set DLY_CC = %u\n", mmc_hostname(host->mmc), value);
|
||||||
|
|
||||||
|
/* Update DLY_CC in HW */
|
||||||
|
regmap_update_bits(sdhci_sparx5->cpu_ctrl,
|
||||||
|
CPU_REGS_GENERAL_CTRL,
|
||||||
|
MSHC_DLY_CC_MASK,
|
||||||
|
(value << MSHC_DLY_CC_SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_sparx5_set_emmc(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
if (!mmc_card_is_removable(host->mmc)) {
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
value = sdhci_readb(host, MSHC2_EMMC_CTRL);
|
||||||
|
if (!(value & MSHC2_EMMC_CTRL_IS_EMMC)) {
|
||||||
|
value |= MSHC2_EMMC_CTRL_IS_EMMC;
|
||||||
|
pr_debug("%s: Set EMMC_CTRL: 0x%08x\n",
|
||||||
|
mmc_hostname(host->mmc), value);
|
||||||
|
sdhci_writeb(host, value, MSHC2_EMMC_CTRL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_sparx5_reset_emmc(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
pr_debug("%s: Toggle EMMC_CTRL.EMMC_RST_N\n", mmc_hostname(host->mmc));
|
||||||
|
value = sdhci_readb(host, MSHC2_EMMC_CTRL) &
|
||||||
|
~MSHC2_EMMC_CTRL_EMMC_RST_N;
|
||||||
|
sdhci_writeb(host, value, MSHC2_EMMC_CTRL);
|
||||||
|
/* For eMMC, minimum is 1us but give it 10us for good measure */
|
||||||
|
usleep_range(10, 20);
|
||||||
|
sdhci_writeb(host, value | MSHC2_EMMC_CTRL_EMMC_RST_N,
|
||||||
|
MSHC2_EMMC_CTRL);
|
||||||
|
/* For eMMC, minimum is 200us but give it 300us for good measure */
|
||||||
|
usleep_range(300, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_sparx5_reset(struct sdhci_host *host, u8 mask)
|
||||||
|
{
|
||||||
|
pr_debug("%s: *** RESET: mask %d\n", mmc_hostname(host->mmc), mask);
|
||||||
|
|
||||||
|
sdhci_reset(host, mask);
|
||||||
|
|
||||||
|
/* Be sure CARD_IS_EMMC stays set */
|
||||||
|
sdhci_sparx5_set_emmc(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sdhci_ops sdhci_sparx5_ops = {
|
||||||
|
.set_clock = sdhci_set_clock,
|
||||||
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
|
.reset = sdhci_sparx5_reset,
|
||||||
|
.adma_write_desc = sdhci_sparx5_adma_write_desc,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_pltfm_data sdhci_sparx5_pdata = {
|
||||||
|
.quirks = 0,
|
||||||
|
.quirks2 = SDHCI_QUIRK2_HOST_NO_CMD23 | /* Controller issue */
|
||||||
|
SDHCI_QUIRK2_NO_1_8_V, /* No sdr104, ddr50, etc */
|
||||||
|
.ops = &sdhci_sparx5_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int sdhci_sparx5_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
const char *syscon = "microchip,sparx5-cpu-syscon";
|
||||||
|
struct sdhci_host *host;
|
||||||
|
struct sdhci_pltfm_host *pltfm_host;
|
||||||
|
struct sdhci_sparx5_data *sdhci_sparx5;
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
u32 value;
|
||||||
|
u32 extra;
|
||||||
|
|
||||||
|
host = sdhci_pltfm_init(pdev, &sdhci_sparx5_pdata,
|
||||||
|
sizeof(*sdhci_sparx5));
|
||||||
|
|
||||||
|
if (IS_ERR(host))
|
||||||
|
return PTR_ERR(host);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* extra adma table cnt for cross 128M boundary handling.
|
||||||
|
*/
|
||||||
|
extra = DIV_ROUND_UP_ULL(dma_get_required_mask(&pdev->dev), SZ_128M);
|
||||||
|
if (extra > SDHCI_MAX_SEGS)
|
||||||
|
extra = SDHCI_MAX_SEGS;
|
||||||
|
host->adma_table_cnt += extra;
|
||||||
|
|
||||||
|
pltfm_host = sdhci_priv(host);
|
||||||
|
sdhci_sparx5 = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
sdhci_sparx5->host = host;
|
||||||
|
|
||||||
|
pltfm_host->clk = devm_clk_get(&pdev->dev, "core");
|
||||||
|
if (IS_ERR(pltfm_host->clk)) {
|
||||||
|
ret = PTR_ERR(pltfm_host->clk);
|
||||||
|
dev_err(&pdev->dev, "failed to get core clk: %d\n", ret);
|
||||||
|
goto free_pltfm;
|
||||||
|
}
|
||||||
|
ret = clk_prepare_enable(pltfm_host->clk);
|
||||||
|
if (ret)
|
||||||
|
goto free_pltfm;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(np, "microchip,clock-delay", &value) &&
|
||||||
|
(value > 0 && value <= MSHC_DLY_CC_MAX))
|
||||||
|
sdhci_sparx5->delay_clock = value;
|
||||||
|
|
||||||
|
sdhci_get_of_property(pdev);
|
||||||
|
|
||||||
|
ret = mmc_of_parse(host->mmc);
|
||||||
|
if (ret)
|
||||||
|
goto err_clk;
|
||||||
|
|
||||||
|
sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon);
|
||||||
|
if (IS_ERR(sdhci_sparx5->cpu_ctrl)) {
|
||||||
|
dev_err(&pdev->dev, "No CPU syscon regmap !\n");
|
||||||
|
ret = PTR_ERR(sdhci_sparx5->cpu_ctrl);
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sdhci_sparx5->delay_clock >= 0)
|
||||||
|
sparx5_set_delay(host, sdhci_sparx5->delay_clock);
|
||||||
|
|
||||||
|
if (!mmc_card_is_removable(host->mmc)) {
|
||||||
|
/* Do a HW reset of eMMC card */
|
||||||
|
sdhci_sparx5_reset_emmc(host);
|
||||||
|
/* Update EMMC_CTRL */
|
||||||
|
sdhci_sparx5_set_emmc(host);
|
||||||
|
/* If eMMC, disable SD and SDIO */
|
||||||
|
host->mmc->caps2 |= (MMC_CAP2_NO_SDIO|MMC_CAP2_NO_SD);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sdhci_add_host(host);
|
||||||
|
if (ret)
|
||||||
|
goto err_clk;
|
||||||
|
|
||||||
|
/* Set AXI bus master to use un-cached access (for DMA) */
|
||||||
|
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) &&
|
||||||
|
IS_ENABLED(CONFIG_DMA_DECLARE_COHERENT))
|
||||||
|
sparx5_set_cacheable(host, ACP_CACHE_FORCE_ENA);
|
||||||
|
|
||||||
|
pr_debug("%s: SDHC version: 0x%08x\n",
|
||||||
|
mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_VERSION));
|
||||||
|
pr_debug("%s: SDHC type: 0x%08x\n",
|
||||||
|
mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err_clk:
|
||||||
|
clk_disable_unprepare(pltfm_host->clk);
|
||||||
|
free_pltfm:
|
||||||
|
sdhci_pltfm_free(pdev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id sdhci_sparx5_of_match[] = {
|
||||||
|
{ .compatible = "microchip,dw-sparx5-sdhci" },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sdhci_sparx5_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver sdhci_sparx5_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "sdhci-sparx5",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
|
.of_match_table = sdhci_sparx5_of_match,
|
||||||
|
.pm = &sdhci_pltfm_pmops,
|
||||||
|
},
|
||||||
|
.probe = sdhci_sparx5_probe,
|
||||||
|
.remove = sdhci_pltfm_unregister,
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(sdhci_sparx5_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Sparx5 SDHCI OF driver");
|
||||||
|
MODULE_AUTHOR("Lars Povlsen <lars.povlsen@microchip.com>");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -1297,6 +1297,7 @@ static struct platform_driver sdhci_omap_driver = {
|
||||||
.remove = sdhci_omap_remove,
|
.remove = sdhci_omap_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-omap",
|
.name = "sdhci-omap",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_omap_dev_pm_ops,
|
.pm = &sdhci_omap_dev_pm_ops,
|
||||||
.of_match_table = omap_sdhci_match,
|
.of_match_table = omap_sdhci_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pm_qos.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/mmc/sdhci-pci-data.h>
|
#include <linux/mmc/sdhci-pci-data.h>
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
|
@ -516,6 +518,8 @@ struct intel_host {
|
||||||
bool rpm_retune_ok;
|
bool rpm_retune_ok;
|
||||||
u32 glk_rx_ctrl1;
|
u32 glk_rx_ctrl1;
|
||||||
u32 glk_tun_val;
|
u32 glk_tun_val;
|
||||||
|
u32 active_ltr;
|
||||||
|
u32 idle_ltr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const guid_t intel_dsm_guid =
|
static const guid_t intel_dsm_guid =
|
||||||
|
@ -760,6 +764,108 @@ static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define INTEL_ACTIVELTR 0x804
|
||||||
|
#define INTEL_IDLELTR 0x808
|
||||||
|
|
||||||
|
#define INTEL_LTR_REQ BIT(15)
|
||||||
|
#define INTEL_LTR_SCALE_MASK GENMASK(11, 10)
|
||||||
|
#define INTEL_LTR_SCALE_1US (2 << 10)
|
||||||
|
#define INTEL_LTR_SCALE_32US (3 << 10)
|
||||||
|
#define INTEL_LTR_VALUE_MASK GENMASK(9, 0)
|
||||||
|
|
||||||
|
static void intel_cache_ltr(struct sdhci_pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct intel_host *intel_host = sdhci_pci_priv(slot);
|
||||||
|
struct sdhci_host *host = slot->host;
|
||||||
|
|
||||||
|
intel_host->active_ltr = readl(host->ioaddr + INTEL_ACTIVELTR);
|
||||||
|
intel_host->idle_ltr = readl(host->ioaddr + INTEL_IDLELTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intel_ltr_set(struct device *dev, s32 val)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_chip *chip = dev_get_drvdata(dev);
|
||||||
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
||||||
|
struct intel_host *intel_host = sdhci_pci_priv(slot);
|
||||||
|
struct sdhci_host *host = slot->host;
|
||||||
|
u32 ltr;
|
||||||
|
|
||||||
|
pm_runtime_get_sync(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Program latency tolerance (LTR) accordingly what has been asked
|
||||||
|
* by the PM QoS layer or disable it in case we were passed
|
||||||
|
* negative value or PM_QOS_LATENCY_ANY.
|
||||||
|
*/
|
||||||
|
ltr = readl(host->ioaddr + INTEL_ACTIVELTR);
|
||||||
|
|
||||||
|
if (val == PM_QOS_LATENCY_ANY || val < 0) {
|
||||||
|
ltr &= ~INTEL_LTR_REQ;
|
||||||
|
} else {
|
||||||
|
ltr |= INTEL_LTR_REQ;
|
||||||
|
ltr &= ~INTEL_LTR_SCALE_MASK;
|
||||||
|
ltr &= ~INTEL_LTR_VALUE_MASK;
|
||||||
|
|
||||||
|
if (val > INTEL_LTR_VALUE_MASK) {
|
||||||
|
val >>= 5;
|
||||||
|
if (val > INTEL_LTR_VALUE_MASK)
|
||||||
|
val = INTEL_LTR_VALUE_MASK;
|
||||||
|
ltr |= INTEL_LTR_SCALE_32US | val;
|
||||||
|
} else {
|
||||||
|
ltr |= INTEL_LTR_SCALE_1US | val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ltr == intel_host->active_ltr)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
writel(ltr, host->ioaddr + INTEL_ACTIVELTR);
|
||||||
|
writel(ltr, host->ioaddr + INTEL_IDLELTR);
|
||||||
|
|
||||||
|
/* Cache the values into lpss structure */
|
||||||
|
intel_cache_ltr(slot);
|
||||||
|
out:
|
||||||
|
pm_runtime_put_autosuspend(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool intel_use_ltr(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
switch (chip->pdev->device) {
|
||||||
|
case PCI_DEVICE_ID_INTEL_BYT_EMMC:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BYT_EMMC2:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BYT_SDIO:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BYT_SD:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BSW_EMMC:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BSW_SDIO:
|
||||||
|
case PCI_DEVICE_ID_INTEL_BSW_SD:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intel_ltr_expose(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
struct device *dev = &chip->pdev->dev;
|
||||||
|
|
||||||
|
if (!intel_use_ltr(chip))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev->power.set_latency_tolerance = intel_ltr_set;
|
||||||
|
dev_pm_qos_expose_latency_tolerance(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void intel_ltr_hide(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
struct device *dev = &chip->pdev->dev;
|
||||||
|
|
||||||
|
if (!intel_use_ltr(chip))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dev_pm_qos_hide_latency_tolerance(dev);
|
||||||
|
dev->power.set_latency_tolerance = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
|
struct mmc_host_ops *ops = &slot->host->mmc_host_ops;
|
||||||
|
@ -774,6 +880,43 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
|
ops->start_signal_voltage_switch = intel_start_signal_voltage_switch;
|
||||||
|
|
||||||
device_property_read_u32(dev, "max-frequency", &mmc->f_max);
|
device_property_read_u32(dev, "max-frequency", &mmc->f_max);
|
||||||
|
|
||||||
|
if (!mmc->slotno) {
|
||||||
|
slot->chip->slots[mmc->slotno] = slot;
|
||||||
|
intel_ltr_expose(slot->chip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void byt_add_debugfs(struct sdhci_pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct intel_host *intel_host = sdhci_pci_priv(slot);
|
||||||
|
struct mmc_host *mmc = slot->host->mmc;
|
||||||
|
struct dentry *dir = mmc->debugfs_root;
|
||||||
|
|
||||||
|
if (!intel_use_ltr(slot->chip))
|
||||||
|
return;
|
||||||
|
|
||||||
|
debugfs_create_x32("active_ltr", 0444, dir, &intel_host->active_ltr);
|
||||||
|
debugfs_create_x32("idle_ltr", 0444, dir, &intel_host->idle_ltr);
|
||||||
|
|
||||||
|
intel_cache_ltr(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int byt_add_host(struct sdhci_pci_slot *slot)
|
||||||
|
{
|
||||||
|
int ret = sdhci_add_host(slot->host);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
byt_add_debugfs(slot);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void byt_remove_slot(struct sdhci_pci_slot *slot, int dead)
|
||||||
|
{
|
||||||
|
struct mmc_host *mmc = slot->host->mmc;
|
||||||
|
|
||||||
|
if (!mmc->slotno)
|
||||||
|
intel_ltr_hide(slot->chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
|
@ -855,6 +998,8 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
byt_add_debugfs(slot);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
@ -1032,6 +1177,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
|
||||||
#endif
|
#endif
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = byt_emmc_probe_slot,
|
.probe_slot = byt_emmc_probe_slot,
|
||||||
|
.add_host = byt_add_host,
|
||||||
|
.remove_slot = byt_remove_slot,
|
||||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||||
SDHCI_QUIRK_NO_LED,
|
SDHCI_QUIRK_NO_LED,
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
|
@ -1045,6 +1192,7 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = {
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = glk_emmc_probe_slot,
|
.probe_slot = glk_emmc_probe_slot,
|
||||||
.add_host = glk_emmc_add_host,
|
.add_host = glk_emmc_add_host,
|
||||||
|
.remove_slot = byt_remove_slot,
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
.suspend = sdhci_cqhci_suspend,
|
.suspend = sdhci_cqhci_suspend,
|
||||||
.resume = sdhci_cqhci_resume,
|
.resume = sdhci_cqhci_resume,
|
||||||
|
@ -1075,6 +1223,8 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = {
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = ni_byt_sdio_probe_slot,
|
.probe_slot = ni_byt_sdio_probe_slot,
|
||||||
|
.add_host = byt_add_host,
|
||||||
|
.remove_slot = byt_remove_slot,
|
||||||
.ops = &sdhci_intel_byt_ops,
|
.ops = &sdhci_intel_byt_ops,
|
||||||
.priv_size = sizeof(struct intel_host),
|
.priv_size = sizeof(struct intel_host),
|
||||||
};
|
};
|
||||||
|
@ -1092,6 +1242,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
|
||||||
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.probe_slot = byt_sdio_probe_slot,
|
.probe_slot = byt_sdio_probe_slot,
|
||||||
|
.add_host = byt_add_host,
|
||||||
|
.remove_slot = byt_remove_slot,
|
||||||
.ops = &sdhci_intel_byt_ops,
|
.ops = &sdhci_intel_byt_ops,
|
||||||
.priv_size = sizeof(struct intel_host),
|
.priv_size = sizeof(struct intel_host),
|
||||||
};
|
};
|
||||||
|
@ -1111,6 +1263,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
|
||||||
.allow_runtime_pm = true,
|
.allow_runtime_pm = true,
|
||||||
.own_cd_for_runtime_pm = true,
|
.own_cd_for_runtime_pm = true,
|
||||||
.probe_slot = byt_sd_probe_slot,
|
.probe_slot = byt_sd_probe_slot,
|
||||||
|
.add_host = byt_add_host,
|
||||||
|
.remove_slot = byt_remove_slot,
|
||||||
.ops = &sdhci_intel_byt_ops,
|
.ops = &sdhci_intel_byt_ops,
|
||||||
.priv_size = sizeof(struct intel_host),
|
.priv_size = sizeof(struct intel_host),
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include "sdhci.h"
|
#include "sdhci.h"
|
||||||
#include "sdhci-pci.h"
|
#include "sdhci-pci.h"
|
||||||
|
#include "cqhci.h"
|
||||||
|
|
||||||
/* Genesys Logic extra registers */
|
/* Genesys Logic extra registers */
|
||||||
#define SDHCI_GLI_9750_WT 0x800
|
#define SDHCI_GLI_9750_WT 0x800
|
||||||
|
@ -81,9 +82,16 @@
|
||||||
#define GLI_9763E_VHS_REV_R 0x0
|
#define GLI_9763E_VHS_REV_R 0x0
|
||||||
#define GLI_9763E_VHS_REV_M 0x1
|
#define GLI_9763E_VHS_REV_M 0x1
|
||||||
#define GLI_9763E_VHS_REV_W 0x2
|
#define GLI_9763E_VHS_REV_W 0x2
|
||||||
|
#define PCIE_GLI_9763E_MB 0x888
|
||||||
|
#define GLI_9763E_MB_CMDQ_OFF BIT(19)
|
||||||
#define PCIE_GLI_9763E_SCR 0x8E0
|
#define PCIE_GLI_9763E_SCR 0x8E0
|
||||||
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
||||||
|
|
||||||
|
#define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200
|
||||||
|
#define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \
|
||||||
|
SDHCI_TRNS_BLK_CNT_EN | \
|
||||||
|
SDHCI_TRNS_DMA)
|
||||||
|
|
||||||
#define PCI_GLI_9755_WT 0x800
|
#define PCI_GLI_9755_WT 0x800
|
||||||
#define PCI_GLI_9755_WT_EN BIT(0)
|
#define PCI_GLI_9755_WT_EN BIT(0)
|
||||||
#define GLI_9755_WT_EN_ON 0x1
|
#define GLI_9755_WT_EN_ON 0x1
|
||||||
|
@ -578,6 +586,30 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
|
||||||
|
|
||||||
return sdhci_pci_resume_host(chip);
|
return sdhci_pci_resume_host(chip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_cqhci_gli_resume(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sdhci_pci_gli_resume(chip);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return cqhci_resume(slot->host->mmc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_cqhci_gli_suspend(struct sdhci_pci_chip *chip)
|
||||||
|
{
|
||||||
|
struct sdhci_pci_slot *slot = chip->slots[0];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = cqhci_suspend(slot->host->mmc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return sdhci_suspend_host(slot->host);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
|
static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||||
|
@ -614,6 +646,110 @@ static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
|
||||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_gl9763e_dumpregs(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
sdhci_dumpregs(mmc_priv(mmc));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_gl9763e_cqe_pre_enable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
value = cqhci_readl(cq_host, CQHCI_CFG);
|
||||||
|
value |= CQHCI_ENABLE;
|
||||||
|
cqhci_writel(cq_host, value, CQHCI_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_gl9763e_cqe_enable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
|
||||||
|
sdhci_writew(host, GLI_9763E_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
|
||||||
|
sdhci_cqe_enable(mmc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 sdhci_gl9763e_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||||
|
{
|
||||||
|
int cmd_error = 0;
|
||||||
|
int data_error = 0;
|
||||||
|
|
||||||
|
if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
|
||||||
|
return intmask;
|
||||||
|
|
||||||
|
cqhci_irq(host->mmc, intmask, cmd_error, data_error);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_gl9763e_cqe_post_disable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct cqhci_host *cq_host = mmc->cqe_private;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
value = cqhci_readl(cq_host, CQHCI_CFG);
|
||||||
|
value &= ~CQHCI_ENABLE;
|
||||||
|
cqhci_writel(cq_host, value, CQHCI_CFG);
|
||||||
|
sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct cqhci_host_ops sdhci_gl9763e_cqhci_ops = {
|
||||||
|
.enable = sdhci_gl9763e_cqe_enable,
|
||||||
|
.disable = sdhci_cqe_disable,
|
||||||
|
.dumpregs = sdhci_gl9763e_dumpregs,
|
||||||
|
.pre_enable = sdhci_gl9763e_cqe_pre_enable,
|
||||||
|
.post_disable = sdhci_gl9763e_cqe_post_disable,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gl9763e_add_host(struct sdhci_pci_slot *slot)
|
||||||
|
{
|
||||||
|
struct device *dev = &slot->chip->pdev->dev;
|
||||||
|
struct sdhci_host *host = slot->host;
|
||||||
|
struct cqhci_host *cq_host;
|
||||||
|
bool dma64;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sdhci_setup_host(host);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
cq_host = devm_kzalloc(dev, sizeof(*cq_host), GFP_KERNEL);
|
||||||
|
if (!cq_host) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
cq_host->mmio = host->ioaddr + SDHCI_GLI_9763E_CQE_BASE_ADDR;
|
||||||
|
cq_host->ops = &sdhci_gl9763e_cqhci_ops;
|
||||||
|
|
||||||
|
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||||
|
if (dma64)
|
||||||
|
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||||
|
|
||||||
|
ret = cqhci_init(cq_host, host->mmc, dma64);
|
||||||
|
if (ret)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
ret = __sdhci_add_host(host);
|
||||||
|
if (ret)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
sdhci_cleanup_host(host);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_gl9763e_reset(struct sdhci_host *host, u8 mask)
|
||||||
|
{
|
||||||
|
if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) &&
|
||||||
|
host->mmc->cqe_private)
|
||||||
|
cqhci_deactivate(host->mmc);
|
||||||
|
sdhci_reset(host, mask);
|
||||||
|
}
|
||||||
|
|
||||||
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = slot->chip->pdev;
|
struct pci_dev *pdev = slot->chip->pdev;
|
||||||
|
@ -636,7 +772,9 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||||
|
|
||||||
static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
|
struct pci_dev *pdev = slot->chip->pdev;
|
||||||
struct sdhci_host *host = slot->host;
|
struct sdhci_host *host = slot->host;
|
||||||
|
u32 value;
|
||||||
|
|
||||||
host->mmc->caps |= MMC_CAP_8_BIT_DATA |
|
host->mmc->caps |= MMC_CAP_8_BIT_DATA |
|
||||||
MMC_CAP_1_8V_DDR |
|
MMC_CAP_1_8V_DDR |
|
||||||
|
@ -646,6 +784,11 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
||||||
MMC_CAP2_HS400_ES |
|
MMC_CAP2_HS400_ES |
|
||||||
MMC_CAP2_NO_SDIO |
|
MMC_CAP2_NO_SDIO |
|
||||||
MMC_CAP2_NO_SD;
|
MMC_CAP2_NO_SD;
|
||||||
|
|
||||||
|
pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value);
|
||||||
|
if (!(value & GLI_9763E_MB_CMDQ_OFF))
|
||||||
|
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||||
|
|
||||||
gli_pcie_enable_msi(slot);
|
gli_pcie_enable_msi(slot);
|
||||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||||
gl9763e_hs400_enhanced_strobe;
|
gl9763e_hs400_enhanced_strobe;
|
||||||
|
@ -699,9 +842,10 @@ static const struct sdhci_ops sdhci_gl9763e_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.enable_dma = sdhci_pci_enable_dma,
|
.enable_dma = sdhci_pci_enable_dma,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
.reset = sdhci_reset,
|
.reset = sdhci_gl9763e_reset,
|
||||||
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
|
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
|
||||||
.voltage_switch = sdhci_gli_voltage_switch,
|
.voltage_switch = sdhci_gli_voltage_switch,
|
||||||
|
.irq = sdhci_gl9763e_cqhci_irq,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct sdhci_pci_fixes sdhci_gl9763e = {
|
const struct sdhci_pci_fixes sdhci_gl9763e = {
|
||||||
|
@ -709,6 +853,8 @@ const struct sdhci_pci_fixes sdhci_gl9763e = {
|
||||||
.probe_slot = gli_probe_slot_gl9763e,
|
.probe_slot = gli_probe_slot_gl9763e,
|
||||||
.ops = &sdhci_gl9763e_ops,
|
.ops = &sdhci_gl9763e_ops,
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
.resume = sdhci_pci_gli_resume,
|
.resume = sdhci_cqhci_gli_resume,
|
||||||
|
.suspend = sdhci_cqhci_gli_suspend,
|
||||||
#endif
|
#endif
|
||||||
|
.add_host = gl9763e_add_host,
|
||||||
};
|
};
|
||||||
|
|
|
@ -241,6 +241,7 @@ MODULE_DEVICE_TABLE(of, pic32_sdhci_id_table);
|
||||||
static struct platform_driver pic32_sdhci_driver = {
|
static struct platform_driver pic32_sdhci_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "pic32-sdhci",
|
.name = "pic32-sdhci",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(pic32_sdhci_id_table),
|
.of_match_table = of_match_ptr(pic32_sdhci_id_table),
|
||||||
},
|
},
|
||||||
.probe = pic32_sdhci_probe,
|
.probe = pic32_sdhci_probe,
|
||||||
|
|
|
@ -226,6 +226,7 @@ free:
|
||||||
static struct platform_driver sdhci_pxav2_driver = {
|
static struct platform_driver sdhci_pxav2_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav2",
|
.name = "sdhci-pxav2",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
.of_match_table = of_match_ptr(sdhci_pxav2_of_match),
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -567,6 +567,7 @@ static const struct dev_pm_ops sdhci_pxav3_pmops = {
|
||||||
static struct platform_driver sdhci_pxav3_driver = {
|
static struct platform_driver sdhci_pxav3_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-pxav3",
|
.name = "sdhci-pxav3",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
.of_match_table = of_match_ptr(sdhci_pxav3_of_match),
|
||||||
.pm = &sdhci_pxav3_pmops,
|
.pm = &sdhci_pxav3_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -461,7 +461,9 @@ static int sdhci_s3c_parse_dt(struct device *dev,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id sdhci_s3c_dt_match[];
|
static const struct of_device_id sdhci_s3c_dt_match[];
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
|
static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data(
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
|
@ -784,6 +786,7 @@ static struct platform_driver sdhci_s3c_driver = {
|
||||||
.id_table = sdhci_s3c_driver_ids,
|
.id_table = sdhci_s3c_driver_ids,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "s3c-sdhci",
|
.name = "s3c-sdhci",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
|
.of_match_table = of_match_ptr(sdhci_s3c_dt_match),
|
||||||
.pm = &sdhci_s3c_pmops,
|
.pm = &sdhci_s3c_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -220,6 +220,7 @@ MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
|
||||||
static struct platform_driver sdhci_sirf_driver = {
|
static struct platform_driver sdhci_sirf_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-sirf",
|
.name = "sdhci-sirf",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_sirf_of_match,
|
.of_match_table = sdhci_sirf_of_match,
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -181,6 +181,7 @@ MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
|
||||||
static struct platform_driver sdhci_driver = {
|
static struct platform_driver sdhci_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci",
|
.name = "sdhci",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_pm_ops,
|
.pm = &sdhci_pm_ops,
|
||||||
.of_match_table = of_match_ptr(sdhci_spear_id_table),
|
.of_match_table = of_match_ptr(sdhci_spear_id_table),
|
||||||
},
|
},
|
||||||
|
|
|
@ -387,7 +387,7 @@ static void sdhci_sprd_request_done(struct sdhci_host *host,
|
||||||
if (mmc_hsq_finalize_request(host->mmc, mrq))
|
if (mmc_hsq_finalize_request(host->mmc, mrq))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_sprd_ops = {
|
static struct sdhci_ops sdhci_sprd_ops = {
|
||||||
|
@ -433,7 +433,7 @@ static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_sprd_request_atomic(struct mmc_host *mmc,
|
static int sdhci_sprd_request_atomic(struct mmc_host *mmc,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
sdhci_sprd_check_auto_cmd23(mmc, mrq);
|
sdhci_sprd_check_auto_cmd23(mmc, mrq);
|
||||||
|
|
||||||
|
@ -787,6 +787,7 @@ static struct platform_driver sdhci_sprd_driver = {
|
||||||
.remove = sdhci_sprd_remove,
|
.remove = sdhci_sprd_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci_sprd_r11",
|
.name = "sdhci_sprd_r11",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(sdhci_sprd_of_match),
|
.of_match_table = of_match_ptr(sdhci_sprd_of_match),
|
||||||
.pm = &sdhci_sprd_pm_ops,
|
.pm = &sdhci_sprd_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -521,6 +521,7 @@ static struct platform_driver sdhci_st_driver = {
|
||||||
.remove = sdhci_st_remove,
|
.remove = sdhci_st_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-st",
|
.name = "sdhci-st",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sdhci_st_pmops,
|
.pm = &sdhci_st_pmops,
|
||||||
.of_match_table = of_match_ptr(st_sdhci_match),
|
.of_match_table = of_match_ptr(st_sdhci_match),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1660,11 +1660,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
clk = devm_clk_get(mmc_dev(host->mmc), NULL);
|
clk = devm_clk_get(mmc_dev(host->mmc), NULL);
|
||||||
if (IS_ERR(clk)) {
|
if (IS_ERR(clk)) {
|
||||||
rc = PTR_ERR(clk);
|
rc = dev_err_probe(&pdev->dev, PTR_ERR(clk),
|
||||||
|
"failed to get clock\n");
|
||||||
if (rc != -EPROBE_DEFER)
|
|
||||||
dev_err(&pdev->dev, "failed to get clock: %d\n", rc);
|
|
||||||
|
|
||||||
goto err_clk_get;
|
goto err_clk_get;
|
||||||
}
|
}
|
||||||
clk_prepare_enable(clk);
|
clk_prepare_enable(clk);
|
||||||
|
@ -1785,6 +1782,7 @@ static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
|
||||||
static struct platform_driver sdhci_tegra_driver = {
|
static struct platform_driver sdhci_tegra_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-tegra",
|
.name = "sdhci-tegra",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_tegra_dt_match,
|
.of_match_table = sdhci_tegra_dt_match,
|
||||||
.pm = &sdhci_tegra_dev_pm_ops,
|
.pm = &sdhci_tegra_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -677,6 +677,7 @@ MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
|
||||||
static struct platform_driver sdhci_xenon_driver = {
|
static struct platform_driver sdhci_xenon_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "xenon-sdhci",
|
.name = "xenon-sdhci",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_xenon_dt_ids,
|
.of_match_table = sdhci_xenon_dt_ids,
|
||||||
.pm = &sdhci_xenon_dev_pm_ops,
|
.pm = &sdhci_xenon_dev_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,10 +2,11 @@
|
||||||
/*
|
/*
|
||||||
* sdhci_am654.c - SDHCI driver for TI's AM654 SOCs
|
* sdhci_am654.c - SDHCI driver for TI's AM654 SOCs
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
|
* Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
@ -18,9 +19,11 @@
|
||||||
|
|
||||||
/* CTL_CFG Registers */
|
/* CTL_CFG Registers */
|
||||||
#define CTL_CFG_2 0x14
|
#define CTL_CFG_2 0x14
|
||||||
|
#define CTL_CFG_3 0x18
|
||||||
|
|
||||||
#define SLOTTYPE_MASK GENMASK(31, 30)
|
#define SLOTTYPE_MASK GENMASK(31, 30)
|
||||||
#define SLOTTYPE_EMBEDDED BIT(30)
|
#define SLOTTYPE_EMBEDDED BIT(30)
|
||||||
|
#define TUNINGFORSDR50_MASK BIT(13)
|
||||||
|
|
||||||
/* PHY Registers */
|
/* PHY Registers */
|
||||||
#define PHY_CTRL1 0x100
|
#define PHY_CTRL1 0x100
|
||||||
|
@ -65,6 +68,14 @@
|
||||||
#define RETRIM_MASK BIT(RETRIM_SHIFT)
|
#define RETRIM_MASK BIT(RETRIM_SHIFT)
|
||||||
#define SELDLYTXCLK_SHIFT 17
|
#define SELDLYTXCLK_SHIFT 17
|
||||||
#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
|
#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
|
||||||
|
#define SELDLYRXCLK_SHIFT 16
|
||||||
|
#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT)
|
||||||
|
#define ITAPDLYSEL_SHIFT 0
|
||||||
|
#define ITAPDLYSEL_MASK GENMASK(4, 0)
|
||||||
|
#define ITAPDLYENA_SHIFT 8
|
||||||
|
#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT)
|
||||||
|
#define ITAPCHGWIN_SHIFT 9
|
||||||
|
#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT)
|
||||||
|
|
||||||
#define DRIVER_STRENGTH_50_OHM 0x0
|
#define DRIVER_STRENGTH_50_OHM 0x0
|
||||||
#define DRIVER_STRENGTH_33_OHM 0x1
|
#define DRIVER_STRENGTH_33_OHM 0x1
|
||||||
|
@ -72,7 +83,7 @@
|
||||||
#define DRIVER_STRENGTH_100_OHM 0x3
|
#define DRIVER_STRENGTH_100_OHM 0x3
|
||||||
#define DRIVER_STRENGTH_40_OHM 0x4
|
#define DRIVER_STRENGTH_40_OHM 0x4
|
||||||
|
|
||||||
#define CLOCK_TOO_SLOW_HZ 400000
|
#define CLOCK_TOO_SLOW_HZ 50000000
|
||||||
|
|
||||||
/* Command Queue Host Controller Interface Base address */
|
/* Command Queue Host Controller Interface Base address */
|
||||||
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
|
#define SDHCI_AM654_CQE_BASE_ADDR 0x200
|
||||||
|
@ -84,14 +95,56 @@ static struct regmap_config sdhci_am654_regmap_config = {
|
||||||
.fast_io = true,
|
.fast_io = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct timing_data {
|
||||||
|
const char *otap_binding;
|
||||||
|
const char *itap_binding;
|
||||||
|
u32 capability;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct timing_data td[] = {
|
||||||
|
[MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy",
|
||||||
|
"ti,itap-del-sel-legacy",
|
||||||
|
0},
|
||||||
|
[MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs",
|
||||||
|
"ti,itap-del-sel-mmc-hs",
|
||||||
|
MMC_CAP_MMC_HIGHSPEED},
|
||||||
|
[MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs",
|
||||||
|
"ti,itap-del-sel-sd-hs",
|
||||||
|
MMC_CAP_SD_HIGHSPEED},
|
||||||
|
[MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12",
|
||||||
|
"ti,itap-del-sel-sdr12",
|
||||||
|
MMC_CAP_UHS_SDR12},
|
||||||
|
[MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25",
|
||||||
|
"ti,itap-del-sel-sdr25",
|
||||||
|
MMC_CAP_UHS_SDR25},
|
||||||
|
[MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50",
|
||||||
|
NULL,
|
||||||
|
MMC_CAP_UHS_SDR50},
|
||||||
|
[MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104",
|
||||||
|
NULL,
|
||||||
|
MMC_CAP_UHS_SDR104},
|
||||||
|
[MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50",
|
||||||
|
NULL,
|
||||||
|
MMC_CAP_UHS_DDR50},
|
||||||
|
[MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52",
|
||||||
|
"ti,itap-del-sel-ddr52",
|
||||||
|
MMC_CAP_DDR},
|
||||||
|
[MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200",
|
||||||
|
NULL,
|
||||||
|
MMC_CAP2_HS200},
|
||||||
|
[MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400",
|
||||||
|
NULL,
|
||||||
|
MMC_CAP2_HS400},
|
||||||
|
};
|
||||||
|
|
||||||
struct sdhci_am654_data {
|
struct sdhci_am654_data {
|
||||||
struct regmap *base;
|
struct regmap *base;
|
||||||
bool legacy_otapdly;
|
bool legacy_otapdly;
|
||||||
int otap_del_sel[11];
|
int otap_del_sel[ARRAY_SIZE(td)];
|
||||||
|
int itap_del_sel[ARRAY_SIZE(td)];
|
||||||
int clkbuf_sel;
|
int clkbuf_sel;
|
||||||
int trm_icp;
|
int trm_icp;
|
||||||
int drv_strength;
|
int drv_strength;
|
||||||
bool dll_on;
|
|
||||||
int strb_sel;
|
int strb_sel;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
};
|
};
|
||||||
|
@ -106,26 +159,6 @@ struct sdhci_am654_driver_data {
|
||||||
#define DLL_CALIB (1 << 4)
|
#define DLL_CALIB (1 << 4)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct timing_data {
|
|
||||||
const char *binding;
|
|
||||||
u32 capability;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct timing_data td[] = {
|
|
||||||
[MMC_TIMING_LEGACY] = {"ti,otap-del-sel-legacy", 0},
|
|
||||||
[MMC_TIMING_MMC_HS] = {"ti,otap-del-sel-mmc-hs", MMC_CAP_MMC_HIGHSPEED},
|
|
||||||
[MMC_TIMING_SD_HS] = {"ti,otap-del-sel-sd-hs", MMC_CAP_SD_HIGHSPEED},
|
|
||||||
[MMC_TIMING_UHS_SDR12] = {"ti,otap-del-sel-sdr12", MMC_CAP_UHS_SDR12},
|
|
||||||
[MMC_TIMING_UHS_SDR25] = {"ti,otap-del-sel-sdr25", MMC_CAP_UHS_SDR25},
|
|
||||||
[MMC_TIMING_UHS_SDR50] = {"ti,otap-del-sel-sdr50", MMC_CAP_UHS_SDR50},
|
|
||||||
[MMC_TIMING_UHS_SDR104] = {"ti,otap-del-sel-sdr104",
|
|
||||||
MMC_CAP_UHS_SDR104},
|
|
||||||
[MMC_TIMING_UHS_DDR50] = {"ti,otap-del-sel-ddr50", MMC_CAP_UHS_DDR50},
|
|
||||||
[MMC_TIMING_MMC_DDR52] = {"ti,otap-del-sel-ddr52", MMC_CAP_DDR},
|
|
||||||
[MMC_TIMING_MMC_HS200] = {"ti,otap-del-sel-hs200", MMC_CAP2_HS200},
|
|
||||||
[MMC_TIMING_MMC_HS400] = {"ti,otap-del-sel-hs400", MMC_CAP2_HS400},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
|
static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
@ -134,6 +167,10 @@ static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
|
||||||
u32 mask, val;
|
u32 mask, val;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Disable delay chain mode */
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
|
||||||
|
SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
|
||||||
|
|
||||||
if (sdhci_am654->flags & FREQSEL_2_BIT) {
|
if (sdhci_am654->flags & FREQSEL_2_BIT) {
|
||||||
switch (clock) {
|
switch (clock) {
|
||||||
case 200000000:
|
case 200000000:
|
||||||
|
@ -188,8 +225,32 @@ static void sdhci_am654_setup_dll(struct sdhci_host *host, unsigned int clock)
|
||||||
dev_err(mmc_dev(host->mmc), "DLL failed to relock\n");
|
dev_err(mmc_dev(host->mmc), "DLL failed to relock\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sdhci_am654->dll_on = true;
|
static void sdhci_am654_write_itapdly(struct sdhci_am654_data *sdhci_am654,
|
||||||
|
u32 itapdly)
|
||||||
|
{
|
||||||
|
/* Set ITAPCHGWIN before writing to ITAPDLY */
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK,
|
||||||
|
1 << ITAPCHGWIN_SHIFT);
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYSEL_MASK,
|
||||||
|
itapdly << ITAPDLYSEL_SHIFT);
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_am654_setup_delay_chain(struct sdhci_am654_data *sdhci_am654,
|
||||||
|
unsigned char timing)
|
||||||
|
{
|
||||||
|
u32 mask, val;
|
||||||
|
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
|
||||||
|
|
||||||
|
val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
|
||||||
|
mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, mask, val);
|
||||||
|
|
||||||
|
sdhci_am654_write_itapdly(sdhci_am654,
|
||||||
|
sdhci_am654->itap_del_sel[timing]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
@ -201,11 +262,7 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
u32 otap_del_ena;
|
u32 otap_del_ena;
|
||||||
u32 mask, val;
|
u32 mask, val;
|
||||||
|
|
||||||
if (sdhci_am654->dll_on) {
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
|
||||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL1, ENDLL_MASK, 0);
|
|
||||||
|
|
||||||
sdhci_am654->dll_on = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sdhci_set_clock(host, clock);
|
sdhci_set_clock(host, clock);
|
||||||
|
|
||||||
|
@ -233,14 +290,10 @@ static void sdhci_am654_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
|
||||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, mask, val);
|
||||||
|
|
||||||
if (timing > MMC_TIMING_UHS_SDR25 && clock > CLOCK_TOO_SLOW_HZ) {
|
if (timing > MMC_TIMING_UHS_SDR25 && clock >= CLOCK_TOO_SLOW_HZ)
|
||||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
|
|
||||||
SELDLYTXCLK_MASK, 0);
|
|
||||||
sdhci_am654_setup_dll(host, clock);
|
sdhci_am654_setup_dll(host, clock);
|
||||||
} else {
|
else
|
||||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5,
|
sdhci_am654_setup_delay_chain(sdhci_am654, timing);
|
||||||
SELDLYTXCLK_MASK, 1 << SELDLYTXCLK_SHIFT);
|
|
||||||
}
|
|
||||||
|
|
||||||
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK,
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL5, CLKBUFSEL_MASK,
|
||||||
sdhci_am654->clkbuf_sel);
|
sdhci_am654->clkbuf_sel);
|
||||||
|
@ -272,9 +325,19 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host,
|
||||||
sdhci_set_clock(host, clock);
|
sdhci_set_clock(host, clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg)
|
||||||
|
{
|
||||||
|
writeb(val, host->ioaddr + reg);
|
||||||
|
usleep_range(1000, 10000);
|
||||||
|
return readb(host->ioaddr + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_POWER_ON_TIMEOUT 1500000 /* us */
|
||||||
static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg)
|
static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg)
|
||||||
{
|
{
|
||||||
unsigned char timing = host->mmc->ios.timing;
|
unsigned char timing = host->mmc->ios.timing;
|
||||||
|
u8 pwr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (reg == SDHCI_HOST_CONTROL) {
|
if (reg == SDHCI_HOST_CONTROL) {
|
||||||
switch (timing) {
|
switch (timing) {
|
||||||
|
@ -291,6 +354,19 @@ static void sdhci_am654_write_b(struct sdhci_host *host, u8 val, int reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
writeb(val, host->ioaddr + reg);
|
writeb(val, host->ioaddr + reg);
|
||||||
|
if (reg == SDHCI_POWER_CONTROL && (val & SDHCI_POWER_ON)) {
|
||||||
|
/*
|
||||||
|
* Power on will not happen until the card detect debounce
|
||||||
|
* timer expires. Wait at least 1.5 seconds for the power on
|
||||||
|
* bit to be set
|
||||||
|
*/
|
||||||
|
ret = read_poll_timeout(sdhci_am654_write_power_on, pwr,
|
||||||
|
pwr & SDHCI_POWER_ON, 0,
|
||||||
|
MAX_POWER_ON_TIMEOUT, false, host, val,
|
||||||
|
reg);
|
||||||
|
if (ret)
|
||||||
|
dev_warn(mmc_dev(host->mmc), "Power on failed\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
static int sdhci_am654_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
|
@ -322,7 +398,46 @@ static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ITAP_MAX 32
|
||||||
|
static int sdhci_am654_platform_execute_tuning(struct sdhci_host *host,
|
||||||
|
u32 opcode)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
int cur_val, prev_val = 1, fail_len = 0, pass_window = 0, pass_len;
|
||||||
|
u32 itap;
|
||||||
|
|
||||||
|
/* Enable ITAPDLY */
|
||||||
|
regmap_update_bits(sdhci_am654->base, PHY_CTRL4, ITAPDLYENA_MASK,
|
||||||
|
1 << ITAPDLYENA_SHIFT);
|
||||||
|
|
||||||
|
for (itap = 0; itap < ITAP_MAX; itap++) {
|
||||||
|
sdhci_am654_write_itapdly(sdhci_am654, itap);
|
||||||
|
|
||||||
|
cur_val = !mmc_send_tuning(host->mmc, opcode, NULL);
|
||||||
|
if (cur_val && !prev_val)
|
||||||
|
pass_window = itap;
|
||||||
|
|
||||||
|
if (!cur_val)
|
||||||
|
fail_len++;
|
||||||
|
|
||||||
|
prev_val = cur_val;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Having determined the length of the failing window and start of
|
||||||
|
* the passing window calculate the length of the passing window and
|
||||||
|
* set the final value halfway through it considering the range as a
|
||||||
|
* circular buffer
|
||||||
|
*/
|
||||||
|
pass_len = ITAP_MAX - fail_len;
|
||||||
|
itap = (pass_window + (pass_len >> 1)) % ITAP_MAX;
|
||||||
|
sdhci_am654_write_itapdly(sdhci_am654, itap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_am654_ops = {
|
static struct sdhci_ops sdhci_am654_ops = {
|
||||||
|
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
|
@ -352,6 +467,7 @@ static const struct sdhci_am654_driver_data sdhci_am654_drvdata = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_j721e_8bit_ops = {
|
static struct sdhci_ops sdhci_j721e_8bit_ops = {
|
||||||
|
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
|
@ -375,6 +491,7 @@ static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_j721e_4bit_ops = {
|
static struct sdhci_ops sdhci_j721e_4bit_ops = {
|
||||||
|
.platform_execute_tuning = sdhci_am654_platform_execute_tuning,
|
||||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
|
@ -445,7 +562,7 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
|
||||||
int i;
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].binding,
|
ret = device_property_read_u32(dev, td[MMC_TIMING_LEGACY].otap_binding,
|
||||||
&sdhci_am654->otap_del_sel[MMC_TIMING_LEGACY]);
|
&sdhci_am654->otap_del_sel[MMC_TIMING_LEGACY]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
/*
|
/*
|
||||||
|
@ -468,11 +585,11 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
|
||||||
|
|
||||||
for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {
|
for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||||
|
|
||||||
ret = device_property_read_u32(dev, td[i].binding,
|
ret = device_property_read_u32(dev, td[i].otap_binding,
|
||||||
&sdhci_am654->otap_del_sel[i]);
|
&sdhci_am654->otap_del_sel[i]);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_dbg(dev, "Couldn't find %s\n",
|
dev_dbg(dev, "Couldn't find %s\n",
|
||||||
td[i].binding);
|
td[i].otap_binding);
|
||||||
/*
|
/*
|
||||||
* Remove the corresponding capability
|
* Remove the corresponding capability
|
||||||
* if an otap-del-sel value is not found
|
* if an otap-del-sel value is not found
|
||||||
|
@ -482,6 +599,10 @@ static int sdhci_am654_get_otap_delay(struct sdhci_host *host,
|
||||||
else
|
else
|
||||||
host->mmc->caps2 &= ~td[i].capability;
|
host->mmc->caps2 &= ~td[i].capability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (td[i].itap_binding)
|
||||||
|
device_property_read_u32(dev, td[i].itap_binding,
|
||||||
|
&sdhci_am654->itap_del_sel[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -527,6 +648,10 @@ static int sdhci_am654_init(struct sdhci_host *host)
|
||||||
regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK,
|
regmap_update_bits(sdhci_am654->base, CTL_CFG_2, SLOTTYPE_MASK,
|
||||||
ctl_cfg_2);
|
ctl_cfg_2);
|
||||||
|
|
||||||
|
/* Enable tuning for SDR50 */
|
||||||
|
regmap_update_bits(sdhci_am654->base, CTL_CFG_3, TUNINGFORSDR50_MASK,
|
||||||
|
TUNINGFORSDR50_MASK);
|
||||||
|
|
||||||
ret = sdhci_setup_host(host);
|
ret = sdhci_setup_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -614,6 +739,7 @@ static const struct of_device_id sdhci_am654_of_match[] = {
|
||||||
},
|
},
|
||||||
{ /* sentinel */ }
|
{ /* sentinel */ }
|
||||||
};
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, sdhci_am654_of_match);
|
||||||
|
|
||||||
static int sdhci_am654_probe(struct platform_device *pdev)
|
static int sdhci_am654_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
@ -721,6 +847,7 @@ static int sdhci_am654_remove(struct platform_device *pdev)
|
||||||
static struct platform_driver sdhci_am654_driver = {
|
static struct platform_driver sdhci_am654_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-am654",
|
.name = "sdhci-am654",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = sdhci_am654_of_match,
|
.of_match_table = sdhci_am654_of_match,
|
||||||
},
|
},
|
||||||
.probe = sdhci_am654_probe,
|
.probe = sdhci_am654_probe,
|
||||||
|
|
|
@ -219,6 +219,7 @@ MODULE_DEVICE_TABLE(acpi, f_sdh30_acpi_ids);
|
||||||
static struct platform_driver sdhci_f_sdh30_driver = {
|
static struct platform_driver sdhci_f_sdh30_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "f_sdh30",
|
.name = "f_sdh30",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(f_sdh30_dt_ids),
|
.of_match_table = of_match_ptr(f_sdh30_dt_ids),
|
||||||
.acpi_match_table = ACPI_PTR(f_sdh30_acpi_ids),
|
.acpi_match_table = ACPI_PTR(f_sdh30_acpi_ids),
|
||||||
.pm = &sdhci_pltfm_pmops,
|
.pm = &sdhci_pltfm_pmops,
|
||||||
|
|
|
@ -1562,6 +1562,7 @@ static struct platform_driver sh_mmcif_driver = {
|
||||||
.remove = sh_mmcif_remove,
|
.remove = sh_mmcif_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = DRIVER_NAME,
|
.name = DRIVER_NAME,
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &sh_mmcif_dev_pm_ops,
|
.pm = &sh_mmcif_dev_pm_ops,
|
||||||
.of_match_table = sh_mmcif_of_match,
|
.of_match_table = sh_mmcif_of_match,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1514,6 +1514,7 @@ static const struct dev_pm_ops sunxi_mmc_pm_ops = {
|
||||||
static struct platform_driver sunxi_mmc_driver = {
|
static struct platform_driver sunxi_mmc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "sunxi-mmc",
|
.name = "sunxi-mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
|
.of_match_table = of_match_ptr(sunxi_mmc_of_match),
|
||||||
.pm = &sunxi_mmc_pm_ops,
|
.pm = &sunxi_mmc_pm_ops,
|
||||||
},
|
},
|
||||||
|
|
|
@ -77,18 +77,10 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
|
||||||
|
|
||||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||||
{
|
{
|
||||||
/* FIXME - should we set stop clock reg here */
|
|
||||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
|
||||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||||
usleep_range(10000, 11000);
|
usleep_range(10000, 11000);
|
||||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
|
||||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
|
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
|
||||||
usleep_range(10000, 11000);
|
usleep_range(10000, 11000);
|
||||||
|
|
||||||
if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) {
|
|
||||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
|
||||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
|
@ -221,6 +213,7 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
|
||||||
static struct platform_driver tmio_mmc_driver = {
|
static struct platform_driver tmio_mmc_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "tmio-mmc",
|
.name = "tmio-mmc",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &tmio_mmc_dev_pm_ops,
|
.pm = &tmio_mmc_dev_pm_ops,
|
||||||
},
|
},
|
||||||
.probe = tmio_mmc_probe,
|
.probe = tmio_mmc_probe,
|
||||||
|
|
|
@ -178,14 +178,8 @@ struct tmio_mmc_host {
|
||||||
unsigned int direction, int blk_size);
|
unsigned int direction, int blk_size);
|
||||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||||
void (*reset)(struct tmio_mmc_host *host);
|
void (*reset)(struct tmio_mmc_host *host);
|
||||||
void (*hw_reset)(struct tmio_mmc_host *host);
|
|
||||||
bool (*check_retune)(struct tmio_mmc_host *host);
|
bool (*check_retune)(struct tmio_mmc_host *host);
|
||||||
|
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||||
/*
|
|
||||||
* Mandatory callback for tuning to occur which is optional for SDR50
|
|
||||||
* and mandatory for SDR104.
|
|
||||||
*/
|
|
||||||
int (*execute_tuning)(struct tmio_mmc_host *host, u32 opcode);
|
|
||||||
|
|
||||||
void (*prepare_hs400_tuning)(struct tmio_mmc_host *host);
|
void (*prepare_hs400_tuning)(struct tmio_mmc_host *host);
|
||||||
void (*hs400_downgrade)(struct tmio_mmc_host *host);
|
void (*hs400_downgrade)(struct tmio_mmc_host *host);
|
||||||
|
|
|
@ -172,24 +172,15 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||||
usleep_range(10000, 11000);
|
usleep_range(10000, 11000);
|
||||||
|
|
||||||
|
if (host->reset)
|
||||||
|
host->reset(host);
|
||||||
|
|
||||||
if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) {
|
if (host->pdata->flags & TMIO_MMC_SDIO_IRQ) {
|
||||||
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
|
||||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tmio_mmc_hw_reset(struct mmc_host *mmc)
|
|
||||||
{
|
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
host->reset(host);
|
|
||||||
|
|
||||||
tmio_mmc_abort_dma(host);
|
|
||||||
|
|
||||||
if (host->hw_reset)
|
|
||||||
host->hw_reset(host);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tmio_mmc_reset_work(struct work_struct *work)
|
static void tmio_mmc_reset_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host,
|
struct tmio_mmc_host *host = container_of(work, struct tmio_mmc_host,
|
||||||
|
@ -228,11 +219,12 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->lock, flags);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
tmio_mmc_hw_reset(host->mmc);
|
tmio_mmc_reset(host);
|
||||||
|
|
||||||
/* Ready for new calls */
|
/* Ready for new calls */
|
||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
|
|
||||||
|
tmio_mmc_abort_dma(host);
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -720,24 +712,6 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|
||||||
{
|
|
||||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!host->execute_tuning)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ret = host->execute_tuning(host, opcode);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_warn(&host->pdev->dev, "Tuning procedure failed\n");
|
|
||||||
tmio_mmc_hw_reset(mmc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tmio_process_mrq(struct tmio_mmc_host *host,
|
static void tmio_process_mrq(struct tmio_mmc_host *host,
|
||||||
struct mmc_request *mrq)
|
struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
|
@ -835,6 +809,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host->fixup_request)
|
||||||
|
host->fixup_request(host, mrq);
|
||||||
|
|
||||||
mmc_request_done(host->mmc, mrq);
|
mmc_request_done(host->mmc, mrq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1011,8 +988,6 @@ static struct mmc_host_ops tmio_mmc_ops = {
|
||||||
.get_cd = tmio_mmc_get_cd,
|
.get_cd = tmio_mmc_get_cd,
|
||||||
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
|
||||||
.multi_io_quirk = tmio_multi_io_quirk,
|
.multi_io_quirk = tmio_multi_io_quirk,
|
||||||
.hw_reset = tmio_mmc_hw_reset,
|
|
||||||
.execute_tuning = tmio_mmc_execute_tuning,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
|
||||||
|
@ -1156,9 +1131,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
||||||
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
mmc->caps & MMC_CAP_NEEDS_POLL ||
|
||||||
!mmc_card_is_removable(mmc));
|
!mmc_card_is_removable(mmc));
|
||||||
|
|
||||||
if (!_host->reset)
|
|
||||||
_host->reset = tmio_mmc_reset;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* On Gen2+, eMMC with NONREMOVABLE currently fails because native
|
* On Gen2+, eMMC with NONREMOVABLE currently fails because native
|
||||||
* hotplug gets disabled. It seems RuntimePM related yet we need further
|
* hotplug gets disabled. It seems RuntimePM related yet we need further
|
||||||
|
@ -1180,7 +1152,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
||||||
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
|
||||||
|
|
||||||
_host->set_clock(_host, 0);
|
_host->set_clock(_host, 0);
|
||||||
tmio_mmc_hw_reset(mmc);
|
tmio_mmc_reset(_host);
|
||||||
|
|
||||||
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
|
_host->sdcard_irq_mask = sd_ctrl_read16_and_16_as_32(_host, CTL_IRQ_MASK);
|
||||||
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
tmio_mmc_disable_mmc_irqs(_host, TMIO_MASK_ALL);
|
||||||
|
@ -1283,7 +1255,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
||||||
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
tmio_mmc_clk_enable(host);
|
tmio_mmc_clk_enable(host);
|
||||||
tmio_mmc_hw_reset(host->mmc);
|
tmio_mmc_reset(host);
|
||||||
|
|
||||||
if (host->clk_cache)
|
if (host->clk_cache)
|
||||||
host->set_clock(host, host->clk_cache);
|
host->set_clock(host, host->clk_cache);
|
||||||
|
|
|
@ -409,8 +409,9 @@ static void uniphier_sd_clk_disable(struct tmio_mmc_host *host)
|
||||||
clk_disable_unprepare(priv->clk);
|
clk_disable_unprepare(priv->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uniphier_sd_hw_reset(struct tmio_mmc_host *host)
|
static void uniphier_sd_hw_reset(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
|
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
|
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
|
||||||
|
|
||||||
reset_control_assert(priv->rst_hw);
|
reset_control_assert(priv->rst_hw);
|
||||||
|
@ -597,7 +598,7 @@ static int uniphier_sd_probe(struct platform_device *pdev)
|
||||||
ret = PTR_ERR(priv->rst_hw);
|
ret = PTR_ERR(priv->rst_hw);
|
||||||
goto free_host;
|
goto free_host;
|
||||||
}
|
}
|
||||||
host->hw_reset = uniphier_sd_hw_reset;
|
host->ops.hw_reset = uniphier_sd_hw_reset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->mmc->caps & MMC_CAP_UHS) {
|
if (host->mmc->caps & MMC_CAP_UHS) {
|
||||||
|
@ -684,6 +685,7 @@ static struct platform_driver uniphier_sd_driver = {
|
||||||
.remove = uniphier_sd_remove,
|
.remove = uniphier_sd_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "uniphier-sd",
|
.name = "uniphier-sd",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = uniphier_sd_match,
|
.of_match_table = uniphier_sd_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1890,6 +1890,7 @@ static struct platform_driver usdhi6_driver = {
|
||||||
.remove = usdhi6_remove,
|
.remove = usdhi6_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "usdhi6rol0",
|
.name = "usdhi6rol0",
|
||||||
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.of_match_table = usdhi6_of_match,
|
.of_match_table = usdhi6_of_match,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1257,11 +1257,14 @@ static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host)
|
||||||
static int __maybe_unused via_sd_suspend(struct device *dev)
|
static int __maybe_unused via_sd_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct via_crdr_mmc_host *host;
|
struct via_crdr_mmc_host *host;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
host = dev_get_drvdata(dev);
|
host = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
via_save_pcictrlreg(host);
|
via_save_pcictrlreg(host);
|
||||||
via_save_sdcreg(host);
|
via_save_sdcreg(host);
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
device_wakeup_enable(dev);
|
device_wakeup_enable(dev);
|
||||||
|
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче