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:
Linus Torvalds 2020-06-02 12:48:58 -07:00
commit c5d6c13843
98 changed files with 3886 additions and 801 deletions

View file

@ -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";
};

View file

@ -18,12 +18,21 @@ Required Properties:
- "xlnx,zynqmp-8.9a": ZynqMP SDHCI 8.9a PHY
For this device it is strongly suggested to include clock-output-names and
#clock-cells.
- "xlnx,versal-8.9a": Versal SDHCI 8.9a PHY
For this device it is strongly suggested to include clock-output-names and
#clock-cells.
- "ti,am654-sdhci-5.1", "arasan,sdhci-5.1": TI AM654 MMC PHY
Note: This binding has been deprecated and moved to [5].
- "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel LGM eMMC PHY
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
- "intel,lgm-sdhci-5.1-sdxc", "arasan,sdhci-5.1": Intel LGM SDXC PHY
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
- "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1": Intel Keem Bay eMMC
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
- "intel,keembay-sdhci-5.1-sd": Intel Keem Bay SD controller
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
- "intel,keembay-sdhci-5.1-sdio": Intel Keem Bay SDIO controller
For this device it is strongly suggested to include arasan,soc-ctl-syscon.
[5] Documentation/devicetree/bindings/mmc/sdhci-am654.txt
@ -104,6 +113,18 @@ Example:
clk-phase-sd-hs = <63>, <72>;
};
sdhci: mmc@f1040000 {
compatible = "xlnx,versal-8.9a", "arasan,sdhci-8.9a";
interrupt-parent = <&gic>;
interrupts = <0 126 4>;
reg = <0x0 0xf1040000 0x0 0x10000>;
clocks = <&clk200>, <&clk200>;
clock-names = "clk_xin", "clk_ahb";
clock-output-names = "clk_out_sd0", "clk_in_sd0";
#clock-cells = <1>;
clk-phase-sd-hs = <132>, <60>;
};
emmc: sdhci@ec700000 {
compatible = "intel,lgm-sdhci-5.1-emmc", "arasan,sdhci-5.1";
reg = <0xec700000 0x300>;
@ -133,3 +154,39 @@ Example:
phy-names = "phy_arasan";
arasan,soc-ctl-syscon = <&sysconf>;
};
mmc: mmc@33000000 {
compatible = "intel,keembay-sdhci-5.1-emmc", "arasan,sdhci-5.1";
interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0x33000000 0x0 0x300>;
clock-names = "clk_xin", "clk_ahb";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>,
<&scmi_clk KEEM_BAY_PSS_EMMC>;
phys = <&emmc_phy>;
phy-names = "phy_arasan";
assigned-clocks = <&scmi_clk KEEM_BAY_PSS_AUX_EMMC>;
assigned-clock-rates = <200000000>;
clock-output-names = "emmc_cardclock";
#clock-cells = <0>;
arasan,soc-ctl-syscon = <&mmc_phy_syscon>;
};
sd0: mmc@31000000 {
compatible = "intel,keembay-sdhci-5.1-sd";
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0x31000000 0x0 0x300>;
clock-names = "clk_xin", "clk_ahb";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD0>,
<&scmi_clk KEEM_BAY_PSS_SD0>;
arasan,soc-ctl-syscon = <&sd0_phy_syscon>;
};
sd1: mmc@32000000 {
compatible = "intel,keembay-sdhci-5.1-sdio";
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
reg = <0x0 0x32000000 0x0 0x300>;
clock-names = "clk_xin", "clk_ahb";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_SD1>,
<&scmi_clk KEEM_BAY_PSS_SD1>;
arasan,soc-ctl-syscon = <&sd1_phy_syscon>;
};

View file

@ -11,6 +11,7 @@ Required properties:
- "renesas,mmcif-r7s72100" for the MMCIF found in r7s72100 SoCs
- "renesas,mmcif-r8a73a4" for the MMCIF found in r8a73a4 SoCs
- "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs
- "renesas,mmcif-r8a7742" for the MMCIF found in r8a7742 SoCs
- "renesas,mmcif-r8a7743" for the MMCIF found in r8a7743 SoCs
- "renesas,mmcif-r8a7744" for the MMCIF found in r8a7744 SoCs
- "renesas,mmcif-r8a7745" for the MMCIF found in r8a7745 SoCs
@ -24,8 +25,8 @@ Required properties:
- interrupts: Some SoCs have only 1 shared interrupt, while others have either
2 or 3 individual interrupts (error, int, card detect). Below is the number
of interrupts for each SoC:
1: r8a73a4, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791, r8a7793,
r8a7794
1: r8a73a4, r8a7742, r8a7743, r8a7744, r8a7745, r8a7778, r8a7790, r8a7791,
r8a7793, r8a7794
2: r8a7740, sh73a0
3: r7s72100

View file

@ -7,6 +7,7 @@ Required properties:
"renesas,sdhi-r7s9210" - SDHI IP on R7S9210 SoC
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
"renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
"renesas,sdhi-r8a7742" - SDHI IP on R8A7742 SoC
"renesas,sdhi-r8a7743" - SDHI IP on R8A7743 SoC
"renesas,sdhi-r8a7744" - SDHI IP on R8A7744 SoC
"renesas,sdhi-r8a7745" - SDHI IP on R8A7745 SoC

View file

@ -17,6 +17,7 @@ Required properties:
"qcom,msm8916-sdhci", "qcom,sdhci-msm-v4"
"qcom,msm8992-sdhci", "qcom,sdhci-msm-v4"
"qcom,msm8996-sdhci", "qcom,sdhci-msm-v4"
"qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"
"qcom,sdm845-sdhci", "qcom,sdhci-msm-v5"
"qcom,qcs404-sdhci", "qcom,sdhci-msm-v5"
"qcom,sc7180-sdhci", "qcom,sdhci-msm-v5";
@ -46,6 +47,13 @@ Required properties:
"cal" - reference clock for RCLK delay calibration (optional)
"sleep" - sleep clock for RCLK delay calibration (optional)
- qcom,ddr-config: Certain chipsets and platforms require particular settings
for the DDR_CONFIG register. Use this field to specify the register
value as per the Hardware Programming Guide.
- qcom,dll-config: Chipset and Platform specific value. Use this field to
specify the DLL_CONFIG register value as per Hardware Programming Guide.
Example:
sdhc_1: sdhci@f9824900 {
@ -63,6 +71,9 @@ Example:
clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>;
clock-names = "core", "iface";
qcom,dll-config = <0x000f642c>;
qcom,ddr-config = <0x80040868>;
};
sdhc_2: sdhci@f98a4900 {
@ -80,4 +91,7 @@ Example:
clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>;
clock-names = "core", "iface";
qcom,dll-config = <0x0007642c>;
qcom,ddr-config = <0x80040868>;
};

View file

@ -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>;
};

View 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>;
};
...

View file

@ -6733,6 +6733,13 @@ S: Maintained
F: Documentation/devicetree/bindings/crypto/fsl-sec4.txt
F: drivers/crypto/caam/
FREESCALE COLDFIRE M5441X MMC DRIVER
M: Angelo Dureghello <angelo.dureghello@timesys.com>
L: linux-mmc@vger.kernel.org
S: Maintained
F: drivers/mmc/host/sdhci-esdhc-mcf.c
F: include/linux/platform_data/mmc-esdhc-mcf.h
FREESCALE DIU FRAMEBUFFER DRIVER
M: Timur Tabi <timur@kernel.org>
L: linux-fbdev@vger.kernel.org

View file

@ -22,6 +22,7 @@
#include <asm/mcfqspi.h>
#include <linux/platform_data/edma.h>
#include <linux/platform_data/dma-mcf-edma.h>
#include <linux/platform_data/mmc-esdhc-mcf.h>
/*
* All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
@ -551,9 +552,35 @@ static struct platform_device mcf_edma = {
.platform_data = &mcf_edma_data,
}
};
#endif /* IS_ENABLED(CONFIG_MCF_EDMA) */
#if IS_ENABLED(CONFIG_MMC)
static struct mcf_esdhc_platform_data mcf_esdhc_data = {
.max_bus_width = 4,
.cd_type = ESDHC_CD_NONE,
};
static struct resource mcf_esdhc_resources[] = {
{
.start = MCFSDHC_BASE,
.end = MCFSDHC_BASE + MCFSDHC_SIZE - 1,
.flags = IORESOURCE_MEM,
}, {
.start = MCF_IRQ_SDHC,
.end = MCF_IRQ_SDHC,
.flags = IORESOURCE_IRQ,
},
};
static struct platform_device mcf_esdhc = {
.name = "sdhci-esdhc-mcf",
.id = 0,
.num_resources = ARRAY_SIZE(mcf_esdhc_resources),
.resource = mcf_esdhc_resources,
.dev.platform_data = &mcf_esdhc_data,
};
#endif /* IS_ENABLED(CONFIG_MMC) */
static struct platform_device *mcf_devices[] __initdata = {
&mcf_uart,
#if IS_ENABLED(CONFIG_FEC)
@ -586,6 +613,9 @@ static struct platform_device *mcf_devices[] __initdata = {
#if IS_ENABLED(CONFIG_MCF_EDMA)
&mcf_edma,
#endif
#if IS_ENABLED(CONFIG_MMC)
&mcf_esdhc,
#endif
};
/*
@ -614,4 +644,3 @@ static int __init mcf_init_devices(void)
}
arch_initcall(mcf_init_devices);

View file

@ -52,7 +52,7 @@ DEFINE_CLK(0, "mcfssi.0", 47, MCF_CLK);
DEFINE_CLK(0, "pll.0", 48, MCF_CLK);
DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
DEFINE_CLK(0, "mcfssi.1", 50, MCF_CLK);
DEFINE_CLK(0, "mcfsdhc.0", 51, MCF_CLK);
DEFINE_CLK(0, "sdhci-esdhc-mcf.0", 51, MCF_CLK);
DEFINE_CLK(0, "enet-fec.0", 53, MCF_CLK);
DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
DEFINE_CLK(0, "switch.0", 55, MCF_CLK);
@ -74,6 +74,10 @@ DEFINE_CLK(1, "mcfpwm.0", 34, MCF_BUSCLK);
DEFINE_CLK(1, "sys.0", 36, MCF_BUSCLK);
DEFINE_CLK(1, "gpio.0", 37, MCF_BUSCLK);
DEFINE_CLK(2, "ipg.0", 0, MCF_CLK);
DEFINE_CLK(2, "ahb.0", 1, MCF_CLK);
DEFINE_CLK(2, "per.0", 2, MCF_CLK);
struct clk *mcf_clks[] = {
&__clk_0_2,
&__clk_0_8,
@ -131,6 +135,11 @@ struct clk *mcf_clks[] = {
&__clk_1_34,
&__clk_1_36,
&__clk_1_37,
&__clk_2_0,
&__clk_2_1,
&__clk_2_2,
NULL,
};
@ -151,6 +160,7 @@ static struct clk * const enable_clks[] __initconst = {
&__clk_0_33, /* pit.1 */
&__clk_0_37, /* eport */
&__clk_0_48, /* pll */
&__clk_0_51, /* esdhc */
&__clk_1_36, /* CCM/reset module/Power management */
&__clk_1_37, /* gpio */
@ -194,6 +204,21 @@ static struct clk * const disable_clks[] __initconst = {
&__clk_1_29, /* uart 9 */
};
static void __clk_enable2(struct clk *clk)
{
__raw_writel(__raw_readl(MCFSDHC_CLK) | (1 << clk->slot), MCFSDHC_CLK);
}
static void __clk_disable2(struct clk *clk)
{
__raw_writel(__raw_readl(MCFSDHC_CLK) & ~(1 << clk->slot), MCFSDHC_CLK);
}
struct clk_ops clk_ops2 = {
.enable = __clk_enable2,
.disable = __clk_disable2,
};
static void __init m5441x_clk_init(void)
{
unsigned i;

View file

@ -278,6 +278,13 @@
#define MCFGPIO_IRQ_VECBASE (MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
#define MCFGPIO_PIN_MAX 87
/*
* Phase Locked Loop (PLL)
*/
#define MCF_PLL_CR 0xFC0C0000
#define MCF_PLL_DR 0xFC0C0004
#define MCF_PLL_SR 0xFC0C0008
/*
* DSPI module.
*/
@ -298,5 +305,13 @@
#define MCFEDMA_IRQ_INTR16 (MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
#define MCFEDMA_IRQ_INTR56 (MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
#define MCFEDMA_IRQ_ERR (MCFINT0_VECBASE + MCFINT0_EDMA_ERR)
/*
* esdhc module.
*/
#define MCFSDHC_BASE 0xfc0cc000
#define MCFSDHC_SIZE 256
#define MCFINT2_SDHC 31
#define MCF_IRQ_SDHC (MCFINT2_VECBASE + MCFINT2_SDHC)
#define MCFSDHC_CLK (MCFSDHC_BASE + 0x2c)
#endif /* m5441xsim_h */

View file

@ -30,6 +30,8 @@ extern struct clk_ops clk_ops0;
extern struct clk_ops clk_ops1;
#endif /* MCFPM_PPMCR1 */
extern struct clk_ops clk_ops2;
#define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
static struct clk __clk_##clk_bank##_##clk_slot = { \
.name = clk_name, \

View file

@ -355,31 +355,31 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
static const struct sdio_device_id btmrvl_sdio_ids[] = {
/* Marvell SD8688 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8688_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
/* Marvell SD8787 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
/* Marvell SD8787 Bluetooth AMP device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_BT_AMP),
.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
/* Marvell SD8797 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
/* Marvell SD8887 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
/* Marvell SD8897 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
/* Marvell SD8977 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9146),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8977 },
/* Marvell SD8987 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x914A),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8987 },
/* Marvell SD8997 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_BT),
.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
{ } /* Terminating entry */

View file

@ -51,9 +51,9 @@ static const struct btmtksdio_data mt7668_data = {
};
static const struct sdio_device_id btmtksdio_table[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7663),
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7663),
.driver_data = (kernel_ulong_t)&mt7663_data },
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
{SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, SDIO_DEVICE_ID_MEDIATEK_MT7668),
.driver_data = (kernel_ulong_t)&mt7668_data },
{ } /* Terminating entry */
};

View file

@ -58,15 +58,15 @@ static const struct sdio_device_id smssdio_ids[] = {
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
.driver_data = SMS1XXX_BOARD_SIANO_VEGA},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x302),
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_MING),
.driver_data = SMS1XXX_BOARD_SIANO_MING},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x500),
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_PELE),
.driver_data = SMS1XXX_BOARD_SIANO_PELE},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x600),
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_RIO),
.driver_data = SMS1XXX_BOARD_SIANO_RIO},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x700),
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_2160),
.driver_data = SMS1XXX_BOARD_SIANO_DENVER_2160},
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, 0x800),
{SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_DENVER_1530),
.driver_data = SMS1XXX_BOARD_SIANO_DENVER_1530},
{ /* end: all zeroes */ },
};

View file

@ -93,6 +93,20 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
return retval;
}
if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) {
retval = add_uevent_var(env, "SDIO_ID=%04X:%04X",
card->cis.vendor, card->cis.device);
if (retval)
return retval;
}
/*
* SDIO (non-combo) cards are not handled by mmc_block driver and do not
* have accessible CID register which used by mmc_card_name() function.
*/
if (card->type == MMC_TYPE_SDIO)
return 0;
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
if (retval)
return retval;

View file

@ -1815,8 +1815,7 @@ int mmc_erase(struct mmc_card *card, unsigned int from, unsigned int nr,
unsigned int rem, to = from + nr;
int err;
if (!(card->host->caps & MMC_CAP_ERASE) ||
!(card->csd.cmdclass & CCC_ERASE))
if (!(card->csd.cmdclass & CCC_ERASE))
return -EOPNOTSUPP;
if (!card->erase_size)
@ -1872,8 +1871,7 @@ EXPORT_SYMBOL(mmc_erase);
int mmc_can_erase(struct mmc_card *card)
{
if ((card->host->caps & MMC_CAP_ERASE) &&
(card->csd.cmdclass & CCC_ERASE) && card->erase_size)
if (card->csd.cmdclass & CCC_ERASE && card->erase_size)
return 1;
return 0;
}

View file

@ -219,7 +219,7 @@ static int mmc_clock_opt_set(void *data, u64 val)
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
"%llu\n");
void mmc_add_host_debugfs(struct mmc_host *host)
@ -232,8 +232,8 @@ void mmc_add_host_debugfs(struct mmc_host *host)
debugfs_create_file("ios", S_IRUSR, root, host, &mmc_ios_fops);
debugfs_create_x32("caps", S_IRUSR, root, &host->caps);
debugfs_create_x32("caps2", S_IRUSR, root, &host->caps2);
debugfs_create_file("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops);
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
&mmc_clock_fops);
#ifdef CONFIG_FAIL_MMC_REQUEST
if (fail_request)

View file

@ -647,6 +647,9 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd)
mmc_hostname(card->host),
card->ext_csd.cmdq_depth);
}
card->ext_csd.enhanced_rpmb_supported =
(card->ext_csd.rel_param &
EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR);
}
out:
return err;
@ -786,6 +789,8 @@ MMC_DEV_ATTR(enhanced_area_offset, "%llu\n",
card->ext_csd.enhanced_area_offset);
MMC_DEV_ATTR(enhanced_area_size, "%u\n", card->ext_csd.enhanced_area_size);
MMC_DEV_ATTR(raw_rpmb_size_mult, "%#x\n", card->ext_csd.raw_rpmb_size_mult);
MMC_DEV_ATTR(enhanced_rpmb_supported, "%#x\n",
card->ext_csd.enhanced_rpmb_supported);
MMC_DEV_ATTR(rel_sectors, "%#x\n", card->ext_csd.rel_sectors);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
@ -843,6 +848,7 @@ static struct attribute *mmc_std_attrs[] = {
&dev_attr_enhanced_area_offset.attr,
&dev_attr_enhanced_area_size.attr,
&dev_attr_raw_rpmb_size_mult.attr,
&dev_attr_enhanced_rpmb_supported.attr,
&dev_attr_rel_sectors.attr,
&dev_attr_ocr.attr,
&dev_attr_rca.attr,

View file

@ -139,7 +139,7 @@ static const struct mmc_fixup sdio_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN,
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_F0,
add_limit_rate_quirk, 150000000),
END_FIXUP

View file

@ -136,6 +136,8 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
int min_uV, int target_uV,
int max_uV)
{
int current_uV;
/*
* Check if supported first to avoid errors since we may try several
* signal levels during power up and don't want to show errors.
@ -143,6 +145,14 @@ static int mmc_regulator_set_voltage_if_supported(struct regulator *regulator,
if (!regulator_is_supported_voltage(regulator, min_uV, max_uV))
return -EINVAL;
/*
* The voltage is already set, no need to switch.
* Return 1 to indicate that no switch happened.
*/
current_uV = regulator_get_voltage(regulator);
if (current_uV == target_uV)
return 1;
return regulator_set_voltage_triplet(regulator, min_uV, target_uV,
max_uV);
}
@ -198,9 +208,10 @@ int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios)
* voltage in two steps and try to stay close to vmmc
* with a 0.3V tolerance at first.
*/
if (!mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
min_uV, volt, max_uV))
return 0;
ret = mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
min_uV, volt, max_uV);
if (ret >= 0)
return ret;
return mmc_regulator_set_voltage_if_supported(mmc->supply.vqmmc,
2700000, volt, 3600000);

View file

@ -376,11 +376,11 @@ int mmc_sd_switch_hs(struct mmc_card *card)
if (!status)
return -ENOMEM;
err = mmc_sd_switch(card, 1, 0, 1, status);
err = mmc_sd_switch(card, 1, 0, HIGH_SPEED_BUS_SPEED, status);
if (err)
goto out;
if ((status[16] & 0xF) != 1) {
if ((status[16] & 0xF) != HIGH_SPEED_BUS_SPEED) {
pr_warn("%s: Problem switching card into high-speed mode!\n",
mmc_hostname(card->host));
err = 0;
@ -707,7 +707,12 @@ static ssize_t mmc_dsr_show(struct device *dev,
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
static struct attribute *sd_std_attrs[] = {
&dev_attr_vendor.attr,
&dev_attr_device.attr,
&dev_attr_cid.attr,
&dev_attr_csd.attr,
&dev_attr_scr.attr,
@ -726,7 +731,26 @@ static struct attribute *sd_std_attrs[] = {
&dev_attr_dsr.attr,
NULL,
};
ATTRIBUTE_GROUPS(sd_std);
static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct mmc_card *card = mmc_dev_to_card(dev);
/* CIS vendor and device ids are available only for Combo cards */
if ((attr == &dev_attr_vendor.attr || attr == &dev_attr_device.attr) &&
card->type != MMC_TYPE_SD_COMBO)
return 0;
return attr->mode;
}
static const struct attribute_group sd_std_group = {
.attrs = sd_std_attrs,
.is_visible = sd_std_is_visible,
};
__ATTRIBUTE_GROUPS(sd_std);
struct device_type sd_type = {
.groups = sd_std_groups,

View file

@ -27,6 +27,24 @@
#include "sdio_ops.h"
#include "sdio_cis.h"
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
MMC_DEV_ATTR(ocr, "0x%08x\n", card->ocr);
MMC_DEV_ATTR(rca, "0x%04x\n", card->rca);
static struct attribute *sdio_std_attrs[] = {
&dev_attr_vendor.attr,
&dev_attr_device.attr,
&dev_attr_ocr.attr,
&dev_attr_rca.attr,
NULL,
};
ATTRIBUTE_GROUPS(sdio_std);
static struct device_type sdio_type = {
.groups = sdio_std_groups,
};
static int sdio_read_fbr(struct sdio_func *func)
{
int ret;
@ -543,13 +561,33 @@ out:
return err;
}
static void mmc_sdio_resend_if_cond(struct mmc_host *host,
struct mmc_card *card)
static int mmc_sdio_pre_init(struct mmc_host *host, u32 ocr,
struct mmc_card *card)
{
if (card)
mmc_remove_card(card);
/*
* Reset the card by performing the same steps that are taken by
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
*
* sdio_reset() is technically not needed. Having just powered up the
* hardware, it should already be in reset state. However, some
* platforms (such as SD8686 on OLPC) do not instantly cut power,
* meaning that a reset is required when restoring power soon after
* powering off. It is harmless in other cases.
*
* The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
* is not necessary for non-removable cards. However, it is required
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
* harmless in other situations.
*
*/
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->ocr_avail);
mmc_remove_card(card);
mmc_send_if_cond(host, ocr);
return mmc_send_io_op_cond(host, 0, NULL);
}
/*
@ -584,7 +622,7 @@ try_again:
*/
err = mmc_send_io_op_cond(host, ocr, &rocr);
if (err)
goto err;
return err;
/*
* For SPI, enable CRC as appropriate.
@ -592,17 +630,15 @@ try_again:
if (mmc_host_is_spi(host)) {
err = mmc_spi_set_crc(host, use_spi_crc);
if (err)
goto err;
return err;
}
/*
* Allocate card structure.
*/
card = mmc_alloc_card(host, NULL);
if (IS_ERR(card)) {
err = PTR_ERR(card);
goto err;
}
card = mmc_alloc_card(host, &sdio_type);
if (IS_ERR(card))
return PTR_ERR(card);
if ((rocr & R4_MEMORY_PRESENT) &&
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
@ -610,19 +646,15 @@ try_again:
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
mmc_remove_card(card);
pr_debug("%s: Perhaps the card was replaced\n",
mmc_hostname(host));
return -ENOENT;
err = -ENOENT;
goto mismatch;
}
} else {
card->type = MMC_TYPE_SDIO;
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
mmc_remove_card(card);
pr_debug("%s: Perhaps the card was replaced\n",
mmc_hostname(host));
return -ENOENT;
err = -ENOENT;
goto mismatch;
}
}
@ -646,7 +678,7 @@ try_again:
if (rocr & ocr & R4_18V_PRESENT) {
err = mmc_set_uhs_voltage(host, ocr_card);
if (err == -EAGAIN) {
mmc_sdio_resend_if_cond(host, card);
mmc_sdio_pre_init(host, ocr_card, card);
retries--;
goto try_again;
} else if (err) {
@ -677,7 +709,7 @@ try_again:
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_get_csd(host, card);
if (err)
return err;
goto remove;
mmc_decode_cid(card);
}
@ -704,7 +736,12 @@ try_again:
mmc_set_timing(card->host, MMC_TIMING_SD_HS);
}
goto finish;
if (oldcard)
mmc_remove_card(card);
else
host->card = card;
return 0;
}
/*
@ -713,14 +750,13 @@ try_again:
*/
err = sdio_read_cccr(card, ocr);
if (err) {
mmc_sdio_resend_if_cond(host, card);
mmc_sdio_pre_init(host, ocr_card, card);
if (ocr & R4_18V_PRESENT) {
/* Retry init sequence, but without R4_18V_PRESENT. */
retries = 0;
goto try_again;
} else {
goto remove;
}
return err;
}
/*
@ -731,16 +767,14 @@ try_again:
goto remove;
if (oldcard) {
int same = (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device);
mmc_remove_card(card);
if (!same) {
pr_debug("%s: Perhaps the card was replaced\n",
mmc_hostname(host));
return -ENOENT;
if (card->cis.vendor == oldcard->cis.vendor &&
card->cis.device == oldcard->cis.device) {
mmc_remove_card(card);
card = oldcard;
} else {
err = -ENOENT;
goto mismatch;
}
card = oldcard;
}
card->ocr = ocr_card;
mmc_fixup_device(card, sdio_fixup_methods);
@ -801,16 +835,15 @@ try_again:
err = -EINVAL;
goto remove;
}
finish:
if (!oldcard)
host->card = card;
host->card = card;
return 0;
mismatch:
pr_debug("%s: Perhaps the card was replaced\n", mmc_hostname(host));
remove:
if (!oldcard)
if (oldcard != card)
mmc_remove_card(card);
err:
return err;
}
@ -818,28 +851,7 @@ static int mmc_sdio_reinit_card(struct mmc_host *host)
{
int ret;
/*
* Reset the card by performing the same steps that are taken by
* mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe.
*
* sdio_reset() is technically not needed. Having just powered up the
* hardware, it should already be in reset state. However, some
* platforms (such as SD8686 on OLPC) do not instantly cut power,
* meaning that a reset is required when restoring power soon after
* powering off. It is harmless in other cases.
*
* The CMD5 reset (mmc_send_io_op_cond()), according to the SDIO spec,
* is not necessary for non-removable cards. However, it is required
* for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and
* harmless in other situations.
*
*/
sdio_reset(host);
mmc_go_idle(host);
mmc_send_if_cond(host, host->card->ocr);
ret = mmc_send_io_op_cond(host, 0, NULL);
ret = mmc_sdio_pre_init(host, host->card->ocr, NULL);
if (ret)
return ret;

View file

@ -171,7 +171,7 @@ config MMC_SDHCI_OF_ASPEED
config MMC_SDHCI_OF_AT91
tristate "SDHCI OF support for the Atmel SDMMC controller"
depends on MMC_SDHCI_PLTFM
depends on OF
depends on OF && HAVE_CLK
help
This selects the Atmel SDMMC driver
@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
If unsure, say N.
config MMC_SDHCI_ESDHC_MCF
tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
depends on M5441x
depends on MMC_SDHCI_PLTFM
select MMC_SDHCI_IO_ACCESSORS
help
This selects the Freescale eSDHC controller support for
ColdFire mcf5441x devices.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_ESDHC_IMX
tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
depends on ARCH_MXC
@ -405,6 +418,20 @@ config MMC_MESON_GX
If you have a controller with this interface, say Y here.
config MMC_MESON_MX_SDHC
tristate "Amlogic Meson SDHC Host Controller support"
depends on (ARM && ARCH_MESON) || COMPILE_TEST
depends on COMMON_CLK
depends on OF
help
This selects support for the SDHC Host Controller on
Amlogic Meson6, Meson8, Meson8b and Meson8m2 SoCs.
The controller supports the SD/SDIO Spec 3.x and eMMC Spec 4.5x
with 1, 4, and 8 bit bus widths.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_MESON_MX_SDIO
tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support"
depends on ARCH_MESON || COMPILE_TEST

View file

@ -68,6 +68,8 @@ obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
meson-mx-sdhc-objs := meson-mx-sdhc-clkc.o meson-mx-sdhc-mmc.o
obj-$(CONFIG_MMC_MESON_MX_SDHC) += meson-mx-sdhc.o
obj-$(CONFIG_MMC_MESON_MX_SDIO) += meson-mx-sdio.o
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
@ -82,6 +84,7 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o

View file

@ -27,7 +27,6 @@
#include <linux/mutex.h>
#include <linux/scatterlist.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
@ -404,14 +403,6 @@ static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
host->mrq = req;
goldfish_mmc_prepare_data(host, req);
goldfish_mmc_start_command(host, req->cmd);
/*
* This is to avoid accidentally being detected as an SDIO card
* in mmc_attach_sdio().
*/
if (req->cmd->opcode == SD_IO_SEND_OP_COND &&
req->cmd->flags == (MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR))
req->cmd->error = -EINVAL;
}
static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
@ -482,6 +473,7 @@ static int goldfish_mmc_probe(struct platform_device *pdev)
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA;
mmc->caps2 = MMC_CAP2_NO_SDIO;
/* Use scatterlist DMA to reduce per-transfer costs.
* NOTE max_seg_size assumption that small blocks aren't

View file

@ -169,6 +169,7 @@
#define atmci_writel(port, reg, value) \
__raw_writel((value), (port)->regs + reg)
#define ATMCI_CMD_TIMEOUT_MS 2000
#define AUTOSUSPEND_DELAY 50
#define ATMCI_DATA_ERROR_FLAGS (ATMCI_DCRCE | ATMCI_DTOE | ATMCI_OVRE | ATMCI_UNRE)
@ -808,6 +809,9 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
static void atmci_send_command(struct atmel_mci *host,
struct mmc_command *cmd, u32 cmd_flags)
{
unsigned int timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
ATMCI_CMD_TIMEOUT_MS;
WARN_ON(host->cmd);
host->cmd = cmd;
@ -817,6 +821,8 @@ static void atmci_send_command(struct atmel_mci *host,
atmci_writel(host, ATMCI_ARGR, cmd->arg);
atmci_writel(host, ATMCI_CMDR, cmd_flags);
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
}
static void atmci_send_stop_cmd(struct atmel_mci *host, struct mmc_data *data)
@ -1314,8 +1320,6 @@ static void atmci_start_request(struct atmel_mci *host,
* prepared yet.)
*/
atmci_writel(host, ATMCI_IER, iflags);
mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000));
}
static void atmci_queue_request(struct atmel_mci *host,
@ -1557,6 +1561,8 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
WARN_ON(host->cmd || host->data);
del_timer(&host->timer);
/*
* Update the MMC clock rate if necessary. This may be
* necessary if set_ios() is called when a different slot is
@ -1583,8 +1589,6 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq)
host->state = STATE_IDLE;
}
del_timer(&host->timer);
spin_unlock(&host->lock);
mmc_request_done(prev_mmc, mrq);
spin_lock(&host->lock);

View file

@ -259,7 +259,7 @@ static void au1xmmc_tasklet_finish(unsigned long param)
au1xmmc_finish_request(host);
}
static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
static int au1xmmc_send_command(struct au1xmmc_host *host,
struct mmc_command *cmd, struct mmc_data *data)
{
u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
@ -302,9 +302,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
__raw_writel(cmd->arg, HOST_CMDARG(host));
wmb(); /* drain writebuffer */
if (wait)
IRQ_OFF(host, SD_CONFIG_CR);
__raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
wmb(); /* drain writebuffer */
@ -312,19 +309,6 @@ static int au1xmmc_send_command(struct au1xmmc_host *host, int wait,
while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO)
/* nop */;
/* Wait for the command to come back */
if (wait) {
u32 status = __raw_readl(HOST_STATUS(host));
while (!(status & SD_STATUS_CR))
status = __raw_readl(HOST_STATUS(host));
/* Clear the CR status */
__raw_writel(SD_STATUS_CR, HOST_STATUS(host));
IRQ_ON(host, SD_CONFIG_CR);
}
return 0;
}
@ -711,7 +695,7 @@ static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
}
if (!ret)
ret = au1xmmc_send_command(host, 0, mrq->cmd, mrq->data);
ret = au1xmmc_send_command(host, mrq->cmd, mrq->data);
if (ret) {
mrq->cmd->error = ret;

View file

@ -1280,8 +1280,7 @@ static int bcm2835_add_host(struct bcm2835_host *host)
/* host controller capabilities */
mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_ERASE |
MMC_CAP_CMD23;
MMC_CAP_NEEDS_POLL | MMC_CAP_HW_RESET | MMC_CAP_CMD23;
spin_lock_init(&host->lock);
mutex_init(&host->mutex);

View file

@ -1038,8 +1038,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host)
* Disable bounce buffers for max_segs = 1
*/
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD |
MMC_CAP_3_3V_DDR;
MMC_CAP_CMD23 | MMC_CAP_POWER_OFF_CARD | MMC_CAP_3_3V_DDR;
if (host->use_sg)
mmc->max_segs = 16;

View file

@ -10,6 +10,8 @@
#include <linux/delay.h>
#include "cb710-mmc.h"
#define CB710_MMC_REQ_TIMEOUT_MS 2000
static const u8 cb710_clock_divider_log2[8] = {
/* 1, 2, 4, 8, 16, 32, 128, 512 */
0, 1, 2, 3, 4, 5, 7, 9
@ -707,6 +709,12 @@ static int cb710_mmc_init(struct platform_device *pdev)
mmc->f_min = val >> cb710_clock_divider_log2[CB710_MAX_DIVIDER_IDX];
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
mmc->caps = MMC_CAP_4_BIT_DATA;
/*
* In cb710_wait_for_event() we use a fixed timeout of ~2s, hence let's
* inform the core about it. A future improvement should instead make
* use of the cmd->busy_timeout.
*/
mmc->max_busy_timeout = CB710_MMC_REQ_TIMEOUT_MS;
reader = mmc_priv(mmc);

View file

@ -424,7 +424,7 @@ static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc,
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
dev_err(host->dev, "Regulator set error %d\n", ret);
return ret;
}

View file

@ -1546,8 +1546,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
dev_dbg(&mmc->class_dev,
"Regulator set error %d - %s V\n",
ret, uhs & v18 ? "1.8" : "3.3");
@ -2752,12 +2751,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot)
if (host->pdata->caps)
mmc->caps = host->pdata->caps;
/*
* Support MMC_CAP_ERASE by default.
* It needs to use trim/discard/erase commands.
*/
mmc->caps |= MMC_CAP_ERASE;
if (host->pdata->pm_caps)
mmc->pm_caps = host->pdata->pm_caps;

View file

@ -108,6 +108,7 @@
#define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0)
#define JZ_MMC_CLK_RATE 24000000
#define JZ_MMC_REQ_TIMEOUT_MS 5000
enum jz4740_mmc_version {
JZ_MMC_JZ4740,
@ -440,7 +441,8 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
if (timeout == 0) {
set_bit(0, &host->waiting);
mod_timer(&host->timeout_timer, jiffies + 5*HZ);
mod_timer(&host->timeout_timer,
jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
jz4740_mmc_set_irq_enabled(host, irq, true);
return true;
}
@ -893,7 +895,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
host->state = JZ4740_MMC_STATE_READ_RESPONSE;
set_bit(0, &host->waiting);
mod_timer(&host->timeout_timer, jiffies + 5*HZ);
mod_timer(&host->timeout_timer,
jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
jz4740_mmc_send_command(host, req->cmd);
}
@ -1023,6 +1026,12 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
mmc->f_min = mmc->f_max / 128;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
/*
* We use a fixed timeout of 5s, hence inform the core about it. A
* future improvement should instead respect the cmd->busy_timeout.
*/
mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS;
mmc->max_blk_size = (1 << 10) - 1;
mmc->max_blk_count = (1 << 15) - 1;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;

View file

@ -1004,6 +1004,8 @@ static int meson_mmc_card_busy(struct mmc_host *mmc)
static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
int ret;
/* vqmmc regulator is available */
if (!IS_ERR(mmc->supply.vqmmc)) {
/*
@ -1013,7 +1015,8 @@ static int meson_mmc_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
* to 1.8v. Please make sure the regulator framework is aware
* of your own regulator constraints
*/
return mmc_regulator_set_vqmmc(mmc, ios);
ret = mmc_regulator_set_vqmmc(mmc, ios);
return ret < 0 ? ret : 0;
}
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */

View 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;
}

View 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");

View 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_ */

View file

@ -246,6 +246,9 @@ static void meson_mx_mmc_request_done(struct meson_mx_mmc_host *host)
mrq = host->mrq;
if (host->cmd->error)
meson_mx_mmc_soft_reset(host);
host->mrq = NULL;
host->cmd = NULL;
@ -561,7 +564,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
mmc->f_max = clk_round_rate(host->cfg_div_clk,
clk_get_rate(host->parent_clk));
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
mmc->ops = &meson_mx_mmc_ops;
ret = mmc_of_parse(mmc);

View file

@ -16,11 +16,20 @@
#define HSQ_NUM_SLOTS 64
#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
static void mmc_hsq_retry_handler(struct work_struct *work)
{
struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work);
struct mmc_host *mmc = hsq->mmc;
mmc->ops->request(mmc, hsq->mrq);
}
static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
{
struct mmc_host *mmc = hsq->mmc;
struct hsq_slot *slot;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&hsq->lock, flags);
@ -42,7 +51,24 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
spin_unlock_irqrestore(&hsq->lock, flags);
mmc->ops->request(mmc, hsq->mrq);
if (mmc->ops->request_atomic)
ret = mmc->ops->request_atomic(mmc, hsq->mrq);
else
mmc->ops->request(mmc, hsq->mrq);
/*
* If returning BUSY from request_atomic(), which means the card
* may be busy now, and we should change to non-atomic context to
* try again for this unusual case, to avoid time-consuming operations
* in the atomic context.
*
* Note: we just give a warning for other error cases, since the host
* driver will handle them.
*/
if (ret == -EBUSY)
schedule_work(&hsq->retry_work);
else
WARN_ON_ONCE(ret);
}
static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
@ -325,6 +351,7 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
hsq->mmc->cqe_private = hsq;
mmc->cqe_ops = &mmc_hsq_ops;
INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
spin_lock_init(&hsq->lock);
init_waitqueue_head(&hsq->wait_queue);

View file

@ -12,6 +12,7 @@ struct mmc_hsq {
wait_queue_head_t wait_queue;
struct hsq_slot *slot;
spinlock_t lock;
struct work_struct retry_work;
int next_tag;
int num_slots;

View file

@ -77,14 +77,8 @@
#define MMC_SPI_BLOCKSIZE 512
/* These fixed timeouts come from the latest SD specs, which say to ignore
* the CSD values. The R1B value is for card erase (e.g. the "I forgot the
* card's password" scenario); it's mostly applied to STOP_TRANSMISSION after
* reads which takes nowhere near that long. Older cards may be able to use
* shorter timeouts ... but why bother?
*/
#define r1b_timeout (HZ * 3)
#define MMC_SPI_R1B_TIMEOUT_MS 3000
#define MMC_SPI_INIT_TIMEOUT_MS 3000
/* One of the critical speed parameters is the amount of data which may
* be transferred in one command. If this value is too low, the SD card
@ -248,6 +242,7 @@ static char *maptype(struct mmc_command *cmd)
static int mmc_spi_response_get(struct mmc_spi_host *host,
struct mmc_command *cmd, int cs_on)
{
unsigned long timeout_ms;
u8 *cp = host->data->status;
u8 *end = cp + host->t.len;
int value = 0;
@ -346,8 +341,11 @@ checkstatus:
/* maybe we read all the busy tokens already */
while (cp < end && *cp == 0)
cp++;
if (cp == end)
mmc_spi_wait_unbusy(host, r1b_timeout);
if (cp == end) {
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
MMC_SPI_R1B_TIMEOUT_MS;
mmc_spi_wait_unbusy(host, msecs_to_jiffies(timeout_ms));
}
break;
/* SPI R2 == R1 + second status byte; SEND_STATUS
@ -1118,7 +1116,7 @@ static void mmc_spi_initsequence(struct mmc_spi_host *host)
/* Try to be very sure any previous command has completed;
* wait till not-busy, skip debris from any old commands.
*/
mmc_spi_wait_unbusy(host, r1b_timeout);
mmc_spi_wait_unbusy(host, msecs_to_jiffies(MMC_SPI_INIT_TIMEOUT_MS));
mmc_spi_readbytes(host, 10);
/*

View file

@ -1861,31 +1861,17 @@ static int mmci_get_cd(struct mmc_host *mmc)
static int mmci_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
int ret = 0;
int ret;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
switch (ios->signal_voltage) {
case MMC_SIGNAL_VOLTAGE_330:
ret = regulator_set_voltage(mmc->supply.vqmmc,
2700000, 3600000);
break;
case MMC_SIGNAL_VOLTAGE_180:
ret = regulator_set_voltage(mmc->supply.vqmmc,
1700000, 1950000);
break;
case MMC_SIGNAL_VOLTAGE_120:
ret = regulator_set_voltage(mmc->supply.vqmmc,
1100000, 1300000);
break;
}
if (!ret && host->ops && host->ops->post_sig_volt_switch)
ret = host->ops->post_sig_volt_switch(host, ios);
else if (ret)
ret = 0;
if (!ret && host->ops && host->ops->post_sig_volt_switch)
ret = host->ops->post_sig_volt_switch(host, ios);
if (ret)
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
}
if (ret < 0)
dev_warn(mmc_dev(mmc), "Voltage switch failed\n");
return ret;
}

View file

@ -119,20 +119,19 @@ static void sdmmc_idma_unprep_data(struct mmci_host *host,
static int sdmmc_idma_setup(struct mmci_host *host)
{
struct sdmmc_idma *idma;
struct device *dev = mmc_dev(host->mmc);
idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL);
idma = devm_kzalloc(dev, sizeof(*idma), GFP_KERNEL);
if (!idma)
return -ENOMEM;
host->dma_priv = idma;
if (host->variant->dma_lli) {
idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc),
SDMMC_LLI_BUF_LEN,
idma->sg_cpu = dmam_alloc_coherent(dev, SDMMC_LLI_BUF_LEN,
&idma->sg_dma, GFP_KERNEL);
if (!idma->sg_cpu) {
dev_err(mmc_dev(host->mmc),
"Failed to alloc IDMA descriptor\n");
dev_err(dev, "Failed to alloc IDMA descriptor\n");
return -ENOMEM;
}
host->mmc->max_segs = SDMMC_LLI_BUF_LEN /
@ -143,7 +142,7 @@ static int sdmmc_idma_setup(struct mmci_host *host)
host->mmc->max_seg_size = host->mmc->max_req_size;
}
return 0;
return dma_set_max_seg_size(dev, host->mmc->max_seg_size);
}
static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
@ -188,6 +187,9 @@ static int sdmmc_idma_start(struct mmci_host *host, unsigned int *datactrl)
static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data)
{
writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR);
if (!data->host_cookie)
sdmmc_idma_unprep_data(host, data, 0);
}
static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
@ -519,6 +521,7 @@ void sdmmc_variant_init(struct mmci_host *host)
struct sdmmc_dlyb *dlyb;
host->ops = &sdmmc_variant_ops;
host->pwr_reg = readl_relaxed(host->base + MMCIPOWER);
base_dlyb = devm_of_iomap(mmc_dev(host->mmc), np, 1, NULL);
if (IS_ERR(base_dlyb))

View file

@ -1369,7 +1369,7 @@ static void msdc_set_buswidth(struct msdc_host *host, u32 width)
static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct msdc_host *host = mmc_priv(mmc);
int ret = 0;
int ret;
if (!IS_ERR(mmc->supply.vqmmc)) {
if (ios->signal_voltage != MMC_SIGNAL_VOLTAGE_330 &&
@ -1379,18 +1379,19 @@ static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios)
}
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
dev_dbg(host->dev, "Regulator set error %d (%d)\n",
ret, ios->signal_voltage);
} else {
/* Apply different pinctrl settings for different signal voltage */
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
pinctrl_select_state(host->pinctrl, host->pins_uhs);
else
pinctrl_select_state(host->pinctrl, host->pins_default);
return ret;
}
/* Apply different pinctrl settings for different signal voltage */
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
pinctrl_select_state(host->pinctrl, host->pins_uhs);
else
pinctrl_select_state(host->pinctrl, host->pins_default);
}
return ret;
return 0;
}
static int msdc_card_busy(struct mmc_host *mmc)
@ -2325,7 +2326,7 @@ static int msdc_drv_probe(struct platform_device *pdev)
if (mmc->caps & MMC_CAP_SDIO_IRQ)
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps |= MMC_CAP_CMD23;
/* MMC core transfer sizes tunable parameters */
mmc->max_segs = MAX_BD_NUM;
if (host->dev_comp->support_64g)

View file

@ -752,8 +752,6 @@ static int mvsd_probe(struct platform_device *pdev)
if (maxfreq)
mmc->f_max = maxfreq;
mmc->caps |= MMC_CAP_ERASE;
spin_lock_init(&host->lock);
host->base = devm_platform_ioremap_resource(pdev, 0);

View file

@ -634,8 +634,7 @@ static int mxs_mmc_probe(struct platform_device *pdev)
/* set mmc core parameters */
mmc->ops = &mxs_mmc_ops;
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23 |
MMC_CAP_ERASE;
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL | MMC_CAP_CMD23;
host->broken_cd = of_property_read_bool(np, "broken-cd");

View file

@ -1244,7 +1244,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->caps = 0;
if (host->pdata->slots[id].wires >= 4)
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
mmc->caps |= MMC_CAP_4_BIT_DATA;
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;

View file

@ -1922,7 +1922,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23;
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_CMD23;
mmc->caps |= mmc_pdata(host)->caps;
if (mmc->caps & MMC_CAP_8_BIT_DATA)

View file

@ -92,6 +92,8 @@
#define OWL_SD_STATE_RC16ER BIT(1)
#define OWL_SD_STATE_CRC7ER BIT(0)
#define OWL_CMD_TIMEOUT_MS 30000
struct owl_mmc_host {
struct device *dev;
struct reset_control *reset;
@ -172,6 +174,7 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
struct mmc_command *cmd,
struct mmc_data *data)
{
unsigned long timeout;
u32 mode, state, resp[2];
u32 cmd_rsp_mask = 0;
@ -239,7 +242,10 @@ static void owl_mmc_send_cmd(struct owl_mmc_host *owl_host,
if (data)
return;
if (!wait_for_completion_timeout(&owl_host->sdc_complete, 30 * HZ)) {
timeout = msecs_to_jiffies(cmd->busy_timeout ? cmd->busy_timeout :
OWL_CMD_TIMEOUT_MS);
if (!wait_for_completion_timeout(&owl_host->sdc_complete, timeout)) {
dev_err(owl_host->dev, "CMD interrupt timeout\n");
cmd->error = -ETIMEDOUT;
return;

View file

@ -36,6 +36,7 @@ struct renesas_sdhi_of_data {
struct renesas_sdhi_quirks {
bool hs400_disabled;
bool hs400_4taps;
u32 hs400_bad_taps;
};
struct tmio_mmc_dma {
@ -61,8 +62,10 @@ struct renesas_sdhi {
/* Tuning values: 1 for success, 0 for failure */
DECLARE_BITMAP(taps, BITS_PER_LONG);
/* Sampling data comparison: 1 for match, 0 for mismatch */
DECLARE_BITMAP(smpcmp, BITS_PER_LONG);
unsigned int tap_num;
unsigned long tap_set;
unsigned int tap_set;
};
#define host_to_priv(host) \

View file

@ -24,6 +24,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/mmc/host.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/mfd/tmio.h>
@ -82,16 +83,11 @@ static int renesas_sdhi_clk_enable(struct tmio_mmc_host *host)
{
struct mmc_host *mmc = host->mmc;
struct renesas_sdhi *priv = host_to_priv(host);
int ret = clk_prepare_enable(priv->clk);
if (ret < 0)
return ret;
int ret;
ret = clk_prepare_enable(priv->clk_cd);
if (ret < 0) {
clk_disable_unprepare(priv->clk);
if (ret < 0)
return ret;
}
/*
* The clock driver may not know what maximum frequency
@ -197,7 +193,6 @@ static void renesas_sdhi_clk_disable(struct tmio_mmc_host *host)
{
struct renesas_sdhi *priv = host_to_priv(host);
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk_cd);
}
@ -237,7 +232,7 @@ static int renesas_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
MMC_SIGNAL_VOLTAGE_330 ? 0 : -EINVAL;
ret = mmc_regulator_set_vqmmc(host->mmc, ios);
if (ret)
if (ret < 0)
return ret;
return pinctrl_select_state(priv->pinctrl, pin_state);
@ -325,6 +320,8 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
{
struct tmio_mmc_host *host = mmc_priv(mmc);
struct renesas_sdhi *priv = host_to_priv(host);
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
@ -352,10 +349,23 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
0x4 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
/* Avoid bad TAP */
if (bad_taps & BIT(priv->tap_set)) {
u32 new_tap = (priv->tap_set + 1) % priv->tap_num;
if (priv->quirks && priv->quirks->hs400_4taps)
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
priv->tap_set / 2);
if (bad_taps & BIT(new_tap))
new_tap = (priv->tap_set - 1) % priv->tap_num;
if (bad_taps & BIT(new_tap)) {
new_tap = priv->tap_set;
dev_dbg(&host->pdev->dev, "Can't handle three bad tap in a row\n");
}
priv->tap_set = new_tap;
}
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET,
priv->tap_set / (use_4tap ? 2 : 1));
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
@ -422,20 +432,16 @@ static int renesas_sdhi_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_io
return 0;
}
#define SH_MOBILE_SDHI_MAX_TAP 3
#define SH_MOBILE_SDHI_MIN_TAP_ROW 3
static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
{
struct renesas_sdhi *priv = host_to_priv(host);
unsigned long tap_cnt; /* counter of tuning success */
unsigned long tap_start;/* start position of tuning success */
unsigned long tap_end; /* end position of tuning success */
unsigned long ntap; /* temporary counter of tuning success */
unsigned long i;
unsigned int tap_start = 0, tap_end = 0, tap_cnt = 0, rs, re, i;
unsigned int taps_size = priv->tap_num * 2, min_tap_row;
unsigned long *bitmap;
priv->doing_tune = false;
/* Clear SCC_RVSREQ */
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
/*
@ -443,42 +449,42 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host)
* result requiring the tap to be good in both runs before
* considering it for tuning selection.
*/
for (i = 0; i < priv->tap_num * 2; i++) {
for (i = 0; i < taps_size; i++) {
int offset = priv->tap_num * (i < priv->tap_num ? 1 : -1);
if (!test_bit(i, priv->taps))
clear_bit(i + offset, priv->taps);
if (!test_bit(i, priv->smpcmp))
clear_bit(i + offset, priv->smpcmp);
}
/*
* Find the longest consecutive run of successful probes. If that
* is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
* center index as the tap.
* If all TAP are OK, the sampling clock position is selected by
* identifying the change point of data.
*/
tap_cnt = 0;
ntap = 0;
tap_start = 0;
tap_end = 0;
for (i = 0; i < priv->tap_num * 2; i++) {
if (test_bit(i, priv->taps)) {
ntap++;
} else {
if (ntap > tap_cnt) {
tap_start = i - ntap;
tap_end = i - 1;
tap_cnt = ntap;
}
ntap = 0;
if (bitmap_full(priv->taps, taps_size)) {
bitmap = priv->smpcmp;
min_tap_row = 1;
} else {
bitmap = priv->taps;
min_tap_row = SH_MOBILE_SDHI_MIN_TAP_ROW;
}
/*
* Find the longest consecutive run of successful probes. If that
* is at least SH_MOBILE_SDHI_MIN_TAP_ROW probes long then use the
* center index as the tap, otherwise bail out.
*/
bitmap_for_each_set_region(bitmap, rs, re, 0, taps_size) {
if (re - rs > tap_cnt) {
tap_end = re;
tap_start = rs;
tap_cnt = tap_end - tap_start;
}
}
if (ntap > tap_cnt) {
tap_start = i - ntap;
tap_end = i - 1;
tap_cnt = ntap;
}
if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
if (tap_cnt >= min_tap_row)
priv->tap_set = (tap_start + tap_end) / 2 % priv->tap_num;
else
return -EIO;
@ -511,6 +517,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
priv->doing_tune = true;
bitmap_zero(priv->taps, priv->tap_num * 2);
bitmap_zero(priv->smpcmp, priv->tap_num * 2);
/* Issue CMD19 twice for each tap */
for (i = 0; i < 2 * priv->tap_num; i++) {
@ -519,6 +526,9 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
if (mmc_send_tuning(host->mmc, opcode, NULL) == 0)
set_bit(i, priv->taps);
if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) == 0)
set_bit(i, priv->smpcmp);
}
return renesas_sdhi_select_tuning(host);
@ -527,7 +537,7 @@ static int renesas_sdhi_execute_tuning(struct tmio_mmc_host *host, u32 opcode)
static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_4tap)
{
struct renesas_sdhi *priv = host_to_priv(host);
unsigned long new_tap = priv->tap_set;
unsigned int new_tap = priv->tap_set, error_tap = priv->tap_set;
u32 val;
val = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ);
@ -539,20 +549,32 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
/* Change TAP position according to correction status */
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
/*
* With HS400, the DAT signal is based on DS, not CLK.
* Therefore, use only CMD status.
*/
u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP) &
SH_MOBILE_SDHI_SCC_SMPCMP_CMD_ERR;
if (!smpcmp)
if (!smpcmp) {
return false; /* no error in CMD signal */
else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP)
} else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQUP) {
new_tap++;
else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN)
error_tap--;
} else if (smpcmp == SH_MOBILE_SDHI_SCC_SMPCMP_CMD_REQDOWN) {
new_tap--;
else
error_tap++;
} else {
return true; /* need retune */
}
/*
* When new_tap is a bad tap, we cannot change. Then, we compare
* with the HS200 tuning result. When smpcmp[error_tap] is OK,
* we can at least retune.
*/
if (bad_taps & BIT(new_tap % priv->tap_num))
return test_bit(error_tap % priv->tap_num, priv->smpcmp);
} else {
if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)
return true; /* need retune */
@ -705,17 +727,35 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400 = {
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
.hs400_4taps = true,
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
};
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
.hs400_disabled = true,
};
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = {
.hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7),
};
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
};
/*
* Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now.
* So, we want to treat them equally and only have a match for ES1.2 to enforce
* this if there ever will be a way to distinguish ES1.2.
*/
static const struct soc_device_attribute sdhi_quirks_match[] = {
{ .soc_id = "r8a774a1", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
{ .soc_id = "r8a7795", .revision = "ES1.*", .data = &sdhi_quirks_4tap_nohs400 },
{ .soc_id = "r8a7795", .revision = "ES2.0", .data = &sdhi_quirks_4tap },
{ .soc_id = "r8a7795", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps2367 },
{ .soc_id = "r8a7796", .revision = "ES1.[012]", .data = &sdhi_quirks_4tap_nohs400 },
{ .soc_id = "r8a7796", .revision = "ES1.*", .data = &sdhi_quirks_4tap },
{ .soc_id = "r8a7796", .revision = "ES3.*", .data = &sdhi_quirks_bad_taps1357 },
{ .soc_id = "r8a77965", .data = &sdhi_quirks_bad_taps2367 },
{ .soc_id = "r8a77980", .data = &sdhi_quirks_nohs400 },
{ /* Sentinel. */ },
};
@ -860,6 +900,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
/* All SDHI have SDIO status bits which must be 1 */
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_SETBITS;
dev_pm_domain_start(&pdev->dev);
ret = renesas_sdhi_clk_enable(host);
if (ret)
goto efree;
@ -933,10 +975,8 @@ int renesas_sdhi_probe(struct platform_device *pdev,
goto eirq;
}
dev_info(&pdev->dev, "%s base at 0x%08lx max clock rate %u MHz\n",
mmc_hostname(host->mmc), (unsigned long)
(platform_get_resource(pdev, IORESOURCE_MEM, 0)->start),
host->mmc->f_max / 1000000);
dev_info(&pdev->dev, "%s base at %pa, max clock rate %u MHz\n",
mmc_hostname(host->mmc), &res->start, host->mmc->f_max / 1000000);
return ret;

View file

@ -1347,7 +1347,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_ERASE;
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
mmc->max_current_330 = 400;
mmc->max_current_180 = 800;

View file

@ -1314,7 +1314,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
MMC_CAP_ERASE | MMC_CAP_SYNC_RUNTIME_PM;
MMC_CAP_SYNC_RUNTIME_PM;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE |
MMC_CAP2_NO_SDIO;

View file

@ -958,13 +958,6 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
{
u32 dcon, imsk, stoptries = 3;
/* write DCON register */
if (!data) {
writel(0, host->base + S3C2410_SDIDCON);
return 0;
}
if ((data->blksz & 3) != 0) {
/* We cannot deal with unaligned blocks with more than
* one block being transferred. */

View file

@ -97,6 +97,11 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
u32 tmp;
int ret;
ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
0, 10);
if (ret)
return ret;
tmp = FIELD_PREP(SDHCI_CDNS_HRS04_WDATA, data) |
FIELD_PREP(SDHCI_CDNS_HRS04_ADDR, addr);
writel(tmp, reg);
@ -111,7 +116,10 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv,
tmp &= ~SDHCI_CDNS_HRS04_WR;
writel(tmp, reg);
return 0;
ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS04_ACK),
0, 10);
return ret;
}
static unsigned int sdhci_cdns_phy_param_count(struct device_node *np)

View file

@ -8,6 +8,7 @@
* Author: Wolfram Sang <kernel@pengutronix.de>
*/
#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/delay.h>
@ -89,7 +90,8 @@
#define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
#define ESDHC_TUNING_START_TAP_MASK 0xff
#define ESDHC_TUNING_START_TAP_MASK 0x7f
#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE (1 << 7)
#define ESDHC_TUNING_STEP_MASK 0x00070000
#define ESDHC_TUNING_STEP_SHIFT 16
@ -214,6 +216,7 @@ static const struct esdhc_soc_data usdhc_imx6sl_data = {
static const struct esdhc_soc_data usdhc_imx6sll_data = {
.flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
@ -399,7 +402,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
| SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING
| (SDHCI_TUNING_MODE_3 << SDHCI_RETUNING_MODE_SHIFT);
| FIELD_PREP(SDHCI_RETUNING_MODE_MASK,
SDHCI_TUNING_MODE_3);
if (imx_data->socdata->flags & ESDHC_FLAG_HS400)
val |= SDHCI_SUPPORT_HS400;
@ -417,9 +421,9 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg)
if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) {
val = 0;
val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT;
val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT;
val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT;
val |= FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, 0xFF);
val |= FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, 0xFF);
val |= FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, 0xFF);
}
if (unlikely(reg == SDHCI_INT_STATUS)) {
@ -1313,6 +1317,18 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
tmp |= imx_data->boarddata.tuning_step
<< ESDHC_TUNING_STEP_SHIFT;
}
/* Disable the CMD CRC check for tuning, if not, need to
* add some delay after every tuning command, because
* hardware standard tuning logic will directly go to next
* step once it detect the CMD CRC error, will not wait for
* the card side to finally send out the tuning data, trigger
* the buffer read ready interrupt immediately. If usdhc send
* the next tuning command some eMMC card will stuck, can't
* response, block the tuning procedure or the first command
* after the whole tuning procedure always can't get any response.
*/
tmp |= ESDHC_TUNING_CMD_CRC_CHECK_DISABLE;
writel(tmp, host->ioaddr + ESDHC_TUNING_CTRL);
} else if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
/*
@ -1596,6 +1612,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (esdhc_is_usdhc(imx_data)) {
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR;
/* GPIO CD can be set as a wakeup source */
host->mmc->caps |= MMC_CAP_CD_WAKE;
if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200))
host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
@ -1653,8 +1673,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (err)
goto disable_ahb_clk;
host->tuning_delay = 1;
sdhci_esdhc_imx_hwinit(host);
err = sdhci_add_host(host);
@ -1731,8 +1749,14 @@ static int sdhci_esdhc_suspend(struct device *dev)
mmc_retune_needed(host->mmc);
ret = sdhci_suspend_host(host);
if (!ret)
return pinctrl_pm_select_sleep_state(dev);
if (ret)
return ret;
ret = pinctrl_pm_select_sleep_state(dev);
if (ret)
return ret;
ret = mmc_gpio_set_cd_wake(host->mmc, true);
return ret;
}
@ -1756,6 +1780,9 @@ static int sdhci_esdhc_resume(struct device *dev)
if (host->mmc->caps2 & MMC_CAP2_CQE)
ret = cqhci_resume(host->mmc);
if (!ret)
ret = mmc_gpio_set_cd_wake(host->mmc, false);
return ret;
}
#endif

