MMC core:
- Enable erase/discard/trim support for all (e)MMC/SD hosts - Export information through sysfs about enhanced RPMB support (eMMC v5.1+) - Align the initialization commands for SDIO cards - Fix SDIO initialization to prevent memory leaks and NULL pointer errors - Do not export undefined MMC_NAME/MODALIAS for SDIO cards - Export device/vendor field from common CIS for SDIO cards - Move SDIO IDs from functional drivers to the common SDIO header - Introduce the ->request_atomic() host ops MMC host: - Improve support for HW busy signaling for several hosts - Converting some DT bindings to the json-schema - meson-mx-sdhc: Add driver and DT doc for the Amlogic Meson SDHC controller - meson-mx-sdio: Run a soft reset to recover from timeout/CRC error - mmci: Convert to use mmc_regulator_set_vqmmc() - mmci_stm32_sdmmc: Fix a couple of DMA bugs - mmci_stm32_sdmmc: Fix power on issue - renesas,mmcif,sdhci: Document r8a7742 DT bindings - renesas_sdhi: Add support for M3-W ES1.2 and 1.3 revisions - renesas_sdhi: Improvements to the TAP selection - renesas_sdhi/tmio: Further fixup runtime PM management at ->remove() - sdhci: Introduce ops to dump vendor specific registers - sdhci-cadence: Fix PHY write sequence - sdhci-esdhc-imx: Improve tunings - sdhci-esdhc-imx: Enable GPIO card detect as system wakeup - sdhci-esdhc-imx: Add HS400 support for i.MX6SLL - sdhci-esdhc-mcf: Add driver for the Coldfire/M5441X esdhc controller - m68k: mcf5441x: Add platform data to enable esdhc mmc controller - sdhci-msm: Improve HS400 tuning - sdhci-msm: Dump vendor specific registers at error - sdhci-msm: Add support for DLL/DDR properties provided from DT - sdhci-msm: Add support for the sm8250 variant - sdhci-msm: Add support for DVFS by converting to dev_pm_opp_set_rate() - sdhci-of-arasan: Add support for Intel Keem Bay variant - sdhci-of-arasan: Add support for Xilinx Versal SD variant - sdhci-of-dwcmshc: Add support for system suspend/resume - sdhci-of-dwcmshc: Fix UHS signaling support - sdhci-of-esdhc: Fix tuning for eMMC HS400 mode - sdhci-pci-gli: Add Genesys Logic GL9763E support - sdhci-sprd: Add support for the ->request_atomic() ops - sdhci-tegra: Avoid reading autocal timeout values when not applicable MEMSTICK: - Minor trivial update. -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAl7UuIIXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnzog/9GLeCHnaXan3uKU0NwWBpbiP0 RIqty/wYJpnWwL1J+otWLjVamuy/DSF4Bv4Fz+L+lXTzXWK+htXpWUragsvOeCj5 KHxSl/rASVo/zg5FOCCcq1+rYfRitAFqkWTYv0uFX+G/jcAcrspVJET3SfVYpVI4 fT2VP3zWXEh4yxJUCwnzqVPR3rsTdRob9csvpz2tuBRBUt0Vg+Kfc+NLi2EZzdsJ GDmiYOlch4Lx+8tUtRQQUX4MTVYMWCCkfL0RhYH8+kgcD8xK/LorN0Xb6FPTln95 hVPFTP3siojnj41UWQETqVnps7nsD+1ekLqehvNq6oLy7X4JDhWxu6bk3w7Gb2ox fk/L5x1ZFP+NFN8bvb3KPESfCKf4miuQ3fgRNad+FES7oqjN8ec55gB3oC2q5K8/ RrBFrtrcFTWBqxeYPxBMdBdj1tS7yfNPOavQg9iVGjzqJfnoMXfsKHrrTeZmGdZZ 4HudtV4BzSNUCmkvqho6TvFbLslfJ2a1RkV3tXijgbFknDoqD5rclm7KPp/ZQhH6 5ExnQpqd2EsHykJE2Zu4UxWW5f8TUBT2iBhVmTyQzrzijCtdmUkpbViaJXU0/yLD /k+MbiiJQ4ZTpcOFMeZ73J2WFU4xLyjY4magtWtBlPUZiONQy68zQ2GVdQ0AlXEt kbWvC7mMAYXiMJyAF9c= =1bkc -----END PGP SIGNATURE----- Merge tag 'mmc-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Enable erase/discard/trim support for all (e)MMC/SD hosts - Export information through sysfs about enhanced RPMB support (eMMC v5.1+) - Align the initialization commands for SDIO cards - Fix SDIO initialization to prevent memory leaks and NULL pointer errors - Do not export undefined MMC_NAME/MODALIAS for SDIO cards - Export device/vendor field from common CIS for SDIO cards - Move SDIO IDs from functional drivers to the common SDIO header - Introduce the ->request_atomic() host ops MMC host: - Improve support for HW busy signaling for several hosts - Converting some DT bindings to the json-schema - meson-mx-sdhc: Add driver and DT doc for the Amlogic Meson SDHC controller - meson-mx-sdio: Run a soft reset to recover from timeout/CRC error - mmci: Convert to use mmc_regulator_set_vqmmc() - mmci_stm32_sdmmc: Fix a couple of DMA bugs - mmci_stm32_sdmmc: Fix power on issue - renesas,mmcif,sdhci: Document r8a7742 DT bindings - renesas_sdhi: Add support for M3-W ES1.2 and 1.3 revisions - renesas_sdhi: Improvements to the TAP selection - renesas_sdhi/tmio: Further fixup runtime PM management at ->remove() - sdhci: Introduce ops to dump vendor specific registers - sdhci-cadence: Fix PHY write sequence - sdhci-esdhc-imx: Improve tunings - sdhci-esdhc-imx: Enable GPIO card detect as system wakeup - sdhci-esdhc-imx: Add HS400 support for i.MX6SLL - sdhci-esdhc-mcf: Add driver for the Coldfire/M5441X esdhc controller - m68k: mcf5441x: Add platform data to enable esdhc mmc controller - sdhci-msm: Improve HS400 tuning - sdhci-msm: Dump vendor specific registers at error - sdhci-msm: Add support for DLL/DDR properties provided from DT - sdhci-msm: Add support for the sm8250 variant - sdhci-msm: Add support for DVFS by converting to dev_pm_opp_set_rate() - sdhci-of-arasan: Add support for Intel Keem Bay variant - sdhci-of-arasan: Add support for Xilinx Versal SD variant - sdhci-of-dwcmshc: Add support for system suspend/resume - sdhci-of-dwcmshc: Fix UHS signaling support - sdhci-of-esdhc: Fix tuning for eMMC HS400 mode - sdhci-pci-gli: Add Genesys Logic GL9763E support - sdhci-sprd: Add support for the ->request_atomic() ops - sdhci-tegra: Avoid reading autocal timeout values when not applicable MEMSTICK: - Minor trivial update" * tag 'mmc-v5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (127 commits) dt-bindings: mmc: Convert sdhci-pxa to json-schema mmc: sdhci-msm: Clear tuning done flag while hs400 tuning mmc: core: Export device/vendor ids from Common CIS for SDIO cards mmc: core: Do not export MMC_NAME= and MODALIAS=mmc:block for SDIO cards mmc: sdhci-of-at91: fix CALCR register being rewritten mmc: sdhci-esdhc-imx: disable the CMD CRC check for standard tuning mmc: sdhci-esdhc-imx: fix the mask for tuning start point mmc: host: sdhci-esdhc-imx: add wakeup feature for GPIO CD pin mmc: mmci_sdmmc: fix DMA API warning max segment size mmc: mmci_sdmmc: fix DMA API warning overlapping mappings mmc: sdhci-of-arasan: Add support for Intel Keem Bay dt-bindings: mmc: arasan: Add compatible strings for Intel Keem Bay mmc: sdhci-cadence: fix PHY write mmc: sdio: Sort all SDIO IDs in common include file mmc: sdio: Fix Cypress SDIO IDs macros in common include file mmc: sdio: Move SDIO IDs from b43-sdio driver to common include file mmc: sdio: Move SDIO IDs from ath10k driver to common include file mmc: sdio: Move SDIO IDs from ath6kl driver to common include file mmc: sdio: Move SDIO IDs from smssdio driver to common include file mmc: sdio: Move SDIO IDs from btmtksdio driver to common include file ...
This commit is contained in:
Коммит
c5d6c13843
|
@ -0,0 +1,68 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/amlogic,meson-mx-sdhc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic Meson SDHC controller Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "mmc-controller.yaml"
|
||||
|
||||
maintainers:
|
||||
- Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
|
||||
description: |
|
||||
The SDHC MMC host controller on Amlogic SoCs provides an eMMC and MMC
|
||||
card interface with 1/4/8-bit bus width.
|
||||
It supports eMMC spec 4.4x/4.5x including HS200 (up to 100MHz clock).
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- amlogic,meson8-sdhc
|
||||
- amlogic,meson8b-sdhc
|
||||
- amlogic,meson8m2-sdhc
|
||||
- const: amlogic,meson-mx-sdhc
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 5
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: clkin0
|
||||
- const: clkin1
|
||||
- const: clkin2
|
||||
- const: clkin3
|
||||
- const: pclk
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
sdhc: mmc@8e00 {
|
||||
compatible = "amlogic,meson8-sdhc", "amlogic,meson-mx-sdhc";
|
||||
reg = <0x8e00 0x42>;
|
||||
interrupts = <GIC_SPI 78 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&xtal>,
|
||||
<&fclk_div4>,
|
||||
<&fclk_div3>,
|
||||
<&fclk_div5>,
|
||||
<&sdhc_pclk>;
|
||||
clock-names = "clkin0", "clkin1", "clkin2", "clkin3", "pclk";
|
||||
};
|
|
@ -18,12 +18,21 @@ Required Properties:
|
|||
- "xlnx,zynqmp-8.9a": ZynqMP SDHCI 8.9a PHY
|
||||
For this device it is strongly suggested to include clock-output-names and
|
||||
#clock-cells.
|
||||
- "xlnx,versal-8.9a": Versal SDHCI 8.9a PHY
|
||||
For this device it is strongly suggested to include clock-output-names and
|
||||
#clock-cells.
|
||||
- "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
|
||||
Note: This binding has been deprecated and moved to [5].
|
||||
- "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel LGM eMMC PHY
|
||||
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||
- "intel,lgm-sdhci-5.1-sdxc", "arasan,sdhci-5.1": Intel LGM SDXC PHY
|
||||
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||
- "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel Keem Bay eMMC
|
||||
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||
- "intel,keembay-sdhci-5.1-sd": Intel Keem Bay SD controller
|
||||
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||
- "intel,keembay-sdhci-5.1-sdio": Intel Keem Bay SDIO controller
|
||||
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
|
||||
|
||||
[5] Documentation/devicetree/bindings/mmc/sdhci-am654.txt
|
||||
|
||||
|
@ -104,6 +113,18 @@ Example:
|
|||
clk-phase-sd-hs = <63>, <72>;
|
||||
};
|
||||
|
||||
sdhci: mmc@f1040000 {
|
||||
compatible = "xlnx,versal-8.9a", "arasan,sdhci-8.9a";
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 126 4>;
|
||||
reg = <0x0 0xf1040000 0x0 0x10000>;
|
||||
clocks = <&clk200>, <&clk200>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clock-output-names = "clk_out_sd0", "clk_in_sd0";
|
||||
#clock-cells = <1>;
|
||||
clk-phase-sd-hs = <132>, <60>;
|
||||
};
|
||||
|
||||
emmc: sdhci@ec700000 {
|
||||
compatible = "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1";
|
||||
reg = <0xec700000 0x300>;
|
||||
|
@ -133,3 +154,39 @@ Example:
|
|||
phy-names = "phy_arasan";
|
||||
arasan,soc-ctl-syscon = <&sysconf>;
|
||||
};
|
||||
|
||||
mmc: mmc@33000000 {
|
||||
compatible = "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1";
|
||||
interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0x0 0x33000000 0x0 0x300>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>,
|
||||
<&scmi_clk KEEM_BAY_PSS_EMMC>;
|
||||
phys = <&emmc_phy>;
|
||||
phy-names = "phy_arasan";
|
||||
assigned-clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>;
|
||||
assigned-clock-rates = <200000000>;
|
||||
clock-output-names = "emmc_cardclock";
|
||||
#clock-cells = <0>;
|
||||
arasan,soc-ctl-syscon = <&mmc_phy_syscon>;
|
||||
};
|
||||
|
||||
sd0: mmc@31000000 {
|
||||
compatible = "intel,keembay-sdhci-5.1-sd";
|
||||
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0x0 0x31000000 0x0 0x300>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD0>,
|
||||
<&scmi_clk KEEM_BAY_PSS_SD0>;
|
||||
arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
|
||||
};
|
||||
|
||||
sd1: mmc@32000000 {
|
||||
compatible = "intel,keembay-sdhci-5.1-sdio";
|
||||
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reg = <0x0 0x32000000 0x0 0x300>;
|
||||
clock-names = "clk_xin", "clk_ahb";
|
||||
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD1>,
|
||||
<&scmi_clk KEEM_BAY_PSS_SD1>;
|
||||
arasan,soc-ctl-syscon = <&sd1_phy_syscon>;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@ Required properties:
|
|||
- "renesas,mmcif-r7s72100" for the MMCIF found in r7s72100 SoCs
|
||||
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
|
||||
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
|
||||
- "renesas,mmcif-r8a7742" for the MMCIF found in r8a7742 SoCs
|
||||
- "renesas,mmcif-r8a7743" for the MMCIF found in r8a7743 SoCs
|
||||
- "renesas,mmcif-r8a7744" for the MMCIF found in r8a7744 SoCs
|
||||
- "renesas,mmcif-r8a7745" for the MMCIF found in r8a7745 SoCs
|
||||
|
@ -24,8 +25,8 @@ Required properties:
|
|||
- interrupts: Some SoCs have only 1 shared interrupt, while others have either
|
||||
2 or 3 individual interrupts (error, int, card detect). Below is the number
|
||||
of interrupts for each SoC:
|
||||
1: r8a73a4, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791, r8a7793,
|
||||
r8a7794
|
||||
1: r8a73a4, r8a7742, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791,
|
||||
r8a7793, r8a7794
|
||||
2: r8a7740, sh73a0
|
||||
3: r7s72100
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ Required properties:
|
|||
"renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC
|
||||
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
|
||||
"renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
|
||||
"renesas,sdhi-r8a7742" - SDHI IP on R8A7742 SoC
|
||||
"renesas,sdhi-r8a7743" - SDHI IP on R8A7743 SoC
|
||||
"renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC
|
||||
"renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC
|
||||
|
|
|
@ -17,6 +17,7 @@ Required properties:
|
|||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
|
||||
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
||||
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
|
||||
|
@ -46,6 +47,13 @@ Required properties:
|
|||
"cal" - reference clock for RCLK delay calibration (optional)
|
||||
"sleep" - sleep clock for RCLK delay calibration (optional)
|
||||
|
||||
- qcom,ddr-config: Certain chipsets and platforms require particular settings
|
||||
for the DDR_CONFIG register. Use this field to specify the register
|
||||
value as per the Hardware Programming Guide.
|
||||
|
||||
- qcom,dll-config: Chipset and Platform specific value. Use this field to
|
||||
specify the DLL_CONFIG register value as per Hardware Programming Guide.
|
||||
|
||||
Example:
|
||||
|
||||
sdhc_1: sdhci@f9824900 {
|
||||
|
@ -63,6 +71,9 @@ Example:
|
|||
|
||||
clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
qcom,dll-config = <0x000f642c>;
|
||||
qcom,ddr-config = <0x80040868>;
|
||||
};
|
||||
|
||||
sdhc_2: sdhci@f98a4900 {
|
||||
|
@ -80,4 +91,7 @@ Example:
|
|||
|
||||
clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
|
||||
clock-names = "core", "iface";
|
||||
|
||||
qcom,dll-config = <0x0007642c>;
|
||||
qcom,ddr-config = <0x80040868>;
|
||||
};
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
* Marvell sdhci-pxa v2/v3 controller
|
||||
|
||||
This file documents differences between the core properties in mmc.txt
|
||||
and the properties used by the sdhci-pxav2 and sdhci-pxav3 drivers.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "mrvl,pxav2-mmc", "mrvl,pxav3-mmc" or
|
||||
"marvell,armada-380-sdhci".
|
||||
- reg:
|
||||
* for "mrvl,pxav2-mmc" and "mrvl,pxav3-mmc", one register area for
|
||||
the SDHCI registers.
|
||||
|
||||
* for "marvell,armada-380-sdhci", three register areas. The first
|
||||
one for the SDHCI registers themselves, the second one for the
|
||||
AXI/Mbus bridge registers of the SDHCI unit, the third one for the
|
||||
SDIO3 Configuration register
|
||||
- reg names: should be "sdhci", "mbus", "conf-sdio3". only mandatory
|
||||
for "marvell,armada-380-sdhci"
|
||||
- clocks: Array of clocks required for SDHCI; requires at least one for
|
||||
I/O clock.
|
||||
- clock-names: Array of names corresponding to clocks property; shall be
|
||||
"io" for I/O clock and "core" for optional core clock.
|
||||
|
||||
Optional properties:
|
||||
- mrvl,clk-delay-cycles: Specify a number of cycles to delay for tuning.
|
||||
|
||||
Example:
|
||||
|
||||
sdhci@d4280800 {
|
||||
compatible = "mrvl,pxav3-mmc";
|
||||
reg = <0xd4280800 0x800>;
|
||||
bus-width = <8>;
|
||||
interrupts = <27>;
|
||||
clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
|
||||
clock-names = "io", "core";
|
||||
non-removable;
|
||||
mrvl,clk-delay-cycles = <31>;
|
||||
};
|
||||
|
||||
sdhci@d8000 {
|
||||
compatible = "marvell,armada-380-sdhci";
|
||||
reg-names = "sdhci", "mbus", "conf-sdio3";
|
||||
reg = <0xd8000 0x1000>,
|
||||
<0xdc000 0x100>;
|
||||
<0x18454 0x4>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&gateclk 17>;
|
||||
clock-names = "io";
|
||||
mrvl,clk-delay-cycles = <0x1F>;
|
||||
};
|
|
@ -0,0 +1,102 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/sdhci-pxa.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Marvell PXA SDHCI v2/v3 bindings
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: marvell,armada-380-sdhci
|
||||
then:
|
||||
properties:
|
||||
regs:
|
||||
minItems: 3
|
||||
reg-names:
|
||||
minItems: 3
|
||||
required:
|
||||
- reg-names
|
||||
else:
|
||||
properties:
|
||||
regs:
|
||||
maxItems: 1
|
||||
reg-names:
|
||||
maxItems: 1
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- mrvl,pxav2-mmc
|
||||
- mrvl,pxav3-mmc
|
||||
- marvell,armada-380-sdhci
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 3
|
||||
|
||||
reg-names:
|
||||
items:
|
||||
- const: sdhci
|
||||
- const: mbus
|
||||
- const: conf-sdio3
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
- const: io
|
||||
- const: core
|
||||
|
||||
mrvl,clk-delay-cycles:
|
||||
description: Specify a number of cycles to delay for tuning.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/berlin2.h>
|
||||
mmc@d4280800 {
|
||||
compatible = "mrvl,pxav3-mmc";
|
||||
reg = <0xd4280800 0x800>;
|
||||
bus-width = <8>;
|
||||
interrupts = <27>;
|
||||
clocks = <&chip CLKID_SDIO1XIN>, <&chip CLKID_SDIO1>;
|
||||
clock-names = "io", "core";
|
||||
non-removable;
|
||||
mrvl,clk-delay-cycles = <31>;
|
||||
};
|
||||
- |
|
||||
mmc@d8000 {
|
||||
compatible = "marvell,armada-380-sdhci";
|
||||
reg-names = "sdhci", "mbus", "conf-sdio3";
|
||||
reg = <0xd8000 0x1000>,
|
||||
<0xdc000 0x100>,
|
||||
<0x18454 0x4>;
|
||||
interrupts = <0 25 0x4>;
|
||||
clocks = <&gateclk 17>;
|
||||
clock-names = "io";
|
||||
mrvl,clk-delay-cycles = <0x1F>;
|
||||
};
|
||||
|
||||
...
|
|
@ -6733,6 +6733,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/crypto/fsl-sec4.txt
|
||||
F: drivers/crypto/caam/
|
||||
|
||||
FREESCALE COLDFIRE M5441X MMC DRIVER
|
||||
M: Angelo Dureghello <angelo.dureghello@timesys.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/sdhci-esdhc-mcf.c
|
||||
F: include/linux/platform_data/mmc-esdhc-mcf.h
|
||||
|
||||
FREESCALE DIU FRAMEBUFFER DRIVER
|
||||
M: Timur Tabi <timur@kernel.org>
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <asm/mcfqspi.h>
|
||||
#include <linux/platform_data/edma.h>
|
||||
#include <linux/platform_data/dma-mcf-edma.h>
|
||||
#include <linux/platform_data/mmc-esdhc-mcf.h>
|
||||
|
||||
/*
|
||||
* All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
|
||||
|
@ -551,9 +552,35 @@ static struct platform_device mcf_edma = {
|
|||
.platform_data = &mcf_edma_data,
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_MCF_EDMA) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_MMC)
|
||||
static struct mcf_esdhc_platform_data mcf_esdhc_data = {
|
||||
.max_bus_width = 4,
|
||||
.cd_type = ESDHC_CD_NONE,
|
||||
};
|
||||
|
||||
static struct resource mcf_esdhc_resources[] = {
|
||||
{
|
||||
.start = MCFSDHC_BASE,
|
||||
.end = MCFSDHC_BASE + MCFSDHC_SIZE - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
}, {
|
||||
.start = MCF_IRQ_SDHC,
|
||||
.end = MCF_IRQ_SDHC,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device mcf_esdhc = {
|
||||
.name = "sdhci-esdhc-mcf",
|
||||
.id = 0,
|
||||
.num_resources = ARRAY_SIZE(mcf_esdhc_resources),
|
||||
.resource = mcf_esdhc_resources,
|
||||
.dev.platform_data = &mcf_esdhc_data,
|
||||
};
|
||||
#endif /* IS_ENABLED(CONFIG_MMC) */
|
||||
|
||||
static struct platform_device *mcf_devices[] __initdata = {
|
||||
&mcf_uart,
|
||||
#if IS_ENABLED(CONFIG_FEC)
|
||||
|
@ -586,6 +613,9 @@ static struct platform_device *mcf_devices[] __initdata = {
|
|||
#if IS_ENABLED(CONFIG_MCF_EDMA)
|
||||
&mcf_edma,
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_MMC)
|
||||
&mcf_esdhc,
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -614,4 +644,3 @@ static int __init mcf_init_devices(void)
|
|||
}
|
||||
|
||||
arch_initcall(mcf_init_devices);
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ DEFINE_CLK(0, "mcfssi.0", 47, MCF_CLK);
|
|||
DEFINE_CLK(0, "pll.0", 48, MCF_CLK);
|
||||
DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
|
||||
DEFINE_CLK(0, "mcfssi.1", 50, MCF_CLK);
|
||||
DEFINE_CLK(0, "mcfsdhc.0", 51, MCF_CLK);
|
||||
DEFINE_CLK(0, "sdhci-esdhc-mcf.0", 51, MCF_CLK);
|
||||
DEFINE_CLK(0, "enet-fec.0", 53, MCF_CLK);
|
||||
DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
|
||||
DEFINE_CLK(0, "switch.0", 55, MCF_CLK);
|
||||
|
@ -74,6 +74,10 @@ DEFINE_CLK(1, "mcfpwm.0", 34, MCF_BUSCLK);
|
|||
DEFINE_CLK(1, "sys.0", 36, MCF_BUSCLK);
|
||||
DEFINE_CLK(1, "gpio.0", 37, MCF_BUSCLK);
|
||||
|
||||
DEFINE_CLK(2, "ipg.0", 0, MCF_CLK);
|
||||
DEFINE_CLK(2, "ahb.0", 1, MCF_CLK);
|
||||
DEFINE_CLK(2, "per.0", 2, MCF_CLK);
|
||||
|
||||
struct clk *mcf_clks[] = {
|
||||
&__clk_0_2,
|
||||
&__clk_0_8,
|
||||
|
@ -131,6 +135,11 @@ struct clk *mcf_clks[] = {
|
|||
&__clk_1_34,
|
||||
&__clk_1_36,
|
||||
&__clk_1_37,
|
||||
|
||||
&__clk_2_0,
|
||||
&__clk_2_1,
|
||||
&__clk_2_2,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -151,6 +160,7 @@ static struct clk * const enable_clks[] __initconst = {
|
|||
&__clk_0_33, /* pit.1 */
|
||||
&__clk_0_37, /* eport */
|
||||
&__clk_0_48, /* pll */
|
||||
&__clk_0_51, /* esdhc */
|
||||
|
||||
&__clk_1_36, /* CCM/reset module/Power management */
|
||||
&__clk_1_37, /* gpio */
|
||||
|
@ -194,6 +204,21 @@ static struct clk * const disable_clks[] __initconst = {
|
|||
&__clk_1_29, /* uart 9 */
|
||||
};
|
||||
|
||||
static void __clk_enable2(struct clk *clk)
|
||||
{
|
||||
__raw_writel(__raw_readl(MCFSDHC_CLK) | (1 << clk->slot), MCFSDHC_CLK);
|
||||
}
|
||||
|
||||
static void __clk_disable2(struct clk *clk)
|
||||
{
|
||||
__raw_writel(__raw_readl(MCFSDHC_CLK) & ~(1 << clk->slot), MCFSDHC_CLK);
|
||||
}
|
||||
|
||||
struct clk_ops clk_ops2 = {
|
||||
.enable = __clk_enable2,
|
||||
.disable = __clk_disable2,
|
||||
};
|
||||
|
||||
static void __init m5441x_clk_init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
|
|
@ -278,6 +278,13 @@
|
|||
#define MCFGPIO_IRQ_VECBASE (MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
|
||||
#define MCFGPIO_PIN_MAX 87
|
||||
|
||||
/*
|
||||
* Phase Locked Loop (PLL)
|
||||
*/
|
||||
#define MCF_PLL_CR 0xFC0C0000
|
||||
#define MCF_PLL_DR 0xFC0C0004
|
||||
#define MCF_PLL_SR 0xFC0C0008
|
||||
|
||||
/*
|
||||
* DSPI module.
|
||||
*/
|
||||
|
@ -298,5 +305,13 @@
|
|||
#define MCFEDMA_IRQ_INTR16 (MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
|
||||
#define MCFEDMA_IRQ_INTR56 (MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
|
||||
#define MCFEDMA_IRQ_ERR (MCFINT0_VECBASE + MCFINT0_EDMA_ERR)
|
||||
/*
|
||||
* esdhc module.
|
||||
*/
|
||||
#define MCFSDHC_BASE 0xfc0cc000
|
||||
#define MCFSDHC_SIZE 256
|
||||
#define MCFINT2_SDHC 31
|
||||
#define MCF_IRQ_SDHC (MCFINT2_VECBASE + MCFINT2_SDHC)
|
||||
#define MCFSDHC_CLK (MCFSDHC_BASE + 0x2c)
|
||||
|
||||
#endif /* m5441xsim_h */
|
||||
|
|
|
@ -30,6 +30,8 @@ extern struct clk_ops clk_ops0;
|
|||
extern struct clk_ops clk_ops1;
|
||||
#endif /* MCFPM_PPMCR1 */
|
||||
|
||||
extern struct clk_ops clk_ops2;
|
||||
|
||||
#define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
|
||||
static struct clk __clk_##clk_bank##_##clk_slot = { \
|
||||
.name = clk_name, \
|
||||
|
|
|
@ -355,31 +355,31 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
|
|||
|
||||
static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
||||
/* Marvell SD8688 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
|
||||
/* Marvell SD8787 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||
/* Marvell SD8787 Bluetooth AMP device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT_AMP),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||
/* Marvell SD8797 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
|
||||
/* Marvell SD8887 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
|
||||
/* Marvell SD8897 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
|
||||
/* Marvell SD8977 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9146),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8977 },
|
||||
/* Marvell SD8987 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x914A),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8987 },
|
||||
/* Marvell SD8997 Bluetooth device */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_BT),
|
||||
.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
|
|
|
@ -51,9 +51,9 @@ static const struct btmtksdio_data mt7668_data = {
|
|||
};
|
||||
|
||||
static const struct sdio_device_id btmtksdio_table[] = {
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7663),
|
||||
.driver_data = (kernel_ulong_t)&mt7663_data },
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7668),
|
||||
.driver_data = (kernel_ulong_t)&mt7668_data },
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
|
|
@ -58,15 +58,15 @@ static const struct sdio_device_id smssdio_ids[] = {
|
|||
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_MING),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_MING},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_PELE),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_PELE},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_RIO),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_RIO},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_2160),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_1530),
|
||||
.driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
|
|
@ -93,6 +93,20 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||
return retval;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
|
||||
retval = add_uevent_var(env, "SDIO_ID=%04X:%04X",
|
||||
card->cis.vendor, card->cis.device);
|
||||
if (retval)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* SDIO (non-combo) cards are not handled by mmc_block driver and do not
|
||||
* have accessible CID register which used by mmc_card_name() function.
|
||||
*/
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
return 0;
|
||||
|
||||
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
|
||||
if (retval)
|
||||
return retval;
|
||||
|
|
|
@ -1815,8 +1815,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
|
|||
unsigned int rem, to = from + nr;
|
||||
int err;
|
||||
|
||||
if (!(card->host->caps & MMC_CAP_ERASE) ||
|
||||
!(card->csd.cmdclass & CCC_ERASE))
|
||||
if (!(card->csd.cmdclass & CCC_ERASE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!card->erase_size)
|
||||
|
@ -1872,8 +1871,7 @@ EXPORT_SYMBOL(mmc_erase);
|
|||
|
||||
int mmc_can_erase(struct mmc_card *card)
|
||||
{
|
||||
if ((card->host->caps & MMC_CAP_ERASE) &&
|
||||
(card->csd.cmdclass & CCC_ERASE) && card->erase_size)
|
||||
if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@ static int mmc_clock_opt_set(void *data, u64 val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
"%llu\n");
|
||||
|
||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
|
@ -232,8 +232,8 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
|||
debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
|
||||
debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
|
||||
debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
|
||||
debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops);
|
||||
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops);
|
||||
|
||||
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||
if (fail_request)
|
||||
|
|
|
@ -647,6 +647,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
|
|||
mmc_hostname(card->host),
|
||||
card->ext_csd.cmdq_depth);
|
||||
}
|
||||
card->ext_csd.enhanced_rpmb_supported =
|
||||
(card->ext_csd.rel_param &
|
||||
EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
|
@ -786,6 +789,8 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
|||
card->ext_csd.enhanced_area_offset);
|
||||
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
||||
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
|
||||
MMC_DEV_ATTR(enhanced_rpmb_supported, "%#x\n",
|
||||
card->ext_csd.enhanced_rpmb_supported);
|
||||
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
||||
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
||||
|
@ -843,6 +848,7 @@ static struct attribute *mmc_std_attrs[] = {
|
|||
&dev_attr_enhanced_area_offset.attr,
|
||||
&dev_attr_enhanced_area_size.attr,
|
||||
&dev_attr_raw_rpmb_size_mult.attr,
|
||||
&dev_attr_enhanced_rpmb_supported.attr,
|
||||
&dev_attr_rel_sectors.attr,
|
||||
&dev_attr_ocr.attr,
|
||||
&dev_attr_rca.attr,
|
||||
|
|
|
@ -139,7 +139,7 @@ static const struct mmc_fixup sdio_fixup_methods[] = {
|
|||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
||||
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN,
|
||||
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_F0,
|
||||
add_limit_rate_quirk, 150000000),
|
||||
|
||||
END_FIXUP
|
||||
|
|
|
@ -136,6 +136,8 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
|
|||
int min_uV, int target_uV,
|
||||
int max_uV)
|
||||
{
|
||||
int current_uV;
|
||||
|
||||
/*
|
||||
* Check if supported first to avoid errors since we may try several
|
||||
* signal levels during power up and don't want to show errors.
|
||||
|
@ -143,6 +145,14 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
|
|||
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The voltage is already set, no need to switch.
|
||||
* Return 1 to indicate that no switch happened.
|
||||
*/
|
||||
current_uV = regulator_get_voltage(regulator);
|
||||
if (current_uV == target_uV)
|
||||
return 1;
|
||||
|
||||
return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
|
||||
max_uV);
|
||||
}
|
||||
|
@ -198,9 +208,10 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
* voltage in two steps and try to stay close to vmmc
|
||||
* with a 0.3V tolerance at first.
|
||||
*/
|
||||
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
min_uV, volt, max_uV))
|
||||
return 0;
|
||||
ret = mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
min_uV, volt, max_uV);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||
2700000, volt, 3600000);
|
||||
|
|
|
@ -376,11 +376,11 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
|||
if (!status)
|
||||
return -ENOMEM;
|
||||
|
||||
err = mmc_sd_switch(card, 1, 0, 1, status);
|
||||
err = mmc_sd_switch(card, 1, 0, HIGH_SPEED_BUS_SPEED, status);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if ((status[16] & 0xF) != 1) {
|
||||
if ((status[16] & 0xF) != HIGH_SPEED_BUS_SPEED) {
|
||||
pr_warn("%s: Problem switching card into high-speed mode!\n",
|
||||
mmc_hostname(card->host));
|
||||
err = 0;
|
||||
|
@ -707,7 +707,12 @@ static ssize_t mmc_dsr_show(struct device *dev,
|
|||
|
||||
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
||||
|
||||
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
||||
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
||||
|
||||
static struct attribute *sd_std_attrs[] = {
|
||||
&dev_attr_vendor.attr,
|
||||
&dev_attr_device.attr,
|
||||
&dev_attr_cid.attr,
|
||||
&dev_attr_csd.attr,
|
||||
&dev_attr_scr.attr,
|
||||
|
@ -726,7 +731,26 @@ static struct attribute *sd_std_attrs[] = {
|
|||
&dev_attr_dsr.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(sd_std);
|
||||
|
||||
static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct mmc_card *card = mmc_dev_to_card(dev);
|
||||
|
||||
/* CIS vendor and device ids are available only for Combo cards */
|
||||
if ((attr == &dev_attr_vendor.attr || attr == &dev_attr_device.attr) &&
|
||||
card->type != MMC_TYPE_SD_COMBO)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group sd_std_group = {
|
||||
.attrs = sd_std_attrs,
|
||||
.is_visible = sd_std_is_visible,
|
||||
};
|
||||
__ATTRIBUTE_GROUPS(sd_std);
|
||||
|
||||
struct device_type sd_type = {
|
||||
.groups = sd_std_groups,
|
||||
|
|
|
@ -27,6 +27,24 @@
|
|||
#include "sdio_ops.h"
|
||||
#include "sdio_cis.h"
|
||||
|
||||
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
||||
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
||||
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
||||
|
||||
static struct attribute *sdio_std_attrs[] = {
|
||||
&dev_attr_vendor.attr,
|
||||
&dev_attr_device.attr,
|
||||
&dev_attr_ocr.attr,
|
||||
&dev_attr_rca.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(sdio_std);
|
||||
|
||||
static struct device_type sdio_type = {
|
||||
.groups = sdio_std_groups,
|
||||
};
|
||||
|
||||
static int sdio_read_fbr(struct sdio_func *func)
|
||||
{
|
||||
int ret;
|
||||
|
@ -543,13 +561,33 @@ out:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void mmc_sdio_resend_if_cond(struct mmc_host *host,
|
||||
struct mmc_card *card)
|
||||
static int mmc_sdio_pre_init(struct mmc_host *host, u32 ocr,
|
||||
struct mmc_card *card)
|
||||
{
|
||||
if (card)
|
||||
mmc_remove_card(card);
|
||||
|
||||
/*
|
||||
* Reset the card by performing the same steps that are taken by
|
||||
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
|
||||
*
|
||||
* sdio_reset() is technically not needed. Having just powered up the
|
||||
* hardware, it should already be in reset state. However, some
|
||||
* platforms (such as SD8686 on OLPC) do not instantly cut power,
|
||||
* meaning that a reset is required when restoring power soon after
|
||||
* powering off. It is harmless in other cases.
|
||||
*
|
||||
* The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
|
||||
* is not necessary for non-removable cards. However, it is required
|
||||
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
|
||||
* harmless in other situations.
|
||||
*
|
||||
*/
|
||||
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->ocr_avail);
|
||||
mmc_remove_card(card);
|
||||
mmc_send_if_cond(host, ocr);
|
||||
return mmc_send_io_op_cond(host, 0, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -584,7 +622,7 @@ try_again:
|
|||
*/
|
||||
err = mmc_send_io_op_cond(host, ocr, &rocr);
|
||||
if (err)
|
||||
goto err;
|
||||
return err;
|
||||
|
||||
/*
|
||||
* For SPI, enable CRC as appropriate.
|
||||
|
@ -592,17 +630,15 @@ try_again:
|
|||
if (mmc_host_is_spi(host)) {
|
||||
err = mmc_spi_set_crc(host, use_spi_crc);
|
||||
if (err)
|
||||
goto err;
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate card structure.
|
||||
*/
|
||||
card = mmc_alloc_card(host, NULL);
|
||||
if (IS_ERR(card)) {
|
||||
err = PTR_ERR(card);
|
||||
goto err;
|
||||
}
|
||||
card = mmc_alloc_card(host, &sdio_type);
|
||||
if (IS_ERR(card))
|
||||
return PTR_ERR(card);
|
||||
|
||||
if ((rocr & R4_MEMORY_PRESENT) &&
|
||||
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
|
||||
|
@ -610,19 +646,15 @@ try_again:
|
|||
|
||||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
||||
mmc_remove_card(card);
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
}
|
||||
} else {
|
||||
card->type = MMC_TYPE_SDIO;
|
||||
|
||||
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
||||
mmc_remove_card(card);
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -646,7 +678,7 @@ try_again:
|
|||
if (rocr & ocr & R4_18V_PRESENT) {
|
||||
err = mmc_set_uhs_voltage(host, ocr_card);
|
||||
if (err == -EAGAIN) {
|
||||
mmc_sdio_resend_if_cond(host, card);
|
||||
mmc_sdio_pre_init(host, ocr_card, card);
|
||||
retries--;
|
||||
goto try_again;
|
||||
} else if (err) {
|
||||
|
@ -677,7 +709,7 @@ try_again:
|
|||
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
||||
err = mmc_sd_get_csd(host, card);
|
||||
if (err)
|
||||
return err;
|
||||
goto remove;
|
||||
|
||||
mmc_decode_cid(card);
|
||||
}
|
||||
|
@ -704,7 +736,12 @@ try_again:
|
|||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
||||
}
|
||||
|
||||
goto finish;
|
||||
if (oldcard)
|
||||
mmc_remove_card(card);
|
||||
else
|
||||
host->card = card;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -713,14 +750,13 @@ try_again:
|
|||
*/
|
||||
err = sdio_read_cccr(card, ocr);
|
||||
if (err) {
|
||||
mmc_sdio_resend_if_cond(host, card);
|
||||
mmc_sdio_pre_init(host, ocr_card, card);
|
||||
if (ocr & R4_18V_PRESENT) {
|
||||
/* Retry init sequence, but without R4_18V_PRESENT. */
|
||||
retries = 0;
|
||||
goto try_again;
|
||||
} else {
|
||||
goto remove;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -731,16 +767,14 @@ try_again:
|
|||
goto remove;
|
||||
|
||||
if (oldcard) {
|
||||
int same = (card->cis.vendor == oldcard->cis.vendor &&
|
||||
card->cis.device == oldcard->cis.device);
|
||||
mmc_remove_card(card);
|
||||
if (!same) {
|
||||
pr_debug("%s: Perhaps the card was replaced\n",
|
||||
mmc_hostname(host));
|
||||
return -ENOENT;
|
||||
if (card->cis.vendor == oldcard->cis.vendor &&
|
||||
card->cis.device == oldcard->cis.device) {
|
||||
mmc_remove_card(card);
|
||||
card = oldcard;
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
}
|
||||
|
||||
card = oldcard;
|
||||
}
|
||||
card->ocr = ocr_card;
|
||||
mmc_fixup_device(card, sdio_fixup_methods);
|
||||
|
@ -801,16 +835,15 @@ try_again:
|
|||
err = -EINVAL;
|
||||
goto remove;
|
||||
}
|
||||
finish:
|
||||
if (!oldcard)
|
||||
host->card = card;
|
||||
|
||||
host->card = card;
|
||||
return 0;
|
||||
|
||||
mismatch:
|
||||
pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
|
||||
remove:
|
||||
if (!oldcard)
|
||||
if (oldcard != card)
|
||||
mmc_remove_card(card);
|
||||
|
||||
err:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -818,28 +851,7 @@ static int mmc_sdio_reinit_card(struct mmc_host *host)
|
|||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Reset the card by performing the same steps that are taken by
|
||||
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
|
||||
*
|
||||
* sdio_reset() is technically not needed. Having just powered up the
|
||||
* hardware, it should already be in reset state. However, some
|
||||
* platforms (such as SD8686 on OLPC) do not instantly cut power,
|
||||
* meaning that a reset is required when restoring power soon after
|
||||
* powering off. It is harmless in other cases.
|
||||
*
|
||||
* The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
|
||||
* is not necessary for non-removable cards. However, it is required
|
||||
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
|
||||
* harmless in other situations.
|
||||
*
|
||||
*/
|
||||
|
||||
sdio_reset(host);
|
||||
mmc_go_idle(host);
|
||||
mmc_send_if_cond(host, host->card->ocr);
|
||||
|
||||
ret = mmc_send_io_op_cond(host, 0, NULL);
|
||||
ret = mmc_sdio_pre_init(host, host->card->ocr, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -171,7 +171,7 @@ config MMC_SDHCI_OF_ASPEED
|
|||
config MMC_SDHCI_OF_AT91
|
||||
tristate "SDHCI OF support for the Atmel SDMMC controller"
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
depends on OF
|
||||
depends on OF && HAVE_CLK
|
||||
help
|
||||
This selects the Atmel SDMMC driver
|
||||
|
||||
|
@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_ESDHC_MCF
|
||||
tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
|
||||
depends on M5441x
|
||||
depends on MMC_SDHCI_PLTFM
|
||||
select MMC_SDHCI_IO_ACCESSORS
|
||||
help
|
||||
This selects the Freescale eSDHC controller support for
|
||||
ColdFire mcf5441x devices.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_SDHCI_ESDHC_IMX
|
||||
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
||||
depends on ARCH_MXC
|
||||
|
@ -405,6 +418,20 @@ config MMC_MESON_GX
|
|||
|
||||
If you have a controller with this interface, say Y here.
|
||||
|
||||
config MMC_MESON_MX_SDHC
|
||||
tristate "Amlogic Meson SDHC Host Controller support"
|
||||
depends on (ARM && ARCH_MESON) || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
depends on OF
|
||||
help
|
||||
This selects support for the SDHC Host Controller on
|
||||
Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
|
||||
The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
|
||||
with 1, 4, and 8 bit bus widths.
|
||||
|
||||
If you have a controller with this interface, say Y or M here.
|
||||
If unsure, say N.
|
||||
|
||||
config MMC_MESON_MX_SDIO
|
||||
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
|
|
|
@ -68,6 +68,8 @@ obj-$(CONFIG_MMC_VUB300) += vub300.o
|
|||
obj-$(CONFIG_MMC_USHC) += ushc.o
|
||||
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
||||
obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
|
||||
meson-mx-sdhc-objs := meson-mx-sdhc-clkc.o meson-mx-sdhc-mmc.o
|
||||
obj-$(CONFIG_MMC_MESON_MX_SDHC) += meson-mx-sdhc.o
|
||||
obj-$(CONFIG_MMC_MESON_MX_SDIO) += meson-mx-sdio.o
|
||||
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
||||
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
|
||||
|
@ -82,6 +84,7 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
|
|||
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
|
||||
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
|
||||
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o
|
||||
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
||||
|
@ -404,14 +403,6 @@ static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||
host->mrq = req;
|
||||
goldfish_mmc_prepare_data(host, req);
|
||||
goldfish_mmc_start_command(host, req->cmd);
|
||||
|
||||
/*
|
||||
* This is to avoid accidentally being detected as an SDIO card
|
||||
* in mmc_attach_sdio().
|
||||
*/
|
||||
if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
|
||||
req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR))
|
||||
req->cmd->error = -EINVAL;
|
||||
}
|
||||
|
||||
static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
|
@ -482,6 +473,7 @@ static int goldfish_mmc_probe(struct platform_device *pdev)
|
|||
mmc->f_max = 24000000;
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||
mmc->caps2 = MMC_CAP2_NO_SDIO;
|
||||
|
||||
/* Use scatterlist DMA to reduce per-transfer costs.
|
||||
* NOTE max_seg_size assumption that small blocks aren't
|
||||
|
|
|
@ -169,6 +169,7 @@
|
|||
#define atmci_writel(port, reg, value) \
|
||||
__raw_writel((value), (port)->regs + reg)
|
||||
|
||||
#define ATMCI_CMD_TIMEOUT_MS 2000
|
||||
#define AUTOSUSPEND_DELAY 50
|
||||
|
||||
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
||||
|
@ -808,6 +809,9 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
|
|||
static void atmci_send_command(struct atmel_mci *host,
|
||||
struct mmc_command *cmd, u32 cmd_flags)
|
||||
{
|
||||
unsigned int timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||
ATMCI_CMD_TIMEOUT_MS;
|
||||
|
||||
WARN_ON(host->cmd);
|
||||
host->cmd = cmd;
|
||||
|
||||
|
@ -817,6 +821,8 @@ static void atmci_send_command(struct atmel_mci *host,
|
|||
|
||||
atmci_writel(host, ATMCI_ARGR, cmd->arg);
|
||||
atmci_writel(host, ATMCI_CMDR, cmd_flags);
|
||||
|
||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
|
||||
}
|
||||
|
||||
static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
|
||||
|
@ -1314,8 +1320,6 @@ static void atmci_start_request(struct atmel_mci *host,
|
|||
* prepared yet.)
|
||||
*/
|
||||
atmci_writel(host, ATMCI_IER, iflags);
|
||||
|
||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
|
||||
}
|
||||
|
||||
static void atmci_queue_request(struct atmel_mci *host,
|
||||
|
@ -1557,6 +1561,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
|||
|
||||
WARN_ON(host->cmd || host->data);
|
||||
|
||||
del_timer(&host->timer);
|
||||
|
||||
/*
|
||||
* Update the MMC clock rate if necessary. This may be
|
||||
* necessary if set_ios() is called when a different slot is
|
||||
|
@ -1583,8 +1589,6 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
|
|||
host->state = STATE_IDLE;
|
||||
}
|
||||
|
||||
del_timer(&host->timer);
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
mmc_request_done(prev_mmc, mrq);
|
||||
spin_lock(&host->lock);
|
||||
|
|
|
@ -259,7 +259,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
|
|||
au1xmmc_finish_request(host);
|
||||
}
|
||||
|
||||
static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
|
||||
static int au1xmmc_send_command(struct au1xmmc_host *host,
|
||||
struct mmc_command *cmd, struct mmc_data *data)
|
||||
{
|
||||
u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
|
||||
|
@ -302,9 +302,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
|
|||
__raw_writel(cmd->arg, HOST_CMDARG(host));
|
||||
wmb(); /* drain writebuffer */
|
||||
|
||||
if (wait)
|
||||
IRQ_OFF(host, SD_CONFIG_CR);
|
||||
|
||||
__raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
|
||||
wmb(); /* drain writebuffer */
|
||||
|
||||
|
@ -312,19 +309,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
|
|||
while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO)
|
||||
/* nop */;
|
||||
|
||||
/* Wait for the command to come back */
|
||||
if (wait) {
|
||||
u32 status = __raw_readl(HOST_STATUS(host));
|
||||
|
||||
while (!(status & SD_STATUS_CR))
|
||||
status = __raw_readl(HOST_STATUS(host));
|
||||
|
||||
/* Clear the CR status */
|
||||
__raw_writel(SD_STATUS_CR, HOST_STATUS(host));
|
||||
|
||||
IRQ_ON(host, SD_CONFIG_CR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -711,7 +695,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
|
|||
}
|
||||
|
||||
if (!ret)
|
||||
ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
|
||||
ret = au1xmmc_send_command(host, mrq->cmd, mrq->data);
|
||||
|
||||
if (ret) {
|
||||
mrq->cmd->error = ret;
|
||||
|
|
|
@ -1280,8 +1280,7 @@ static int bcm2835_add_host(struct bcm2835_host *host)
|
|||
|
||||
/* host controller capabilities */
|
||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE |
|
||||
MMC_CAP_CMD23;
|
||||
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_CMD23;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
mutex_init(&host->mutex);
|
||||
|
|
|
@ -1038,8 +1038,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
|
|||
* Disable bounce buffers for max_segs = 1
|
||||
*/
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD |
|
||||
MMC_CAP_3_3V_DDR;
|
||||
MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR;
|
||||
|
||||
if (host->use_sg)
|
||||
mmc->max_segs = 16;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include <linux/delay.h>
|
||||
#include "cb710-mmc.h"
|
||||
|
||||
#define CB710_MMC_REQ_TIMEOUT_MS 2000
|
||||
|
||||
static const u8 cb710_clock_divider_log2[8] = {
|
||||
/* 1, 2, 4, 8, 16, 32, 128, 512 */
|
||||
0, 1, 2, 3, 4, 5, 7, 9
|
||||
|
@ -707,6 +709,12 @@ static int cb710_mmc_init(struct platform_device *pdev)
|
|||
mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
|
||||
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||
/*
|
||||
* In cb710_wait_for_event() we use a fixed timeout of ~2s, hence let's
|
||||
* inform the core about it. A future improvement should instead make
|
||||
* use of the cmd->busy_timeout.
|
||||
*/
|
||||
mmc->max_busy_timeout = CB710_MMC_REQ_TIMEOUT_MS;
|
||||
|
||||
reader = mmc_priv(mmc);
|
||||
|
||||
|
|
|
@ -424,7 +424,7 @@ static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc,
|
|||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_err(host->dev, "Regulator set error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1546,8 +1546,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_dbg(&mmc->class_dev,
|
||||
"Regulator set error %d - %s V\n",
|
||||
ret, uhs & v18 ? "1.8" : "3.3");
|
||||
|
@ -2752,12 +2751,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
|
|||
if (host->pdata->caps)
|
||||
mmc->caps = host->pdata->caps;
|
||||
|
||||
/*
|
||||
* Support MMC_CAP_ERASE by default.
|
||||
* It needs to use trim/discard/erase commands.
|
||||
*/
|
||||
mmc->caps |= MMC_CAP_ERASE;
|
||||
|
||||
if (host->pdata->pm_caps)
|
||||
mmc->pm_caps = host->pdata->pm_caps;
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
#define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0)
|
||||
|
||||
#define JZ_MMC_CLK_RATE 24000000
|
||||
#define JZ_MMC_REQ_TIMEOUT_MS 5000
|
||||
|
||||
enum jz4740_mmc_version {
|
||||
JZ_MMC_JZ4740,
|
||||
|
@ -440,7 +441,8 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
|
|||
|
||||
if (timeout == 0) {
|
||||
set_bit(0, &host->waiting);
|
||||
mod_timer(&host->timeout_timer, jiffies + 5*HZ);
|
||||
mod_timer(&host->timeout_timer,
|
||||
jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
|
||||
jz4740_mmc_set_irq_enabled(host, irq, true);
|
||||
return true;
|
||||
}
|
||||
|
@ -893,7 +895,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
|
|||
|
||||
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
|
||||
set_bit(0, &host->waiting);
|
||||
mod_timer(&host->timeout_timer, jiffies + 5*HZ);
|
||||
mod_timer(&host->timeout_timer,
|
||||
jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
|
||||
jz4740_mmc_send_command(host, req->cmd);
|
||||
}
|
||||
|
||||
|
@ -1023,6 +1026,12 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
|
|||
mmc->f_min = mmc->f_max / 128;
|
||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
/*
|
||||
* We use a fixed timeout of 5s, hence inform the core about it. A
|
||||
* future improvement should instead respect the cmd->busy_timeout.
|
||||
*/
|
||||
mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS;
|
||||
|
||||
mmc->max_blk_size = (1 << 10) - 1;
|
||||
mmc->max_blk_count = (1 << 15) - 1;
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
|
|
@ -1004,6 +1004,8 @@ static int meson_mmc_card_busy(struct mmc_host *mmc)
|
|||
|
||||
static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* vqmmc regulator is available */
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
/*
|
||||
|
@ -1013,7 +1015,8 @@ static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
* to 1.8v. Please make sure the regulator framework is aware
|
||||
* of your own regulator constraints
|
||||
*/
|
||||
return mmc_regulator_set_vqmmc(mmc, ios);
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Amlogic Meson SDHC clock controller
|
||||
*
|
||||
* Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "meson-mx-sdhc.h"
|
||||
|
||||
#define MESON_SDHC_NUM_BUILTIN_CLKS 6
|
||||
|
||||
struct meson_mx_sdhc_clkc {
|
||||
struct clk_mux src_sel;
|
||||
struct clk_divider div;
|
||||
struct clk_gate mod_clk_en;
|
||||
struct clk_gate tx_clk_en;
|
||||
struct clk_gate rx_clk_en;
|
||||
struct clk_gate sd_clk_en;
|
||||
};
|
||||
|
||||
static const struct clk_parent_data meson_mx_sdhc_src_sel_parents[4] = {
|
||||
{ .fw_name = "clkin0" },
|
||||
{ .fw_name = "clkin1" },
|
||||
{ .fw_name = "clkin2" },
|
||||
{ .fw_name = "clkin3" },
|
||||
};
|
||||
|
||||
static const struct clk_div_table meson_mx_sdhc_div_table[] = {
|
||||
{ .div = 6, .val = 5, },
|
||||
{ .div = 8, .val = 7, },
|
||||
{ .div = 9, .val = 8, },
|
||||
{ .div = 10, .val = 9, },
|
||||
{ .div = 12, .val = 11, },
|
||||
{ .div = 16, .val = 15, },
|
||||
{ .div = 18, .val = 17, },
|
||||
{ .div = 34, .val = 33, },
|
||||
{ .div = 142, .val = 141, },
|
||||
{ .div = 850, .val = 849, },
|
||||
{ .div = 2126, .val = 2125, },
|
||||
{ .div = 4096, .val = 4095, },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static int meson_mx_sdhc_clk_hw_register(struct device *dev,
|
||||
const char *name_suffix,
|
||||
const struct clk_parent_data *parents,
|
||||
unsigned int num_parents,
|
||||
const struct clk_ops *ops,
|
||||
struct clk_hw *hw)
|
||||
{
|
||||
struct clk_init_data init = { };
|
||||
char clk_name[32];
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "%s#%s", dev_name(dev),
|
||||
name_suffix);
|
||||
|
||||
init.name = clk_name;
|
||||
init.ops = ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_data = parents;
|
||||
init.num_parents = num_parents;
|
||||
|
||||
hw->init = &init;
|
||||
|
||||
return devm_clk_hw_register(dev, hw);
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev,
|
||||
const char *name_suffix,
|
||||
struct clk_hw *parent,
|
||||
struct clk_hw *hw)
|
||||
{
|
||||
struct clk_parent_data parent_data = { .hw = parent };
|
||||
|
||||
return meson_mx_sdhc_clk_hw_register(dev, name_suffix, &parent_data, 1,
|
||||
&clk_gate_ops, hw);
|
||||
}
|
||||
|
||||
int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
|
||||
struct clk_bulk_data *clk_bulk_data)
|
||||
{
|
||||
struct clk_parent_data div_parent = { };
|
||||
struct meson_mx_sdhc_clkc *clkc_data;
|
||||
int ret;
|
||||
|
||||
clkc_data = devm_kzalloc(dev, sizeof(*clkc_data), GFP_KERNEL);
|
||||
if (!clkc_data)
|
||||
return -ENOMEM;
|
||||
|
||||
clkc_data->src_sel.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->src_sel.mask = 0x3;
|
||||
clkc_data->src_sel.shift = 16;
|
||||
ret = meson_mx_sdhc_clk_hw_register(dev, "src_sel",
|
||||
meson_mx_sdhc_src_sel_parents, 4,
|
||||
&clk_mux_ops,
|
||||
&clkc_data->src_sel.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkc_data->div.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->div.shift = 0;
|
||||
clkc_data->div.width = 12;
|
||||
clkc_data->div.table = meson_mx_sdhc_div_table;
|
||||
div_parent.hw = &clkc_data->src_sel.hw;
|
||||
ret = meson_mx_sdhc_clk_hw_register(dev, "div", &div_parent, 1,
|
||||
&clk_divider_ops,
|
||||
&clkc_data->div.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkc_data->mod_clk_en.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->mod_clk_en.bit_idx = 15;
|
||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "mod_clk_on",
|
||||
&clkc_data->div.hw,
|
||||
&clkc_data->mod_clk_en.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkc_data->tx_clk_en.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->tx_clk_en.bit_idx = 14;
|
||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "tx_clk_on",
|
||||
&clkc_data->div.hw,
|
||||
&clkc_data->tx_clk_en.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkc_data->rx_clk_en.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->rx_clk_en.bit_idx = 13;
|
||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "rx_clk_on",
|
||||
&clkc_data->div.hw,
|
||||
&clkc_data->rx_clk_en.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clkc_data->sd_clk_en.reg = base + MESON_SDHC_CLKC;
|
||||
clkc_data->sd_clk_en.bit_idx = 12;
|
||||
ret = meson_mx_sdhc_gate_clk_hw_register(dev, "sd_clk_on",
|
||||
&clkc_data->div.hw,
|
||||
&clkc_data->sd_clk_en.hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* TODO: Replace clk_hw.clk with devm_clk_hw_get_clk() once that is
|
||||
* available.
|
||||
*/
|
||||
clk_bulk_data[0].clk = clkc_data->mod_clk_en.hw.clk;
|
||||
clk_bulk_data[1].clk = clkc_data->sd_clk_en.hw.clk;
|
||||
clk_bulk_data[2].clk = clkc_data->tx_clk_en.hw.clk;
|
||||
clk_bulk_data[3].clk = clkc_data->rx_clk_en.hw.clk;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,914 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Amlogic Meson6/Meson8/Meson8b/Meson8m2 SDHC MMC host controller driver.
|
||||
*
|
||||
* Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sdio.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
|
||||
#include "meson-mx-sdhc.h"
|
||||
|
||||
#define MESON_SDHC_NUM_BULK_CLKS 4
|
||||
#define MESON_SDHC_MAX_BLK_SIZE 512
|
||||
#define MESON_SDHC_NUM_TUNING_TRIES 10
|
||||
|
||||
#define MESON_SDHC_WAIT_CMD_READY_SLEEP_US 1
|
||||
#define MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US 100000
|
||||
#define MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US 1
|
||||
#define MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US 200
|
||||
|
||||
struct meson_mx_sdhc_data {
|
||||
void (*init_hw)(struct mmc_host *mmc);
|
||||
void (*set_pdma)(struct mmc_host *mmc);
|
||||
void (*wait_before_send)(struct mmc_host *mmc);
|
||||
bool hardware_flush_all_cmds;
|
||||
};
|
||||
|
||||
struct meson_mx_sdhc_host {
|
||||
struct mmc_host *mmc;
|
||||
|
||||
struct mmc_request *mrq;
|
||||
struct mmc_command *cmd;
|
||||
int error;
|
||||
|
||||
struct regmap *regmap;
|
||||
|
||||
struct clk *pclk;
|
||||
struct clk *sd_clk;
|
||||
struct clk_bulk_data bulk_clks[MESON_SDHC_NUM_BULK_CLKS];
|
||||
bool bulk_clks_enabled;
|
||||
|
||||
const struct meson_mx_sdhc_data *platform;
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_mx_sdhc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = MESON_SDHC_CLK2,
|
||||
};
|
||||
|
||||
static void meson_mx_sdhc_hw_reset(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_MAIN_CTRL |
|
||||
MESON_SDHC_SRST_RXFIFO | MESON_SDHC_SRST_TXFIFO |
|
||||
MESON_SDHC_SRST_DPHY_RX | MESON_SDHC_SRST_DPHY_TX |
|
||||
MESON_SDHC_SRST_DMA_IF);
|
||||
usleep_range(10, 100);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_SRST, 0);
|
||||
usleep_range(10, 100);
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_clear_fifo(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 stat;
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
|
||||
if (!FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) &&
|
||||
!FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
|
||||
return;
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_SRST, MESON_SDHC_SRST_RXFIFO |
|
||||
MESON_SDHC_SRST_TXFIFO | MESON_SDHC_SRST_MAIN_CTRL);
|
||||
udelay(5);
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
|
||||
if (FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat) ||
|
||||
FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat))
|
||||
dev_warn(mmc_dev(host->mmc),
|
||||
"Failed to clear FIFOs, RX: %lu, TX: %lu\n",
|
||||
FIELD_GET(MESON_SDHC_STAT_RXFIFO_CNT, stat),
|
||||
FIELD_GET(MESON_SDHC_STAT_TXFIFO_CNT, stat));
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_wait_cmd_ready(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 stat, esta;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT, stat,
|
||||
!(stat & MESON_SDHC_STAT_CMD_BUSY),
|
||||
MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
|
||||
MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_warn(mmc_dev(mmc),
|
||||
"Failed to poll for CMD_BUSY while processing CMD%d\n",
|
||||
host->cmd->opcode);
|
||||
meson_mx_sdhc_hw_reset(mmc);
|
||||
}
|
||||
|
||||
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, esta,
|
||||
!(esta & MESON_SDHC_ESTA_11_13),
|
||||
MESON_SDHC_WAIT_CMD_READY_SLEEP_US,
|
||||
MESON_SDHC_WAIT_CMD_READY_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_warn(mmc_dev(mmc),
|
||||
"Failed to poll for ESTA[13:11] while processing CMD%d\n",
|
||||
host->cmd->opcode);
|
||||
meson_mx_sdhc_hw_reset(mmc);
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_start_cmd(struct mmc_host *mmc,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 ictl, send;
|
||||
int pack_len;
|
||||
|
||||
host->cmd = cmd;
|
||||
|
||||
ictl = MESON_SDHC_ICTL_DATA_TIMEOUT | MESON_SDHC_ICTL_DATA_ERR_CRC |
|
||||
MESON_SDHC_ICTL_RXFIFO_FULL | MESON_SDHC_ICTL_TXFIFO_EMPTY |
|
||||
MESON_SDHC_ICTL_RESP_TIMEOUT | MESON_SDHC_ICTL_RESP_ERR_CRC;
|
||||
|
||||
send = FIELD_PREP(MESON_SDHC_SEND_CMD_INDEX, cmd->opcode);
|
||||
|
||||
if (cmd->data) {
|
||||
send |= MESON_SDHC_SEND_CMD_HAS_DATA;
|
||||
send |= FIELD_PREP(MESON_SDHC_SEND_TOTAL_PACK,
|
||||
cmd->data->blocks - 1);
|
||||
|
||||
if (cmd->data->blksz < MESON_SDHC_MAX_BLK_SIZE)
|
||||
pack_len = cmd->data->blksz;
|
||||
else
|
||||
pack_len = 0;
|
||||
|
||||
if (cmd->data->flags & MMC_DATA_WRITE)
|
||||
send |= MESON_SDHC_SEND_DATA_DIR;
|
||||
|
||||
/*
|
||||
* If command with no data, just wait response done
|
||||
* interrupt(int[0]), and if command with data transfer, just
|
||||
* wait dma done interrupt(int[11]), don't need care about
|
||||
* dat0 busy or not.
|
||||
*/
|
||||
if (host->platform->hardware_flush_all_cmds ||
|
||||
cmd->data->flags & MMC_DATA_WRITE)
|
||||
/* hardware flush: */
|
||||
ictl |= MESON_SDHC_ICTL_DMA_DONE;
|
||||
else
|
||||
/* software flush: */
|
||||
ictl |= MESON_SDHC_ICTL_DATA_XFER_OK;
|
||||
} else {
|
||||
pack_len = 0;
|
||||
|
||||
ictl |= MESON_SDHC_ICTL_RESP_OK;
|
||||
}
|
||||
|
||||
if (cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||
send |= MESON_SDHC_SEND_DATA_STOP;
|
||||
|
||||
if (cmd->flags & MMC_RSP_PRESENT)
|
||||
send |= MESON_SDHC_SEND_CMD_HAS_RESP;
|
||||
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
send |= MESON_SDHC_SEND_RESP_LEN;
|
||||
send |= MESON_SDHC_SEND_RESP_NO_CRC;
|
||||
}
|
||||
|
||||
if (!(cmd->flags & MMC_RSP_CRC))
|
||||
send |= MESON_SDHC_SEND_RESP_NO_CRC;
|
||||
|
||||
if (cmd->flags & MMC_RSP_BUSY)
|
||||
send |= MESON_SDHC_SEND_R1B;
|
||||
|
||||
/* enable the new IRQs and mask all pending ones */
|
||||
regmap_write(host->regmap, MESON_SDHC_ICTL, ictl);
|
||||
regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_ARGU, cmd->arg);
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
|
||||
MESON_SDHC_CTRL_PACK_LEN,
|
||||
FIELD_PREP(MESON_SDHC_CTRL_PACK_LEN, pack_len));
|
||||
|
||||
if (cmd->data)
|
||||
regmap_write(host->regmap, MESON_SDHC_ADDR,
|
||||
sg_dma_address(cmd->data->sg));
|
||||
|
||||
meson_mx_sdhc_wait_cmd_ready(mmc);
|
||||
|
||||
if (cmd->data)
|
||||
host->platform->set_pdma(mmc);
|
||||
|
||||
if (host->platform->wait_before_send)
|
||||
host->platform->wait_before_send(mmc);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_SEND, send);
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_disable_clks(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (!host->bulk_clks_enabled)
|
||||
return;
|
||||
|
||||
clk_bulk_disable_unprepare(MESON_SDHC_NUM_BULK_CLKS, host->bulk_clks);
|
||||
|
||||
host->bulk_clks_enabled = false;
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
if (host->bulk_clks_enabled)
|
||||
return 0;
|
||||
|
||||
ret = clk_bulk_prepare_enable(MESON_SDHC_NUM_BULK_CLKS,
|
||||
host->bulk_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
host->bulk_clks_enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 rx_clk_phase;
|
||||
int ret;
|
||||
|
||||
meson_mx_sdhc_disable_clks(mmc);
|
||||
|
||||
if (ios->clock) {
|
||||
ret = clk_set_rate(host->sd_clk, ios->clock);
|
||||
if (ret) {
|
||||
dev_warn(mmc_dev(mmc),
|
||||
"Failed to set MMC clock to %uHz: %d\n",
|
||||
ios->clock, host->error);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = meson_mx_sdhc_enable_clks(mmc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mmc->actual_clock = clk_get_rate(host->sd_clk);
|
||||
|
||||
/*
|
||||
* according to Amlogic the following latching points are
|
||||
* selected with empirical values, there is no (known) formula
|
||||
* to calculate these.
|
||||
*/
|
||||
if (mmc->actual_clock > 100000000) {
|
||||
rx_clk_phase = 1;
|
||||
} else if (mmc->actual_clock > 45000000) {
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
rx_clk_phase = 15;
|
||||
else
|
||||
rx_clk_phase = 11;
|
||||
} else if (mmc->actual_clock >= 25000000) {
|
||||
rx_clk_phase = 15;
|
||||
} else if (mmc->actual_clock > 5000000) {
|
||||
rx_clk_phase = 23;
|
||||
} else if (mmc->actual_clock > 1000000) {
|
||||
rx_clk_phase = 55;
|
||||
} else {
|
||||
rx_clk_phase = 1061;
|
||||
}
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
|
||||
MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
rx_clk_phase));
|
||||
} else {
|
||||
mmc->actual_clock = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
unsigned short vdd = ios->vdd;
|
||||
|
||||
switch (ios->power_mode) {
|
||||
case MMC_POWER_OFF:
|
||||
vdd = 0;
|
||||
fallthrough;
|
||||
|
||||
case MMC_POWER_UP:
|
||||
if (!IS_ERR(mmc->supply.vmmc)) {
|
||||
host->error = mmc_regulator_set_ocr(mmc,
|
||||
mmc->supply.vmmc,
|
||||
vdd);
|
||||
if (host->error)
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MMC_POWER_ON:
|
||||
break;
|
||||
}
|
||||
|
||||
host->error = meson_mx_sdhc_set_clk(mmc, ios);
|
||||
if (host->error)
|
||||
return;
|
||||
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
|
||||
MESON_SDHC_CTRL_DAT_TYPE,
|
||||
FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 0));
|
||||
break;
|
||||
|
||||
case MMC_BUS_WIDTH_4:
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
|
||||
MESON_SDHC_CTRL_DAT_TYPE,
|
||||
FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 1));
|
||||
break;
|
||||
|
||||
case MMC_BUS_WIDTH_8:
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CTRL,
|
||||
MESON_SDHC_CTRL_DAT_TYPE,
|
||||
FIELD_PREP(MESON_SDHC_CTRL_DAT_TYPE, 2));
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(mmc_dev(mmc), "unsupported bus width: %d\n",
|
||||
ios->bus_width);
|
||||
host->error = -EINVAL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_map_dma(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct mmc_data *data = mrq->data;
|
||||
int dma_len;
|
||||
|
||||
if (!data)
|
||||
return 0;
|
||||
|
||||
dma_len = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len,
|
||||
mmc_get_dma_dir(data));
|
||||
if (dma_len <= 0) {
|
||||
dev_err(mmc_dev(mmc), "dma_map_sg failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
struct mmc_command *cmd = mrq->cmd;
|
||||
|
||||
if (!host->error)
|
||||
host->error = meson_mx_sdhc_map_dma(mmc, mrq);
|
||||
|
||||
if (host->error) {
|
||||
cmd->error = host->error;
|
||||
mmc_request_done(mmc, mrq);
|
||||
return;
|
||||
}
|
||||
|
||||
host->mrq = mrq;
|
||||
|
||||
meson_mx_sdhc_start_cmd(mmc, mrq->cmd);
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_card_busy(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 stat;
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_STAT, &stat);
|
||||
return FIELD_GET(MESON_SDHC_STAT_DAT3_0, stat) == 0;
|
||||
}
|
||||
|
||||
static bool meson_mx_sdhc_tuning_point_matches(struct mmc_host *mmc,
|
||||
u32 opcode)
|
||||
{
|
||||
unsigned int i, num_matches = 0;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < MESON_SDHC_NUM_TUNING_TRIES; i++) {
|
||||
ret = mmc_send_tuning(mmc, opcode, NULL);
|
||||
if (!ret)
|
||||
num_matches++;
|
||||
}
|
||||
|
||||
return num_matches == MESON_SDHC_NUM_TUNING_TRIES;
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
int div, start, len, best_start, best_len;
|
||||
int curr_phase, old_phase, new_phase;
|
||||
u32 val;
|
||||
|
||||
len = 0;
|
||||
start = 0;
|
||||
best_len = 0;
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_CLK2, &val);
|
||||
old_phase = FIELD_GET(MESON_SDHC_CLK2_RX_CLK_PHASE, val);
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_CLKC, &val);
|
||||
div = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val);
|
||||
|
||||
for (curr_phase = 0; curr_phase <= div; curr_phase++) {
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
|
||||
MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
curr_phase));
|
||||
|
||||
if (meson_mx_sdhc_tuning_point_matches(mmc, opcode)) {
|
||||
if (!len) {
|
||||
start = curr_phase;
|
||||
|
||||
dev_dbg(mmc_dev(mmc),
|
||||
"New RX phase window starts at %u\n",
|
||||
start);
|
||||
}
|
||||
|
||||
len++;
|
||||
} else {
|
||||
if (len > best_len) {
|
||||
best_start = start;
|
||||
best_len = len;
|
||||
|
||||
dev_dbg(mmc_dev(mmc),
|
||||
"New best RX phase window: %u - %u\n",
|
||||
best_start, best_start + best_len);
|
||||
}
|
||||
|
||||
/* reset the current window */
|
||||
len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (len > best_len)
|
||||
/* the last window is the best (or possibly only) window */
|
||||
new_phase = start + (len / 2);
|
||||
else if (best_len)
|
||||
/* there was a better window than the last */
|
||||
new_phase = best_start + (best_len / 2);
|
||||
else
|
||||
/* no window was found at all, reset to the original phase */
|
||||
new_phase = old_phase;
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
|
||||
MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
|
||||
new_phase));
|
||||
|
||||
if (!len && !best_len)
|
||||
return -EIO;
|
||||
|
||||
dev_dbg(mmc_dev(mmc), "Tuned RX clock phase to %u\n", new_phase);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mmc_host_ops meson_mx_sdhc_ops = {
|
||||
.hw_reset = meson_mx_sdhc_hw_reset,
|
||||
.request = meson_mx_sdhc_request,
|
||||
.set_ios = meson_mx_sdhc_set_ios,
|
||||
.card_busy = meson_mx_sdhc_card_busy,
|
||||
.execute_tuning = meson_mx_sdhc_execute_tuning,
|
||||
.get_cd = mmc_gpio_get_cd,
|
||||
.get_ro = mmc_gpio_get_ro,
|
||||
};
|
||||
|
||||
static void meson_mx_sdhc_request_done(struct meson_mx_sdhc_host *host)
|
||||
{
|
||||
struct mmc_request *mrq = host->mrq;
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
|
||||
/* disable interrupts and mask all pending ones */
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_ICTL,
|
||||
MESON_SDHC_ICTL_ALL_IRQS, 0);
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_ISTA,
|
||||
MESON_SDHC_ISTA_ALL_IRQS, MESON_SDHC_ISTA_ALL_IRQS);
|
||||
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
|
||||
mmc_request_done(mmc, mrq);
|
||||
}
|
||||
|
||||
static u32 meson_mx_sdhc_read_response(struct meson_mx_sdhc_host *host, u8 idx)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_DMA_MODE, 0);
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_PIO_RDRESP,
|
||||
FIELD_PREP(MESON_SDHC_PDMA_PIO_RDRESP, idx));
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_ARGU, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static irqreturn_t meson_mx_sdhc_irq(int irq, void *data)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = data;
|
||||
struct mmc_command *cmd = host->cmd;
|
||||
u32 ictl, ista;
|
||||
|
||||
regmap_read(host->regmap, MESON_SDHC_ICTL, &ictl);
|
||||
regmap_read(host->regmap, MESON_SDHC_ISTA, &ista);
|
||||
|
||||
if (!(ictl & ista))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (ista & MESON_SDHC_ISTA_RXFIFO_FULL ||
|
||||
ista & MESON_SDHC_ISTA_TXFIFO_EMPTY)
|
||||
cmd->error = -EIO;
|
||||
else if (ista & MESON_SDHC_ISTA_RESP_ERR_CRC)
|
||||
cmd->error = -EILSEQ;
|
||||
else if (ista & MESON_SDHC_ISTA_RESP_TIMEOUT)
|
||||
cmd->error = -ETIMEDOUT;
|
||||
|
||||
if (cmd->data) {
|
||||
if (ista & MESON_SDHC_ISTA_DATA_ERR_CRC)
|
||||
cmd->data->error = -EILSEQ;
|
||||
else if (ista & MESON_SDHC_ISTA_DATA_TIMEOUT)
|
||||
cmd->data->error = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (cmd->error || (cmd->data && cmd->data->error))
|
||||
dev_dbg(mmc_dev(host->mmc), "CMD%d error, ISTA: 0x%08x\n",
|
||||
cmd->opcode, ista);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t meson_mx_sdhc_irq_thread(int irq, void *irq_data)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = irq_data;
|
||||
struct mmc_command *cmd;
|
||||
u32 val;
|
||||
|
||||
cmd = host->cmd;
|
||||
if (WARN_ON(!cmd))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (cmd->data && !cmd->data->error) {
|
||||
if (!host->platform->hardware_flush_all_cmds &&
|
||||
cmd->data->flags & MMC_DATA_READ) {
|
||||
meson_mx_sdhc_wait_cmd_ready(host->mmc);
|
||||
|
||||
/*
|
||||
* If MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH was
|
||||
* previously 0x1 then it has to be set to 0x3. If it
|
||||
* was 0x0 before then it has to be set to 0x2. Without
|
||||
* this reading SD cards sometimes transfers garbage,
|
||||
* which results in cards not being detected due to:
|
||||
* unrecognised SCR structure version <random number>
|
||||
*/
|
||||
val = FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
|
||||
2);
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA, val,
|
||||
val);
|
||||
}
|
||||
|
||||
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
|
||||
cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
|
||||
|
||||
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
|
||||
}
|
||||
|
||||
meson_mx_sdhc_wait_cmd_ready(host->mmc);
|
||||
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
cmd->resp[0] = meson_mx_sdhc_read_response(host, 4);
|
||||
cmd->resp[1] = meson_mx_sdhc_read_response(host, 3);
|
||||
cmd->resp[2] = meson_mx_sdhc_read_response(host, 2);
|
||||
cmd->resp[3] = meson_mx_sdhc_read_response(host, 1);
|
||||
} else {
|
||||
cmd->resp[0] = meson_mx_sdhc_read_response(host, 0);
|
||||
}
|
||||
|
||||
if (cmd->error == -EIO || cmd->error == -ETIMEDOUT)
|
||||
meson_mx_sdhc_hw_reset(host->mmc);
|
||||
else if (cmd->data)
|
||||
/*
|
||||
* Clear the FIFOs after completing data transfers to prevent
|
||||
* corrupting data on write access. It's not clear why this is
|
||||
* needed (for reads and writes), but it mimics what the BSP
|
||||
* kernel did.
|
||||
*/
|
||||
meson_mx_sdhc_clear_fifo(host->mmc);
|
||||
|
||||
meson_mx_sdhc_request_done(host);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_init_hw_meson8(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_MISC,
|
||||
FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 7) |
|
||||
FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
|
||||
FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_ENHC,
|
||||
FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 63) |
|
||||
MESON_SDHC_ENHC_MESON6_DMA_WR_RESP |
|
||||
FIELD_PREP(MESON_SDHC_ENHC_MESON6_RX_TIMEOUT, 255) |
|
||||
FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
|
||||
};
|
||||
|
||||
static void meson_mx_sdhc_set_pdma_meson8(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (host->cmd->data->flags & MMC_DATA_WRITE)
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_DMA_MODE |
|
||||
MESON_SDHC_PDMA_RD_BURST |
|
||||
MESON_SDHC_PDMA_TXFIFO_FILL,
|
||||
MESON_SDHC_PDMA_DMA_MODE |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 31) |
|
||||
MESON_SDHC_PDMA_TXFIFO_FILL);
|
||||
else
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_DMA_MODE |
|
||||
MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
|
||||
MESON_SDHC_PDMA_DMA_MODE |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH,
|
||||
1));
|
||||
|
||||
if (host->cmd->data->flags & MMC_DATA_WRITE)
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_RD_BURST,
|
||||
FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15));
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_wait_before_send_meson8(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_ESTA, val,
|
||||
val == 0,
|
||||
MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
|
||||
MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_warn(mmc_dev(mmc),
|
||||
"Failed to wait for ESTA to clear: 0x%08x\n", val);
|
||||
|
||||
if (host->cmd->data && host->cmd->data->flags & MMC_DATA_WRITE) {
|
||||
ret = regmap_read_poll_timeout(host->regmap, MESON_SDHC_STAT,
|
||||
val, val & MESON_SDHC_STAT_TXFIFO_CNT,
|
||||
MESON_SDHC_WAIT_BEFORE_SEND_SLEEP_US,
|
||||
MESON_SDHC_WAIT_BEFORE_SEND_TIMEOUT_US);
|
||||
if (ret)
|
||||
dev_warn(mmc_dev(mmc),
|
||||
"Failed to wait for TX FIFO to fill\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_init_hw_meson8m2(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_MISC,
|
||||
FIELD_PREP(MESON_SDHC_MISC_TXSTART_THRES, 6) |
|
||||
FIELD_PREP(MESON_SDHC_MISC_WCRC_ERR_PATT, 5) |
|
||||
FIELD_PREP(MESON_SDHC_MISC_WCRC_OK_PATT, 2));
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_ENHC,
|
||||
FIELD_PREP(MESON_SDHC_ENHC_RXFIFO_TH, 64) |
|
||||
FIELD_PREP(MESON_SDHC_ENHC_MESON8M2_DEBUG, 1) |
|
||||
MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE |
|
||||
FIELD_PREP(MESON_SDHC_ENHC_SDIO_IRQ_PERIOD, 12));
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_set_pdma_meson8m2(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
regmap_update_bits(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_DMA_MODE, MESON_SDHC_PDMA_DMA_MODE);
|
||||
}
|
||||
|
||||
static void meson_mx_sdhc_init_hw(struct mmc_host *mmc)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
||||
|
||||
meson_mx_sdhc_hw_reset(mmc);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_CTRL,
|
||||
FIELD_PREP(MESON_SDHC_CTRL_RX_PERIOD, 0xf) |
|
||||
FIELD_PREP(MESON_SDHC_CTRL_RX_TIMEOUT, 0x7f) |
|
||||
FIELD_PREP(MESON_SDHC_CTRL_RX_ENDIAN, 0x7) |
|
||||
FIELD_PREP(MESON_SDHC_CTRL_TX_ENDIAN, 0x7));
|
||||
|
||||
/*
|
||||
* start with a valid divider and enable the memory (un-setting
|
||||
* MESON_SDHC_CLKC_MEM_PWR_OFF).
|
||||
*/
|
||||
regmap_write(host->regmap, MESON_SDHC_CLKC, MESON_SDHC_CLKC_CLK_DIV);
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_CLK2,
|
||||
FIELD_PREP(MESON_SDHC_CLK2_SD_CLK_PHASE, 1));
|
||||
|
||||
regmap_write(host->regmap, MESON_SDHC_PDMA,
|
||||
MESON_SDHC_PDMA_DMA_URGENT |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_WR_BURST, 7) |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_TXFIFO_TH, 49) |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_RD_BURST, 15) |
|
||||
FIELD_PREP(MESON_SDHC_PDMA_RXFIFO_TH, 7));
|
||||
|
||||
/* some initialization bits depend on the SoC: */
|
||||
host->platform->init_hw(mmc);
|
||||
|
||||
/* disable and mask all interrupts: */
|
||||
regmap_write(host->regmap, MESON_SDHC_ICTL, 0);
|
||||
regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS);
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_mx_sdhc_host *host;
|
||||
struct mmc_host *mmc;
|
||||
void __iomem *base;
|
||||
int ret, irq;
|
||||
|
||||
mmc = mmc_alloc_host(sizeof(*host), dev);
|
||||
if (!mmc)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, (void(*)(void *))mmc_free_host,
|
||||
mmc);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register mmc_free_host action\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
host->platform = device_get_match_data(dev);
|
||||
if (!host->platform)
|
||||
return -EINVAL;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
host->regmap = devm_regmap_init_mmio(dev, base,
|
||||
&meson_mx_sdhc_regmap_config);
|
||||
if (IS_ERR(host->regmap))
|
||||
return PTR_ERR(host->regmap);
|
||||
|
||||
host->pclk = devm_clk_get(dev, "pclk");
|
||||
if (IS_ERR(host->pclk))
|
||||
return PTR_ERR(host->pclk);
|
||||
|
||||
/* accessing any register requires the module clock to be enabled: */
|
||||
ret = clk_prepare_enable(host->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable 'pclk' clock\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
meson_mx_sdhc_init_hw(mmc);
|
||||
|
||||
ret = meson_mx_sdhc_register_clkc(dev, base, host->bulk_clks);
|
||||
if (ret)
|
||||
goto err_disable_pclk;
|
||||
|
||||
host->sd_clk = host->bulk_clks[1].clk;
|
||||
|
||||
/* Get regulators and the supported OCR mask */
|
||||
ret = mmc_regulator_get_supply(mmc);
|
||||
if (ret)
|
||||
goto err_disable_pclk;
|
||||
|
||||
mmc->max_req_size = SZ_128K;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
mmc->max_blk_count = FIELD_GET(MESON_SDHC_SEND_TOTAL_PACK, ~0);
|
||||
mmc->max_blk_size = MESON_SDHC_MAX_BLK_SIZE;
|
||||
mmc->max_busy_timeout = 30 * MSEC_PER_SEC;
|
||||
mmc->f_min = clk_round_rate(host->sd_clk, 1);
|
||||
mmc->f_max = clk_round_rate(host->sd_clk, ULONG_MAX);
|
||||
mmc->max_current_180 = 300;
|
||||
mmc->max_current_330 = 300;
|
||||
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_HW_RESET;
|
||||
mmc->ops = &meson_mx_sdhc_ops;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
if (ret)
|
||||
goto err_disable_pclk;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_threaded_irq(dev, irq, meson_mx_sdhc_irq,
|
||||
meson_mx_sdhc_irq_thread, IRQF_ONESHOT,
|
||||
NULL, host);
|
||||
if (ret)
|
||||
goto err_disable_pclk;
|
||||
|
||||
ret = mmc_add_host(mmc);
|
||||
if (ret)
|
||||
goto err_disable_pclk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_pclk:
|
||||
clk_disable_unprepare(host->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_mx_sdhc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_mx_sdhc_host *host = platform_get_drvdata(pdev);
|
||||
|
||||
mmc_remove_host(host->mmc);
|
||||
|
||||
meson_mx_sdhc_disable_clks(host->mmc);
|
||||
|
||||
clk_disable_unprepare(host->pclk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8 = {
|
||||
.init_hw = meson_mx_sdhc_init_hw_meson8,
|
||||
.set_pdma = meson_mx_sdhc_set_pdma_meson8,
|
||||
.wait_before_send = meson_mx_sdhc_wait_before_send_meson8,
|
||||
.hardware_flush_all_cmds = false,
|
||||
};
|
||||
|
||||
static const struct meson_mx_sdhc_data meson_mx_sdhc_data_meson8m2 = {
|
||||
.init_hw = meson_mx_sdhc_init_hw_meson8m2,
|
||||
.set_pdma = meson_mx_sdhc_set_pdma_meson8m2,
|
||||
.hardware_flush_all_cmds = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id meson_mx_sdhc_of_match[] = {
|
||||
{
|
||||
.compatible = "amlogic,meson8-sdhc",
|
||||
.data = &meson_mx_sdhc_data_meson8
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson8b-sdhc",
|
||||
.data = &meson_mx_sdhc_data_meson8
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,meson8m2-sdhc",
|
||||
.data = &meson_mx_sdhc_data_meson8m2
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_mx_sdhc_of_match);
|
||||
|
||||
static struct platform_driver meson_mx_sdhc_driver = {
|
||||
.probe = meson_mx_sdhc_probe,
|
||||
.remove = meson_mx_sdhc_remove,
|
||||
.driver = {
|
||||
.name = "meson-mx-sdhc",
|
||||
.of_match_table = of_match_ptr(meson_mx_sdhc_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(meson_mx_sdhc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Meson6, Meson8, Meson8b and Meson8m2 SDHC Host Driver");
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -0,0 +1,141 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) 2020 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
*/
|
||||
|
||||
#ifndef _MESON_MX_SDHC_H_
|
||||
#define _MESON_MX_SDHC_H_
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#define MESON_SDHC_ARGU 0x00
|
||||
|
||||
#define MESON_SDHC_SEND 0x04
|
||||
#define MESON_SDHC_SEND_CMD_INDEX GENMASK(5, 0)
|
||||
#define MESON_SDHC_SEND_CMD_HAS_RESP BIT(6)
|
||||
#define MESON_SDHC_SEND_CMD_HAS_DATA BIT(7)
|
||||
#define MESON_SDHC_SEND_RESP_LEN BIT(8)
|
||||
#define MESON_SDHC_SEND_RESP_NO_CRC BIT(9)
|
||||
#define MESON_SDHC_SEND_DATA_DIR BIT(10)
|
||||
#define MESON_SDHC_SEND_DATA_STOP BIT(11)
|
||||
#define MESON_SDHC_SEND_R1B BIT(12)
|
||||
#define MESON_SDHC_SEND_TOTAL_PACK GENMASK(31, 16)
|
||||
|
||||
#define MESON_SDHC_CTRL 0x08
|
||||
#define MESON_SDHC_CTRL_DAT_TYPE GENMASK(1, 0)
|
||||
#define MESON_SDHC_CTRL_DDR_MODE BIT(2)
|
||||
#define MESON_SDHC_CTRL_TX_CRC_NOCHECK BIT(3)
|
||||
#define MESON_SDHC_CTRL_PACK_LEN GENMASK(12, 4)
|
||||
#define MESON_SDHC_CTRL_RX_TIMEOUT GENMASK(19, 13)
|
||||
#define MESON_SDHC_CTRL_RX_PERIOD GENMASK(23, 20)
|
||||
#define MESON_SDHC_CTRL_RX_ENDIAN GENMASK(26, 24)
|
||||
#define MESON_SDHC_CTRL_SDIO_IRQ_MODE BIT(27)
|
||||
#define MESON_SDHC_CTRL_DAT0_IRQ_SEL BIT(28)
|
||||
#define MESON_SDHC_CTRL_TX_ENDIAN GENMASK(31, 29)
|
||||
|
||||
#define MESON_SDHC_STAT 0x0c
|
||||
#define MESON_SDHC_STAT_CMD_BUSY BIT(0)
|
||||
#define MESON_SDHC_STAT_DAT3_0 GENMASK(4, 1)
|
||||
#define MESON_SDHC_STAT_CMD BIT(5)
|
||||
#define MESON_SDHC_STAT_RXFIFO_CNT GENMASK(12, 6)
|
||||
#define MESON_SDHC_STAT_TXFIFO_CNT GENMASK(19, 13)
|
||||
#define MESON_SDHC_STAT_DAT7_4 GENMASK(23, 20)
|
||||
|
||||
#define MESON_SDHC_CLKC 0x10
|
||||
#define MESON_SDHC_CLKC_CLK_DIV GENMASK(11, 0)
|
||||
#define MESON_SDHC_CLKC_CLK_JIC BIT(24)
|
||||
#define MESON_SDHC_CLKC_MEM_PWR_OFF GENMASK(26, 25)
|
||||
|
||||
#define MESON_SDHC_ADDR 0x14
|
||||
|
||||
#define MESON_SDHC_PDMA 0x18
|
||||
#define MESON_SDHC_PDMA_DMA_MODE BIT(0)
|
||||
#define MESON_SDHC_PDMA_PIO_RDRESP GENMASK(3, 1)
|
||||
#define MESON_SDHC_PDMA_DMA_URGENT BIT(4)
|
||||
#define MESON_SDHC_PDMA_WR_BURST GENMASK(9, 5)
|
||||
#define MESON_SDHC_PDMA_RD_BURST GENMASK(14, 10)
|
||||
#define MESON_SDHC_PDMA_RXFIFO_TH GENMASK(21, 15)
|
||||
#define MESON_SDHC_PDMA_TXFIFO_TH GENMASK(28, 22)
|
||||
#define MESON_SDHC_PDMA_RXFIFO_MANUAL_FLUSH GENMASK(30, 29)
|
||||
#define MESON_SDHC_PDMA_TXFIFO_FILL BIT(31)
|
||||
|
||||
#define MESON_SDHC_MISC 0x1c
|
||||
#define MESON_SDHC_MISC_WCRC_ERR_PATT GENMASK(6, 4)
|
||||
#define MESON_SDHC_MISC_WCRC_OK_PATT GENMASK(9, 7)
|
||||
#define MESON_SDHC_MISC_BURST_NUM GENMASK(21, 16)
|
||||
#define MESON_SDHC_MISC_THREAD_ID GENMASK(27, 22)
|
||||
#define MESON_SDHC_MISC_MANUAL_STOP BIT(28)
|
||||
#define MESON_SDHC_MISC_TXSTART_THRES GENMASK(31, 29)
|
||||
|
||||
#define MESON_SDHC_DATA 0x20
|
||||
|
||||
#define MESON_SDHC_ICTL 0x24
|
||||
#define MESON_SDHC_ICTL_RESP_OK BIT(0)
|
||||
#define MESON_SDHC_ICTL_RESP_TIMEOUT BIT(1)
|
||||
#define MESON_SDHC_ICTL_RESP_ERR_CRC BIT(2)
|
||||
#define MESON_SDHC_ICTL_RESP_OK_NOCLEAR BIT(3)
|
||||
#define MESON_SDHC_ICTL_DATA_1PACK_OK BIT(4)
|
||||
#define MESON_SDHC_ICTL_DATA_TIMEOUT BIT(5)
|
||||
#define MESON_SDHC_ICTL_DATA_ERR_CRC BIT(6)
|
||||
#define MESON_SDHC_ICTL_DATA_XFER_OK BIT(7)
|
||||
#define MESON_SDHC_ICTL_RX_HIGHER BIT(8)
|
||||
#define MESON_SDHC_ICTL_RX_LOWER BIT(9)
|
||||
#define MESON_SDHC_ICTL_DAT1_IRQ BIT(10)
|
||||
#define MESON_SDHC_ICTL_DMA_DONE BIT(11)
|
||||
#define MESON_SDHC_ICTL_RXFIFO_FULL BIT(12)
|
||||
#define MESON_SDHC_ICTL_TXFIFO_EMPTY BIT(13)
|
||||
#define MESON_SDHC_ICTL_ADDI_DAT1_IRQ BIT(14)
|
||||
#define MESON_SDHC_ICTL_ALL_IRQS GENMASK(14, 0)
|
||||
#define MESON_SDHC_ICTL_DAT1_IRQ_DELAY GENMASK(17, 16)
|
||||
|
||||
#define MESON_SDHC_ISTA 0x28
|
||||
#define MESON_SDHC_ISTA_RESP_OK BIT(0)
|
||||
#define MESON_SDHC_ISTA_RESP_TIMEOUT BIT(1)
|
||||
#define MESON_SDHC_ISTA_RESP_ERR_CRC BIT(2)
|
||||
#define MESON_SDHC_ISTA_RESP_OK_NOCLEAR BIT(3)
|
||||
#define MESON_SDHC_ISTA_DATA_1PACK_OK BIT(4)
|
||||
#define MESON_SDHC_ISTA_DATA_TIMEOUT BIT(5)
|
||||
#define MESON_SDHC_ISTA_DATA_ERR_CRC BIT(6)
|
||||
#define MESON_SDHC_ISTA_DATA_XFER_OK BIT(7)
|
||||
#define MESON_SDHC_ISTA_RX_HIGHER BIT(8)
|
||||
#define MESON_SDHC_ISTA_RX_LOWER BIT(9)
|
||||
#define MESON_SDHC_ISTA_DAT1_IRQ BIT(10)
|
||||
#define MESON_SDHC_ISTA_DMA_DONE BIT(11)
|
||||
#define MESON_SDHC_ISTA_RXFIFO_FULL BIT(12)
|
||||
#define MESON_SDHC_ISTA_TXFIFO_EMPTY BIT(13)
|
||||
#define MESON_SDHC_ISTA_ADDI_DAT1_IRQ BIT(14)
|
||||
#define MESON_SDHC_ISTA_ALL_IRQS GENMASK(14, 0)
|
||||
|
||||
#define MESON_SDHC_SRST 0x2c
|
||||
#define MESON_SDHC_SRST_MAIN_CTRL BIT(0)
|
||||
#define MESON_SDHC_SRST_RXFIFO BIT(1)
|
||||
#define MESON_SDHC_SRST_TXFIFO BIT(2)
|
||||
#define MESON_SDHC_SRST_DPHY_RX BIT(3)
|
||||
#define MESON_SDHC_SRST_DPHY_TX BIT(4)
|
||||
#define MESON_SDHC_SRST_DMA_IF BIT(5)
|
||||
|
||||
#define MESON_SDHC_ESTA 0x30
|
||||
#define MESON_SDHC_ESTA_11_13 GENMASK(13, 11)
|
||||
|
||||
#define MESON_SDHC_ENHC 0x34
|
||||
#define MESON_SDHC_ENHC_MESON8M2_WRRSP_MODE BIT(0)
|
||||
#define MESON_SDHC_ENHC_MESON8M2_CHK_WRRSP BIT(1)
|
||||
#define MESON_SDHC_ENHC_MESON8M2_CHK_DMA BIT(2)
|
||||
#define MESON_SDHC_ENHC_MESON8M2_DEBUG GENMASK(5, 3)
|
||||
#define MESON_SDHC_ENHC_MESON6_RX_TIMEOUT GENMASK(7, 0)
|
||||
#define MESON_SDHC_ENHC_MESON6_DMA_RD_RESP BIT(16)
|
||||
#define MESON_SDHC_ENHC_MESON6_DMA_WR_RESP BIT(17)
|
||||
#define MESON_SDHC_ENHC_SDIO_IRQ_PERIOD GENMASK(15, 8)
|
||||
#define MESON_SDHC_ENHC_RXFIFO_TH GENMASK(24, 18)
|
||||
#define MESON_SDHC_ENHC_TXFIFO_TH GENMASK(31, 25)
|
||||
|
||||
#define MESON_SDHC_CLK2 0x38
|
||||
#define MESON_SDHC_CLK2_RX_CLK_PHASE GENMASK(11, 0)
|
||||
#define MESON_SDHC_CLK2_SD_CLK_PHASE GENMASK(23, 12)
|
||||
|
||||
struct clk_bulk_data;
|
||||
|
||||
int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base,
|
||||
struct clk_bulk_data *clk_bulk_data);
|
||||
|
||||
#endif /* _MESON_MX_SDHC_H_ */
|
|
@ -246,6 +246,9 @@ static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host)
|
|||
|
||||
mrq = host->mrq;
|
||||
|
||||
if (host->cmd->error)
|
||||
meson_mx_mmc_soft_reset(host);
|
||||
|
||||
host->mrq = NULL;
|
||||
host->cmd = NULL;
|
||||
|
||||
|
@ -561,7 +564,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
|
|||
mmc->f_max = clk_round_rate(host->cfg_div_clk,
|
||||
clk_get_rate(host->parent_clk));
|
||||
|
||||
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
||||
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
||||
mmc->ops = &meson_mx_mmc_ops;
|
||||
|
||||
ret = mmc_of_parse(mmc);
|
||||
|
|
|
@ -16,11 +16,20 @@
|
|||
#define HSQ_NUM_SLOTS 64
|
||||
#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
|
||||
|
||||
static void mmc_hsq_retry_handler(struct work_struct *work)
|
||||
{
|
||||
struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work);
|
||||
struct mmc_host *mmc = hsq->mmc;
|
||||
|
||||
mmc->ops->request(mmc, hsq->mrq);
|
||||
}
|
||||
|
||||
static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
|
||||
{
|
||||
struct mmc_host *mmc = hsq->mmc;
|
||||
struct hsq_slot *slot;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&hsq->lock, flags);
|
||||
|
||||
|
@ -42,7 +51,24 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
|
|||
|
||||
spin_unlock_irqrestore(&hsq->lock, flags);
|
||||
|
||||
mmc->ops->request(mmc, hsq->mrq);
|
||||
if (mmc->ops->request_atomic)
|
||||
ret = mmc->ops->request_atomic(mmc, hsq->mrq);
|
||||
else
|
||||
mmc->ops->request(mmc, hsq->mrq);
|
||||
|
||||
/*
|
||||
* If returning BUSY from request_atomic(), which means the card
|
||||
* may be busy now, and we should change to non-atomic context to
|
||||
* try again for this unusual case, to avoid time-consuming operations
|
||||
* in the atomic context.
|
||||
*
|
||||
* Note: we just give a warning for other error cases, since the host
|
||||
* driver will handle them.
|
||||
*/
|
||||
if (ret == -EBUSY)
|
||||
schedule_work(&hsq->retry_work);
|
||||
else
|
||||
WARN_ON_ONCE(ret);
|
||||
}
|
||||
|
||||
static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
|
||||
|
@ -325,6 +351,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
|
|||
hsq->mmc->cqe_private = hsq;
|
||||
mmc->cqe_ops = &mmc_hsq_ops;
|
||||
|
||||
INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
|
||||
spin_lock_init(&hsq->lock);
|
||||
init_waitqueue_head(&hsq->wait_queue);
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ struct mmc_hsq {
|
|||
wait_queue_head_t wait_queue;
|
||||
struct hsq_slot *slot;
|
||||
spinlock_t lock;
|
||||
struct work_struct retry_work;
|
||||
|
||||
int next_tag;
|
||||
int num_slots;
|
||||
|
|
|
@ -77,14 +77,8 @@
|
|||
|
||||
#define MMC_SPI_BLOCKSIZE 512
|
||||
|
||||
|
||||
/* These fixed timeouts come from the latest SD specs, which say to ignore
|
||||
* the CSD values. The R1B value is for card erase (e.g. the "I forgot the
|
||||
* card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
|
||||
* reads which takes nowhere near that long. Older cards may be able to use
|
||||
* shorter timeouts ... but why bother?
|
||||
*/
|
||||
#define r1b_timeout (HZ * 3)
|
||||
#define MMC_SPI_R1B_TIMEOUT_MS 3000
|
||||
#define MMC_SPI_INIT_TIMEOUT_MS 3000
|
||||
|
||||
/* One of the critical speed parameters is the amount of data which may
|
||||
* be transferred in one command. If this value is too low, the SD card
|
||||
|
@ -248,6 +242,7 @@ static char *maptype(struct mmc_command *cmd)
|
|||
static int mmc_spi_response_get(struct mmc_spi_host *host,
|
||||
struct mmc_command *cmd, int cs_on)
|
||||
{
|
||||
unsigned long timeout_ms;
|
||||
u8 *cp = host->data->status;
|
||||
u8 *end = cp + host->t.len;
|
||||
int value = 0;
|
||||
|
@ -346,8 +341,11 @@ checkstatus:
|
|||
/* maybe we read all the busy tokens already */
|
||||
while (cp < end && *cp == 0)
|
||||
cp++;
|
||||
if (cp == end)
|
||||
mmc_spi_wait_unbusy(host, r1b_timeout);
|
||||
if (cp == end) {
|
||||
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||
MMC_SPI_R1B_TIMEOUT_MS;
|
||||
mmc_spi_wait_unbusy(host, msecs_to_jiffies(timeout_ms));
|
||||
}
|
||||
break;
|
||||
|
||||
/* SPI R2 == R1 + second status byte; SEND_STATUS
|
||||
|
@ -1118,7 +1116,7 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host)
|
|||
/* Try to be very sure any previous command has completed;
|
||||
* wait till not-busy, skip debris from any old commands.
|
||||
*/
|
||||
mmc_spi_wait_unbusy(host, r1b_timeout);
|
||||
mmc_spi_wait_unbusy(host, msecs_to_jiffies(MMC_SPI_INIT_TIMEOUT_MS));
|
||||
mmc_spi_readbytes(host, 10);
|
||||
|
||||
/*
|
||||
|
|
|
@ -1861,31 +1861,17 @@ static int mmci_get_cd(struct mmc_host *mmc)
|
|||
static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct mmci_host *host = mmc_priv(mmc);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
|
||||
switch (ios->signal_voltage) {
|
||||
case MMC_SIGNAL_VOLTAGE_330:
|
||||
ret = regulator_set_voltage(mmc->supply.vqmmc,
|
||||
2700000, 3600000);
|
||||
break;
|
||||
case MMC_SIGNAL_VOLTAGE_180:
|
||||
ret = regulator_set_voltage(mmc->supply.vqmmc,
|
||||
1700000, 1950000);
|
||||
break;
|
||||
case MMC_SIGNAL_VOLTAGE_120:
|
||||
ret = regulator_set_voltage(mmc->supply.vqmmc,
|
||||
1100000, 1300000);
|
||||
break;
|
||||
}
|
||||
if (!ret && host->ops && host->ops->post_sig_volt_switch)
|
||||
ret = host->ops->post_sig_volt_switch(host, ios);
|
||||
else if (ret)
|
||||
ret = 0;
|
||||
|
||||
if (!ret && host->ops && host->ops->post_sig_volt_switch)
|
||||
ret = host->ops->post_sig_volt_switch(host, ios);
|
||||
|
||||
if (ret)
|
||||
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
|
||||
}
|
||||
if (ret < 0)
|
||||
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -119,20 +119,19 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host,
|
|||
static int sdmmc_idma_setup(struct mmci_host *host)
|
||||
{
|
||||
struct sdmmc_idma *idma;
|
||||
struct device *dev = mmc_dev(host->mmc);
|
||||
|
||||
idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL);
|
||||
idma = devm_kzalloc(dev, sizeof(*idma), GFP_KERNEL);
|
||||
if (!idma)
|
||||
return -ENOMEM;
|
||||
|
||||
host->dma_priv = idma;
|
||||
|
||||
if (host->variant->dma_lli) {
|
||||
idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc),
|
||||
SDMMC_LLI_BUF_LEN,
|
||||
idma->sg_cpu = dmam_alloc_coherent(dev, SDMMC_LLI_BUF_LEN,
|
||||
&idma->sg_dma, GFP_KERNEL);
|
||||
if (!idma->sg_cpu) {
|
||||
dev_err(mmc_dev(host->mmc),
|
||||
"Failed to alloc IDMA descriptor\n");
|
||||
dev_err(dev, "Failed to alloc IDMA descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
host->mmc->max_segs = SDMMC_LLI_BUF_LEN /
|
||||
|
@ -143,7 +142,7 @@ static int sdmmc_idma_setup(struct mmci_host *host)
|
|||
host->mmc->max_seg_size = host->mmc->max_req_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return dma_set_max_seg_size(dev, host->mmc->max_seg_size);
|
||||
}
|
||||
|
||||
static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
|
||||
|
@ -188,6 +187,9 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
|
|||
static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data)
|
||||
{
|
||||
writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR);
|
||||
|
||||
if (!data->host_cookie)
|
||||
sdmmc_idma_unprep_data(host, data, 0);
|
||||
}
|
||||
|
||||
static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
|
||||
|
@ -519,6 +521,7 @@ void sdmmc_variant_init(struct mmci_host *host)
|
|||
struct sdmmc_dlyb *dlyb;
|
||||
|
||||
host->ops = &sdmmc_variant_ops;
|
||||
host->pwr_reg = readl_relaxed(host->base + MMCIPOWER);
|
||||
|
||||
base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL);
|
||||
if (IS_ERR(base_dlyb))
|
||||
|
|
|
@ -1369,7 +1369,7 @@ static void msdc_set_buswidth(struct msdc_host *host, u32 width)
|
|||
static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
|
||||
|
@ -1379,18 +1379,19 @@ static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
}
|
||||
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
|
||||
ret, ios->signal_voltage);
|
||||
} else {
|
||||
/* Apply different pinctrl settings for different signal voltage */
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
else
|
||||
pinctrl_select_state(host->pinctrl, host->pins_default);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Apply different pinctrl settings for different signal voltage */
|
||||
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
else
|
||||
pinctrl_select_state(host->pinctrl, host->pins_default);
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msdc_card_busy(struct mmc_host *mmc)
|
||||
|
@ -2325,7 +2326,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
|||
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
mmc->caps |= MMC_CAP_CMD23;
|
||||
/* MMC core transfer sizes tunable parameters */
|
||||
mmc->max_segs = MAX_BD_NUM;
|
||||
if (host->dev_comp->support_64g)
|
||||
|
|
|
@ -752,8 +752,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
|||
if (maxfreq)
|
||||
mmc->f_max = maxfreq;
|
||||
|
||||
mmc->caps |= MMC_CAP_ERASE;
|
||||
|
||||
spin_lock_init(&host->lock);
|
||||
|
||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
|
|
|
@ -634,8 +634,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
|
|||
/* set mmc core parameters */
|
||||
mmc->ops = &mxs_mmc_ops;
|
||||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 |
|
||||
MMC_CAP_ERASE;
|
||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
|
||||
|
||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
||||
|
||||
|
|
|
@ -1244,7 +1244,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
|
|||
|
||||
mmc->caps = 0;
|
||||
if (host->pdata->slots[id].wires >= 4)
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
|
||||
mmc->ops = &mmc_omap_ops;
|
||||
mmc->f_min = 400000;
|
||||
|
|
|
@ -1922,7 +1922,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
|
|||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_CMD23;
|
||||
|
||||
mmc->caps |= mmc_pdata(host)->caps;
|
||||
if (mmc->caps & MMC_CAP_8_BIT_DATA)
|
||||
|
|
|
@ -92,6 +92,8 @@
|
|||
#define OWL_SD_STATE_RC16ER BIT(1)
|
||||
#define OWL_SD_STATE_CRC7ER BIT(0)
|
||||
|
||||
#define OWL_CMD_TIMEOUT_MS 30000
|
||||
|
||||
struct owl_mmc_host {
|
||||
struct device *dev;
|
||||
struct reset_control *reset;
|
||||
|
@ -172,6 +174,7 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
|
|||
struct mmc_command *cmd,
|
||||
struct mmc_data *data)
|
||||
{
|
||||
unsigned long timeout;
|
||||
u32 mode, state, resp[2];
|
||||
u32 cmd_rsp_mask = 0;
|
||||
|
||||
|
@ -239,7 +242,10 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
|
|||
if (data)
|
||||
return;
|
||||
|
||||
if (!wait_for_completion_timeout(&owl_host->sdc_complete, 30 * HZ)) {
|
||||
timeout = msecs_to_jiffies(cmd->busy_timeout ? cmd->busy_timeout :
|
||||
OWL_CMD_TIMEOUT_MS);
|
||||
|
||||
if (!wait_for_completion_timeout(&owl_host->sdc_complete, timeout)) {
|
||||
dev_err(owl_host->dev, "CMD interrupt timeout\n");
|
||||
cmd->error = -ETIMEDOUT;
|
||||
return;
|
||||
|
|
|
@ -36,6 +36,7 @@ struct renesas_sdhi_of_data {
|
|||
struct renesas_sdhi_quirks {
|
||||
bool hs400_disabled;
|
||||
bool hs400_4taps;
|
||||
u32 hs400_bad_taps;
|
||||
};
|
||||
|
||||
struct tmio_mmc_dma {
|
||||
|
@ -61,8 +62,10 @@ struct renesas_sdhi {
|
|||
|
||||
/* Tuning values: 1 for success, 0 for failure */
|
||||
DECLARE_BITMAP(taps, BITS_PER_LONG);
|
||||
/* Sampling data comparison: 1 for match, 0 for mismatch */
|
||||
DECLARE_BITMAP(smpcmp, BITS_PER_LONG);
|
||||
unsigned int tap_num;
|
||||
unsigned long tap_set;
|
||||
unsigned int tap_set;
|
||||
};
|
||||
|
||||
#define host_to_priv(host) \
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/slot-gpio.h>
|
||||
#include <linux/mfd/tmio.h>
|
||||
|
@ -82,16 +83,11 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
|
|||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
int ret = clk_prepare_enable(priv->clk);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->clk_cd);
|
||||
if (ret < 0) {
|
||||
clk_disable_unprepare(priv->clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The clock driver may not know what maximum frequency
|
||||
|
@ -197,7 +193,6 @@ static void renesas_sdhi_clk_disable(struct tmio_mmc_host *host)
|
|||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
|
||||
clk_disable_unprepare(priv->clk);
|
||||
clk_disable_unprepare(priv->clk_cd);
|
||||
}
|
||||
|
||||
|
@ -237,7 +232,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
|
||||
|
||||
ret = mmc_regulator_set_vqmmc(host->mmc, ios);
|
||||
if (ret)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return pinctrl_select_state(priv->pinctrl, pin_state);
|
||||
|
@ -325,6 +320,8 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
|||
{
|
||||
struct tmio_mmc_host *host = mmc_priv(mmc);
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
|
||||
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
|
||||
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
|
||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||
|
@ -352,10 +349,23 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
|||
SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
|
||||
0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
|
||||
|
||||
/* Avoid bad TAP */
|
||||
if (bad_taps & BIT(priv->tap_set)) {
|
||||
u32 new_tap = (priv->tap_set + 1) % priv->tap_num;
|
||||
|
||||
if (priv->quirks && priv->quirks->hs400_4taps)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
|
||||
priv->tap_set / 2);
|
||||
if (bad_taps & BIT(new_tap))
|
||||
new_tap = (priv->tap_set - 1) % priv->tap_num;
|
||||
|
||||
if (bad_taps & BIT(new_tap)) {
|
||||
new_tap = priv->tap_set;
|
||||
dev_dbg(&host->pdev->dev, "Can't handle three bad tap in a row\n");
|
||||
}
|
||||
|
||||
priv->tap_set = new_tap;
|
||||
}
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
|
||||
priv->tap_set / (use_4tap ? 2 : 1));
|
||||
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
|
||||
SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
|
||||
|
@ -422,20 +432,16 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define SH_MOBILE_SDHI_MAX_TAP 3
|
||||
#define SH_MOBILE_SDHI_MIN_TAP_ROW 3
|
||||
|
||||
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
unsigned long tap_cnt; /* counter of tuning success */
|
||||
unsigned long tap_start;/* start position of tuning success */
|
||||
unsigned long tap_end; /* end position of tuning success */
|
||||
unsigned long ntap; /* temporary counter of tuning success */
|
||||
unsigned long i;
|
||||
unsigned int tap_start = 0, tap_end = 0, tap_cnt = 0, rs, re, i;
|
||||
unsigned int taps_size = priv->tap_num * 2, min_tap_row;
|
||||
unsigned long *bitmap;
|
||||
|
||||
priv->doing_tune = false;
|
||||
|
||||
/* Clear SCC_RVSREQ */
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
||||
|
||||
/*
|
||||
|
@ -443,42 +449,42 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
|||
* result requiring the tap to be good in both runs before
|
||||
* considering it for tuning selection.
|
||||
*/
|
||||
for (i = 0; i < priv->tap_num * 2; i++) {
|
||||
for (i = 0; i < taps_size; i++) {
|
||||
int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1);
|
||||
|
||||
if (!test_bit(i, priv->taps))
|
||||
clear_bit(i + offset, priv->taps);
|
||||
|
||||
if (!test_bit(i, priv->smpcmp))
|
||||
clear_bit(i + offset, priv->smpcmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the longest consecutive run of successful probes. If that
|
||||
* is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
|
||||
* center index as the tap.
|
||||
* If all TAP are OK, the sampling clock position is selected by
|
||||
* identifying the change point of data.
|
||||
*/
|
||||
tap_cnt = 0;
|
||||
ntap = 0;
|
||||
tap_start = 0;
|
||||
tap_end = 0;
|
||||
for (i = 0; i < priv->tap_num * 2; i++) {
|
||||
if (test_bit(i, priv->taps)) {
|
||||
ntap++;
|
||||
} else {
|
||||
if (ntap > tap_cnt) {
|
||||
tap_start = i - ntap;
|
||||
tap_end = i - 1;
|
||||
tap_cnt = ntap;
|
||||
}
|
||||
ntap = 0;
|
||||
if (bitmap_full(priv->taps, taps_size)) {
|
||||
bitmap = priv->smpcmp;
|
||||
min_tap_row = 1;
|
||||
} else {
|
||||
bitmap = priv->taps;
|
||||
min_tap_row = SH_MOBILE_SDHI_MIN_TAP_ROW;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the longest consecutive run of successful probes. If that
|
||||
* is at least SH_MOBILE_SDHI_MIN_TAP_ROW probes long then use the
|
||||
* center index as the tap, otherwise bail out.
|
||||
*/
|
||||
bitmap_for_each_set_region(bitmap, rs, re, 0, taps_size) {
|
||||
if (re - rs > tap_cnt) {
|
||||
tap_end = re;
|
||||
tap_start = rs;
|
||||
tap_cnt = tap_end - tap_start;
|
||||
}
|
||||
}
|
||||
|
||||
if (ntap > tap_cnt) {
|
||||
tap_start = i - ntap;
|
||||
tap_end = i - 1;
|
||||
tap_cnt = ntap;
|
||||
}
|
||||
|
||||
if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
|
||||
if (tap_cnt >= min_tap_row)
|
||||
priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num;
|
||||
else
|
||||
return -EIO;
|
||||
|
@ -511,6 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
|||
|
||||
priv->doing_tune = true;
|
||||
bitmap_zero(priv->taps, priv->tap_num * 2);
|
||||
bitmap_zero(priv->smpcmp, priv->tap_num * 2);
|
||||
|
||||
/* Issue CMD19 twice for each tap */
|
||||
for (i = 0; i < 2 * priv->tap_num; i++) {
|
||||
|
@ -519,6 +526,9 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
|||
|
||||
if (mmc_send_tuning(host->mmc, opcode, NULL) == 0)
|
||||
set_bit(i, priv->taps);
|
||||
|
||||
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
|
||||
set_bit(i, priv->smpcmp);
|
||||
}
|
||||
|
||||
return renesas_sdhi_select_tuning(host);
|
||||
|
@ -527,7 +537,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
|||
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
unsigned long new_tap = priv->tap_set;
|
||||
unsigned int new_tap = priv->tap_set, error_tap = priv->tap_set;
|
||||
u32 val;
|
||||
|
||||
val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ);
|
||||
|
@ -539,20 +549,32 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
|
|||
/* Change TAP position according to correction status */
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
|
||||
/*
|
||||
* With HS400, the DAT signal is based on DS, not CLK.
|
||||
* Therefore, use only CMD status.
|
||||
*/
|
||||
u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) &
|
||||
SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR;
|
||||
if (!smpcmp)
|
||||
if (!smpcmp) {
|
||||
return false; /* no error in CMD signal */
|
||||
else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP)
|
||||
} else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) {
|
||||
new_tap++;
|
||||
else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN)
|
||||
error_tap--;
|
||||
} else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) {
|
||||
new_tap--;
|
||||
else
|
||||
error_tap++;
|
||||
} else {
|
||||
return true; /* need retune */
|
||||
}
|
||||
|
||||
/*
|
||||
* When new_tap is a bad tap, we cannot change. Then, we compare
|
||||
* with the HS200 tuning result. When smpcmp[error_tap] is OK,
|
||||
* we can at least retune.
|
||||
*/
|
||||
if (bad_taps & BIT(new_tap % priv->tap_num))
|
||||
return test_bit(error_tap % priv->tap_num, priv->smpcmp);
|
||||
} else {
|
||||
if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
|
||||
return true; /* need retune */
|
||||
|
@ -705,17 +727,35 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400 = {
|
|||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
|
||||
.hs400_4taps = true,
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
||||
.hs400_disabled = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = {
|
||||
.hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7),
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
};
|
||||
|
||||
/*
|
||||
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
|
||||
* So, we want to treat them equally and only have a match for ES1.2 to enforce
|
||||
* this if there ever will be a way to distinguish ES1.2.
|
||||
*/
|
||||
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 = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||
{ .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap },
|
||||
{ .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
|
||||
{ .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 },
|
||||
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
||||
{ /* Sentinel. */ },
|
||||
};
|
||||
|
@ -860,6 +900,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
/* All SDHI have SDIO status bits which must be 1 */
|
||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
|
||||
|
||||
dev_pm_domain_start(&pdev->dev);
|
||||
|
||||
ret = renesas_sdhi_clk_enable(host);
|
||||
if (ret)
|
||||
goto efree;
|
||||
|
@ -933,10 +975,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||
goto eirq;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
|
||||
mmc_hostname(host->mmc), (unsigned long)
|
||||
(platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
|
||||
host->mmc->f_max / 1000000);
|
||||
dev_info(&pdev->dev, "%s base at %pa, max clock rate %u MHz\n",
|
||||
mmc_hostname(host->mmc), &res->start, host->mmc->f_max / 1000000);
|
||||
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -1347,7 +1347,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
|
|||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
|
||||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
||||
mmc->max_current_330 = 400;
|
||||
mmc->max_current_180 = 800;
|
||||
|
|
|
@ -1314,7 +1314,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
|
|||
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
|
||||
MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
|
||||
MMC_CAP_SYNC_RUNTIME_PM;
|
||||
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
|
||||
MMC_CAP2_NO_SDIO;
|
||||
|
||||
|
|
|
@ -958,13 +958,6 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
|
|||
{
|
||||
u32 dcon, imsk, stoptries = 3;
|
||||
|
||||
/* write DCON register */
|
||||
|
||||
if (!data) {
|
||||
writel(0, host->base + S3C2410_SDIDCON);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((data->blksz & 3) != 0) {
|
||||
/* We cannot deal with unaligned blocks with more than
|
||||
* one block being transferred. */
|
||||
|
|
|
@ -97,6 +97,11 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
|||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
|
||||
0, 10);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
|
||||
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
|
||||
writel(tmp, reg);
|
||||
|
@ -111,7 +116,10 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
|||
tmp &= ~SDHCI_CDNS_HRS04_WR;
|
||||
writel(tmp, reg);
|
||||
|
||||
return 0;
|
||||
ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
|
||||
0, 10);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
* Author: Wolfram Sang <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/delay.h>
|
||||
|
@ -89,7 +90,8 @@
|
|||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
|
||||
#define ESDHC_TUNING_START_TAP_MASK 0xff
|
||||
#define ESDHC_TUNING_START_TAP_MASK 0x7f
|
||||
#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE (1 << 7)
|
||||
#define ESDHC_TUNING_STEP_MASK 0x00070000
|
||||
#define ESDHC_TUNING_STEP_SHIFT 16
|
||||
|
||||
|
@ -214,6 +216,7 @@ static const struct esdhc_soc_data usdhc_imx6sl_data = {
|
|||
static const struct esdhc_soc_data usdhc_imx6sll_data = {
|
||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||
| ESDHC_FLAG_HS400
|
||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
||||
};
|
||||
|
||||
|
@ -399,7 +402,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
|||
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
||||
| SDHCI_SUPPORT_SDR50
|
||||
| SDHCI_USE_SDR50_TUNING
|
||||
| (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
|
||||
| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
|
||||
SDHCI_TUNING_MODE_3);
|
||||
|
||||
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||
val |= SDHCI_SUPPORT_HS400;
|
||||
|
@ -417,9 +421,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
|
|||
|
||||
if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
|
||||
val = 0;
|
||||
val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
|
||||
val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
|
||||
val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
|
||||
val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
|
||||
val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
|
||||
val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
|
||||
}
|
||||
|
||||
if (unlikely(reg == SDHCI_INT_STATUS)) {
|
||||
|
@ -1313,6 +1317,18 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
|
|||
tmp |= imx_data->boarddata.tuning_step
|
||||
<< ESDHC_TUNING_STEP_SHIFT;
|
||||
}
|
||||
|
||||
/* Disable the CMD CRC check for tuning, if not, need to
|
||||
* add some delay after every tuning command, because
|
||||
* hardware standard tuning logic will directly go to next
|
||||
* step once it detect the CMD CRC error, will not wait for
|
||||
* the card side to finally send out the tuning data, trigger
|
||||
* the buffer read ready interrupt immediately. If usdhc send
|
||||
* the next tuning command some eMMC card will stuck, can't
|
||||
* response, block the tuning procedure or the first command
|
||||
* after the whole tuning procedure always can't get any response.
|
||||
*/
|
||||
tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
|
||||
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||
/*
|
||||
|
@ -1596,6 +1612,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
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;
|
||||
|
||||
/* GPIO CD can be set as a wakeup source */
|
||||
host->mmc->caps |= MMC_CAP_CD_WAKE;
|
||||
|
||||
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||
|
||||
|
@ -1653,8 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto disable_ahb_clk;
|
||||
|
||||
host->tuning_delay = 1;
|
||||
|
||||
sdhci_esdhc_imx_hwinit(host);
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
|
@ -1731,8 +1749,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
|||
mmc_retune_needed(host->mmc);
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (!ret)
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pinctrl_pm_select_sleep_state(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mmc_gpio_set_cd_wake(host->mmc, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1756,6 +1780,9 @@ static int sdhci_esdhc_resume(struct device *dev)
|
|||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||
ret = cqhci_resume(host->mmc);
|
||||
|
||||
if (!ret)
|
||||
ret = mmc_gpio_set_cd_wake(host->mmc, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,521 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Freescale eSDHC ColdFire family controller driver, platform bus.
|
||||
*
|
||||
* Copyright (c) 2020 Timesys Corporation
|
||||
* Author: Angelo Dureghello <angelo.dureghello@timesys.it>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_data/mmc-esdhc-mcf.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "sdhci-esdhc.h"
|
||||
|
||||
#define ESDHC_PROCTL_D3CD 0x08
|
||||
#define ESDHC_SYS_CTRL_DTOCV_MASK 0x0f
|
||||
#define ESDHC_DEFAULT_HOST_CONTROL 0x28
|
||||
|
||||
/*
|
||||
* Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25.
|
||||
*/
|
||||
#define ESDHC_INT_VENDOR_SPEC_DMA_ERR BIT(28)
|
||||
|
||||
struct pltfm_mcf_data {
|
||||
struct clk *clk_ipg;
|
||||
struct clk *clk_ahb;
|
||||
struct clk *clk_per;
|
||||
int aside;
|
||||
int current_bus_width;
|
||||
};
|
||||
|
||||
static inline void esdhc_mcf_buffer_swap32(u32 *buf, int len)
|
||||
{
|
||||
int i;
|
||||
u32 temp;
|
||||
|
||||
len = (len + 3) >> 2;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
temp = swab32(*buf);
|
||||
*buf++ = temp;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void esdhc_clrset_be(struct sdhci_host *host,
|
||||
u32 mask, u32 val, int reg)
|
||||
{
|
||||
void __iomem *base = host->ioaddr + (reg & ~3);
|
||||
u8 shift = (reg & 3) << 3;
|
||||
|
||||
mask <<= shift;
|
||||
val <<= shift;
|
||||
|
||||
if (reg == SDHCI_HOST_CONTROL)
|
||||
val |= ESDHC_PROCTL_D3CD;
|
||||
|
||||
writel((readl(base) & ~mask) | val, base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: mcf is big-endian, single bytes need to be accessed at big endian
|
||||
* offsets.
|
||||
*/
|
||||
static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
|
||||
{
|
||||
void __iomem *base = host->ioaddr + (reg & ~3);
|
||||
u8 shift = (reg & 3) << 3;
|
||||
u32 mask = ~(0xff << shift);
|
||||
|
||||
if (reg == SDHCI_HOST_CONTROL) {
|
||||
u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL;
|
||||
u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3;
|
||||
u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1);
|
||||
|
||||
tmp &= ~0x03;
|
||||
tmp |= dma_bits;
|
||||
|
||||
/*
|
||||
* Recomposition needed, restore always endianness and
|
||||
* keep D3CD and AI, just setting bus width.
|
||||
*/
|
||||
host_ctrl |= val;
|
||||
host_ctrl |= (dma_bits << 8);
|
||||
writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
writel((readl(base) & mask) | (val << shift), base);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
|
||||
void __iomem *base = host->ioaddr + (reg & ~3);
|
||||
u8 shift = (reg & 3) << 3;
|
||||
u32 mask = ~(0xffff << shift);
|
||||
|
||||
switch (reg) {
|
||||
case SDHCI_TRANSFER_MODE:
|
||||
mcf_data->aside = val;
|
||||
return;
|
||||
case SDHCI_COMMAND:
|
||||
if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||
val |= SDHCI_CMD_ABORTCMD;
|
||||
|
||||
/*
|
||||
* As for the fsl driver,
|
||||
* we have to set the mode in a single write here.
|
||||
*/
|
||||
writel(val << 16 | mcf_data->aside,
|
||||
host->ioaddr + SDHCI_TRANSFER_MODE);
|
||||
return;
|
||||
}
|
||||
|
||||
writel((readl(base) & mask) | (val << shift), base);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg)
|
||||
{
|
||||
writel(val, host->ioaddr + reg);
|
||||
}
|
||||
|
||||
static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
|
||||
{
|
||||
if (reg == SDHCI_HOST_CONTROL) {
|
||||
u8 __iomem *base = host->ioaddr + (reg & ~3);
|
||||
u16 val = readw(base + 2);
|
||||
u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK;
|
||||
u8 host_ctrl = val & 0xff;
|
||||
|
||||
host_ctrl &= ~SDHCI_CTRL_DMA_MASK;
|
||||
host_ctrl |= dma_bits;
|
||||
|
||||
return host_ctrl;
|
||||
}
|
||||
|
||||
return readb(host->ioaddr + (reg ^ 0x3));
|
||||
}
|
||||
|
||||
static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
|
||||
{
|
||||
/*
|
||||
* For SDHCI_HOST_VERSION, sdhci specs defines 0xFE,
|
||||
* a wrong offset for us, we are at 0xFC.
|
||||
*/
|
||||
if (reg == SDHCI_HOST_VERSION)
|
||||
reg -= 2;
|
||||
|
||||
return readw(host->ioaddr + (reg ^ 0x2));
|
||||
}
|
||||
|
||||
static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(host->ioaddr + reg);
|
||||
|
||||
/*
|
||||
* RM (25.3.9) sd pin clock must never exceed 25Mhz.
|
||||
* So forcing legacy mode at 25Mhz.
|
||||
*/
|
||||
if (unlikely(reg == SDHCI_CAPABILITIES))
|
||||
val &= ~SDHCI_CAN_DO_HISPD;
|
||||
|
||||
if (unlikely(reg == SDHCI_INT_STATUS)) {
|
||||
if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
|
||||
val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
|
||||
val |= SDHCI_INT_ADMA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host)
|
||||
{
|
||||
return 1 << 27;
|
||||
}
|
||||
|
||||
static void esdhc_mcf_set_timeout(struct sdhci_host *host,
|
||||
struct mmc_command *cmd)
|
||||
{
|
||||
/* Use maximum timeout counter */
|
||||
esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE,
|
||||
SDHCI_TIMEOUT_CONTROL);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
|
||||
esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
|
||||
mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
|
||||
|
||||
sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
|
||||
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
|
||||
}
|
||||
|
||||
static unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return pltfm_host->clock;
|
||||
}
|
||||
|
||||
static unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return pltfm_host->clock / 256 / 16;
|
||||
}
|
||||
|
||||
static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host,
|
||||
unsigned int clock)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR;
|
||||
u32 fvco, fsys, fesdhc, temp;
|
||||
const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256};
|
||||
int delta, old_delta = clock;
|
||||
int i, q, ri, rq;
|
||||
|
||||
if (clock == 0) {
|
||||
host->mmc->actual_clock = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ColdFire eSDHC clock.s
|
||||
*
|
||||
* pll -+-> / outdiv1 --> fsys
|
||||
* +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS
|
||||
*
|
||||
* mcf5441x datasheet says:
|
||||
* (8.1.2) eSDHC should be 40 MHz max
|
||||
* (25.3.9) eSDHC input is, as example, 96 Mhz ...
|
||||
* (25.3.9) sd pin clock must never exceed 25Mhz
|
||||
*
|
||||
* fvco = fsys * outdvi1 + 1
|
||||
* fshdc = fvco / outdiv3 + 1
|
||||
*/
|
||||
temp = readl(pll_dr);
|
||||
fsys = pltfm_host->clock;
|
||||
fvco = fsys * ((temp & 0x1f) + 1);
|
||||
fesdhc = fvco / (((temp >> 10) & 0x1f) + 1);
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
int result = fesdhc / sdclkfs[i];
|
||||
|
||||
for (q = 1; q < 17; ++q) {
|
||||
int finale = result / q;
|
||||
|
||||
delta = abs(clock - finale);
|
||||
|
||||
if (delta < old_delta) {
|
||||
old_delta = delta;
|
||||
ri = i;
|
||||
rq = q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply divisors and re-enable all the clocks
|
||||
*/
|
||||
temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) |
|
||||
(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN);
|
||||
esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
host->mmc->actual_clock = clock;
|
||||
|
||||
mdelay(1);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
switch (width) {
|
||||
case MMC_BUS_WIDTH_4:
|
||||
mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS;
|
||||
break;
|
||||
default:
|
||||
mcf_data->current_bus_width = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
|
||||
mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_request_done(struct sdhci_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
u32 *buffer;
|
||||
int i;
|
||||
|
||||
if (!mrq->data || !mrq->data->bytes_xfered)
|
||||
goto exit_done;
|
||||
|
||||
if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE)
|
||||
goto exit_done;
|
||||
|
||||
/*
|
||||
* On mcf5441x there is no hw sdma option/flag to select the dma
|
||||
* transfer endiannes. A swap after the transfer is needed.
|
||||
*/
|
||||
for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
|
||||
buffer = (u32 *)sg_virt(sg);
|
||||
esdhc_mcf_buffer_swap32(buffer, sg->length);
|
||||
}
|
||||
|
||||
exit_done:
|
||||
mmc_request_done(host->mmc, mrq);
|
||||
}
|
||||
|
||||
static void esdhc_mcf_copy_to_bounce_buffer(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
unsigned int length)
|
||||
{
|
||||
sg_copy_to_buffer(data->sg, data->sg_len,
|
||||
host->bounce_buffer, length);
|
||||
|
||||
esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
|
||||
data->blksz * data->blocks);
|
||||
}
|
||||
|
||||
static struct sdhci_ops sdhci_esdhc_ops = {
|
||||
.reset = esdhc_mcf_reset,
|
||||
.set_clock = esdhc_mcf_pltfm_set_clock,
|
||||
.get_max_clock = esdhc_mcf_pltfm_get_max_clock,
|
||||
.get_min_clock = esdhc_mcf_pltfm_get_min_clock,
|
||||
.set_bus_width = esdhc_mcf_pltfm_set_bus_width,
|
||||
.get_max_timeout_count = esdhc_mcf_get_max_timeout_count,
|
||||
.set_timeout = esdhc_mcf_set_timeout,
|
||||
.write_b = esdhc_mcf_writeb_be,
|
||||
.write_w = esdhc_mcf_writew_be,
|
||||
.write_l = esdhc_mcf_writel_be,
|
||||
.read_b = esdhc_mcf_readb_be,
|
||||
.read_w = esdhc_mcf_readw_be,
|
||||
.read_l = esdhc_mcf_readl_be,
|
||||
.copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
|
||||
.request_done = esdhc_mcf_request_done,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = {
|
||||
.ops = &sdhci_esdhc_ops,
|
||||
.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA,
|
||||
/*
|
||||
* Mandatory quirk,
|
||||
* controller does not support cmd23,
|
||||
* without, on > 8G cards cmd23 is used, and
|
||||
* driver times out.
|
||||
*/
|
||||
SDHCI_QUIRK2_HOST_NO_CMD23,
|
||||
};
|
||||
|
||||
static int esdhc_mcf_plat_init(struct sdhci_host *host,
|
||||
struct pltfm_mcf_data *mcf_data)
|
||||
{
|
||||
struct mcf_esdhc_platform_data *plat_data;
|
||||
|
||||
if (!host->mmc->parent->platform_data) {
|
||||
dev_err(mmc_dev(host->mmc), "no platform data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
plat_data = (struct mcf_esdhc_platform_data *)
|
||||
host->mmc->parent->platform_data;
|
||||
|
||||
/* Card_detect */
|
||||
switch (plat_data->cd_type) {
|
||||
default:
|
||||
case ESDHC_CD_CONTROLLER:
|
||||
/* We have a working card_detect back */
|
||||
host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
|
||||
break;
|
||||
case ESDHC_CD_PERMANENT:
|
||||
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
|
||||
break;
|
||||
case ESDHC_CD_NONE:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (plat_data->max_bus_width) {
|
||||
case 4:
|
||||
host->mmc->caps |= MMC_CAP_4_BIT_DATA;
|
||||
break;
|
||||
case 1:
|
||||
default:
|
||||
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_mcf_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct pltfm_mcf_data *mcf_data;
|
||||
int err;
|
||||
|
||||
host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata,
|
||||
sizeof(*mcf_data));
|
||||
|
||||
if (IS_ERR(host))
|
||||
return PTR_ERR(host);
|
||||
|
||||
pltfm_host = sdhci_priv(host);
|
||||
mcf_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
host->sdma_boundary = 0;
|
||||
|
||||
host->flags |= SDHCI_AUTO_CMD12;
|
||||
|
||||
mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
|
||||
if (IS_ERR(mcf_data->clk_ipg)) {
|
||||
err = PTR_ERR(mcf_data->clk_ipg);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
|
||||
if (IS_ERR(mcf_data->clk_ahb)) {
|
||||
err = PTR_ERR(mcf_data->clk_ahb);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
mcf_data->clk_per = devm_clk_get(&pdev->dev, "per");
|
||||
if (IS_ERR(mcf_data->clk_per)) {
|
||||
err = PTR_ERR(mcf_data->clk_per);
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
pltfm_host->clk = mcf_data->clk_per;
|
||||
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
|
||||
err = clk_prepare_enable(mcf_data->clk_per);
|
||||
if (err)
|
||||
goto err_exit;
|
||||
|
||||
err = clk_prepare_enable(mcf_data->clk_ipg);
|
||||
if (err)
|
||||
goto unprep_per;
|
||||
|
||||
err = clk_prepare_enable(mcf_data->clk_ahb);
|
||||
if (err)
|
||||
goto unprep_ipg;
|
||||
|
||||
err = esdhc_mcf_plat_init(host, mcf_data);
|
||||
if (err)
|
||||
goto unprep_ahb;
|
||||
|
||||
err = sdhci_setup_host(host);
|
||||
if (err)
|
||||
goto unprep_ahb;
|
||||
|
||||
if (!host->bounce_buffer) {
|
||||
dev_err(&pdev->dev, "bounce buffer not allocated");
|
||||
err = -ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
err = __sdhci_add_host(host);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
sdhci_cleanup_host(host);
|
||||
unprep_ahb:
|
||||
clk_disable_unprepare(mcf_data->clk_ahb);
|
||||
unprep_ipg:
|
||||
clk_disable_unprepare(mcf_data->clk_ipg);
|
||||
unprep_per:
|
||||
clk_disable_unprepare(mcf_data->clk_per);
|
||||
err_exit:
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sdhci_esdhc_mcf_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
sdhci_remove_host(host, 0);
|
||||
|
||||
clk_disable_unprepare(mcf_data->clk_ipg);
|
||||
clk_disable_unprepare(mcf_data->clk_ahb);
|
||||
clk_disable_unprepare(mcf_data->clk_per);
|
||||
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_esdhc_mcf_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-esdhc-mcf",
|
||||
},
|
||||
.probe = sdhci_esdhc_mcf_probe,
|
||||
.remove = sdhci_esdhc_mcf_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(sdhci_esdhc_mcf_driver);
|
||||
|
||||
MODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC");
|
||||
MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright (c) 2007 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||
* Copyright (c) 2010 Pengutronix e.K.
|
||||
* Author: Wolfram Sang <w.sang@pengutronix.de>
|
||||
* Author: Wolfram Sang <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -56,19 +57,27 @@
|
|||
#define CORE_FLL_CYCLE_CNT BIT(18)
|
||||
#define CORE_DLL_CLOCK_DISABLE BIT(21)
|
||||
|
||||
#define CORE_VENDOR_SPEC_POR_VAL 0xa1c
|
||||
#define DLL_USR_CTL_POR_VAL 0x10800
|
||||
#define ENABLE_DLL_LOCK_STATUS BIT(26)
|
||||
#define FINE_TUNE_MODE_EN BIT(27)
|
||||
#define BIAS_OK_SIGNAL BIT(29)
|
||||
|
||||
#define DLL_CONFIG_3_LOW_FREQ_VAL 0x08
|
||||
#define DLL_CONFIG_3_HIGH_FREQ_VAL 0x10
|
||||
|
||||
#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
|
||||
#define CORE_CLK_PWRSAVE BIT(1)
|
||||
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
||||
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
||||
#define CORE_HC_MCLK_SEL_MASK (3 << 8)
|
||||
#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15)
|
||||
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
|
||||
#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15)
|
||||
#define CORE_IO_PAD_PWR_SWITCH BIT(16)
|
||||
#define CORE_HC_SELECT_IN_EN BIT(18)
|
||||
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
||||
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
||||
|
||||
#define CORE_3_0V_SUPPORT (1 << 25)
|
||||
#define CORE_1_8V_SUPPORT (1 << 26)
|
||||
#define CORE_3_0V_SUPPORT BIT(25)
|
||||
#define CORE_1_8V_SUPPORT BIT(26)
|
||||
#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
|
||||
|
||||
#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
||||
|
@ -156,6 +165,7 @@ struct sdhci_msm_offset {
|
|||
u32 core_dll_config_3;
|
||||
u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */
|
||||
u32 core_ddr_config;
|
||||
u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
|
||||
|
@ -185,6 +195,7 @@ static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
|
|||
.core_dll_config_2 = 0x254,
|
||||
.core_dll_config_3 = 0x258,
|
||||
.core_ddr_config = 0x25c,
|
||||
.core_dll_usr_ctl = 0x388,
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_offset sdhci_msm_mci_offset = {
|
||||
|
@ -230,6 +241,7 @@ struct sdhci_msm_variant_ops {
|
|||
struct sdhci_msm_variant_info {
|
||||
bool mci_removed;
|
||||
bool restore_dll_config;
|
||||
bool uses_tassadar_dll;
|
||||
const struct sdhci_msm_variant_ops *var_ops;
|
||||
const struct sdhci_msm_offset *offset;
|
||||
};
|
||||
|
@ -243,6 +255,8 @@ struct sdhci_msm_host {
|
|||
struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
|
||||
unsigned long clk_rate;
|
||||
struct mmc_host *mmc;
|
||||
struct opp_table *opp_table;
|
||||
bool has_opp_table;
|
||||
bool use_14lpp_dll_reset;
|
||||
bool tuning_done;
|
||||
bool calibration_done;
|
||||
|
@ -260,6 +274,9 @@ struct sdhci_msm_host {
|
|||
bool use_cdr;
|
||||
u32 transfer_mode;
|
||||
bool updated_ddr_cfg;
|
||||
bool uses_tassadar_dll;
|
||||
u32 dll_config;
|
||||
u32 ddr_config;
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
|
||||
|
@ -332,7 +349,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
|||
int rc;
|
||||
|
||||
clock = msm_get_clock_rate_for_bus_mode(host, clock);
|
||||
rc = clk_set_rate(core_clk, clock);
|
||||
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
|
||||
if (rc) {
|
||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||
mmc_hostname(host->mmc), clock,
|
||||
|
@ -601,6 +618,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
config &= ~CORE_CLK_PWRSAVE;
|
||||
writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
|
||||
|
||||
config = msm_host->dll_config;
|
||||
writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config);
|
||||
|
||||
if (msm_host->use_14lpp_dll_reset) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_dll_config);
|
||||
|
@ -626,7 +646,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
config |= CORE_DLL_PDN;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
msm_offset->core_dll_config);
|
||||
msm_cm_dll_set_freq(host);
|
||||
|
||||
if (!msm_host->dll_config)
|
||||
msm_cm_dll_set_freq(host);
|
||||
|
||||
if (msm_host->use_14lpp_dll_reset &&
|
||||
!IS_ERR_OR_NULL(msm_host->xo_clk)) {
|
||||
|
@ -666,7 +688,8 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
msm_offset->core_dll_config);
|
||||
|
||||
if (msm_host->use_14lpp_dll_reset) {
|
||||
msm_cm_dll_set_freq(host);
|
||||
if (!msm_host->dll_config)
|
||||
msm_cm_dll_set_freq(host);
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_dll_config_2);
|
||||
config &= ~CORE_DLL_CLOCK_DISABLE;
|
||||
|
@ -674,6 +697,27 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||
msm_offset->core_dll_config_2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure DLL user control register to enable DLL status.
|
||||
* This setting is applicable to SDCC v5.1 onwards only.
|
||||
*/
|
||||
if (msm_host->uses_tassadar_dll) {
|
||||
config = DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN |
|
||||
ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
msm_offset->core_dll_usr_ctl);
|
||||
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_dll_config_3);
|
||||
config &= ~0xFF;
|
||||
if (msm_host->clk_rate < 150000000)
|
||||
config |= DLL_CONFIG_3_LOW_FREQ_VAL;
|
||||
else
|
||||
config |= DLL_CONFIG_3_HIGH_FREQ_VAL;
|
||||
writel_relaxed(config, host->ioaddr +
|
||||
msm_offset->core_dll_config_3);
|
||||
}
|
||||
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_dll_config);
|
||||
config |= CORE_DLL_EN;
|
||||
|
@ -951,7 +995,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
|
|||
ddr_cfg_offset = msm_offset->core_ddr_config;
|
||||
else
|
||||
ddr_cfg_offset = msm_offset->core_ddr_config_old;
|
||||
writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + ddr_cfg_offset);
|
||||
writel_relaxed(msm_host->ddr_config, host->ioaddr + ddr_cfg_offset);
|
||||
|
||||
if (mmc->ios.enhanced_strobe) {
|
||||
config = readl_relaxed(host->ioaddr +
|
||||
|
@ -1129,6 +1173,12 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
/* Clock-Data-Recovery used to dynamically adjust RX sampling point */
|
||||
msm_host->use_cdr = true;
|
||||
|
||||
/*
|
||||
* Clear tuning_done flag before tuning to ensure proper
|
||||
* HS400 settings.
|
||||
*/
|
||||
msm_host->tuning_done = 0;
|
||||
|
||||
/*
|
||||
* For HS400 tuning in HS200 timing requires:
|
||||
* - select MCLK/2 in VENDOR_SPEC
|
||||
|
@ -1830,6 +1880,36 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask)
|
|||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
#define DRIVER_NAME "sdhci_msm"
|
||||
#define SDHCI_MSM_DUMP(f, x...) \
|
||||
pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
|
||||
|
||||
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_msm_offset *msm_offset = msm_host->offset;
|
||||
|
||||
SDHCI_MSM_DUMP("----------- VENDOR REGISTER DUMP -----------\n");
|
||||
|
||||
SDHCI_MSM_DUMP(
|
||||
"DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n",
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_dll_status),
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_dll_config),
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2));
|
||||
SDHCI_MSM_DUMP(
|
||||
"DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n",
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_dll_config_3),
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_dll_usr_ctl),
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_ddr_config));
|
||||
SDHCI_MSM_DUMP(
|
||||
"Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n",
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec),
|
||||
readl_relaxed(host->ioaddr +
|
||||
msm_offset->core_vendor_spec_func2),
|
||||
readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3));
|
||||
}
|
||||
|
||||
static const struct sdhci_msm_variant_ops mci_var_ops = {
|
||||
.msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed,
|
||||
.msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed,
|
||||
|
@ -1858,10 +1938,18 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
|
|||
.offset = &sdhci_msm_v5_offset,
|
||||
};
|
||||
|
||||
static const struct sdhci_msm_variant_info sm8250_sdhci_var = {
|
||||
.mci_removed = true,
|
||||
.uses_tassadar_dll = true,
|
||||
.var_ops = &v5_var_ops,
|
||||
.offset = &sdhci_msm_v5_offset,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||
{.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -1877,16 +1965,34 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
|||
.write_w = sdhci_msm_writew,
|
||||
.write_b = sdhci_msm_writeb,
|
||||
.irq = sdhci_msm_cqe_irq,
|
||||
.dump_vendor_regs = sdhci_msm_dump_vendor_regs,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
|
||||
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
.ops = &sdhci_msm_ops,
|
||||
};
|
||||
|
||||
static inline void sdhci_msm_get_of_property(struct platform_device *pdev,
|
||||
struct sdhci_host *host)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
if (of_property_read_u32(node, "qcom,ddr-config",
|
||||
&msm_host->ddr_config))
|
||||
msm_host->ddr_config = DDR_CONFIG_POR_VAL;
|
||||
|
||||
of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config);
|
||||
}
|
||||
|
||||
|
||||
static int sdhci_msm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
|
@ -1925,10 +2031,12 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
msm_host->restore_dll_config = var_info->restore_dll_config;
|
||||
msm_host->var_ops = var_info->var_ops;
|
||||
msm_host->offset = var_info->offset;
|
||||
msm_host->uses_tassadar_dll = var_info->uses_tassadar_dll;
|
||||
|
||||
msm_offset = msm_host->offset;
|
||||
|
||||
sdhci_get_of_property(pdev);
|
||||
sdhci_msm_get_of_property(pdev, host);
|
||||
|
||||
msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
|
||||
|
||||
|
@ -1962,8 +2070,23 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
}
|
||||
msm_host->bulk_clks[0].clk = clk;
|
||||
|
||||
msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
|
||||
if (IS_ERR(msm_host->opp_table)) {
|
||||
ret = PTR_ERR(msm_host->opp_table);
|
||||
goto bus_clk_disable;
|
||||
}
|
||||
|
||||
/* OPP table is optional */
|
||||
ret = dev_pm_opp_of_add_table(&pdev->dev);
|
||||
if (!ret) {
|
||||
msm_host->has_opp_table = true;
|
||||
} else if (ret != -ENODEV) {
|
||||
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
|
||||
goto opp_cleanup;
|
||||
}
|
||||
|
||||
/* Vote for maximum clock rate for maximum performance */
|
||||
ret = clk_set_rate(clk, INT_MAX);
|
||||
ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX);
|
||||
if (ret)
|
||||
dev_warn(&pdev->dev, "core clock boost failed\n");
|
||||
|
||||
|
@ -1980,7 +2103,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
|
|||
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
msm_host->bulk_clks);
|
||||
if (ret)
|
||||
goto bus_clk_disable;
|
||||
goto opp_cleanup;
|
||||
|
||||
/*
|
||||
* xo clock is needed for FLL feature of cm_dll.
|
||||
|
@ -2117,6 +2240,10 @@ pm_runtime_disable:
|
|||
clk_disable:
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
msm_host->bulk_clks);
|
||||
opp_cleanup:
|
||||
if (msm_host->has_opp_table)
|
||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||
dev_pm_opp_put_clkname(msm_host->opp_table);
|
||||
bus_clk_disable:
|
||||
if (!IS_ERR(msm_host->bus_clk))
|
||||
clk_disable_unprepare(msm_host->bus_clk);
|
||||
|
@ -2135,6 +2262,9 @@ static int sdhci_msm_remove(struct platform_device *pdev)
|
|||
|
||||
sdhci_remove_host(host, dead);
|
||||
|
||||
if (msm_host->has_opp_table)
|
||||
dev_pm_opp_of_remove_table(&pdev->dev);
|
||||
dev_pm_opp_put_clkname(msm_host->opp_table);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
@ -2153,6 +2283,8 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
|
|||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
/* Drop the performance vote */
|
||||
dev_pm_opp_set_rate(dev, 0);
|
||||
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
||||
msm_host->bulk_clks);
|
||||
|
||||
|
@ -2175,9 +2307,11 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
|
|||
* restore the SDR DLL settings when the clock is ungated.
|
||||
*/
|
||||
if (msm_host->restore_dll_config && msm_host->clk_rate)
|
||||
return sdhci_msm_restore_sdr_dll_config(host);
|
||||
ret = sdhci_msm_restore_sdr_dll_config(host);
|
||||
|
||||
return 0;
|
||||
dev_pm_opp_set_rate(dev, msm_host->clk_rate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops sdhci_msm_pm_ops = {
|
||||
|
|
|
@ -28,15 +28,26 @@
|
|||
#include "sdhci-pltfm.h"
|
||||
|
||||
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
|
||||
|
||||
#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
|
||||
#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
|
||||
|
||||
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
|
||||
#define VENDOR_ENHANCED_STROBE BIT(0)
|
||||
|
||||
#define PHY_CLK_TOO_SLOW_HZ 400000
|
||||
|
||||
#define SDHCI_ITAPDLY_CHGWIN 0x200
|
||||
#define SDHCI_ITAPDLY_ENABLE 0x100
|
||||
#define SDHCI_OTAPDLY_ENABLE 0x40
|
||||
|
||||
/* Default settings for ZynqMP Clock Phases */
|
||||
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
|
||||
#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
|
||||
|
||||
#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
|
||||
#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
|
||||
|
||||
/*
|
||||
* On some SoCs the syscon area has a feature where the upper 16-bits of
|
||||
* each 32-bit register act as a write mask for the lower 16-bits. This allows
|
||||
|
@ -62,22 +73,36 @@ struct sdhci_arasan_soc_ctl_field {
|
|||
/**
|
||||
* struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
|
||||
*
|
||||
* @baseclkfreq: Where to find corecfg_baseclkfreq
|
||||
* @clockmultiplier: Where to find corecfg_clockmultiplier
|
||||
* @support64b: Where to find SUPPORT64B bit
|
||||
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
|
||||
*
|
||||
* It's up to the licensee of the Arsan IP block to make these available
|
||||
* somewhere if needed. Presumably these will be scattered somewhere that's
|
||||
* accessible via the syscon API.
|
||||
*
|
||||
* @baseclkfreq: Where to find corecfg_baseclkfreq
|
||||
* @clockmultiplier: Where to find corecfg_clockmultiplier
|
||||
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
|
||||
*/
|
||||
struct sdhci_arasan_soc_ctl_map {
|
||||
struct sdhci_arasan_soc_ctl_field baseclkfreq;
|
||||
struct sdhci_arasan_soc_ctl_field clockmultiplier;
|
||||
struct sdhci_arasan_soc_ctl_field support64b;
|
||||
bool hiword_update;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdhci_arasan_clk_data
|
||||
* struct sdhci_arasan_clk_ops - Clock Operations for Arasan SD controller
|
||||
*
|
||||
* @sdcardclk_ops: The output clock related operations
|
||||
* @sampleclk_ops: The sample clock related operations
|
||||
*/
|
||||
struct sdhci_arasan_clk_ops {
|
||||
const struct clk_ops *sdcardclk_ops;
|
||||
const struct clk_ops *sampleclk_ops;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
|
||||
*
|
||||
* @sdcardclk_hw: Struct for the clock we might provide to a PHY.
|
||||
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
|
||||
* @sampleclk_hw: Struct for the clock we might provide to a PHY.
|
||||
|
@ -103,14 +128,18 @@ struct sdhci_arasan_zynqmp_clk_data {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct sdhci_arasan_data
|
||||
* struct sdhci_arasan_data - Arasan Controller Data
|
||||
*
|
||||
* @host: Pointer to the main SDHCI host structure.
|
||||
* @clk_ahb: Pointer to the AHB clock
|
||||
* @phy: Pointer to the generic phy
|
||||
* @is_phy_on: True if the PHY is on; false if not.
|
||||
* @has_cqe: True if controller has command queuing engine.
|
||||
* @clk_data: Struct for the Arasan Controller Clock Data.
|
||||
* @clk_ops: Struct for the Arasan Controller Clock Operations.
|
||||
* @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers.
|
||||
* @soc_ctl_map: Map to get offsets into soc_ctl registers.
|
||||
* @quirks: Arasan deviations from spec.
|
||||
*/
|
||||
struct sdhci_arasan_data {
|
||||
struct sdhci_host *host;
|
||||
|
@ -120,10 +149,11 @@ struct sdhci_arasan_data {
|
|||
|
||||
bool has_cqe;
|
||||
struct sdhci_arasan_clk_data clk_data;
|
||||
const struct sdhci_arasan_clk_ops *clk_ops;
|
||||
|
||||
struct regmap *soc_ctl_base;
|
||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
||||
unsigned int quirks; /* Arasan deviations from spec */
|
||||
unsigned int quirks;
|
||||
|
||||
/* Controller does not have CD wired and will not function normally without */
|
||||
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
|
||||
|
@ -135,6 +165,7 @@ struct sdhci_arasan_data {
|
|||
struct sdhci_arasan_of_data {
|
||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
||||
const struct sdhci_pltfm_data *pdata;
|
||||
const struct sdhci_arasan_clk_ops *clk_ops;
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
|
||||
|
@ -155,17 +186,26 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
|
|||
.hiword_update = false,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
|
||||
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
|
||||
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
|
||||
.support64b = { .reg = 0x4, .width = 1, .shift = 24 },
|
||||
.hiword_update = false,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
* @fld: The field to write to
|
||||
* @val: The value to write
|
||||
*
|
||||
* This function allows writing to fields in sdhci_arasan_soc_ctl_map.
|
||||
* Note that if a field is specified as not available (shift < 0) then
|
||||
* this function will silently return an error code. It will be noisy
|
||||
* and print errors for any other (unexpected) errors.
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
* @fld: The field to write to
|
||||
* @val: The value to write
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
|
||||
const struct sdhci_arasan_soc_ctl_field *fld,
|
||||
|
@ -335,29 +375,6 @@ static const struct sdhci_ops sdhci_arasan_ops = {
|
|||
.set_power = sdhci_set_power_and_bus_voltage,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_data = {
|
||||
.pdata = &sdhci_arasan_pdata,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
|
||||
.pdata = &sdhci_arasan_zynqmp_pdata,
|
||||
};
|
||||
|
||||
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||
{
|
||||
int cmd_error = 0;
|
||||
|
@ -414,28 +431,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
|
|||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
|
||||
.soc_ctl_map = &rk3399_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
|
||||
.soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
|
||||
.soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* sdhci_arasan_suspend - Suspend method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Put the device in a low power state.
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_arasan_suspend(struct device *dev)
|
||||
{
|
||||
|
@ -476,9 +479,10 @@ static int sdhci_arasan_suspend(struct device *dev)
|
|||
/**
|
||||
* sdhci_arasan_resume - Resume method for the driver
|
||||
* @dev: Address of the device structure
|
||||
* Returns 0 on success and error value on error
|
||||
*
|
||||
* Resume operation after suspend
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_arasan_resume(struct device *dev)
|
||||
{
|
||||
|
@ -524,54 +528,19 @@ static int sdhci_arasan_resume(struct device *dev)
|
|||
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
||||
sdhci_arasan_resume);
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
/* SoC-specific compatible strings w/ soc_ctl_map */
|
||||
{
|
||||
.compatible = "rockchip,rk3399-sdhci-5.1",
|
||||
.data = &sdhci_arasan_rk3399_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,lgm-sdhci-5.1-emmc",
|
||||
.data = &intel_lgm_emmc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,lgm-sdhci-5.1-sdxc",
|
||||
.data = &intel_lgm_sdxc_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.compatible = "arasan,sdhci-8.9a",
|
||||
.data = &sdhci_arasan_data,
|
||||
},
|
||||
{
|
||||
.compatible = "arasan,sdhci-5.1",
|
||||
.data = &sdhci_arasan_data,
|
||||
},
|
||||
{
|
||||
.compatible = "arasan,sdhci-4.9a",
|
||||
.data = &sdhci_arasan_data,
|
||||
},
|
||||
{
|
||||
.compatible = "xlnx,zynqmp-8.9a",
|
||||
.data = &sdhci_arasan_zynqmp_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||
|
||||
/**
|
||||
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @parent_rate: The parent rate (should be rate of clk_xin).
|
||||
*
|
||||
* Return the current actual rate of the SD card clock. This can be used
|
||||
* to communicate with out PHY.
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @parent_rate The parent rate (should be rate of clk_xin).
|
||||
* Returns the card clock rate.
|
||||
* Return: The card clock rate.
|
||||
*/
|
||||
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
||||
|
@ -589,16 +558,16 @@ static const struct clk_ops arasan_sdcardclk_ops = {
|
|||
/**
|
||||
* sdhci_arasan_sampleclk_recalc_rate - Return the sampling clock rate
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @parent_rate: The parent rate (should be rate of clk_xin).
|
||||
*
|
||||
* Return the current actual rate of the sampling clock. This can be used
|
||||
* to communicate with out PHY.
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @parent_rate The parent rate (should be rate of clk_xin).
|
||||
* Returns the sample clock rate.
|
||||
* Return: The sample clock rate.
|
||||
*/
|
||||
static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
||||
|
@ -616,14 +585,14 @@ static const struct clk_ops arasan_sampleclk_ops = {
|
|||
/**
|
||||
* sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees: The clock phase shift between 0 - 359.
|
||||
*
|
||||
* Set the SD Output Clock Tap Delays for Output path
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees The clock phase shift between 0 - 359.
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
||||
|
@ -688,14 +657,14 @@ static const struct clk_ops zynqmp_sdcardclk_ops = {
|
|||
/**
|
||||
* sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees: The clock phase shift between 0 - 359.
|
||||
*
|
||||
* Set the SD Input Clock Tap Delays for Input path
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees The clock phase shift between 0 - 359.
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
||||
|
@ -757,6 +726,152 @@ static const struct clk_ops zynqmp_sampleclk_ops = {
|
|||
.set_phase = sdhci_zynqmp_sampleclk_set_phase,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees: The clock phase shift between 0 - 359.
|
||||
*
|
||||
* Set the SD Output Clock Tap Delays for Output path
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
|
||||
/*
|
||||
* This is applicable for SDHCI_SPEC_300 and above
|
||||
* Versal does not set phase for <=25MHz clock.
|
||||
* If degrees is zero, no need to do anything.
|
||||
*/
|
||||
if (host->version < SDHCI_SPEC_300 ||
|
||||
host->timing == MMC_TIMING_LEGACY ||
|
||||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
|
||||
return 0;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_SD_HS:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
/* For 50MHz clock, 30 Taps are available */
|
||||
tap_max = 30;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
/* For 100MHz clock, 15 Taps are available */
|
||||
tap_max = 15;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
/* For 200MHz clock, 8 Taps are available */
|
||||
tap_max = 8;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
if (tap_delay) {
|
||||
u32 regval;
|
||||
|
||||
regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
||||
regval |= SDHCI_OTAPDLY_ENABLE;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
||||
regval |= tap_delay;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops versal_sdcardclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
|
||||
.set_phase = sdhci_versal_sdcardclk_set_phase,
|
||||
};
|
||||
|
||||
/**
|
||||
* sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
|
||||
*
|
||||
* @hw: Pointer to the hardware clock structure.
|
||||
* @degrees: The clock phase shift between 0 - 359.
|
||||
*
|
||||
* Set the SD Input Clock Tap Delays for Input path
|
||||
*
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
||||
{
|
||||
struct sdhci_arasan_clk_data *clk_data =
|
||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
||||
struct sdhci_arasan_data *sdhci_arasan =
|
||||
container_of(clk_data, struct sdhci_arasan_data, clk_data);
|
||||
struct sdhci_host *host = sdhci_arasan->host;
|
||||
u8 tap_delay, tap_max = 0;
|
||||
|
||||
/*
|
||||
* This is applicable for SDHCI_SPEC_300 and above
|
||||
* Versal does not set phase for <=25MHz clock.
|
||||
* If degrees is zero, no need to do anything.
|
||||
*/
|
||||
if (host->version < SDHCI_SPEC_300 ||
|
||||
host->timing == MMC_TIMING_LEGACY ||
|
||||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
|
||||
return 0;
|
||||
|
||||
switch (host->timing) {
|
||||
case MMC_TIMING_MMC_HS:
|
||||
case MMC_TIMING_SD_HS:
|
||||
case MMC_TIMING_UHS_SDR25:
|
||||
case MMC_TIMING_UHS_DDR50:
|
||||
case MMC_TIMING_MMC_DDR52:
|
||||
/* For 50MHz clock, 120 Taps are available */
|
||||
tap_max = 120;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR50:
|
||||
/* For 100MHz clock, 60 Taps are available */
|
||||
tap_max = 60;
|
||||
break;
|
||||
case MMC_TIMING_UHS_SDR104:
|
||||
case MMC_TIMING_MMC_HS200:
|
||||
/* For 200MHz clock, 30 Taps are available */
|
||||
tap_max = 30;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tap_delay = (degrees * tap_max) / 360;
|
||||
|
||||
/* Set the Clock Phase */
|
||||
if (tap_delay) {
|
||||
u32 regval;
|
||||
|
||||
regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
||||
regval |= SDHCI_ITAPDLY_CHGWIN;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
||||
regval |= SDHCI_ITAPDLY_ENABLE;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
||||
regval |= tap_delay;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
||||
regval &= ~SDHCI_ITAPDLY_CHGWIN;
|
||||
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops versal_sampleclk_ops = {
|
||||
.recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
|
||||
.set_phase = sdhci_versal_sampleclk_set_phase,
|
||||
};
|
||||
|
||||
static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
@ -804,6 +919,9 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
/**
|
||||
* sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
* @value: The value to write
|
||||
*
|
||||
* The corecfg_clockmultiplier is supposed to contain clock multiplier
|
||||
* value of programmable clock generator.
|
||||
*
|
||||
|
@ -815,8 +933,6 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
* - The value of corecfg_clockmultiplier should sync with that of corresponding
|
||||
* value reading from sdhci_capability_register. So this function is called
|
||||
* once at probe time and never called again.
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
*/
|
||||
static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
||||
u32 value)
|
||||
|
@ -843,6 +959,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
|||
/**
|
||||
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
*
|
||||
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
|
||||
* function can be used to make that happen.
|
||||
*
|
||||
|
@ -854,8 +972,6 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
|||
* - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
|
||||
* to achieve lower clock rates. That means that this function is called once
|
||||
* at probe time and never called again.
|
||||
*
|
||||
* @host: The sdhci_host
|
||||
*/
|
||||
static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
|
||||
{
|
||||
|
@ -919,10 +1035,10 @@ static void arasan_dt_read_clk_phase(struct device *dev,
|
|||
/**
|
||||
* arasan_dt_parse_clk_phases - Read Clock Delay values from DT
|
||||
*
|
||||
* Called at initialization to parse the values of Clock Delays.
|
||||
*
|
||||
* @dev: Pointer to our struct device.
|
||||
* @clk_data: Pointer to the Clock Data structure
|
||||
*
|
||||
* Called at initialization to parse the values of Clock Delays.
|
||||
*/
|
||||
static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||
struct sdhci_arasan_clk_data *clk_data)
|
||||
|
@ -954,6 +1070,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) {
|
||||
iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE;
|
||||
oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE;
|
||||
|
||||
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
|
||||
clk_data->clk_phase_in[i] = iclk_phase[i];
|
||||
clk_data->clk_phase_out[i] = oclk_phase[i];
|
||||
}
|
||||
}
|
||||
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
|
||||
"clk-phase-legacy");
|
||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
|
||||
|
@ -978,17 +1104,191 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
|
|||
"clk-phase-mmc-hs400");
|
||||
}
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_clk_ops arasan_clk_ops = {
|
||||
.sdcardclk_ops = &arasan_sdcardclk_ops,
|
||||
.sampleclk_ops = &arasan_sampleclk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
|
||||
.pdata = &sdhci_arasan_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
|
||||
.ops = &sdhci_arasan_cqe_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED |
|
||||
SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE |
|
||||
SDHCI_QUIRK_32BIT_ADMA_SIZE,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_keembay_sd_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED |
|
||||
SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE |
|
||||
SDHCI_QUIRK_32BIT_ADMA_SIZE,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC |
|
||||
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_keembay_sdio_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
|
||||
SDHCI_QUIRK_NO_LED |
|
||||
SDHCI_QUIRK_32BIT_DMA_ADDR |
|
||||
SDHCI_QUIRK_32BIT_DMA_SIZE |
|
||||
SDHCI_QUIRK_32BIT_ADMA_SIZE,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_HOST_OFF_CARD_ON |
|
||||
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
|
||||
.soc_ctl_map = &rk3399_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
|
||||
.soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
|
||||
.soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
|
||||
.pdata = &sdhci_arasan_cqe_pdata,
|
||||
.clk_ops = &arasan_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
|
||||
.ops = &sdhci_arasan_ops,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
|
||||
SDHCI_QUIRK2_STOP_WITH_TC,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = {
|
||||
.sdcardclk_ops = &zynqmp_sdcardclk_ops,
|
||||
.sampleclk_ops = &zynqmp_sampleclk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
|
||||
.pdata = &sdhci_arasan_zynqmp_pdata,
|
||||
.clk_ops = &zynqmp_clk_ops,
|
||||
};
|
||||
|
||||
static const struct sdhci_arasan_clk_ops versal_clk_ops = {
|
||||
.sdcardclk_ops = &versal_sdcardclk_ops,
|
||||
.sampleclk_ops = &versal_sampleclk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data sdhci_arasan_versal_data = {
|
||||
.pdata = &sdhci_arasan_zynqmp_pdata,
|
||||
.clk_ops = &versal_clk_ops,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_keembay_emmc_data = {
|
||||
.soc_ctl_map = &intel_keembay_soc_ctl_map,
|
||||
.pdata = &sdhci_keembay_emmc_pdata,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_keembay_sd_data = {
|
||||
.soc_ctl_map = &intel_keembay_soc_ctl_map,
|
||||
.pdata = &sdhci_keembay_sd_pdata,
|
||||
};
|
||||
|
||||
static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
|
||||
.soc_ctl_map = &intel_keembay_soc_ctl_map,
|
||||
.pdata = &sdhci_keembay_sdio_pdata,
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_arasan_of_match[] = {
|
||||
/* SoC-specific compatible strings w/ soc_ctl_map */
|
||||
{
|
||||
.compatible = "rockchip,rk3399-sdhci-5.1",
|
||||
.data = &sdhci_arasan_rk3399_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,lgm-sdhci-5.1-emmc",
|
||||
.data = &intel_lgm_emmc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,lgm-sdhci-5.1-sdxc",
|
||||
.data = &intel_lgm_sdxc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,keembay-sdhci-5.1-emmc",
|
||||
.data = &intel_keembay_emmc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,keembay-sdhci-5.1-sd",
|
||||
.data = &intel_keembay_sd_data,
|
||||
},
|
||||
{
|
||||
.compatible = "intel,keembay-sdhci-5.1-sdio",
|
||||
.data = &intel_keembay_sdio_data,
|
||||
},
|
||||
/* Generic compatible below here */
|
||||
{
|
||||
.compatible = "arasan,sdhci-8.9a",
|
||||
.data = &sdhci_arasan_generic_data,
|
||||
},
|
||||
{
|
||||
.compatible = "arasan,sdhci-5.1",
|
||||
.data = &sdhci_arasan_generic_data,
|
||||
},
|
||||
{
|
||||
.compatible = "arasan,sdhci-4.9a",
|
||||
.data = &sdhci_arasan_generic_data,
|
||||
},
|
||||
{
|
||||
.compatible = "xlnx,zynqmp-8.9a",
|
||||
.data = &sdhci_arasan_zynqmp_data,
|
||||
},
|
||||
{
|
||||
.compatible = "xlnx,versal-8.9a",
|
||||
.data = &sdhci_arasan_versal_data,
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
|
||||
|
||||
/**
|
||||
* sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
*
|
||||
* Some PHY devices need to know what the actual card clock is. In order for
|
||||
* them to find out, we'll provide a clock through the common clock framework
|
||||
* for them to query.
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
* Returns 0 on success and error value on error
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int
|
||||
sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
|
||||
|
@ -1012,10 +1312,7 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
sdcardclk_init.parent_names = &parent_clk_name;
|
||||
sdcardclk_init.num_parents = 1;
|
||||
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
||||
sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
|
||||
else
|
||||
sdcardclk_init.ops = &arasan_sdcardclk_ops;
|
||||
sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops;
|
||||
|
||||
clk_data->sdcardclk_hw.init = &sdcardclk_init;
|
||||
clk_data->sdcardclk =
|
||||
|
@ -1033,14 +1330,15 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
/**
|
||||
* sdhci_arasan_register_sampleclk - Register the sampleclk for a PHY to use
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
*
|
||||
* Some PHY devices need to know what the actual card clock is. In order for
|
||||
* them to find out, we'll provide a clock through the common clock framework
|
||||
* for them to query.
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
* Returns 0 on success and error value on error
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int
|
||||
sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
|
||||
|
@ -1064,10 +1362,7 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
sampleclk_init.parent_names = &parent_clk_name;
|
||||
sampleclk_init.num_parents = 1;
|
||||
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
||||
sampleclk_init.ops = &zynqmp_sampleclk_ops;
|
||||
else
|
||||
sampleclk_init.ops = &arasan_sampleclk_ops;
|
||||
sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops;
|
||||
|
||||
clk_data->sampleclk_hw.init = &sampleclk_init;
|
||||
clk_data->sampleclk =
|
||||
|
@ -1085,10 +1380,10 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
|
|||
/**
|
||||
* sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
|
||||
*
|
||||
* @dev: Pointer to our struct device.
|
||||
*
|
||||
* Should be called any time we're exiting and sdhci_arasan_register_sdclk()
|
||||
* returned success.
|
||||
*
|
||||
* @dev: Pointer to our struct device.
|
||||
*/
|
||||
static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
||||
{
|
||||
|
@ -1100,9 +1395,47 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
|||
of_clk_del_provider(dev->of_node);
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
|
||||
*
|
||||
* This should be set based on the System Address Bus.
|
||||
* 0: the Core supports only 32-bit System Address Bus.
|
||||
* 1: the Core supports 64-bit System Address Bus.
|
||||
*
|
||||
* NOTES:
|
||||
* - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
|
||||
* Keem Bay does not support 64-bit access.
|
||||
*
|
||||
* @host The sdhci_host
|
||||
*/
|
||||
static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
|
||||
sdhci_arasan->soc_ctl_map;
|
||||
|
||||
/* Having a map is optional */
|
||||
if (!soc_ctl_map)
|
||||
return;
|
||||
|
||||
/* If we have a map, we expect to have a syscon */
|
||||
if (!sdhci_arasan->soc_ctl_base) {
|
||||
pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
|
||||
mmc_hostname(host->mmc));
|
||||
return;
|
||||
}
|
||||
|
||||
sdhci_arasan_syscon_write(host, &soc_ctl_map->support64b, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* sdhci_arasan_register_sdclk - Register the sdcardclk for a PHY to use
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
*
|
||||
* Some PHY devices need to know what the actual card clock is. In order for
|
||||
* them to find out, we'll provide a clock through the common clock framework
|
||||
* for them to query.
|
||||
|
@ -1115,10 +1448,7 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
|||
* to create nice clean device tree bindings and later (if needed) we can try
|
||||
* re-architecting SDHCI if we see some benefit to it.
|
||||
*
|
||||
* @sdhci_arasan: Our private data structure.
|
||||
* @clk_xin: Pointer to the functional clock
|
||||
* @dev: Pointer to our struct device.
|
||||
* Returns 0 on success and error value on error
|
||||
* Return: 0 on success and error value on error
|
||||
*/
|
||||
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
|
||||
struct clk *clk_xin,
|
||||
|
@ -1215,6 +1545,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||
sdhci_arasan->host = host;
|
||||
|
||||
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
|
||||
sdhci_arasan->clk_ops = data->clk_ops;
|
||||
|
||||
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
|
||||
if (node) {
|
||||
|
@ -1270,6 +1601,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||
"rockchip,rk3399-sdhci-5.1"))
|
||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
||||
|
||||
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
|
||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
|
||||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
|
||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
||||
sdhci_arasan_update_support64b(host, 0x0);
|
||||
|
||||
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
}
|
||||
|
||||
sdhci_arasan_update_baseclkfreq(host);
|
||||
|
||||
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
|
@ -120,9 +121,12 @@ static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
|
|||
|| mmc_gpio_get_cd(host->mmc) >= 0)
|
||||
sdhci_at91_set_force_card_detect(host);
|
||||
|
||||
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL))
|
||||
sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
|
||||
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) {
|
||||
u32 calcr = sdhci_readl(host, SDMMC_CALCR);
|
||||
|
||||
sdhci_writel(host, calcr | SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
|
||||
SDMMC_CALCR);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
|
||||
|
@ -179,9 +183,9 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
|
|||
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;
|
||||
caps0 |= FIELD_PREP(SDHCI_CLOCK_V3_BASE_MASK, clk_base);
|
||||
caps1 &= ~SDHCI_CLOCK_MUL_MASK;
|
||||
caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK;
|
||||
caps1 |= FIELD_PREP(SDHCI_CLOCK_MUL_MASK, clk_mul);
|
||||
/* Set capabilities in r/w mode. */
|
||||
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
|
||||
writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
#include "sdhci-pltfm.h"
|
||||
|
||||
/* DWCMSHC specific Mode Select value */
|
||||
#define DWCMSHC_CTRL_HS400 0x7
|
||||
|
||||
#define BOUNDARY_OK(addr, len) \
|
||||
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
||||
|
||||
|
@ -46,10 +49,36 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
|
|||
sdhci_adma_write_desc(host, desc, addr, len, cmd);
|
||||
}
|
||||
|
||||
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
|
||||
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_UHS_SDR25) ||
|
||||
(timing == MMC_TIMING_MMC_HS))
|
||||
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 |= DWCMSHC_CTRL_HS400;
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.reset = sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
|
@ -134,6 +163,48 @@ static int dwcmshc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int dwcmshc_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
if (!IS_ERR(priv->bus_clk))
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dwcmshc_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!IS_ERR(priv->bus_clk)) {
|
||||
ret = clk_prepare_enable(priv->bus_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sdhci_resume_host(host);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
|
||||
|
||||
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
{ .compatible = "snps,dwcmshc-sdhci" },
|
||||
{}
|
||||
|
@ -144,6 +215,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
|
|||
.driver = {
|
||||
.name = "sdhci-dwcmshc",
|
||||
.of_match_table = sdhci_dwcmshc_dt_ids,
|
||||
.pm = &dwcmshc_pmops,
|
||||
},
|
||||
.probe = dwcmshc_probe,
|
||||
.remove = dwcmshc_remove,
|
||||
|
|
|
@ -1135,6 +1135,40 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
static void esdhc_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* There are specific registers setting for HS400 mode.
|
||||
* Clean all of them if controller is in HS400 mode to
|
||||
* exit HS400 mode before re-setting any speed mode.
|
||||
*/
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
if (val & ESDHC_HS400_MODE) {
|
||||
val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
|
||||
val &= ~ESDHC_FLW_CTL_BG;
|
||||
sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_SDCLKCTL);
|
||||
val &= ~ESDHC_CMD_CLK_CTL;
|
||||
sdhci_writel(host, val, ESDHC_SDCLKCTL);
|
||||
|
||||
esdhc_clock_enable(host, false);
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val &= ~ESDHC_HS400_MODE;
|
||||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
esdhc_clock_enable(host, true);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_DLLCFG0);
|
||||
val &= ~(ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL);
|
||||
sdhci_writel(host, val, ESDHC_DLLCFG0);
|
||||
|
||||
val = sdhci_readl(host, ESDHC_TBCTL);
|
||||
val &= ~ESDHC_HS400_WNDW_ADJUST;
|
||||
sdhci_writel(host, val, ESDHC_TBCTL);
|
||||
|
||||
esdhc_tuning_block_enable(host, false);
|
||||
}
|
||||
|
||||
if (timing == MMC_TIMING_MMC_HS400)
|
||||
esdhc_tuning_block_enable(host, true);
|
||||
else
|
||||
|
|
|
@ -249,12 +249,8 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
|
|||
static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
slot->host->caps =
|
||||
((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
|
||||
& SDHCI_TIMEOUT_CLK_MASK) |
|
||||
|
||||
((0x21 << SDHCI_CLOCK_BASE_SHIFT)
|
||||
& SDHCI_CLOCK_BASE_MASK) |
|
||||
|
||||
FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
|
||||
FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
|
||||
SDHCI_TIMEOUT_CLK_UNIT |
|
||||
SDHCI_CAN_VDD_330 |
|
||||
SDHCI_CAN_DO_HISPD |
|
||||
|
@ -1749,6 +1745,7 @@ static const struct pci_device_id pci_ids[] = {
|
|||
SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
|
||||
SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
|
||||
SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
|
||||
SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e),
|
||||
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
|
||||
/* Generic SD host controller */
|
||||
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
|
||||
|
|
|
@ -63,6 +63,19 @@
|
|||
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
|
||||
#define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1
|
||||
|
||||
#define SDHCI_GLI_9763E_CTRL_HS400 0x7
|
||||
|
||||
#define SDHCI_GLI_9763E_HS400_ES_REG 0x52C
|
||||
#define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8)
|
||||
|
||||
#define PCIE_GLI_9763E_VHS 0x884
|
||||
#define GLI_9763E_VHS_REV GENMASK(19, 16)
|
||||
#define GLI_9763E_VHS_REV_R 0x0
|
||||
#define GLI_9763E_VHS_REV_M 0x1
|
||||
#define GLI_9763E_VHS_REV_W 0x2
|
||||
#define PCIE_GLI_9763E_SCR 0x8E0
|
||||
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
||||
|
||||
#define GLI_MAX_TUNING_LOOP 40
|
||||
|
||||
/* Genesys Logic chipset */
|
||||
|
@ -351,6 +364,81 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
|
||||
struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
u32 val;
|
||||
|
||||
val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG);
|
||||
if (ios->enhanced_strobe)
|
||||
val |= SDHCI_GLI_9763E_HS400_ES_BIT;
|
||||
else
|
||||
val &= ~SDHCI_GLI_9763E_HS400_ES_BIT;
|
||||
|
||||
sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG);
|
||||
}
|
||||
|
||||
static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
|
||||
if (timing == MMC_TIMING_MMC_HS200)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
|
||||
else if (timing == MMC_TIMING_MMC_HS)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
|
||||
else if (timing == MMC_TIMING_MMC_DDR52)
|
||||
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
|
||||
else if (timing == MMC_TIMING_MMC_HS400)
|
||||
ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400;
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct pci_dev *pdev = slot->chip->pdev;
|
||||
u32 value;
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
||||
value &= ~GLI_9763E_VHS_REV;
|
||||
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value);
|
||||
value |= GLI_9763E_SCR_AXI_REQ;
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value);
|
||||
|
||||
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
|
||||
value &= ~GLI_9763E_VHS_REV;
|
||||
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
|
||||
}
|
||||
|
||||
static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct sdhci_host *host = slot->host;
|
||||
|
||||
host->mmc->caps |= MMC_CAP_8_BIT_DATA |
|
||||
MMC_CAP_1_8V_DDR |
|
||||
MMC_CAP_NONREMOVABLE;
|
||||
host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR |
|
||||
MMC_CAP2_HS400_1_8V |
|
||||
MMC_CAP2_HS400_ES |
|
||||
MMC_CAP2_NO_SDIO |
|
||||
MMC_CAP2_NO_SD;
|
||||
gli_pcie_enable_msi(slot);
|
||||
host->mmc_host_ops.hs400_enhanced_strobe =
|
||||
gl9763e_hs400_enhanced_strobe;
|
||||
gli_set_gl9763e(slot);
|
||||
sdhci_enable_v4_mode(host);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_gl9755_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
|
@ -390,3 +478,21 @@ const struct sdhci_pci_fixes sdhci_gl9750 = {
|
|||
.resume = sdhci_pci_gli_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_gl9763e_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.enable_dma = sdhci_pci_enable_dma,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.reset = sdhci_reset,
|
||||
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
|
||||
.voltage_switch = sdhci_gli_voltage_switch,
|
||||
};
|
||||
|
||||
const struct sdhci_pci_fixes sdhci_gl9763e = {
|
||||
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
|
||||
.probe_slot = gli_probe_slot_gl9763e,
|
||||
.ops = &sdhci_gl9763e_ops,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.resume = sdhci_pci_gli_resume,
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -494,7 +494,7 @@ static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk)
|
|||
}
|
||||
}
|
||||
|
||||
void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
{
|
||||
u16 clk;
|
||||
|
||||
|
@ -509,7 +509,7 @@ void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
sdhci_o2_enable_clk(host, clk);
|
||||
}
|
||||
|
||||
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
||||
static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
||||
{
|
||||
struct sdhci_pci_chip *chip;
|
||||
struct sdhci_host *host;
|
||||
|
@ -578,7 +578,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
||||
static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
int ret;
|
||||
u8 scratch;
|
||||
|
@ -783,7 +783,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
|
||||
static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
sdhci_pci_o2_probe(chip);
|
||||
return sdhci_pci_resume_host(chip);
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
#define PCI_DEVICE_ID_GLI_9755 0x9755
|
||||
#define PCI_DEVICE_ID_GLI_9750 0x9750
|
||||
#define PCI_DEVICE_ID_GLI_9763E 0xe763
|
||||
|
||||
/*
|
||||
* PCI device class and mask
|
||||
|
@ -195,5 +196,6 @@ extern const struct sdhci_pci_fixes sdhci_snps;
|
|||
extern const struct sdhci_pci_fixes sdhci_o2;
|
||||
extern const struct sdhci_pci_fixes sdhci_gl9750;
|
||||
extern const struct sdhci_pci_fixes sdhci_gl9755;
|
||||
extern const struct sdhci_pci_fixes sdhci_gl9763e;
|
||||
|
||||
#endif /* __SDHCI_PCI_H */
|
||||
|
|
|
@ -406,7 +406,8 @@ static struct sdhci_ops sdhci_sprd_ops = {
|
|||
.request_done = sdhci_sprd_request_done,
|
||||
};
|
||||
|
||||
static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
static void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
|
||||
|
@ -422,10 +423,23 @@ static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
|
||||
(host->flags & SDHCI_AUTO_CMD23))
|
||||
host->flags &= ~SDHCI_AUTO_CMD23;
|
||||
}
|
||||
|
||||
static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
sdhci_sprd_check_auto_cmd23(mmc, mrq);
|
||||
|
||||
sdhci_request(mmc, mrq);
|
||||
}
|
||||
|
||||
static int sdhci_sprd_request_atomic(struct mmc_host *mmc,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
sdhci_sprd_check_auto_cmd23(mmc, mrq);
|
||||
|
||||
return sdhci_request_atomic(mmc, mrq);
|
||||
}
|
||||
|
||||
static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
|
@ -434,7 +448,7 @@ static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
pr_err("%s: Switching signalling voltage failed\n",
|
||||
mmc_hostname(mmc));
|
||||
return ret;
|
||||
|
@ -556,11 +570,17 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
|
|||
sdhci_sprd_voltage_switch;
|
||||
|
||||
host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||
MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
||||
MMC_CAP_WAIT_WHILE_BUSY;
|
||||
|
||||
ret = mmc_of_parse(host->mmc);
|
||||
if (ret)
|
||||
goto pltfm_free;
|
||||
|
||||
if (!mmc_card_is_removable(host->mmc))
|
||||
host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic;
|
||||
else
|
||||
host->always_defer_done = true;
|
||||
|
||||
sprd_host = TO_SPRD_HOST(host);
|
||||
sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node);
|
||||
|
||||
|
@ -654,8 +674,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_cleanup_host;
|
||||
|
||||
host->always_defer_done = true;
|
||||
|
||||
ret = __sdhci_add_host(host);
|
||||
if (ret)
|
||||
goto err_cleanup_host;
|
||||
|
|
|
@ -604,6 +604,39 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
|
|||
if (err)
|
||||
autocal->pull_down_1v8 = 0;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-sdr104",
|
||||
&autocal->pull_up_sdr104);
|
||||
if (err)
|
||||
autocal->pull_up_sdr104 = autocal->pull_up_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-sdr104",
|
||||
&autocal->pull_down_sdr104);
|
||||
if (err)
|
||||
autocal->pull_down_sdr104 = autocal->pull_down_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-hs400",
|
||||
&autocal->pull_up_hs400);
|
||||
if (err)
|
||||
autocal->pull_up_hs400 = autocal->pull_up_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-hs400",
|
||||
&autocal->pull_down_hs400);
|
||||
if (err)
|
||||
autocal->pull_down_hs400 = autocal->pull_down_1v8;
|
||||
|
||||
/*
|
||||
* Different fail-safe drive strength values based on the signaling
|
||||
* voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls.
|
||||
* So, avoid reading below device tree properties for SoCs that don't
|
||||
* have NVQUIRK_NEEDS_PAD_CONTROL.
|
||||
*/
|
||||
if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
|
||||
return;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
|
||||
&autocal->pull_up_3v3_timeout);
|
||||
|
@ -647,30 +680,6 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
|
|||
mmc_hostname(host->mmc));
|
||||
autocal->pull_down_1v8_timeout = 0;
|
||||
}
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-sdr104",
|
||||
&autocal->pull_up_sdr104);
|
||||
if (err)
|
||||
autocal->pull_up_sdr104 = autocal->pull_up_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-sdr104",
|
||||
&autocal->pull_down_sdr104);
|
||||
if (err)
|
||||
autocal->pull_down_sdr104 = autocal->pull_down_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-up-offset-hs400",
|
||||
&autocal->pull_up_hs400);
|
||||
if (err)
|
||||
autocal->pull_up_hs400 = autocal->pull_up_1v8;
|
||||
|
||||
err = device_property_read_u32(host->mmc->parent,
|
||||
"nvidia,pad-autocal-pull-down-offset-hs400",
|
||||
&autocal->pull_down_hs400);
|
||||
if (err)
|
||||
autocal->pull_down_hs400 = autocal->pull_down_1v8;
|
||||
}
|
||||
|
||||
static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
|
|
|
@ -48,10 +48,10 @@
|
|||
static unsigned int debug_quirks = 0;
|
||||
static unsigned int debug_quirks2;
|
||||
|
||||
static void sdhci_finish_data(struct sdhci_host *);
|
||||
|
||||
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
|
||||
|
||||
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
|
||||
|
||||
void sdhci_dumpregs(struct sdhci_host *host)
|
||||
{
|
||||
SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
|
||||
|
@ -111,6 +111,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
|
|||
}
|
||||
}
|
||||
|
||||
if (host->ops->dump_vendor_regs)
|
||||
host->ops->dump_vendor_regs(host);
|
||||
|
||||
SDHCI_DUMP("============================================\n");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_dumpregs);
|
||||
|
@ -317,6 +320,7 @@ out:
|
|||
static void sdhci_init(struct sdhci_host *host, int soft)
|
||||
{
|
||||
struct mmc_host *mmc = host->mmc;
|
||||
unsigned long flags;
|
||||
|
||||
if (soft)
|
||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||
|
@ -326,7 +330,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
|
|||
if (host->v4_mode)
|
||||
sdhci_do_enable_v4_mode(host);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
sdhci_set_default_irqs(host);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
host->cqe_on = false;
|
||||
|
||||
|
@ -634,9 +640,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
|
|||
}
|
||||
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
|
||||
/* Copy the data to the bounce buffer */
|
||||
sg_copy_to_buffer(data->sg, data->sg_len,
|
||||
host->bounce_buffer,
|
||||
length);
|
||||
if (host->ops->copy_to_bounce_buffer) {
|
||||
host->ops->copy_to_bounce_buffer(host,
|
||||
data, length);
|
||||
} else {
|
||||
sg_copy_to_buffer(data->sg, data->sg_len,
|
||||
host->bounce_buffer, length);
|
||||
}
|
||||
}
|
||||
/* Switch ownership to the DMA */
|
||||
dma_sync_single_for_device(host->mmc->parent,
|
||||
|
@ -1350,13 +1360,25 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
|
|||
!mrq->cap_cmd_during_tfr;
|
||||
}
|
||||
|
||||
static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
|
||||
}
|
||||
|
||||
static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
|
||||
struct mmc_request *mrq)
|
||||
{
|
||||
return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
|
||||
}
|
||||
|
||||
static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
|
||||
struct mmc_command *cmd,
|
||||
u16 *mode)
|
||||
{
|
||||
bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) &&
|
||||
(cmd->opcode != SD_IO_RW_EXTENDED);
|
||||
bool use_cmd23 = cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
|
||||
bool use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq);
|
||||
u16 ctrl2;
|
||||
|
||||
/*
|
||||
|
@ -1416,7 +1438,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
|
|||
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
|
||||
mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
|
||||
sdhci_auto_cmd_select(host, cmd, &mode);
|
||||
if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23))
|
||||
if (sdhci_auto_cmd23(host, cmd->mrq))
|
||||
sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
|
||||
}
|
||||
|
||||
|
@ -1466,6 +1488,9 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
|||
if (host->data_cmd && host->data_cmd->mrq == mrq)
|
||||
host->data_cmd = NULL;
|
||||
|
||||
if (host->deferred_cmd && host->deferred_cmd->mrq == mrq)
|
||||
host->deferred_cmd = NULL;
|
||||
|
||||
if (host->data && host->data->mrq == mrq)
|
||||
host->data = NULL;
|
||||
|
||||
|
@ -1487,7 +1512,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
|
|||
queue_work(host->complete_wq, &host->complete_work);
|
||||
}
|
||||
|
||||
static void sdhci_finish_data(struct sdhci_host *host)
|
||||
static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
|
||||
{
|
||||
struct mmc_command *data_cmd = host->data_cmd;
|
||||
struct mmc_data *data = host->data;
|
||||
|
@ -1539,14 +1564,31 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||
} else {
|
||||
/* Avoid triggering warning in sdhci_send_command() */
|
||||
host->cmd = NULL;
|
||||
sdhci_send_command(host, data->stop);
|
||||
if (!sdhci_send_command(host, data->stop)) {
|
||||
if (sw_data_timeout) {
|
||||
/*
|
||||
* This is anyway a sw data timeout, so
|
||||
* give up now.
|
||||
*/
|
||||
data->stop->error = -EIO;
|
||||
__sdhci_finish_mrq(host, data->mrq);
|
||||
} else {
|
||||
WARN_ON(host->deferred_cmd);
|
||||
host->deferred_cmd = data->stop;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
__sdhci_finish_mrq(host, data->mrq);
|
||||
}
|
||||
}
|
||||
|
||||
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
static void sdhci_finish_data(struct sdhci_host *host)
|
||||
{
|
||||
__sdhci_finish_data(host, false);
|
||||
}
|
||||
|
||||
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
int flags;
|
||||
u32 mask;
|
||||
|
@ -1561,9 +1603,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||
cmd->flags |= MMC_RSP_BUSY;
|
||||
|
||||
/* Wait max 10 ms */
|
||||
timeout = 10;
|
||||
|
||||
mask = SDHCI_CMD_INHIBIT;
|
||||
if (sdhci_data_line_cmd(cmd))
|
||||
mask |= SDHCI_DATA_INHIBIT;
|
||||
|
@ -1573,18 +1612,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
|
||||
mask &= ~SDHCI_DATA_INHIBIT;
|
||||
|
||||
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
||||
if (timeout == 0) {
|
||||
pr_err("%s: Controller never released inhibit bit(s).\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_dumpregs(host);
|
||||
cmd->error = -EIO;
|
||||
sdhci_finish_mrq(host, cmd->mrq);
|
||||
return;
|
||||
}
|
||||
timeout--;
|
||||
mdelay(1);
|
||||
}
|
||||
if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
|
||||
return false;
|
||||
|
||||
host->cmd = cmd;
|
||||
host->data_timeout = 0;
|
||||
|
@ -1606,11 +1635,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
sdhci_set_transfer_mode(host, cmd);
|
||||
|
||||
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
||||
pr_err("%s: Unsupported response type!\n",
|
||||
mmc_hostname(host->mmc));
|
||||
cmd->error = -EINVAL;
|
||||
sdhci_finish_mrq(host, cmd->mrq);
|
||||
return;
|
||||
WARN_ONCE(1, "Unsupported response type!\n");
|
||||
/*
|
||||
* This does not happen in practice because 136-bit response
|
||||
* commands never have busy waiting, so rather than complicate
|
||||
* the error path, just remove busy waiting and continue.
|
||||
*/
|
||||
cmd->flags &= ~MMC_RSP_BUSY;
|
||||
}
|
||||
|
||||
if (!(cmd->flags & MMC_RSP_PRESENT))
|
||||
|
@ -1645,8 +1676,61 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||
sdhci_external_dma_pre_transfer(host, cmd);
|
||||
|
||||
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sdhci_present_error(struct sdhci_host *host,
|
||||
struct mmc_command *cmd, bool present)
|
||||
{
|
||||
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
||||
cmd->error = -ENOMEDIUM;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sdhci_send_command_retry(struct sdhci_host *host,
|
||||
struct mmc_command *cmd,
|
||||
unsigned long flags)
|
||||
__releases(host->lock)
|
||||
__acquires(host->lock)
|
||||
{
|
||||
struct mmc_command *deferred_cmd = host->deferred_cmd;
|
||||
int timeout = 10; /* Approx. 10 ms */
|
||||
bool present;
|
||||
|
||||
while (!sdhci_send_command(host, cmd)) {
|
||||
if (!timeout--) {
|
||||
pr_err("%s: Controller never released inhibit bit(s).\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_dumpregs(host);
|
||||
cmd->error = -EIO;
|
||||
return false;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
usleep_range(1000, 1250);
|
||||
|
||||
present = host->mmc->ops->get_cd(host->mmc);
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
/* A deferred command might disappear, handle that */
|
||||
if (cmd == deferred_cmd && cmd != host->deferred_cmd)
|
||||
return true;
|
||||
|
||||
if (sdhci_present_error(host, cmd, present))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmd == host->deferred_cmd)
|
||||
host->deferred_cmd = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_send_command);
|
||||
|
||||
static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
|
@ -1707,7 +1791,10 @@ static void sdhci_finish_command(struct sdhci_host *host)
|
|||
|
||||
/* Finished CMD23, now send actual command. */
|
||||
if (cmd == cmd->mrq->sbc) {
|
||||
sdhci_send_command(host, cmd->mrq->cmd);
|
||||
if (!sdhci_send_command(host, cmd->mrq->cmd)) {
|
||||
WARN_ON(host->deferred_cmd);
|
||||
host->deferred_cmd = cmd->mrq->cmd;
|
||||
}
|
||||
} else {
|
||||
|
||||
/* Processed actual command. */
|
||||
|
@ -2037,11 +2124,10 @@ EXPORT_SYMBOL_GPL(sdhci_set_power_and_bus_voltage);
|
|||
|
||||
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
int present;
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct mmc_command *cmd;
|
||||
unsigned long flags;
|
||||
|
||||
host = mmc_priv(mmc);
|
||||
bool present;
|
||||
|
||||
/* Firstly check card presence */
|
||||
present = mmc->ops->get_cd(mmc);
|
||||
|
@ -2050,20 +2136,58 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
sdhci_led_activate(host);
|
||||
|
||||
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
||||
mrq->cmd->error = -ENOMEDIUM;
|
||||
sdhci_finish_mrq(host, mrq);
|
||||
} else {
|
||||
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
|
||||
sdhci_send_command(host, mrq->sbc);
|
||||
else
|
||||
sdhci_send_command(host, mrq->cmd);
|
||||
}
|
||||
if (sdhci_present_error(host, mrq->cmd, present))
|
||||
goto out_finish;
|
||||
|
||||
cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
|
||||
|
||||
if (!sdhci_send_command_retry(host, cmd, flags))
|
||||
goto out_finish;
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
return;
|
||||
|
||||
out_finish:
|
||||
sdhci_finish_mrq(host, mrq);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_request);
|
||||
|
||||
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
{
|
||||
struct sdhci_host *host = mmc_priv(mmc);
|
||||
struct mmc_command *cmd;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
if (sdhci_present_error(host, mrq->cmd, true)) {
|
||||
sdhci_finish_mrq(host, mrq);
|
||||
goto out_finish;
|
||||
}
|
||||
|
||||
cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
|
||||
|
||||
/*
|
||||
* The HSQ may send a command in interrupt context without polling
|
||||
* the busy signaling, which means we should return BUSY if controller
|
||||
* has not released inhibit bits to allow HSQ trying to send request
|
||||
* again in non-atomic context. So we should not finish this request
|
||||
* here.
|
||||
*/
|
||||
if (!sdhci_send_command(host, cmd))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
sdhci_led_activate(host);
|
||||
|
||||
out_finish:
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sdhci_request_atomic);
|
||||
|
||||
void sdhci_set_bus_width(struct sdhci_host *host, int width)
|
||||
{
|
||||
u8 ctrl;
|
||||
|
@ -2411,7 +2535,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
|
||||
mmc_hostname(mmc));
|
||||
return -EIO;
|
||||
|
@ -2434,7 +2558,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
return -EINVAL;
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
|
||||
mmc_hostname(mmc));
|
||||
return -EIO;
|
||||
|
@ -2466,7 +2590,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||
return -EINVAL;
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
|
||||
mmc_hostname(mmc));
|
||||
return -EIO;
|
||||
|
@ -2600,7 +2724,11 @@ void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
|
|||
*/
|
||||
sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
|
||||
|
||||
sdhci_send_command(host, &cmd);
|
||||
if (!sdhci_send_command_retry(host, &cmd, flags)) {
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
host->tuning_done = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
host->cmd = NULL;
|
||||
|
||||
|
@ -3018,7 +3146,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
|
|||
|
||||
if (host->data) {
|
||||
host->data->error = -ETIMEDOUT;
|
||||
sdhci_finish_data(host);
|
||||
__sdhci_finish_data(host, true);
|
||||
queue_work(host->complete_wq, &host->complete_work);
|
||||
} else if (host->data_cmd) {
|
||||
host->data_cmd->error = -ETIMEDOUT;
|
||||
|
@ -3390,6 +3518,9 @@ cont:
|
|||
}
|
||||
}
|
||||
out:
|
||||
if (host->deferred_cmd)
|
||||
result = IRQ_WAKE_THREAD;
|
||||
|
||||
spin_unlock(&host->lock);
|
||||
|
||||
/* Process mrqs ready for immediate completion */
|
||||
|
@ -3415,6 +3546,7 @@ out:
|
|||
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct sdhci_host *host = dev_id;
|
||||
struct mmc_command *cmd;
|
||||
unsigned long flags;
|
||||
u32 isr;
|
||||
|
||||
|
@ -3422,8 +3554,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|||
;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
|
||||
isr = host->thread_isr;
|
||||
host->thread_isr = 0;
|
||||
|
||||
cmd = host->deferred_cmd;
|
||||
if (cmd && !sdhci_send_command_retry(host, cmd, flags))
|
||||
sdhci_finish_mrq(host, cmd->mrq);
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
||||
|
@ -4114,11 +4252,9 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
}
|
||||
|
||||
if (host->version >= SDHCI_SPEC_300)
|
||||
host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
|
||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
||||
host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
|
||||
else
|
||||
host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
|
||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
||||
host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps);
|
||||
|
||||
host->max_clk *= 1000000;
|
||||
if (host->max_clk == 0 || host->quirks &
|
||||
|
@ -4136,8 +4272,7 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
* In case of Host Controller v3.00, find out whether clock
|
||||
* multiplier is supported.
|
||||
*/
|
||||
host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
|
||||
SDHCI_CLOCK_MUL_SHIFT;
|
||||
host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1);
|
||||
|
||||
/*
|
||||
* In case the value in Clock Multiplier is 0, then programmable
|
||||
|
@ -4170,8 +4305,7 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
mmc->f_max = max_clk;
|
||||
|
||||
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
||||
host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
|
||||
SDHCI_TIMEOUT_CLK_SHIFT;
|
||||
host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
|
||||
|
||||
if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
|
||||
host->timeout_clk *= 1000;
|
||||
|
@ -4201,7 +4335,7 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
!host->ops->get_max_timeout_count)
|
||||
mmc->max_busy_timeout = 0;
|
||||
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
|
||||
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
|
||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||
|
||||
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
|
||||
|
@ -4323,8 +4457,8 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
||||
|
||||
/* Initial value for re-tuning timer count */
|
||||
host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
||||
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
||||
host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
|
||||
host->caps1);
|
||||
|
||||
/*
|
||||
* In case Re-tuning Timer is not disabled, the actual value of
|
||||
|
@ -4334,8 +4468,7 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
host->tuning_count = 1 << (host->tuning_count - 1);
|
||||
|
||||
/* Re-tuning mode supported by the Host Controller */
|
||||
host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
|
||||
SDHCI_RETUNING_MODE_SHIFT;
|
||||
host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
|
||||
|
||||
ocr_avail = 0;
|
||||
|
||||
|
@ -4357,35 +4490,32 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||
|
||||
curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
|
||||
max_current_caps =
|
||||
(curr << SDHCI_MAX_CURRENT_330_SHIFT) |
|
||||
(curr << SDHCI_MAX_CURRENT_300_SHIFT) |
|
||||
(curr << SDHCI_MAX_CURRENT_180_SHIFT);
|
||||
FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, curr) |
|
||||
FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, curr) |
|
||||
FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, curr);
|
||||
}
|
||||
}
|
||||
|
||||
if (host->caps & SDHCI_CAN_VDD_330) {
|
||||
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||
|
||||
mmc->max_current_330 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_330_MASK) >>
|
||||
SDHCI_MAX_CURRENT_330_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
|
||||
max_current_caps) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
}
|
||||
if (host->caps & SDHCI_CAN_VDD_300) {
|
||||
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
||||
|
||||
mmc->max_current_300 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_300_MASK) >>
|
||||
SDHCI_MAX_CURRENT_300_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
|
||||
max_current_caps) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
}
|
||||
if (host->caps & SDHCI_CAN_VDD_180) {
|
||||
ocr_avail |= MMC_VDD_165_195;
|
||||
|
||||
mmc->max_current_180 = ((max_current_caps &
|
||||
SDHCI_MAX_CURRENT_180_MASK) >>
|
||||
SDHCI_MAX_CURRENT_180_SHIFT) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
|
||||
max_current_caps) *
|
||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||
}
|
||||
|
||||
/* If OCR set by host, use it instead. */
|
||||
|
|
|
@ -200,12 +200,10 @@
|
|||
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
|
||||
|
||||
#define SDHCI_CAPABILITIES 0x40
|
||||
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
|
||||
#define SDHCI_TIMEOUT_CLK_SHIFT 0
|
||||
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
|
||||
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
||||
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
|
||||
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
|
||||
#define SDHCI_CLOCK_BASE_SHIFT 8
|
||||
#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
|
||||
#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
|
||||
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
||||
#define SDHCI_MAX_BLOCK_SHIFT 16
|
||||
#define SDHCI_CAN_DO_8BIT 0x00040000
|
||||
|
@ -220,32 +218,25 @@
|
|||
#define SDHCI_CAN_64BIT_V4 0x08000000
|
||||
#define SDHCI_CAN_64BIT 0x10000000
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
#define SDHCI_SUPPORT_SDR50 0x00000001
|
||||
#define SDHCI_SUPPORT_SDR104 0x00000002
|
||||
#define SDHCI_SUPPORT_DDR50 0x00000004
|
||||
#define SDHCI_DRIVER_TYPE_A 0x00000010
|
||||
#define SDHCI_DRIVER_TYPE_C 0x00000020
|
||||
#define SDHCI_DRIVER_TYPE_D 0x00000040
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
|
||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
|
||||
#define SDHCI_USE_SDR50_TUNING 0x00002000
|
||||
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
|
||||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
||||
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
|
||||
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
|
||||
#define SDHCI_CAN_DO_ADMA3 0x08000000
|
||||
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
||||
|
||||
#define SDHCI_CAPABILITIES_1 0x44
|
||||
|
||||
#define SDHCI_MAX_CURRENT 0x48
|
||||
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
|
||||
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
|
||||
#define SDHCI_MAX_CURRENT_330_SHIFT 0
|
||||
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
|
||||
#define SDHCI_MAX_CURRENT_300_SHIFT 8
|
||||
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
|
||||
#define SDHCI_MAX_CURRENT_180_SHIFT 16
|
||||
#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
|
||||
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
|
||||
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
|
||||
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
|
||||
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
|
||||
|
||||
/* 4C-4F reserved for more max current */
|
||||
|
@ -540,6 +531,7 @@ struct sdhci_host {
|
|||
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
|
||||
struct mmc_command *cmd; /* Current command */
|
||||
struct mmc_command *data_cmd; /* Current data command */
|
||||
struct mmc_command *deferred_cmd; /* Deferred command */
|
||||
struct mmc_data *data; /* Current data request */
|
||||
unsigned int data_early:1; /* Data finished before cmd */
|
||||
|
||||
|
@ -653,8 +645,12 @@ struct sdhci_ops {
|
|||
void (*voltage_switch)(struct sdhci_host *host);
|
||||
void (*adma_write_desc)(struct sdhci_host *host, void **desc,
|
||||
dma_addr_t addr, int len, unsigned int cmd);
|
||||
void (*copy_to_bounce_buffer)(struct sdhci_host *host,
|
||||
struct mmc_data *data,
|
||||
unsigned int length);
|
||||
void (*request_done)(struct sdhci_host *host,
|
||||
struct mmc_request *mrq);
|
||||
void (*dump_vendor_regs)(struct sdhci_host *host);
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
||||
|
@ -757,7 +753,6 @@ void sdhci_cleanup_host(struct sdhci_host *host);
|
|||
int __sdhci_add_host(struct sdhci_host *host);
|
||||
int sdhci_add_host(struct sdhci_host *host);
|
||||
void sdhci_remove_host(struct sdhci_host *host, int dead);
|
||||
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
|
||||
|
||||
static inline void sdhci_read_caps(struct sdhci_host *host)
|
||||
{
|
||||
|
@ -776,6 +771,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
|
|||
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
|
||||
unsigned short vdd);
|
||||
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
||||
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
|
||||
void sdhci_set_bus_width(struct sdhci_host *host, int width);
|
||||
void sdhci_reset(struct sdhci_host *host, u8 mask);
|
||||
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include <linux/io.h>
|
||||
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
|
||||
#define DRIVER_NAME "sdricoh_cs"
|
||||
|
||||
|
@ -57,10 +59,8 @@ static unsigned int switchlocked;
|
|||
#define STATUS_BUSY 0x40000000
|
||||
|
||||
/* timeouts */
|
||||
#define INIT_TIMEOUT 100
|
||||
#define CMD_TIMEOUT 100000
|
||||
#define TRANSFER_TIMEOUT 100000
|
||||
#define BUSY_TIMEOUT 32767
|
||||
#define SDRICOH_CMD_TIMEOUT_US 1000000
|
||||
#define SDRICOH_DATA_TIMEOUT_US 1000000
|
||||
|
||||
/* list of supported pcmcia devices */
|
||||
static const struct pcmcia_device_id pcmcia_ids[] = {
|
||||
|
@ -124,19 +124,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
|
|||
return value;
|
||||
}
|
||||
|
||||
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
|
||||
unsigned int timeout){
|
||||
unsigned int loop;
|
||||
static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status,
|
||||
unsigned int wanted)
|
||||
{
|
||||
sdricoh_writel(host, R2E4_STATUS_RESP, status);
|
||||
return status & wanted;
|
||||
}
|
||||
|
||||
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted)
|
||||
{
|
||||
int ret;
|
||||
unsigned int status = 0;
|
||||
struct device *dev = host->dev;
|
||||
for (loop = 0; loop < timeout; loop++) {
|
||||
status = sdricoh_readl(host, R21C_STATUS);
|
||||
sdricoh_writel(host, R2E4_STATUS_RESP, status);
|
||||
if (status & wanted)
|
||||
break;
|
||||
}
|
||||
|
||||
if (loop == timeout) {
|
||||
ret = read_poll_timeout(sdricoh_readl, status,
|
||||
sdricoh_status_ok(host, status, wanted),
|
||||
32, SDRICOH_DATA_TIMEOUT_US, false,
|
||||
host, R21C_STATUS);
|
||||
if (ret) {
|
||||
dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
@ -150,35 +155,46 @@ static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
|
|||
|
||||
}
|
||||
|
||||
static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
|
||||
unsigned int arg)
|
||||
static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd)
|
||||
{
|
||||
unsigned int status;
|
||||
int result = 0;
|
||||
unsigned int loop = 0;
|
||||
unsigned int status, timeout_us;
|
||||
int ret;
|
||||
unsigned char opcode = cmd->opcode;
|
||||
|
||||
/* reset status reg? */
|
||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||
|
||||
/* MMC_APP_CMDs need some special handling */
|
||||
if (host->app_cmd) {
|
||||
opcode |= 64;
|
||||
host->app_cmd = 0;
|
||||
} else if (opcode == MMC_APP_CMD)
|
||||
host->app_cmd = 1;
|
||||
|
||||
/* fill parameters */
|
||||
sdricoh_writel(host, R204_CMD_ARG, arg);
|
||||
sdricoh_writel(host, R204_CMD_ARG, cmd->arg);
|
||||
sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
|
||||
|
||||
/* wait for command completion */
|
||||
if (opcode) {
|
||||
for (loop = 0; loop < CMD_TIMEOUT; loop++) {
|
||||
status = sdricoh_readl(host, R21C_STATUS);
|
||||
sdricoh_writel(host, R2E4_STATUS_RESP, status);
|
||||
if (status & STATUS_CMD_FINISHED)
|
||||
break;
|
||||
}
|
||||
/* don't check for timeout in the loop it is not always
|
||||
reset correctly
|
||||
*/
|
||||
if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
|
||||
result = -ETIMEDOUT;
|
||||
if (!opcode)
|
||||
return 0;
|
||||
|
||||
}
|
||||
timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 :
|
||||
SDRICOH_CMD_TIMEOUT_US;
|
||||
|
||||
return result;
|
||||
ret = read_poll_timeout(sdricoh_readl, status,
|
||||
sdricoh_status_ok(host, status, STATUS_CMD_FINISHED),
|
||||
32, timeout_us, false,
|
||||
host, R21C_STATUS);
|
||||
|
||||
/*
|
||||
* Don't check for timeout status in the loop, as it's not always reset
|
||||
* correctly.
|
||||
*/
|
||||
if (ret || status & STATUS_CMD_TIMEOUT)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdricoh_reset(struct sdricoh_host *host)
|
||||
|
@ -207,8 +223,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
|
|||
u32 data = 0;
|
||||
/* wait until the data is available */
|
||||
if (read) {
|
||||
if (sdricoh_query_status(host, STATUS_READY_TO_READ,
|
||||
TRANSFER_TIMEOUT))
|
||||
if (sdricoh_query_status(host, STATUS_READY_TO_READ))
|
||||
return -ETIMEDOUT;
|
||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||
/* read data */
|
||||
|
@ -224,8 +239,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
|
||||
TRANSFER_TIMEOUT))
|
||||
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE))
|
||||
return -ETIMEDOUT;
|
||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||
/* write data */
|
||||
|
@ -251,28 +265,20 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
struct mmc_command *cmd = mrq->cmd;
|
||||
struct mmc_data *data = cmd->data;
|
||||
struct device *dev = host->dev;
|
||||
unsigned char opcode = cmd->opcode;
|
||||
int i;
|
||||
|
||||
dev_dbg(dev, "=============================\n");
|
||||
dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
|
||||
dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode);
|
||||
|
||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||
|
||||
/* MMC_APP_CMDs need some special handling */
|
||||
if (host->app_cmd) {
|
||||
opcode |= 64;
|
||||
host->app_cmd = 0;
|
||||
} else if (opcode == 55)
|
||||
host->app_cmd = 1;
|
||||
|
||||
/* read/write commands seem to require this */
|
||||
if (data) {
|
||||
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
|
||||
sdricoh_writel(host, R208_DATAIO, 0);
|
||||
}
|
||||
|
||||
cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
|
||||
cmd->error = sdricoh_mmc_cmd(host, cmd);
|
||||
|
||||
/* read response buffer */
|
||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
||||
|
@ -323,8 +329,7 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
|
||||
sdricoh_writel(host, R208_DATAIO, 1);
|
||||
|
||||
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
|
||||
TRANSFER_TIMEOUT)) {
|
||||
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) {
|
||||
dev_err(dev, "sdricoh_request: transfer end error\n");
|
||||
cmd->error = -EINVAL;
|
||||
}
|
||||
|
|
|
@ -951,9 +951,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* vqmmc regulator is available */
|
||||
if (!IS_ERR(mmc->supply.vqmmc))
|
||||
return mmc_regulator_set_vqmmc(mmc, ios);
|
||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
|
||||
if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
||||
|
@ -1390,7 +1394,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
|
|||
mmc->f_min = 400000;
|
||||
mmc->f_max = 52000000;
|
||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
|
||||
MMC_CAP_SDIO_IRQ;
|
||||
|
||||
/*
|
||||
* Some H5 devices do not have signal traces precise enough to
|
||||
|
|
|
@ -73,6 +73,8 @@ module_param(fixed_timeout, bool, 0644);
|
|||
|
||||
#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL
|
||||
|
||||
#define TIFM_MMCSD_REQ_TIMEOUT_MS 1000
|
||||
|
||||
enum {
|
||||
CMD_READY = 0x0001,
|
||||
FIFO_READY = 0x0002,
|
||||
|
@ -959,7 +961,12 @@ static int tifm_sd_probe(struct tifm_dev *sock)
|
|||
host = mmc_priv(mmc);
|
||||
tifm_set_drvdata(sock, mmc);
|
||||
host->dev = sock;
|
||||
host->timeout_jiffies = msecs_to_jiffies(1000);
|
||||
host->timeout_jiffies = msecs_to_jiffies(TIFM_MMCSD_REQ_TIMEOUT_MS);
|
||||
/*
|
||||
* We use a fixed request timeout of 1s, hence inform the core about it.
|
||||
* A future improvement should instead respect the cmd->busy_timeout.
|
||||
*/
|
||||
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
|
||||
|
||||
tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
|
||||
(unsigned long)host);
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -1128,7 +1127,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
|
||||
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
|
||||
mmc->caps2 |= pdata->capabilities2;
|
||||
mmc->max_segs = pdata->max_segs ? : 32;
|
||||
mmc->max_blk_size = TMIO_MAX_BLK_SIZE;
|
||||
|
@ -1192,7 +1191,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||
/* See if we also get DMA */
|
||||
tmio_mmc_request_dma(_host, pdata);
|
||||
|
||||
dev_pm_domain_start(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
||||
|
@ -1231,12 +1229,14 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
|
|||
cancel_work_sync(&host->done);
|
||||
cancel_delayed_work_sync(&host->delayed_reset_work);
|
||||
tmio_mmc_release_dma(host);
|
||||
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
|
||||
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
if (host->native_hotplug)
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tmio_mmc_host_remove);
|
||||
|
||||
|
|
|
@ -610,11 +610,6 @@ static int uniphier_sd_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
|
||||
dev_name(dev), host);
|
||||
if (ret)
|
||||
goto free_host;
|
||||
|
||||
if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP)
|
||||
host->dma_ops = &uniphier_sd_internal_dma_ops;
|
||||
else
|
||||
|
@ -642,8 +637,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto free_host;
|
||||
|
||||
ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
|
||||
dev_name(dev), host);
|
||||
if (ret)
|
||||
goto remove_host;
|
||||
|
||||
return 0;
|
||||
|
||||
remove_host:
|
||||
tmio_mmc_host_remove(host);
|
||||
free_host:
|
||||
tmio_mmc_host_free(host);
|
||||
|
||||
|
|
|
@ -136,6 +136,8 @@
|
|||
|
||||
#define USDHI6_MIN_DMA 64
|
||||
|
||||
#define USDHI6_REQ_TIMEOUT_MS 4000
|
||||
|
||||
enum usdhi6_wait_for {
|
||||
USDHI6_WAIT_FOR_REQUEST,
|
||||
USDHI6_WAIT_FOR_CMD,
|
||||
|
@ -1763,7 +1765,12 @@ static int usdhi6_probe(struct platform_device *pdev)
|
|||
host = mmc_priv(mmc);
|
||||
host->mmc = mmc;
|
||||
host->wait = USDHI6_WAIT_FOR_REQUEST;
|
||||
host->timeout = msecs_to_jiffies(4000);
|
||||
host->timeout = msecs_to_jiffies(USDHI6_REQ_TIMEOUT_MS);
|
||||
/*
|
||||
* We use a fixed timeout of 4s, hence inform the core about it. A
|
||||
* future improvement should instead respect the cmd->busy_timeout.
|
||||
*/
|
||||
mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS;
|
||||
|
||||
host->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (IS_ERR(host->pinctrl)) {
|
||||
|
|
|
@ -319,6 +319,8 @@ struct via_crdr_mmc_host {
|
|||
/* some devices need a very long delay for power to stabilize */
|
||||
#define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001
|
||||
|
||||
#define VIA_CMD_TIMEOUT_MS 1000
|
||||
|
||||
static const struct pci_device_id via_ids[] = {
|
||||
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
|
||||
|
@ -551,14 +553,17 @@ static void via_sdc_send_command(struct via_crdr_mmc_host *host,
|
|||
{
|
||||
void __iomem *addrbase;
|
||||
struct mmc_data *data;
|
||||
unsigned int timeout_ms;
|
||||
u32 cmdctrl = 0;
|
||||
|
||||
WARN_ON(host->cmd);
|
||||
|
||||
data = cmd->data;
|
||||
mod_timer(&host->timer, jiffies + HZ);
|
||||
host->cmd = cmd;
|
||||
|
||||
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : VIA_CMD_TIMEOUT_MS;
|
||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
|
||||
|
||||
/*Command index*/
|
||||
cmdctrl = cmd->opcode << 8;
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include <linux/pnp.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/mmc/host.h>
|
||||
#include <linux/mmc/mmc.h>
|
||||
#include <linux/mmc/sd.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
@ -770,22 +772,22 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
* interrupts.
|
||||
*/
|
||||
switch (cmd->opcode) {
|
||||
case 11:
|
||||
case 17:
|
||||
case 18:
|
||||
case 20:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 30:
|
||||
case 42:
|
||||
case 56:
|
||||
case SD_SWITCH_VOLTAGE:
|
||||
case MMC_READ_SINGLE_BLOCK:
|
||||
case MMC_READ_MULTIPLE_BLOCK:
|
||||
case MMC_WRITE_DAT_UNTIL_STOP:
|
||||
case MMC_WRITE_BLOCK:
|
||||
case MMC_WRITE_MULTIPLE_BLOCK:
|
||||
case MMC_PROGRAM_CID:
|
||||
case MMC_PROGRAM_CSD:
|
||||
case MMC_SEND_WRITE_PROT:
|
||||
case MMC_LOCK_UNLOCK:
|
||||
case MMC_GEN_CMD:
|
||||
break;
|
||||
|
||||
/* ACMDs. We don't keep track of state, so we just treat them
|
||||
* like any other command. */
|
||||
case 51:
|
||||
case SD_APP_SEND_SCR:
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -1083,10 +1083,10 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
|
|||
|
||||
mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
|
||||
|
||||
dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, device);
|
||||
dev_id_chiprev = FIELD_GET(QCA_MANUFACTURER_ID_REV_MASK, device);
|
||||
dev_id_base = (device & 0x0F00);
|
||||
dev_id_chiprev = (device & 0x00FF);
|
||||
switch (dev_id_base) {
|
||||
case QCA_MANUFACTURER_ID_AR6005_BASE:
|
||||
case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
|
||||
if (dev_id_chiprev < 4)
|
||||
mbox_info->ext_info[0].htc_ext_sz =
|
||||
ATH10K_HIF_MBOX0_EXT_WIDTH;
|
||||
|
@ -1097,7 +1097,7 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
|
|||
mbox_info->ext_info[0].htc_ext_sz =
|
||||
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
||||
break;
|
||||
case QCA_MANUFACTURER_ID_QCA9377_BASE:
|
||||
case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
|
||||
mbox_info->ext_info[0].htc_ext_sz =
|
||||
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
||||
break;
|
||||
|
@ -2185,19 +2185,16 @@ static int ath10k_sdio_probe(struct sdio_func *func,
|
|||
skb_queue_head_init(&ar_sdio->rx_head);
|
||||
INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
|
||||
|
||||
dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device);
|
||||
switch (dev_id_base) {
|
||||
case QCA_MANUFACTURER_ID_AR6005_BASE:
|
||||
case QCA_MANUFACTURER_ID_QCA9377_BASE:
|
||||
ar->dev_id = QCA9377_1_0_DEVICE_ID;
|
||||
break;
|
||||
default:
|
||||
dev_id_base = (id->device & 0x0F00);
|
||||
if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
|
||||
dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
|
||||
ret = -ENODEV;
|
||||
ath10k_err(ar, "unsupported device id %u (0x%x)\n",
|
||||
dev_id_base, id->device);
|
||||
goto err_free_wq;
|
||||
}
|
||||
|
||||
ar->dev_id = QCA9377_1_0_DEVICE_ID;
|
||||
ar->id.vendor = id->vendor;
|
||||
ar->id.device = id->device;
|
||||
|
||||
|
@ -2246,10 +2243,8 @@ static void ath10k_sdio_remove(struct sdio_func *func)
|
|||
}
|
||||
|
||||
static const struct sdio_device_id ath10k_sdio_devices[] = {
|
||||
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
|
||||
(QCA_SDIO_ID_AR6005_BASE | 0xA))},
|
||||
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
|
||||
(QCA_SDIO_ID_QCA9377_BASE | 0x1))},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -10,14 +10,6 @@
|
|||
|
||||
#define ATH10K_HIF_MBOX_BLOCK_SIZE 256
|
||||
|
||||
#define QCA_MANUFACTURER_ID_BASE GENMASK(11, 8)
|
||||
#define QCA_MANUFACTURER_ID_AR6005_BASE 0x5
|
||||
#define QCA_MANUFACTURER_ID_QCA9377_BASE 0x7
|
||||
#define QCA_SDIO_ID_AR6005_BASE 0x500
|
||||
#define QCA_SDIO_ID_QCA9377_BASE 0x700
|
||||
#define QCA_MANUFACTURER_ID_REV_MASK 0x00FF
|
||||
#define QCA_MANUFACTURER_CODE 0x271 /* Qualcomm/Atheros */
|
||||
|
||||
#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/
|
||||
|
||||
/* Mailbox address in SDIO address space */
|
||||
|
|
|
@ -35,12 +35,6 @@
|
|||
#define MAX_SCATTER_ENTRIES_PER_REQ 16
|
||||
#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
|
||||
|
||||
#define MANUFACTURER_ID_AR6003_BASE 0x300
|
||||
#define MANUFACTURER_ID_AR6004_BASE 0x400
|
||||
/* SDIO manufacturer ID and Codes */
|
||||
#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00
|
||||
#define MANUFACTURER_CODE 0x271 /* Atheros */
|
||||
|
||||
/* Mailbox address in SDIO address space */
|
||||
#define HIF_MBOX_BASE_ADDR 0x800
|
||||
#define HIF_MBOX_WIDTH 0x800
|
||||
|
|
|
@ -799,8 +799,7 @@ static int ath6kl_sdio_config(struct ath6kl *ar)
|
|||
|
||||
sdio_claim_host(func);
|
||||
|
||||
if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
|
||||
MANUFACTURER_ID_AR6003_BASE) {
|
||||
if (ar_sdio->id->device >= SDIO_DEVICE_ID_ATHEROS_AR6003_00) {
|
||||
/* enable 4-bit ASYNC interrupt on AR6003 or later */
|
||||
ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
|
||||
CCCR_SDIO_IRQ_MODE_REG,
|
||||
|
@ -1409,13 +1408,13 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
|
|||
}
|
||||
|
||||
static const struct sdio_device_id ath6kl_sdio_devices[] = {
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
|
||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x19))},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_00)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_01)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_00)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_01)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_02)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_18)},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_19)},
|
||||
{},
|
||||
};
|
||||
|
||||
|
|
|
@ -180,8 +180,8 @@ static void b43_sdio_remove(struct sdio_func *func)
|
|||
}
|
||||
|
||||
static const struct sdio_device_id b43_sdio_ids[] = {
|
||||
{ SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
|
||||
{ SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII) },
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_CGUYS, SDIO_DEVICE_ID_CGUYS_EW_CG1102GC) },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -970,9 +970,9 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
|
|||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012),
|
||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359),
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
|
||||
|
|
|
@ -4187,7 +4187,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
|
|||
bus->hostintmask, NULL);
|
||||
|
||||
switch (sdiod->func1->device) {
|
||||
case SDIO_DEVICE_ID_CYPRESS_4373:
|
||||
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
|
||||
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
||||
CY_4373_F2_WATERMARK);
|
||||
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
|
||||
|
@ -4201,7 +4201,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
|
|||
CY_4373_F2_WATERMARK |
|
||||
SBSDIO_MESBUSYCTRL_ENAB, &err);
|
||||
break;
|
||||
case SDIO_DEVICE_ID_CYPRESS_43012:
|
||||
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012:
|
||||
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
||||
CY_43012_F2_WATERMARK);
|
||||
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
|
||||
|
|
|
@ -65,7 +65,7 @@ static const struct sdio_device_id if_sdio_ids[] = {
|
|||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
|
||||
SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
|
||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
|
||||
SDIO_DEVICE_ID_MARVELL_8688WLAN) },
|
||||
SDIO_DEVICE_ID_MARVELL_8688_WLAN) },
|
||||
{ /* end: all zeroes */ },
|
||||
};
|
||||
|
||||
|
|
|
@ -480,45 +480,25 @@ static void mwifiex_sdio_coredump(struct device *dev)
|
|||
schedule_work(&card->work);
|
||||
}
|
||||
|
||||
/* Device ID for SD8786 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116)
|
||||
/* Device ID for SD8787 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
|
||||
/* Device ID for SD8797 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
|
||||
/* Device ID for SD8897 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
|
||||
/* Device ID for SD8887 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135)
|
||||
/* Device ID for SD8801 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139)
|
||||
/* Device ID for SD8977 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8977 (0x9145)
|
||||
/* Device ID for SD8987 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8987 (0x9149)
|
||||
/* Device ID for SD8997 */
|
||||
#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141)
|
||||
|
||||
|
||||
/* WLAN IDs */
|
||||
static const struct sdio_device_id mwifiex_ids[] = {
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786_WLAN),
|
||||
.driver_data = (unsigned long) &mwifiex_sdio_sd8786},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_WLAN),
|
||||
.driver_data = (unsigned long) &mwifiex_sdio_sd8787},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_WLAN),
|
||||
.driver_data = (unsigned long) &mwifiex_sdio_sd8797},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_WLAN),
|
||||
.driver_data = (unsigned long) &mwifiex_sdio_sd8897},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_WLAN),
|
||||
.driver_data = (unsigned long)&mwifiex_sdio_sd8887},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801_WLAN),
|
||||
.driver_data = (unsigned long)&mwifiex_sdio_sd8801},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN),
|
||||
.driver_data = (unsigned long)&mwifiex_sdio_sd8977},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN),
|
||||
.driver_data = (unsigned long)&mwifiex_sdio_sd8987},
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
|
||||
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN),
|
||||
.driver_data = (unsigned long)&mwifiex_sdio_sd8997},
|
||||
{},
|
||||
};
|
||||
|
|
|
@ -67,7 +67,6 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r)
|
|||
((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) |
|
||||
((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) |
|
||||
((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) |
|
||||
((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) |
|
||||
((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) |
|
||||
((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) |
|
||||
((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) |
|
||||
|
@ -411,6 +410,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
|
|||
struct gb_sdio_command_request request = {0};
|
||||
struct gb_sdio_command_response response;
|
||||
struct mmc_data *data = host->mrq->data;
|
||||
unsigned int timeout_ms;
|
||||
u8 cmd_flags;
|
||||
u8 cmd_type;
|
||||
int i;
|
||||
|
@ -469,9 +469,12 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
|
|||
request.data_blksz = cpu_to_le16(data->blksz);
|
||||
}
|
||||
|
||||
ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND,
|
||||
&request, sizeof(request), &response,
|
||||
sizeof(response));
|
||||
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||
GB_OPERATION_TIMEOUT_DEFAULT;
|
||||
|
||||
ret = gb_operation_sync_timeout(host->connection, GB_SDIO_TYPE_COMMAND,
|
||||
&request, sizeof(request), &response,
|
||||
sizeof(response), timeout_ms);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ struct memstick_host {
|
|||
int (*set_param)(struct memstick_host *host,
|
||||
enum memstick_param param,
|
||||
int value);
|
||||
unsigned long private[0] ____cacheline_aligned;
|
||||
unsigned long private[] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
struct memstick_driver {
|
||||
|
|
|
@ -48,6 +48,7 @@ struct mmc_ext_csd {
|
|||
u8 sec_feature_support;
|
||||
u8 rel_sectors;
|
||||
u8 rel_param;
|
||||
bool enhanced_rpmb_supported;
|
||||
u8 part_config;
|
||||
u8 cache_ctrl;
|
||||
u8 rst_n_function;
|
||||
|
|
|
@ -92,6 +92,9 @@ struct mmc_host_ops {
|
|||
int err);
|
||||
void (*pre_req)(struct mmc_host *host, struct mmc_request *req);
|
||||
void (*request)(struct mmc_host *host, struct mmc_request *req);
|
||||
/* Submit one request to host in atomic context. */
|
||||
int (*request_atomic)(struct mmc_host *host,
|
||||
struct mmc_request *req);
|
||||
|
||||
/*
|
||||
* Avoid calling the next three functions too often or in a "fast
|
||||
|
@ -318,7 +321,6 @@ struct mmc_host {
|
|||
#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */
|
||||
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
|
||||
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
|
||||
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
|
||||
#define MMC_CAP_3_3V_DDR (1 << 11) /* Host supports eMMC DDR 3.3V */
|
||||
#define MMC_CAP_1_8V_DDR (1 << 12) /* Host supports eMMC DDR 1.8V */
|
||||
#define MMC_CAP_1_2V_DDR (1 << 13) /* Host supports eMMC DDR 1.2V */
|
||||
|
|
|
@ -325,6 +325,7 @@ static inline bool mmc_ready_for_data(u32 status)
|
|||
*/
|
||||
|
||||
#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
|
||||
#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1<<4)
|
||||
|
||||
#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40)
|
||||
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)
|
||||
|
|
|
@ -24,27 +24,9 @@
|
|||
/*
|
||||
* Vendors and devices. Sort key: vendor first, device next.
|
||||
*/
|
||||
#define SDIO_VENDOR_ID_BROADCOM 0x02d0
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4339 0x4339
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43364 0xa9a4
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359
|
||||
#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
|
||||
#define SDIO_DEVICE_ID_CYPRESS_43012 43012
|
||||
#define SDIO_DEVICE_ID_CYPRESS_89359 0x4355
|
||||
|
||||
#define SDIO_VENDOR_ID_STE 0x0020
|
||||
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||
|
||||
#define SDIO_VENDOR_ID_INTEL 0x0089
|
||||
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
|
||||
|
@ -54,29 +36,89 @@
|
|||
#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406
|
||||
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
|
||||
|
||||
#define SDIO_VENDOR_ID_CGUYS 0x0092
|
||||
#define SDIO_DEVICE_ID_CGUYS_EW_CG1102GC 0x0004
|
||||
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
|
||||
#define SDIO_VENDOR_ID_ATHEROS 0x0271
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6003_00 0x0300
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6003_01 0x0301
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6004_00 0x0400
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6004_01 0x0401
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6004_02 0x0402
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6004_18 0x0418
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6004_19 0x0419
|
||||
#define SDIO_DEVICE_ID_ATHEROS_AR6005 0x050A
|
||||
#define SDIO_DEVICE_ID_ATHEROS_QCA9377 0x0701
|
||||
|
||||
#define SDIO_VENDOR_ID_BROADCOM 0x02d0
|
||||
#define SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII 0x044b
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4339 0x4339
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
|
||||
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 0x4355
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
|
||||
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359
|
||||
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 0x4373
|
||||
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 0xa804
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43364 0xa9a4
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
|
||||
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
|
||||
|
||||
#define SDIO_VENDOR_ID_MARVELL 0x02df
|
||||
#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
|
||||
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
|
||||
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
|
||||
#define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104
|
||||
#define SDIO_DEVICE_ID_MARVELL_8688_BT 0x9105
|
||||
#define SDIO_DEVICE_ID_MARVELL_8786_WLAN 0x9116
|
||||
#define SDIO_DEVICE_ID_MARVELL_8787_WLAN 0x9119
|
||||
#define SDIO_DEVICE_ID_MARVELL_8787_BT 0x911a
|
||||
#define SDIO_DEVICE_ID_MARVELL_8787_BT_AMP 0x911b
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
|
||||
#define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797_WLAN 0x9129
|
||||
#define SDIO_DEVICE_ID_MARVELL_8797_BT 0x912a
|
||||
#define SDIO_DEVICE_ID_MARVELL_8897_WLAN 0x912d
|
||||
#define SDIO_DEVICE_ID_MARVELL_8897_BT 0x912e
|
||||
#define SDIO_DEVICE_ID_MARVELL_8887_F0 0x9134
|
||||
#define SDIO_DEVICE_ID_MARVELL_8887_WLAN 0x9135
|
||||
#define SDIO_DEVICE_ID_MARVELL_8887_BT 0x9136
|
||||
#define SDIO_DEVICE_ID_MARVELL_8801_WLAN 0x9139
|
||||
#define SDIO_DEVICE_ID_MARVELL_8997_F0 0x9140
|
||||
#define SDIO_DEVICE_ID_MARVELL_8997_WLAN 0x9141
|
||||
#define SDIO_DEVICE_ID_MARVELL_8997_BT 0x9142
|
||||
#define SDIO_DEVICE_ID_MARVELL_8977_WLAN 0x9145
|
||||
#define SDIO_DEVICE_ID_MARVELL_8977_BT 0x9146
|
||||
#define SDIO_DEVICE_ID_MARVELL_8987_WLAN 0x9149
|
||||
#define SDIO_DEVICE_ID_MARVELL_8987_BT 0x914a
|
||||
|
||||
#define SDIO_VENDOR_ID_MEDIATEK 0x037a
|
||||
#define SDIO_DEVICE_ID_MEDIATEK_MT7663 0x7663
|
||||
#define SDIO_DEVICE_ID_MEDIATEK_MT7668 0x7668
|
||||
|
||||
#define SDIO_VENDOR_ID_SIANO 0x039a
|
||||
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
||||
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
|
||||
#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
|
||||
#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
|
||||
#define SDIO_DEVICE_ID_SIANO_MING 0x0302
|
||||
#define SDIO_DEVICE_ID_SIANO_PELE 0x0500
|
||||
#define SDIO_DEVICE_ID_SIANO_RIO 0x0600
|
||||
#define SDIO_DEVICE_ID_SIANO_DENVER_2160 0x0700
|
||||
#define SDIO_DEVICE_ID_SIANO_DENVER_1530 0x0800
|
||||
#define SDIO_DEVICE_ID_SIANO_NOVA_A0 0x1100
|
||||
#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
|
||||
|
||||
#define SDIO_VENDOR_ID_TI 0x0097
|
||||
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
|
||||
#define SDIO_VENDOR_ID_TI_WL1251 0x104c
|
||||
#define SDIO_DEVICE_ID_TI_WL1251 0x9066
|
||||
|
||||
#define SDIO_VENDOR_ID_STE 0x0020
|
||||
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||
|
||||
#endif /* LINUX_MMC_SDIO_IDS_H */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright 2010 Wolfram Sang <w.sang@pengutronix.de>
|
||||
* Copyright 2010 Wolfram Sang <kernel@pengutronix.de>
|
||||
*/
|
||||
|
||||
#ifndef __ASM_ARCH_IMX_ESDHC_H
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
|
||||
#define __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
|
||||
|
||||
enum cd_types {
|
||||
ESDHC_CD_NONE, /* no CD, neither controller nor gpio */
|
||||
ESDHC_CD_CONTROLLER, /* mmc controller internal CD */
|
||||
ESDHC_CD_PERMANENT, /* no CD, card permanently wired to host */
|
||||
};
|
||||
|
||||
struct mcf_esdhc_platform_data {
|
||||
int max_bus_width;
|
||||
int cd_type;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_PLATFORM_DATA_MCF_ESDHC_H__ */
|
|
@ -3,6 +3,7 @@
|
|||
#define LINUX_MMC_IOCTL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/major.h>
|
||||
|
||||
struct mmc_ioc_cmd {
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче