mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
MMC core:
- Convert to reasonable timeouts for all CMD6 commands (updates for BKOPS, CACHE_FLUSH and INAND_CMD38_ARG_EXT_CSD) for eMMC - Respect f_max clock rate at card initialization - Add gpiod_toggle_active_low() API - Consolidate slot-gpio code by using gpiod_toggle_active_low() MMC host: - Add pinctrl_select_default_state() API - Consolidate pintctrl code by using pinctrl_select_default_state() - mmci: Support any block sizes for SDIO for some variants - mmci: Enable reset control for stm32_sdmmc - mmc_spi: Toggle SPI_CS_HIGH polarity rather than hard-coding it - renesas_sdhi: Add support for the r8a77961 variant - renesas_sdhi: A few minor improvements - rockchip-dw-mshc: Add support for the rk3308 variant - sdhci: Enable support for external DMA controllers - sdhci: Fixup error path when sending CMD12 - sdhci-brcmstb: Add support for 7216b0 variant - sdhci-brcmstb: Add support for command queuing (CQHCI) - sdhci-brcmstb: Add support for eMMC HS400ES mode - sdhci-msm: Add support for the sc7180 variant - sdhci-msm: Add support for command queuing (CQHCI) - sdhci-of-at91: Add support for the SAM9x60 variant - sdhci-of-at91: Improve support for tunings - sdhci-of-esdhc: A few fixups for some clock related issues - sdhci-omap: Add support for the am335x and the am437x variants - sdhci-omap: Improve support for erase operations - sdhci-omap: Add support for external DMA -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAl4uww8XHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjClg8w/+Iv7Emp5oBmj0aY9BA2rWpOVj rNPynDbRPI0qrrQPqXrZ/UdGDc6lgWHoeHdtv70u8WK8UEgi/OYw7PlE3eij9DGB VcOnMyDg+Q6+Hq3vjvADWZzNrHGjdvjcGLxx386DHpTCsc1zD3EL2AG5e6i2/dfH /FiSdfxVtRKw4GEn5jIDhHh63Av8mMCaPPfLw0DKxMQIuJ3atMvplgsr1KXIAydo QTjKWxDJDJnKddtdHcnjP9Tck+ksrATDHHHQ9qGVFuOr3Yy9TgvOMlLo8vxJ364l 3bIwYfbaeE4/NKvqJGDzIF1jr4R5JoHZhvHLGqr+hUwMzxnwcbyCUSghk87oOqMt FKAfKqwdEvVyq8QnmV9ArPXRnVmW4ElJN3edTeC00/7MoxWIx2TRHvEO5HejqPkq +kw0NEIqWWiztV3/aa5zFJtlp8QphwsopvLzwEAJB+1G0tiSiNcfokrJ0AyODC5/ K4kXTGfQh28RSIr6ait7FNphyOug1WAvXqvt3ydY1lQPH0G36DMzLMx06mjgTA4O DNYXzLoLPzRaPU4NvpHu3/axgRLYCN3CbGdx3U8GT9wgmerRyY/jW9ldSF7w6Wim W0Zx1j3SdvmgafMXVsFcxMUC0IvfOFiMzXJ6DOus37xTYLRKW40wttg9SSxtm+9E 3IfRRlXnlkHVvTZ1ewI= =L0cJ -----END PGP SIGNATURE----- Merge tag 'mmc-v5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "There are no updates for the MEMSTICK subsystem this time. But note that I am also carrying a patch from the pinctrl tree, which has been shared through an immutable branch. Summary: MMC core: - Convert to reasonable timeouts for all CMD6 commands (updates for BKOPS, CACHE_FLUSH and INAND_CMD38_ARG_EXT_CSD) for eMMC - Respect f_max clock rate at card initialization - Add gpiod_toggle_active_low() API - Consolidate slot-gpio code by using gpiod_toggle_active_low() MMC host: - Add pinctrl_select_default_state() API - Consolidate pintctrl code by using pinctrl_select_default_state() - mmci: Support any block sizes for SDIO for some variants - mmci: Enable reset control for stm32_sdmmc - mmc_spi: Toggle SPI_CS_HIGH polarity rather than hard-coding it - renesas_sdhi: Add support for the r8a77961 variant - renesas_sdhi: A few minor improvements - rockchip-dw-mshc: Add support for the rk3308 variant - sdhci: Enable support for external DMA controllers - sdhci: Fixup error path when sending CMD12 - sdhci-brcmstb: Add support for 7216b0 variant - sdhci-brcmstb: Add support for command queuing (CQHCI) - sdhci-brcmstb: Add support for eMMC HS400ES mode - sdhci-msm: Add support for the sc7180 variant - sdhci-msm: Add support for command queuing (CQHCI) - sdhci-of-at91: Add support for the SAM9x60 variant - sdhci-of-at91: Improve support for tunings - sdhci-of-esdhc: A few fixups for some clock related issues - sdhci-omap: Add support for the am335x and the am437x variants - sdhci-omap: Improve support for erase operations - sdhci-omap: Add support for external DMA" * tag 'mmc-v5.6' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (89 commits) mmc: core: Default to generic_cmd6_time as timeout in __mmc_switch() mmc: block: Use generic_cmd6_time when modifying INAND_CMD38_ARG_EXT_CSD mmc: core: Specify timeouts for BKOPS and CACHE_FLUSH for eMMC mmc: sdhci-cadence: remove unneeded 'inline' marker dt-bindings: mmc: rockchip-dw-mshc: add description for rk3308 dt-bindings: mmc: convert rockchip dw-mshc bindings to yaml dt-bindings: mmc: convert synopsys dw-mshc bindings to yaml mmc: sdhci-msm: Add CQHCI support for sdhci-msm mmc: sdhci: Let a vendor driver supply and update ADMA descriptor size mmc: sdhci-of-esdhc: fix serious issue clock is always disabled mmc: sdhci-of-esdhc: fix transfer mode register reading mmc: sdhci-brcmstb: Fix incorrect switch to HS mode mmc: sdhci-brcmstb: Add support for Command Queuing (CQE) mmc: sdhci-brcmstb: Add shutdown callback mmc: sdhci-brcmstb: Fix driver to defer on clk_get defer mmc: sdhci-brcmstb: Add ability to use HS400ES transfer mode dt-bindings: mmc: brcm,sdhci-brcmstb: Add support for 7216b0 mmc: core: limit probe clock frequency to configured f_max mmc: sdhci-milbeaut: Remove redundant platform_get_irq error message mmc: sdhci: fix an issue of mixing different types ...
This commit is contained in:
commit
9e1af7567b
70 changed files with 1649 additions and 763 deletions
|
@ -11,28 +11,43 @@ Required properties:
|
|||
- compatible: should be one of the following
|
||||
- "brcm,bcm7425-sdhci"
|
||||
- "brcm,bcm7445-sdhci"
|
||||
- "brcm,bcm7216-sdhci"
|
||||
|
||||
Refer to clocks/clock-bindings.txt for generic clock consumer properties.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci@f03e0100 {
|
||||
compatible = "brcm,bcm7425-sdhci";
|
||||
reg = <0xf03e0000 0x100>;
|
||||
interrupts = <0x0 0x26 0x0>;
|
||||
sdhci,auto-cmd12;
|
||||
clocks = <&sw_sdio>;
|
||||
sdhci@84b0000 {
|
||||
sd-uhs-sdr50;
|
||||
sd-uhs-ddr50;
|
||||
sd-uhs-sdr104;
|
||||
sdhci,auto-cmd12;
|
||||
compatible = "brcm,bcm7216-sdhci",
|
||||
"brcm,bcm7445-sdhci",
|
||||
"brcm,sdhci-brcmstb";
|
||||
reg = <0x84b0000 0x260 0x84b0300 0x200>;
|
||||
reg-names = "host", "cfg";
|
||||
interrupts = <0x0 0x26 0x4>;
|
||||
interrupt-names = "sdio0_0";
|
||||
clocks = <&scmi_clk 245>;
|
||||
clock-names = "sw_sdio";
|
||||
};
|
||||
|
||||
sdhci@f03e0300 {
|
||||
sdhci@84b1000 {
|
||||
mmc-ddr-1_8v;
|
||||
mmc-hs200-1_8v;
|
||||
mmc-hs400-1_8v;
|
||||
mmc-hs400-enhanced-strobe;
|
||||
supports-cqe;
|
||||
non-removable;
|
||||
bus-width = <0x8>;
|
||||
compatible = "brcm,bcm7425-sdhci";
|
||||
reg = <0xf03e0200 0x100>;
|
||||
interrupts = <0x0 0x27 0x0>;
|
||||
sdhci,auto-cmd12;
|
||||
clocks = <sw_sdio>;
|
||||
mmc-hs200-1_8v;
|
||||
compatible = "brcm,bcm7216-sdhci",
|
||||
"brcm,bcm7445-sdhci",
|
||||
"brcm,sdhci-brcmstb";
|
||||
reg = <0x84b1000 0x260 0x84b1300 0x200>;
|
||||
reg-names = "host", "cfg";
|
||||
interrupts = <0x0 0x27 0x4>;
|
||||
interrupt-names = "sdio1_0";
|
||||
clocks = <&scmi_clk 245>;
|
||||
clock-names = "sw_sdio";
|
||||
};
|
||||
|
|
|
@ -21,6 +21,7 @@ Required properties:
|
|||
"fsl,imx8mq-usdhc"
|
||||
"fsl,imx8mm-usdhc"
|
||||
"fsl,imx8mn-usdhc"
|
||||
"fsl,imx8mp-usdhc"
|
||||
"fsl,imx8qxp-usdhc"
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -23,7 +23,8 @@ Required properties:
|
|||
"renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
|
||||
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
|
||||
"renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC
|
||||
"renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC
|
||||
"renesas,sdhi-r8a7796" - SDHI IP on R8A77960 SoC
|
||||
"renesas,sdhi-r8a77961" - SDHI IP on R8A77961 SoC
|
||||
"renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC
|
||||
"renesas,sdhi-r8a77970" - SDHI IP on R8A77970 SoC
|
||||
"renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
* Rockchip specific extensions to the Synopsys Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the Rockchip specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
|
||||
before RK3288
|
||||
- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
|
||||
- "rockchip,rv1108-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RV1108
|
||||
- "rockchip,px30-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip PX30
|
||||
- "rockchip,rk3036-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3036
|
||||
- "rockchip,rk3228-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK322x
|
||||
- "rockchip,rk3328-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3328
|
||||
- "rockchip,rk3368-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3368
|
||||
- "rockchip,rk3399-dw-mshc", "rockchip,rk3288-dw-mshc": for Rockchip RK3399
|
||||
|
||||
Optional Properties:
|
||||
* clocks: from common clock binding: if ciu-drive and ciu-sample are
|
||||
specified in clock-names, should contain handles to these clocks.
|
||||
|
||||
* clock-names: Apart from the clock-names described in synopsys-dw-mshc.txt
|
||||
two more clocks "ciu-drive" and "ciu-sample" are supported. They are used
|
||||
to control the clock phases, "ciu-sample" is required for tuning high-
|
||||
speed modes.
|
||||
|
||||
* rockchip,default-sample-phase: The default phase to set ciu-sample at
|
||||
probing, low speeds or in case where all phases work at tuning time.
|
||||
If not specified 0 deg will be used.
|
||||
|
||||
* rockchip,desired-num-phases: The desired number of times that the host
|
||||
execute tuning when needed. If not specified, the host will do tuning
|
||||
for 360 times, namely tuning for each degree.
|
||||
|
||||
Example:
|
||||
|
||||
rkdwmmc0@12200000 {
|
||||
compatible = "rockchip,rk3288-dw-mshc";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
125
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
Normal file
125
Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
Normal file
|
@ -0,0 +1,125 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/rockchip-dw-mshc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Rockchip designware mobile storage host controller device tree bindings
|
||||
|
||||
description:
|
||||
Rockchip uses the Synopsys designware mobile storage host controller
|
||||
to interface a SoC with storage medium such as eMMC or SD/MMC cards.
|
||||
This file documents the combined properties for the core Synopsys dw mshc
|
||||
controller that are not already included in the synopsys-dw-mshc-common.yaml
|
||||
file and the Rockchip specific extensions.
|
||||
|
||||
allOf:
|
||||
- $ref: "synopsys-dw-mshc-common.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Heiko Stuebner <heiko@sntech.de>
|
||||
|
||||
# Everything else is described in the common file
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
# for Rockchip RK2928 and before RK3288
|
||||
- const: rockchip,rk2928-dw-mshc
|
||||
# for Rockchip RK3288
|
||||
- const: rockchip,rk3288-dw-mshc
|
||||
- items:
|
||||
- enum:
|
||||
# for Rockchip PX30
|
||||
- rockchip,px30-dw-mshc
|
||||
# for Rockchip RK3036
|
||||
- rockchip,rk3036-dw-mshc
|
||||
# for Rockchip RK322x
|
||||
- rockchip,rk3228-dw-mshc
|
||||
# for Rockchip RK3308
|
||||
- rockchip,rk3308-dw-mshc
|
||||
# for Rockchip RK3328
|
||||
- rockchip,rk3328-dw-mshc
|
||||
# for Rockchip RK3368
|
||||
- rockchip,rk3368-dw-mshc
|
||||
# for Rockchip RK3399
|
||||
- rockchip,rk3399-dw-mshc
|
||||
# for Rockchip RV1108
|
||||
- rockchip,rv1108-dw-mshc
|
||||
- const: rockchip,rk3288-dw-mshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 4
|
||||
description:
|
||||
Handle to "biu" and "ciu" clocks for the bus interface unit clock and
|
||||
the card interface unit clock. If "ciu-drive" and "ciu-sample" are
|
||||
specified in clock-names, it should also contain
|
||||
handles to these clocks.
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: biu
|
||||
- const: ciu
|
||||
- const: ciu-drive
|
||||
- const: ciu-sample
|
||||
description:
|
||||
Apart from the clock-names "biu" and "ciu" two more clocks
|
||||
"ciu-drive" and "ciu-sample" are supported. They are used
|
||||
to control the clock phases, "ciu-sample" is required for tuning
|
||||
high speed modes.
|
||||
|
||||
rockchip,default-sample-phase:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 360
|
||||
default: 0
|
||||
description:
|
||||
The default phase to set "ciu-sample" at probing,
|
||||
low speeds or in case where all phases work at tuning time.
|
||||
If not specified 0 deg will be used.
|
||||
|
||||
rockchip,desired-num-phases:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 360
|
||||
default: 360
|
||||
description:
|
||||
The desired number of times that the host execute tuning when needed.
|
||||
If not specified, the host will do tuning for 360 times,
|
||||
namely tuning for each degree.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/rk3288-cru.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
sdmmc: mmc@ff0c0000 {
|
||||
compatible = "rockchip,rk3288-dw-mshc";
|
||||
reg = <0x0 0xff0c0000 0x0 0x4000>;
|
||||
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&cru HCLK_SDMMC>, <&cru SCLK_SDMMC>,
|
||||
<&cru SCLK_SDMMC_DRV>, <&cru SCLK_SDMMC_SAMPLE>;
|
||||
clock-names = "biu", "ciu", "ciu-drive", "ciu-sample";
|
||||
resets = <&cru SRST_MMC0>;
|
||||
reset-names = "reset";
|
||||
fifo-depth = <0x100>;
|
||||
max-frequency = <150000000>;
|
||||
};
|
||||
|
||||
...
|
|
@ -5,11 +5,16 @@ Documentation/devicetree/bindings/mmc/mmc.txt and the properties used by the
|
|||
sdhci-of-at91 driver.
|
||||
|
||||
Required properties:
|
||||
- compatible: Must be "atmel,sama5d2-sdhci".
|
||||
- compatible: Must be "atmel,sama5d2-sdhci" or "microchip,sam9x60-sdhci".
|
||||
- clocks: Phandlers to the clocks.
|
||||
- clock-names: Must be "hclock", "multclk", "baseclk";
|
||||
- clock-names: Must be "hclock", "multclk", "baseclk" for
|
||||
"atmel,sama5d2-sdhci".
|
||||
Must be "hclock", "multclk" for "microchip,sam9x60-sdhci".
|
||||
|
||||
Optional properties:
|
||||
- assigned-clocks: The same with "multclk".
|
||||
- assigned-clock-rates The rate of "multclk" in order to not rely on the
|
||||
gck configuration set by previous components.
|
||||
- microchip,sdcal-inverted: when present, polarity on the SDCAL SoC pin is
|
||||
inverted. The default polarity for this signal is described in the datasheet.
|
||||
For instance on SAMA5D2, the pin is usually tied to the GND with a resistor
|
||||
|
@ -17,10 +22,12 @@ Optional properties:
|
|||
|
||||
Example:
|
||||
|
||||
sdmmc0: sdio-host@a0000000 {
|
||||
mmc0: sdio-host@a0000000 {
|
||||
compatible = "atmel,sama5d2-sdhci";
|
||||
reg = <0xa0000000 0x300>;
|
||||
interrupts = <31 IRQ_TYPE_LEVEL_HIGH 0>;
|
||||
clocks = <&sdmmc0_hclk>, <&sdmmc0_gclk>, <&main>;
|
||||
clock-names = "hclock", "multclk", "baseclk";
|
||||
assigned-clocks = <&sdmmc0_gclk>;
|
||||
assigned-clock-rates = <480000000>;
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ Required properties:
|
|||
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
|
||||
NOTE that some old device tree files may be floating around that only
|
||||
have the string "qcom,sdhci-msm-v4" without the SoC compatible string
|
||||
but doing that should be considered a deprecated practice.
|
||||
|
|
|
@ -7,6 +7,8 @@ For UHS devices which require tuning, the device tree should have a "cpu_thermal
|
|||
Required properties:
|
||||
- compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers
|
||||
Should be "ti,k2g-sdhci" for K2G
|
||||
Should be "ti,am335-sdhci" for am335x controllers
|
||||
Should be "ti,am437-sdhci" for am437x controllers
|
||||
- ti,hwmods: Must be "mmc<n>", <n> is controller instance starting 1
|
||||
(Not required for K2G).
|
||||
- pinctrl-names: Should be subset of "default", "hs", "sdr12", "sdr25", "sdr50",
|
||||
|
@ -15,6 +17,13 @@ Required properties:
|
|||
"hs200_1_8v",
|
||||
- pinctrl-<n> : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt
|
||||
|
||||
Optional properties:
|
||||
- dmas: List of DMA specifiers with the controller specific format as described
|
||||
in the generic DMA client binding. A tx and rx specifier is required.
|
||||
- dma-names: List of DMA request names. These strings correspond 1:1 with the
|
||||
DMA specifiers listed in dmas. The string naming is to be "tx"
|
||||
and "rx" for TX and RX DMA requests, respectively.
|
||||
|
||||
Example:
|
||||
mmc1: mmc@4809c000 {
|
||||
compatible = "ti,dra7-sdhci";
|
||||
|
@ -22,4 +31,6 @@ Example:
|
|||
ti,hwmods = "mmc1";
|
||||
bus-width = <4>;
|
||||
vmmc-supply = <&vmmc>; /* phandle to regulator node */
|
||||
dmas = <&sdma 61 &sdma 62>;
|
||||
dma-names = "tx", "rx";
|
||||
};
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/synopsys-dw-mshc-common.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Synopsys Designware Mobile Storage Host Controller Common Properties
|
||||
|
||||
allOf:
|
||||
- $ref: "mmc-controller.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
# Everything else is described in the common file
|
||||
properties:
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
reset-names:
|
||||
const: reset
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Should be the frequency (in Hz) of the ciu clock. If this
|
||||
is specified and the ciu clock is specified then we'll try to set the ciu
|
||||
clock to this at probe time.
|
||||
|
||||
fifo-depth:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The maximum size of the tx/rx fifo's. If this property is not
|
||||
specified, the default value of the fifo size is determined from the
|
||||
controller registers.
|
||||
|
||||
card-detect-delay:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
- default: 0
|
||||
description:
|
||||
Delay in milli-seconds before detecting card after card
|
||||
insert event. The default value is 0.
|
||||
|
||||
data-addr:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Override fifo address with value provided by DT. The default FIFO reg
|
||||
offset is assumed as 0x100 (version < 0x240A) and 0x200(version >= 0x240A)
|
||||
by driver. If the controller does not follow this rule, please use
|
||||
this property to set fifo address in device tree.
|
||||
|
||||
fifo-watermark-aligned:
|
||||
allOf:
|
||||
- $ref: /schemas/types.yaml#/definitions/flag
|
||||
description:
|
||||
Data done irq is expected if data length is less than
|
||||
watermark in PIO mode. But fifo watermark is requested to be aligned
|
||||
with data length in some SoC so that TX/RX irq can be generated with
|
||||
data done irq. Add this watermark quirk to mark this requirement and
|
||||
force fifo watermark setting accordingly.
|
||||
|
||||
dmas:
|
||||
maxItems: 1
|
||||
|
||||
dma-names:
|
||||
const: rx-tx
|
|
@ -1,141 +0,0 @@
|
|||
* Synopsys Designware Mobile Storage Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core mmc properties described by mmc.txt and the
|
||||
properties used by the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- snps,dw-mshc: for controllers compliant with synopsys dw-mshc.
|
||||
* #address-cells: should be 1.
|
||||
* #size-cells: should be 0.
|
||||
|
||||
# Slots (DEPRECATED): The slot specific information are contained within
|
||||
child-nodes with each child-node representing a supported slot. There should
|
||||
be atleast one child node representing a card slot. The name of the child node
|
||||
representing the slot is recommended to be slot@n where n is the unique number
|
||||
of the slot connected to the controller. The following are optional properties
|
||||
which can be included in the slot child node.
|
||||
|
||||
* reg: specifies the physical slot number. The valid values of this
|
||||
property is 0 to (num-slots -1), where num-slots is the value
|
||||
specified by the num-slots property.
|
||||
|
||||
* bus-width: as documented in mmc core bindings.
|
||||
|
||||
* wp-gpios: specifies the write protect gpio line. The format of the
|
||||
gpio specifier depends on the gpio controller. If a GPIO is not used
|
||||
for write-protect, this property is optional.
|
||||
|
||||
* disable-wp: If the wp-gpios property isn't present then (by default)
|
||||
we'd assume that the write protect is hooked up directly to the
|
||||
controller's special purpose write protect line (accessible via
|
||||
the WRTPRT register). However, it's possible that we simply don't
|
||||
want write protect. In that case specify 'disable-wp'.
|
||||
NOTE: This property is not required for slots known to always
|
||||
connect to eMMC or SDIO cards.
|
||||
|
||||
Optional properties:
|
||||
|
||||
* resets: phandle + reset specifier pair, intended to represent hardware
|
||||
reset signal present internally in some host controller IC designs.
|
||||
See Documentation/devicetree/bindings/reset/reset.txt for details.
|
||||
|
||||
* reset-names: request name for using "resets" property. Must be "reset".
|
||||
(It will be used together with "resets" property.)
|
||||
|
||||
* clocks: from common clock binding: handle to biu and ciu clocks for the
|
||||
bus interface unit clock and the card interface unit clock.
|
||||
|
||||
* clock-names: from common clock binding: Shall be "biu" and "ciu".
|
||||
If the biu clock is missing we'll simply skip enabling it. If the
|
||||
ciu clock is missing we'll just assume that the clock is running at
|
||||
clock-frequency. It is an error to omit both the ciu clock and the
|
||||
clock-frequency.
|
||||
|
||||
* clock-frequency: should be the frequency (in Hz) of the ciu clock. If this
|
||||
is specified and the ciu clock is specified then we'll try to set the ciu
|
||||
clock to this at probe time.
|
||||
|
||||
* fifo-depth: The maximum size of the tx/rx fifo's. If this property is not
|
||||
specified, the default value of the fifo size is determined from the
|
||||
controller registers.
|
||||
|
||||
* card-detect-delay: Delay in milli-seconds before detecting card after card
|
||||
insert event. The default value is 0.
|
||||
|
||||
* data-addr: Override fifo address with value provided by DT. The default FIFO reg
|
||||
offset is assumed as 0x100 (version < 0x240A) and 0x200(version >= 0x240A) by
|
||||
driver. If the controller does not follow this rule, please use this property
|
||||
to set fifo address in device tree.
|
||||
|
||||
* fifo-watermark-aligned: Data done irq is expected if data length is less than
|
||||
watermark in PIO mode. But fifo watermark is requested to be aligned with data
|
||||
length in some SoC so that TX/RX irq can be generated with data done irq. Add this
|
||||
watermark quirk to mark this requirement and force fifo watermark setting
|
||||
accordingly.
|
||||
|
||||
* vmmc-supply: The phandle to the regulator to use for vmmc. If this is
|
||||
specified we'll defer probe until we can find this regulator.
|
||||
|
||||
* dmas: List of DMA specifiers with the controller specific format as described
|
||||
in the generic DMA client binding. Refer to dma.txt for details.
|
||||
|
||||
* dma-names: request names for generic DMA client binding. Must be "rx-tx".
|
||||
Refer to dma.txt for details.
|
||||
|
||||
Aliases:
|
||||
|
||||
- All the MSHC controller nodes should be represented in the aliases node using
|
||||
the following format 'mshc{n}' where n is a unique number for the alias.
|
||||
|
||||
Example:
|
||||
|
||||
The MSHC controller node can be split into two portions, SoC specific and
|
||||
board specific portions as listed below.
|
||||
|
||||
dwmmc0@12200000 {
|
||||
compatible = "snps,dw-mshc";
|
||||
clocks = <&clock 351>, <&clock 132>;
|
||||
clock-names = "biu", "ciu";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
data-addr = <0x200>;
|
||||
fifo-watermark-aligned;
|
||||
resets = <&rst 20>;
|
||||
reset-names = "reset";
|
||||
};
|
||||
|
||||
[board specific internal DMA resources]
|
||||
|
||||
dwmmc0@12200000 {
|
||||
clock-frequency = <400000000>;
|
||||
clock-freq-min-max = <400000 200000000>;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
vmmc-supply = <&buck8>;
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
};
|
||||
|
||||
[board specific generic DMA request binding]
|
||||
|
||||
dwmmc0@12200000 {
|
||||
clock-frequency = <400000000>;
|
||||
clock-freq-min-max = <400000 200000000>;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
vmmc-supply = <&buck8>;
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
dmas = <&pdma 12>;
|
||||
dma-names = "rx-tx";
|
||||
};
|
70
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml
Normal file
70
Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.yaml
Normal file
|
@ -0,0 +1,70 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/synopsys-dw-mshc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Synopsys Designware Mobile Storage Host Controller Binding
|
||||
|
||||
allOf:
|
||||
- $ref: "synopsys-dw-mshc-common.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
# Everything else is described in the common file
|
||||
properties:
|
||||
compatible:
|
||||
const: snps,dw-mshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
description:
|
||||
Handle to "biu" and "ciu" clocks for the
|
||||
bus interface unit clock and the card interface unit clock.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: biu
|
||||
- const: ciu
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
mmc@12200000 {
|
||||
compatible = "snps,dw-mshc";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
clocks = <&clock 351>, <&clock 132>;
|
||||
clock-names = "biu", "ciu";
|
||||
dmas = <&pdma 12>;
|
||||
dma-names = "rx-tx";
|
||||
resets = <&rst 20>;
|
||||
reset-names = "reset";
|
||||
vmmc-supply = <&buck8>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
broken-cd;
|
||||
bus-width = <8>;
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
card-detect-delay = <200>;
|
||||
clock-freq-min-max = <400000 200000000>;
|
||||
clock-frequency = <400000000>;
|
||||
data-addr = <0x200>;
|
||||
fifo-depth = <0x80>;
|
||||
fifo-watermark-aligned;
|
||||
};
|
|
@ -2251,6 +2251,7 @@ L: linux-rockchip@lists.infradead.org
|
|||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/i2c/i2c-rk3x.txt
|
||||
F: Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml
|
||||
F: arch/arm/boot/dts/rk3*
|
||||
F: arch/arm/boot/dts/rv1108*
|
||||
F: arch/arm/mach-rockchip/
|
||||
|
|
|
@ -131,27 +131,6 @@ static void of_gpio_flags_quirks(struct device_node *np,
|
|||
enum of_gpio_flags *flags,
|
||||
int index)
|
||||
{
|
||||
/*
|
||||
* Handle MMC "cd-inverted" and "wp-inverted" semantics.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_MMC)) {
|
||||
/*
|
||||
* Active low is the default according to the
|
||||
* SDHCI specification and the device tree
|
||||
* bindings. However the code in the current
|
||||
* kernel was written such that the phandle
|
||||
* flags were always respected, and "cd-inverted"
|
||||
* would invert the flag from the device phandle.
|
||||
*/
|
||||
if (!strcmp(propname, "cd-gpios")) {
|
||||
if (of_property_read_bool(np, "cd-inverted"))
|
||||
*flags ^= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
if (!strcmp(propname, "wp-gpios")) {
|
||||
if (of_property_read_bool(np, "wp-inverted"))
|
||||
*flags ^= OF_GPIO_ACTIVE_LOW;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Some GPIO fixed regulator quirks.
|
||||
* Note that active low is the default.
|
||||
|
|
|
@ -3371,6 +3371,17 @@ int gpiod_is_active_low(const struct gpio_desc *desc)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_is_active_low);
|
||||
|
||||
/**
|
||||
* gpiod_toggle_active_low - toggle whether a GPIO is active-low or not
|
||||
* @desc: the gpio descriptor to change
|
||||
*/
|
||||
void gpiod_toggle_active_low(struct gpio_desc *desc)
|
||||
{
|
||||
VALIDATE_DESC_VOID(desc);
|
||||
change_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_toggle_active_low);
|
||||
|
||||
/* I/O calls are only valid after configuration completed; the relevant
|
||||
* "is this a valid GPIO" error checks should already have been done.
|
||||
*
|
||||
|
|
|
@ -1107,7 +1107,7 @@ static void mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
|
|||
card->erase_arg == MMC_TRIM_ARG ?
|
||||
INAND_CMD38_ARG_TRIM :
|
||||
INAND_CMD38_ARG_ERASE,
|
||||
0);
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
}
|
||||
if (!err)
|
||||
err = mmc_erase(card, from, nr, card->erase_arg);
|
||||
|
@ -1149,7 +1149,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
|||
arg == MMC_SECURE_TRIM1_ARG ?
|
||||
INAND_CMD38_ARG_SECTRIM1 :
|
||||
INAND_CMD38_ARG_SECERASE,
|
||||
0);
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err)
|
||||
goto out_retry;
|
||||
}
|
||||
|
@ -1167,7 +1167,7 @@ static void mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
|
|||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
INAND_CMD38_ARG_EXT_CSD,
|
||||
INAND_CMD38_ARG_SECTRIM2,
|
||||
0);
|
||||
card->ext_csd.generic_cmd6_time);
|
||||
if (err)
|
||||
goto out_retry;
|
||||
}
|
||||
|
|
|
@ -2330,7 +2330,13 @@ void mmc_rescan(struct work_struct *work)
|
|||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
|
||||
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
|
||||
unsigned int freq = freqs[i];
|
||||
if (freq > host->f_max) {
|
||||
if (i + 1 < ARRAY_SIZE(freqs))
|
||||
continue;
|
||||
freq = host->f_max;
|
||||
}
|
||||
if (!mmc_rescan_try_freq(host, max(freq, host->f_min)))
|
||||
break;
|
||||
if (freqs[i] <= host->f_min)
|
||||
break;
|
||||
|
@ -2344,7 +2350,7 @@ void mmc_rescan(struct work_struct *work)
|
|||
|
||||
void mmc_start_host(struct mmc_host *host)
|
||||
{
|
||||
host->f_init = max(freqs[0], host->f_min);
|
||||
host->f_init = max(min(freqs[0], host->f_max), host->f_min);
|
||||
host->rescan_disable = 0;
|
||||
host->ios.power_mode = MMC_POWER_UNDEFINED;
|
||||
|
||||
|
|
|
@ -175,8 +175,6 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
struct device *dev = host->parent;
|
||||
u32 bus_width, drv_type, cd_debounce_delay_ms;
|
||||
int ret;
|
||||
bool cd_cap_invert, cd_gpio_invert = false;
|
||||
bool ro_cap_invert, ro_gpio_invert = false;
|
||||
|
||||
if (!dev || !dev_fwnode(dev))
|
||||
return 0;
|
||||
|
@ -219,10 +217,12 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
*/
|
||||
|
||||
/* Parse Card Detection */
|
||||
|
||||
if (device_property_read_bool(dev, "non-removable")) {
|
||||
host->caps |= MMC_CAP_NONREMOVABLE;
|
||||
} else {
|
||||
cd_cap_invert = device_property_read_bool(dev, "cd-inverted");
|
||||
if (device_property_read_bool(dev, "cd-inverted"))
|
||||
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
|
||||
if (device_property_read_u32(dev, "cd-debounce-delay-ms",
|
||||
&cd_debounce_delay_ms))
|
||||
|
@ -232,32 +232,19 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
host->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ret = mmc_gpiod_request_cd(host, "cd", 0, false,
|
||||
cd_debounce_delay_ms * 1000,
|
||||
&cd_gpio_invert);
|
||||
cd_debounce_delay_ms * 1000);
|
||||
if (!ret)
|
||||
dev_info(host->parent, "Got CD GPIO\n");
|
||||
else if (ret != -ENOENT && ret != -ENOSYS)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There are two ways to flag that the CD line is inverted:
|
||||
* through the cd-inverted flag and by the GPIO line itself
|
||||
* being inverted from the GPIO subsystem. This is a leftover
|
||||
* from the times when the GPIO subsystem did not make it
|
||||
* possible to flag a line as inverted.
|
||||
*
|
||||
* If the capability on the host AND the GPIO line are
|
||||
* both inverted, the end result is that the CD line is
|
||||
* not inverted.
|
||||
*/
|
||||
if (cd_cap_invert ^ cd_gpio_invert)
|
||||
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
/* Parse Write Protection */
|
||||
ro_cap_invert = device_property_read_bool(dev, "wp-inverted");
|
||||
|
||||
ret = mmc_gpiod_request_ro(host, "wp", 0, 0, &ro_gpio_invert);
|
||||
if (device_property_read_bool(dev, "wp-inverted"))
|
||||
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
ret = mmc_gpiod_request_ro(host, "wp", 0, 0);
|
||||
if (!ret)
|
||||
dev_info(host->parent, "Got WP GPIO\n");
|
||||
else if (ret != -ENOENT && ret != -ENOSYS)
|
||||
|
@ -266,10 +253,6 @@ int mmc_of_parse(struct mmc_host *host)
|
|||
if (device_property_read_bool(dev, "disable-wp"))
|
||||
host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
|
||||
|
||||
/* See the comment on CD inversion above */
|
||||
if (ro_cap_invert ^ ro_gpio_invert)
|
||||
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
if (device_property_read_bool(dev, "cap-sd-highspeed"))
|
||||
host->caps |= MMC_CAP_SD_HIGHSPEED;
|
||||
if (device_property_read_bool(dev, "cap-mmc-highspeed"))
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
#include "host.h"
|
||||
#include "mmc_ops.h"
|
||||
|
||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
|
||||
#define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10min*/
|
||||
#define MMC_BKOPS_TIMEOUT_MS (120 * 1000) /* 120s */
|
||||
#define MMC_CACHE_FLUSH_TIMEOUT_MS (30 * 1000) /* 30s */
|
||||
|
||||
static const u8 tuning_blk_pattern_4bit[] = {
|
||||
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
|
||||
|
@ -458,10 +460,6 @@ static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
|
|||
bool expired = false;
|
||||
bool busy = false;
|
||||
|
||||
/* We have an unspecified cmd timeout, use the fallback value. */
|
||||
if (!timeout_ms)
|
||||
timeout_ms = MMC_OPS_TIMEOUT_MS;
|
||||
|
||||
/*
|
||||
* In cases when not allowed to poll by using CMD13 or because we aren't
|
||||
* capable of polling by using ->card_busy(), then rely on waiting the
|
||||
|
@ -534,14 +532,19 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
|
||||
mmc_retune_hold(host);
|
||||
|
||||
if (!timeout_ms) {
|
||||
pr_warn("%s: unspecified timeout for CMD6 - use generic\n",
|
||||
mmc_hostname(host));
|
||||
timeout_ms = card->ext_csd.generic_cmd6_time;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the cmd timeout and the max_busy_timeout of the host are both
|
||||
* specified, let's validate them. A failure means we need to prevent
|
||||
* the host from doing hw busy detection, which is done by converting
|
||||
* to a R1 response instead of a R1B.
|
||||
* If the max_busy_timeout of the host is specified, make sure it's
|
||||
* enough to fit the used timeout_ms. In case it's not, let's instruct
|
||||
* the host to avoid HW busy detection, by converting to a R1 response
|
||||
* instead of a R1B.
|
||||
*/
|
||||
if (timeout_ms && host->max_busy_timeout &&
|
||||
(timeout_ms > host->max_busy_timeout))
|
||||
if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
|
||||
use_r1b_resp = false;
|
||||
|
||||
cmd.opcode = MMC_SWITCH;
|
||||
|
@ -552,10 +555,6 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
|
|||
cmd.flags = MMC_CMD_AC;
|
||||
if (use_r1b_resp) {
|
||||
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
|
||||
/*
|
||||
* A busy_timeout of zero means the host can decide to use
|
||||
* whatever value it finds suitable.
|
||||
*/
|
||||
cmd.busy_timeout = timeout_ms;
|
||||
} else {
|
||||
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
|
||||
|
@ -941,7 +940,7 @@ void mmc_run_bkops(struct mmc_card *card)
|
|||
* urgent levels by using an asynchronous background task, when idle.
|
||||
*/
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_BKOPS_START, 1, MMC_OPS_TIMEOUT_MS);
|
||||
EXT_CSD_BKOPS_START, 1, MMC_BKOPS_TIMEOUT_MS);
|
||||
if (err)
|
||||
pr_warn("%s: Error %d starting bkops\n",
|
||||
mmc_hostname(card->host), err);
|
||||
|
@ -961,7 +960,8 @@ int mmc_flush_cache(struct mmc_card *card)
|
|||
(card->ext_csd.cache_size > 0) &&
|
||||
(card->ext_csd.cache_ctrl & 1)) {
|
||||
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
||||
EXT_CSD_FLUSH_CACHE, 1, 0);
|
||||
EXT_CSD_FLUSH_CACHE, 1,
|
||||
MMC_CACHE_FLUSH_TIMEOUT_MS);
|
||||
if (err)
|
||||
pr_err("%s: cache flush error %d\n",
|
||||
mmc_hostname(card->host), err);
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
struct mmc_gpio {
|
||||
struct gpio_desc *ro_gpio;
|
||||
struct gpio_desc *cd_gpio;
|
||||
bool override_cd_active_level;
|
||||
irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id);
|
||||
char *ro_label;
|
||||
char *cd_label;
|
||||
|
@ -80,13 +79,6 @@ int mmc_gpio_get_cd(struct mmc_host *host)
|
|||
return -ENOSYS;
|
||||
|
||||
cansleep = gpiod_cansleep(ctx->cd_gpio);
|
||||
if (ctx->override_cd_active_level) {
|
||||
int value = cansleep ?
|
||||
gpiod_get_raw_value_cansleep(ctx->cd_gpio) :
|
||||
gpiod_get_raw_value(ctx->cd_gpio);
|
||||
return !value ^ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
|
||||
}
|
||||
|
||||
return cansleep ?
|
||||
gpiod_get_value_cansleep(ctx->cd_gpio) :
|
||||
gpiod_get_value(ctx->cd_gpio);
|
||||
|
@ -168,8 +160,6 @@ EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
|
|||
* @idx: index of the GPIO to obtain in the consumer
|
||||
* @override_active_level: ignore %GPIO_ACTIVE_LOW flag
|
||||
* @debounce: debounce time in microseconds
|
||||
* @gpio_invert: will return whether the GPIO line is inverted or not, set
|
||||
* to NULL to ignore
|
||||
*
|
||||
* Note that this must be called prior to mmc_add_host()
|
||||
* otherwise the caller must also call mmc_gpiod_request_cd_irq().
|
||||
|
@ -178,7 +168,7 @@ EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
|
|||
*/
|
||||
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert)
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
struct gpio_desc *desc;
|
||||
|
@ -194,10 +184,14 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
|||
ctx->cd_debounce_delay_ms = debounce / 1000;
|
||||
}
|
||||
|
||||
if (gpio_invert)
|
||||
*gpio_invert = !gpiod_is_active_low(desc);
|
||||
/* override forces default (active-low) polarity ... */
|
||||
if (override_active_level && !gpiod_is_active_low(desc))
|
||||
gpiod_toggle_active_low(desc);
|
||||
|
||||
/* ... or active-high */
|
||||
if (host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
|
||||
gpiod_toggle_active_low(desc);
|
||||
|
||||
ctx->override_cd_active_level = override_active_level;
|
||||
ctx->cd_gpio = desc;
|
||||
|
||||
return 0;
|
||||
|
@ -218,14 +212,11 @@ EXPORT_SYMBOL(mmc_can_gpio_cd);
|
|||
* @con_id: function within the GPIO consumer
|
||||
* @idx: index of the GPIO to obtain in the consumer
|
||||
* @debounce: debounce time in microseconds
|
||||
* @gpio_invert: will return whether the GPIO line is inverted or not,
|
||||
* set to NULL to ignore
|
||||
*
|
||||
* Returns zero on success, else an error.
|
||||
*/
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx,
|
||||
unsigned int debounce, bool *gpio_invert)
|
||||
unsigned int idx, unsigned int debounce)
|
||||
{
|
||||
struct mmc_gpio *ctx = host->slot.handler_priv;
|
||||
struct gpio_desc *desc;
|
||||
|
@ -241,8 +232,8 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (gpio_invert)
|
||||
*gpio_invert = !gpiod_is_active_low(desc);
|
||||
if (host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH)
|
||||
gpiod_toggle_active_low(desc);
|
||||
|
||||
ctx->ro_gpio = desc;
|
||||
|
||||
|
|
|
@ -501,6 +501,7 @@ config MMC_SDHCI_MSM
|
|||
depends on ARCH_QCOM || (ARM && COMPILE_TEST)
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
select MMC_CQHCI
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in Qualcomm SOCs. The controller supports
|
||||
|
@ -990,6 +991,7 @@ config MMC_SDHCI_BRCMSTB
|
|||
tristate "Broadcom SDIO/SD/MMC support"
|
||||
depends on ARCH_BRCMSTB || BMIPS_GENERIC
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_CQHCI
|
||||
default y
|
||||
help
|
||||
This selects support for the SDIO/SD/MMC Host Controller on
|
||||
|
@ -1010,6 +1012,7 @@ config MMC_SDHCI_OMAP
|
|||
depends on MMC_SDHCI_PLTFM && OF
|
||||
select THERMAL
|
||||
imply TI_SOC_THERMAL
|
||||
select MMC_SDHCI_EXTERNAL_DMA if DMA_ENGINE
|
||||
help
|
||||
This selects the Secure Digital Host Controller Interface (SDHCI)
|
||||
support present in TI's DRA7 SOCs. The controller supports
|
||||
|
@ -1040,3 +1043,6 @@ config MMC_OWL
|
|||
help
|
||||
This selects support for the SD/MMC Host Controller on
|
||||
Actions Semi Owl SoCs.
|
||||
|
||||
config MMC_SDHCI_EXTERNAL_DMA
|
||||
bool
|
||||
|
|
|
@ -2645,7 +2645,7 @@ static int atmci_runtime_resume(struct device *dev)
|
|||
{
|
||||
struct atmel_mci *host = dev_get_drvdata(dev);
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
pinctrl_select_default_state(dev);
|
||||
|
||||
return clk_prepare_enable(host->mck);
|
||||
}
|
||||
|
|
|
@ -984,12 +984,9 @@ static int au1xmmc_probe(struct platform_device *pdev)
|
|||
goto out2;
|
||||
}
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "no IRQ defined\n");
|
||||
host->irq = platform_get_irq(pdev, 0);
|
||||
if (host->irq < 0)
|
||||
goto out3;
|
||||
}
|
||||
host->irq = r->start;
|
||||
|
||||
mmc->ops = &au1xmmc_ops;
|
||||
|
||||
|
|
|
@ -1393,7 +1393,17 @@ static int bcm2835_probe(struct platform_device *pdev)
|
|||
host->dma_chan = NULL;
|
||||
host->dma_desc = NULL;
|
||||
|
||||
host->dma_chan_rxtx = dma_request_slave_channel(dev, "rx-tx");
|
||||
host->dma_chan_rxtx = dma_request_chan(dev, "rx-tx");
|
||||
if (IS_ERR(host->dma_chan_rxtx)) {
|
||||
ret = PTR_ERR(host->dma_chan_rxtx);
|
||||
host->dma_chan_rxtx = NULL;
|
||||
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err;
|
||||
|
||||
/* Ignore errors to fall back to PIO mode */
|
||||
}
|
||||
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
|
|
|
@ -76,8 +76,10 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
|||
return ret;
|
||||
|
||||
host->base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
|
||||
if (!host->base)
|
||||
return -EINVAL;
|
||||
if (!host->base) {
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* On ThunderX these are identical */
|
||||
host->dma_base = host->base;
|
||||
|
@ -86,12 +88,14 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
|||
host->reg_off_dma = 0x160;
|
||||
|
||||
host->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(host->clk))
|
||||
return PTR_ERR(host->clk);
|
||||
if (IS_ERR(host->clk)) {
|
||||
ret = PTR_ERR(host->clk);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(host->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto error;
|
||||
host->sys_freq = clk_get_rate(host->clk);
|
||||
|
||||
spin_lock_init(&host->irq_handler_lock);
|
||||
|
@ -157,6 +161,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
|||
}
|
||||
}
|
||||
clk_disable_unprepare(host->clk);
|
||||
pci_release_regions(pdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -175,6 +180,7 @@ static void thunder_mmc_remove(struct pci_dev *pdev)
|
|||
writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host));
|
||||
|
||||
clk_disable_unprepare(host->clk);
|
||||
pci_release_regions(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id thunder_mmc_id_table[] = {
|
||||
|
|
|
@ -1174,13 +1174,13 @@ static int mmc_davinci_parse_pdata(struct mmc_host *mmc)
|
|||
mmc->caps |= pdata->caps;
|
||||
|
||||
/* Register a cd gpio, if there is not one, enable polling */
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
else if (ret)
|
||||
mmc->caps |= MMC_CAP_NEEDS_POLL;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -833,12 +833,14 @@ static int dw_mci_edmac_init(struct dw_mci *host)
|
|||
if (!host->dms)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dms->ch = dma_request_slave_channel(host->dev, "rx-tx");
|
||||
if (!host->dms->ch) {
|
||||
host->dms->ch = dma_request_chan(host->dev, "rx-tx");
|
||||
if (IS_ERR(host->dms->ch)) {
|
||||
int ret = PTR_ERR(host->dms->ch);
|
||||
|
||||
dev_err(host->dev, "Failed to get external DMA channel.\n");
|
||||
kfree(host->dms);
|
||||
host->dms = NULL;
|
||||
return -ENXIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1108,7 +1108,7 @@ static int jz4740_mmc_suspend(struct device *dev)
|
|||
|
||||
static int jz4740_mmc_resume(struct device *dev)
|
||||
{
|
||||
return pinctrl_pm_select_default_state(dev);
|
||||
return pinctrl_select_default_state(dev);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
|
||||
|
|
|
@ -161,7 +161,6 @@ struct meson_host {
|
|||
bool dram_access_quirk;
|
||||
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_clk_gate;
|
||||
|
||||
unsigned int bounce_buf_size;
|
||||
|
@ -327,7 +326,7 @@ static void meson_mmc_clk_ungate(struct meson_host *host)
|
|||
u32 cfg;
|
||||
|
||||
if (host->pins_clk_gate)
|
||||
pinctrl_select_state(host->pinctrl, host->pins_default);
|
||||
pinctrl_select_default_state(host->dev);
|
||||
|
||||
/* Make sure the clock is not stopped in the controller */
|
||||
cfg = readl(host->regs + SD_EMMC_CFG);
|
||||
|
@ -1101,13 +1100,6 @@ static int meson_mmc_probe(struct platform_device *pdev)
|
|||
goto free_host;
|
||||
}
|
||||
|
||||
host->pins_default = pinctrl_lookup_state(host->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
if (IS_ERR(host->pins_default)) {
|
||||
ret = PTR_ERR(host->pins_default);
|
||||
goto free_host;
|
||||
}
|
||||
|
||||
host->pins_clk_gate = pinctrl_lookup_state(host->pinctrl,
|
||||
"clk-gate");
|
||||
if (IS_ERR(host->pins_clk_gate)) {
|
||||
|
|
|
@ -638,7 +638,6 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
|
|||
struct platform_device *slot_pdev;
|
||||
struct mmc_host *mmc;
|
||||
struct meson_mx_mmc_host *host;
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
u32 conf;
|
||||
|
||||
|
@ -663,8 +662,7 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(host->controller_dev, res);
|
||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto error_free_mmc;
|
||||
|
|
|
@ -1134,17 +1134,22 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host)
|
|||
* SPI protocol. Another is that when chipselect is released while
|
||||
* the card returns BUSY status, the clock must issue several cycles
|
||||
* with chipselect high before the card will stop driving its output.
|
||||
*
|
||||
* SPI_CS_HIGH means "asserted" here. In some cases like when using
|
||||
* GPIOs for chip select, SPI_CS_HIGH is set but this will be logically
|
||||
* inverted by gpiolib, so if we want to ascertain to drive it high
|
||||
* we should toggle the default with an XOR as we do here.
|
||||
*/
|
||||
host->spi->mode |= SPI_CS_HIGH;
|
||||
host->spi->mode ^= SPI_CS_HIGH;
|
||||
if (spi_setup(host->spi) != 0) {
|
||||
/* Just warn; most cards work without it. */
|
||||
dev_warn(&host->spi->dev,
|
||||
"can't change chip-select polarity\n");
|
||||
host->spi->mode &= ~SPI_CS_HIGH;
|
||||
host->spi->mode ^= SPI_CS_HIGH;
|
||||
} else {
|
||||
mmc_spi_readbytes(host, 18);
|
||||
|
||||
host->spi->mode &= ~SPI_CS_HIGH;
|
||||
host->spi->mode ^= SPI_CS_HIGH;
|
||||
if (spi_setup(host->spi) != 0) {
|
||||
/* Wot, we can't get the same setup we had before? */
|
||||
dev_err(&host->spi->dev,
|
||||
|
@ -1421,7 +1426,7 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
* Index 0 is card detect
|
||||
* Old boardfiles were specifying 1 ms as debounce
|
||||
*/
|
||||
status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1000, NULL);
|
||||
status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1000);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto fail_add_host;
|
||||
if (!status) {
|
||||
|
@ -1436,7 +1441,7 @@ static int mmc_spi_probe(struct spi_device *spi)
|
|||
mmc_detect_change(mmc, 0);
|
||||
|
||||
/* Index 1 is write protect/read only */
|
||||
status = mmc_gpiod_request_ro(mmc, NULL, 1, 0, NULL);
|
||||
status = mmc_gpiod_request_ro(mmc, NULL, 1, 0);
|
||||
if (status == -EPROBE_DEFER)
|
||||
goto fail_add_host;
|
||||
if (!status)
|
||||
|
|
|
@ -169,6 +169,8 @@ static struct variant_data variant_ux500 = {
|
|||
.cmdreg_srsp = MCI_CPSM_RESPONSE,
|
||||
.datalength_bits = 24,
|
||||
.datactrl_blocksz = 11,
|
||||
.datactrl_any_blocksz = true,
|
||||
.dma_power_of_2 = true,
|
||||
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
|
||||
.st_sdio = true,
|
||||
.st_clkdiv = true,
|
||||
|
@ -202,6 +204,8 @@ static struct variant_data variant_ux500v2 = {
|
|||
.datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE,
|
||||
.datalength_bits = 24,
|
||||
.datactrl_blocksz = 11,
|
||||
.datactrl_any_blocksz = true,
|
||||
.dma_power_of_2 = true,
|
||||
.datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
|
||||
.st_sdio = true,
|
||||
.st_clkdiv = true,
|
||||
|
@ -261,6 +265,7 @@ static struct variant_data variant_stm32_sdmmc = {
|
|||
.datacnt_useless = true,
|
||||
.datalength_bits = 25,
|
||||
.datactrl_blocksz = 14,
|
||||
.datactrl_any_blocksz = true,
|
||||
.stm32_idmabsize_mask = GENMASK(12, 5),
|
||||
.busy_timeout = true,
|
||||
.busy_detect = true,
|
||||
|
@ -284,6 +289,7 @@ static struct variant_data variant_qcom = {
|
|||
.data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
|
||||
.datalength_bits = 24,
|
||||
.datactrl_blocksz = 11,
|
||||
.datactrl_any_blocksz = true,
|
||||
.pwrreg_powerup = MCI_PWR_UP,
|
||||
.f_max = 208000000,
|
||||
.explicit_mclk_control = true,
|
||||
|
@ -452,10 +458,11 @@ static void mmci_dma_setup(struct mmci_host *host)
|
|||
static int mmci_validate_data(struct mmci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
struct variant_data *variant = host->variant;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
if (!is_power_of_2(data->blksz)) {
|
||||
if (!is_power_of_2(data->blksz) && !variant->datactrl_any_blocksz) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"unsupported block size (%d bytes)\n", data->blksz);
|
||||
return -EINVAL;
|
||||
|
@ -520,7 +527,9 @@ static int mmci_dma_start(struct mmci_host *host, unsigned int datactrl)
|
|||
"Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
|
||||
data->sg_len, data->blksz, data->blocks, data->flags);
|
||||
|
||||
host->ops->dma_start(host, &datactrl);
|
||||
ret = host->ops->dma_start(host, &datactrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Trigger the DMA transfer */
|
||||
mmci_write_datactrlreg(host, datactrl);
|
||||
|
@ -706,10 +715,20 @@ int mmci_dmae_setup(struct mmci_host *host)
|
|||
|
||||
host->dma_priv = dmae;
|
||||
|
||||
dmae->rx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
|
||||
"rx");
|
||||
dmae->tx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
|
||||
"tx");
|
||||
dmae->rx_channel = dma_request_chan(mmc_dev(host->mmc), "rx");
|
||||
if (IS_ERR(dmae->rx_channel)) {
|
||||
int ret = PTR_ERR(dmae->rx_channel);
|
||||
dmae->rx_channel = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dmae->tx_channel = dma_request_chan(mmc_dev(host->mmc), "tx");
|
||||
if (IS_ERR(dmae->tx_channel)) {
|
||||
if (PTR_ERR(dmae->tx_channel) == -EPROBE_DEFER)
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"Deferred probe for TX channel ignored\n");
|
||||
dmae->tx_channel = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If only an RX channel is specified, the driver will
|
||||
|
@ -888,6 +907,18 @@ static int _mmci_dmae_prep_data(struct mmci_host *host, struct mmc_data *data,
|
|||
if (data->blksz * data->blocks <= variant->fifosize)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* This is necessary to get SDIO working on the Ux500. We do not yet
|
||||
* know if this is a bug in:
|
||||
* - The Ux500 DMA controller (DMA40)
|
||||
* - The MMCI DMA interface on the Ux500
|
||||
* some power of two blocks (such as 64 bytes) are sent regularly
|
||||
* during SDIO traffic and those work fine so for these we enable DMA
|
||||
* transfers.
|
||||
*/
|
||||
if (host->variant->dma_power_of_2 && !is_power_of_2(data->blksz))
|
||||
return -EINVAL;
|
||||
|
||||
device = chan->device;
|
||||
nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
|
@ -938,9 +969,14 @@ int mmci_dmae_prep_data(struct mmci_host *host,
|
|||
int mmci_dmae_start(struct mmci_host *host, unsigned int *datactrl)
|
||||
{
|
||||
struct mmci_dmae_priv *dmae = host->dma_priv;
|
||||
int ret;
|
||||
|
||||
host->dma_in_progress = true;
|
||||
dmaengine_submit(dmae->desc_current);
|
||||
ret = dma_submit_error(dmaengine_submit(dmae->desc_current));
|
||||
if (ret < 0) {
|
||||
host->dma_in_progress = false;
|
||||
return ret;
|
||||
}
|
||||
dma_async_issue_pending(dmae->cur);
|
||||
|
||||
*datactrl |= MCI_DPSM_DMAENABLE;
|
||||
|
@ -1321,6 +1357,7 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||
} else if (host->variant->busy_timeout && busy_resp &&
|
||||
status & MCI_DATATIMEOUT) {
|
||||
cmd->error = -ETIMEDOUT;
|
||||
host->irq_action = IRQ_WAKE_THREAD;
|
||||
} else {
|
||||
cmd->resp[0] = readl(base + MMCIRESPONSE0);
|
||||
cmd->resp[1] = readl(base + MMCIRESPONSE1);
|
||||
|
@ -1339,7 +1376,10 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
|
|||
return;
|
||||
}
|
||||
}
|
||||
mmci_request_end(host, host->mrq);
|
||||
|
||||
if (host->irq_action != IRQ_WAKE_THREAD)
|
||||
mmci_request_end(host, host->mrq);
|
||||
|
||||
} else if (sbc) {
|
||||
mmci_start_command(host, host->mrq->cmd, 0);
|
||||
} else if (!host->variant->datactrl_first &&
|
||||
|
@ -1532,9 +1572,9 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
|
|||
{
|
||||
struct mmci_host *host = dev_id;
|
||||
u32 status;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock(&host->lock);
|
||||
host->irq_action = IRQ_HANDLED;
|
||||
|
||||
do {
|
||||
status = readl(host->base + MMCISTATUS);
|
||||
|
@ -1574,12 +1614,41 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
|
|||
if (host->variant->busy_detect_flag)
|
||||
status &= ~host->variant->busy_detect_flag;
|
||||
|
||||
ret = 1;
|
||||
} while (status);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
|
||||
return IRQ_RETVAL(ret);
|
||||
return host->irq_action;
|
||||
}
|
||||
|
||||
/*
|
||||
* mmci_irq_thread() - A threaded IRQ handler that manages a reset of the HW.
|
||||
*
|
||||
* A reset is needed for some variants, where a datatimeout for a R1B request
|
||||
* causes the DPSM to stay busy (non-functional).
|
||||
*/
|
||||
static irqreturn_t mmci_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct mmci_host *host = dev_id;
|
||||
unsigned long flags;
|
||||
|
||||
if (host->rst) {
|
||||
reset_control_assert(host->rst);
|
||||
udelay(2);
|
||||
reset_control_deassert(host->rst);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
writel(host->clk_reg, host->base + MMCICLOCK);
|
||||
writel(host->pwr_reg, host->base + MMCIPOWER);
|
||||
writel(MCI_IRQENABLE | host->variant->start_err,
|
||||
host->base + MMCIMASK0);
|
||||
|
||||
host->irq_action = IRQ_HANDLED;
|
||||
mmci_request_end(host, host->mrq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return host->irq_action;
|
||||
}
|
||||
|
||||
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
|
@ -1704,7 +1773,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
|
||||
pinctrl_select_state(host->pinctrl, host->pins_opendrain);
|
||||
else
|
||||
pinctrl_select_state(host->pinctrl, host->pins_default);
|
||||
pinctrl_select_default_state(mmc_dev(mmc));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1877,14 +1946,6 @@ static int mmci_probe(struct amba_device *dev,
|
|||
goto host_free;
|
||||
}
|
||||
|
||||
host->pins_default = pinctrl_lookup_state(host->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
if (IS_ERR(host->pins_default)) {
|
||||
dev_err(mmc_dev(mmc), "Can't select default pins\n");
|
||||
ret = PTR_ERR(host->pins_default);
|
||||
goto host_free;
|
||||
}
|
||||
|
||||
host->pins_opendrain = pinctrl_lookup_state(host->pinctrl,
|
||||
MMCI_PINCTRL_STATE_OPENDRAIN);
|
||||
if (IS_ERR(host->pins_opendrain)) {
|
||||
|
@ -2062,17 +2123,18 @@ static int mmci_probe(struct amba_device *dev,
|
|||
* silently of these do not exist
|
||||
*/
|
||||
if (!np) {
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_disable;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto clk_disable;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED,
|
||||
DRIVER_NAME " (cmd)", host);
|
||||
ret = devm_request_threaded_irq(&dev->dev, dev->irq[0], mmci_irq,
|
||||
mmci_irq_thread, IRQF_SHARED,
|
||||
DRIVER_NAME " (cmd)", host);
|
||||
if (ret)
|
||||
goto clk_disable;
|
||||
|
||||
|
@ -2203,7 +2265,7 @@ static int mmci_runtime_resume(struct device *dev)
|
|||
struct mmci_host *host = mmc_priv(mmc);
|
||||
clk_prepare_enable(host->clk);
|
||||
mmci_restore(host);
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
pinctrl_select_default_state(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -279,7 +279,11 @@ struct mmci_host;
|
|||
* @stm32_clkdiv: true if using a STM32-specific clock divider algorithm
|
||||
* @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
|
||||
* @datactrl_mask_sdio: SDIO enable mask in datactrl register
|
||||
* @datactrl_blksz: block size in power of two
|
||||
* @datactrl_blocksz: block size in power of two
|
||||
* @datactrl_any_blocksz: true if block any block sizes are accepted by
|
||||
* hardware, such as with some SDIO traffic that send
|
||||
* odd packets.
|
||||
* @dma_power_of_2: DMA only works with blocks that are a power of 2.
|
||||
* @datactrl_first: true if data must be setup before send command
|
||||
* @datacnt_useless: true if you could not use datacnt register to read
|
||||
* remaining data
|
||||
|
@ -326,6 +330,8 @@ struct variant_data {
|
|||
unsigned int datactrl_mask_ddrmode;
|
||||
unsigned int datactrl_mask_sdio;
|
||||
unsigned int datactrl_blocksz;
|
||||
u8 datactrl_any_blocksz:1;
|
||||
u8 dma_power_of_2:1;
|
||||
u8 datactrl_first:1;
|
||||
u8 datacnt_useless:1;
|
||||
u8 st_sdio:1;
|
||||
|
@ -404,7 +410,6 @@ struct mmci_host {
|
|||
struct mmci_host_ops *ops;
|
||||
struct variant_data *variant;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_opendrain;
|
||||
|
||||
u8 hw_designer;
|
||||
|
@ -412,6 +417,7 @@ struct mmci_host {
|
|||
|
||||
struct timer_list timer;
|
||||
unsigned int oldstat;
|
||||
u32 irq_action;
|
||||
|
||||
/* pio stuff */
|
||||
struct sg_mapping_iter sg_miter;
|
||||
|
|
|
@ -2194,8 +2194,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto host_free;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto host_free;
|
||||
|
|
|
@ -696,16 +696,14 @@ static int mvsd_probe(struct platform_device *pdev)
|
|||
struct mmc_host *mmc = NULL;
|
||||
struct mvsd_host *host = NULL;
|
||||
const struct mbus_dram_target_info *dram;
|
||||
struct resource *r;
|
||||
int ret, irq;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "no DT node\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!r || irq < 0)
|
||||
if (irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
|
||||
|
@ -758,7 +756,7 @@ static int mvsd_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
host->base = devm_ioremap_resource(&pdev->dev, r);
|
||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto out;
|
||||
|
|
|
@ -1121,7 +1121,16 @@ static int mxcmci_probe(struct platform_device *pdev)
|
|||
mxcmci_writel(host, host->default_irq_mask, MMC_REG_INT_CNTR);
|
||||
|
||||
if (!host->pdata) {
|
||||
host->dma = dma_request_slave_channel(&pdev->dev, "rx-tx");
|
||||
host->dma = dma_request_chan(&pdev->dev, "rx-tx");
|
||||
if (IS_ERR(host->dma)) {
|
||||
if (PTR_ERR(host->dma) == -EPROBE_DEFER) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto out_clk_put;
|
||||
}
|
||||
|
||||
/* Ignore errors to fall back to PIO mode */
|
||||
host->dma = NULL;
|
||||
}
|
||||
} else {
|
||||
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
||||
if (res) {
|
||||
|
|
|
@ -623,11 +623,11 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx");
|
||||
if (!ssp->dmach) {
|
||||
ssp->dmach = dma_request_chan(&pdev->dev, "rx-tx");
|
||||
if (IS_ERR(ssp->dmach)) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"%s: failed to request dma\n", __func__);
|
||||
ret = -ENODEV;
|
||||
ret = PTR_ERR(ssp->dmach);
|
||||
goto out_clk_disable;
|
||||
}
|
||||
|
||||
|
|
|
@ -1605,12 +1605,6 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host)
|
|||
ret = PTR_ERR(p);
|
||||
goto err_free_irq;
|
||||
}
|
||||
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) {
|
||||
dev_info(host->dev, "missing default pinctrl state\n");
|
||||
devm_pinctrl_put(p);
|
||||
ret = -EINVAL;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_IDLE))) {
|
||||
dev_info(host->dev, "missing idle pinctrl state\n");
|
||||
|
@ -2153,14 +2147,14 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
|
|||
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
|
||||
(host->flags & HSMMC_SDIO_IRQ_ENABLED)) {
|
||||
|
||||
pinctrl_pm_select_default_state(host->dev);
|
||||
pinctrl_select_default_state(host->dev);
|
||||
|
||||
/* irq lost, if pinmux incorrect */
|
||||
OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR);
|
||||
OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN);
|
||||
OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN);
|
||||
} else {
|
||||
pinctrl_pm_select_default_state(host->dev);
|
||||
pinctrl_select_default_state(host->dev);
|
||||
}
|
||||
spin_unlock_irqrestore(&host->irq_lock, flags);
|
||||
return 0;
|
||||
|
|
|
@ -616,10 +616,10 @@ static int owl_mmc_probe(struct platform_device *pdev)
|
|||
|
||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
|
||||
owl_host->dma = dma_request_slave_channel(&pdev->dev, "mmc");
|
||||
if (!owl_host->dma) {
|
||||
owl_host->dma = dma_request_chan(&pdev->dev, "mmc");
|
||||
if (IS_ERR(owl_host->dma)) {
|
||||
dev_err(owl_host->dev, "Failed to get external DMA channel.\n");
|
||||
ret = -ENXIO;
|
||||
ret = PTR_ERR(owl_host->dma);
|
||||
goto err_free_host;
|
||||
}
|
||||
|
||||
|
|
|
@ -710,17 +710,19 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, mmc);
|
||||
|
||||
host->dma_chan_rx = dma_request_slave_channel(dev, "rx");
|
||||
if (host->dma_chan_rx == NULL) {
|
||||
host->dma_chan_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(host->dma_chan_rx)) {
|
||||
dev_err(dev, "unable to request rx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
ret = PTR_ERR(host->dma_chan_rx);
|
||||
host->dma_chan_rx = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
host->dma_chan_tx = dma_request_slave_channel(dev, "tx");
|
||||
if (host->dma_chan_tx == NULL) {
|
||||
host->dma_chan_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(host->dma_chan_tx)) {
|
||||
dev_err(dev, "unable to request tx dma channel\n");
|
||||
ret = -ENODEV;
|
||||
ret = PTR_ERR(host->dma_chan_tx);
|
||||
host->dma_chan_tx = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -734,22 +736,22 @@ static int pxamci_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
/* FIXME: should we pass detection delay to debounce? */
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
|
||||
if (ret && ret != -ENOENT) {
|
||||
dev_err(dev, "Failed requesting gpio_cd\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0, NULL);
|
||||
if (!host->pdata->gpio_card_ro_invert)
|
||||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0);
|
||||
if (ret && ret != -ENOENT) {
|
||||
dev_err(dev, "Failed requesting gpio_ro\n");
|
||||
goto out;
|
||||
}
|
||||
if (!ret) {
|
||||
if (!ret)
|
||||
host->use_ro_gpio = true;
|
||||
mmc->caps2 |= host->pdata->gpio_card_ro_invert ?
|
||||
0 : MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
if (host->pdata->init)
|
||||
host->pdata->init(dev, pxamci_detect_irq, mmc);
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
struct renesas_sdhi_scc {
|
||||
unsigned long clk_rate; /* clock rate for SDR104 */
|
||||
u32 tap; /* sampling clock position for SDR104 */
|
||||
u32 tap_hs400; /* sampling clock position for HS400 */
|
||||
u32 tap; /* sampling clock position for SDR104/HS400 (8 TAP) */
|
||||
u32 tap_hs400_4tap; /* sampling clock position for HS400 (4 TAP) */
|
||||
};
|
||||
|
||||
struct renesas_sdhi_of_data {
|
||||
|
@ -33,6 +33,11 @@ struct renesas_sdhi_of_data {
|
|||
unsigned short max_segs;
|
||||
};
|
||||
|
||||
struct renesas_sdhi_quirks {
|
||||
bool hs400_disabled;
|
||||
bool hs400_4taps;
|
||||
};
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
enum dma_slave_buswidth dma_buswidth;
|
||||
bool (*filter)(struct dma_chan *chan, void *arg);
|
||||
|
@ -46,6 +51,7 @@ struct renesas_sdhi {
|
|||
struct clk *clk_cd;
|
||||
struct tmio_mmc_data mmc_data;
|
||||
struct tmio_mmc_dma dma_priv;
|
||||
const struct renesas_sdhi_quirks *quirks;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default, *pins_uhs;
|
||||
void __iomem *scc_ctl;
|
||||
|
|
|
@ -46,11 +46,6 @@
|
|||
#define SDHI_VER_GEN3_SD 0xcc10
|
||||
#define SDHI_VER_GEN3_SDMMC 0xcd10
|
||||
|
||||
struct renesas_sdhi_quirks {
|
||||
bool hs400_disabled;
|
||||
bool hs400_4taps;
|
||||
};
|
||||
|
||||
static void renesas_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
|
||||
{
|
||||
u32 val;
|
||||
|
@ -355,7 +350,7 @@ static void renesas_sdhi_hs400_complete(struct tmio_mmc_host *host)
|
|||
0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
|
||||
|
||||
|
||||
if (host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400)
|
||||
if (priv->quirks && priv->quirks->hs400_4taps)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
|
||||
host->tap_set / 2);
|
||||
|
||||
|
@ -493,7 +488,7 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
|||
static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
bool use_4tap = host->pdata->flags & TMIO_MMC_HAVE_4TAP_HS400;
|
||||
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
|
||||
|
||||
/*
|
||||
* Skip checking SCC errors when running on 4 taps in HS400 mode as
|
||||
|
@ -627,10 +622,10 @@ static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
|||
};
|
||||
|
||||
static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||
{ .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
||||
{ /* Sentinel. */ },
|
||||
};
|
||||
|
@ -665,6 +660,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->quirks = quirks;
|
||||
mmc_data = &priv->mmc_data;
|
||||
dma_priv = &priv->dma_priv;
|
||||
|
||||
|
@ -724,9 +720,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
if (quirks && quirks->hs400_disabled)
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
|
||||
|
||||
if (quirks && quirks->hs400_4taps)
|
||||
mmc_data->flags |= TMIO_MMC_HAVE_4TAP_HS400;
|
||||
|
||||
/* For some SoC, we disable internal WP. GPIO may override this */
|
||||
if (mmc_can_gpio_ro(host->mmc))
|
||||
mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT;
|
||||
|
@ -800,20 +793,23 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
|
||||
MMC_CAP2_HS400_1_8V))) {
|
||||
const struct renesas_sdhi_scc *taps = of_data->taps;
|
||||
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
|
||||
bool hit = false;
|
||||
|
||||
for (i = 0; i < of_data->taps_num; i++) {
|
||||
if (taps[i].clk_rate == 0 ||
|
||||
taps[i].clk_rate == host->mmc->f_max) {
|
||||
priv->scc_tappos = taps->tap;
|
||||
priv->scc_tappos_hs400 = taps->tap_hs400;
|
||||
priv->scc_tappos_hs400 = use_4tap ?
|
||||
taps->tap_hs400_4tap :
|
||||
taps->tap;
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hit)
|
||||
dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
|
||||
dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n");
|
||||
|
||||
host->init_tuning = renesas_sdhi_init_tuning;
|
||||
host->prepare_tuning = renesas_sdhi_prepare_tuning;
|
||||
|
|
|
@ -82,7 +82,7 @@ static struct renesas_sdhi_scc rcar_gen3_scc_taps[] = {
|
|||
{
|
||||
.clk_rate = 0,
|
||||
.tap = 0x00000300,
|
||||
.tap_hs400 = 0x00000704,
|
||||
.tap_hs400_4tap = 0x00000100,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -298,38 +298,23 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
|
|||
* Whitelist of specific R-Car Gen3 SoC ES versions to use this DMAC
|
||||
* implementation as others may use a different implementation.
|
||||
*/
|
||||
static const struct soc_device_attribute soc_whitelist[] = {
|
||||
/* specific ones */
|
||||
static const struct soc_device_attribute soc_dma_quirks[] = {
|
||||
{ .soc_id = "r7s9210",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY) },
|
||||
{ .soc_id = "r8a7795", .revision = "ES1.*",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.0",
|
||||
.data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) },
|
||||
/* generic ones */
|
||||
{ .soc_id = "r8a774a1" },
|
||||
{ .soc_id = "r8a774b1" },
|
||||
{ .soc_id = "r8a774c0" },
|
||||
{ .soc_id = "r8a77470" },
|
||||
{ .soc_id = "r8a7795" },
|
||||
{ .soc_id = "r8a7796" },
|
||||
{ .soc_id = "r8a77965" },
|
||||
{ .soc_id = "r8a77970" },
|
||||
{ .soc_id = "r8a77980" },
|
||||
{ .soc_id = "r8a77990" },
|
||||
{ .soc_id = "r8a77995" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct soc_device_attribute *soc = soc_device_match(soc_whitelist);
|
||||
const struct soc_device_attribute *soc = soc_device_match(soc_dma_quirks);
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
if (!soc)
|
||||
return -ENODEV;
|
||||
|
||||
global_flags |= (unsigned long)soc->data;
|
||||
if (soc)
|
||||
global_flags |= (unsigned long)soc->data;
|
||||
|
||||
dev->dma_parms = devm_kzalloc(dev, sizeof(*dev->dma_parms), GFP_KERNEL);
|
||||
if (!dev->dma_parms)
|
||||
|
|
|
@ -1505,14 +1505,14 @@ static int s3cmci_probe_pdata(struct s3cmci_host *host)
|
|||
mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
/* If we get -ENOENT we have no card detect GPIO line */
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0, NULL);
|
||||
ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0);
|
||||
if (ret != -ENOENT) {
|
||||
dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
|
||||
ret);
|
||||
|
|
|
@ -752,7 +752,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
|
|||
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
|
||||
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
|
||||
|
||||
err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL);
|
||||
err = mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0);
|
||||
if (err) {
|
||||
if (err == -EPROBE_DEFER)
|
||||
goto err_free;
|
||||
|
|
|
@ -9,29 +9,236 @@
|
|||
#include <linux/mmc/host.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
static const struct sdhci_ops sdhci_brcmstb_ops = {
|
||||
#define SDHCI_VENDOR 0x78
|
||||
#define SDHCI_VENDOR_ENHANCED_STRB 0x1
|
||||
|
||||
#define BRCMSTB_PRIV_FLAGS_NO_64BIT BIT(0)
|
||||
#define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT BIT(1)
|
||||
|
||||
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
|
||||
|
||||
struct sdhci_brcmstb_priv {
|
||||
void __iomem *cfg_regs;
|
||||
bool has_cqe;
|
||||
};
|
||||
|
||||
struct brcmstb_match_priv {
|
||||
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
|
||||
struct sdhci_ops *ops;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
||||
u32 reg;
|
||||
|
||||
dev_dbg(mmc_dev(mmc), "%s(): Setting HS400-Enhanced-Strobe mode\n",
|
||||
__func__);
|
||||
reg = readl(host->ioaddr + SDHCI_VENDOR);
|
||||
if (ios->enhanced_strobe)
|
||||
reg |= SDHCI_VENDOR_ENHANCED_STRB;
|
||||
else
|
||||
reg &= ~SDHCI_VENDOR_ENHANCED_STRB;
|
||||
writel(reg, host->ioaddr + SDHCI_VENDOR);
|
||||
}
|
||||
|
||||
static void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
|
||||
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
if (clock == 0)
|
||||
return;
|
||||
|
||||
sdhci_enable_clk(host, clk);
|
||||
}
|
||||
|
||||
static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "%s: Setting UHS signaling for %d timing\n",
|
||||
__func__, timing);
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
/* Select Bus Speed Mode for host */
|
||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||
if ((timing == MMC_TIMING_MMC_HS200) ||
|
||||
(timing == MMC_TIMING_UHS_SDR104))
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
else if (timing == MMC_TIMING_UHS_SDR12)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
|
||||
else if (timing == MMC_TIMING_SD_HS ||
|
||||
timing == MMC_TIMING_MMC_HS ||
|
||||
timing == MMC_TIMING_UHS_SDR25)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
||||
else if (timing == MMC_TIMING_UHS_SDR50)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
|
||||
else if ((timing == MMC_TIMING_UHS_DDR50) ||
|
||||
(timing == MMC_TIMING_MMC_DDR52))
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||
else if (timing == MMC_TIMING_MMC_HS400)
|
||||
ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
|
||||
{
|
||||
sdhci_dumpregs(mmc_priv(mmc));
|
||||
}
|
||||
|
||||
static void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
u32 reg;
|
||||
|
||||
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
while (reg & SDHCI_DATA_AVAILABLE) {
|
||||
sdhci_readl(host, SDHCI_BUFFER);
|
||||
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||
}
|
||||
|
||||
sdhci_cqe_enable(mmc);
|
||||
}
|
||||
|
||||
static const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops = {
|
||||
.enable = sdhci_brcmstb_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
.dumpregs = sdhci_brcmstb_dumpregs,
|
||||
};
|
||||
|
||||
static struct sdhci_ops sdhci_brcmstb_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_brcmstb_pdata = {
|
||||
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
|
||||
.set_clock = sdhci_brcmstb_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling,
|
||||
};
|
||||
|
||||
static struct brcmstb_match_priv match_priv_7425 = {
|
||||
.flags = BRCMSTB_PRIV_FLAGS_NO_64BIT |
|
||||
BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT,
|
||||
.ops = &sdhci_brcmstb_ops,
|
||||
};
|
||||
|
||||
static struct brcmstb_match_priv match_priv_7445 = {
|
||||
.flags = BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT,
|
||||
.ops = &sdhci_brcmstb_ops,
|
||||
};
|
||||
|
||||
static const struct brcmstb_match_priv match_priv_7216 = {
|
||||
.hs400es = sdhci_brcmstb_hs400es,
|
||||
.ops = &sdhci_brcmstb_ops_7216,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_brcm_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
|
||||
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
|
||||
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
|
||||
{},
|
||||
};
|
||||
|
||||
static u32 sdhci_brcmstb_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 int sdhci_brcmstb_add_host(struct sdhci_host *host,
|
||||
struct sdhci_brcmstb_priv *priv)
|
||||
{
|
||||
struct cqhci_host *cq_host;
|
||||
bool dma64;
|
||||
int ret;
|
||||
|
||||
if (!priv->has_cqe)
|
||||
return sdhci_add_host(host);
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "CQE is enabled\n");
|
||||
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
ret = sdhci_setup_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cq_host = devm_kzalloc(mmc_dev(host->mmc),
|
||||
sizeof(*cq_host), GFP_KERNEL);
|
||||
if (!cq_host) {
|
||||
ret = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cq_host->mmio = host->ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR;
|
||||
cq_host->ops = &sdhci_brcmstb_cqhci_ops;
|
||||
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
if (dma64) {
|
||||
dev_dbg(mmc_dev(host->mmc), "Using 64 bit DMA\n");
|
||||
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
|
||||
cq_host->quirks |= CQHCI_QUIRK_SHORT_TXFR_DESC_SZ;
|
||||
}
|
||||
|
||||
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 int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
const struct brcmstb_match_priv *match_priv;
|
||||
struct sdhci_pltfm_data brcmstb_pdata;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
const struct of_device_id *match;
|
||||
struct sdhci_brcmstb_priv *priv;
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
bool has_cqe = false;
|
||||
struct clk *clk;
|
||||
int res;
|
||||
|
||||
match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node);
|
||||
match_priv = match->data;
|
||||
|
||||
dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible);
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
if (PTR_ERR(clk) == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
dev_err(&pdev->dev, "Clock not found in Device Tree\n");
|
||||
clk = NULL;
|
||||
}
|
||||
|
@ -39,36 +246,64 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
|||
if (res)
|
||||
return res;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0);
|
||||
memset(&brcmstb_pdata, 0, sizeof(brcmstb_pdata));
|
||||
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
|
||||
has_cqe = true;
|
||||
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
|
||||
}
|
||||
brcmstb_pdata.ops = match_priv->ops;
|
||||
host = sdhci_pltfm_init(pdev, &brcmstb_pdata,
|
||||
sizeof(struct sdhci_brcmstb_priv));
|
||||
if (IS_ERR(host)) {
|
||||
res = PTR_ERR(host);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->has_cqe = has_cqe;
|
||||
|
||||
/* Map in the non-standard CFG registers */
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
|
||||
if (IS_ERR(priv->cfg_regs)) {
|
||||
res = PTR_ERR(priv->cfg_regs);
|
||||
goto err;
|
||||
}
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
res = mmc_of_parse(host->mmc);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* If the chip has enhanced strobe and it's enabled, add
|
||||
* callback
|
||||
*/
|
||||
if (match_priv->hs400es &&
|
||||
(host->mmc->caps2 & MMC_CAP2_HS400_ES))
|
||||
host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
|
||||
|
||||
/*
|
||||
* Supply the existing CAPS, but clear the UHS modes. This
|
||||
* will allow these modes to be specified by device tree
|
||||
* properties through mmc_of_parse().
|
||||
*/
|
||||
host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm7425-sdhci"))
|
||||
if (match_priv->flags & BRCMSTB_PRIV_FLAGS_NO_64BIT)
|
||||
host->caps &= ~SDHCI_CAN_64BIT;
|
||||
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
|
||||
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
|
||||
SDHCI_SUPPORT_DDR50);
|
||||
host->quirks |= SDHCI_QUIRK_MISSING_CAPS |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
SDHCI_SUPPORT_DDR50);
|
||||
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
|
||||
|
||||
res = sdhci_add_host(host);
|
||||
if (match_priv->flags & BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
res = sdhci_brcmstb_add_host(host, priv);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
pltfm_host->clk = clk;
|
||||
return res;
|
||||
|
||||
|
@ -79,11 +314,15 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
|||
return res;
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_brcm_of_match[] = {
|
||||
{ .compatible = "brcm,bcm7425-sdhci" },
|
||||
{ .compatible = "brcm,bcm7445-sdhci" },
|
||||
{},
|
||||
};
|
||||
static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to shutdown\n");
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
|
||||
|
||||
static struct platform_driver sdhci_brcmstb_driver = {
|
||||
|
@ -94,6 +333,7 @@ static struct platform_driver sdhci_brcmstb_driver = {
|
|||
},
|
||||
.probe = sdhci_brcmstb_probe,
|
||||
.remove = sdhci_pltfm_unregister,
|
||||
.shutdown = sdhci_brcmstb_shutdown,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_brcmstb_driver);
|
||||
|
|
|
@ -158,7 +158,7 @@ static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void *sdhci_cdns_priv(struct sdhci_host *host)
|
||||
static void *sdhci_cdns_priv(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
|
|
|
@ -224,7 +224,6 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = {
|
|||
struct pltfm_imx_data {
|
||||
u32 scratchpad;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_100mhz;
|
||||
struct pinctrl_state *pins_200mhz;
|
||||
const struct esdhc_soc_data *socdata;
|
||||
|
@ -951,7 +950,6 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs);
|
||||
|
||||
if (IS_ERR(imx_data->pinctrl) ||
|
||||
IS_ERR(imx_data->pins_default) ||
|
||||
IS_ERR(imx_data->pins_100mhz) ||
|
||||
IS_ERR(imx_data->pins_200mhz))
|
||||
return -EINVAL;
|
||||
|
@ -968,7 +966,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
|
|||
break;
|
||||
default:
|
||||
/* back to default state for other legacy timing */
|
||||
pinctrl = imx_data->pins_default;
|
||||
return pinctrl_select_default_state(mmc_dev(host->mmc));
|
||||
}
|
||||
|
||||
return pinctrl_select_state(imx_data->pinctrl, pinctrl);
|
||||
|
@ -1338,7 +1336,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev,
|
|||
|
||||
mmc_of_parse_voltage(np, &host->ocr_mask);
|
||||
|
||||
if (esdhc_is_usdhc(imx_data) && !IS_ERR(imx_data->pins_default)) {
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
||||
ESDHC_PINCTRL_STATE_100MHZ);
|
||||
imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl,
|
||||
|
@ -1381,19 +1379,20 @@ static int sdhci_esdhc_imx_probe_nondt(struct platform_device *pdev,
|
|||
host->mmc->parent->platform_data);
|
||||
/* write_protect */
|
||||
if (boarddata->wp_type == ESDHC_WP_GPIO) {
|
||||
err = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0, NULL);
|
||||
host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
|
||||
err = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request write-protect gpio!\n");
|
||||
return err;
|
||||
}
|
||||
host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
|
||||
}
|
||||
|
||||
/* card_detect */
|
||||
switch (boarddata->cd_type) {
|
||||
case ESDHC_CD_GPIO:
|
||||
err = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL);
|
||||
err = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"failed to request card-detect gpio!\n");
|
||||
|
@ -1492,11 +1491,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
goto disable_ahb_clk;
|
||||
}
|
||||
|
||||
imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
if (IS_ERR(imx_data->pins_default))
|
||||
dev_warn(mmc_dev(host->mmc), "could not get default state\n");
|
||||
|
||||
if (esdhc_is_usdhc(imx_data)) {
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
|
||||
|
|
|
@ -242,15 +242,12 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int irq, ret = 0;
|
||||
struct f_sdhost_priv *priv;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "%s: no irq specified\n", __func__);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
}
|
||||
|
||||
host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv));
|
||||
if (IS_ERR(host))
|
||||
|
@ -280,8 +277,7 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev)
|
|||
host->ops = &sdhci_milbeaut_ops;
|
||||
host->irq = irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
goto err;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
#define CORE_MCI_VERSION 0x50
|
||||
#define CORE_VERSION_MAJOR_SHIFT 28
|
||||
|
@ -122,6 +123,10 @@
|
|||
#define msm_host_writel(msm_host, val, host, offset) \
|
||||
msm_host->var_ops->msm_writel_relaxed(val, host, offset)
|
||||
|
||||
/* CQHCI vendor specific registers */
|
||||
#define CQHCI_VENDOR_CFG1 0xA00
|
||||
#define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13)
|
||||
|
||||
struct sdhci_msm_offset {
|
||||
u32 core_hc_mode;
|
||||
u32 core_mci_data_cnt;
|
||||
|
@ -1567,6 +1572,127 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
__sdhci_msm_set_clock(host, clock);
|
||||
}
|
||||
|
||||
/*****************************************************************************\
|
||||
* *
|
||||
* MSM Command Queue Engine (CQE) *
|
||||
* *
|
||||
\*****************************************************************************/
|
||||
|
||||
static u32 sdhci_msm_cqe_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;
|
||||
}
|
||||
|
||||
void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
u32 ctrl;
|
||||
|
||||
/*
|
||||
* When CQE is halted, the legacy SDHCI path operates only
|
||||
* on 16-byte descriptors in 64bit mode.
|
||||
*/
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
host->desc_sz = 16;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/*
|
||||
* During CQE command transfers, command complete bit gets latched.
|
||||
* So s/w should clear command complete interrupt status when CQE is
|
||||
* either halted or disabled. Otherwise unexpected SDCHI legacy
|
||||
* interrupt gets triggered when CQE is halted/disabled.
|
||||
*/
|
||||
ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
|
||||
ctrl |= SDHCI_INT_RESPONSE;
|
||||
sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
sdhci_cqe_disable(mmc, recovery);
|
||||
}
|
||||
|
||||
static const struct cqhci_host_ops sdhci_msm_cqhci_ops = {
|
||||
.enable = sdhci_cqe_enable,
|
||||
.disable = sdhci_msm_cqe_disable,
|
||||
};
|
||||
|
||||
static int sdhci_msm_cqe_add_host(struct sdhci_host *host,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct cqhci_host *cq_host;
|
||||
bool dma64;
|
||||
u32 cqcfg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* When CQE is halted, SDHC operates only on 16byte ADMA descriptors.
|
||||
* So ensure ADMA table is allocated for 16byte descriptors.
|
||||
*/
|
||||
if (host->caps & SDHCI_CAN_64BIT)
|
||||
host->alloc_desc_sz = 16;
|
||||
|
||||
ret = sdhci_setup_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cq_host = cqhci_pltfm_init(pdev);
|
||||
if (IS_ERR(cq_host)) {
|
||||
ret = PTR_ERR(cq_host);
|
||||
dev_err(&pdev->dev, "cqhci-pltfm init: failed: %d\n", ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
msm_host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
|
||||
cq_host->ops = &sdhci_msm_cqhci_ops;
|
||||
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
ret = cqhci_init(cq_host, host->mmc, dma64);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n",
|
||||
mmc_hostname(host->mmc), ret);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Disable cqe reset due to cqe enable signal */
|
||||
cqcfg = cqhci_readl(cq_host, CQHCI_VENDOR_CFG1);
|
||||
cqcfg |= CQHCI_VENDOR_DIS_RST_ON_CQ_EN;
|
||||
cqhci_writel(cq_host, cqcfg, CQHCI_VENDOR_CFG1);
|
||||
|
||||
/*
|
||||
* SDHC expects 12byte ADMA descriptors till CQE is enabled.
|
||||
* So limit desc_sz to 12 so that the data commands that are sent
|
||||
* during card initialization (before CQE gets enabled) would
|
||||
* get executed without any issues.
|
||||
*/
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA)
|
||||
host->desc_sz = 12;
|
||||
|
||||
ret = __sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
dev_info(&pdev->dev, "%s: CQE init: success\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return ret;
|
||||
|
||||
cleanup:
|
||||
sdhci_cleanup_host(host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform specific register write functions. This is so that, if any
|
||||
* register write needs to be followed up by platform specific actions,
|
||||
|
@ -1731,6 +1857,7 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
|||
.set_uhs_signaling = sdhci_msm_set_uhs_signaling,
|
||||
.write_w = sdhci_msm_writew,
|
||||
.write_b = sdhci_msm_writeb,
|
||||
.irq = sdhci_msm_cqe_irq,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||
|
@ -1746,7 +1873,6 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_msm_host *msm_host;
|
||||
struct resource *core_memres;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
u16 host_version, core_minor;
|
||||
|
@ -1754,6 +1880,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
u8 core_major;
|
||||
const struct sdhci_msm_offset *msm_offset;
|
||||
const struct sdhci_msm_variant_info *var_info;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
|
||||
if (IS_ERR(host))
|
||||
|
@ -1847,10 +1974,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
if (!msm_host->mci_removed) {
|
||||
core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
msm_host->core_mem = devm_ioremap_resource(&pdev->dev,
|
||||
core_memres);
|
||||
|
||||
msm_host->core_mem = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(msm_host->core_mem)) {
|
||||
ret = PTR_ERR(msm_host->core_mem);
|
||||
goto clk_disable;
|
||||
|
@ -1952,7 +2076,10 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
|
||||
host->mmc_host_ops.execute_tuning = sdhci_msm_execute_tuning;
|
||||
ret = sdhci_add_host(host);
|
||||
if (of_property_read_bool(node, "supports-cqe"))
|
||||
ret = sdhci_msm_cqe_add_host(host, pdev);
|
||||
else
|
||||
ret = sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto pm_runtime_disable;
|
||||
sdhci_msm_set_regulator_caps(msm_host);
|
||||
|
|
|
@ -33,7 +33,14 @@
|
|||
|
||||
#define SDHCI_AT91_PRESET_COMMON_CONF 0x400 /* drv type B, programmable clock mode */
|
||||
|
||||
struct sdhci_at91_soc_data {
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
bool baseclk_is_generated_internally;
|
||||
unsigned int divider_for_baseclk;
|
||||
};
|
||||
|
||||
struct sdhci_at91_priv {
|
||||
const struct sdhci_at91_soc_data *soc_data;
|
||||
struct clk *hclock;
|
||||
struct clk *gck;
|
||||
struct clk *mainck;
|
||||
|
@ -141,12 +148,24 @@ static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
|
|||
.set_power = sdhci_at91_set_power,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data soc_data_sama5d2 = {
|
||||
static const struct sdhci_pltfm_data sdhci_sama5d2_pdata = {
|
||||
.ops = &sdhci_at91_sama5d2_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_at91_soc_data soc_data_sama5d2 = {
|
||||
.pdata = &sdhci_sama5d2_pdata,
|
||||
.baseclk_is_generated_internally = false,
|
||||
};
|
||||
|
||||
static const struct sdhci_at91_soc_data soc_data_sam9x60 = {
|
||||
.pdata = &sdhci_sama5d2_pdata,
|
||||
.baseclk_is_generated_internally = true,
|
||||
.divider_for_baseclk = 2,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_at91_dt_match[] = {
|
||||
{ .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 },
|
||||
{ .compatible = "microchip,sam9x60-sdhci", .data = &soc_data_sam9x60 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match);
|
||||
|
@ -156,50 +175,37 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
|
|||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
unsigned int caps0, caps1;
|
||||
unsigned int clk_base, clk_mul;
|
||||
unsigned int gck_rate, real_gck_rate;
|
||||
unsigned int gck_rate, clk_base_rate;
|
||||
unsigned int preset_div;
|
||||
|
||||
/*
|
||||
* The mult clock is provided by as a generated clock by the PMC
|
||||
* controller. In order to set the rate of gck, we have to get the
|
||||
* base clock rate and the clock mult from capabilities.
|
||||
*/
|
||||
clk_prepare_enable(priv->hclock);
|
||||
caps0 = readl(host->ioaddr + SDHCI_CAPABILITIES);
|
||||
caps1 = readl(host->ioaddr + SDHCI_CAPABILITIES_1);
|
||||
clk_base = (caps0 & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
|
||||
clk_mul = (caps1 & SDHCI_CLOCK_MUL_MASK) >> SDHCI_CLOCK_MUL_SHIFT;
|
||||
gck_rate = clk_base * 1000000 * (clk_mul + 1);
|
||||
ret = clk_set_rate(priv->gck, gck_rate);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to set gck");
|
||||
clk_disable_unprepare(priv->hclock);
|
||||
return ret;
|
||||
}
|
||||
/*
|
||||
* We need to check if we have the requested rate for gck because in
|
||||
* some cases this rate could be not supported. If it happens, the rate
|
||||
* is the closest one gck can provide. We have to update the value
|
||||
* of clk mul.
|
||||
*/
|
||||
real_gck_rate = clk_get_rate(priv->gck);
|
||||
if (real_gck_rate != gck_rate) {
|
||||
clk_mul = real_gck_rate / (clk_base * 1000000) - 1;
|
||||
caps1 &= (~SDHCI_CLOCK_MUL_MASK);
|
||||
caps1 |= ((clk_mul << SDHCI_CLOCK_MUL_SHIFT) &
|
||||
SDHCI_CLOCK_MUL_MASK);
|
||||
/* Set capabilities in r/w mode. */
|
||||
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN,
|
||||
host->ioaddr + SDMMC_CACR);
|
||||
writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1);
|
||||
/* Set capabilities in ro mode. */
|
||||
writel(0, host->ioaddr + SDMMC_CACR);
|
||||
dev_info(dev, "update clk mul to %u as gck rate is %u Hz\n",
|
||||
clk_mul, real_gck_rate);
|
||||
}
|
||||
|
||||
gck_rate = clk_get_rate(priv->gck);
|
||||
if (priv->soc_data->baseclk_is_generated_internally)
|
||||
clk_base_rate = gck_rate / priv->soc_data->divider_for_baseclk;
|
||||
else
|
||||
clk_base_rate = clk_get_rate(priv->mainck);
|
||||
|
||||
clk_base = clk_base_rate / 1000000;
|
||||
clk_mul = gck_rate / clk_base_rate - 1;
|
||||
|
||||
caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK;
|
||||
caps0 |= (clk_base << SDHCI_CLOCK_BASE_SHIFT) & SDHCI_CLOCK_V3_BASE_MASK;
|
||||
caps1 &= ~SDHCI_CLOCK_MUL_MASK;
|
||||
caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK;
|
||||
/* Set capabilities in r/w mode. */
|
||||
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
|
||||
writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);
|
||||
writel(caps1, host->ioaddr + SDHCI_CAPABILITIES_1);
|
||||
/* Set capabilities in ro mode. */
|
||||
writel(0, host->ioaddr + SDMMC_CACR);
|
||||
|
||||
dev_info(dev, "update clk mul to %u as gck rate is %u Hz and clk base is %u Hz\n",
|
||||
clk_mul, gck_rate, clk_base_rate);
|
||||
|
||||
/*
|
||||
* We have to set preset values because it depends on the clk_mul
|
||||
|
@ -207,19 +213,19 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
|
|||
* maximum sd clock value is 120 MHz instead of 208 MHz. For that
|
||||
* reason, we need to use presets to support SDR104.
|
||||
*/
|
||||
preset_div = DIV_ROUND_UP(real_gck_rate, 24000000) - 1;
|
||||
preset_div = DIV_ROUND_UP(gck_rate, 24000000) - 1;
|
||||
writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
|
||||
host->ioaddr + SDHCI_PRESET_FOR_SDR12);
|
||||
preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1;
|
||||
preset_div = DIV_ROUND_UP(gck_rate, 50000000) - 1;
|
||||
writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
|
||||
host->ioaddr + SDHCI_PRESET_FOR_SDR25);
|
||||
preset_div = DIV_ROUND_UP(real_gck_rate, 100000000) - 1;
|
||||
preset_div = DIV_ROUND_UP(gck_rate, 100000000) - 1;
|
||||
writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
|
||||
host->ioaddr + SDHCI_PRESET_FOR_SDR50);
|
||||
preset_div = DIV_ROUND_UP(real_gck_rate, 120000000) - 1;
|
||||
preset_div = DIV_ROUND_UP(gck_rate, 120000000) - 1;
|
||||
writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
|
||||
host->ioaddr + SDHCI_PRESET_FOR_SDR104);
|
||||
preset_div = DIV_ROUND_UP(real_gck_rate, 50000000) - 1;
|
||||
preset_div = DIV_ROUND_UP(gck_rate, 50000000) - 1;
|
||||
writew(SDHCI_AT91_PRESET_COMMON_CONF | preset_div,
|
||||
host->ioaddr + SDHCI_PRESET_FOR_DDR50);
|
||||
|
||||
|
@ -314,7 +320,7 @@ static const struct dev_pm_ops sdhci_at91_dev_pm_ops = {
|
|||
static int sdhci_at91_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
const struct sdhci_pltfm_data *soc_data;
|
||||
const struct sdhci_at91_soc_data *soc_data;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_at91_priv *priv;
|
||||
|
@ -325,29 +331,37 @@ static int sdhci_at91_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
soc_data = match->data;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, soc_data, sizeof(*priv));
|
||||
host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*priv));
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->soc_data = soc_data;
|
||||
|
||||
priv->mainck = devm_clk_get(&pdev->dev, "baseclk");
|
||||
if (IS_ERR(priv->mainck)) {
|
||||
dev_err(&pdev->dev, "failed to get baseclk\n");
|
||||
return PTR_ERR(priv->mainck);
|
||||
if (soc_data->baseclk_is_generated_internally) {
|
||||
priv->mainck = NULL;
|
||||
} else {
|
||||
dev_err(&pdev->dev, "failed to get baseclk\n");
|
||||
ret = PTR_ERR(priv->mainck);
|
||||
goto sdhci_pltfm_free;
|
||||
}
|
||||
}
|
||||
|
||||
priv->hclock = devm_clk_get(&pdev->dev, "hclock");
|
||||
if (IS_ERR(priv->hclock)) {
|
||||
dev_err(&pdev->dev, "failed to get hclock\n");
|
||||
return PTR_ERR(priv->hclock);
|
||||
ret = PTR_ERR(priv->hclock);
|
||||
goto sdhci_pltfm_free;
|
||||
}
|
||||
|
||||
priv->gck = devm_clk_get(&pdev->dev, "multclk");
|
||||
if (IS_ERR(priv->gck)) {
|
||||
dev_err(&pdev->dev, "failed to get multclk\n");
|
||||
return PTR_ERR(priv->gck);
|
||||
ret = PTR_ERR(priv->gck);
|
||||
goto sdhci_pltfm_free;
|
||||
}
|
||||
|
||||
ret = sdhci_at91_set_clks_presets(&pdev->dev);
|
||||
|
|
|
@ -173,6 +173,9 @@ static u16 esdhc_readw_fixup(struct sdhci_host *host,
|
|||
u16 ret;
|
||||
int shift = (spec_reg & 0x2) * 8;
|
||||
|
||||
if (spec_reg == SDHCI_TRANSFER_MODE)
|
||||
return pltfm_host->xfer_mode_shadow;
|
||||
|
||||
if (spec_reg == SDHCI_HOST_VERSION)
|
||||
ret = value & 0xffff;
|
||||
else
|
||||
|
@ -562,32 +565,46 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host)
|
|||
|
||||
static void esdhc_clock_enable(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
ktime_t timeout;
|
||||
u32 val, clk_en;
|
||||
|
||||
clk_en = ESDHC_CLOCK_SDCLKEN;
|
||||
|
||||
/*
|
||||
* IPGEN/HCKEN/PEREN bits exist on eSDHC whose vendor version
|
||||
* is 2.2 or lower.
|
||||
*/
|
||||
if (esdhc->vendor_ver <= VENDOR_V_22)
|
||||
clk_en |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
||||
ESDHC_CLOCK_PEREN);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
if (enable)
|
||||
val |= ESDHC_CLOCK_SDCLKEN;
|
||||
val |= clk_en;
|
||||
else
|
||||
val &= ~ESDHC_CLOCK_SDCLKEN;
|
||||
val &= ~clk_en;
|
||||
|
||||
sdhci_writel(host, val, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
/* Wait max 20 ms */
|
||||
/*
|
||||
* Wait max 20 ms. If vendor version is 2.2 or lower, do not
|
||||
* wait clock stable bit which does not exist.
|
||||
*/
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
val = ESDHC_CLOCK_STABLE;
|
||||
while (1) {
|
||||
while (esdhc->vendor_ver > VENDOR_V_22) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & val)
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
break;
|
||||
}
|
||||
udelay(10);
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,77 +638,97 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
int pre_div = 1;
|
||||
int div = 1;
|
||||
int division;
|
||||
unsigned int pre_div = 1, div = 1;
|
||||
unsigned int clock_fixup = 0;
|
||||
ktime_t timeout;
|
||||
long fixup = 0;
|
||||
u32 temp;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
if (clock == 0) {
|
||||
host->mmc->actual_clock = 0;
|
||||
esdhc_clock_enable(host, false);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
|
||||
/* Start pre_div at 2 for vendor version < 2.3. */
|
||||
if (esdhc->vendor_ver < VENDOR_V_23)
|
||||
pre_div = 2;
|
||||
|
||||
/* Fix clock value. */
|
||||
if (host->mmc->card && mmc_card_sd(host->mmc->card) &&
|
||||
esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
|
||||
fixup = esdhc->clk_fixup->sd_dflt_max_clk;
|
||||
esdhc->clk_fixup && host->mmc->ios.timing == MMC_TIMING_LEGACY)
|
||||
clock_fixup = esdhc->clk_fixup->sd_dflt_max_clk;
|
||||
else if (esdhc->clk_fixup)
|
||||
fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
|
||||
clock_fixup = esdhc->clk_fixup->max_clk[host->mmc->ios.timing];
|
||||
|
||||
if (fixup && clock > fixup)
|
||||
clock = fixup;
|
||||
if (clock_fixup == 0 || clock < clock_fixup)
|
||||
clock_fixup = clock;
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp &= ~(ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
|
||||
ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK);
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
while (host->max_clk / pre_div / 16 > clock && pre_div < 256)
|
||||
/* Calculate pre_div and div. */
|
||||
while (host->max_clk / pre_div / 16 > clock_fixup && pre_div < 256)
|
||||
pre_div *= 2;
|
||||
|
||||
while (host->max_clk / pre_div / div > clock && div < 16)
|
||||
while (host->max_clk / pre_div / div > clock_fixup && div < 16)
|
||||
div++;
|
||||
|
||||
esdhc->div_ratio = pre_div * div;
|
||||
|
||||
/* Limit clock division for HS400 200MHz clock for quirk. */
|
||||
if (esdhc->quirk_limited_clk_division &&
|
||||
clock == MMC_HS200_MAX_DTR &&
|
||||
(host->mmc->ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
host->flags & SDHCI_HS400_TUNING)) {
|
||||
division = pre_div * div;
|
||||
if (division <= 4) {
|
||||
if (esdhc->div_ratio <= 4) {
|
||||
pre_div = 4;
|
||||
div = 1;
|
||||
} else if (division <= 8) {
|
||||
} else if (esdhc->div_ratio <= 8) {
|
||||
pre_div = 4;
|
||||
div = 2;
|
||||
} else if (division <= 12) {
|
||||
} else if (esdhc->div_ratio <= 12) {
|
||||
pre_div = 4;
|
||||
div = 3;
|
||||
} else {
|
||||
pr_warn("%s: using unsupported clock division.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
}
|
||||
esdhc->div_ratio = pre_div * div;
|
||||
}
|
||||
|
||||
host->mmc->actual_clock = host->max_clk / esdhc->div_ratio;
|
||||
|
||||
dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n",
|
||||
clock, host->max_clk / pre_div / div);
|
||||
host->mmc->actual_clock = host->max_clk / pre_div / div;
|
||||
esdhc->div_ratio = pre_div * div;
|
||||
clock, host->mmc->actual_clock);
|
||||
|
||||
/* Set clock division into register. */
|
||||
pre_div >>= 1;
|
||||
div--;
|
||||
|
||||
esdhc_clock_enable(host, false);
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
|
||||
| (div << ESDHC_DIVIDER_SHIFT)
|
||||
| (pre_div << ESDHC_PREDIV_SHIFT));
|
||||
temp &= ~ESDHC_CLOCK_MASK;
|
||||
temp |= ((div << ESDHC_DIVIDER_SHIFT) |
|
||||
(pre_div << ESDHC_PREDIV_SHIFT));
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
|
||||
/*
|
||||
* Wait max 20 ms. If vendor version is 2.2 or lower, do not
|
||||
* wait clock stable bit which does not exist.
|
||||
*/
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
while (esdhc->vendor_ver > VENDOR_V_22) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
break;
|
||||
}
|
||||
usleep_range(10, 20);
|
||||
}
|
||||
|
||||
/* Additional setting for HS400. */
|
||||
if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400 &&
|
||||
clock == MMC_HS200_MAX_DTR) {
|
||||
temp = sdhci_readl(host, ESDHC_TBCTL);
|
||||
|
@ -711,25 +748,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
esdhc_clock_enable(host, false);
|
||||
esdhc_flush_async_fifo(host);
|
||||
}
|
||||
|
||||
/* Wait max 20 ms */
|
||||
timeout = ktime_add_ms(ktime_get(), 20);
|
||||
while (1) {
|
||||
bool timedout = ktime_after(ktime_get(), timeout);
|
||||
|
||||
if (sdhci_readl(host, ESDHC_PRSSTAT) & ESDHC_CLOCK_STABLE)
|
||||
break;
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return;
|
||||
}
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL);
|
||||
temp |= ESDHC_CLOCK_SDCLKEN;
|
||||
sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL);
|
||||
esdhc_clock_enable(host, true);
|
||||
}
|
||||
|
||||
static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
|
@ -758,23 +777,58 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u32 val;
|
||||
u32 val, bus_width = 0;
|
||||
|
||||
/*
|
||||
* Add delay to make sure all the DMA transfers are finished
|
||||
* for quirk.
|
||||
*/
|
||||
if (esdhc->quirk_delay_before_data_reset &&
|
||||
(mask & SDHCI_RESET_DATA) &&
|
||||
(host->flags & SDHCI_REQ_USE_DMA))
|
||||
mdelay(5);
|
||||
|
||||
/*
|
||||
* Save bus-width for eSDHC whose vendor version is 2.2
|
||||
* or lower for data reset.
|
||||
*/
|
||||
if ((mask & SDHCI_RESET_DATA) &&
|
||||
(esdhc->vendor_ver <= VENDOR_V_22)) {
|
||||
val = sdhci_readl(host, ESDHC_PROCTL);
|
||||
bus_width = val & ESDHC_CTRL_BUSWIDTH_MASK;
|
||||
}
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
/*
|
||||
* Restore bus-width setting and interrupt registers for eSDHC
|
||||
* whose vendor version is 2.2 or lower for data reset.
|
||||
*/
|
||||
if ((mask & SDHCI_RESET_DATA) &&
|
||||
(esdhc->vendor_ver <= VENDOR_V_22)) {
|
||||
val = sdhci_readl(host, ESDHC_PROCTL);
|
||||
val &= ~ESDHC_CTRL_BUSWIDTH_MASK;
|
||||
val |= bus_width;
|
||||
sdhci_writel(host, val, ESDHC_PROCTL);
|
||||
|
||||
if (mask & SDHCI_RESET_ALL) {
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some bits have to be cleaned manually for eSDHC whose spec
|
||||
* version is higher than 3.0 for all reset.
|
||||
*/
|
||||
if ((mask & SDHCI_RESET_ALL) &&
|
||||
(esdhc->spec_ver >= SDHCI_SPEC_300)) {
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val &= ~ESDHC_TB_EN;
|
||||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
|
||||
/*
|
||||
* Initialize eSDHC_DLLCFG1[DLL_PD_PULSE_STRETCH_SEL] to
|
||||
* 0 for quirk.
|
||||
*/
|
||||
if (esdhc->quirk_unreliable_pulse_detection) {
|
||||
val = sdhci_readl(host, ESDHC_DLLCFG1);
|
||||
val &= ~ESDHC_DLL_PD_PULSE_STRETCH_SEL;
|
||||
|
@ -854,20 +908,20 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc,
|
|||
}
|
||||
|
||||
static struct soc_device_attribute soc_tuning_erratum_type1[] = {
|
||||
{ .family = "QorIQ T1023", .revision = "1.0", },
|
||||
{ .family = "QorIQ T1040", .revision = "1.0", },
|
||||
{ .family = "QorIQ T2080", .revision = "1.0", },
|
||||
{ .family = "QorIQ LS1021A", .revision = "1.0", },
|
||||
{ .family = "QorIQ T1023", },
|
||||
{ .family = "QorIQ T1040", },
|
||||
{ .family = "QorIQ T2080", },
|
||||
{ .family = "QorIQ LS1021A", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct soc_device_attribute soc_tuning_erratum_type2[] = {
|
||||
{ .family = "QorIQ LS1012A", .revision = "1.0", },
|
||||
{ .family = "QorIQ LS1043A", .revision = "1.*", },
|
||||
{ .family = "QorIQ LS1046A", .revision = "1.0", },
|
||||
{ .family = "QorIQ LS1080A", .revision = "1.0", },
|
||||
{ .family = "QorIQ LS2080A", .revision = "1.0", },
|
||||
{ .family = "QorIQ LA1575A", .revision = "1.0", },
|
||||
{ .family = "QorIQ LS1012A", },
|
||||
{ .family = "QorIQ LS1043A", },
|
||||
{ .family = "QorIQ LS1046A", },
|
||||
{ .family = "QorIQ LS1080A", },
|
||||
{ .family = "QorIQ LS2080A", },
|
||||
{ .family = "QorIQ LA1575A", },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -888,20 +942,11 @@ static void esdhc_tuning_block_enable(struct sdhci_host *host, bool enable)
|
|||
esdhc_clock_enable(host, true);
|
||||
}
|
||||
|
||||
static void esdhc_prepare_sw_tuning(struct sdhci_host *host, u8 *window_start,
|
||||
static void esdhc_tuning_window_ptr(struct sdhci_host *host, u8 *window_start,
|
||||
u8 *window_end)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u8 tbstat_15_8, tbstat_7_0;
|
||||
u32 val;
|
||||
|
||||
if (esdhc->quirk_tuning_erratum_type1) {
|
||||
*window_start = 5 * esdhc->div_ratio;
|
||||
*window_end = 3 * esdhc->div_ratio;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write TBCTL[11:8]=4'h8 */
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val &= ~(0xf << 8);
|
||||
|
@ -920,20 +965,37 @@ static void esdhc_prepare_sw_tuning(struct sdhci_host *host, u8 *window_start,
|
|||
val = sdhci_readl(host, ESDHC_TBSTAT);
|
||||
val = sdhci_readl(host, ESDHC_TBSTAT);
|
||||
|
||||
*window_end = val & 0xff;
|
||||
*window_start = (val >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static void esdhc_prepare_sw_tuning(struct sdhci_host *host, u8 *window_start,
|
||||
u8 *window_end)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
|
||||
u8 start_ptr, end_ptr;
|
||||
|
||||
if (esdhc->quirk_tuning_erratum_type1) {
|
||||
*window_start = 5 * esdhc->div_ratio;
|
||||
*window_end = 3 * esdhc->div_ratio;
|
||||
return;
|
||||
}
|
||||
|
||||
esdhc_tuning_window_ptr(host, &start_ptr, &end_ptr);
|
||||
|
||||
/* Reset data lines by setting ESDHCCTL[RSTD] */
|
||||
sdhci_reset(host, SDHCI_RESET_DATA);
|
||||
/* Write 32'hFFFF_FFFF to IRQSTAT register */
|
||||
sdhci_writel(host, 0xFFFFFFFF, SDHCI_INT_STATUS);
|
||||
|
||||
/* If TBSTAT[15:8]-TBSTAT[7:0] > 4 * div_ratio
|
||||
* or TBSTAT[7:0]-TBSTAT[15:8] > 4 * div_ratio,
|
||||
/* If TBSTAT[15:8]-TBSTAT[7:0] > (4 * div_ratio) + 2
|
||||
* or TBSTAT[7:0]-TBSTAT[15:8] > (4 * div_ratio) + 2,
|
||||
* then program TBPTR[TB_WNDW_END_PTR] = 4 * div_ratio
|
||||
* and program TBPTR[TB_WNDW_START_PTR] = 8 * div_ratio.
|
||||
*/
|
||||
tbstat_7_0 = val & 0xff;
|
||||
tbstat_15_8 = (val >> 8) & 0xff;
|
||||
|
||||
if (abs(tbstat_15_8 - tbstat_7_0) > (4 * esdhc->div_ratio)) {
|
||||
if (abs(start_ptr - end_ptr) > (4 * esdhc->div_ratio + 2)) {
|
||||
*window_start = 8 * esdhc->div_ratio;
|
||||
*window_end = 4 * esdhc->div_ratio;
|
||||
} else {
|
||||
|
@ -1006,6 +1068,19 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
if (ret)
|
||||
break;
|
||||
|
||||
/* For type2 affected platforms of the tuning erratum,
|
||||
* tuning may succeed although eSDHC might not have
|
||||
* tuned properly. Need to check tuning window.
|
||||
*/
|
||||
if (esdhc->quirk_tuning_erratum_type2 &&
|
||||
!host->tuning_err) {
|
||||
esdhc_tuning_window_ptr(host, &window_start,
|
||||
&window_end);
|
||||
if (abs(window_start - window_end) >
|
||||
(4 * esdhc->div_ratio + 2))
|
||||
host->tuning_err = -EAGAIN;
|
||||
}
|
||||
|
||||
/* If HW tuning fails and triggers erratum,
|
||||
* try workaround.
|
||||
*/
|
||||
|
@ -1238,7 +1313,8 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
|
|||
* 1/2 peripheral clock.
|
||||
*/
|
||||
if (of_device_is_compatible(np, "fsl,ls1046a-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,ls1028a-esdhc"))
|
||||
of_device_is_compatible(np, "fsl,ls1028a-esdhc") ||
|
||||
of_device_is_compatible(np, "fsl,ls1088a-esdhc"))
|
||||
esdhc->peripheral_clock = clk_get_rate(clk) / 2;
|
||||
else
|
||||
esdhc->peripheral_clock = clk_get_rate(clk);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -85,6 +86,7 @@
|
|||
|
||||
/* sdhci-omap controller flags */
|
||||
#define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
|
||||
#define SDHCI_OMAP_SPECIAL_RESET BIT(1)
|
||||
|
||||
struct sdhci_omap_data {
|
||||
u32 offset;
|
||||
|
@ -685,7 +687,11 @@ static int sdhci_omap_enable_dma(struct sdhci_host *host)
|
|||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON);
|
||||
reg |= CON_DMA_MASTER;
|
||||
reg &= ~CON_DMA_MASTER;
|
||||
/* Switch to DMA slave mode when using external DMA */
|
||||
if (!host->use_external_dma)
|
||||
reg |= CON_DMA_MASTER;
|
||||
|
||||
sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg);
|
||||
|
||||
return 0;
|
||||
|
@ -774,15 +780,35 @@ static void sdhci_omap_set_uhs_signaling(struct sdhci_host *host,
|
|||
sdhci_omap_start_clock(omap_host);
|
||||
}
|
||||
|
||||
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
|
||||
static void sdhci_omap_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host);
|
||||
unsigned long limit = MMC_TIMEOUT_US;
|
||||
unsigned long i = 0;
|
||||
|
||||
/* Don't reset data lines during tuning operation */
|
||||
if (omap_host->is_tuning)
|
||||
mask &= ~SDHCI_RESET_DATA;
|
||||
|
||||
if (omap_host->flags & SDHCI_OMAP_SPECIAL_RESET) {
|
||||
sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET);
|
||||
while ((!(sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask)) &&
|
||||
(i++ < limit))
|
||||
udelay(1);
|
||||
i = 0;
|
||||
while ((sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) &&
|
||||
(i++ < limit))
|
||||
udelay(1);
|
||||
|
||||
if (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask)
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Timeout waiting on controller reset in %s\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
|
@ -823,6 +849,15 @@ static u32 sdhci_omap_irq(struct sdhci_host *host, u32 intmask)
|
|||
return intmask;
|
||||
}
|
||||
|
||||
static void sdhci_omap_set_timeout(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
if (cmd->opcode == MMC_ERASE)
|
||||
sdhci_set_data_timeout_irq(host, false);
|
||||
|
||||
__sdhci_set_timeout(host, cmd);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_omap_ops = {
|
||||
.set_clock = sdhci_omap_set_clock,
|
||||
.set_power = sdhci_omap_set_power,
|
||||
|
@ -834,6 +869,7 @@ static struct sdhci_ops sdhci_omap_ops = {
|
|||
.reset = sdhci_omap_reset,
|
||||
.set_uhs_signaling = sdhci_omap_set_uhs_signaling,
|
||||
.irq = sdhci_omap_irq,
|
||||
.set_timeout = sdhci_omap_set_timeout,
|
||||
};
|
||||
|
||||
static int sdhci_omap_set_capabilities(struct sdhci_omap_host *omap_host)
|
||||
|
@ -883,6 +919,16 @@ static const struct sdhci_omap_data k2g_data = {
|
|||
.offset = 0x200,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data am335_data = {
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data am437_data = {
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_SPECIAL_RESET,
|
||||
};
|
||||
|
||||
static const struct sdhci_omap_data dra7_data = {
|
||||
.offset = 0x200,
|
||||
.flags = SDHCI_OMAP_REQUIRE_IODELAY,
|
||||
|
@ -891,6 +937,8 @@ static const struct sdhci_omap_data dra7_data = {
|
|||
static const struct of_device_id omap_sdhci_match[] = {
|
||||
{ .compatible = "ti,dra7-sdhci", .data = &dra7_data },
|
||||
{ .compatible = "ti,k2g-sdhci", .data = &k2g_data },
|
||||
{ .compatible = "ti,am335-sdhci", .data = &am335_data },
|
||||
{ .compatible = "ti,am437-sdhci", .data = &am437_data },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, omap_sdhci_match);
|
||||
|
@ -1037,6 +1085,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||
const struct of_device_id *match;
|
||||
struct sdhci_omap_data *data;
|
||||
const struct soc_device_attribute *soc;
|
||||
struct resource *regs;
|
||||
|
||||
match = of_match_device(omap_sdhci_match, dev);
|
||||
if (!match)
|
||||
|
@ -1049,6 +1098,10 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||
}
|
||||
offset = data->offset;
|
||||
|
||||
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!regs)
|
||||
return -ENXIO;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_omap_pdata,
|
||||
sizeof(*omap_host));
|
||||
if (IS_ERR(host)) {
|
||||
|
@ -1065,6 +1118,7 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||
omap_host->timing = MMC_TIMING_LEGACY;
|
||||
omap_host->flags = data->flags;
|
||||
host->ioaddr += offset;
|
||||
host->mapbase = regs->start + offset;
|
||||
|
||||
mmc = host->mmc;
|
||||
sdhci_get_of_property(pdev);
|
||||
|
@ -1134,6 +1188,10 @@ static int sdhci_omap_probe(struct platform_device *pdev)
|
|||
host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning;
|
||||
host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq;
|
||||
|
||||
/* Switch to external DMA only if there is the "dmas" property */
|
||||
if (of_find_property(dev->of_node, "dmas", NULL))
|
||||
sdhci_switch_external_dma(host, true);
|
||||
|
||||
ret = sdhci_setup_host(host);
|
||||
if (ret)
|
||||
goto err_put_sync;
|
||||
|
|
|
@ -1991,12 +1991,12 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
|
|||
|
||||
if (slot->cd_idx >= 0) {
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", slot->cd_idx,
|
||||
slot->cd_override_level, 0, NULL);
|
||||
slot->cd_override_level, 0);
|
||||
if (ret && ret != -EPROBE_DEFER)
|
||||
ret = mmc_gpiod_request_cd(host->mmc, NULL,
|
||||
slot->cd_idx,
|
||||
slot->cd_override_level,
|
||||
0, NULL);
|
||||
0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto remove;
|
||||
|
||||
|
|
|
@ -117,7 +117,6 @@ struct sdhci_s3c {
|
|||
struct s3c_sdhci_platdata *pdata;
|
||||
int cur_clk;
|
||||
int ext_cd_irq;
|
||||
int ext_cd_gpio;
|
||||
|
||||
struct clk *clk_io;
|
||||
struct clk *clk_bus[MAX_BUS_CLK];
|
||||
|
@ -481,7 +480,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
struct device *dev = &pdev->dev;
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_s3c *sc;
|
||||
struct resource *res;
|
||||
int ret, irq, ptr, clks;
|
||||
|
||||
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
|
||||
|
@ -512,7 +510,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
goto err_pdata_io_clk;
|
||||
} else {
|
||||
memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata));
|
||||
sc->ext_cd_gpio = -1; /* invalid gpio number */
|
||||
}
|
||||
|
||||
drv_data = sdhci_s3c_get_driver_data(pdev);
|
||||
|
@ -555,8 +552,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
|
|||
goto err_no_busclks;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
goto err_req_regs;
|
||||
|
|
|
@ -194,7 +194,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
|
|||
* We must request the IRQ after sdhci_add_host(), as the tasklet only
|
||||
* gets setup in sdhci_add_host() and we oops.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto err_request_cd;
|
||||
if (!ret)
|
||||
|
|
|
@ -43,7 +43,6 @@ static const struct sdhci_ops sdhci_pltfm_ops = {
|
|||
static int sdhci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
struct spear_sdhci *sdhci;
|
||||
struct device *dev;
|
||||
int ret;
|
||||
|
@ -56,8 +55,7 @@ static int sdhci_probe(struct platform_device *pdev)
|
|||
goto err;
|
||||
}
|
||||
|
||||
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
|
||||
host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
|
||||
|
@ -98,7 +96,7 @@ static int sdhci_probe(struct platform_device *pdev)
|
|||
* It is optional to use GPIOs for sdhci card detection. If we
|
||||
* find a descriptor using slot GPIO, we use it.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto disable_clk;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -992,7 +993,7 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
|
|||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
||||
static void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable)
|
||||
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable)
|
||||
{
|
||||
if (enable)
|
||||
host->ier |= SDHCI_INT_DATA_TIMEOUT;
|
||||
|
@ -1001,42 +1002,36 @@ static void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable)
|
|||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_set_data_timeout_irq);
|
||||
|
||||
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
bool too_big = false;
|
||||
u8 count = sdhci_calc_timeout(host, cmd, &too_big);
|
||||
|
||||
if (too_big &&
|
||||
host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
|
||||
sdhci_calc_sw_timeout(host, cmd);
|
||||
sdhci_set_data_timeout_irq(host, false);
|
||||
} else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) {
|
||||
sdhci_set_data_timeout_irq(host, true);
|
||||
}
|
||||
|
||||
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__sdhci_set_timeout);
|
||||
|
||||
static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
u8 count;
|
||||
|
||||
if (host->ops->set_timeout) {
|
||||
if (host->ops->set_timeout)
|
||||
host->ops->set_timeout(host, cmd);
|
||||
} else {
|
||||
bool too_big = false;
|
||||
|
||||
count = sdhci_calc_timeout(host, cmd, &too_big);
|
||||
|
||||
if (too_big &&
|
||||
host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) {
|
||||
sdhci_calc_sw_timeout(host, cmd);
|
||||
sdhci_set_data_timeout_irq(host, false);
|
||||
} else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) {
|
||||
sdhci_set_data_timeout_irq(host, true);
|
||||
}
|
||||
|
||||
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
|
||||
}
|
||||
else
|
||||
__sdhci_set_timeout(host, cmd);
|
||||
}
|
||||
|
||||
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
static void sdhci_initialize_data(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
host->data_timeout = 0;
|
||||
|
||||
if (sdhci_data_line_cmd(cmd))
|
||||
sdhci_set_timeout(host, cmd);
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
WARN_ON(host->data);
|
||||
|
||||
/* Sanity checks */
|
||||
|
@ -1047,6 +1042,34 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
host->data = data;
|
||||
host->data_early = 0;
|
||||
host->data->bytes_xfered = 0;
|
||||
}
|
||||
|
||||
static inline void sdhci_set_block_info(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
/* Set the DMA boundary value and block size */
|
||||
sdhci_writew(host,
|
||||
SDHCI_MAKE_BLKSZ(host->sdma_boundary, data->blksz),
|
||||
SDHCI_BLOCK_SIZE);
|
||||
/*
|
||||
* For Version 4.10 onwards, if v4 mode is enabled, 32-bit Block Count
|
||||
* can be supported, in that case 16-bit block count register must be 0.
|
||||
*/
|
||||
if (host->version >= SDHCI_SPEC_410 && host->v4_mode &&
|
||||
(host->quirks2 & SDHCI_QUIRK2_USE_32BIT_BLK_CNT)) {
|
||||
if (sdhci_readw(host, SDHCI_BLOCK_COUNT))
|
||||
sdhci_writew(host, 0, SDHCI_BLOCK_COUNT);
|
||||
sdhci_writew(host, data->blocks, SDHCI_32BIT_BLK_CNT);
|
||||
} else {
|
||||
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
sdhci_initialize_data(host, data);
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
struct scatterlist *sg;
|
||||
|
@ -1133,24 +1156,192 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
|
||||
sdhci_set_transfer_irqs(host);
|
||||
|
||||
/* Set the DMA boundary value and block size */
|
||||
sdhci_writew(host, SDHCI_MAKE_BLKSZ(host->sdma_boundary, data->blksz),
|
||||
SDHCI_BLOCK_SIZE);
|
||||
sdhci_set_block_info(host, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* For Version 4.10 onwards, if v4 mode is enabled, 32-bit Block Count
|
||||
* can be supported, in that case 16-bit block count register must be 0.
|
||||
*/
|
||||
if (host->version >= SDHCI_SPEC_410 && host->v4_mode &&
|
||||
(host->quirks2 & SDHCI_QUIRK2_USE_32BIT_BLK_CNT)) {
|
||||
if (sdhci_readw(host, SDHCI_BLOCK_COUNT))
|
||||
sdhci_writew(host, 0, SDHCI_BLOCK_COUNT);
|
||||
sdhci_writew(host, data->blocks, SDHCI_32BIT_BLK_CNT);
|
||||
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
|
||||
|
||||
static int sdhci_external_dma_init(struct sdhci_host *host)
|
||||
{
|
||||
int ret = 0;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
host->tx_chan = dma_request_chan(mmc->parent, "tx");
|
||||
if (IS_ERR(host->tx_chan)) {
|
||||
ret = PTR_ERR(host->tx_chan);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
pr_warn("Failed to request TX DMA channel.\n");
|
||||
host->tx_chan = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
host->rx_chan = dma_request_chan(mmc->parent, "rx");
|
||||
if (IS_ERR(host->rx_chan)) {
|
||||
if (host->tx_chan) {
|
||||
dma_release_channel(host->tx_chan);
|
||||
host->tx_chan = NULL;
|
||||
}
|
||||
|
||||
ret = PTR_ERR(host->rx_chan);
|
||||
if (ret != -EPROBE_DEFER)
|
||||
pr_warn("Failed to request RX DMA channel.\n");
|
||||
host->rx_chan = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
|
||||
}
|
||||
|
||||
static int sdhci_external_dma_setup(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
int ret, i;
|
||||
enum dma_transfer_direction dir;
|
||||
struct dma_async_tx_descriptor *desc;
|
||||
struct mmc_data *data = cmd->data;
|
||||
struct dma_chan *chan;
|
||||
struct dma_slave_config cfg;
|
||||
dma_cookie_t cookie;
|
||||
int sg_cnt;
|
||||
|
||||
if (!host->mapbase)
|
||||
return -EINVAL;
|
||||
|
||||
cfg.src_addr = host->mapbase + SDHCI_BUFFER;
|
||||
cfg.dst_addr = host->mapbase + SDHCI_BUFFER;
|
||||
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
cfg.src_maxburst = data->blksz / 4;
|
||||
cfg.dst_maxburst = data->blksz / 4;
|
||||
|
||||
/* Sanity check: all the SG entries must be aligned by block size. */
|
||||
for (i = 0; i < data->sg_len; i++) {
|
||||
if ((data->sg + i)->length % data->blksz)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chan = sdhci_external_dma_channel(host, data);
|
||||
|
||||
ret = dmaengine_slave_config(chan, &cfg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sg_cnt = sdhci_pre_dma_transfer(host, data, COOKIE_MAPPED);
|
||||
if (sg_cnt <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
dir = data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
|
||||
desc = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len, dir,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc)
|
||||
return -EINVAL;
|
||||
|
||||
desc->callback = NULL;
|
||||
desc->callback_param = NULL;
|
||||
|
||||
cookie = dmaengine_submit(desc);
|
||||
if (dma_submit_error(cookie))
|
||||
ret = cookie;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdhci_external_dma_release(struct sdhci_host *host)
|
||||
{
|
||||
if (host->tx_chan) {
|
||||
dma_release_channel(host->tx_chan);
|
||||
host->tx_chan = NULL;
|
||||
}
|
||||
|
||||
if (host->rx_chan) {
|
||||
dma_release_channel(host->rx_chan);
|
||||
host->rx_chan = NULL;
|
||||
}
|
||||
|
||||
sdhci_switch_external_dma(host, false);
|
||||
}
|
||||
|
||||
static void __sdhci_external_dma_prepare_data(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
struct mmc_data *data = cmd->data;
|
||||
|
||||
sdhci_initialize_data(host, data);
|
||||
|
||||
host->flags |= SDHCI_REQ_USE_DMA;
|
||||
sdhci_set_transfer_irqs(host);
|
||||
|
||||
sdhci_set_block_info(host, data);
|
||||
}
|
||||
|
||||
static void sdhci_external_dma_prepare_data(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
if (!sdhci_external_dma_setup(host, cmd)) {
|
||||
__sdhci_external_dma_prepare_data(host, cmd);
|
||||
} else {
|
||||
sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT);
|
||||
sdhci_external_dma_release(host);
|
||||
pr_err("%s: Cannot use external DMA, switch to the DMA/PIO which standard SDHCI provides.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_prepare_data(host, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
struct dma_chan *chan;
|
||||
|
||||
if (!cmd->data)
|
||||
return;
|
||||
|
||||
chan = sdhci_external_dma_channel(host, cmd->data);
|
||||
if (chan)
|
||||
dma_async_issue_pending(chan);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int sdhci_external_dma_init(struct sdhci_host *host)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void sdhci_external_dma_release(struct sdhci_host *host)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void sdhci_external_dma_prepare_data(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
/* This should never happen */
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
||||
static inline void sdhci_external_dma_pre_transfer(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
}
|
||||
|
||||
static inline struct dma_chan *sdhci_external_dma_channel(struct sdhci_host *host,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void sdhci_switch_external_dma(struct sdhci_host *host, bool en)
|
||||
{
|
||||
host->use_external_dma = en;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_switch_external_dma);
|
||||
|
||||
static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
|
@ -1245,22 +1436,10 @@ static bool sdhci_needs_reset(struct sdhci_host *host, struct mmc_request *mrq)
|
|||
(host->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)));
|
||||
}
|
||||
|
||||
static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
||||
static void sdhci_set_mrq_done(struct sdhci_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (host->cmd && host->cmd->mrq == mrq)
|
||||
host->cmd = NULL;
|
||||
|
||||
if (host->data_cmd && host->data_cmd->mrq == mrq)
|
||||
host->data_cmd = NULL;
|
||||
|
||||
if (host->data && host->data->mrq == mrq)
|
||||
host->data = NULL;
|
||||
|
||||
if (sdhci_needs_reset(host, mrq))
|
||||
host->pending_reset = true;
|
||||
|
||||
for (i = 0; i < SDHCI_MAX_MRQS; i++) {
|
||||
if (host->mrqs_done[i] == mrq) {
|
||||
WARN_ON(1);
|
||||
|
@ -1276,6 +1455,23 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
|||
}
|
||||
|
||||
WARN_ON(i >= SDHCI_MAX_MRQS);
|
||||
}
|
||||
|
||||
static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
||||
{
|
||||
if (host->cmd && host->cmd->mrq == mrq)
|
||||
host->cmd = NULL;
|
||||
|
||||
if (host->data_cmd && host->data_cmd->mrq == mrq)
|
||||
host->data_cmd = NULL;
|
||||
|
||||
if (host->data && host->data->mrq == mrq)
|
||||
host->data = NULL;
|
||||
|
||||
if (sdhci_needs_reset(host, mrq))
|
||||
host->pending_reset = true;
|
||||
|
||||
sdhci_set_mrq_done(host, mrq);
|
||||
|
||||
sdhci_del_timer(host, mrq);
|
||||
|
||||
|
@ -1326,12 +1522,12 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||
|
||||
/*
|
||||
* Need to send CMD12 if -
|
||||
* a) open-ended multiblock transfer (no CMD23)
|
||||
* a) open-ended multiblock transfer not using auto CMD12 (no CMD23)
|
||||
* b) error in multiblock transfer
|
||||
*/
|
||||
if (data->stop &&
|
||||
(data->error ||
|
||||
!data->mrq->sbc)) {
|
||||
((!data->mrq->sbc && !sdhci_auto_cmd12(host, data->mrq)) ||
|
||||
data->error)) {
|
||||
/*
|
||||
* 'cap_cmd_during_tfr' request must not use the command line
|
||||
* after mmc_command_done() has been called. It is upper layer's
|
||||
|
@ -1390,12 +1586,19 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
}
|
||||
|
||||
host->cmd = cmd;
|
||||
host->data_timeout = 0;
|
||||
if (sdhci_data_line_cmd(cmd)) {
|
||||
WARN_ON(host->data_cmd);
|
||||
host->data_cmd = cmd;
|
||||
sdhci_set_timeout(host, cmd);
|
||||
}
|
||||
|
||||
sdhci_prepare_data(host, cmd);
|
||||
if (cmd->data) {
|
||||
if (host->use_external_dma)
|
||||
sdhci_external_dma_prepare_data(host, cmd);
|
||||
else
|
||||
sdhci_prepare_data(host, cmd);
|
||||
}
|
||||
|
||||
sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT);
|
||||
|
||||
|
@ -1437,6 +1640,9 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
timeout += 10 * HZ;
|
||||
sdhci_mod_timer(host, cmd->mrq, timeout);
|
||||
|
||||
if (host->use_external_dma)
|
||||
sdhci_external_dma_pre_transfer(host, cmd);
|
||||
|
||||
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_send_command);
|
||||
|
@ -1825,17 +2031,6 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
sdhci_led_activate(host);
|
||||
|
||||
/*
|
||||
* Ensure we don't send the STOP for non-SET_BLOCK_COUNTED
|
||||
* requests if Auto-CMD12 is enabled.
|
||||
*/
|
||||
if (sdhci_auto_cmd12(host, mrq)) {
|
||||
if (mrq->stop) {
|
||||
mrq->data->stop = NULL;
|
||||
mrq->stop = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
sdhci_finish_mrq(host, mrq);
|
||||
|
@ -2661,6 +2856,17 @@ static bool sdhci_request_done(struct sdhci_host *host)
|
|||
if (host->flags & SDHCI_REQ_USE_DMA) {
|
||||
struct mmc_data *data = mrq->data;
|
||||
|
||||
if (host->use_external_dma && data &&
|
||||
(mrq->cmd->error || data->error)) {
|
||||
struct dma_chan *chan = sdhci_external_dma_channel(host, data);
|
||||
|
||||
host->mrqs_done[i] = NULL;
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
dmaengine_terminate_sync(chan);
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
sdhci_set_mrq_done(host, mrq);
|
||||
}
|
||||
|
||||
if (data && data->host_cookie == COOKIE_MAPPED) {
|
||||
if (host->bounce_buffer) {
|
||||
/*
|
||||
|
@ -3796,6 +4002,21 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
if (sdhci_can_64bit_dma(host))
|
||||
host->flags |= SDHCI_USE_64_BIT_DMA;
|
||||
|
||||
if (host->use_external_dma) {
|
||||
ret = sdhci_external_dma_init(host);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
goto unreg;
|
||||
/*
|
||||
* Fall back to use the DMA/PIO integrated in standard SDHCI
|
||||
* instead of external DMA devices.
|
||||
*/
|
||||
else if (ret)
|
||||
sdhci_switch_external_dma(host, false);
|
||||
/* Disable internal DMA sources */
|
||||
else
|
||||
host->flags &= ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
|
||||
}
|
||||
|
||||
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
|
||||
if (host->ops->set_dma_mask)
|
||||
ret = host->ops->set_dma_mask(host);
|
||||
|
@ -3822,15 +4043,13 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
dma_addr_t dma;
|
||||
void *buf;
|
||||
|
||||
if (host->flags & SDHCI_USE_64_BIT_DMA) {
|
||||
host->adma_table_sz = host->adma_table_cnt *
|
||||
SDHCI_ADMA2_64_DESC_SZ(host);
|
||||
host->desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
|
||||
} else {
|
||||
host->adma_table_sz = host->adma_table_cnt *
|
||||
SDHCI_ADMA2_32_DESC_SZ;
|
||||
host->desc_sz = SDHCI_ADMA2_32_DESC_SZ;
|
||||
}
|
||||
if (!(host->flags & SDHCI_USE_64_BIT_DMA))
|
||||
host->alloc_desc_sz = SDHCI_ADMA2_32_DESC_SZ;
|
||||
else if (!host->alloc_desc_sz)
|
||||
host->alloc_desc_sz = SDHCI_ADMA2_64_DESC_SZ(host);
|
||||
|
||||
host->desc_sz = host->alloc_desc_sz;
|
||||
host->adma_table_sz = host->adma_table_cnt * host->desc_sz;
|
||||
|
||||
host->align_buffer_sz = SDHCI_MAX_SEGS * SDHCI_ADMA2_ALIGN;
|
||||
/*
|
||||
|
@ -4278,6 +4497,10 @@ void sdhci_cleanup_host(struct sdhci_host *host)
|
|||
dma_free_coherent(mmc_dev(mmc), host->align_buffer_sz +
|
||||
host->adma_table_sz, host->align_buffer,
|
||||
host->align_addr);
|
||||
|
||||
if (host->use_external_dma)
|
||||
sdhci_external_dma_release(host);
|
||||
|
||||
host->adma_table = NULL;
|
||||
host->align_buffer = NULL;
|
||||
}
|
||||
|
@ -4323,6 +4546,7 @@ int __sdhci_add_host(struct sdhci_host *host)
|
|||
|
||||
pr_info("%s: SDHCI controller on %s [%s] using %s\n",
|
||||
mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
|
||||
host->use_external_dma ? "External DMA" :
|
||||
(host->flags & SDHCI_USE_ADMA) ?
|
||||
(host->flags & SDHCI_USE_64_BIT_DMA) ? "ADMA 64-bit" : "ADMA" :
|
||||
(host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO");
|
||||
|
@ -4411,6 +4635,9 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
|
|||
host->adma_table_sz, host->align_buffer,
|
||||
host->align_addr);
|
||||
|
||||
if (host->use_external_dma)
|
||||
sdhci_external_dma_release(host);
|
||||
|
||||
host->adma_table = NULL;
|
||||
host->align_buffer = NULL;
|
||||
}
|
||||
|
|
|
@ -487,6 +487,7 @@ struct sdhci_host {
|
|||
|
||||
int irq; /* Device IRQ */
|
||||
void __iomem *ioaddr; /* Mapped address */
|
||||
phys_addr_t mapbase; /* physical address base */
|
||||
char *bounce_buffer; /* For packing SDMA reads/writes */
|
||||
dma_addr_t bounce_addr;
|
||||
unsigned int bounce_buffer_size;
|
||||
|
@ -535,6 +536,7 @@ struct sdhci_host {
|
|||
bool pending_reset; /* Cmd/data reset is pending */
|
||||
bool irq_wake_enabled; /* IRQ wakeup is enabled */
|
||||
bool v4_mode; /* Host Version 4 Enable */
|
||||
bool use_external_dma; /* Host selects to use external DMA */
|
||||
|
||||
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
|
@ -556,7 +558,8 @@ struct sdhci_host {
|
|||
dma_addr_t adma_addr; /* Mapped ADMA descr. table */
|
||||
dma_addr_t align_addr; /* Mapped bounce buffer */
|
||||
|
||||
unsigned int desc_sz; /* ADMA descriptor size */
|
||||
unsigned int desc_sz; /* ADMA current descriptor size */
|
||||
unsigned int alloc_desc_sz; /* ADMA descr. max size host supports */
|
||||
|
||||
struct workqueue_struct *complete_wq; /* Request completion wq */
|
||||
struct work_struct complete_work; /* Request completion work */
|
||||
|
@ -564,6 +567,11 @@ struct sdhci_host {
|
|||
struct timer_list timer; /* Timer for timeouts */
|
||||
struct timer_list data_timer; /* Timer for data timeouts */
|
||||
|
||||
#if IS_ENABLED(CONFIG_MMC_SDHCI_EXTERNAL_DMA)
|
||||
struct dma_chan *rx_chan;
|
||||
struct dma_chan *tx_chan;
|
||||
#endif
|
||||
|
||||
u32 caps; /* CAPABILITY_0 */
|
||||
u32 caps1; /* CAPABILITY_1 */
|
||||
bool read_caps; /* Capability flags have been read */
|
||||
|
@ -795,5 +803,8 @@ void sdhci_end_tuning(struct sdhci_host *host);
|
|||
void sdhci_reset_tuning(struct sdhci_host *host);
|
||||
void sdhci_send_tuning(struct sdhci_host *host, u32 opcode);
|
||||
void sdhci_abort_tuning(struct sdhci_host *host, u32 opcode);
|
||||
void sdhci_switch_external_dma(struct sdhci_host *host, bool en);
|
||||
void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable);
|
||||
void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd);
|
||||
|
||||
#endif /* __SDHCI_HW_H */
|
||||
|
|
|
@ -505,7 +505,6 @@ static int sdhci_am654_probe(struct platform_device *pdev)
|
|||
struct sdhci_am654_data *sdhci_am654;
|
||||
const struct of_device_id *match;
|
||||
struct sdhci_host *host;
|
||||
struct resource *res;
|
||||
struct clk *clk_xin;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
|
@ -538,8 +537,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
|
|||
goto pm_runtime_disable;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
base = devm_platform_ioremap_resource(pdev, 1);
|
||||
if (IS_ERR(base)) {
|
||||
ret = PTR_ERR(base);
|
||||
goto pm_runtime_put;
|
||||
|
|
|
@ -89,7 +89,6 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct sdhci_host *host;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
int irq, ctrl = 0, ret = 0;
|
||||
struct f_sdhost_priv *priv;
|
||||
u32 reg = 0;
|
||||
|
@ -123,8 +122,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
|
|||
host->ops = &sdhci_f_sdh30_ops;
|
||||
host->irq = irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
|
||||
host->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->ioaddr)) {
|
||||
ret = PTR_ERR(host->ioaddr);
|
||||
goto err;
|
||||
|
|
|
@ -432,8 +432,12 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host)
|
|||
host->chan_rx = sh_mmcif_request_dma_pdata(host,
|
||||
pdata->slave_id_rx);
|
||||
} else {
|
||||
host->chan_tx = dma_request_slave_channel(dev, "tx");
|
||||
host->chan_rx = dma_request_slave_channel(dev, "rx");
|
||||
host->chan_tx = dma_request_chan(dev, "tx");
|
||||
if (IS_ERR(host->chan_tx))
|
||||
host->chan_tx = NULL;
|
||||
host->chan_rx = dma_request_chan(dev, "rx");
|
||||
if (IS_ERR(host->chan_rx))
|
||||
host->chan_rx = NULL;
|
||||
}
|
||||
dev_dbg(dev, "%s: got channel TX %p RX %p\n", __func__, host->chan_tx,
|
||||
host->chan_rx);
|
||||
|
@ -1388,7 +1392,6 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
struct sh_mmcif_host *host;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct sh_mmcif_plat_data *pd = dev->platform_data;
|
||||
struct resource *res;
|
||||
void __iomem *reg;
|
||||
const char *name;
|
||||
|
||||
|
@ -1397,8 +1400,7 @@ static int sh_mmcif_probe(struct platform_device *pdev)
|
|||
if (irq[0] < 0)
|
||||
return -ENXIO;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg = devm_ioremap_resource(dev, res);
|
||||
reg = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(reg))
|
||||
return PTR_ERR(reg);
|
||||
|
||||
|
|
|
@ -1273,8 +1273,7 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->reg_base = devm_ioremap_resource(&pdev->dev,
|
||||
platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||
host->reg_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->reg_base))
|
||||
return PTR_ERR(host->reg_base);
|
||||
|
||||
|
|
|
@ -1109,12 +1109,10 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev,
|
|||
{
|
||||
struct tmio_mmc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
struct resource *res;
|
||||
void __iomem *ctl;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctl = devm_ioremap_resource(&pdev->dev, res);
|
||||
ctl = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctl))
|
||||
return ERR_CAST(ctl);
|
||||
|
||||
|
@ -1181,7 +1179,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||
* Look for a card detect GPIO, if it fails with anything
|
||||
* else than a probe deferral, just live without it.
|
||||
*/
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
|
||||
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -59,7 +59,6 @@
|
|||
struct uniphier_sd_priv {
|
||||
struct tmio_mmc_data tmio_data;
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pinstate_default;
|
||||
struct pinctrl_state *pinstate_uhs;
|
||||
struct clk *clk;
|
||||
struct reset_control *rst;
|
||||
|
@ -500,13 +499,12 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct uniphier_sd_priv *priv = uniphier_sd_priv(host);
|
||||
struct pinctrl_state *pinstate;
|
||||
struct pinctrl_state *pinstate = NULL;
|
||||
u32 val, tmp;
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
val = UNIPHIER_SD_VOLT_330;
|
||||
pinstate = priv->pinstate_default;
|
||||
break;
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
val = UNIPHIER_SD_VOLT_180;
|
||||
|
@ -521,7 +519,10 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
tmp |= FIELD_PREP(UNIPHIER_SD_VOLT_MASK, val);
|
||||
writel(tmp, host->ctl + UNIPHIER_SD_VOLT);
|
||||
|
||||
pinctrl_select_state(priv->pinctrl, pinstate);
|
||||
if (pinstate)
|
||||
pinctrl_select_state(priv->pinctrl, pinstate);
|
||||
else
|
||||
pinctrl_select_default_state(mmc_dev(mmc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -533,11 +534,6 @@ static int uniphier_sd_uhs_init(struct tmio_mmc_host *host,
|
|||
if (IS_ERR(priv->pinctrl))
|
||||
return PTR_ERR(priv->pinctrl);
|
||||
|
||||
priv->pinstate_default = pinctrl_lookup_state(priv->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
if (IS_ERR(priv->pinstate_default))
|
||||
return PTR_ERR(priv->pinstate_default);
|
||||
|
||||
priv->pinstate_uhs = pinctrl_lookup_state(priv->pinctrl, "uhs");
|
||||
if (IS_ERR(priv->pinstate_uhs))
|
||||
return PTR_ERR(priv->pinstate_uhs);
|
||||
|
|
|
@ -199,7 +199,6 @@ struct usdhi6_host {
|
|||
|
||||
/* Pin control */
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_uhs;
|
||||
};
|
||||
|
||||
|
@ -677,12 +676,14 @@ static void usdhi6_dma_request(struct usdhi6_host *host, phys_addr_t start)
|
|||
};
|
||||
int ret;
|
||||
|
||||
host->chan_tx = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
|
||||
host->chan_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
|
||||
dev_dbg(mmc_dev(host->mmc), "%s: TX: got channel %p\n", __func__,
|
||||
host->chan_tx);
|
||||
|
||||
if (!host->chan_tx)
|
||||
if (IS_ERR(host->chan_tx)) {
|
||||
host->chan_tx = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
cfg.direction = DMA_MEM_TO_DEV;
|
||||
cfg.dst_addr = start + USDHI6_SD_BUF0;
|
||||
|
@ -692,12 +693,14 @@ static void usdhi6_dma_request(struct usdhi6_host *host, phys_addr_t start)
|
|||
if (ret < 0)
|
||||
goto e_release_tx;
|
||||
|
||||
host->chan_rx = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
|
||||
host->chan_rx = dma_request_chan(mmc_dev(host->mmc), "rx");
|
||||
dev_dbg(mmc_dev(host->mmc), "%s: RX: got channel %p\n", __func__,
|
||||
host->chan_rx);
|
||||
|
||||
if (!host->chan_rx)
|
||||
if (IS_ERR(host->chan_rx)) {
|
||||
host->chan_rx = NULL;
|
||||
goto e_release_tx;
|
||||
}
|
||||
|
||||
cfg.direction = DMA_DEV_TO_MEM;
|
||||
cfg.src_addr = cfg.dst_addr;
|
||||
|
@ -1162,8 +1165,7 @@ static int usdhi6_set_pinstates(struct usdhi6_host *host, int voltage)
|
|||
host->pins_uhs);
|
||||
|
||||
default:
|
||||
return pinctrl_select_state(host->pinctrl,
|
||||
host->pins_default);
|
||||
return pinctrl_select_default_state(mmc_dev(host->mmc));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1770,17 +1772,6 @@ static int usdhi6_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs");
|
||||
if (!IS_ERR(host->pins_uhs)) {
|
||||
host->pins_default = pinctrl_lookup_state(host->pinctrl,
|
||||
PINCTRL_STATE_DEFAULT);
|
||||
|
||||
if (IS_ERR(host->pins_default)) {
|
||||
dev_err(dev,
|
||||
"UHS pinctrl requires a default pin state.\n");
|
||||
ret = PTR_ERR(host->pins_default);
|
||||
goto e_free_mmc;
|
||||
}
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
host->base = devm_ioremap_resource(dev, res);
|
||||
|
|
|
@ -1535,15 +1535,8 @@ int pinctrl_init_done(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* pinctrl_pm_select_state() - select pinctrl state for PM
|
||||
* @dev: device to select default state for
|
||||
* @state: state to set
|
||||
*/
|
||||
static int pinctrl_pm_select_state(struct device *dev,
|
||||
struct pinctrl_state *state)
|
||||
static int pinctrl_select_bound_state(struct device *dev,
|
||||
struct pinctrl_state *state)
|
||||
{
|
||||
struct dev_pin_info *pins = dev->pins;
|
||||
int ret;
|
||||
|
@ -1557,16 +1550,28 @@ static int pinctrl_pm_select_state(struct device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pinctrl_select_default_state() - select default pinctrl state
|
||||
* @dev: device to select default state for
|
||||
*/
|
||||
int pinctrl_select_default_state(struct device *dev)
|
||||
{
|
||||
if (!dev->pins)
|
||||
return 0;
|
||||
|
||||
return pinctrl_select_bound_state(dev, dev->pins->default_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_select_default_state);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* pinctrl_pm_select_default_state() - select default pinctrl state for PM
|
||||
* @dev: device to select default state for
|
||||
*/
|
||||
int pinctrl_pm_select_default_state(struct device *dev)
|
||||
{
|
||||
if (!dev->pins)
|
||||
return 0;
|
||||
|
||||
return pinctrl_pm_select_state(dev, dev->pins->default_state);
|
||||
return pinctrl_select_default_state(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_pm_select_default_state);
|
||||
|
||||
|
@ -1579,7 +1584,7 @@ int pinctrl_pm_select_sleep_state(struct device *dev)
|
|||
if (!dev->pins)
|
||||
return 0;
|
||||
|
||||
return pinctrl_pm_select_state(dev, dev->pins->sleep_state);
|
||||
return pinctrl_select_bound_state(dev, dev->pins->sleep_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_pm_select_sleep_state);
|
||||
|
||||
|
@ -1592,7 +1597,7 @@ int pinctrl_pm_select_idle_state(struct device *dev)
|
|||
if (!dev->pins)
|
||||
return 0;
|
||||
|
||||
return pinctrl_pm_select_state(dev, dev->pins->idle_state);
|
||||
return pinctrl_select_bound_state(dev, dev->pins->idle_state);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pinctrl_pm_select_idle_state);
|
||||
#endif
|
||||
|
|
|
@ -158,6 +158,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
|||
|
||||
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
||||
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
||||
void gpiod_toggle_active_low(struct gpio_desc *desc);
|
||||
|
||||
int gpiod_is_active_low(const struct gpio_desc *desc);
|
||||
int gpiod_cansleep(const struct gpio_desc *desc);
|
||||
|
@ -483,6 +484,12 @@ static inline int gpiod_set_transitory(struct gpio_desc *desc, bool transitory)
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline void gpiod_toggle_active_low(struct gpio_desc *desc)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(desc);
|
||||
}
|
||||
|
||||
static inline int gpiod_is_active_low(const struct gpio_desc *desc)
|
||||
{
|
||||
/* GPIO can never have been requested */
|
||||
|
|
|
@ -79,9 +79,6 @@
|
|||
/* Some controllers have a CBSY bit */
|
||||
#define TMIO_MMC_HAVE_CBSY BIT(11)
|
||||
|
||||
/* Some controllers that support HS400 use 4 taps while others use 8. */
|
||||
#define TMIO_MMC_HAVE_4TAP_HS400 BIT(13)
|
||||
|
||||
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
|
||||
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
|
||||
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
|
||||
|
|
|
@ -17,10 +17,9 @@ int mmc_gpio_get_ro(struct mmc_host *host);
|
|||
int mmc_gpio_get_cd(struct mmc_host *host);
|
||||
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx, bool override_active_level,
|
||||
unsigned int debounce, bool *gpio_invert);
|
||||
unsigned int debounce);
|
||||
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
|
||||
unsigned int idx,
|
||||
unsigned int debounce, bool *gpio_invert);
|
||||
unsigned int idx, unsigned int debounce);
|
||||
void mmc_gpio_set_cd_isr(struct mmc_host *host,
|
||||
irqreturn_t (*isr)(int irq, void *dev_id));
|
||||
int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on);
|
||||
|
|
|
@ -40,6 +40,7 @@ extern int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s);
|
|||
|
||||
extern struct pinctrl * __must_check devm_pinctrl_get(struct device *dev);
|
||||
extern void devm_pinctrl_put(struct pinctrl *p);
|
||||
extern int pinctrl_select_default_state(struct device *dev);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
extern int pinctrl_pm_select_default_state(struct device *dev);
|
||||
|
@ -122,6 +123,11 @@ static inline void devm_pinctrl_put(struct pinctrl *p)
|
|||
{
|
||||
}
|
||||
|
||||
static inline int pinctrl_select_default_state(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int pinctrl_pm_select_default_state(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
|
|
Loading…
Reference in a new issue