View 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");

View file

@ -5,7 +5,7 @@
* Copyright (c) 2007 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
* Copyright (c) 2010 Pengutronix e.K.
* Author: Wolfram Sang <w.sang@pengutronix.de>
* Author: Wolfram Sang <kernel@pengutronix.de>
*/
#ifndef _DRIVERS_MMC_SDHCI_ESDHC_H

View file

@ -10,6 +10,7 @@
#include <linux/delay.h>
#include <linux/mmc/mmc.h>
#include <linux/pm_runtime.h>
#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/iopoll.h>
#include <linux/regulator/consumer.h>
@ -56,19 +57,27 @@
#define CORE_FLL_CYCLE_CNT BIT(18)
#define CORE_DLL_CLOCK_DISABLE BIT(21)
#define CORE_VENDOR_SPEC_POR_VAL 0xa1c
#define DLL_USR_CTL_POR_VAL 0x10800
#define ENABLE_DLL_LOCK_STATUS BIT(26)
#define FINE_TUNE_MODE_EN BIT(27)
#define BIAS_OK_SIGNAL BIT(29)
#define DLL_CONFIG_3_LOW_FREQ_VAL 0x08
#define DLL_CONFIG_3_HIGH_FREQ_VAL 0x10
#define CORE_VENDOR_SPEC_POR_VAL 0xa9c
#define CORE_CLK_PWRSAVE BIT(1)
#define CORE_HC_MCLK_SEL_DFLT (2 << 8)
#define CORE_HC_MCLK_SEL_HS400 (3 << 8)
#define CORE_HC_MCLK_SEL_MASK (3 << 8)
#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15)
#define CORE_IO_PAD_PWR_SWITCH (1 << 16)
#define CORE_IO_PAD_PWR_SWITCH_EN BIT(15)
#define CORE_IO_PAD_PWR_SWITCH BIT(16)
#define CORE_HC_SELECT_IN_EN BIT(18)
#define CORE_HC_SELECT_IN_HS400 (6 << 19)
#define CORE_HC_SELECT_IN_MASK (7 << 19)
#define CORE_3_0V_SUPPORT (1 << 25)
#define CORE_1_8V_SUPPORT (1 << 26)
#define CORE_3_0V_SUPPORT BIT(25)
#define CORE_1_8V_SUPPORT BIT(26)
#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT)
#define CORE_CSR_CDC_CTLR_CFG0 0x130
@ -156,6 +165,7 @@ struct sdhci_msm_offset {
u32 core_dll_config_3;
u32 core_ddr_config_old; /* Applicable to sdcc minor ver < 0x49 */
u32 core_ddr_config;
u32 core_dll_usr_ctl; /* Present on SDCC5.1 onwards */
};
static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
@ -185,6 +195,7 @@ static const struct sdhci_msm_offset sdhci_msm_v5_offset = {
.core_dll_config_2 = 0x254,
.core_dll_config_3 = 0x258,
.core_ddr_config = 0x25c,
.core_dll_usr_ctl = 0x388,
};
static const struct sdhci_msm_offset sdhci_msm_mci_offset = {
@ -230,6 +241,7 @@ struct sdhci_msm_variant_ops {
struct sdhci_msm_variant_info {
bool mci_removed;
bool restore_dll_config;
bool uses_tassadar_dll;
const struct sdhci_msm_variant_ops *var_ops;
const struct sdhci_msm_offset *offset;
};
@ -243,6 +255,8 @@ struct sdhci_msm_host {
struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */
unsigned long clk_rate;
struct mmc_host *mmc;
struct opp_table *opp_table;
bool has_opp_table;
bool use_14lpp_dll_reset;
bool tuning_done;
bool calibration_done;
@ -260,6 +274,9 @@ struct sdhci_msm_host {
bool use_cdr;
u32 transfer_mode;
bool updated_ddr_cfg;
bool uses_tassadar_dll;
u32 dll_config;
u32 ddr_config;
};
static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host)
@ -332,7 +349,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
int rc;
clock = msm_get_clock_rate_for_bus_mode(host, clock);
rc = clk_set_rate(core_clk, clock);
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock);
if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
mmc_hostname(host->mmc), clock,
@ -601,6 +618,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
config &= ~CORE_CLK_PWRSAVE;
writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec);
config = msm_host->dll_config;
writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config);
if (msm_host->use_14lpp_dll_reset) {
config = readl_relaxed(host->ioaddr +
msm_offset->core_dll_config);
@ -626,7 +646,9 @@ static int msm_init_cm_dll(struct sdhci_host *host)
config |= CORE_DLL_PDN;
writel_relaxed(config, host->ioaddr +
msm_offset->core_dll_config);
msm_cm_dll_set_freq(host);
if (!msm_host->dll_config)
msm_cm_dll_set_freq(host);
if (msm_host->use_14lpp_dll_reset &&
!IS_ERR_OR_NULL(msm_host->xo_clk)) {
@ -666,7 +688,8 @@ static int msm_init_cm_dll(struct sdhci_host *host)
msm_offset->core_dll_config);
if (msm_host->use_14lpp_dll_reset) {
msm_cm_dll_set_freq(host);
if (!msm_host->dll_config)
msm_cm_dll_set_freq(host);
config = readl_relaxed(host->ioaddr +
msm_offset->core_dll_config_2);
config &= ~CORE_DLL_CLOCK_DISABLE;
@ -674,6 +697,27 @@ static int msm_init_cm_dll(struct sdhci_host *host)
msm_offset->core_dll_config_2);
}
/*
* Configure DLL user control register to enable DLL status.
* This setting is applicable to SDCC v5.1 onwards only.
*/
if (msm_host->uses_tassadar_dll) {
config = DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN |
ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL;
writel_relaxed(config, host->ioaddr +
msm_offset->core_dll_usr_ctl);
config = readl_relaxed(host->ioaddr +
msm_offset->core_dll_config_3);
config &= ~0xFF;
if (msm_host->clk_rate < 150000000)
config |= DLL_CONFIG_3_LOW_FREQ_VAL;
else
config |= DLL_CONFIG_3_HIGH_FREQ_VAL;
writel_relaxed(config, host->ioaddr +
msm_offset->core_dll_config_3);
}
config = readl_relaxed(host->ioaddr +
msm_offset->core_dll_config);
config |= CORE_DLL_EN;
@ -951,7 +995,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host)
ddr_cfg_offset = msm_offset->core_ddr_config;
else
ddr_cfg_offset = msm_offset->core_ddr_config_old;
writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + ddr_cfg_offset);
writel_relaxed(msm_host->ddr_config, host->ioaddr + ddr_cfg_offset);
if (mmc->ios.enhanced_strobe) {
config = readl_relaxed(host->ioaddr +
@ -1129,6 +1173,12 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
/* Clock-Data-Recovery used to dynamically adjust RX sampling point */
msm_host->use_cdr = true;
/*
* Clear tuning_done flag before tuning to ensure proper
* HS400 settings.
*/
msm_host->tuning_done = 0;
/*
* For HS400 tuning in HS200 timing requires:
* - select MCLK/2 in VENDOR_SPEC
@ -1830,6 +1880,36 @@ static void sdhci_msm_reset(struct sdhci_host *host, u8 mask)
sdhci_reset(host, mask);
}
#define DRIVER_NAME "sdhci_msm"
#define SDHCI_MSM_DUMP(f, x...) \
pr_err("%s: " DRIVER_NAME ": " f, mmc_hostname(host->mmc), ## x)
void sdhci_msm_dump_vendor_regs(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_msm_offset *msm_offset = msm_host->offset;
SDHCI_MSM_DUMP("----------- VENDOR REGISTER DUMP -----------\n");
SDHCI_MSM_DUMP(
"DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n",
readl_relaxed(host->ioaddr + msm_offset->core_dll_status),
readl_relaxed(host->ioaddr + msm_offset->core_dll_config),
readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2));
SDHCI_MSM_DUMP(
"DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n",
readl_relaxed(host->ioaddr + msm_offset->core_dll_config_3),
readl_relaxed(host->ioaddr + msm_offset->core_dll_usr_ctl),
readl_relaxed(host->ioaddr + msm_offset->core_ddr_config));
SDHCI_MSM_DUMP(
"Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n",
readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec),
readl_relaxed(host->ioaddr +
msm_offset->core_vendor_spec_func2),
readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3));
}
static const struct sdhci_msm_variant_ops mci_var_ops = {
.msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed,
.msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed,
@ -1858,10 +1938,18 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
.offset = &sdhci_msm_v5_offset,
};
static const struct sdhci_msm_variant_info sm8250_sdhci_var = {
.mci_removed = true,
.uses_tassadar_dll = true,
.var_ops = &v5_var_ops,
.offset = &sdhci_msm_v5_offset,
};
static const struct of_device_id sdhci_msm_dt_match[] = {
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
{.compatible = "qcom,sm8250-sdhci", .data = &sm8250_sdhci_var},
{},
};
@ -1877,16 +1965,34 @@ static const struct sdhci_ops sdhci_msm_ops = {
.write_w = sdhci_msm_writew,
.write_b = sdhci_msm_writeb,
.irq = sdhci_msm_cqe_irq,
.dump_vendor_regs = sdhci_msm_dump_vendor_regs,
};
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_SINGLE_POWER_WRITE |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.ops = &sdhci_msm_ops,
};
static inline void sdhci_msm_get_of_property(struct platform_device *pdev,
struct sdhci_host *host)
{
struct device_node *node = pdev->dev.of_node;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
if (of_property_read_u32(node, "qcom,ddr-config",
&msm_host->ddr_config))
msm_host->ddr_config = DDR_CONFIG_POR_VAL;
of_property_read_u32(node, "qcom,dll-config", &msm_host->dll_config);
}
static int sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
@ -1925,10 +2031,12 @@ static int sdhci_msm_probe(struct platform_device *pdev)
msm_host->restore_dll_config = var_info->restore_dll_config;
msm_host->var_ops = var_info->var_ops;
msm_host->offset = var_info->offset;
msm_host->uses_tassadar_dll = var_info->uses_tassadar_dll;
msm_offset = msm_host->offset;
sdhci_get_of_property(pdev);
sdhci_msm_get_of_property(pdev, host);
msm_host->saved_tuning_phase = INVALID_TUNING_PHASE;
@ -1962,8 +2070,23 @@ static int sdhci_msm_probe(struct platform_device *pdev)
}
msm_host->bulk_clks[0].clk = clk;
msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
if (IS_ERR(msm_host->opp_table)) {
ret = PTR_ERR(msm_host->opp_table);
goto bus_clk_disable;
}
/* OPP table is optional */
ret = dev_pm_opp_of_add_table(&pdev->dev);
if (!ret) {
msm_host->has_opp_table = true;
} else if (ret != -ENODEV) {
dev_err(&pdev->dev, "Invalid OPP table in Device tree\n");
goto opp_cleanup;
}
/* Vote for maximum clock rate for maximum performance */
ret = clk_set_rate(clk, INT_MAX);
ret = dev_pm_opp_set_rate(&pdev->dev, INT_MAX);
if (ret)
dev_warn(&pdev->dev, "core clock boost failed\n");
@ -1980,7 +2103,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
if (ret)
goto bus_clk_disable;
goto opp_cleanup;
/*
* xo clock is needed for FLL feature of cm_dll.
@ -2117,6 +2240,10 @@ pm_runtime_disable:
clk_disable:
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
opp_cleanup:
if (msm_host->has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(msm_host->opp_table);
bus_clk_disable:
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare(msm_host->bus_clk);
@ -2135,6 +2262,9 @@ static int sdhci_msm_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead);
if (msm_host->has_opp_table)
dev_pm_opp_of_remove_table(&pdev->dev);
dev_pm_opp_put_clkname(msm_host->opp_table);
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
@ -2153,6 +2283,8 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
/* Drop the performance vote */
dev_pm_opp_set_rate(dev, 0);
clk_bulk_disable_unprepare(ARRAY_SIZE(msm_host->bulk_clks),
msm_host->bulk_clks);
@ -2175,9 +2307,11 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev)
* restore the SDR DLL settings when the clock is ungated.
*/
if (msm_host->restore_dll_config && msm_host->clk_rate)
return sdhci_msm_restore_sdr_dll_config(host);
ret = sdhci_msm_restore_sdr_dll_config(host);
return 0;
dev_pm_opp_set_rate(dev, msm_host->clk_rate);
return ret;
}
static const struct dev_pm_ops sdhci_msm_pm_ops = {

View file

@ -28,15 +28,26 @@
#include "sdhci-pltfm.h"
#define SDHCI_ARASAN_VENDOR_REGISTER 0x78
#define SDHCI_ARASAN_ITAPDLY_REGISTER 0xF0F8
#define SDHCI_ARASAN_OTAPDLY_REGISTER 0xF0FC
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define VENDOR_ENHANCED_STROBE BIT(0)
#define PHY_CLK_TOO_SLOW_HZ 400000
#define SDHCI_ITAPDLY_CHGWIN 0x200
#define SDHCI_ITAPDLY_ENABLE 0x100
#define SDHCI_OTAPDLY_ENABLE 0x40
/* Default settings for ZynqMP Clock Phases */
#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
#define VERSAL_ICLK_PHASE {0, 132, 132, 0, 132, 0, 0, 162, 90, 0, 0}
#define VERSAL_OCLK_PHASE {0, 60, 48, 0, 48, 72, 90, 36, 60, 90, 0}
/*
* On some SoCs the syscon area has a feature where the upper 16-bits of
* each 32-bit register act as a write mask for the lower 16-bits. This allows
@ -62,22 +73,36 @@ struct sdhci_arasan_soc_ctl_field {
/**
* struct sdhci_arasan_soc_ctl_map - Map in syscon to corecfg registers
*
* @baseclkfreq: Where to find corecfg_baseclkfreq
* @clockmultiplier: Where to find corecfg_clockmultiplier
* @support64b: Where to find SUPPORT64B bit
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
*
* It's up to the licensee of the Arsan IP block to make these available
* somewhere if needed. Presumably these will be scattered somewhere that's
* accessible via the syscon API.
*
* @baseclkfreq: Where to find corecfg_baseclkfreq
* @clockmultiplier: Where to find corecfg_clockmultiplier
* @hiword_update: If true, use HIWORD_UPDATE to access the syscon
*/
struct sdhci_arasan_soc_ctl_map {
struct sdhci_arasan_soc_ctl_field baseclkfreq;
struct sdhci_arasan_soc_ctl_field clockmultiplier;
struct sdhci_arasan_soc_ctl_field support64b;
bool hiword_update;
};
/**
* struct sdhci_arasan_clk_data
* struct sdhci_arasan_clk_ops - Clock Operations for Arasan SD controller
*
* @sdcardclk_ops: The output clock related operations
* @sampleclk_ops: The sample clock related operations
*/
struct sdhci_arasan_clk_ops {
const struct clk_ops *sdcardclk_ops;
const struct clk_ops *sampleclk_ops;
};
/**
* struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
*
* @sdcardclk_hw: Struct for the clock we might provide to a PHY.
* @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
* @sampleclk_hw: Struct for the clock we might provide to a PHY.
@ -103,14 +128,18 @@ struct sdhci_arasan_zynqmp_clk_data {
};
/**
* struct sdhci_arasan_data
* struct sdhci_arasan_data - Arasan Controller Data
*
* @host: Pointer to the main SDHCI host structure.
* @clk_ahb: Pointer to the AHB clock
* @phy: Pointer to the generic phy
* @is_phy_on: True if the PHY is on; false if not.
* @has_cqe: True if controller has command queuing engine.
* @clk_data: Struct for the Arasan Controller Clock Data.
* @clk_ops: Struct for the Arasan Controller Clock Operations.
* @soc_ctl_base: Pointer to regmap for syscon for soc_ctl registers.
* @soc_ctl_map: Map to get offsets into soc_ctl registers.
* @quirks: Arasan deviations from spec.
*/
struct sdhci_arasan_data {
struct sdhci_host *host;
@ -120,10 +149,11 @@ struct sdhci_arasan_data {
bool has_cqe;
struct sdhci_arasan_clk_data clk_data;
const struct sdhci_arasan_clk_ops *clk_ops;
struct regmap *soc_ctl_base;
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
unsigned int quirks; /* Arasan deviations from spec */
unsigned int quirks;
/* Controller does not have CD wired and will not function normally without */
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
@ -135,6 +165,7 @@ struct sdhci_arasan_data {
struct sdhci_arasan_of_data {
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map;
const struct sdhci_pltfm_data *pdata;
const struct sdhci_arasan_clk_ops *clk_ops;
};
static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = {
@ -155,17 +186,26 @@ static const struct sdhci_arasan_soc_ctl_map intel_lgm_sdxc_soc_ctl_map = {
.hiword_update = false,
};
static const struct sdhci_arasan_soc_ctl_map intel_keembay_soc_ctl_map = {
.baseclkfreq = { .reg = 0x0, .width = 8, .shift = 14 },
.clockmultiplier = { .reg = 0x4, .width = 8, .shift = 14 },
.support64b = { .reg = 0x4, .width = 1, .shift = 24 },
.hiword_update = false,
};
/**
* sdhci_arasan_syscon_write - Write to a field in soc_ctl registers
*
* @host: The sdhci_host
* @fld: The field to write to
* @val: The value to write
*
* This function allows writing to fields in sdhci_arasan_soc_ctl_map.
* Note that if a field is specified as not available (shift < 0) then
* this function will silently return an error code. It will be noisy
* and print errors for any other (unexpected) errors.
*
* @host: The sdhci_host
* @fld: The field to write to
* @val: The value to write
* Return: 0 on success and error value on error
*/
static int sdhci_arasan_syscon_write(struct sdhci_host *host,
const struct sdhci_arasan_soc_ctl_field *fld,
@ -335,29 +375,6 @@ static const struct sdhci_ops sdhci_arasan_ops = {
.set_power = sdhci_set_power_and_bus_voltage,
};
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
.ops = &sdhci_arasan_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
static struct sdhci_arasan_of_data sdhci_arasan_data = {
.pdata = &sdhci_arasan_pdata,
};
static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
.ops = &sdhci_arasan_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
.pdata = &sdhci_arasan_zynqmp_pdata,
};
static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
@ -414,28 +431,14 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = {
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
};
static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
.soc_ctl_map = &rk3399_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
};
static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
.soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
};
static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
.soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
};
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
* @dev: Address of the device structure
* Returns 0 on success and error value on error
*
* Put the device in a low power state.
*
* Return: 0 on success and error value on error
*/
static int sdhci_arasan_suspend(struct device *dev)
{
@ -476,9 +479,10 @@ static int sdhci_arasan_suspend(struct device *dev)
/**
* sdhci_arasan_resume - Resume method for the driver
* @dev: Address of the device structure
* Returns 0 on success and error value on error
*
* Resume operation after suspend
*
* Return: 0 on success and error value on error
*/
static int sdhci_arasan_resume(struct device *dev)
{
@ -524,54 +528,19 @@ static int sdhci_arasan_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
{
.compatible = "rockchip,rk3399-sdhci-5.1",
.data = &sdhci_arasan_rk3399_data,
},
{
.compatible = "intel,lgm-sdhci-5.1-emmc",
.data = &intel_lgm_emmc_data,
},
{
.compatible = "intel,lgm-sdhci-5.1-sdxc",
.data = &intel_lgm_sdxc_data,
},
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
.data = &sdhci_arasan_data,
},
{
.compatible = "arasan,sdhci-5.1",
.data = &sdhci_arasan_data,
},
{
.compatible = "arasan,sdhci-4.9a",
.data = &sdhci_arasan_data,
},
{
.compatible = "xlnx,zynqmp-8.9a",
.data = &sdhci_arasan_zynqmp_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
/**
* sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate
*
* @hw: Pointer to the hardware clock structure.
* @parent_rate: The parent rate (should be rate of clk_xin).
*
* Return the current actual rate of the SD card clock. This can be used
* to communicate with out PHY.
*
* @hw: Pointer to the hardware clock structure.
* @parent_rate The parent rate (should be rate of clk_xin).
* Returns the card clock rate.
* Return: The card clock rate.
*/
static unsigned long sdhci_arasan_sdcardclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
@ -589,16 +558,16 @@ static const struct clk_ops arasan_sdcardclk_ops = {
/**
* sdhci_arasan_sampleclk_recalc_rate - Return the sampling clock rate
*
* @hw: Pointer to the hardware clock structure.
* @parent_rate: The parent rate (should be rate of clk_xin).
*
* Return the current actual rate of the sampling clock. This can be used
* to communicate with out PHY.
*
* @hw: Pointer to the hardware clock structure.
* @parent_rate The parent rate (should be rate of clk_xin).
* Returns the sample clock rate.
* Return: The sample clock rate.
*/
static unsigned long sdhci_arasan_sampleclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
@ -616,14 +585,14 @@ static const struct clk_ops arasan_sampleclk_ops = {
/**
* sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
*
* @hw: Pointer to the hardware clock structure.
* @degrees: The clock phase shift between 0 - 359.
*
* Set the SD Output Clock Tap Delays for Output path
*
* @hw: Pointer to the hardware clock structure.
* @degrees The clock phase shift between 0 - 359.
* Return: 0 on success and error value on error
*/
static int sdhci_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
@ -688,14 +657,14 @@ static const struct clk_ops zynqmp_sdcardclk_ops = {
/**
* sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
*
* @hw: Pointer to the hardware clock structure.
* @degrees: The clock phase shift between 0 - 359.
*
* Set the SD Input Clock Tap Delays for Input path
*
* @hw: Pointer to the hardware clock structure.
* @degrees The clock phase shift between 0 - 359.
* Return: 0 on success and error value on error
*/
static int sdhci_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
@ -757,6 +726,152 @@ static const struct clk_ops zynqmp_sampleclk_ops = {
.set_phase = sdhci_zynqmp_sampleclk_set_phase,
};
/**
* sdhci_versal_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
*
* @hw: Pointer to the hardware clock structure.
* @degrees: The clock phase shift between 0 - 359.
*
* Set the SD Output Clock Tap Delays for Output path
*
* Return: 0 on success and error value on error
*/
static int sdhci_versal_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
struct sdhci_arasan_data *sdhci_arasan =
container_of(clk_data, struct sdhci_arasan_data, clk_data);
struct sdhci_host *host = sdhci_arasan->host;
u8 tap_delay, tap_max = 0;
/*
* This is applicable for SDHCI_SPEC_300 and above
* Versal does not set phase for <=25MHz clock.
* If degrees is zero, no need to do anything.
*/
if (host->version < SDHCI_SPEC_300 ||
host->timing == MMC_TIMING_LEGACY ||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
return 0;
switch (host->timing) {
case MMC_TIMING_MMC_HS:
case MMC_TIMING_SD_HS:
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
/* For 50MHz clock, 30 Taps are available */
tap_max = 30;
break;
case MMC_TIMING_UHS_SDR50:
/* For 100MHz clock, 15 Taps are available */
tap_max = 15;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
/* For 200MHz clock, 8 Taps are available */
tap_max = 8;
default:
break;
}
tap_delay = (degrees * tap_max) / 360;
/* Set the Clock Phase */
if (tap_delay) {
u32 regval;
regval = sdhci_readl(host, SDHCI_ARASAN_OTAPDLY_REGISTER);
regval |= SDHCI_OTAPDLY_ENABLE;
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
regval |= tap_delay;
sdhci_writel(host, regval, SDHCI_ARASAN_OTAPDLY_REGISTER);
}
return 0;
}
static const struct clk_ops versal_sdcardclk_ops = {
.recalc_rate = sdhci_arasan_sdcardclk_recalc_rate,
.set_phase = sdhci_versal_sdcardclk_set_phase,
};
/**
* sdhci_versal_sampleclk_set_phase - Set the SD Input Clock Tap Delays
*
* @hw: Pointer to the hardware clock structure.
* @degrees: The clock phase shift between 0 - 359.
*
* Set the SD Input Clock Tap Delays for Input path
*
* Return: 0 on success and error value on error
*/
static int sdhci_versal_sampleclk_set_phase(struct clk_hw *hw, int degrees)
{
struct sdhci_arasan_clk_data *clk_data =
container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
struct sdhci_arasan_data *sdhci_arasan =
container_of(clk_data, struct sdhci_arasan_data, clk_data);
struct sdhci_host *host = sdhci_arasan->host;
u8 tap_delay, tap_max = 0;
/*
* This is applicable for SDHCI_SPEC_300 and above
* Versal does not set phase for <=25MHz clock.
* If degrees is zero, no need to do anything.
*/
if (host->version < SDHCI_SPEC_300 ||
host->timing == MMC_TIMING_LEGACY ||
host->timing == MMC_TIMING_UHS_SDR12 || !degrees)
return 0;
switch (host->timing) {
case MMC_TIMING_MMC_HS:
case MMC_TIMING_SD_HS:
case MMC_TIMING_UHS_SDR25:
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
/* For 50MHz clock, 120 Taps are available */
tap_max = 120;
break;
case MMC_TIMING_UHS_SDR50:
/* For 100MHz clock, 60 Taps are available */
tap_max = 60;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
/* For 200MHz clock, 30 Taps are available */
tap_max = 30;
default:
break;
}
tap_delay = (degrees * tap_max) / 360;
/* Set the Clock Phase */
if (tap_delay) {
u32 regval;
regval = sdhci_readl(host, SDHCI_ARASAN_ITAPDLY_REGISTER);
regval |= SDHCI_ITAPDLY_CHGWIN;
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
regval |= SDHCI_ITAPDLY_ENABLE;
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
regval |= tap_delay;
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
regval &= ~SDHCI_ITAPDLY_CHGWIN;
sdhci_writel(host, regval, SDHCI_ARASAN_ITAPDLY_REGISTER);
}
return 0;
}
static const struct clk_ops versal_sampleclk_ops = {
.recalc_rate = sdhci_arasan_sampleclk_recalc_rate,
.set_phase = sdhci_versal_sampleclk_set_phase,
};
static void arasan_zynqmp_dll_reset(struct sdhci_host *host, u32 deviceid)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -804,6 +919,9 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
/**
* sdhci_arasan_update_clockmultiplier - Set corecfg_clockmultiplier
*
* @host: The sdhci_host
* @value: The value to write
*
* The corecfg_clockmultiplier is supposed to contain clock multiplier
* value of programmable clock generator.
*
@ -815,8 +933,6 @@ static int arasan_zynqmp_execute_tuning(struct mmc_host *mmc, u32 opcode)
* - The value of corecfg_clockmultiplier should sync with that of corresponding
* value reading from sdhci_capability_register. So this function is called
* once at probe time and never called again.
*
* @host: The sdhci_host
*/
static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
u32 value)
@ -843,6 +959,8 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
/**
* sdhci_arasan_update_baseclkfreq - Set corecfg_baseclkfreq
*
* @host: The sdhci_host
*
* The corecfg_baseclkfreq is supposed to contain the MHz of clk_xin. This
* function can be used to make that happen.
*
@ -854,8 +972,6 @@ static void sdhci_arasan_update_clockmultiplier(struct sdhci_host *host,
* - It's assumed that clk_xin is not dynamic and that we use the SDHCI divider
* to achieve lower clock rates. That means that this function is called once
* at probe time and never called again.
*
* @host: The sdhci_host
*/
static void sdhci_arasan_update_baseclkfreq(struct sdhci_host *host)
{
@ -919,10 +1035,10 @@ static void arasan_dt_read_clk_phase(struct device *dev,
/**
* arasan_dt_parse_clk_phases - Read Clock Delay values from DT
*
* Called at initialization to parse the values of Clock Delays.
*
* @dev: Pointer to our struct device.
* @clk_data: Pointer to the Clock Data structure
*
* Called at initialization to parse the values of Clock Delays.
*/
static void arasan_dt_parse_clk_phases(struct device *dev,
struct sdhci_arasan_clk_data *clk_data)
@ -954,6 +1070,16 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
}
}
if (of_device_is_compatible(dev->of_node, "xlnx,versal-8.9a")) {
iclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_ICLK_PHASE;
oclk_phase = (int [MMC_TIMING_MMC_HS400 + 1]) VERSAL_OCLK_PHASE;
for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) {
clk_data->clk_phase_in[i] = iclk_phase[i];
clk_data->clk_phase_out[i] = oclk_phase[i];
}
}
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
"clk-phase-legacy");
arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
@ -978,17 +1104,191 @@ static void arasan_dt_parse_clk_phases(struct device *dev,
"clk-phase-mmc-hs400");
}
static const struct sdhci_pltfm_data sdhci_arasan_pdata = {
.ops = &sdhci_arasan_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
static const struct sdhci_arasan_clk_ops arasan_clk_ops = {
.sdcardclk_ops = &arasan_sdcardclk_ops,
.sampleclk_ops = &arasan_sampleclk_ops,
};
static struct sdhci_arasan_of_data sdhci_arasan_generic_data = {
.pdata = &sdhci_arasan_pdata,
.clk_ops = &arasan_clk_ops,
};
static const struct sdhci_pltfm_data sdhci_keembay_emmc_pdata = {
.ops = &sdhci_arasan_cqe_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED |
SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 |
SDHCI_QUIRK2_STOP_WITH_TC |
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
};
static const struct sdhci_pltfm_data sdhci_keembay_sd_pdata = {
.ops = &sdhci_arasan_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED |
SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC |
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
};
static const struct sdhci_pltfm_data sdhci_keembay_sdio_pdata = {
.ops = &sdhci_arasan_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
SDHCI_QUIRK_NO_LED |
SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_32BIT_DMA_SIZE |
SDHCI_QUIRK_32BIT_ADMA_SIZE,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_HOST_OFF_CARD_ON |
SDHCI_QUIRK2_BROKEN_64_BIT_DMA,
};
static struct sdhci_arasan_of_data sdhci_arasan_rk3399_data = {
.soc_ctl_map = &rk3399_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
.clk_ops = &arasan_clk_ops,
};
static struct sdhci_arasan_of_data intel_lgm_emmc_data = {
.soc_ctl_map = &intel_lgm_emmc_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
.clk_ops = &arasan_clk_ops,
};
static struct sdhci_arasan_of_data intel_lgm_sdxc_data = {
.soc_ctl_map = &intel_lgm_sdxc_soc_ctl_map,
.pdata = &sdhci_arasan_cqe_pdata,
.clk_ops = &arasan_clk_ops,
};
static const struct sdhci_pltfm_data sdhci_arasan_zynqmp_pdata = {
.ops = &sdhci_arasan_ops,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = {
.sdcardclk_ops = &zynqmp_sdcardclk_ops,
.sampleclk_ops = &zynqmp_sampleclk_ops,
};
static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = {
.pdata = &sdhci_arasan_zynqmp_pdata,
.clk_ops = &zynqmp_clk_ops,
};
static const struct sdhci_arasan_clk_ops versal_clk_ops = {
.sdcardclk_ops = &versal_sdcardclk_ops,
.sampleclk_ops = &versal_sampleclk_ops,
};
static struct sdhci_arasan_of_data sdhci_arasan_versal_data = {
.pdata = &sdhci_arasan_zynqmp_pdata,
.clk_ops = &versal_clk_ops,
};
static struct sdhci_arasan_of_data intel_keembay_emmc_data = {
.soc_ctl_map = &intel_keembay_soc_ctl_map,
.pdata = &sdhci_keembay_emmc_pdata,
};
static struct sdhci_arasan_of_data intel_keembay_sd_data = {
.soc_ctl_map = &intel_keembay_soc_ctl_map,
.pdata = &sdhci_keembay_sd_pdata,
};
static struct sdhci_arasan_of_data intel_keembay_sdio_data = {
.soc_ctl_map = &intel_keembay_soc_ctl_map,
.pdata = &sdhci_keembay_sdio_pdata,
};
static const struct of_device_id sdhci_arasan_of_match[] = {
/* SoC-specific compatible strings w/ soc_ctl_map */
{
.compatible = "rockchip,rk3399-sdhci-5.1",
.data = &sdhci_arasan_rk3399_data,
},
{
.compatible = "intel,lgm-sdhci-5.1-emmc",
.data = &intel_lgm_emmc_data,
},
{
.compatible = "intel,lgm-sdhci-5.1-sdxc",
.data = &intel_lgm_sdxc_data,
},
{
.compatible = "intel,keembay-sdhci-5.1-emmc",
.data = &intel_keembay_emmc_data,
},
{
.compatible = "intel,keembay-sdhci-5.1-sd",
.data = &intel_keembay_sd_data,
},
{
.compatible = "intel,keembay-sdhci-5.1-sdio",
.data = &intel_keembay_sdio_data,
},
/* Generic compatible below here */
{
.compatible = "arasan,sdhci-8.9a",
.data = &sdhci_arasan_generic_data,
},
{
.compatible = "arasan,sdhci-5.1",
.data = &sdhci_arasan_generic_data,
},
{
.compatible = "arasan,sdhci-4.9a",
.data = &sdhci_arasan_generic_data,
},
{
.compatible = "xlnx,zynqmp-8.9a",
.data = &sdhci_arasan_zynqmp_data,
},
{
.compatible = "xlnx,versal-8.9a",
.data = &sdhci_arasan_versal_data,
},
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
/**
* sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
*
* Some PHY devices need to know what the actual card clock is. In order for
* them to find out, we'll provide a clock through the common clock framework
* for them to query.
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
* Returns 0 on success and error value on error
* Return: 0 on success and error value on error
*/
static int
sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
@ -1012,10 +1312,7 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
sdcardclk_init.parent_names = &parent_clk_name;
sdcardclk_init.num_parents = 1;
sdcardclk_init.flags = CLK_GET_RATE_NOCACHE;
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
sdcardclk_init.ops = &zynqmp_sdcardclk_ops;
else
sdcardclk_init.ops = &arasan_sdcardclk_ops;
sdcardclk_init.ops = sdhci_arasan->clk_ops->sdcardclk_ops;
clk_data->sdcardclk_hw.init = &sdcardclk_init;
clk_data->sdcardclk =
@ -1033,14 +1330,15 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan,
/**
* sdhci_arasan_register_sampleclk - Register the sampleclk for a PHY to use
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
*
* Some PHY devices need to know what the actual card clock is. In order for
* them to find out, we'll provide a clock through the common clock framework
* for them to query.
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
* Returns 0 on success and error value on error
* Return: 0 on success and error value on error
*/
static int
sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
@ -1064,10 +1362,7 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
sampleclk_init.parent_names = &parent_clk_name;
sampleclk_init.num_parents = 1;
sampleclk_init.flags = CLK_GET_RATE_NOCACHE;
if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
sampleclk_init.ops = &zynqmp_sampleclk_ops;
else
sampleclk_init.ops = &arasan_sampleclk_ops;
sampleclk_init.ops = sdhci_arasan->clk_ops->sampleclk_ops;
clk_data->sampleclk_hw.init = &sampleclk_init;
clk_data->sampleclk =
@ -1085,10 +1380,10 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan,
/**
* sdhci_arasan_unregister_sdclk - Undoes sdhci_arasan_register_sdclk()
*
* @dev: Pointer to our struct device.
*
* Should be called any time we're exiting and sdhci_arasan_register_sdclk()
* returned success.
*
* @dev: Pointer to our struct device.
*/
static void sdhci_arasan_unregister_sdclk(struct device *dev)
{
@ -1100,9 +1395,47 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
of_clk_del_provider(dev->of_node);
}
/**
* sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support)
*
* This should be set based on the System Address Bus.
* 0: the Core supports only 32-bit System Address Bus.
* 1: the Core supports 64-bit System Address Bus.
*
* NOTES:
* - For Keem Bay, it is required to clear this bit. Its default value is 1'b1.
* Keem Bay does not support 64-bit access.
*
* @host The sdhci_host
*/
static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
const struct sdhci_arasan_soc_ctl_map *soc_ctl_map =
sdhci_arasan->soc_ctl_map;
/* Having a map is optional */
if (!soc_ctl_map)
return;
/* If we have a map, we expect to have a syscon */
if (!sdhci_arasan->soc_ctl_base) {
pr_warn("%s: Have regmap, but no soc-ctl-syscon\n",
mmc_hostname(host->mmc));
return;
}
sdhci_arasan_syscon_write(host, &soc_ctl_map->support64b, value);
}
/**
* sdhci_arasan_register_sdclk - Register the sdcardclk for a PHY to use
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
*
* Some PHY devices need to know what the actual card clock is. In order for
* them to find out, we'll provide a clock through the common clock framework
* for them to query.
@ -1115,10 +1448,7 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev)
* to create nice clean device tree bindings and later (if needed) we can try
* re-architecting SDHCI if we see some benefit to it.
*
* @sdhci_arasan: Our private data structure.
* @clk_xin: Pointer to the functional clock
* @dev: Pointer to our struct device.
* Returns 0 on success and error value on error
* Return: 0 on success and error value on error
*/
static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
struct clk *clk_xin,
@ -1215,6 +1545,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
sdhci_arasan->host = host;
sdhci_arasan->soc_ctl_map = data->soc_ctl_map;
sdhci_arasan->clk_ops = data->clk_ops;
node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0);
if (node) {
@ -1270,6 +1601,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
"rockchip,rk3399-sdhci-5.1"))
sdhci_arasan_update_clockmultiplier(host, 0x0);
if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") ||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") ||
of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) {
sdhci_arasan_update_clockmultiplier(host, 0x0);
sdhci_arasan_update_support64b(host, 0x0);
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
}
sdhci_arasan_update_baseclkfreq(host);
ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev);

