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:
commit
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
|
- "xlnx,zynqmp-8.9a": ZynqMP SDHCI 8.9a PHY
|
||||||
For this device it is strongly suggested to include clock-output-names and
|
For this device it is strongly suggested to include clock-output-names and
|
||||||
#clock-cells.
|
#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
|
- "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
|
||||||
Note: This binding has been deprecated and moved to [5].
|
Note: This binding has been deprecated and moved to [5].
|
||||||
- "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel LGM eMMC PHY
|
- "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.
|
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
|
- "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.
|
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
|
[5] Documentation/devicetree/bindings/mmc/sdhci-am654.txt
|
||||||
|
|
||||||
@ -104,6 +113,18 @@ Example:
|
|||||||
clk-phase-sd-hs = <63>, <72>;
|
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 {
|
emmc: sdhci@ec700000 {
|
||||||
compatible = "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1";
|
compatible = "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1";
|
||||||
reg = <0xec700000 0x300>;
|
reg = <0xec700000 0x300>;
|
||||||
@ -133,3 +154,39 @@ Example:
|
|||||||
phy-names = "phy_arasan";
|
phy-names = "phy_arasan";
|
||||||
arasan,soc-ctl-syscon = <&sysconf>;
|
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-r7s72100" for the MMCIF found in r7s72100 SoCs
|
||||||
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
|
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
|
||||||
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 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-r8a7743" for the MMCIF found in r8a7743 SoCs
|
||||||
- "renesas,mmcif-r8a7744" for the MMCIF found in r8a7744 SoCs
|
- "renesas,mmcif-r8a7744" for the MMCIF found in r8a7744 SoCs
|
||||||
- "renesas,mmcif-r8a7745" for the MMCIF found in r8a7745 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
|
- 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
|
2 or 3 individual interrupts (error, int, card detect). Below is the number
|
||||||
of interrupts for each SoC:
|
of interrupts for each SoC:
|
||||||
1: r8a73a4, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791, r8a7793,
|
1: r8a73a4, r8a7742, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791,
|
||||||
r8a7794
|
r8a7793, r8a7794
|
||||||
2: r8a7740, sh73a0
|
2: r8a7740, sh73a0
|
||||||
3: r7s72100
|
3: r7s72100
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ Required properties:
|
|||||||
"renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC
|
"renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC
|
||||||
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
|
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
|
||||||
"renesas,sdhi-r8a7740" - SDHI IP on R8A7740 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-r8a7743" - SDHI IP on R8A7743 SoC
|
||||||
"renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC
|
"renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC
|
||||||
"renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC
|
"renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC
|
||||||
|
@ -17,6 +17,7 @@ Required properties:
|
|||||||
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
|
||||||
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
|
||||||
"qcom,msm8996-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,sdm845-sdhci", "qcom,sdhci-msm-v5"
|
||||||
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
|
||||||
"qcom,sc7180-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)
|
"cal" - reference clock for RCLK delay calibration (optional)
|
||||||
"sleep" - sleep 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:
|
Example:
|
||||||
|
|
||||||
sdhc_1: sdhci@f9824900 {
|
sdhc_1: sdhci@f9824900 {
|
||||||
@ -63,6 +71,9 @@ Example:
|
|||||||
|
|
||||||
clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
|
clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
|
||||||
clock-names = "core", "iface";
|
clock-names = "core", "iface";
|
||||||
|
|
||||||
|
qcom,dll-config = <0x000f642c>;
|
||||||
|
qcom,ddr-config = <0x80040868>;
|
||||||
};
|
};
|
||||||
|
|
||||||
sdhc_2: sdhci@f98a4900 {
|
sdhc_2: sdhci@f98a4900 {
|
||||||
@ -80,4 +91,7 @@ Example:
|
|||||||
|
|
||||||
clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
|
clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
|
||||||
clock-names = "core", "iface";
|
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>;
|
|
||||||
};
|
|
102
Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml
Normal file
102
Documentation/devicetree/bindings/mmc/sdhci-pxa.yaml
Normal file
@ -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: Documentation/devicetree/bindings/crypto/fsl-sec4.txt
|
||||||
F: drivers/crypto/caam/
|
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
|
FREESCALE DIU FRAMEBUFFER DRIVER
|
||||||
M: Timur Tabi <timur@kernel.org>
|
M: Timur Tabi <timur@kernel.org>
|
||||||
L: linux-fbdev@vger.kernel.org
|
L: linux-fbdev@vger.kernel.org
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <asm/mcfqspi.h>
|
#include <asm/mcfqspi.h>
|
||||||
#include <linux/platform_data/edma.h>
|
#include <linux/platform_data/edma.h>
|
||||||
#include <linux/platform_data/dma-mcf-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.
|
* 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,
|
.platform_data = &mcf_edma_data,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* IS_ENABLED(CONFIG_MCF_EDMA) */
|
#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 = {
|
static struct platform_device *mcf_devices[] __initdata = {
|
||||||
&mcf_uart,
|
&mcf_uart,
|
||||||
#if IS_ENABLED(CONFIG_FEC)
|
#if IS_ENABLED(CONFIG_FEC)
|
||||||
@ -586,6 +613,9 @@ static struct platform_device *mcf_devices[] __initdata = {
|
|||||||
#if IS_ENABLED(CONFIG_MCF_EDMA)
|
#if IS_ENABLED(CONFIG_MCF_EDMA)
|
||||||
&mcf_edma,
|
&mcf_edma,
|
||||||
#endif
|
#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);
|
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, "pll.0", 48, MCF_CLK);
|
||||||
DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
|
DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
|
||||||
DEFINE_CLK(0, "mcfssi.1", 50, 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.0", 53, MCF_CLK);
|
||||||
DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
|
DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
|
||||||
DEFINE_CLK(0, "switch.0", 55, 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, "sys.0", 36, MCF_BUSCLK);
|
||||||
DEFINE_CLK(1, "gpio.0", 37, 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[] = {
|
struct clk *mcf_clks[] = {
|
||||||
&__clk_0_2,
|
&__clk_0_2,
|
||||||
&__clk_0_8,
|
&__clk_0_8,
|
||||||
@ -131,6 +135,11 @@ struct clk *mcf_clks[] = {
|
|||||||
&__clk_1_34,
|
&__clk_1_34,
|
||||||
&__clk_1_36,
|
&__clk_1_36,
|
||||||
&__clk_1_37,
|
&__clk_1_37,
|
||||||
|
|
||||||
|
&__clk_2_0,
|
||||||
|
&__clk_2_1,
|
||||||
|
&__clk_2_2,
|
||||||
|
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,6 +160,7 @@ static struct clk * const enable_clks[] __initconst = {
|
|||||||
&__clk_0_33, /* pit.1 */
|
&__clk_0_33, /* pit.1 */
|
||||||
&__clk_0_37, /* eport */
|
&__clk_0_37, /* eport */
|
||||||
&__clk_0_48, /* pll */
|
&__clk_0_48, /* pll */
|
||||||
|
&__clk_0_51, /* esdhc */
|
||||||
|
|
||||||
&__clk_1_36, /* CCM/reset module/Power management */
|
&__clk_1_36, /* CCM/reset module/Power management */
|
||||||
&__clk_1_37, /* gpio */
|
&__clk_1_37, /* gpio */
|
||||||
@ -194,6 +204,21 @@ static struct clk * const disable_clks[] __initconst = {
|
|||||||
&__clk_1_29, /* uart 9 */
|
&__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)
|
static void __init m5441x_clk_init(void)
|
||||||
{
|
{
|
||||||
unsigned i;
|
unsigned i;
|
||||||
|
@ -278,6 +278,13 @@
|
|||||||
#define MCFGPIO_IRQ_VECBASE (MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
|
#define MCFGPIO_IRQ_VECBASE (MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
|
||||||
#define MCFGPIO_PIN_MAX 87
|
#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.
|
* DSPI module.
|
||||||
*/
|
*/
|
||||||
@ -298,5 +305,13 @@
|
|||||||
#define MCFEDMA_IRQ_INTR16 (MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
|
#define MCFEDMA_IRQ_INTR16 (MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
|
||||||
#define MCFEDMA_IRQ_INTR56 (MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
|
#define MCFEDMA_IRQ_INTR56 (MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
|
||||||
#define MCFEDMA_IRQ_ERR (MCFINT0_VECBASE + MCFINT0_EDMA_ERR)
|
#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 */
|
#endif /* m5441xsim_h */
|
||||||
|
@ -30,6 +30,8 @@ extern struct clk_ops clk_ops0;
|
|||||||
extern struct clk_ops clk_ops1;
|
extern struct clk_ops clk_ops1;
|
||||||
#endif /* MCFPM_PPMCR1 */
|
#endif /* MCFPM_PPMCR1 */
|
||||||
|
|
||||||
|
extern struct clk_ops clk_ops2;
|
||||||
|
|
||||||
#define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
|
#define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
|
||||||
static struct clk __clk_##clk_bank##_##clk_slot = { \
|
static struct clk __clk_##clk_bank##_##clk_slot = { \
|
||||||
.name = clk_name, \
|
.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[] = {
|
static const struct sdio_device_id btmrvl_sdio_ids[] = {
|
||||||
/* Marvell SD8688 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
|
||||||
/* Marvell SD8787 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||||
/* Marvell SD8787 Bluetooth AMP device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
|
||||||
/* Marvell SD8797 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
|
||||||
/* Marvell SD8887 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
|
||||||
/* Marvell SD8897 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
|
||||||
/* Marvell SD8977 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8977 },
|
||||||
/* Marvell SD8987 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8987 },
|
||||||
/* Marvell SD8997 Bluetooth device */
|
/* 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 },
|
.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
|
||||||
|
|
||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
|
@ -51,9 +51,9 @@ static const struct btmtksdio_data mt7668_data = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdio_device_id btmtksdio_table[] = {
|
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 },
|
.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 },
|
.driver_data = (kernel_ulong_t)&mt7668_data },
|
||||||
{ } /* Terminating entry */
|
{ } /* Terminating entry */
|
||||||
};
|
};
|
||||||
|
@ -58,15 +58,15 @@ static const struct sdio_device_id smssdio_ids[] = {
|
|||||||
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
||||||
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
|
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
|
||||||
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
|
||||||
{ /* end: all zeroes */ },
|
{ /* end: all zeroes */ },
|
||||||
};
|
};
|
||||||
|
@ -93,6 +93,20 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|||||||
return retval;
|
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));
|
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
|
||||||
if (retval)
|
if (retval)
|
||||||
return 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;
|
unsigned int rem, to = from + nr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!(card->host->caps & MMC_CAP_ERASE) ||
|
if (!(card->csd.cmdclass & CCC_ERASE))
|
||||||
!(card->csd.cmdclass & CCC_ERASE))
|
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!card->erase_size)
|
if (!card->erase_size)
|
||||||
@ -1872,8 +1871,7 @@ EXPORT_SYMBOL(mmc_erase);
|
|||||||
|
|
||||||
int mmc_can_erase(struct mmc_card *card)
|
int mmc_can_erase(struct mmc_card *card)
|
||||||
{
|
{
|
||||||
if ((card->host->caps & MMC_CAP_ERASE) &&
|
if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
|
||||||
(card->csd.cmdclass & CCC_ERASE) && card->erase_size)
|
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -219,7 +219,7 @@ static int mmc_clock_opt_set(void *data, u64 val)
|
|||||||
return 0;
|
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");
|
"%llu\n");
|
||||||
|
|
||||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
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_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
|
||||||
debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
|
debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
|
||||||
debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
|
debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
|
||||||
debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
|
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
|
||||||
&mmc_clock_fops);
|
&mmc_clock_fops);
|
||||||
|
|
||||||
#ifdef CONFIG_FAIL_MMC_REQUEST
|
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||||
if (fail_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),
|
mmc_hostname(card->host),
|
||||||
card->ext_csd.cmdq_depth);
|
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:
|
out:
|
||||||
return err;
|
return err;
|
||||||
@ -786,6 +789,8 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
|
|||||||
card->ext_csd.enhanced_area_offset);
|
card->ext_csd.enhanced_area_offset);
|
||||||
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
|
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(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(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
|
||||||
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
|
||||||
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
|
||||||
@ -843,6 +848,7 @@ static struct attribute *mmc_std_attrs[] = {
|
|||||||
&dev_attr_enhanced_area_offset.attr,
|
&dev_attr_enhanced_area_offset.attr,
|
||||||
&dev_attr_enhanced_area_size.attr,
|
&dev_attr_enhanced_area_size.attr,
|
||||||
&dev_attr_raw_rpmb_size_mult.attr,
|
&dev_attr_raw_rpmb_size_mult.attr,
|
||||||
|
&dev_attr_enhanced_rpmb_supported.attr,
|
||||||
&dev_attr_rel_sectors.attr,
|
&dev_attr_rel_sectors.attr,
|
||||||
&dev_attr_ocr.attr,
|
&dev_attr_ocr.attr,
|
||||||
&dev_attr_rca.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,
|
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
|
||||||
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
|
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),
|
add_limit_rate_quirk, 150000000),
|
||||||
|
|
||||||
END_FIXUP
|
END_FIXUP
|
||||||
|
@ -136,6 +136,8 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
|
|||||||
int min_uV, int target_uV,
|
int min_uV, int target_uV,
|
||||||
int max_uV)
|
int max_uV)
|
||||||
{
|
{
|
||||||
|
int current_uV;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if supported first to avoid errors since we may try several
|
* Check if supported first to avoid errors since we may try several
|
||||||
* signal levels during power up and don't want to show errors.
|
* 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))
|
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
|
||||||
return -EINVAL;
|
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,
|
return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
|
||||||
max_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
|
* voltage in two steps and try to stay close to vmmc
|
||||||
* with a 0.3V tolerance at first.
|
* with a 0.3V tolerance at first.
|
||||||
*/
|
*/
|
||||||
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
ret = mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||||
min_uV, volt, max_uV))
|
min_uV, volt, max_uV);
|
||||||
return 0;
|
if (ret >= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
|
||||||
2700000, volt, 3600000);
|
2700000, volt, 3600000);
|
||||||
|
@ -376,11 +376,11 @@ int mmc_sd_switch_hs(struct mmc_card *card)
|
|||||||
if (!status)
|
if (!status)
|
||||||
return -ENOMEM;
|
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)
|
if (err)
|
||||||
goto out;
|
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",
|
pr_warn("%s: Problem switching card into high-speed mode!\n",
|
||||||
mmc_hostname(card->host));
|
mmc_hostname(card->host));
|
||||||
err = 0;
|
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);
|
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[] = {
|
static struct attribute *sd_std_attrs[] = {
|
||||||
|
&dev_attr_vendor.attr,
|
||||||
|
&dev_attr_device.attr,
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
&dev_attr_csd.attr,
|
&dev_attr_csd.attr,
|
||||||
&dev_attr_scr.attr,
|
&dev_attr_scr.attr,
|
||||||
@ -726,7 +731,26 @@ static struct attribute *sd_std_attrs[] = {
|
|||||||
&dev_attr_dsr.attr,
|
&dev_attr_dsr.attr,
|
||||||
NULL,
|
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 = {
|
struct device_type sd_type = {
|
||||||
.groups = sd_std_groups,
|
.groups = sd_std_groups,
|
||||||
|
@ -27,6 +27,24 @@
|
|||||||
#include "sdio_ops.h"
|
#include "sdio_ops.h"
|
||||||
#include "sdio_cis.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)
|
static int sdio_read_fbr(struct sdio_func *func)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -543,13 +561,33 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmc_sdio_resend_if_cond(struct mmc_host *host,
|
static int mmc_sdio_pre_init(struct mmc_host *host, u32 ocr,
|
||||||
struct mmc_card *card)
|
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);
|
sdio_reset(host);
|
||||||
mmc_go_idle(host);
|
mmc_go_idle(host);
|
||||||
mmc_send_if_cond(host, host->ocr_avail);
|
mmc_send_if_cond(host, ocr);
|
||||||
mmc_remove_card(card);
|
return mmc_send_io_op_cond(host, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -584,7 +622,7 @@ try_again:
|
|||||||
*/
|
*/
|
||||||
err = mmc_send_io_op_cond(host, ocr, &rocr);
|
err = mmc_send_io_op_cond(host, ocr, &rocr);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
return err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For SPI, enable CRC as appropriate.
|
* For SPI, enable CRC as appropriate.
|
||||||
@ -592,17 +630,15 @@ try_again:
|
|||||||
if (mmc_host_is_spi(host)) {
|
if (mmc_host_is_spi(host)) {
|
||||||
err = mmc_spi_set_crc(host, use_spi_crc);
|
err = mmc_spi_set_crc(host, use_spi_crc);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate card structure.
|
* Allocate card structure.
|
||||||
*/
|
*/
|
||||||
card = mmc_alloc_card(host, NULL);
|
card = mmc_alloc_card(host, &sdio_type);
|
||||||
if (IS_ERR(card)) {
|
if (IS_ERR(card))
|
||||||
err = PTR_ERR(card);
|
return PTR_ERR(card);
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((rocr & R4_MEMORY_PRESENT) &&
|
if ((rocr & R4_MEMORY_PRESENT) &&
|
||||||
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
|
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 ||
|
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
||||||
mmc_remove_card(card);
|
err = -ENOENT;
|
||||||
pr_debug("%s: Perhaps the card was replaced\n",
|
goto mismatch;
|
||||||
mmc_hostname(host));
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
card->type = MMC_TYPE_SDIO;
|
card->type = MMC_TYPE_SDIO;
|
||||||
|
|
||||||
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
||||||
mmc_remove_card(card);
|
err = -ENOENT;
|
||||||
pr_debug("%s: Perhaps the card was replaced\n",
|
goto mismatch;
|
||||||
mmc_hostname(host));
|
|
||||||
return -ENOENT;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -646,7 +678,7 @@ try_again:
|
|||||||
if (rocr & ocr & R4_18V_PRESENT) {
|
if (rocr & ocr & R4_18V_PRESENT) {
|
||||||
err = mmc_set_uhs_voltage(host, ocr_card);
|
err = mmc_set_uhs_voltage(host, ocr_card);
|
||||||
if (err == -EAGAIN) {
|
if (err == -EAGAIN) {
|
||||||
mmc_sdio_resend_if_cond(host, card);
|
mmc_sdio_pre_init(host, ocr_card, card);
|
||||||
retries--;
|
retries--;
|
||||||
goto try_again;
|
goto try_again;
|
||||||
} else if (err) {
|
} else if (err) {
|
||||||
@ -677,7 +709,7 @@ try_again:
|
|||||||
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
||||||
err = mmc_sd_get_csd(host, card);
|
err = mmc_sd_get_csd(host, card);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto remove;
|
||||||
|
|
||||||
mmc_decode_cid(card);
|
mmc_decode_cid(card);
|
||||||
}
|
}
|
||||||
@ -704,7 +736,12 @@ try_again:
|
|||||||
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
|
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);
|
err = sdio_read_cccr(card, ocr);
|
||||||
if (err) {
|
if (err) {
|
||||||
mmc_sdio_resend_if_cond(host, card);
|
mmc_sdio_pre_init(host, ocr_card, card);
|
||||||
if (ocr & R4_18V_PRESENT) {
|
if (ocr & R4_18V_PRESENT) {
|
||||||
/* Retry init sequence, but without R4_18V_PRESENT. */
|
/* Retry init sequence, but without R4_18V_PRESENT. */
|
||||||
retries = 0;
|
retries = 0;
|
||||||
goto try_again;
|
goto try_again;
|
||||||
} else {
|
|
||||||
goto remove;
|
|
||||||
}
|
}
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -731,16 +767,14 @@ try_again:
|
|||||||
goto remove;
|
goto remove;
|
||||||
|
|
||||||
if (oldcard) {
|
if (oldcard) {
|
||||||
int same = (card->cis.vendor == oldcard->cis.vendor &&
|
if (card->cis.vendor == oldcard->cis.vendor &&
|
||||||
card->cis.device == oldcard->cis.device);
|
card->cis.device == oldcard->cis.device) {
|
||||||
mmc_remove_card(card);
|
mmc_remove_card(card);
|
||||||
if (!same) {
|
card = oldcard;
|
||||||
pr_debug("%s: Perhaps the card was replaced\n",
|
} else {
|
||||||
mmc_hostname(host));
|
err = -ENOENT;
|
||||||
return -ENOENT;
|
goto mismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
card = oldcard;
|
|
||||||
}
|
}
|
||||||
card->ocr = ocr_card;
|
card->ocr = ocr_card;
|
||||||
mmc_fixup_device(card, sdio_fixup_methods);
|
mmc_fixup_device(card, sdio_fixup_methods);
|
||||||
@ -801,16 +835,15 @@ try_again:
|
|||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto remove;
|
goto remove;
|
||||||
}
|
}
|
||||||
finish:
|
|
||||||
if (!oldcard)
|
host->card = card;
|
||||||
host->card = card;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
mismatch:
|
||||||
|
pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
|
||||||
remove:
|
remove:
|
||||||
if (!oldcard)
|
if (oldcard != card)
|
||||||
mmc_remove_card(card);
|
mmc_remove_card(card);
|
||||||
|
|
||||||
err:
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,28 +851,7 @@ static int mmc_sdio_reinit_card(struct mmc_host *host)
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
ret = mmc_sdio_pre_init(host, host->card->ocr, NULL);
|
||||||
* 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);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -171,7 +171,7 @@ config MMC_SDHCI_OF_ASPEED
|
|||||||
config MMC_SDHCI_OF_AT91
|
config MMC_SDHCI_OF_AT91
|
||||||
tristate "SDHCI OF support for the Atmel SDMMC controller"
|
tristate "SDHCI OF support for the Atmel SDMMC controller"
|
||||||
depends on MMC_SDHCI_PLTFM
|
depends on MMC_SDHCI_PLTFM
|
||||||
depends on OF
|
depends on OF && HAVE_CLK
|
||||||
help
|
help
|
||||||
This selects the Atmel SDMMC driver
|
This selects the Atmel SDMMC driver
|
||||||
|
|
||||||
@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
|
|||||||
|
|
||||||
If unsure, say N.
|
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
|
config MMC_SDHCI_ESDHC_IMX
|
||||||
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC
|
||||||
@ -405,6 +418,20 @@ config MMC_MESON_GX
|
|||||||
|
|
||||||
If you have a controller with this interface, say Y here.
|
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
|
config MMC_MESON_MX_SDIO
|
||||||
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
|
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
|
||||||
depends on ARCH_MESON || COMPILE_TEST
|
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_USHC) += ushc.o
|
||||||
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
|
||||||
obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.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_MESON_MX_SDIO) += meson-mx-sdio.o
|
||||||
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
|
||||||
obj-$(CONFIG_MMC_SUNXI) += sunxi-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_PLTFM) += sdhci-pltfm.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
|
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.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_ESDHC_IMX) += sdhci-esdhc-imx.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
|
||||||
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/mmc/sdio.h>
|
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/card.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;
|
host->mrq = req;
|
||||||
goldfish_mmc_prepare_data(host, req);
|
goldfish_mmc_prepare_data(host, req);
|
||||||
goldfish_mmc_start_command(host, req->cmd);
|
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)
|
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->f_max = 24000000;
|
||||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
mmc->caps = MMC_CAP_4_BIT_DATA;
|
||||||
|
mmc->caps2 = MMC_CAP2_NO_SDIO;
|
||||||
|
|
||||||
/* Use scatterlist DMA to reduce per-transfer costs.
|
/* Use scatterlist DMA to reduce per-transfer costs.
|
||||||
* NOTE max_seg_size assumption that small blocks aren't
|
* NOTE max_seg_size assumption that small blocks aren't
|
||||||
|
@ -169,6 +169,7 @@
|
|||||||
#define atmci_writel(port, reg, value) \
|
#define atmci_writel(port, reg, value) \
|
||||||
__raw_writel((value), (port)->regs + reg)
|
__raw_writel((value), (port)->regs + reg)
|
||||||
|
|
||||||
|
#define ATMCI_CMD_TIMEOUT_MS 2000
|
||||||
#define AUTOSUSPEND_DELAY 50
|
#define AUTOSUSPEND_DELAY 50
|
||||||
|
|
||||||
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
|
#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,
|
static void atmci_send_command(struct atmel_mci *host,
|
||||||
struct mmc_command *cmd, u32 cmd_flags)
|
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);
|
WARN_ON(host->cmd);
|
||||||
host->cmd = 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_ARGR, cmd->arg);
|
||||||
atmci_writel(host, ATMCI_CMDR, cmd_flags);
|
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)
|
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.)
|
* prepared yet.)
|
||||||
*/
|
*/
|
||||||
atmci_writel(host, ATMCI_IER, iflags);
|
atmci_writel(host, ATMCI_IER, iflags);
|
||||||
|
|
||||||
mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atmci_queue_request(struct atmel_mci *host,
|
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);
|
WARN_ON(host->cmd || host->data);
|
||||||
|
|
||||||
|
del_timer(&host->timer);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the MMC clock rate if necessary. This may be
|
* Update the MMC clock rate if necessary. This may be
|
||||||
* necessary if set_ios() is called when a different slot is
|
* 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;
|
host->state = STATE_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
del_timer(&host->timer);
|
|
||||||
|
|
||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
mmc_request_done(prev_mmc, mrq);
|
mmc_request_done(prev_mmc, mrq);
|
||||||
spin_lock(&host->lock);
|
spin_lock(&host->lock);
|
||||||
|
@ -259,7 +259,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
|
|||||||
au1xmmc_finish_request(host);
|
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)
|
struct mmc_command *cmd, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
|
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));
|
__raw_writel(cmd->arg, HOST_CMDARG(host));
|
||||||
wmb(); /* drain writebuffer */
|
wmb(); /* drain writebuffer */
|
||||||
|
|
||||||
if (wait)
|
|
||||||
IRQ_OFF(host, SD_CONFIG_CR);
|
|
||||||
|
|
||||||
__raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
|
__raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
|
||||||
wmb(); /* drain writebuffer */
|
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)
|
while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO)
|
||||||
/* nop */;
|
/* 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,7 +695,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
|
ret = au1xmmc_send_command(host, mrq->cmd, mrq->data);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
mrq->cmd->error = ret;
|
mrq->cmd->error = ret;
|
||||||
|
@ -1280,8 +1280,7 @@ static int bcm2835_add_host(struct bcm2835_host *host)
|
|||||||
|
|
||||||
/* host controller capabilities */
|
/* host controller capabilities */
|
||||||
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||||
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE |
|
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_CMD23;
|
||||||
MMC_CAP_CMD23;
|
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
mutex_init(&host->mutex);
|
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
|
* Disable bounce buffers for max_segs = 1
|
||||||
*/
|
*/
|
||||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD |
|
MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR;
|
||||||
MMC_CAP_3_3V_DDR;
|
|
||||||
|
|
||||||
if (host->use_sg)
|
if (host->use_sg)
|
||||||
mmc->max_segs = 16;
|
mmc->max_segs = 16;
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include "cb710-mmc.h"
|
#include "cb710-mmc.h"
|
||||||
|
|
||||||
|
#define CB710_MMC_REQ_TIMEOUT_MS 2000
|
||||||
|
|
||||||
static const u8 cb710_clock_divider_log2[8] = {
|
static const u8 cb710_clock_divider_log2[8] = {
|
||||||
/* 1, 2, 4, 8, 16, 32, 128, 512 */
|
/* 1, 2, 4, 8, 16, 32, 128, 512 */
|
||||||
0, 1, 2, 3, 4, 5, 7, 9
|
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->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
|
||||||
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
|
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
|
||||||
mmc->caps = MMC_CAP_4_BIT_DATA;
|
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);
|
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)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
dev_err(host->dev, "Regulator set error %d\n", ret);
|
dev_err(host->dev, "Regulator set error %d\n", ret);
|
||||||
return 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)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
|
if (ret < 0) {
|
||||||
if (ret) {
|
|
||||||
dev_dbg(&mmc->class_dev,
|
dev_dbg(&mmc->class_dev,
|
||||||
"Regulator set error %d - %s V\n",
|
"Regulator set error %d - %s V\n",
|
||||||
ret, uhs & v18 ? "1.8" : "3.3");
|
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)
|
if (host->pdata->caps)
|
||||||
mmc->caps = 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)
|
if (host->pdata->pm_caps)
|
||||||
mmc->pm_caps = 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_LPM_LOW_POWER_MODE_EN BIT(0)
|
||||||
|
|
||||||
#define JZ_MMC_CLK_RATE 24000000
|
#define JZ_MMC_CLK_RATE 24000000
|
||||||
|
#define JZ_MMC_REQ_TIMEOUT_MS 5000
|
||||||
|
|
||||||
enum jz4740_mmc_version {
|
enum jz4740_mmc_version {
|
||||||
JZ_MMC_JZ4740,
|
JZ_MMC_JZ4740,
|
||||||
@ -440,7 +441,8 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
|
|||||||
|
|
||||||
if (timeout == 0) {
|
if (timeout == 0) {
|
||||||
set_bit(0, &host->waiting);
|
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);
|
jz4740_mmc_set_irq_enabled(host, irq, true);
|
||||||
return 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;
|
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
|
||||||
set_bit(0, &host->waiting);
|
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);
|
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->f_min = mmc->f_max / 128;
|
||||||
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
|
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_size = (1 << 10) - 1;
|
||||||
mmc->max_blk_count = (1 << 15) - 1;
|
mmc->max_blk_count = (1 << 15) - 1;
|
||||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
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)
|
static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* vqmmc regulator is available */
|
/* vqmmc regulator is available */
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
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
|
* to 1.8v. Please make sure the regulator framework is aware
|
||||||
* of your own regulator constraints
|
* 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 */
|
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
|
||||||
|
158
drivers/mmc/host/meson-mx-sdhc-clkc.c
Normal file
158
drivers/mmc/host/meson-mx-sdhc-clkc.c
Normal file
@ -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;
|
||||||
|
}
|
914
drivers/mmc/host/meson-mx-sdhc-mmc.c
Normal file
914
drivers/mmc/host/meson-mx-sdhc-mmc.c
Normal file
@ -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");
|
141
drivers/mmc/host/meson-mx-sdhc.h
Normal file
141
drivers/mmc/host/meson-mx-sdhc.h
Normal file
@ -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;
|
mrq = host->mrq;
|
||||||
|
|
||||||
|
if (host->cmd->error)
|
||||||
|
meson_mx_mmc_soft_reset(host);
|
||||||
|
|
||||||
host->mrq = NULL;
|
host->mrq = NULL;
|
||||||
host->cmd = 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,
|
mmc->f_max = clk_round_rate(host->cfg_div_clk,
|
||||||
clk_get_rate(host->parent_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;
|
mmc->ops = &meson_mx_mmc_ops;
|
||||||
|
|
||||||
ret = mmc_of_parse(mmc);
|
ret = mmc_of_parse(mmc);
|
||||||
|
@ -16,11 +16,20 @@
|
|||||||
#define HSQ_NUM_SLOTS 64
|
#define HSQ_NUM_SLOTS 64
|
||||||
#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
|
#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)
|
static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = hsq->mmc;
|
struct mmc_host *mmc = hsq->mmc;
|
||||||
struct hsq_slot *slot;
|
struct hsq_slot *slot;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&hsq->lock, flags);
|
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);
|
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)
|
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;
|
hsq->mmc->cqe_private = hsq;
|
||||||
mmc->cqe_ops = &mmc_hsq_ops;
|
mmc->cqe_ops = &mmc_hsq_ops;
|
||||||
|
|
||||||
|
INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
|
||||||
spin_lock_init(&hsq->lock);
|
spin_lock_init(&hsq->lock);
|
||||||
init_waitqueue_head(&hsq->wait_queue);
|
init_waitqueue_head(&hsq->wait_queue);
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ struct mmc_hsq {
|
|||||||
wait_queue_head_t wait_queue;
|
wait_queue_head_t wait_queue;
|
||||||
struct hsq_slot *slot;
|
struct hsq_slot *slot;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
|
struct work_struct retry_work;
|
||||||
|
|
||||||
int next_tag;
|
int next_tag;
|
||||||
int num_slots;
|
int num_slots;
|
||||||
|
@ -77,14 +77,8 @@
|
|||||||
|
|
||||||
#define MMC_SPI_BLOCKSIZE 512
|
#define MMC_SPI_BLOCKSIZE 512
|
||||||
|
|
||||||
|
#define MMC_SPI_R1B_TIMEOUT_MS 3000
|
||||||
/* These fixed timeouts come from the latest SD specs, which say to ignore
|
#define MMC_SPI_INIT_TIMEOUT_MS 3000
|
||||||
* 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)
|
|
||||||
|
|
||||||
/* One of the critical speed parameters is the amount of data which may
|
/* 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
|
* 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,
|
static int mmc_spi_response_get(struct mmc_spi_host *host,
|
||||||
struct mmc_command *cmd, int cs_on)
|
struct mmc_command *cmd, int cs_on)
|
||||||
{
|
{
|
||||||
|
unsigned long timeout_ms;
|
||||||
u8 *cp = host->data->status;
|
u8 *cp = host->data->status;
|
||||||
u8 *end = cp + host->t.len;
|
u8 *end = cp + host->t.len;
|
||||||
int value = 0;
|
int value = 0;
|
||||||
@ -346,8 +341,11 @@ checkstatus:
|
|||||||
/* maybe we read all the busy tokens already */
|
/* maybe we read all the busy tokens already */
|
||||||
while (cp < end && *cp == 0)
|
while (cp < end && *cp == 0)
|
||||||
cp++;
|
cp++;
|
||||||
if (cp == end)
|
if (cp == end) {
|
||||||
mmc_spi_wait_unbusy(host, r1b_timeout);
|
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||||
|
MMC_SPI_R1B_TIMEOUT_MS;
|
||||||
|
mmc_spi_wait_unbusy(host, msecs_to_jiffies(timeout_ms));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* SPI R2 == R1 + second status byte; SEND_STATUS
|
/* 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;
|
/* Try to be very sure any previous command has completed;
|
||||||
* wait till not-busy, skip debris from any old commands.
|
* 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);
|
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)
|
static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct mmci_host *host = mmc_priv(mmc);
|
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) {
|
if (!ret && host->ops && host->ops->post_sig_volt_switch)
|
||||||
case MMC_SIGNAL_VOLTAGE_330:
|
ret = host->ops->post_sig_volt_switch(host, ios);
|
||||||
ret = regulator_set_voltage(mmc->supply.vqmmc,
|
else if (ret)
|
||||||
2700000, 3600000);
|
ret = 0;
|
||||||
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)
|
if (ret < 0)
|
||||||
ret = host->ops->post_sig_volt_switch(host, ios);
|
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
|
||||||
|
|
||||||
if (ret)
|
|
||||||
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
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)
|
static int sdmmc_idma_setup(struct mmci_host *host)
|
||||||
{
|
{
|
||||||
struct sdmmc_idma *idma;
|
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)
|
if (!idma)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
host->dma_priv = idma;
|
host->dma_priv = idma;
|
||||||
|
|
||||||
if (host->variant->dma_lli) {
|
if (host->variant->dma_lli) {
|
||||||
idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc),
|
idma->sg_cpu = dmam_alloc_coherent(dev, SDMMC_LLI_BUF_LEN,
|
||||||
SDMMC_LLI_BUF_LEN,
|
|
||||||
&idma->sg_dma, GFP_KERNEL);
|
&idma->sg_dma, GFP_KERNEL);
|
||||||
if (!idma->sg_cpu) {
|
if (!idma->sg_cpu) {
|
||||||
dev_err(mmc_dev(host->mmc),
|
dev_err(dev, "Failed to alloc IDMA descriptor\n");
|
||||||
"Failed to alloc IDMA descriptor\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
host->mmc->max_segs = SDMMC_LLI_BUF_LEN /
|
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;
|
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)
|
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)
|
static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data)
|
||||||
{
|
{
|
||||||
writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR);
|
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)
|
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;
|
struct sdmmc_dlyb *dlyb;
|
||||||
|
|
||||||
host->ops = &sdmmc_variant_ops;
|
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);
|
base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL);
|
||||||
if (IS_ERR(base_dlyb))
|
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)
|
static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct msdc_host *host = mmc_priv(mmc);
|
struct msdc_host *host = mmc_priv(mmc);
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
|
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);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
|
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
|
||||||
ret, ios->signal_voltage);
|
ret, ios->signal_voltage);
|
||||||
} else {
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
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)
|
if (mmc->caps & MMC_CAP_SDIO_IRQ)
|
||||||
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
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 core transfer sizes tunable parameters */
|
||||||
mmc->max_segs = MAX_BD_NUM;
|
mmc->max_segs = MAX_BD_NUM;
|
||||||
if (host->dev_comp->support_64g)
|
if (host->dev_comp->support_64g)
|
||||||
|
@ -752,8 +752,6 @@ static int mvsd_probe(struct platform_device *pdev)
|
|||||||
if (maxfreq)
|
if (maxfreq)
|
||||||
mmc->f_max = maxfreq;
|
mmc->f_max = maxfreq;
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_ERASE;
|
|
||||||
|
|
||||||
spin_lock_init(&host->lock);
|
spin_lock_init(&host->lock);
|
||||||
|
|
||||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
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 */
|
/* set mmc core parameters */
|
||||||
mmc->ops = &mxs_mmc_ops;
|
mmc->ops = &mxs_mmc_ops;
|
||||||
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
||||||
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 |
|
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
|
||||||
MMC_CAP_ERASE;
|
|
||||||
|
|
||||||
host->broken_cd = of_property_read_bool(np, "broken-cd");
|
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;
|
mmc->caps = 0;
|
||||||
if (host->pdata->slots[id].wires >= 4)
|
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->ops = &mmc_omap_ops;
|
||||||
mmc->f_min = 400000;
|
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->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
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;
|
mmc->caps |= mmc_pdata(host)->caps;
|
||||||
if (mmc->caps & MMC_CAP_8_BIT_DATA)
|
if (mmc->caps & MMC_CAP_8_BIT_DATA)
|
||||||
|
@ -92,6 +92,8 @@
|
|||||||
#define OWL_SD_STATE_RC16ER BIT(1)
|
#define OWL_SD_STATE_RC16ER BIT(1)
|
||||||
#define OWL_SD_STATE_CRC7ER BIT(0)
|
#define OWL_SD_STATE_CRC7ER BIT(0)
|
||||||
|
|
||||||
|
#define OWL_CMD_TIMEOUT_MS 30000
|
||||||
|
|
||||||
struct owl_mmc_host {
|
struct owl_mmc_host {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct reset_control *reset;
|
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_command *cmd,
|
||||||
struct mmc_data *data)
|
struct mmc_data *data)
|
||||||
{
|
{
|
||||||
|
unsigned long timeout;
|
||||||
u32 mode, state, resp[2];
|
u32 mode, state, resp[2];
|
||||||
u32 cmd_rsp_mask = 0;
|
u32 cmd_rsp_mask = 0;
|
||||||
|
|
||||||
@ -239,7 +242,10 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
|
|||||||
if (data)
|
if (data)
|
||||||
return;
|
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");
|
dev_err(owl_host->dev, "CMD interrupt timeout\n");
|
||||||
cmd->error = -ETIMEDOUT;
|
cmd->error = -ETIMEDOUT;
|
||||||
return;
|
return;
|
||||||
|
@ -36,6 +36,7 @@ struct renesas_sdhi_of_data {
|
|||||||
struct renesas_sdhi_quirks {
|
struct renesas_sdhi_quirks {
|
||||||
bool hs400_disabled;
|
bool hs400_disabled;
|
||||||
bool hs400_4taps;
|
bool hs400_4taps;
|
||||||
|
u32 hs400_bad_taps;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tmio_mmc_dma {
|
struct tmio_mmc_dma {
|
||||||
@ -61,8 +62,10 @@ struct renesas_sdhi {
|
|||||||
|
|
||||||
/* Tuning values: 1 for success, 0 for failure */
|
/* Tuning values: 1 for success, 0 for failure */
|
||||||
DECLARE_BITMAP(taps, BITS_PER_LONG);
|
DECLARE_BITMAP(taps, BITS_PER_LONG);
|
||||||
|
/* Sampling data comparison: 1 for match, 0 for mismatch */
|
||||||
|
DECLARE_BITMAP(smpcmp, BITS_PER_LONG);
|
||||||
unsigned int tap_num;
|
unsigned int tap_num;
|
||||||
unsigned long tap_set;
|
unsigned int tap_set;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define host_to_priv(host) \
|
#define host_to_priv(host) \
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_device.h>
|
#include <linux/of_device.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
#include <linux/mmc/slot-gpio.h>
|
#include <linux/mmc/slot-gpio.h>
|
||||||
#include <linux/mfd/tmio.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 mmc_host *mmc = host->mmc;
|
||||||
struct renesas_sdhi *priv = host_to_priv(host);
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
int ret = clk_prepare_enable(priv->clk);
|
int ret;
|
||||||
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(priv->clk_cd);
|
ret = clk_prepare_enable(priv->clk_cd);
|
||||||
if (ret < 0) {
|
if (ret < 0)
|
||||||
clk_disable_unprepare(priv->clk);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The clock driver may not know what maximum frequency
|
* 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);
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
|
||||||
clk_disable_unprepare(priv->clk);
|
|
||||||
clk_disable_unprepare(priv->clk_cd);
|
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;
|
MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
|
||||||
|
|
||||||
ret = mmc_regulator_set_vqmmc(host->mmc, ios);
|
ret = mmc_regulator_set_vqmmc(host->mmc, ios);
|
||||||
if (ret)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
return pinctrl_select_state(priv->pinctrl, pin_state);
|
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 tmio_mmc_host *host = mmc_priv(mmc);
|
||||||
struct renesas_sdhi *priv = host_to_priv(host);
|
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_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
|
||||||
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
|
||||||
@ -352,10 +349,23 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
|||||||
SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
|
SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
|
||||||
0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
|
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)
|
if (bad_taps & BIT(new_tap))
|
||||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
|
new_tap = (priv->tap_set - 1) % priv->tap_num;
|
||||||
priv->tap_set / 2);
|
|
||||||
|
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,
|
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
|
||||||
SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
|
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;
|
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)
|
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
|
||||||
{
|
{
|
||||||
struct renesas_sdhi *priv = host_to_priv(host);
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
unsigned long tap_cnt; /* counter of tuning success */
|
unsigned int tap_start = 0, tap_end = 0, tap_cnt = 0, rs, re, i;
|
||||||
unsigned long tap_start;/* start position of tuning success */
|
unsigned int taps_size = priv->tap_num * 2, min_tap_row;
|
||||||
unsigned long tap_end; /* end position of tuning success */
|
unsigned long *bitmap;
|
||||||
unsigned long ntap; /* temporary counter of tuning success */
|
|
||||||
unsigned long i;
|
|
||||||
|
|
||||||
priv->doing_tune = false;
|
priv->doing_tune = false;
|
||||||
|
|
||||||
/* Clear SCC_RVSREQ */
|
|
||||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
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
|
* result requiring the tap to be good in both runs before
|
||||||
* considering it for tuning selection.
|
* 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);
|
int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1);
|
||||||
|
|
||||||
if (!test_bit(i, priv->taps))
|
if (!test_bit(i, priv->taps))
|
||||||
clear_bit(i + offset, 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
|
* If all TAP are OK, the sampling clock position is selected by
|
||||||
* is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
|
* identifying the change point of data.
|
||||||
* center index as the tap.
|
|
||||||
*/
|
*/
|
||||||
tap_cnt = 0;
|
if (bitmap_full(priv->taps, taps_size)) {
|
||||||
ntap = 0;
|
bitmap = priv->smpcmp;
|
||||||
tap_start = 0;
|
min_tap_row = 1;
|
||||||
tap_end = 0;
|
} else {
|
||||||
for (i = 0; i < priv->tap_num * 2; i++) {
|
bitmap = priv->taps;
|
||||||
if (test_bit(i, priv->taps)) {
|
min_tap_row = SH_MOBILE_SDHI_MIN_TAP_ROW;
|
||||||
ntap++;
|
}
|
||||||
} else {
|
|
||||||
if (ntap > tap_cnt) {
|
/*
|
||||||
tap_start = i - ntap;
|
* Find the longest consecutive run of successful probes. If that
|
||||||
tap_end = i - 1;
|
* is at least SH_MOBILE_SDHI_MIN_TAP_ROW probes long then use the
|
||||||
tap_cnt = ntap;
|
* center index as the tap, otherwise bail out.
|
||||||
}
|
*/
|
||||||
ntap = 0;
|
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) {
|
if (tap_cnt >= min_tap_row)
|
||||||
tap_start = i - ntap;
|
|
||||||
tap_end = i - 1;
|
|
||||||
tap_cnt = ntap;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
|
|
||||||
priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num;
|
priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num;
|
||||||
else
|
else
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@ -511,6 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
|
|||||||
|
|
||||||
priv->doing_tune = true;
|
priv->doing_tune = true;
|
||||||
bitmap_zero(priv->taps, priv->tap_num * 2);
|
bitmap_zero(priv->taps, priv->tap_num * 2);
|
||||||
|
bitmap_zero(priv->smpcmp, priv->tap_num * 2);
|
||||||
|
|
||||||
/* Issue CMD19 twice for each tap */
|
/* Issue CMD19 twice for each tap */
|
||||||
for (i = 0; i < 2 * priv->tap_num; i++) {
|
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)
|
if (mmc_send_tuning(host->mmc, opcode, NULL) == 0)
|
||||||
set_bit(i, priv->taps);
|
set_bit(i, priv->taps);
|
||||||
|
|
||||||
|
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
|
||||||
|
set_bit(i, priv->smpcmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return renesas_sdhi_select_tuning(host);
|
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)
|
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
|
||||||
{
|
{
|
||||||
struct renesas_sdhi *priv = host_to_priv(host);
|
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;
|
u32 val;
|
||||||
|
|
||||||
val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ);
|
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 */
|
/* Change TAP position according to correction status */
|
||||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
|
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
|
||||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
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.
|
* With HS400, the DAT signal is based on DS, not CLK.
|
||||||
* Therefore, use only CMD status.
|
* Therefore, use only CMD status.
|
||||||
*/
|
*/
|
||||||
u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) &
|
u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) &
|
||||||
SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR;
|
SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR;
|
||||||
if (!smpcmp)
|
if (!smpcmp) {
|
||||||
return false; /* no error in CMD signal */
|
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++;
|
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--;
|
new_tap--;
|
||||||
else
|
error_tap++;
|
||||||
|
} else {
|
||||||
return true; /* need retune */
|
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 {
|
} else {
|
||||||
if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
|
if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
|
||||||
return true; /* need retune */
|
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 = {
|
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
|
||||||
.hs400_4taps = true,
|
.hs400_4taps = true,
|
||||||
|
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
||||||
.hs400_disabled = true,
|
.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[] = {
|
static const struct soc_device_attribute sdhi_quirks_match[] = {
|
||||||
{ .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
{ .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 = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 },
|
||||||
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
|
||||||
|
{ .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
|
||||||
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
|
||||||
|
{ .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap },
|
||||||
|
{ .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
|
||||||
|
{ .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 },
|
||||||
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
|
||||||
{ /* Sentinel. */ },
|
{ /* Sentinel. */ },
|
||||||
};
|
};
|
||||||
@ -860,6 +900,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||||||
/* All SDHI have SDIO status bits which must be 1 */
|
/* All SDHI have SDIO status bits which must be 1 */
|
||||||
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
|
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
|
||||||
|
|
||||||
|
dev_pm_domain_start(&pdev->dev);
|
||||||
|
|
||||||
ret = renesas_sdhi_clk_enable(host);
|
ret = renesas_sdhi_clk_enable(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto efree;
|
goto efree;
|
||||||
@ -933,10 +975,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
|||||||
goto eirq;
|
goto eirq;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
|
dev_info(&pdev->dev, "%s base at %pa, max clock rate %u MHz\n",
|
||||||
mmc_hostname(host->mmc), (unsigned long)
|
mmc_hostname(host->mmc), &res->start, host->mmc->f_max / 1000000);
|
||||||
(platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
|
|
||||||
host->mmc->f_max / 1000000);
|
|
||||||
|
|
||||||
return ret;
|
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->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->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
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->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
|
||||||
mmc->max_current_330 = 400;
|
mmc->max_current_330 = 400;
|
||||||
mmc->max_current_180 = 800;
|
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->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
|
||||||
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
|
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->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
|
||||||
MMC_CAP2_NO_SDIO;
|
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;
|
u32 dcon, imsk, stoptries = 3;
|
||||||
|
|
||||||
/* write DCON register */
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
writel(0, host->base + S3C2410_SDIDCON);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((data->blksz & 3) != 0) {
|
if ((data->blksz & 3) != 0) {
|
||||||
/* We cannot deal with unaligned blocks with more than
|
/* We cannot deal with unaligned blocks with more than
|
||||||
* one block being transferred. */
|
* one block being transferred. */
|
||||||
|
@ -97,6 +97,11 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
|||||||
u32 tmp;
|
u32 tmp;
|
||||||
int ret;
|
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) |
|
tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
|
||||||
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
|
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
|
||||||
writel(tmp, reg);
|
writel(tmp, reg);
|
||||||
@ -111,7 +116,10 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
|
|||||||
tmp &= ~SDHCI_CDNS_HRS04_WR;
|
tmp &= ~SDHCI_CDNS_HRS04_WR;
|
||||||
writel(tmp, reg);
|
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)
|
static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
* Author: Wolfram Sang <kernel@pengutronix.de>
|
* Author: Wolfram Sang <kernel@pengutronix.de>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
@ -89,7 +90,8 @@
|
|||||||
#define ESDHC_STD_TUNING_EN (1 << 24)
|
#define ESDHC_STD_TUNING_EN (1 << 24)
|
||||||
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
|
||||||
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
|
#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_MASK 0x00070000
|
||||||
#define ESDHC_TUNING_STEP_SHIFT 16
|
#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 = {
|
static const struct esdhc_soc_data usdhc_imx6sll_data = {
|
||||||
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
|
||||||
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
|
||||||
|
| ESDHC_FLAG_HS400
|
||||||
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
|
| 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
|
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
|
||||||
| SDHCI_SUPPORT_SDR50
|
| SDHCI_SUPPORT_SDR50
|
||||||
| SDHCI_USE_SDR50_TUNING
|
| 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)
|
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
|
||||||
val |= SDHCI_SUPPORT_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)) {
|
if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
|
||||||
val = 0;
|
val = 0;
|
||||||
val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
|
val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
|
||||||
val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
|
val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
|
||||||
val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
|
val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(reg == SDHCI_INT_STATUS)) {
|
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
|
tmp |= imx_data->boarddata.tuning_step
|
||||||
<< ESDHC_TUNING_STEP_SHIFT;
|
<< 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);
|
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
|
||||||
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
|
||||||
/*
|
/*
|
||||||
@ -1596,6 +1612,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
if (esdhc_is_usdhc(imx_data)) {
|
if (esdhc_is_usdhc(imx_data)) {
|
||||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||||
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
|
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))
|
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
|
||||||
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
|
||||||
|
|
||||||
@ -1653,8 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
|
|||||||
if (err)
|
if (err)
|
||||||
goto disable_ahb_clk;
|
goto disable_ahb_clk;
|
||||||
|
|
||||||
host->tuning_delay = 1;
|
|
||||||
|
|
||||||
sdhci_esdhc_imx_hwinit(host);
|
sdhci_esdhc_imx_hwinit(host);
|
||||||
|
|
||||||
err = sdhci_add_host(host);
|
err = sdhci_add_host(host);
|
||||||
@ -1731,8 +1749,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
|
|||||||
mmc_retune_needed(host->mmc);
|
mmc_retune_needed(host->mmc);
|
||||||
|
|
||||||
ret = sdhci_suspend_host(host);
|
ret = sdhci_suspend_host(host);
|
||||||
if (!ret)
|
if (ret)
|
||||||
return pinctrl_pm_select_sleep_state(dev);
|
return ret;
|
||||||
|
|
||||||
|
ret = pinctrl_pm_select_sleep_state(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = mmc_gpio_set_cd_wake(host->mmc, true);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1756,6 +1780,9 @@ static int sdhci_esdhc_resume(struct device *dev)
|
|||||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||||
ret = cqhci_resume(host->mmc);
|
ret = cqhci_resume(host->mmc);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
ret = mmc_gpio_set_cd_wake(host->mmc, false);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
521
drivers/mmc/host/sdhci-esdhc-mcf.c
Normal file
521
drivers/mmc/host/sdhci-esdhc-mcf.c
Normal file
@ -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) 2007 Freescale Semiconductor, Inc.
|
||||||
* Copyright (c) 2009 MontaVista Software, Inc.
|
* Copyright (c) 2009 MontaVista Software, Inc.
|
||||||
* Copyright (c) 2010 Pengutronix e.K.
|
* 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
|
#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/pm_opp.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
@ -56,19 +57,27 @@
|
|||||||
#define CORE_FLL_CYCLE_CNT BIT(18)
|
#define CORE_FLL_CYCLE_CNT BIT(18)
|
||||||
#define CORE_DLL_CLOCK_DISABLE BIT(21)
|
#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_CLK_PWRSAVE BIT(1)
|
||||||
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
|
||||||
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
|
||||||
#define CORE_HC_MCLK_SEL_MASK (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_EN BIT(15)
|
||||||
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
|
#define CORE_IO_PAD_PWR_SWITCH BIT(16)
|
||||||
#define CORE_HC_SELECT_IN_EN BIT(18)
|
#define CORE_HC_SELECT_IN_EN BIT(18)
|
||||||
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
|
||||||
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
#define CORE_HC_SELECT_IN_MASK (7 << 19)
|
||||||
|
|
||||||
#define CORE_3_0V_SUPPORT (1 << 25)
|
#define CORE_3_0V_SUPPORT BIT(25)
|
||||||
#define CORE_1_8V_SUPPORT (1 << 26)
|
#define CORE_1_8V_SUPPORT BIT(26)
|
||||||
#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
|
#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
|
||||||
|
|
||||||
#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
#define CORE_CSR_CDC_CTLR_CFG0 0x130
|
||||||
@ -156,6 +165,7 @@ struct sdhci_msm_offset {
|
|||||||
u32 core_dll_config_3;
|
u32 core_dll_config_3;
|
||||||
u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */
|
u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */
|
||||||
u32 core_ddr_config;
|
u32 core_ddr_config;
|
||||||
|
u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
|
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_2 = 0x254,
|
||||||
.core_dll_config_3 = 0x258,
|
.core_dll_config_3 = 0x258,
|
||||||
.core_ddr_config = 0x25c,
|
.core_ddr_config = 0x25c,
|
||||||
|
.core_dll_usr_ctl = 0x388,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_msm_offset sdhci_msm_mci_offset = {
|
static const struct sdhci_msm_offset sdhci_msm_mci_offset = {
|
||||||
@ -230,6 +241,7 @@ struct sdhci_msm_variant_ops {
|
|||||||
struct sdhci_msm_variant_info {
|
struct sdhci_msm_variant_info {
|
||||||
bool mci_removed;
|
bool mci_removed;
|
||||||
bool restore_dll_config;
|
bool restore_dll_config;
|
||||||
|
bool uses_tassadar_dll;
|
||||||
const struct sdhci_msm_variant_ops *var_ops;
|
const struct sdhci_msm_variant_ops *var_ops;
|
||||||
const struct sdhci_msm_offset *offset;
|
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 */
|
struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
|
||||||
unsigned long clk_rate;
|
unsigned long clk_rate;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
|
struct opp_table *opp_table;
|
||||||
|
bool has_opp_table;
|
||||||
bool use_14lpp_dll_reset;
|
bool use_14lpp_dll_reset;
|
||||||
bool tuning_done;
|
bool tuning_done;
|
||||||
bool calibration_done;
|
bool calibration_done;
|
||||||
@ -260,6 +274,9 @@ struct sdhci_msm_host {
|
|||||||
bool use_cdr;
|
bool use_cdr;
|
||||||
u32 transfer_mode;
|
u32 transfer_mode;
|
||||||
bool updated_ddr_cfg;
|
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)
|
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;
|
int rc;
|
||||||
|
|
||||||
clock = msm_get_clock_rate_for_bus_mode(host, clock);
|
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) {
|
if (rc) {
|
||||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||||
mmc_hostname(host->mmc), clock,
|
mmc_hostname(host->mmc), clock,
|
||||||
@ -601,6 +618,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||||||
config &= ~CORE_CLK_PWRSAVE;
|
config &= ~CORE_CLK_PWRSAVE;
|
||||||
writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
|
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) {
|
if (msm_host->use_14lpp_dll_reset) {
|
||||||
config = readl_relaxed(host->ioaddr +
|
config = readl_relaxed(host->ioaddr +
|
||||||
msm_offset->core_dll_config);
|
msm_offset->core_dll_config);
|
||||||
@ -626,7 +646,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
|
|||||||
config |= CORE_DLL_PDN;
|
config |= CORE_DLL_PDN;
|
||||||
writel_relaxed(config, host->ioaddr +
|
writel_relaxed(config, host->ioaddr +
|
||||||
msm_offset->core_dll_config);
|
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 &&
|
if (msm_host->use_14lpp_dll_reset &&
|
||||||
!IS_ERR_OR_NULL(msm_host->xo_clk)) {
|
!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);
|
msm_offset->core_dll_config);
|
||||||
|
|
||||||
if (msm_host->use_14lpp_dll_reset) {
|
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 +
|
config = readl_relaxed(host->ioaddr +
|
||||||
msm_offset->core_dll_config_2);
|
msm_offset->core_dll_config_2);
|
||||||
config &= ~CORE_DLL_CLOCK_DISABLE;
|
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);
|
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 +
|
config = readl_relaxed(host->ioaddr +
|
||||||
msm_offset->core_dll_config);
|
msm_offset->core_dll_config);
|
||||||
config |= CORE_DLL_EN;
|
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;
|
ddr_cfg_offset = msm_offset->core_ddr_config;
|
||||||
else
|
else
|
||||||
ddr_cfg_offset = msm_offset->core_ddr_config_old;
|
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) {
|
if (mmc->ios.enhanced_strobe) {
|
||||||
config = readl_relaxed(host->ioaddr +
|
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 */
|
/* Clock-Data-Recovery used to dynamically adjust RX sampling point */
|
||||||
msm_host->use_cdr = true;
|
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:
|
* For HS400 tuning in HS200 timing requires:
|
||||||
* - select MCLK/2 in VENDOR_SPEC
|
* - 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);
|
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 = {
|
static const struct sdhci_msm_variant_ops mci_var_ops = {
|
||||||
.msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed,
|
.msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed,
|
||||||
.msm_writel_relaxed = sdhci_msm_mci_variant_writel_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,
|
.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[] = {
|
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||||
|
{.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1877,16 +1965,34 @@ static const struct sdhci_ops sdhci_msm_ops = {
|
|||||||
.write_w = sdhci_msm_writew,
|
.write_w = sdhci_msm_writew,
|
||||||
.write_b = sdhci_msm_writeb,
|
.write_b = sdhci_msm_writeb,
|
||||||
.irq = sdhci_msm_cqe_irq,
|
.irq = sdhci_msm_cqe_irq,
|
||||||
|
.dump_vendor_regs = sdhci_msm_dump_vendor_regs,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
|
||||||
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
|
||||||
SDHCI_QUIRK_SINGLE_POWER_WRITE |
|
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,
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||||
.ops = &sdhci_msm_ops,
|
.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)
|
static int sdhci_msm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
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->restore_dll_config = var_info->restore_dll_config;
|
||||||
msm_host->var_ops = var_info->var_ops;
|
msm_host->var_ops = var_info->var_ops;
|
||||||
msm_host->offset = var_info->offset;
|
msm_host->offset = var_info->offset;
|
||||||
|
msm_host->uses_tassadar_dll = var_info->uses_tassadar_dll;
|
||||||
|
|
||||||
msm_offset = msm_host->offset;
|
msm_offset = msm_host->offset;
|
||||||
|
|
||||||
sdhci_get_of_property(pdev);
|
sdhci_get_of_property(pdev);
|
||||||
|
sdhci_msm_get_of_property(pdev, host);
|
||||||
|
|
||||||
msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
|
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->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 */
|
/* 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)
|
if (ret)
|
||||||
dev_warn(&pdev->dev, "core clock boost failed\n");
|
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),
|
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
|
||||||
msm_host->bulk_clks);
|
msm_host->bulk_clks);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto bus_clk_disable;
|
goto opp_cleanup;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xo clock is needed for FLL feature of cm_dll.
|
* xo clock is needed for FLL feature of cm_dll.
|
||||||
@ -2117,6 +2240,10 @@ pm_runtime_disable:
|
|||||||
clk_disable:
|
clk_disable:
|
||||||
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
||||||
msm_host->bulk_clks);
|
msm_host->bulk_clks);
|
||||||
|
opp_cleanup:
|
||||||
|
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:
|
bus_clk_disable:
|
||||||
if (!IS_ERR(msm_host->bus_clk))
|
if (!IS_ERR(msm_host->bus_clk))
|
||||||
clk_disable_unprepare(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);
|
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_get_sync(&pdev->dev);
|
||||||
pm_runtime_disable(&pdev->dev);
|
pm_runtime_disable(&pdev->dev);
|
||||||
pm_runtime_put_noidle(&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_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_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),
|
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
|
||||||
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.
|
* restore the SDR DLL settings when the clock is ungated.
|
||||||
*/
|
*/
|
||||||
if (msm_host->restore_dll_config && msm_host->clk_rate)
|
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 = {
|
static const struct dev_pm_ops sdhci_msm_pm_ops = {
|
||||||
|
@ -28,15 +28,26 @@
|
|||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
|
#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 SDHCI_ARASAN_CQE_BASE_ADDR 0x200
|
||||||
#define VENDOR_ENHANCED_STROBE BIT(0)
|
#define VENDOR_ENHANCED_STROBE BIT(0)
|
||||||
|
|
||||||
#define PHY_CLK_TOO_SLOW_HZ 400000
|
#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 */
|
/* Default settings for ZynqMP Clock Phases */
|
||||||
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
|
#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 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
|
* 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
|
* 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
|
* 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
|
* 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
|
* somewhere if needed. Presumably these will be scattered somewhere that's
|
||||||
* accessible via the syscon API.
|
* 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_map {
|
||||||
struct sdhci_arasan_soc_ctl_field baseclkfreq;
|
struct sdhci_arasan_soc_ctl_field baseclkfreq;
|
||||||
struct sdhci_arasan_soc_ctl_field clockmultiplier;
|
struct sdhci_arasan_soc_ctl_field clockmultiplier;
|
||||||
|
struct sdhci_arasan_soc_ctl_field support64b;
|
||||||
bool hiword_update;
|
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_hw: Struct for the clock we might provide to a PHY.
|
||||||
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
|
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
|
||||||
* @sampleclk_hw: Struct for the clock we might provide to a PHY.
|
* @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.
|
* @host: Pointer to the main SDHCI host structure.
|
||||||
* @clk_ahb: Pointer to the AHB clock
|
* @clk_ahb: Pointer to the AHB clock
|
||||||
* @phy: Pointer to the generic phy
|
* @phy: Pointer to the generic phy
|
||||||
* @is_phy_on: True if the PHY is on; false if not.
|
* @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_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_base: Pointer to regmap for syscon for soc_ctl registers.
|
||||||
* @soc_ctl_map: Map to get offsets into 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_arasan_data {
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
@ -120,10 +149,11 @@ struct sdhci_arasan_data {
|
|||||||
|
|
||||||
bool has_cqe;
|
bool has_cqe;
|
||||||
struct sdhci_arasan_clk_data clk_data;
|
struct sdhci_arasan_clk_data clk_data;
|
||||||
|
const struct sdhci_arasan_clk_ops *clk_ops;
|
||||||
|
|
||||||
struct regmap *soc_ctl_base;
|
struct regmap *soc_ctl_base;
|
||||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
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 */
|
/* Controller does not have CD wired and will not function normally without */
|
||||||
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
|
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
|
||||||
@ -135,6 +165,7 @@ struct sdhci_arasan_data {
|
|||||||
struct sdhci_arasan_of_data {
|
struct sdhci_arasan_of_data {
|
||||||
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
|
||||||
const struct sdhci_pltfm_data *pdata;
|
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 = {
|
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,
|
.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
|
* 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.
|
* 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
|
* 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
|
* this function will silently return an error code. It will be noisy
|
||||||
* and print errors for any other (unexpected) errors.
|
* and print errors for any other (unexpected) errors.
|
||||||
*
|
*
|
||||||
* @host: The sdhci_host
|
* Return: 0 on success and error value on error
|
||||||
* @fld: The field to write to
|
|
||||||
* @val: The value to write
|
|
||||||
*/
|
*/
|
||||||
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
|
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
|
||||||
const struct sdhci_arasan_soc_ctl_field *fld,
|
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,
|
.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)
|
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
|
||||||
{
|
{
|
||||||
int cmd_error = 0;
|
int cmd_error = 0;
|
||||||
@ -414,28 +431,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
|
|||||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
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
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/**
|
/**
|
||||||
* sdhci_arasan_suspend - Suspend method for the driver
|
* sdhci_arasan_suspend - Suspend method for the driver
|
||||||
* @dev: Address of the device structure
|
* @dev: Address of the device structure
|
||||||
* Returns 0 on success and error value on error
|
|
||||||
*
|
*
|
||||||
* Put the device in a low power state.
|
* 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)
|
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
|
* sdhci_arasan_resume - Resume method for the driver
|
||||||
* @dev: Address of the device structure
|
* @dev: Address of the device structure
|
||||||
* Returns 0 on success and error value on error
|
|
||||||
*
|
*
|
||||||
* Resume operation after suspend
|
* Resume operation after suspend
|
||||||
|
*
|
||||||
|
* Return: 0 on success and error value on error
|
||||||
*/
|
*/
|
||||||
static int sdhci_arasan_resume(struct device *dev)
|
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,
|
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
|
||||||
sdhci_arasan_resume);
|
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
|
* 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
|
* Return the current actual rate of the SD card clock. This can be used
|
||||||
* to communicate with out PHY.
|
* to communicate with out PHY.
|
||||||
*
|
*
|
||||||
* @hw: Pointer to the hardware clock structure.
|
* Return: The card clock rate.
|
||||||
* @parent_rate The parent rate (should be rate of clk_xin).
|
|
||||||
* Returns the card clock rate.
|
|
||||||
*/
|
*/
|
||||||
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
|
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
|
||||||
unsigned long parent_rate)
|
unsigned long parent_rate)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct sdhci_arasan_clk_data *clk_data =
|
struct sdhci_arasan_clk_data *clk_data =
|
||||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
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
|
* 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
|
* Return the current actual rate of the sampling clock. This can be used
|
||||||
* to communicate with out PHY.
|
* to communicate with out PHY.
|
||||||
*
|
*
|
||||||
* @hw: Pointer to the hardware clock structure.
|
* Return: The sample clock rate.
|
||||||
* @parent_rate The parent rate (should be rate of clk_xin).
|
|
||||||
* Returns the sample clock rate.
|
|
||||||
*/
|
*/
|
||||||
static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw,
|
static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw,
|
||||||
unsigned long parent_rate)
|
unsigned long parent_rate)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct sdhci_arasan_clk_data *clk_data =
|
struct sdhci_arasan_clk_data *clk_data =
|
||||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
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
|
* 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
|
* 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
|
* Return: 0 on success and error value on error
|
||||||
*/
|
*/
|
||||||
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct sdhci_arasan_clk_data *clk_data =
|
struct sdhci_arasan_clk_data *clk_data =
|
||||||
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
|
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
|
* 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
|
* 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
|
* Return: 0 on success and error value on error
|
||||||
*/
|
*/
|
||||||
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
|
||||||
|
|
||||||
{
|
{
|
||||||
struct sdhci_arasan_clk_data *clk_data =
|
struct sdhci_arasan_clk_data *clk_data =
|
||||||
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
|
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,
|
.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)
|
static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
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
|
* 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
|
* The corecfg_clockmultiplier is supposed to contain clock multiplier
|
||||||
* value of programmable clock generator.
|
* 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
|
* - The value of corecfg_clockmultiplier should sync with that of corresponding
|
||||||
* value reading from sdhci_capability_register. So this function is called
|
* value reading from sdhci_capability_register. So this function is called
|
||||||
* once at probe time and never called again.
|
* once at probe time and never called again.
|
||||||
*
|
|
||||||
* @host: The sdhci_host
|
|
||||||
*/
|
*/
|
||||||
static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
||||||
u32 value)
|
u32 value)
|
||||||
@ -843,6 +959,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
|
|||||||
/**
|
/**
|
||||||
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
|
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
|
||||||
*
|
*
|
||||||
|
* @host: The sdhci_host
|
||||||
|
*
|
||||||
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
|
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
|
||||||
* function can be used to make that happen.
|
* 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
|
* - 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
|
* to achieve lower clock rates. That means that this function is called once
|
||||||
* at probe time and never called again.
|
* at probe time and never called again.
|
||||||
*
|
|
||||||
* @host: The sdhci_host
|
|
||||||
*/
|
*/
|
||||||
static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *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
|
* 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.
|
* @dev: Pointer to our struct device.
|
||||||
* @clk_data: Pointer to the Clock Data structure
|
* @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,
|
static void arasan_dt_parse_clk_phases(struct device *dev,
|
||||||
struct sdhci_arasan_clk_data *clk_data)
|
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,
|
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
|
||||||
"clk-phase-legacy");
|
"clk-phase-legacy");
|
||||||
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
|
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");
|
"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_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
|
* 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
|
* them to find out, we'll provide a clock through the common clock framework
|
||||||
* for them to query.
|
* for them to query.
|
||||||
*
|
*
|
||||||
* @sdhci_arasan: Our private data structure.
|
* Return: 0 on success and error value on error
|
||||||
* @clk_xin: Pointer to the functional clock
|
|
||||||
* @dev: Pointer to our struct device.
|
|
||||||
* Returns 0 on success and error value on error
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
|
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.parent_names = &parent_clk_name;
|
||||||
sdcardclk_init.num_parents = 1;
|
sdcardclk_init.num_parents = 1;
|
||||||
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
|
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops;
|
||||||
sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
|
|
||||||
else
|
|
||||||
sdcardclk_init.ops = &arasan_sdcardclk_ops;
|
|
||||||
|
|
||||||
clk_data->sdcardclk_hw.init = &sdcardclk_init;
|
clk_data->sdcardclk_hw.init = &sdcardclk_init;
|
||||||
clk_data->sdcardclk =
|
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_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
|
* 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
|
* them to find out, we'll provide a clock through the common clock framework
|
||||||
* for them to query.
|
* for them to query.
|
||||||
*
|
*
|
||||||
* @sdhci_arasan: Our private data structure.
|
* Return: 0 on success and error value on error
|
||||||
* @clk_xin: Pointer to the functional clock
|
|
||||||
* @dev: Pointer to our struct device.
|
|
||||||
* Returns 0 on success and error value on error
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
|
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.parent_names = &parent_clk_name;
|
||||||
sampleclk_init.num_parents = 1;
|
sampleclk_init.num_parents = 1;
|
||||||
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
|
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
|
||||||
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
|
sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops;
|
||||||
sampleclk_init.ops = &zynqmp_sampleclk_ops;
|
|
||||||
else
|
|
||||||
sampleclk_init.ops = &arasan_sampleclk_ops;
|
|
||||||
|
|
||||||
clk_data->sampleclk_hw.init = &sampleclk_init;
|
clk_data->sampleclk_hw.init = &sampleclk_init;
|
||||||
clk_data->sampleclk =
|
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()
|
* 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()
|
* Should be called any time we're exiting and sdhci_arasan_register_sdclk()
|
||||||
* returned success.
|
* returned success.
|
||||||
*
|
|
||||||
* @dev: Pointer to our struct device.
|
|
||||||
*/
|
*/
|
||||||
static void sdhci_arasan_unregister_sdclk(struct device *dev)
|
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);
|
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_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
|
* 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
|
* them to find out, we'll provide a clock through the common clock framework
|
||||||
* for them to query.
|
* 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
|
* to create nice clean device tree bindings and later (if needed) we can try
|
||||||
* re-architecting SDHCI if we see some benefit to it.
|
* re-architecting SDHCI if we see some benefit to it.
|
||||||
*
|
*
|
||||||
* @sdhci_arasan: Our private data structure.
|
* Return: 0 on success and error value on error
|
||||||
* @clk_xin: Pointer to the functional clock
|
|
||||||
* @dev: Pointer to our struct device.
|
|
||||||
* Returns 0 on success and error value on error
|
|
||||||
*/
|
*/
|
||||||
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
|
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
|
||||||
struct clk *clk_xin,
|
struct clk *clk_xin,
|
||||||
@ -1215,6 +1545,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
sdhci_arasan->host = host;
|
sdhci_arasan->host = host;
|
||||||
|
|
||||||
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
|
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);
|
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
|
||||||
if (node) {
|
if (node) {
|
||||||
@ -1270,6 +1601,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
|||||||
"rockchip,rk3399-sdhci-5.1"))
|
"rockchip,rk3399-sdhci-5.1"))
|
||||||
sdhci_arasan_update_clockmultiplier(host, 0x0);
|
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);
|
sdhci_arasan_update_baseclkfreq(host);
|
||||||
|
|
||||||
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
|
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
|
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.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)
|
|| mmc_gpio_get_cd(host->mmc) >= 0)
|
||||||
sdhci_at91_set_force_card_detect(host);
|
sdhci_at91_set_force_card_detect(host);
|
||||||
|
|
||||||
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL))
|
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) {
|
||||||
sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
|
u32 calcr = sdhci_readl(host, SDMMC_CALCR);
|
||||||
|
|
||||||
|
sdhci_writel(host, calcr | SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
|
||||||
SDMMC_CALCR);
|
SDMMC_CALCR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
|
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;
|
clk_mul = gck_rate / clk_base_rate - 1;
|
||||||
|
|
||||||
caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK;
|
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 &= ~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. */
|
/* Set capabilities in r/w mode. */
|
||||||
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
|
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
|
||||||
writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);
|
writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
|
/* DWCMSHC specific Mode Select value */
|
||||||
|
#define DWCMSHC_CTRL_HS400 0x7
|
||||||
|
|
||||||
#define BOUNDARY_OK(addr, len) \
|
#define BOUNDARY_OK(addr, len) \
|
||||||
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
((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);
|
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 = {
|
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.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,
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
.reset = sdhci_reset,
|
.reset = sdhci_reset,
|
||||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||||
@ -134,6 +163,48 @@ static int dwcmshc_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
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[] = {
|
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||||
{ .compatible = "snps,dwcmshc-sdhci" },
|
{ .compatible = "snps,dwcmshc-sdhci" },
|
||||||
{}
|
{}
|
||||||
@ -144,6 +215,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
|
|||||||
.driver = {
|
.driver = {
|
||||||
.name = "sdhci-dwcmshc",
|
.name = "sdhci-dwcmshc",
|
||||||
.of_match_table = sdhci_dwcmshc_dt_ids,
|
.of_match_table = sdhci_dwcmshc_dt_ids,
|
||||||
|
.pm = &dwcmshc_pmops,
|
||||||
},
|
},
|
||||||
.probe = dwcmshc_probe,
|
.probe = dwcmshc_probe,
|
||||||
.remove = dwcmshc_remove,
|
.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,
|
static void esdhc_set_uhs_signaling(struct sdhci_host *host,
|
||||||
unsigned int timing)
|
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)
|
if (timing == MMC_TIMING_MMC_HS400)
|
||||||
esdhc_tuning_block_enable(host, true);
|
esdhc_tuning_block_enable(host, true);
|
||||||
else
|
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)
|
static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
|
||||||
{
|
{
|
||||||
slot->host->caps =
|
slot->host->caps =
|
||||||
((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
|
FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
|
||||||
& SDHCI_TIMEOUT_CLK_MASK) |
|
FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
|
||||||
|
|
||||||
((0x21 << SDHCI_CLOCK_BASE_SHIFT)
|
|
||||||
& SDHCI_CLOCK_BASE_MASK) |
|
|
||||||
|
|
||||||
SDHCI_TIMEOUT_CLK_UNIT |
|
SDHCI_TIMEOUT_CLK_UNIT |
|
||||||
SDHCI_CAN_VDD_330 |
|
SDHCI_CAN_VDD_330 |
|
||||||
SDHCI_CAN_DO_HISPD |
|
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(SYNOPSYS, DWC_MSHC, snps),
|
||||||
SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
|
SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
|
||||||
SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
|
SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
|
||||||
|
SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e),
|
||||||
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
|
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
|
||||||
/* Generic SD host controller */
|
/* Generic SD host controller */
|
||||||
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
|
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},
|
||||||
|
@ -63,6 +63,19 @@
|
|||||||
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
|
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
|
||||||
#define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1
|
#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
|
#define GLI_MAX_TUNING_LOOP 40
|
||||||
|
|
||||||
/* Genesys Logic chipset */
|
/* Genesys Logic chipset */
|
||||||
@ -351,6 +364,81 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
|
|||||||
}
|
}
|
||||||
#endif
|
#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 = {
|
static const struct sdhci_ops sdhci_gl9755_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.enable_dma = sdhci_pci_enable_dma,
|
.enable_dma = sdhci_pci_enable_dma,
|
||||||
@ -390,3 +478,21 @@ const struct sdhci_pci_fixes sdhci_gl9750 = {
|
|||||||
.resume = sdhci_pci_gli_resume,
|
.resume = sdhci_pci_gli_resume,
|
||||||
#endif
|
#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;
|
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);
|
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_pci_chip *chip;
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host;
|
||||||
@ -578,7 +578,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
|
|||||||
return 0;
|
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;
|
int ret;
|
||||||
u8 scratch;
|
u8 scratch;
|
||||||
@ -783,7 +783,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#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);
|
sdhci_pci_o2_probe(chip);
|
||||||
return sdhci_pci_resume_host(chip);
|
return sdhci_pci_resume_host(chip);
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
|
|
||||||
#define PCI_DEVICE_ID_GLI_9755 0x9755
|
#define PCI_DEVICE_ID_GLI_9755 0x9755
|
||||||
#define PCI_DEVICE_ID_GLI_9750 0x9750
|
#define PCI_DEVICE_ID_GLI_9750 0x9750
|
||||||
|
#define PCI_DEVICE_ID_GLI_9763E 0xe763
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* PCI device class and mask
|
* 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_o2;
|
||||||
extern const struct sdhci_pci_fixes sdhci_gl9750;
|
extern const struct sdhci_pci_fixes sdhci_gl9750;
|
||||||
extern const struct sdhci_pci_fixes sdhci_gl9755;
|
extern const struct sdhci_pci_fixes sdhci_gl9755;
|
||||||
|
extern const struct sdhci_pci_fixes sdhci_gl9763e;
|
||||||
|
|
||||||
#endif /* __SDHCI_PCI_H */
|
#endif /* __SDHCI_PCI_H */
|
||||||
|
@ -406,7 +406,8 @@ static struct sdhci_ops sdhci_sprd_ops = {
|
|||||||
.request_done = sdhci_sprd_request_done,
|
.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_host *host = mmc_priv(mmc);
|
||||||
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
|
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) &&
|
mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
|
||||||
(host->flags & SDHCI_AUTO_CMD23))
|
(host->flags & SDHCI_AUTO_CMD23))
|
||||||
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);
|
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)
|
static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = mmc_priv(mmc);
|
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)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
pr_err("%s: Switching signalling voltage failed\n",
|
pr_err("%s: Switching signalling voltage failed\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
return ret;
|
return ret;
|
||||||
@ -556,11 +570,17 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
|
|||||||
sdhci_sprd_voltage_switch;
|
sdhci_sprd_voltage_switch;
|
||||||
|
|
||||||
host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
|
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);
|
ret = mmc_of_parse(host->mmc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto pltfm_free;
|
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);
|
sprd_host = TO_SPRD_HOST(host);
|
||||||
sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node);
|
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)
|
if (ret)
|
||||||
goto err_cleanup_host;
|
goto err_cleanup_host;
|
||||||
|
|
||||||
host->always_defer_done = true;
|
|
||||||
|
|
||||||
ret = __sdhci_add_host(host);
|
ret = __sdhci_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_cleanup_host;
|
goto err_cleanup_host;
|
||||||
|
@ -604,6 +604,39 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
|
|||||||
if (err)
|
if (err)
|
||||||
autocal->pull_down_1v8 = 0;
|
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,
|
err = device_property_read_u32(host->mmc->parent,
|
||||||
"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
|
"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
|
||||||
&autocal->pull_up_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));
|
mmc_hostname(host->mmc));
|
||||||
autocal->pull_down_1v8_timeout = 0;
|
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)
|
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_quirks = 0;
|
||||||
static unsigned int debug_quirks2;
|
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 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)
|
void sdhci_dumpregs(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
|
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");
|
SDHCI_DUMP("============================================\n");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_dumpregs);
|
EXPORT_SYMBOL_GPL(sdhci_dumpregs);
|
||||||
@ -317,6 +320,7 @@ out:
|
|||||||
static void sdhci_init(struct sdhci_host *host, int soft)
|
static void sdhci_init(struct sdhci_host *host, int soft)
|
||||||
{
|
{
|
||||||
struct mmc_host *mmc = host->mmc;
|
struct mmc_host *mmc = host->mmc;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
if (soft)
|
if (soft)
|
||||||
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
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)
|
if (host->v4_mode)
|
||||||
sdhci_do_enable_v4_mode(host);
|
sdhci_do_enable_v4_mode(host);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
sdhci_set_default_irqs(host);
|
sdhci_set_default_irqs(host);
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
host->cqe_on = false;
|
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) {
|
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
|
||||||
/* Copy the data to the bounce buffer */
|
/* Copy the data to the bounce buffer */
|
||||||
sg_copy_to_buffer(data->sg, data->sg_len,
|
if (host->ops->copy_to_bounce_buffer) {
|
||||||
host->bounce_buffer,
|
host->ops->copy_to_bounce_buffer(host,
|
||||||
length);
|
data, length);
|
||||||
|
} else {
|
||||||
|
sg_copy_to_buffer(data->sg, data->sg_len,
|
||||||
|
host->bounce_buffer, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* Switch ownership to the DMA */
|
/* Switch ownership to the DMA */
|
||||||
dma_sync_single_for_device(host->mmc->parent,
|
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;
|
!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,
|
static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
|
||||||
struct mmc_command *cmd,
|
struct mmc_command *cmd,
|
||||||
u16 *mode)
|
u16 *mode)
|
||||||
{
|
{
|
||||||
bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) &&
|
bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) &&
|
||||||
(cmd->opcode != SD_IO_RW_EXTENDED);
|
(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;
|
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) {
|
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
|
||||||
mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
|
mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
|
||||||
sdhci_auto_cmd_select(host, cmd, &mode);
|
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);
|
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)
|
if (host->data_cmd && host->data_cmd->mrq == mrq)
|
||||||
host->data_cmd = NULL;
|
host->data_cmd = NULL;
|
||||||
|
|
||||||
|
if (host->deferred_cmd && host->deferred_cmd->mrq == mrq)
|
||||||
|
host->deferred_cmd = NULL;
|
||||||
|
|
||||||
if (host->data && host->data->mrq == mrq)
|
if (host->data && host->data->mrq == mrq)
|
||||||
host->data = NULL;
|
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);
|
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_command *data_cmd = host->data_cmd;
|
||||||
struct mmc_data *data = host->data;
|
struct mmc_data *data = host->data;
|
||||||
@ -1539,14 +1564,31 @@ static void sdhci_finish_data(struct sdhci_host *host)
|
|||||||
} else {
|
} else {
|
||||||
/* Avoid triggering warning in sdhci_send_command() */
|
/* Avoid triggering warning in sdhci_send_command() */
|
||||||
host->cmd = NULL;
|
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 {
|
} else {
|
||||||
__sdhci_finish_mrq(host, data->mrq);
|
__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;
|
int flags;
|
||||||
u32 mask;
|
u32 mask;
|
||||||
@ -1561,9 +1603,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
|
|||||||
cmd->opcode == MMC_STOP_TRANSMISSION)
|
cmd->opcode == MMC_STOP_TRANSMISSION)
|
||||||
cmd->flags |= MMC_RSP_BUSY;
|
cmd->flags |= MMC_RSP_BUSY;
|
||||||
|
|
||||||
/* Wait max 10 ms */
|
|
||||||
timeout = 10;
|
|
||||||
|
|
||||||
mask = SDHCI_CMD_INHIBIT;
|
mask = SDHCI_CMD_INHIBIT;
|
||||||
if (sdhci_data_line_cmd(cmd))
|
if (sdhci_data_line_cmd(cmd))
|
||||||
mask |= SDHCI_DATA_INHIBIT;
|
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))
|
if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
|
||||||
mask &= ~SDHCI_DATA_INHIBIT;
|
mask &= ~SDHCI_DATA_INHIBIT;
|
||||||
|
|
||||||
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
|
if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
|
||||||
if (timeout == 0) {
|
return false;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
host->cmd = cmd;
|
host->cmd = cmd;
|
||||||
host->data_timeout = 0;
|
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);
|
sdhci_set_transfer_mode(host, cmd);
|
||||||
|
|
||||||
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
|
||||||
pr_err("%s: Unsupported response type!\n",
|
WARN_ONCE(1, "Unsupported response type!\n");
|
||||||
mmc_hostname(host->mmc));
|
/*
|
||||||
cmd->error = -EINVAL;
|
* This does not happen in practice because 136-bit response
|
||||||
sdhci_finish_mrq(host, cmd->mrq);
|
* commands never have busy waiting, so rather than complicate
|
||||||
return;
|
* the error path, just remove busy waiting and continue.
|
||||||
|
*/
|
||||||
|
cmd->flags &= ~MMC_RSP_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(cmd->flags & MMC_RSP_PRESENT))
|
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_external_dma_pre_transfer(host, cmd);
|
||||||
|
|
||||||
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
|
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)
|
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. */
|
/* Finished CMD23, now send actual command. */
|
||||||
if (cmd == cmd->mrq->sbc) {
|
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 {
|
} else {
|
||||||
|
|
||||||
/* Processed actual command. */
|
/* 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)
|
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host;
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
int present;
|
struct mmc_command *cmd;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
bool present;
|
||||||
host = mmc_priv(mmc);
|
|
||||||
|
|
||||||
/* Firstly check card presence */
|
/* Firstly check card presence */
|
||||||
present = mmc->ops->get_cd(mmc);
|
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);
|
sdhci_led_activate(host);
|
||||||
|
|
||||||
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
|
if (sdhci_present_error(host, mrq->cmd, present))
|
||||||
mrq->cmd->error = -ENOMEDIUM;
|
goto out_finish;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(sdhci_request);
|
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)
|
void sdhci_set_bus_width(struct sdhci_host *host, int width)
|
||||||
{
|
{
|
||||||
u8 ctrl;
|
u8 ctrl;
|
||||||
@ -2411,7 +2535,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||||||
|
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
|
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@ -2434,7 +2558,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
|
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
return -EIO;
|
return -EIO;
|
||||||
@ -2466,7 +2590,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (!IS_ERR(mmc->supply.vqmmc)) {
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
ret = mmc_regulator_set_vqmmc(mmc, ios);
|
||||||
if (ret) {
|
if (ret < 0) {
|
||||||
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
|
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
|
||||||
mmc_hostname(mmc));
|
mmc_hostname(mmc));
|
||||||
return -EIO;
|
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_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;
|
host->cmd = NULL;
|
||||||
|
|
||||||
@ -3018,7 +3146,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
|
|||||||
|
|
||||||
if (host->data) {
|
if (host->data) {
|
||||||
host->data->error = -ETIMEDOUT;
|
host->data->error = -ETIMEDOUT;
|
||||||
sdhci_finish_data(host);
|
__sdhci_finish_data(host, true);
|
||||||
queue_work(host->complete_wq, &host->complete_work);
|
queue_work(host->complete_wq, &host->complete_work);
|
||||||
} else if (host->data_cmd) {
|
} else if (host->data_cmd) {
|
||||||
host->data_cmd->error = -ETIMEDOUT;
|
host->data_cmd->error = -ETIMEDOUT;
|
||||||
@ -3390,6 +3518,9 @@ cont:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (host->deferred_cmd)
|
||||||
|
result = IRQ_WAKE_THREAD;
|
||||||
|
|
||||||
spin_unlock(&host->lock);
|
spin_unlock(&host->lock);
|
||||||
|
|
||||||
/* Process mrqs ready for immediate completion */
|
/* Process mrqs ready for immediate completion */
|
||||||
@ -3415,6 +3546,7 @@ out:
|
|||||||
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_id;
|
struct sdhci_host *host = dev_id;
|
||||||
|
struct mmc_command *cmd;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 isr;
|
u32 isr;
|
||||||
|
|
||||||
@ -3422,8 +3554,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
|
|||||||
;
|
;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->lock, flags);
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
|
||||||
isr = host->thread_isr;
|
isr = host->thread_isr;
|
||||||
host->thread_isr = 0;
|
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);
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
|
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)
|
if (host->version >= SDHCI_SPEC_300)
|
||||||
host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
|
host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
|
||||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
||||||
else
|
else
|
||||||
host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
|
host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps);
|
||||||
>> SDHCI_CLOCK_BASE_SHIFT;
|
|
||||||
|
|
||||||
host->max_clk *= 1000000;
|
host->max_clk *= 1000000;
|
||||||
if (host->max_clk == 0 || host->quirks &
|
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
|
* In case of Host Controller v3.00, find out whether clock
|
||||||
* multiplier is supported.
|
* multiplier is supported.
|
||||||
*/
|
*/
|
||||||
host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
|
host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1);
|
||||||
SDHCI_CLOCK_MUL_SHIFT;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case the value in Clock Multiplier is 0, then programmable
|
* 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;
|
mmc->f_max = max_clk;
|
||||||
|
|
||||||
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
|
||||||
host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
|
host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
|
||||||
SDHCI_TIMEOUT_CLK_SHIFT;
|
|
||||||
|
|
||||||
if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
|
if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
|
||||||
host->timeout_clk *= 1000;
|
host->timeout_clk *= 1000;
|
||||||
@ -4201,7 +4335,7 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||||||
!host->ops->get_max_timeout_count)
|
!host->ops->get_max_timeout_count)
|
||||||
mmc->max_busy_timeout = 0;
|
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;
|
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
|
||||||
|
|
||||||
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
|
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;
|
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
|
||||||
|
|
||||||
/* Initial value for re-tuning timer count */
|
/* Initial value for re-tuning timer count */
|
||||||
host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
|
host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
|
||||||
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
|
host->caps1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case Re-tuning Timer is not disabled, the actual value of
|
* 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);
|
host->tuning_count = 1 << (host->tuning_count - 1);
|
||||||
|
|
||||||
/* Re-tuning mode supported by the Host Controller */
|
/* Re-tuning mode supported by the Host Controller */
|
||||||
host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
|
host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
|
||||||
SDHCI_RETUNING_MODE_SHIFT;
|
|
||||||
|
|
||||||
ocr_avail = 0;
|
ocr_avail = 0;
|
||||||
|
|
||||||
@ -4357,35 +4490,32 @@ int sdhci_setup_host(struct sdhci_host *host)
|
|||||||
|
|
||||||
curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
|
curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
|
||||||
max_current_caps =
|
max_current_caps =
|
||||||
(curr << SDHCI_MAX_CURRENT_330_SHIFT) |
|
FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, curr) |
|
||||||
(curr << SDHCI_MAX_CURRENT_300_SHIFT) |
|
FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, curr) |
|
||||||
(curr << SDHCI_MAX_CURRENT_180_SHIFT);
|
FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, curr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host->caps & SDHCI_CAN_VDD_330) {
|
if (host->caps & SDHCI_CAN_VDD_330) {
|
||||||
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
|
||||||
|
|
||||||
mmc->max_current_330 = ((max_current_caps &
|
mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
|
||||||
SDHCI_MAX_CURRENT_330_MASK) >>
|
max_current_caps) *
|
||||||
SDHCI_MAX_CURRENT_330_SHIFT) *
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
||||||
}
|
}
|
||||||
if (host->caps & SDHCI_CAN_VDD_300) {
|
if (host->caps & SDHCI_CAN_VDD_300) {
|
||||||
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
|
||||||
|
|
||||||
mmc->max_current_300 = ((max_current_caps &
|
mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
|
||||||
SDHCI_MAX_CURRENT_300_MASK) >>
|
max_current_caps) *
|
||||||
SDHCI_MAX_CURRENT_300_SHIFT) *
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
||||||
}
|
}
|
||||||
if (host->caps & SDHCI_CAN_VDD_180) {
|
if (host->caps & SDHCI_CAN_VDD_180) {
|
||||||
ocr_avail |= MMC_VDD_165_195;
|
ocr_avail |= MMC_VDD_165_195;
|
||||||
|
|
||||||
mmc->max_current_180 = ((max_current_caps &
|
mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
|
||||||
SDHCI_MAX_CURRENT_180_MASK) >>
|
max_current_caps) *
|
||||||
SDHCI_MAX_CURRENT_180_SHIFT) *
|
SDHCI_MAX_CURRENT_MULTIPLIER;
|
||||||
SDHCI_MAX_CURRENT_MULTIPLIER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If OCR set by host, use it instead. */
|
/* If OCR set by host, use it instead. */
|
||||||
|
@ -200,12 +200,10 @@
|
|||||||
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
|
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
|
||||||
|
|
||||||
#define SDHCI_CAPABILITIES 0x40
|
#define SDHCI_CAPABILITIES 0x40
|
||||||
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
|
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
|
||||||
#define SDHCI_TIMEOUT_CLK_SHIFT 0
|
|
||||||
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
|
||||||
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
|
#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
|
||||||
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
|
#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
|
||||||
#define SDHCI_CLOCK_BASE_SHIFT 8
|
|
||||||
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
#define SDHCI_MAX_BLOCK_MASK 0x00030000
|
||||||
#define SDHCI_MAX_BLOCK_SHIFT 16
|
#define SDHCI_MAX_BLOCK_SHIFT 16
|
||||||
#define SDHCI_CAN_DO_8BIT 0x00040000
|
#define SDHCI_CAN_DO_8BIT 0x00040000
|
||||||
@ -220,32 +218,25 @@
|
|||||||
#define SDHCI_CAN_64BIT_V4 0x08000000
|
#define SDHCI_CAN_64BIT_V4 0x08000000
|
||||||
#define SDHCI_CAN_64BIT 0x10000000
|
#define SDHCI_CAN_64BIT 0x10000000
|
||||||
|
|
||||||
|
#define SDHCI_CAPABILITIES_1 0x44
|
||||||
#define SDHCI_SUPPORT_SDR50 0x00000001
|
#define SDHCI_SUPPORT_SDR50 0x00000001
|
||||||
#define SDHCI_SUPPORT_SDR104 0x00000002
|
#define SDHCI_SUPPORT_SDR104 0x00000002
|
||||||
#define SDHCI_SUPPORT_DDR50 0x00000004
|
#define SDHCI_SUPPORT_DDR50 0x00000004
|
||||||
#define SDHCI_DRIVER_TYPE_A 0x00000010
|
#define SDHCI_DRIVER_TYPE_A 0x00000010
|
||||||
#define SDHCI_DRIVER_TYPE_C 0x00000020
|
#define SDHCI_DRIVER_TYPE_C 0x00000020
|
||||||
#define SDHCI_DRIVER_TYPE_D 0x00000040
|
#define SDHCI_DRIVER_TYPE_D 0x00000040
|
||||||
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
|
#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
|
||||||
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
|
|
||||||
#define SDHCI_USE_SDR50_TUNING 0x00002000
|
#define SDHCI_USE_SDR50_TUNING 0x00002000
|
||||||
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
|
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
|
||||||
#define SDHCI_RETUNING_MODE_SHIFT 14
|
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
|
||||||
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
|
|
||||||
#define SDHCI_CLOCK_MUL_SHIFT 16
|
|
||||||
#define SDHCI_CAN_DO_ADMA3 0x08000000
|
#define SDHCI_CAN_DO_ADMA3 0x08000000
|
||||||
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
|
||||||
|
|
||||||
#define SDHCI_CAPABILITIES_1 0x44
|
|
||||||
|
|
||||||
#define SDHCI_MAX_CURRENT 0x48
|
#define SDHCI_MAX_CURRENT 0x48
|
||||||
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
|
#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
|
||||||
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
|
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
|
||||||
#define SDHCI_MAX_CURRENT_330_SHIFT 0
|
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
|
||||||
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
|
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
|
||||||
#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_MULTIPLIER 4
|
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
|
||||||
|
|
||||||
/* 4C-4F reserved for more max current */
|
/* 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_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
|
||||||
struct mmc_command *cmd; /* Current command */
|
struct mmc_command *cmd; /* Current command */
|
||||||
struct mmc_command *data_cmd; /* Current data command */
|
struct mmc_command *data_cmd; /* Current data command */
|
||||||
|
struct mmc_command *deferred_cmd; /* Deferred command */
|
||||||
struct mmc_data *data; /* Current data request */
|
struct mmc_data *data; /* Current data request */
|
||||||
unsigned int data_early:1; /* Data finished before cmd */
|
unsigned int data_early:1; /* Data finished before cmd */
|
||||||
|
|
||||||
@ -653,8 +645,12 @@ struct sdhci_ops {
|
|||||||
void (*voltage_switch)(struct sdhci_host *host);
|
void (*voltage_switch)(struct sdhci_host *host);
|
||||||
void (*adma_write_desc)(struct sdhci_host *host, void **desc,
|
void (*adma_write_desc)(struct sdhci_host *host, void **desc,
|
||||||
dma_addr_t addr, int len, unsigned int cmd);
|
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,
|
void (*request_done)(struct sdhci_host *host,
|
||||||
struct mmc_request *mrq);
|
struct mmc_request *mrq);
|
||||||
|
void (*dump_vendor_regs)(struct sdhci_host *host);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
|
#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);
|
||||||
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_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)
|
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,
|
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
|
||||||
unsigned short vdd);
|
unsigned short vdd);
|
||||||
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
|
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_set_bus_width(struct sdhci_host *host, int width);
|
||||||
void sdhci_reset(struct sdhci_host *host, u8 mask);
|
void sdhci_reset(struct sdhci_host *host, u8 mask);
|
||||||
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
|
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
|
||||||
#include <pcmcia/cistpl.h>
|
#include <pcmcia/cistpl.h>
|
||||||
@ -22,6 +23,7 @@
|
|||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
|
||||||
#define DRIVER_NAME "sdricoh_cs"
|
#define DRIVER_NAME "sdricoh_cs"
|
||||||
|
|
||||||
@ -57,10 +59,8 @@ static unsigned int switchlocked;
|
|||||||
#define STATUS_BUSY 0x40000000
|
#define STATUS_BUSY 0x40000000
|
||||||
|
|
||||||
/* timeouts */
|
/* timeouts */
|
||||||
#define INIT_TIMEOUT 100
|
#define SDRICOH_CMD_TIMEOUT_US 1000000
|
||||||
#define CMD_TIMEOUT 100000
|
#define SDRICOH_DATA_TIMEOUT_US 1000000
|
||||||
#define TRANSFER_TIMEOUT 100000
|
|
||||||
#define BUSY_TIMEOUT 32767
|
|
||||||
|
|
||||||
/* list of supported pcmcia devices */
|
/* list of supported pcmcia devices */
|
||||||
static const struct pcmcia_device_id pcmcia_ids[] = {
|
static const struct pcmcia_device_id pcmcia_ids[] = {
|
||||||
@ -124,19 +124,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
|
static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status,
|
||||||
unsigned int timeout){
|
unsigned int wanted)
|
||||||
unsigned int loop;
|
{
|
||||||
|
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;
|
unsigned int status = 0;
|
||||||
struct device *dev = host->dev;
|
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);
|
dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
|
||||||
return -ETIMEDOUT;
|
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,
|
static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd)
|
||||||
unsigned int arg)
|
|
||||||
{
|
{
|
||||||
unsigned int status;
|
unsigned int status, timeout_us;
|
||||||
int result = 0;
|
int ret;
|
||||||
unsigned int loop = 0;
|
unsigned char opcode = cmd->opcode;
|
||||||
|
|
||||||
/* reset status reg? */
|
/* reset status reg? */
|
||||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
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 */
|
/* 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);
|
sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
|
||||||
|
|
||||||
/* wait for command completion */
|
/* wait for command completion */
|
||||||
if (opcode) {
|
if (!opcode)
|
||||||
for (loop = 0; loop < CMD_TIMEOUT; loop++) {
|
return 0;
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
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)
|
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;
|
u32 data = 0;
|
||||||
/* wait until the data is available */
|
/* wait until the data is available */
|
||||||
if (read) {
|
if (read) {
|
||||||
if (sdricoh_query_status(host, STATUS_READY_TO_READ,
|
if (sdricoh_query_status(host, STATUS_READY_TO_READ))
|
||||||
TRANSFER_TIMEOUT))
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||||
/* read data */
|
/* read data */
|
||||||
@ -224,8 +239,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
|
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE))
|
||||||
TRANSFER_TIMEOUT))
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
sdricoh_writel(host, R21C_STATUS, 0x18);
|
sdricoh_writel(host, R21C_STATUS, 0x18);
|
||||||
/* write data */
|
/* 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_command *cmd = mrq->cmd;
|
||||||
struct mmc_data *data = cmd->data;
|
struct mmc_data *data = cmd->data;
|
||||||
struct device *dev = host->dev;
|
struct device *dev = host->dev;
|
||||||
unsigned char opcode = cmd->opcode;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
dev_dbg(dev, "=============================\n");
|
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);
|
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 */
|
/* read/write commands seem to require this */
|
||||||
if (data) {
|
if (data) {
|
||||||
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
|
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
|
||||||
sdricoh_writel(host, R208_DATAIO, 0);
|
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 */
|
/* read response buffer */
|
||||||
if (cmd->flags & MMC_RSP_PRESENT) {
|
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);
|
sdricoh_writel(host, R208_DATAIO, 1);
|
||||||
|
|
||||||
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
|
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) {
|
||||||
TRANSFER_TIMEOUT)) {
|
|
||||||
dev_err(dev, "sdricoh_request: transfer end error\n");
|
dev_err(dev, "sdricoh_request: transfer end error\n");
|
||||||
cmd->error = -EINVAL;
|
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)
|
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
/* vqmmc regulator is available */
|
/* vqmmc regulator is available */
|
||||||
if (!IS_ERR(mmc->supply.vqmmc))
|
if (!IS_ERR(mmc->supply.vqmmc)) {
|
||||||
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 */
|
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
|
||||||
if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
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_min = 400000;
|
||||||
mmc->f_max = 52000000;
|
mmc->f_max = 52000000;
|
||||||
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
|
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
|
* 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_MAX_BLOCK_SIZE 0x0800UL
|
||||||
|
|
||||||
|
#define TIFM_MMCSD_REQ_TIMEOUT_MS 1000
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CMD_READY = 0x0001,
|
CMD_READY = 0x0001,
|
||||||
FIFO_READY = 0x0002,
|
FIFO_READY = 0x0002,
|
||||||
@ -959,7 +961,12 @@ static int tifm_sd_probe(struct tifm_dev *sock)
|
|||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
tifm_set_drvdata(sock, mmc);
|
tifm_set_drvdata(sock, mmc);
|
||||||
host->dev = sock;
|
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,
|
tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
|
||||||
(unsigned long)host);
|
(unsigned long)host);
|
||||||
|
@ -39,7 +39,6 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
|
||||||
#include <linux/pm_qos.h>
|
#include <linux/pm_qos.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
@ -1128,7 +1127,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
|||||||
if (ret == -EPROBE_DEFER)
|
if (ret == -EPROBE_DEFER)
|
||||||
return ret;
|
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->caps2 |= pdata->capabilities2;
|
||||||
mmc->max_segs = pdata->max_segs ? : 32;
|
mmc->max_segs = pdata->max_segs ? : 32;
|
||||||
mmc->max_blk_size = TMIO_MAX_BLK_SIZE;
|
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 */
|
/* See if we also get DMA */
|
||||||
tmio_mmc_request_dma(_host, pdata);
|
tmio_mmc_request_dma(_host, pdata);
|
||||||
|
|
||||||
dev_pm_domain_start(&pdev->dev);
|
|
||||||
pm_runtime_get_noresume(&pdev->dev);
|
pm_runtime_get_noresume(&pdev->dev);
|
||||||
pm_runtime_set_active(&pdev->dev);
|
pm_runtime_set_active(&pdev->dev);
|
||||||
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
|
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_work_sync(&host->done);
|
||||||
cancel_delayed_work_sync(&host->delayed_reset_work);
|
cancel_delayed_work_sync(&host->delayed_reset_work);
|
||||||
tmio_mmc_release_dma(host);
|
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)
|
if (host->native_hotplug)
|
||||||
pm_runtime_put_noidle(&pdev->dev);
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
pm_runtime_put_sync(&pdev->dev);
|
|
||||||
pm_runtime_disable(&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);
|
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)
|
if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP)
|
||||||
host->dma_ops = &uniphier_sd_internal_dma_ops;
|
host->dma_ops = &uniphier_sd_internal_dma_ops;
|
||||||
else
|
else
|
||||||
@ -642,8 +637,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto free_host;
|
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;
|
return 0;
|
||||||
|
|
||||||
|
remove_host:
|
||||||
|
tmio_mmc_host_remove(host);
|
||||||
free_host:
|
free_host:
|
||||||
tmio_mmc_host_free(host);
|
tmio_mmc_host_free(host);
|
||||||
|
|
||||||
|
@ -136,6 +136,8 @@
|
|||||||
|
|
||||||
#define USDHI6_MIN_DMA 64
|
#define USDHI6_MIN_DMA 64
|
||||||
|
|
||||||
|
#define USDHI6_REQ_TIMEOUT_MS 4000
|
||||||
|
|
||||||
enum usdhi6_wait_for {
|
enum usdhi6_wait_for {
|
||||||
USDHI6_WAIT_FOR_REQUEST,
|
USDHI6_WAIT_FOR_REQUEST,
|
||||||
USDHI6_WAIT_FOR_CMD,
|
USDHI6_WAIT_FOR_CMD,
|
||||||
@ -1763,7 +1765,12 @@ static int usdhi6_probe(struct platform_device *pdev)
|
|||||||
host = mmc_priv(mmc);
|
host = mmc_priv(mmc);
|
||||||
host->mmc = mmc;
|
host->mmc = mmc;
|
||||||
host->wait = USDHI6_WAIT_FOR_REQUEST;
|
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);
|
host->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||||
if (IS_ERR(host->pinctrl)) {
|
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 */
|
/* some devices need a very long delay for power to stabilize */
|
||||||
#define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001
|
#define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001
|
||||||
|
|
||||||
|
#define VIA_CMD_TIMEOUT_MS 1000
|
||||||
|
|
||||||
static const struct pci_device_id via_ids[] = {
|
static const struct pci_device_id via_ids[] = {
|
||||||
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
|
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
|
||||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
|
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;
|
void __iomem *addrbase;
|
||||||
struct mmc_data *data;
|
struct mmc_data *data;
|
||||||
|
unsigned int timeout_ms;
|
||||||
u32 cmdctrl = 0;
|
u32 cmdctrl = 0;
|
||||||
|
|
||||||
WARN_ON(host->cmd);
|
WARN_ON(host->cmd);
|
||||||
|
|
||||||
data = cmd->data;
|
data = cmd->data;
|
||||||
mod_timer(&host->timer, jiffies + HZ);
|
|
||||||
host->cmd = cmd;
|
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*/
|
/*Command index*/
|
||||||
cmdctrl = cmd->opcode << 8;
|
cmdctrl = cmd->opcode << 8;
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
#include <linux/pnp.h>
|
#include <linux/pnp.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
#include <linux/mmc/host.h>
|
#include <linux/mmc/host.h>
|
||||||
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/mmc/sd.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
@ -770,22 +772,22 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||||||
* interrupts.
|
* interrupts.
|
||||||
*/
|
*/
|
||||||
switch (cmd->opcode) {
|
switch (cmd->opcode) {
|
||||||
case 11:
|
case SD_SWITCH_VOLTAGE:
|
||||||
case 17:
|
case MMC_READ_SINGLE_BLOCK:
|
||||||
case 18:
|
case MMC_READ_MULTIPLE_BLOCK:
|
||||||
case 20:
|
case MMC_WRITE_DAT_UNTIL_STOP:
|
||||||
case 24:
|
case MMC_WRITE_BLOCK:
|
||||||
case 25:
|
case MMC_WRITE_MULTIPLE_BLOCK:
|
||||||
case 26:
|
case MMC_PROGRAM_CID:
|
||||||
case 27:
|
case MMC_PROGRAM_CSD:
|
||||||
case 30:
|
case MMC_SEND_WRITE_PROT:
|
||||||
case 42:
|
case MMC_LOCK_UNLOCK:
|
||||||
case 56:
|
case MMC_GEN_CMD:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* ACMDs. We don't keep track of state, so we just treat them
|
/* ACMDs. We don't keep track of state, so we just treat them
|
||||||
* like any other command. */
|
* like any other command. */
|
||||||
case 51:
|
case SD_APP_SEND_SCR:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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;
|
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_base = (device & 0x0F00);
|
||||||
dev_id_chiprev = FIELD_GET(QCA_MANUFACTURER_ID_REV_MASK, device);
|
dev_id_chiprev = (device & 0x00FF);
|
||||||
switch (dev_id_base) {
|
switch (dev_id_base) {
|
||||||
case QCA_MANUFACTURER_ID_AR6005_BASE:
|
case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
|
||||||
if (dev_id_chiprev < 4)
|
if (dev_id_chiprev < 4)
|
||||||
mbox_info->ext_info[0].htc_ext_sz =
|
mbox_info->ext_info[0].htc_ext_sz =
|
||||||
ATH10K_HIF_MBOX0_EXT_WIDTH;
|
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 =
|
mbox_info->ext_info[0].htc_ext_sz =
|
||||||
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
||||||
break;
|
break;
|
||||||
case QCA_MANUFACTURER_ID_QCA9377_BASE:
|
case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
|
||||||
mbox_info->ext_info[0].htc_ext_sz =
|
mbox_info->ext_info[0].htc_ext_sz =
|
||||||
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
|
||||||
break;
|
break;
|
||||||
@ -2185,19 +2185,16 @@ static int ath10k_sdio_probe(struct sdio_func *func,
|
|||||||
skb_queue_head_init(&ar_sdio->rx_head);
|
skb_queue_head_init(&ar_sdio->rx_head);
|
||||||
INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
|
INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
|
||||||
|
|
||||||
dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device);
|
dev_id_base = (id->device & 0x0F00);
|
||||||
switch (dev_id_base) {
|
if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
|
||||||
case QCA_MANUFACTURER_ID_AR6005_BASE:
|
dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
|
||||||
case QCA_MANUFACTURER_ID_QCA9377_BASE:
|
|
||||||
ar->dev_id = QCA9377_1_0_DEVICE_ID;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
ath10k_err(ar, "unsupported device id %u (0x%x)\n",
|
ath10k_err(ar, "unsupported device id %u (0x%x)\n",
|
||||||
dev_id_base, id->device);
|
dev_id_base, id->device);
|
||||||
goto err_free_wq;
|
goto err_free_wq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ar->dev_id = QCA9377_1_0_DEVICE_ID;
|
||||||
ar->id.vendor = id->vendor;
|
ar->id.vendor = id->vendor;
|
||||||
ar->id.device = id->device;
|
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[] = {
|
static const struct sdio_device_id ath10k_sdio_devices[] = {
|
||||||
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
|
||||||
(QCA_SDIO_ID_AR6005_BASE | 0xA))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
|
||||||
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
|
|
||||||
(QCA_SDIO_ID_QCA9377_BASE | 0x1))},
|
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,14 +10,6 @@
|
|||||||
|
|
||||||
#define ATH10K_HIF_MBOX_BLOCK_SIZE 256
|
#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*/
|
#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/
|
||||||
|
|
||||||
/* Mailbox address in SDIO address space */
|
/* Mailbox address in SDIO address space */
|
||||||
|
@ -35,12 +35,6 @@
|
|||||||
#define MAX_SCATTER_ENTRIES_PER_REQ 16
|
#define MAX_SCATTER_ENTRIES_PER_REQ 16
|
||||||
#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
|
#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 */
|
/* Mailbox address in SDIO address space */
|
||||||
#define HIF_MBOX_BASE_ADDR 0x800
|
#define HIF_MBOX_BASE_ADDR 0x800
|
||||||
#define HIF_MBOX_WIDTH 0x800
|
#define HIF_MBOX_WIDTH 0x800
|
||||||
|
@ -799,8 +799,7 @@ static int ath6kl_sdio_config(struct ath6kl *ar)
|
|||||||
|
|
||||||
sdio_claim_host(func);
|
sdio_claim_host(func);
|
||||||
|
|
||||||
if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
|
if (ar_sdio->id->device >= SDIO_DEVICE_ID_ATHEROS_AR6003_00) {
|
||||||
MANUFACTURER_ID_AR6003_BASE) {
|
|
||||||
/* enable 4-bit ASYNC interrupt on AR6003 or later */
|
/* enable 4-bit ASYNC interrupt on AR6003 or later */
|
||||||
ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
|
ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
|
||||||
CCCR_SDIO_IRQ_MODE_REG,
|
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[] = {
|
static const struct sdio_device_id ath6kl_sdio_devices[] = {
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_00)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_01)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_00)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_01)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_02)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
|
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_18)},
|
||||||
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x19))},
|
{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[] = {
|
static const struct sdio_device_id b43_sdio_ids[] = {
|
||||||
{ SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII) },
|
||||||
{ SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */
|
{ 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_4354),
|
||||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
|
||||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
|
||||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373),
|
||||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012),
|
||||||
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359),
|
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359),
|
||||||
{ /* end: all zeroes */ }
|
{ /* end: all zeroes */ }
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
|
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);
|
bus->hostintmask, NULL);
|
||||||
|
|
||||||
switch (sdiod->func1->device) {
|
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",
|
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
||||||
CY_4373_F2_WATERMARK);
|
CY_4373_F2_WATERMARK);
|
||||||
brcmf_sdiod_writeb(sdiod, SBSDIO_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 |
|
CY_4373_F2_WATERMARK |
|
||||||
SBSDIO_MESBUSYCTRL_ENAB, &err);
|
SBSDIO_MESBUSYCTRL_ENAB, &err);
|
||||||
break;
|
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",
|
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
|
||||||
CY_43012_F2_WATERMARK);
|
CY_43012_F2_WATERMARK);
|
||||||
brcmf_sdiod_writeb(sdiod, SBSDIO_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(SDIO_VENDOR_ID_MARVELL,
|
||||||
SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
|
SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
|
||||||
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
|
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
|
||||||
SDIO_DEVICE_ID_MARVELL_8688WLAN) },
|
SDIO_DEVICE_ID_MARVELL_8688_WLAN) },
|
||||||
{ /* end: all zeroes */ },
|
{ /* end: all zeroes */ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -480,45 +480,25 @@ static void mwifiex_sdio_coredump(struct device *dev)
|
|||||||
schedule_work(&card->work);
|
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 */
|
/* WLAN IDs */
|
||||||
static const struct sdio_device_id mwifiex_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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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},
|
.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_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) |
|
||||||
((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 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_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_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_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) |
|
||||||
((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 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_request request = {0};
|
||||||
struct gb_sdio_command_response response;
|
struct gb_sdio_command_response response;
|
||||||
struct mmc_data *data = host->mrq->data;
|
struct mmc_data *data = host->mrq->data;
|
||||||
|
unsigned int timeout_ms;
|
||||||
u8 cmd_flags;
|
u8 cmd_flags;
|
||||||
u8 cmd_type;
|
u8 cmd_type;
|
||||||
int i;
|
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);
|
request.data_blksz = cpu_to_le16(data->blksz);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND,
|
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
|
||||||
&request, sizeof(request), &response,
|
GB_OPERATION_TIMEOUT_DEFAULT;
|
||||||
sizeof(response));
|
|
||||||
|
ret = gb_operation_sync_timeout(host->connection, GB_SDIO_TYPE_COMMAND,
|
||||||
|
&request, sizeof(request), &response,
|
||||||
|
sizeof(response), timeout_ms);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ struct memstick_host {
|
|||||||
int (*set_param)(struct memstick_host *host,
|
int (*set_param)(struct memstick_host *host,
|
||||||
enum memstick_param param,
|
enum memstick_param param,
|
||||||
int value);
|
int value);
|
||||||
unsigned long private[0] ____cacheline_aligned;
|
unsigned long private[] ____cacheline_aligned;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct memstick_driver {
|
struct memstick_driver {
|
||||||
|
@ -48,6 +48,7 @@ struct mmc_ext_csd {
|
|||||||
u8 sec_feature_support;
|
u8 sec_feature_support;
|
||||||
u8 rel_sectors;
|
u8 rel_sectors;
|
||||||
u8 rel_param;
|
u8 rel_param;
|
||||||
|
bool enhanced_rpmb_supported;
|
||||||
u8 part_config;
|
u8 part_config;
|
||||||
u8 cache_ctrl;
|
u8 cache_ctrl;
|
||||||
u8 rst_n_function;
|
u8 rst_n_function;
|
||||||
|
@ -92,6 +92,9 @@ struct mmc_host_ops {
|
|||||||
int err);
|
int err);
|
||||||
void (*pre_req)(struct mmc_host *host, struct mmc_request *req);
|
void (*pre_req)(struct mmc_host *host, struct mmc_request *req);
|
||||||
void (*request)(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
|
* 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_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */
|
||||||
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
|
#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_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_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_8V_DDR (1 << 12) /* Host supports eMMC DDR 1.8V */
|
||||||
#define MMC_CAP_1_2V_DDR (1 << 13) /* Host supports eMMC DDR 1.2V */
|
#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 (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_PWR_WP_DIS (0x40)
|
||||||
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)
|
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)
|
||||||
|
@ -24,27 +24,9 @@
|
|||||||
/*
|
/*
|
||||||
* Vendors and devices. Sort key: vendor first, device next.
|
* Vendors and devices. Sort key: vendor first, device next.
|
||||||
*/
|
*/
|
||||||
#define SDIO_VENDOR_ID_BROADCOM 0x02d0
|
|
||||||
#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
|
#define SDIO_VENDOR_ID_STE 0x0020
|
||||||
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
|
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
|
||||||
#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_INTEL 0x0089
|
#define SDIO_VENDOR_ID_INTEL 0x0089
|
||||||
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
|
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
|
||||||
@ -54,29 +36,89 @@
|
|||||||
#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406
|
#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406
|
||||||
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
|
#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_VENDOR_ID_MARVELL 0x02df
|
||||||
#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
|
#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
|
||||||
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
|
#define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104
|
||||||
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
|
#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_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_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_VENDOR_ID_SIANO 0x039a
|
||||||
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
|
||||||
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
|
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
|
||||||
#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
|
#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
|
||||||
#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
|
#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_NOVA_A0 0x1100
|
||||||
#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
|
#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_VENDOR_ID_TI_WL1251 0x104c
|
||||||
#define SDIO_DEVICE_ID_TI_WL1251 0x9066
|
#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 */
|
#endif /* LINUX_MMC_SDIO_IDS_H */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
/* 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
|
#ifndef __ASM_ARCH_IMX_ESDHC_H
|
||||||
|
17
include/linux/platform_data/mmc-esdhc-mcf.h
Normal file
17
include/linux/platform_data/mmc-esdhc-mcf.h
Normal file
@ -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
|
#define LINUX_MMC_IOCTL_H
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/major.h>
|
||||||
|
|
||||||
struct mmc_ioc_cmd {
|
struct mmc_ioc_cmd {
|
||||||
/*
|
/*
|
||||||
|
Loading…
Reference in New Issue
Block a user