View file

@ -6,6 +6,7 @@
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
@ -120,9 +121,12 @@ static void sdhci_at91_reset(struct sdhci_host *host, u8 mask)
|| mmc_gpio_get_cd(host->mmc) >= 0)
sdhci_at91_set_force_card_detect(host);
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL))
sdhci_writel(host, SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
if (priv->cal_always_on && (mask & SDHCI_RESET_ALL)) {
u32 calcr = sdhci_readl(host, SDMMC_CALCR);
sdhci_writel(host, calcr | SDMMC_CALCR_ALWYSON | SDMMC_CALCR_EN,
SDMMC_CALCR);
}
}
static const struct sdhci_ops sdhci_at91_sama5d2_ops = {
@ -179,9 +183,9 @@ static int sdhci_at91_set_clks_presets(struct device *dev)
clk_mul = gck_rate / clk_base_rate - 1;
caps0 &= ~SDHCI_CLOCK_V3_BASE_MASK;
caps0 |= (clk_base << SDHCI_CLOCK_BASE_SHIFT) & SDHCI_CLOCK_V3_BASE_MASK;
caps0 |= FIELD_PREP(SDHCI_CLOCK_V3_BASE_MASK, clk_base);
caps1 &= ~SDHCI_CLOCK_MUL_MASK;
caps1 |= (clk_mul << SDHCI_CLOCK_MUL_SHIFT) & SDHCI_CLOCK_MUL_MASK;
caps1 |= FIELD_PREP(SDHCI_CLOCK_MUL_MASK, clk_mul);
/* Set capabilities in r/w mode. */
writel(SDMMC_CACR_KEY | SDMMC_CACR_CAPWREN, host->ioaddr + SDMMC_CACR);
writel(caps0, host->ioaddr + SDHCI_CAPABILITIES);

View file

@ -16,6 +16,9 @@
#include "sdhci-pltfm.h"
/* DWCMSHC specific Mode Select value */
#define DWCMSHC_CTRL_HS400 0x7
#define BOUNDARY_OK(addr, len) \
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
@ -46,10 +49,36 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc,
sdhci_adma_write_desc(host, desc, addr, len, cmd);
}
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
u16 ctrl_2;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
if ((timing == MMC_TIMING_MMC_HS200) ||
(timing == MMC_TIMING_UHS_SDR104))
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (timing == MMC_TIMING_UHS_SDR12)
ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
else if ((timing == MMC_TIMING_UHS_SDR25) ||
(timing == MMC_TIMING_MMC_HS))
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (timing == MMC_TIMING_UHS_SDR50)
ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
else if ((timing == MMC_TIMING_UHS_DDR50) ||
(timing == MMC_TIMING_MMC_DDR52))
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
else if (timing == MMC_TIMING_MMC_HS400)
ctrl_2 |= DWCMSHC_CTRL_HS400;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.reset = sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
@ -134,6 +163,48 @@ static int dwcmshc_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dwcmshc_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = sdhci_suspend_host(host);
if (ret)
return ret;
clk_disable_unprepare(pltfm_host->clk);
if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk);
return ret;
}
static int dwcmshc_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
if (ret)
return ret;
if (!IS_ERR(priv->bus_clk)) {
ret = clk_prepare_enable(priv->bus_clk);
if (ret)
return ret;
}
return sdhci_resume_host(host);
}
#endif
static SIMPLE_DEV_PM_OPS(dwcmshc_pmops, dwcmshc_suspend, dwcmshc_resume);
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
{ .compatible = "snps,dwcmshc-sdhci" },
{}
@ -144,6 +215,7 @@ static struct platform_driver sdhci_dwcmshc_driver = {
.driver = {
.name = "sdhci-dwcmshc",
.of_match_table = sdhci_dwcmshc_dt_ids,
.pm = &dwcmshc_pmops,
},
.probe = dwcmshc_probe,
.remove = dwcmshc_remove,

View file

@ -1135,6 +1135,40 @@ static int esdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
static void esdhc_set_uhs_signaling(struct sdhci_host *host,
unsigned int timing)
{
u32 val;
/*
* There are specific registers setting for HS400 mode.
* Clean all of them if controller is in HS400 mode to
* exit HS400 mode before re-setting any speed mode.
*/
val = sdhci_readl(host, ESDHC_TBCTL);
if (val & ESDHC_HS400_MODE) {
val = sdhci_readl(host, ESDHC_SDTIMNGCTL);
val &= ~ESDHC_FLW_CTL_BG;
sdhci_writel(host, val, ESDHC_SDTIMNGCTL);
val = sdhci_readl(host, ESDHC_SDCLKCTL);
val &= ~ESDHC_CMD_CLK_CTL;
sdhci_writel(host, val, ESDHC_SDCLKCTL);
esdhc_clock_enable(host, false);
val = sdhci_readl(host, ESDHC_TBCTL);
val &= ~ESDHC_HS400_MODE;
sdhci_writel(host, val, ESDHC_TBCTL);
esdhc_clock_enable(host, true);
val = sdhci_readl(host, ESDHC_DLLCFG0);
val &= ~(ESDHC_DLL_ENABLE | ESDHC_DLL_FREQ_SEL);
sdhci_writel(host, val, ESDHC_DLLCFG0);
val = sdhci_readl(host, ESDHC_TBCTL);
val &= ~ESDHC_HS400_WNDW_ADJUST;
sdhci_writel(host, val, ESDHC_TBCTL);
esdhc_tuning_block_enable(host, false);
}
if (timing == MMC_TIMING_MMC_HS400)
esdhc_tuning_block_enable(host, true);
else

View file

@ -249,12 +249,8 @@ static int ricoh_probe(struct sdhci_pci_chip *chip)
static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot)
{
slot->host->caps =
((0x21 << SDHCI_TIMEOUT_CLK_SHIFT)
& SDHCI_TIMEOUT_CLK_MASK) |
((0x21 << SDHCI_CLOCK_BASE_SHIFT)
& SDHCI_CLOCK_BASE_MASK) |
FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) |
FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) |
SDHCI_TIMEOUT_CLK_UNIT |
SDHCI_CAN_VDD_330 |
SDHCI_CAN_DO_HISPD |
@ -1749,6 +1745,7 @@ static const struct pci_device_id pci_ids[] = {
SDHCI_PCI_DEVICE(SYNOPSYS, DWC_MSHC, snps),
SDHCI_PCI_DEVICE(GLI, 9750, gl9750),
SDHCI_PCI_DEVICE(GLI, 9755, gl9755),
SDHCI_PCI_DEVICE(GLI, 9763E, gl9763e),
SDHCI_PCI_DEVICE_CLASS(AMD, SYSTEM_SDHCI, PCI_CLASS_MASK, amd),
/* Generic SD host controller */
{PCI_DEVICE_CLASS(SYSTEM_SDHCI, PCI_CLASS_MASK)},

View file

@ -63,6 +63,19 @@
#define SDHCI_GLI_9750_TUNING_PARAMETERS_RX_DLY GENMASK(2, 0)
#define GLI_9750_TUNING_PARAMETERS_RX_DLY_VALUE 0x1
#define SDHCI_GLI_9763E_CTRL_HS400 0x7
#define SDHCI_GLI_9763E_HS400_ES_REG 0x52C
#define SDHCI_GLI_9763E_HS400_ES_BIT BIT(8)
#define PCIE_GLI_9763E_VHS 0x884
#define GLI_9763E_VHS_REV GENMASK(19, 16)
#define GLI_9763E_VHS_REV_R 0x0
#define GLI_9763E_VHS_REV_M 0x1
#define GLI_9763E_VHS_REV_W 0x2
#define PCIE_GLI_9763E_SCR 0x8E0
#define GLI_9763E_SCR_AXI_REQ BIT(9)
#define GLI_MAX_TUNING_LOOP 40
/* Genesys Logic chipset */
@ -351,6 +364,81 @@ static int sdhci_pci_gli_resume(struct sdhci_pci_chip *chip)
}
#endif
static void gl9763e_hs400_enhanced_strobe(struct mmc_host *mmc,
struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
u32 val;
val = sdhci_readl(host, SDHCI_GLI_9763E_HS400_ES_REG);
if (ios->enhanced_strobe)
val |= SDHCI_GLI_9763E_HS400_ES_BIT;
else
val &= ~SDHCI_GLI_9763E_HS400_ES_BIT;
sdhci_writel(host, val, SDHCI_GLI_9763E_HS400_ES_REG);
}
static void sdhci_set_gl9763e_signaling(struct sdhci_host *host,
unsigned int timing)
{
u16 ctrl_2;
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
if (timing == MMC_TIMING_MMC_HS200)
ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
else if (timing == MMC_TIMING_MMC_HS)
ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
else if (timing == MMC_TIMING_MMC_DDR52)
ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
else if (timing == MMC_TIMING_MMC_HS400)
ctrl_2 |= SDHCI_GLI_9763E_CTRL_HS400;
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
}
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
{
struct pci_dev *pdev = slot->chip->pdev;
u32 value;
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_SCR, &value);
value |= GLI_9763E_SCR_AXI_REQ;
pci_write_config_dword(pdev, PCIE_GLI_9763E_SCR, value);
pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value);
value &= ~GLI_9763E_VHS_REV;
value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R);
pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value);
}
static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot)
{
struct sdhci_host *host = slot->host;
host->mmc->caps |= MMC_CAP_8_BIT_DATA |
MMC_CAP_1_8V_DDR |
MMC_CAP_NONREMOVABLE;
host->mmc->caps2 |= MMC_CAP2_HS200_1_8V_SDR |
MMC_CAP2_HS400_1_8V |
MMC_CAP2_HS400_ES |
MMC_CAP2_NO_SDIO |
MMC_CAP2_NO_SD;
gli_pcie_enable_msi(slot);
host->mmc_host_ops.hs400_enhanced_strobe =
gl9763e_hs400_enhanced_strobe;
gli_set_gl9763e(slot);
sdhci_enable_v4_mode(host);
return 0;
}
static const struct sdhci_ops sdhci_gl9755_ops = {
.set_clock = sdhci_set_clock,
.enable_dma = sdhci_pci_enable_dma,
@ -390,3 +478,21 @@ const struct sdhci_pci_fixes sdhci_gl9750 = {
.resume = sdhci_pci_gli_resume,
#endif
};
static const struct sdhci_ops sdhci_gl9763e_ops = {
.set_clock = sdhci_set_clock,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
.voltage_switch = sdhci_gli_voltage_switch,
};
const struct sdhci_pci_fixes sdhci_gl9763e = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.probe_slot = gli_probe_slot_gl9763e,
.ops = &sdhci_gl9763e_ops,
#ifdef CONFIG_PM_SLEEP
.resume = sdhci_pci_gli_resume,
#endif
};

View file

@ -494,7 +494,7 @@ static void sdhci_o2_enable_clk(struct sdhci_host *host, u16 clk)
}
}
void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
static void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;
@ -509,7 +509,7 @@ void sdhci_pci_o2_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_o2_enable_clk(host, clk);
}
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
static int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
{
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
@ -578,7 +578,7 @@ int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
return 0;
}
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
static int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
{
int ret;
u8 scratch;
@ -783,7 +783,7 @@ int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
}
#ifdef CONFIG_PM_SLEEP
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
static int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
{
sdhci_pci_o2_probe(chip);
return sdhci_pci_resume_host(chip);

View file

@ -72,6 +72,7 @@
#define PCI_DEVICE_ID_GLI_9755 0x9755
#define PCI_DEVICE_ID_GLI_9750 0x9750
#define PCI_DEVICE_ID_GLI_9763E 0xe763
/*
* PCI device class and mask
@ -195,5 +196,6 @@ extern const struct sdhci_pci_fixes sdhci_snps;
extern const struct sdhci_pci_fixes sdhci_o2;
extern const struct sdhci_pci_fixes sdhci_gl9750;
extern const struct sdhci_pci_fixes sdhci_gl9755;
extern const struct sdhci_pci_fixes sdhci_gl9763e;
#endif /* __SDHCI_PCI_H */

View file

@ -406,7 +406,8 @@ static struct sdhci_ops sdhci_sprd_ops = {
.request_done = sdhci_sprd_request_done,
};
static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void sdhci_sprd_check_auto_cmd23(struct mmc_host *mmc,
struct mmc_request *mrq)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host);
@ -422,10 +423,23 @@ static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
mrq->sbc && (mrq->sbc->arg & SDHCI_SPRD_ARG2_STUFF) &&
(host->flags & SDHCI_AUTO_CMD23))
host->flags &= ~SDHCI_AUTO_CMD23;
}
static void sdhci_sprd_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
sdhci_sprd_check_auto_cmd23(mmc, mrq);
sdhci_request(mmc, mrq);
}
static int sdhci_sprd_request_atomic(struct mmc_host *mmc,
struct mmc_request *mrq)
{
sdhci_sprd_check_auto_cmd23(mmc, mrq);
return sdhci_request_atomic(mmc, mrq);
}
static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
@ -434,7 +448,7 @@ static int sdhci_sprd_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
pr_err("%s: Switching signalling voltage failed\n",
mmc_hostname(mmc));
return ret;
@ -556,11 +570,17 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
sdhci_sprd_voltage_switch;
host->mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_ERASE | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
MMC_CAP_WAIT_WHILE_BUSY;
ret = mmc_of_parse(host->mmc);
if (ret)
goto pltfm_free;
if (!mmc_card_is_removable(host->mmc))
host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic;
else
host->always_defer_done = true;
sprd_host = TO_SPRD_HOST(host);
sdhci_sprd_phy_param_parse(sprd_host, pdev->dev.of_node);
@ -654,8 +674,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup_host;
host->always_defer_done = true;
ret = __sdhci_add_host(host);
if (ret)
goto err_cleanup_host;

View file

@ -604,6 +604,39 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
if (err)
autocal->pull_down_1v8 = 0;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-up-offset-sdr104",
&autocal->pull_up_sdr104);
if (err)
autocal->pull_up_sdr104 = autocal->pull_up_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-down-offset-sdr104",
&autocal->pull_down_sdr104);
if (err)
autocal->pull_down_sdr104 = autocal->pull_down_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-up-offset-hs400",
&autocal->pull_up_hs400);
if (err)
autocal->pull_up_hs400 = autocal->pull_up_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-down-offset-hs400",
&autocal->pull_down_hs400);
if (err)
autocal->pull_down_hs400 = autocal->pull_down_1v8;
/*
* Different fail-safe drive strength values based on the signaling
* voltage are applicable for SoCs supporting 3V3 and 1V8 pad controls.
* So, avoid reading below device tree properties for SoCs that don't
* have NVQUIRK_NEEDS_PAD_CONTROL.
*/
if (!(tegra_host->soc_data->nvquirks & NVQUIRK_NEEDS_PAD_CONTROL))
return;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-up-offset-3v3-timeout",
&autocal->pull_up_3v3_timeout);
@ -647,30 +680,6 @@ static void tegra_sdhci_parse_pad_autocal_dt(struct sdhci_host *host)
mmc_hostname(host->mmc));
autocal->pull_down_1v8_timeout = 0;
}
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-up-offset-sdr104",
&autocal->pull_up_sdr104);
if (err)
autocal->pull_up_sdr104 = autocal->pull_up_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-down-offset-sdr104",
&autocal->pull_down_sdr104);
if (err)
autocal->pull_down_sdr104 = autocal->pull_down_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-up-offset-hs400",
&autocal->pull_up_hs400);
if (err)
autocal->pull_up_hs400 = autocal->pull_up_1v8;
err = device_property_read_u32(host->mmc->parent,
"nvidia,pad-autocal-pull-down-offset-hs400",
&autocal->pull_down_hs400);
if (err)
autocal->pull_down_hs400 = autocal->pull_down_1v8;
}
static void tegra_sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)

View file

@ -48,10 +48,10 @@
static unsigned int debug_quirks = 0;
static unsigned int debug_quirks2;
static void sdhci_finish_data(struct sdhci_host *);
static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable);
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
void sdhci_dumpregs(struct sdhci_host *host)
{
SDHCI_DUMP("============ SDHCI REGISTER DUMP ===========\n");
@ -111,6 +111,9 @@ void sdhci_dumpregs(struct sdhci_host *host)
}
}
if (host->ops->dump_vendor_regs)
host->ops->dump_vendor_regs(host);
SDHCI_DUMP("============================================\n");
}
EXPORT_SYMBOL_GPL(sdhci_dumpregs);
@ -317,6 +320,7 @@ out:
static void sdhci_init(struct sdhci_host *host, int soft)
{
struct mmc_host *mmc = host->mmc;
unsigned long flags;
if (soft)
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
@ -326,7 +330,9 @@ static void sdhci_init(struct sdhci_host *host, int soft)
if (host->v4_mode)
sdhci_do_enable_v4_mode(host);
spin_lock_irqsave(&host->lock, flags);
sdhci_set_default_irqs(host);
spin_unlock_irqrestore(&host->lock, flags);
host->cqe_on = false;
@ -634,9 +640,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
}
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
/* Copy the data to the bounce buffer */
sg_copy_to_buffer(data->sg, data->sg_len,
host->bounce_buffer,
length);
if (host->ops->copy_to_bounce_buffer) {
host->ops->copy_to_bounce_buffer(host,
data, length);
} else {
sg_copy_to_buffer(data->sg, data->sg_len,
host->bounce_buffer, length);
}
}
/* Switch ownership to the DMA */
dma_sync_single_for_device(host->mmc->parent,
@ -1350,13 +1360,25 @@ static inline bool sdhci_auto_cmd12(struct sdhci_host *host,
!mrq->cap_cmd_during_tfr;
}
static inline bool sdhci_auto_cmd23(struct sdhci_host *host,
struct mmc_request *mrq)
{
return mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
}
static inline bool sdhci_manual_cmd23(struct sdhci_host *host,
struct mmc_request *mrq)
{
return mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23);
}
static inline void sdhci_auto_cmd_select(struct sdhci_host *host,
struct mmc_command *cmd,
u16 *mode)
{
bool use_cmd12 = sdhci_auto_cmd12(host, cmd->mrq) &&
(cmd->opcode != SD_IO_RW_EXTENDED);
bool use_cmd23 = cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23);
bool use_cmd23 = sdhci_auto_cmd23(host, cmd->mrq);
u16 ctrl2;
/*
@ -1416,7 +1438,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (mmc_op_multi(cmd->opcode) || data->blocks > 1) {
mode = SDHCI_TRNS_BLK_CNT_EN | SDHCI_TRNS_MULTI;
sdhci_auto_cmd_select(host, cmd, &mode);
if (cmd->mrq->sbc && (host->flags & SDHCI_AUTO_CMD23))
if (sdhci_auto_cmd23(host, cmd->mrq))
sdhci_writel(host, cmd->mrq->sbc->arg, SDHCI_ARGUMENT2);
}
@ -1466,6 +1488,9 @@ static void __sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
if (host->data_cmd && host->data_cmd->mrq == mrq)
host->data_cmd = NULL;
if (host->deferred_cmd && host->deferred_cmd->mrq == mrq)
host->deferred_cmd = NULL;
if (host->data && host->data->mrq == mrq)
host->data = NULL;
@ -1487,7 +1512,7 @@ static void sdhci_finish_mrq(struct sdhci_host *host, struct mmc_request *mrq)
queue_work(host->complete_wq, &host->complete_work);
}
static void sdhci_finish_data(struct sdhci_host *host)
static void __sdhci_finish_data(struct sdhci_host *host, bool sw_data_timeout)
{
struct mmc_command *data_cmd = host->data_cmd;
struct mmc_data *data = host->data;
@ -1539,14 +1564,31 @@ static void sdhci_finish_data(struct sdhci_host *host)
} else {
/* Avoid triggering warning in sdhci_send_command() */
host->cmd = NULL;
sdhci_send_command(host, data->stop);
if (!sdhci_send_command(host, data->stop)) {
if (sw_data_timeout) {
/*
* This is anyway a sw data timeout, so
* give up now.
*/
data->stop->error = -EIO;
__sdhci_finish_mrq(host, data->mrq);
} else {
WARN_ON(host->deferred_cmd);
host->deferred_cmd = data->stop;
}
}
}
} else {
__sdhci_finish_mrq(host, data->mrq);
}
}
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
static void sdhci_finish_data(struct sdhci_host *host)
{
__sdhci_finish_data(host, false);
}
static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
{
int flags;
u32 mask;
@ -1561,9 +1603,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
cmd->opcode == MMC_STOP_TRANSMISSION)
cmd->flags |= MMC_RSP_BUSY;
/* Wait max 10 ms */
timeout = 10;
mask = SDHCI_CMD_INHIBIT;
if (sdhci_data_line_cmd(cmd))
mask |= SDHCI_DATA_INHIBIT;
@ -1573,18 +1612,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
if (cmd->mrq->data && (cmd == cmd->mrq->data->stop))
mask &= ~SDHCI_DATA_INHIBIT;
while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) {
if (timeout == 0) {
pr_err("%s: Controller never released inhibit bit(s).\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
sdhci_finish_mrq(host, cmd->mrq);
return;
}
timeout--;
mdelay(1);
}
if (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask)
return false;
host->cmd = cmd;
host->data_timeout = 0;
@ -1606,11 +1635,13 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_set_transfer_mode(host, cmd);
if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) {
pr_err("%s: Unsupported response type!\n",
mmc_hostname(host->mmc));
cmd->error = -EINVAL;
sdhci_finish_mrq(host, cmd->mrq);
return;
WARN_ONCE(1, "Unsupported response type!\n");
/*
* This does not happen in practice because 136-bit response
* commands never have busy waiting, so rather than complicate
* the error path, just remove busy waiting and continue.
*/
cmd->flags &= ~MMC_RSP_BUSY;
}
if (!(cmd->flags & MMC_RSP_PRESENT))
@ -1645,8 +1676,61 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
sdhci_external_dma_pre_transfer(host, cmd);
sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
return true;
}
static bool sdhci_present_error(struct sdhci_host *host,
struct mmc_command *cmd, bool present)
{
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
cmd->error = -ENOMEDIUM;
return true;
}
return false;
}
static bool sdhci_send_command_retry(struct sdhci_host *host,
struct mmc_command *cmd,
unsigned long flags)
__releases(host->lock)
__acquires(host->lock)
{
struct mmc_command *deferred_cmd = host->deferred_cmd;
int timeout = 10; /* Approx. 10 ms */
bool present;
while (!sdhci_send_command(host, cmd)) {
if (!timeout--) {
pr_err("%s: Controller never released inhibit bit(s).\n",
mmc_hostname(host->mmc));
sdhci_dumpregs(host);
cmd->error = -EIO;
return false;
}
spin_unlock_irqrestore(&host->lock, flags);
usleep_range(1000, 1250);
present = host->mmc->ops->get_cd(host->mmc);
spin_lock_irqsave(&host->lock, flags);
/* A deferred command might disappear, handle that */
if (cmd == deferred_cmd && cmd != host->deferred_cmd)
return true;
if (sdhci_present_error(host, cmd, present))
return false;
}
if (cmd == host->deferred_cmd)
host->deferred_cmd = NULL;
return true;
}
EXPORT_SYMBOL_GPL(sdhci_send_command);
static void sdhci_read_rsp_136(struct sdhci_host *host, struct mmc_command *cmd)
{
@ -1707,7 +1791,10 @@ static void sdhci_finish_command(struct sdhci_host *host)
/* Finished CMD23, now send actual command. */
if (cmd == cmd->mrq->sbc) {
sdhci_send_command(host, cmd->mrq->cmd);
if (!sdhci_send_command(host, cmd->mrq->cmd)) {
WARN_ON(host->deferred_cmd);
host->deferred_cmd = cmd->mrq->cmd;
}
} else {
/* Processed actual command. */
@ -2037,11 +2124,10 @@ EXPORT_SYMBOL_GPL(sdhci_set_power_and_bus_voltage);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
int present;
struct sdhci_host *host = mmc_priv(mmc);
struct mmc_command *cmd;
unsigned long flags;
host = mmc_priv(mmc);
bool present;
/* Firstly check card presence */
present = mmc->ops->get_cd(mmc);
@ -2050,20 +2136,58 @@ void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_led_activate(host);
if (!present || host->flags & SDHCI_DEVICE_DEAD) {
mrq->cmd->error = -ENOMEDIUM;
sdhci_finish_mrq(host, mrq);
} else {
if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23))
sdhci_send_command(host, mrq->sbc);
else
sdhci_send_command(host, mrq->cmd);
}
if (sdhci_present_error(host, mrq->cmd, present))
goto out_finish;
cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
if (!sdhci_send_command_retry(host, cmd, flags))
goto out_finish;
spin_unlock_irqrestore(&host->lock, flags);
return;
out_finish:
sdhci_finish_mrq(host, mrq);
spin_unlock_irqrestore(&host->lock, flags);
}
EXPORT_SYMBOL_GPL(sdhci_request);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host = mmc_priv(mmc);
struct mmc_command *cmd;
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&host->lock, flags);
if (sdhci_present_error(host, mrq->cmd, true)) {
sdhci_finish_mrq(host, mrq);
goto out_finish;
}
cmd = sdhci_manual_cmd23(host, mrq) ? mrq->sbc : mrq->cmd;
/*
* The HSQ may send a command in interrupt context without polling
* the busy signaling, which means we should return BUSY if controller
* has not released inhibit bits to allow HSQ trying to send request
* again in non-atomic context. So we should not finish this request
* here.
*/
if (!sdhci_send_command(host, cmd))
ret = -EBUSY;
else
sdhci_led_activate(host);
out_finish:
spin_unlock_irqrestore(&host->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(sdhci_request_atomic);
void sdhci_set_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
@ -2411,7 +2535,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
mmc_hostname(mmc));
return -EIO;
@ -2434,7 +2558,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
mmc_hostname(mmc));
return -EIO;
@ -2466,7 +2590,7 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return -EINVAL;
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
if (ret) {
if (ret < 0) {
pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
mmc_hostname(mmc));
return -EIO;
@ -2600,7 +2724,11 @@ void sdhci_send_tuning(struct sdhci_host *host, u32 opcode)
*/
sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
sdhci_send_command(host, &cmd);
if (!sdhci_send_command_retry(host, &cmd, flags)) {
spin_unlock_irqrestore(&host->lock, flags);
host->tuning_done = 0;
return;
}
host->cmd = NULL;
@ -3018,7 +3146,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
if (host->data) {
host->data->error = -ETIMEDOUT;
sdhci_finish_data(host);
__sdhci_finish_data(host, true);
queue_work(host->complete_wq, &host->complete_work);
} else if (host->data_cmd) {
host->data_cmd->error = -ETIMEDOUT;
@ -3390,6 +3518,9 @@ cont:
}
}
out:
if (host->deferred_cmd)
result = IRQ_WAKE_THREAD;
spin_unlock(&host->lock);
/* Process mrqs ready for immediate completion */
@ -3415,6 +3546,7 @@ out:
static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
{
struct sdhci_host *host = dev_id;
struct mmc_command *cmd;
unsigned long flags;
u32 isr;
@ -3422,8 +3554,14 @@ static irqreturn_t sdhci_thread_irq(int irq, void *dev_id)
;
spin_lock_irqsave(&host->lock, flags);
isr = host->thread_isr;
host->thread_isr = 0;
cmd = host->deferred_cmd;
if (cmd && !sdhci_send_command_retry(host, cmd, flags))
sdhci_finish_mrq(host, cmd->mrq);
spin_unlock_irqrestore(&host->lock, flags);
if (isr & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
@ -4114,11 +4252,9 @@ int sdhci_setup_host(struct sdhci_host *host)
}
if (host->version >= SDHCI_SPEC_300)
host->max_clk = (host->caps & SDHCI_CLOCK_V3_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
else
host->max_clk = (host->caps & SDHCI_CLOCK_BASE_MASK)
>> SDHCI_CLOCK_BASE_SHIFT;
host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps);
host->max_clk *= 1000000;
if (host->max_clk == 0 || host->quirks &
@ -4136,8 +4272,7 @@ int sdhci_setup_host(struct sdhci_host *host)
* In case of Host Controller v3.00, find out whether clock
* multiplier is supported.
*/
host->clk_mul = (host->caps1 & SDHCI_CLOCK_MUL_MASK) >>
SDHCI_CLOCK_MUL_SHIFT;
host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1);
/*
* In case the value in Clock Multiplier is 0, then programmable
@ -4170,8 +4305,7 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->f_max = max_clk;
if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
host->timeout_clk = (host->caps & SDHCI_TIMEOUT_CLK_MASK) >>
SDHCI_TIMEOUT_CLK_SHIFT;
host->timeout_clk = FIELD_GET(SDHCI_TIMEOUT_CLK_MASK, host->caps);
if (host->caps & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
@ -4201,7 +4335,7 @@ int sdhci_setup_host(struct sdhci_host *host)
!host->ops->get_max_timeout_count)
mmc->max_busy_timeout = 0;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23;
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
if (host->quirks & SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12)
@ -4323,8 +4457,8 @@ int sdhci_setup_host(struct sdhci_host *host)
mmc->caps |= MMC_CAP_DRIVER_TYPE_D;
/* Initial value for re-tuning timer count */
host->tuning_count = (host->caps1 & SDHCI_RETUNING_TIMER_COUNT_MASK) >>
SDHCI_RETUNING_TIMER_COUNT_SHIFT;
host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
host->caps1);
/*
* In case Re-tuning Timer is not disabled, the actual value of
@ -4334,8 +4468,7 @@ int sdhci_setup_host(struct sdhci_host *host)
host->tuning_count = 1 << (host->tuning_count - 1);
/* Re-tuning mode supported by the Host Controller */
host->tuning_mode = (host->caps1 & SDHCI_RETUNING_MODE_MASK) >>
SDHCI_RETUNING_MODE_SHIFT;
host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
ocr_avail = 0;
@ -4357,35 +4490,32 @@ int sdhci_setup_host(struct sdhci_host *host)
curr = min_t(u32, curr, SDHCI_MAX_CURRENT_LIMIT);
max_current_caps =
(curr << SDHCI_MAX_CURRENT_330_SHIFT) |
(curr << SDHCI_MAX_CURRENT_300_SHIFT) |
(curr << SDHCI_MAX_CURRENT_180_SHIFT);
FIELD_PREP(SDHCI_MAX_CURRENT_330_MASK, curr) |
FIELD_PREP(SDHCI_MAX_CURRENT_300_MASK, curr) |
FIELD_PREP(SDHCI_MAX_CURRENT_180_MASK, curr);
}
}
if (host->caps & SDHCI_CAN_VDD_330) {
ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->max_current_330 = ((max_current_caps &
SDHCI_MAX_CURRENT_330_MASK) >>
SDHCI_MAX_CURRENT_330_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
mmc->max_current_330 = FIELD_GET(SDHCI_MAX_CURRENT_330_MASK,
max_current_caps) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
if (host->caps & SDHCI_CAN_VDD_300) {
ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31;
mmc->max_current_300 = ((max_current_caps &
SDHCI_MAX_CURRENT_300_MASK) >>
SDHCI_MAX_CURRENT_300_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
mmc->max_current_300 = FIELD_GET(SDHCI_MAX_CURRENT_300_MASK,
max_current_caps) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
if (host->caps & SDHCI_CAN_VDD_180) {
ocr_avail |= MMC_VDD_165_195;
mmc->max_current_180 = ((max_current_caps &
SDHCI_MAX_CURRENT_180_MASK) >>
SDHCI_MAX_CURRENT_180_SHIFT) *
SDHCI_MAX_CURRENT_MULTIPLIER;
mmc->max_current_180 = FIELD_GET(SDHCI_MAX_CURRENT_180_MASK,
max_current_caps) *
SDHCI_MAX_CURRENT_MULTIPLIER;
}
/* If OCR set by host, use it instead. */

View file

@ -200,12 +200,10 @@
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
#define SDHCI_TIMEOUT_CLK_SHIFT 0
#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
#define SDHCI_CLOCK_BASE_SHIFT 8
#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
#define SDHCI_MAX_BLOCK_MASK 0x00030000
#define SDHCI_MAX_BLOCK_SHIFT 16
#define SDHCI_CAN_DO_8BIT 0x00040000
@ -220,32 +218,25 @@
#define SDHCI_CAN_64BIT_V4 0x08000000
#define SDHCI_CAN_64BIT 0x10000000
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
#define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
#define SDHCI_CAN_DO_ADMA3 0x08000000
#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_MAX_CURRENT 0x48
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
#define SDHCI_MAX_CURRENT_330_SHIFT 0
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
#define SDHCI_MAX_CURRENT_300_SHIFT 8
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
#define SDHCI_MAX_CURRENT_180_SHIFT 16
#define SDHCI_MAX_CURRENT_LIMIT GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_330_MASK GENMASK(7, 0)
#define SDHCI_MAX_CURRENT_300_MASK GENMASK(15, 8)
#define SDHCI_MAX_CURRENT_180_MASK GENMASK(23, 16)
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
@ -540,6 +531,7 @@ struct sdhci_host {
struct mmc_request *mrqs_done[SDHCI_MAX_MRQS]; /* Requests done */
struct mmc_command *cmd; /* Current command */
struct mmc_command *data_cmd; /* Current data command */
struct mmc_command *deferred_cmd; /* Deferred command */
struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */
@ -653,8 +645,12 @@ struct sdhci_ops {
void (*voltage_switch)(struct sdhci_host *host);
void (*adma_write_desc)(struct sdhci_host *host, void **desc,
dma_addr_t addr, int len, unsigned int cmd);
void (*copy_to_bounce_buffer)(struct sdhci_host *host,
struct mmc_data *data,
unsigned int length);
void (*request_done)(struct sdhci_host *host,
struct mmc_request *mrq);
void (*dump_vendor_regs)(struct sdhci_host *host);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
@ -757,7 +753,6 @@ void sdhci_cleanup_host(struct sdhci_host *host);
int __sdhci_add_host(struct sdhci_host *host);
int sdhci_add_host(struct sdhci_host *host);
void sdhci_remove_host(struct sdhci_host *host, int dead);
void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd);
static inline void sdhci_read_caps(struct sdhci_host *host)
{
@ -776,6 +771,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host,
void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq);
int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq);
void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);

View file

@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/ioport.h>
#include <linux/iopoll.h>
#include <linux/scatterlist.h>
#include <pcmcia/cistpl.h>
@ -22,6 +23,7 @@
#include <linux/io.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#define DRIVER_NAME "sdricoh_cs"
@ -57,10 +59,8 @@ static unsigned int switchlocked;
#define STATUS_BUSY 0x40000000
/* timeouts */
#define INIT_TIMEOUT 100
#define CMD_TIMEOUT 100000
#define TRANSFER_TIMEOUT 100000
#define BUSY_TIMEOUT 32767
#define SDRICOH_CMD_TIMEOUT_US 1000000
#define SDRICOH_DATA_TIMEOUT_US 1000000
/* list of supported pcmcia devices */
static const struct pcmcia_device_id pcmcia_ids[] = {
@ -124,19 +124,24 @@ static inline unsigned int sdricoh_readb(struct sdricoh_host *host,
return value;
}
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
unsigned int timeout){
unsigned int loop;
static bool sdricoh_status_ok(struct sdricoh_host *host, unsigned int status,
unsigned int wanted)
{
sdricoh_writel(host, R2E4_STATUS_RESP, status);
return status & wanted;
}
static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted)
{
int ret;
unsigned int status = 0;
struct device *dev = host->dev;
for (loop = 0; loop < timeout; loop++) {
status = sdricoh_readl(host, R21C_STATUS);
sdricoh_writel(host, R2E4_STATUS_RESP, status);
if (status & wanted)
break;
}
if (loop == timeout) {
ret = read_poll_timeout(sdricoh_readl, status,
sdricoh_status_ok(host, status, wanted),
32, SDRICOH_DATA_TIMEOUT_US, false,
host, R21C_STATUS);
if (ret) {
dev_err(dev, "query_status: timeout waiting for %x\n", wanted);
return -ETIMEDOUT;
}
@ -150,35 +155,46 @@ static int sdricoh_query_status(struct sdricoh_host *host, unsigned int wanted,
}
static int sdricoh_mmc_cmd(struct sdricoh_host *host, unsigned char opcode,
unsigned int arg)
static int sdricoh_mmc_cmd(struct sdricoh_host *host, struct mmc_command *cmd)
{
unsigned int status;
int result = 0;
unsigned int loop = 0;
unsigned int status, timeout_us;
int ret;
unsigned char opcode = cmd->opcode;
/* reset status reg? */
sdricoh_writel(host, R21C_STATUS, 0x18);
/* MMC_APP_CMDs need some special handling */
if (host->app_cmd) {
opcode |= 64;
host->app_cmd = 0;
} else if (opcode == MMC_APP_CMD)
host->app_cmd = 1;
/* fill parameters */
sdricoh_writel(host, R204_CMD_ARG, arg);
sdricoh_writel(host, R204_CMD_ARG, cmd->arg);
sdricoh_writel(host, R200_CMD, (0x10000 << 8) | opcode);
/* wait for command completion */
if (opcode) {
for (loop = 0; loop < CMD_TIMEOUT; loop++) {
status = sdricoh_readl(host, R21C_STATUS);
sdricoh_writel(host, R2E4_STATUS_RESP, status);
if (status & STATUS_CMD_FINISHED)
break;
}
/* don't check for timeout in the loop it is not always
reset correctly
*/
if (loop == CMD_TIMEOUT || status & STATUS_CMD_TIMEOUT)
result = -ETIMEDOUT;
if (!opcode)
return 0;
}
timeout_us = cmd->busy_timeout ? cmd->busy_timeout * 1000 :
SDRICOH_CMD_TIMEOUT_US;
return result;
ret = read_poll_timeout(sdricoh_readl, status,
sdricoh_status_ok(host, status, STATUS_CMD_FINISHED),
32, timeout_us, false,
host, R21C_STATUS);
/*
* Don't check for timeout status in the loop, as it's not always reset
* correctly.
*/
if (ret || status & STATUS_CMD_TIMEOUT)
return -ETIMEDOUT;
return 0;
}
static int sdricoh_reset(struct sdricoh_host *host)
@ -207,8 +223,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
u32 data = 0;
/* wait until the data is available */
if (read) {
if (sdricoh_query_status(host, STATUS_READY_TO_READ,
TRANSFER_TIMEOUT))
if (sdricoh_query_status(host, STATUS_READY_TO_READ))
return -ETIMEDOUT;
sdricoh_writel(host, R21C_STATUS, 0x18);
/* read data */
@ -224,8 +239,7 @@ static int sdricoh_blockio(struct sdricoh_host *host, int read,
}
}
} else {
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE,
TRANSFER_TIMEOUT))
if (sdricoh_query_status(host, STATUS_READY_TO_WRITE))
return -ETIMEDOUT;
sdricoh_writel(host, R21C_STATUS, 0x18);
/* write data */
@ -251,28 +265,20 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_command *cmd = mrq->cmd;
struct mmc_data *data = cmd->data;
struct device *dev = host->dev;
unsigned char opcode = cmd->opcode;
int i;
dev_dbg(dev, "=============================\n");
dev_dbg(dev, "sdricoh_request opcode=%i\n", opcode);
dev_dbg(dev, "sdricoh_request opcode=%i\n", cmd->opcode);
sdricoh_writel(host, R21C_STATUS, 0x18);
/* MMC_APP_CMDs need some special handling */
if (host->app_cmd) {
opcode |= 64;
host->app_cmd = 0;
} else if (opcode == 55)
host->app_cmd = 1;
/* read/write commands seem to require this */
if (data) {
sdricoh_writew(host, R226_BLOCKSIZE, data->blksz);
sdricoh_writel(host, R208_DATAIO, 0);
}
cmd->error = sdricoh_mmc_cmd(host, opcode, cmd->arg);
cmd->error = sdricoh_mmc_cmd(host, cmd);
/* read response buffer */
if (cmd->flags & MMC_RSP_PRESENT) {
@ -323,8 +329,7 @@ static void sdricoh_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdricoh_writel(host, R208_DATAIO, 1);
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED,
TRANSFER_TIMEOUT)) {
if (sdricoh_query_status(host, STATUS_TRANSFER_FINISHED)) {
dev_err(dev, "sdricoh_request: transfer end error\n");
cmd->error = -EINVAL;
}

View file

@ -951,9 +951,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios)
{
int ret;
/* vqmmc regulator is available */
if (!IS_ERR(mmc->supply.vqmmc))
return mmc_regulator_set_vqmmc(mmc, ios);
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = mmc_regulator_set_vqmmc(mmc, ios);
return ret < 0 ? ret : 0;
}
/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
if (mmc->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330)
@ -1390,7 +1394,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
mmc->f_min = 400000;
mmc->f_max = 52000000;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_ERASE | MMC_CAP_SDIO_IRQ;
MMC_CAP_SDIO_IRQ;
/*
* Some H5 devices do not have signal traces precise enough to

View file

@ -73,6 +73,8 @@ module_param(fixed_timeout, bool, 0644);
#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL
#define TIFM_MMCSD_REQ_TIMEOUT_MS 1000
enum {
CMD_READY = 0x0001,
FIFO_READY = 0x0002,
@ -959,7 +961,12 @@ static int tifm_sd_probe(struct tifm_dev *sock)
host = mmc_priv(mmc);
tifm_set_drvdata(sock, mmc);
host->dev = sock;
host->timeout_jiffies = msecs_to_jiffies(1000);
host->timeout_jiffies = msecs_to_jiffies(TIFM_MMCSD_REQ_TIMEOUT_MS);
/*
* We use a fixed request timeout of 1s, hence inform the core about it.
* A future improvement should instead respect the cmd->busy_timeout.
*/
mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS;
tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd,
(unsigned long)host);

View file

@ -39,7 +39,6 @@
#include <linux/module.h>
#include <linux/pagemap.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_qos.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@ -1128,7 +1127,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
if (ret == -EPROBE_DEFER)
return ret;
mmc->caps |= MMC_CAP_ERASE | MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 |= pdata->capabilities2;
mmc->max_segs = pdata->max_segs ? : 32;
mmc->max_blk_size = TMIO_MAX_BLK_SIZE;
@ -1192,7 +1191,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
/* See if we also get DMA */
tmio_mmc_request_dma(_host, pdata);
dev_pm_domain_start(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
@ -1231,12 +1229,14 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
cancel_work_sync(&host->done);
cancel_delayed_work_sync(&host->delayed_reset_work);
tmio_mmc_release_dma(host);
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
pm_runtime_dont_use_autosuspend(&pdev->dev);
if (host->native_hotplug)
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
}
EXPORT_SYMBOL_GPL(tmio_mmc_host_remove);

View file

@ -610,11 +610,6 @@ static int uniphier_sd_probe(struct platform_device *pdev)
}
}
ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
dev_name(dev), host);
if (ret)
goto free_host;
if (priv->caps & UNIPHIER_SD_CAP_EXTENDED_IP)
host->dma_ops = &uniphier_sd_internal_dma_ops;
else
@ -642,8 +637,15 @@ static int uniphier_sd_probe(struct platform_device *pdev)
if (ret)
goto free_host;
ret = devm_request_irq(dev, irq, tmio_mmc_irq, IRQF_SHARED,
dev_name(dev), host);
if (ret)
goto remove_host;
return 0;
remove_host:
tmio_mmc_host_remove(host);
free_host:
tmio_mmc_host_free(host);

View file

@ -136,6 +136,8 @@
#define USDHI6_MIN_DMA 64
#define USDHI6_REQ_TIMEOUT_MS 4000
enum usdhi6_wait_for {
USDHI6_WAIT_FOR_REQUEST,
USDHI6_WAIT_FOR_CMD,
@ -1763,7 +1765,12 @@ static int usdhi6_probe(struct platform_device *pdev)
host = mmc_priv(mmc);
host->mmc = mmc;
host->wait = USDHI6_WAIT_FOR_REQUEST;
host->timeout = msecs_to_jiffies(4000);
host->timeout = msecs_to_jiffies(USDHI6_REQ_TIMEOUT_MS);
/*
* We use a fixed timeout of 4s, hence inform the core about it. A
* future improvement should instead respect the cmd->busy_timeout.
*/
mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS;
host->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(host->pinctrl)) {

View file

@ -319,6 +319,8 @@ struct via_crdr_mmc_host {
/* some devices need a very long delay for power to stabilize */
#define VIA_CRDR_QUIRK_300MS_PWRDELAY 0x0001
#define VIA_CMD_TIMEOUT_MS 1000
static const struct pci_device_id via_ids[] = {
{PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_9530,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0,},
@ -551,14 +553,17 @@ static void via_sdc_send_command(struct via_crdr_mmc_host *host,
{
void __iomem *addrbase;
struct mmc_data *data;
unsigned int timeout_ms;
u32 cmdctrl = 0;
WARN_ON(host->cmd);
data = cmd->data;
mod_timer(&host->timer, jiffies + HZ);
host->cmd = cmd;
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout : VIA_CMD_TIMEOUT_MS;
mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout_ms));
/*Command index*/
cmdctrl = cmd->opcode << 8;

View file

@ -28,6 +28,8 @@
#include <linux/pnp.h>
#include <linux/highmem.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@ -770,22 +772,22 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
* interrupts.
*/
switch (cmd->opcode) {
case 11:
case 17:
case 18:
case 20:
case 24:
case 25:
case 26:
case 27:
case 30:
case 42:
case 56:
case SD_SWITCH_VOLTAGE:
case MMC_READ_SINGLE_BLOCK:
case MMC_READ_MULTIPLE_BLOCK:
case MMC_WRITE_DAT_UNTIL_STOP:
case MMC_WRITE_BLOCK:
case MMC_WRITE_MULTIPLE_BLOCK:
case MMC_PROGRAM_CID:
case MMC_PROGRAM_CSD:
case MMC_SEND_WRITE_PROT:
case MMC_LOCK_UNLOCK:
case MMC_GEN_CMD:
break;
/* ACMDs. We don't keep track of state, so we just treat them
* like any other command. */
case 51:
case SD_APP_SEND_SCR:
break;
default:

View file

@ -1083,10 +1083,10 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
mbox_info->ext_info[0].htc_ext_addr = ATH10K_HIF_MBOX0_EXT_BASE_ADDR;
dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, device);
dev_id_chiprev = FIELD_GET(QCA_MANUFACTURER_ID_REV_MASK, device);
dev_id_base = (device & 0x0F00);
dev_id_chiprev = (device & 0x00FF);
switch (dev_id_base) {
case QCA_MANUFACTURER_ID_AR6005_BASE:
case (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00):
if (dev_id_chiprev < 4)
mbox_info->ext_info[0].htc_ext_sz =
ATH10K_HIF_MBOX0_EXT_WIDTH;
@ -1097,7 +1097,7 @@ static void ath10k_sdio_set_mbox_info(struct ath10k *ar)
mbox_info->ext_info[0].htc_ext_sz =
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
break;
case QCA_MANUFACTURER_ID_QCA9377_BASE:
case (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00):
mbox_info->ext_info[0].htc_ext_sz =
ATH10K_HIF_MBOX0_EXT_WIDTH_ROME_2_0;
break;
@ -2185,19 +2185,16 @@ static int ath10k_sdio_probe(struct sdio_func *func,
skb_queue_head_init(&ar_sdio->rx_head);
INIT_WORK(&ar_sdio->async_work_rx, ath10k_rx_indication_async_work);
dev_id_base = FIELD_GET(QCA_MANUFACTURER_ID_BASE, id->device);
switch (dev_id_base) {
case QCA_MANUFACTURER_ID_AR6005_BASE:
case QCA_MANUFACTURER_ID_QCA9377_BASE:
ar->dev_id = QCA9377_1_0_DEVICE_ID;
break;
default:
dev_id_base = (id->device & 0x0F00);
if (dev_id_base != (SDIO_DEVICE_ID_ATHEROS_AR6005 & 0x0F00) &&
dev_id_base != (SDIO_DEVICE_ID_ATHEROS_QCA9377 & 0x0F00)) {
ret = -ENODEV;
ath10k_err(ar, "unsupported device id %u (0x%x)\n",
dev_id_base, id->device);
goto err_free_wq;
}
ar->dev_id = QCA9377_1_0_DEVICE_ID;
ar->id.vendor = id->vendor;
ar->id.device = id->device;
@ -2246,10 +2243,8 @@ static void ath10k_sdio_remove(struct sdio_func *func)
}
static const struct sdio_device_id ath10k_sdio_devices[] = {
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
(QCA_SDIO_ID_AR6005_BASE | 0xA))},
{SDIO_DEVICE(QCA_MANUFACTURER_CODE,
(QCA_SDIO_ID_QCA9377_BASE | 0x1))},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6005)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_QCA9377)},
{},
};

View file

@ -10,14 +10,6 @@
#define ATH10K_HIF_MBOX_BLOCK_SIZE 256
#define QCA_MANUFACTURER_ID_BASE GENMASK(11, 8)
#define QCA_MANUFACTURER_ID_AR6005_BASE 0x5
#define QCA_MANUFACTURER_ID_QCA9377_BASE 0x7
#define QCA_SDIO_ID_AR6005_BASE 0x500
#define QCA_SDIO_ID_QCA9377_BASE 0x700
#define QCA_MANUFACTURER_ID_REV_MASK 0x00FF
#define QCA_MANUFACTURER_CODE 0x271 /* Qualcomm/Atheros */
#define ATH10K_SDIO_MAX_BUFFER_SIZE 4096 /*Unsure of this constant*/
/* Mailbox address in SDIO address space */

View file

@ -35,12 +35,6 @@
#define MAX_SCATTER_ENTRIES_PER_REQ 16
#define MAX_SCATTER_REQ_TRANSFER_SIZE (32 * 1024)
#define MANUFACTURER_ID_AR6003_BASE 0x300
#define MANUFACTURER_ID_AR6004_BASE 0x400
/* SDIO manufacturer ID and Codes */
#define MANUFACTURER_ID_ATH6KL_BASE_MASK 0xFF00
#define MANUFACTURER_CODE 0x271 /* Atheros */
/* Mailbox address in SDIO address space */
#define HIF_MBOX_BASE_ADDR 0x800
#define HIF_MBOX_WIDTH 0x800

View file

@ -799,8 +799,7 @@ static int ath6kl_sdio_config(struct ath6kl *ar)
sdio_claim_host(func);
if ((ar_sdio->id->device & MANUFACTURER_ID_ATH6KL_BASE_MASK) >=
MANUFACTURER_ID_AR6003_BASE) {
if (ar_sdio->id->device >= SDIO_DEVICE_ID_ATHEROS_AR6003_00) {
/* enable 4-bit ASYNC interrupt on AR6003 or later */
ret = ath6kl_sdio_func0_cmd52_wr_byte(func->card,
CCCR_SDIO_IRQ_MODE_REG,
@ -1409,13 +1408,13 @@ static void ath6kl_sdio_remove(struct sdio_func *func)
}
static const struct sdio_device_id ath6kl_sdio_devices[] = {
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6003_BASE | 0x1))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x0))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x1))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x2))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x18))},
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_AR6004_BASE | 0x19))},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_00)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6003_01)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_00)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_01)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_02)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_18)},
{SDIO_DEVICE(SDIO_VENDOR_ID_ATHEROS, SDIO_DEVICE_ID_ATHEROS_AR6004_19)},
{},
};

View file

@ -180,8 +180,8 @@ static void b43_sdio_remove(struct sdio_func *func)
}
static const struct sdio_device_id b43_sdio_ids[] = {
{ SDIO_DEVICE(0x02d0, 0x044b) }, /* Nintendo Wii WLAN daughter card */
{ SDIO_DEVICE(0x0092, 0x0004) }, /* C-guys, Inc. EW-CG1102GC */
{ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_CGUYS, SDIO_DEVICE_ID_CGUYS_EW_CG1102GC) },
{ },
};

View file

@ -970,9 +970,9 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = {
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_4373),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_43012),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_CYPRESS_89359),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012),
BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359),
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);

View file

@ -4187,7 +4187,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
bus->hostintmask, NULL);
switch (sdiod->func1->device) {
case SDIO_DEVICE_ID_CYPRESS_4373:
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
CY_4373_F2_WATERMARK);
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,
@ -4201,7 +4201,7 @@ static void brcmf_sdio_firmware_callback(struct device *dev, int err,
CY_4373_F2_WATERMARK |
SBSDIO_MESBUSYCTRL_ENAB, &err);
break;
case SDIO_DEVICE_ID_CYPRESS_43012:
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012:
brcmf_dbg(INFO, "set F2 watermark to 0x%x*4 bytes\n",
CY_43012_F2_WATERMARK);
brcmf_sdiod_writeb(sdiod, SBSDIO_WATERMARK,

View file

@ -65,7 +65,7 @@ static const struct sdio_device_id if_sdio_ids[] = {
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
SDIO_DEVICE_ID_MARVELL_8688WLAN) },
SDIO_DEVICE_ID_MARVELL_8688_WLAN) },
{ /* end: all zeroes */ },
};

View file

@ -480,45 +480,25 @@ static void mwifiex_sdio_coredump(struct device *dev)
schedule_work(&card->work);
}
/* Device ID for SD8786 */
#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116)
/* Device ID for SD8787 */
#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119)
/* Device ID for SD8797 */
#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129)
/* Device ID for SD8897 */
#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d)
/* Device ID for SD8887 */
#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135)
/* Device ID for SD8801 */
#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139)
/* Device ID for SD8977 */
#define SDIO_DEVICE_ID_MARVELL_8977 (0x9145)
/* Device ID for SD8987 */
#define SDIO_DEVICE_ID_MARVELL_8987 (0x9149)
/* Device ID for SD8997 */
#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141)
/* WLAN IDs */
static const struct sdio_device_id mwifiex_ids[] = {
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786_WLAN),
.driver_data = (unsigned long) &mwifiex_sdio_sd8786},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787_WLAN),
.driver_data = (unsigned long) &mwifiex_sdio_sd8787},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_WLAN),
.driver_data = (unsigned long) &mwifiex_sdio_sd8797},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897_WLAN),
.driver_data = (unsigned long) &mwifiex_sdio_sd8897},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8887},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8801},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8977},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8987},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997),
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8997},
{},
};

View file

@ -67,7 +67,6 @@ static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r)
((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) |
((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) |
((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) |
((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) |
((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) |
((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) |
((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) |
@ -411,6 +410,7 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
struct gb_sdio_command_request request = {0};
struct gb_sdio_command_response response;
struct mmc_data *data = host->mrq->data;
unsigned int timeout_ms;
u8 cmd_flags;
u8 cmd_type;
int i;
@ -469,9 +469,12 @@ static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd)
request.data_blksz = cpu_to_le16(data->blksz);
}
ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND,
&request, sizeof(request), &response,
sizeof(response));
timeout_ms = cmd->busy_timeout ? cmd->busy_timeout :
GB_OPERATION_TIMEOUT_DEFAULT;
ret = gb_operation_sync_timeout(host->connection, GB_SDIO_TYPE_COMMAND,
&request, sizeof(request), &response,
sizeof(response), timeout_ms);
if (ret < 0)
goto out;

View file

@ -288,7 +288,7 @@ struct memstick_host {
int (*set_param)(struct memstick_host *host,
enum memstick_param param,
int value);
unsigned long private[0] ____cacheline_aligned;
unsigned long private[] ____cacheline_aligned;
};
struct memstick_driver {

View file

@ -48,6 +48,7 @@ struct mmc_ext_csd {
u8 sec_feature_support;
u8 rel_sectors;
u8 rel_param;
bool enhanced_rpmb_supported;
u8 part_config;
u8 cache_ctrl;
u8 rst_n_function;

View file

@ -92,6 +92,9 @@ struct mmc_host_ops {
int err);
void (*pre_req)(struct mmc_host *host, struct mmc_request *req);
void (*request)(struct mmc_host *host, struct mmc_request *req);
/* Submit one request to host in atomic context. */
int (*request_atomic)(struct mmc_host *host,
struct mmc_request *req);
/*
* Avoid calling the next three functions too often or in a "fast
@ -318,7 +321,6 @@ struct mmc_host {
#define MMC_CAP_AGGRESSIVE_PM (1 << 7) /* Suspend (e)MMC/SD at idle */
#define MMC_CAP_NONREMOVABLE (1 << 8) /* Nonremovable e.g. eMMC */
#define MMC_CAP_WAIT_WHILE_BUSY (1 << 9) /* Waits while card is busy */
#define MMC_CAP_ERASE (1 << 10) /* Allow erase/trim commands */
#define MMC_CAP_3_3V_DDR (1 << 11) /* Host supports eMMC DDR 3.3V */
#define MMC_CAP_1_8V_DDR (1 << 12) /* Host supports eMMC DDR 1.8V */
#define MMC_CAP_1_2V_DDR (1 << 13) /* Host supports eMMC DDR 1.2V */

View file

@ -325,6 +325,7 @@ static inline bool mmc_ready_for_data(u32 status)
*/
#define EXT_CSD_WR_REL_PARAM_EN (1<<2)
#define EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR (1<<4)
#define EXT_CSD_BOOT_WP_B_PWR_WP_DIS (0x40)
#define EXT_CSD_BOOT_WP_B_PERM_WP_DIS (0x10)

View file

@ -24,27 +24,9 @@
/*
* Vendors and devices. Sort key: vendor first, device next.
*/
#define SDIO_VENDOR_ID_BROADCOM 0x02d0
#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
#define SDIO_DEVICE_ID_BROADCOM_4339 0x4339
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
#define SDIO_DEVICE_ID_BROADCOM_43364 0xa9a4
#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359
#define SDIO_DEVICE_ID_CYPRESS_4373 0x4373
#define SDIO_DEVICE_ID_CYPRESS_43012 43012
#define SDIO_DEVICE_ID_CYPRESS_89359 0x4355
#define SDIO_VENDOR_ID_STE 0x0020
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#define SDIO_VENDOR_ID_INTEL 0x0089
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX 0x1402
@ -54,29 +36,89 @@
#define SDIO_DEVICE_ID_INTEL_IWMC3200BT 0x1406
#define SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5 0x1407
#define SDIO_VENDOR_ID_CGUYS 0x0092
#define SDIO_DEVICE_ID_CGUYS_EW_CG1102GC 0x0004
#define SDIO_VENDOR_ID_TI 0x0097
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#define SDIO_VENDOR_ID_ATHEROS 0x0271
#define SDIO_DEVICE_ID_ATHEROS_AR6003_00 0x0300
#define SDIO_DEVICE_ID_ATHEROS_AR6003_01 0x0301
#define SDIO_DEVICE_ID_ATHEROS_AR6004_00 0x0400
#define SDIO_DEVICE_ID_ATHEROS_AR6004_01 0x0401
#define SDIO_DEVICE_ID_ATHEROS_AR6004_02 0x0402
#define SDIO_DEVICE_ID_ATHEROS_AR6004_18 0x0418
#define SDIO_DEVICE_ID_ATHEROS_AR6004_19 0x0419
#define SDIO_DEVICE_ID_ATHEROS_AR6005 0x050A
#define SDIO_DEVICE_ID_ATHEROS_QCA9377 0x0701
#define SDIO_VENDOR_ID_BROADCOM 0x02d0
#define SDIO_DEVICE_ID_BROADCOM_NINTENDO_WII 0x044b
#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324
#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329
#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330
#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334
#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335
#define SDIO_DEVICE_ID_BROADCOM_4339 0x4339
#define SDIO_DEVICE_ID_BROADCOM_4345 0x4345
#define SDIO_DEVICE_ID_BROADCOM_4354 0x4354
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359 0x4355
#define SDIO_DEVICE_ID_BROADCOM_4356 0x4356
#define SDIO_DEVICE_ID_BROADCOM_4359 0x4359
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373 0x4373
#define SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012 0xa804
#define SDIO_DEVICE_ID_BROADCOM_43143 0xa887
#define SDIO_DEVICE_ID_BROADCOM_43340 0xa94c
#define SDIO_DEVICE_ID_BROADCOM_43341 0xa94d
#define SDIO_DEVICE_ID_BROADCOM_43362 0xa962
#define SDIO_DEVICE_ID_BROADCOM_43364 0xa9a4
#define SDIO_DEVICE_ID_BROADCOM_43430 0xa9a6
#define SDIO_DEVICE_ID_BROADCOM_43455 0xa9bf
#define SDIO_VENDOR_ID_MARVELL 0x02df
#define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
#define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104
#define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105
#define SDIO_DEVICE_ID_MARVELL_8688_WLAN 0x9104
#define SDIO_DEVICE_ID_MARVELL_8688_BT 0x9105
#define SDIO_DEVICE_ID_MARVELL_8786_WLAN 0x9116
#define SDIO_DEVICE_ID_MARVELL_8787_WLAN 0x9119
#define SDIO_DEVICE_ID_MARVELL_8787_BT 0x911a
#define SDIO_DEVICE_ID_MARVELL_8787_BT_AMP 0x911b
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
#define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134
#define SDIO_DEVICE_ID_MARVELL_8797_WLAN 0x9129
#define SDIO_DEVICE_ID_MARVELL_8797_BT 0x912a
#define SDIO_DEVICE_ID_MARVELL_8897_WLAN 0x912d
#define SDIO_DEVICE_ID_MARVELL_8897_BT 0x912e
#define SDIO_DEVICE_ID_MARVELL_8887_F0 0x9134
#define SDIO_DEVICE_ID_MARVELL_8887_WLAN 0x9135
#define SDIO_DEVICE_ID_MARVELL_8887_BT 0x9136
#define SDIO_DEVICE_ID_MARVELL_8801_WLAN 0x9139
#define SDIO_DEVICE_ID_MARVELL_8997_F0 0x9140
#define SDIO_DEVICE_ID_MARVELL_8997_WLAN 0x9141
#define SDIO_DEVICE_ID_MARVELL_8997_BT 0x9142
#define SDIO_DEVICE_ID_MARVELL_8977_WLAN 0x9145
#define SDIO_DEVICE_ID_MARVELL_8977_BT 0x9146
#define SDIO_DEVICE_ID_MARVELL_8987_WLAN 0x9149
#define SDIO_DEVICE_ID_MARVELL_8987_BT 0x914a
#define SDIO_VENDOR_ID_MEDIATEK 0x037a
#define SDIO_DEVICE_ID_MEDIATEK_MT7663 0x7663
#define SDIO_DEVICE_ID_MEDIATEK_MT7668 0x7668
#define SDIO_VENDOR_ID_SIANO 0x039a
#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
#define SDIO_DEVICE_ID_SIANO_MING 0x0302
#define SDIO_DEVICE_ID_SIANO_PELE 0x0500
#define SDIO_DEVICE_ID_SIANO_RIO 0x0600
#define SDIO_DEVICE_ID_SIANO_DENVER_2160 0x0700
#define SDIO_DEVICE_ID_SIANO_DENVER_1530 0x0800
#define SDIO_DEVICE_ID_SIANO_NOVA_A0 0x1100
#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
#define SDIO_VENDOR_ID_TI 0x0097
#define SDIO_DEVICE_ID_TI_WL1271 0x4076
#define SDIO_VENDOR_ID_TI_WL1251 0x104c
#define SDIO_DEVICE_ID_TI_WL1251 0x9066
#define SDIO_VENDOR_ID_STE 0x0020
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif /* LINUX_MMC_SDIO_IDS_H */

View file

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright 2010 Wolfram Sang <w.sang@pengutronix.de>
* Copyright 2010 Wolfram Sang <kernel@pengutronix.de>
*/
#ifndef __ASM_ARCH_IMX_ESDHC_H

View 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__ */

View file

@ -3,6 +3,7 @@
#define LINUX_MMC_IOCTL_H
#include <linux/types.h>
#include <linux/major.h>
struct mmc_ioc_cmd {
/*