forked from Minki/linux
MMC core:
- Add support for the asynchronous SDIO wakeup interrupts - Skip redundant evaluation of eMMC HS400 caps when no-MMC-cap - Add support to store error stats from host drivers - Extend debugfs to show error stats from host drivers - Add single I/O read support in the recovery path for 4k sector cards MMC host: - dw_mmc-exynos: Convert corresponding DT bindings to the dtschema - dw_mmc-rockchip: Add support for the Rockchip RV1126 variant - mmc_spi: Convert corresponding DT bindings to the dtschema - mtk-sd: Extend support for interrupts/pinctrls for SDIO low-power mode - mtk-sd: Add support for SDIO wake irqs - mtk-sd: Add support for the Mediatek MT8188 variant - renesas_sdhi: Drop redundant manual tap correction for newer SoCs - renesas_sdhi: Add support for the R-Car S4-8 and generic Gen4 variants - sdhci/cqhci: Add support to capture stats from host errors - sdhci-brcmstb: Add ability to increase max clock rate for SDIO on 72116b0 - sdhci-msm: Add support for the MSM8998 and SM8450 variant - sdhci-of-at91: Fixup UHS-I mode by rewriting of MC1R - sdhci-of-dwcmshc: Add support for the Rockchip rk3588 variant - sdhci-of-dwcmshc: Enable reset support for the Rockchip variants - sdhci-pci-gli: Improve I/O read/write performance for GL9763E - sdhci-s3c: Convert corresponding DT bindings to the dtschema - tmio: Avoid glitches when resetting MEMSTICK core: - A couple of minor fixes and cleanups -----BEGIN PGP SIGNATURE----- iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmLrq9IXHHVsZi5oYW5z c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCnsYg//ZrMGpDfUQf/5gwEgRNQdQDGu lWAicBRMfuvyN8xl222wXx6hMSeLHepL/zij+/p0DngFqkIfCZyWz1WvxLwMgZDJ SeZoJ/cNThKAi/Xum4UlnupK1Q3czWist8tbBI4AXbZ+kbkt4/bqYEKyrpRan8Bz K4qAeaHE0wSHZYeo+Dww/yTjvwYL1OkAN7Rvie8zhNUUSvwlmaxAWt8bwbu3R2bg dqcmtS10zFRyXlqFwYIxMvj1KLpv3S9iZFB2rh/zGGoqOYLiWjyYrZomaqmQ3feN 51Yf81Vp/kcjdYrYR93kUcvZeX1i9zqEFutLvcDlom8dVUPOXk0SVdlCOgBWhNoP TmtjIB0Pp2YSawysLmGw3ywRrgHZB2IwE+DyDbtlUhqwYHkBQ3HgNNPOiYyJZKs8 3llQVBGaQMU8Jeuf9IvWPo9bHfK6P1YSz8NXdr7HRLC2fToSYmJOXLbI9tZqbwP8 Tppi8/XmnVFG9WOv9qGtqnoVt4yAKHrZkfcJEOt8L+VfC0DpRDdgMqzbwhWaRIDW ++I2sYme3IgDm0BLLf5hhEWqzs6E4e7rYx/SoJ4L8Zv9jEeaSaJC2kSh7DQKzIOF 0LcwfapXqxDvvX+9BVfJfYSiI/DrDsYreya+JExxHgPbA3rNzlmLvLx4qHrHmg8D hx0rIRlh4mEL82kMbuc= =Rmw5 -----END PGP SIGNATURE----- Merge tag 'mmc-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc Pull MMC updates from Ulf Hansson: "MMC core: - Add support for the asynchronous SDIO wakeup interrupts - Skip redundant evaluation of eMMC HS400 caps when no-MMC-cap - Add support to store error stats from host drivers - Extend debugfs to show error stats from host drivers - Add single I/O read support in the recovery path for 4k sector cards MMC host: - dw_mmc-exynos: Convert corresponding DT bindings to the dtschema - dw_mmc-rockchip: Add support for the Rockchip RV1126 variant - mmc_spi: Convert corresponding DT bindings to the dtschema - mtk-sd: Extend support for interrupts/pinctrls for SDIO low-power mode - mtk-sd: Add support for SDIO wake irqs - mtk-sd: Add support for the Mediatek MT8188 variant - renesas_sdhi: Drop redundant manual tap correction for newer SoCs - renesas_sdhi: Add support for the R-Car S4-8 and generic Gen4 variants - sdhci/cqhci: Add support to capture stats from host errors - sdhci-brcmstb: Add ability to increase max clock rate for SDIO on 72116b0 - sdhci-msm: Add support for the MSM8998 and SM8450 variant - sdhci-of-at91: Fixup UHS-I mode by rewriting of MC1R - sdhci-of-dwcmshc: Add support for the Rockchip rk3588 variant - sdhci-of-dwcmshc: Enable reset support for the Rockchip variants - sdhci-pci-gli: Improve I/O read/write performance for GL9763E - sdhci-s3c: Convert corresponding DT bindings to the dtschema - tmio: Avoid glitches when resetting MEMSTICK core: - A couple of minor fixes and cleanups" * tag 'mmc-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (61 commits) mmc: mediatek: add support for SDIO eint wakup IRQ mmc: core: Add support for SDIO wakeup interrupt dt-bindings: mmc: mtk-sd: extend interrupts and pinctrls properties dt-bindings: mmc: rockchip-dw-mshc: Document Rockchip RV1126 mmc: renesas_sdhi: newer SoCs don't need manual tap correction mmc: cavium-thunderx: Add of_node_put() when breaking out of loop mmc: cavium-octeon: Add of_node_put() when breaking out of loop mmc: core: quirks: Add of_node_put() when breaking out of loop mmc: sdhci-brcmstb: use clk_get_rate(base_clk) in PM resume dt-bindings: mmc: sdhci-msm: Document the SM8450 compatible mmc: sdhci-msm: drop redundant of_device_id entries dt-bindings: mmc: sdhci-msm: add MSM8998 mmc: block: Add single read for 4k sector cards mmc: mxcmmc: Use mmc_card_sdio macro mmc: core: Use mmc_card_* macro and add a new for the sd_combo type dt-bindings: mmc: sdhci-msm: constrain reg-names per variants dt-bindings: mmc: sdhci-msm: fix reg-names entries dt-bindings: mmc: Add compatible for MediaTek MT8188 dt-bindings: mmc: sdhci-msm: document resets mmc: sdhci-of-at91: fix set_uhs_signaling rewriting of MC1R ...
This commit is contained in:
commit
328141e51e
@ -10,9 +10,6 @@ maintainers:
|
||||
- Al Cooper <alcooperx@gmail.com>
|
||||
- Florian Fainelli <f.fainelli@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
@ -42,23 +39,46 @@ properties:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
description:
|
||||
handle to core clock for the sdhci controller.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: handle to core clock for the sdhci controller
|
||||
- description: handle to improved 150Mhz clock for sdhci controller (Optional clock)
|
||||
|
||||
clock-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: sw_sdio
|
||||
- const: sdio_freq # Optional clock
|
||||
|
||||
clock-frequency:
|
||||
description:
|
||||
Maximum operating frequency of sdio_freq sdhci controller clock
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 100000000
|
||||
maximum: 150000000
|
||||
|
||||
sdhci,auto-cmd12:
|
||||
type: boolean
|
||||
description: Specifies that controller should use auto CMD12
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
clock-names:
|
||||
contains:
|
||||
const: sdio_freq
|
||||
|
||||
then:
|
||||
required:
|
||||
- clock-frequency
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
|
@ -1,94 +0,0 @@
|
||||
* Samsung Exynos specific extensions to the Synopsys Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
The Synopsys designware mobile storage host controller is used to interface
|
||||
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
|
||||
differences between the core Synopsys dw mshc controller properties described
|
||||
by synopsys-dw-mshc.txt and the properties used by the Samsung Exynos specific
|
||||
extensions to the Synopsys Designware Mobile Storage Host Controller.
|
||||
|
||||
Required Properties:
|
||||
|
||||
* compatible: should be
|
||||
- "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210
|
||||
specific extensions.
|
||||
- "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412
|
||||
specific extensions.
|
||||
- "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250
|
||||
specific extensions.
|
||||
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
|
||||
specific extensions.
|
||||
- "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7
|
||||
specific extensions.
|
||||
- "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7
|
||||
specific extensions having an SMU.
|
||||
- "axis,artpec8-dw-mshc": for controllers with ARTPEC-8 specific
|
||||
extensions.
|
||||
|
||||
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
|
||||
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
|
||||
ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7.
|
||||
|
||||
* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value
|
||||
in transmit mode and CIU clock phase shift value in receive mode for single
|
||||
data rate mode operation. Refer notes below for the order of the cells and the
|
||||
valid values.
|
||||
|
||||
* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value
|
||||
in transmit mode and CIU clock phase shift value in receive mode for double
|
||||
data rate mode operation. Refer notes below for the order of the cells and the
|
||||
valid values.
|
||||
* samsung,dw-mshc-hs400-timing: Specifies the value of CIU TX and RX clock phase
|
||||
shift value for hs400 mode operation.
|
||||
|
||||
Notes for the sdr-timing and ddr-timing values:
|
||||
|
||||
The order of the cells should be
|
||||
- First Cell: CIU clock phase shift value for tx mode.
|
||||
- Second Cell: CIU clock phase shift value for rx mode.
|
||||
|
||||
Valid values for SDR and DDR CIU clock timing for Exynos5250:
|
||||
- valid value for tx phase shift and rx phase shift is 0 to 7.
|
||||
- when CIU clock divider value is set to 3, all possible 8 phase shift
|
||||
values can be used.
|
||||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||
phase shift clocks should be 0.
|
||||
|
||||
* samsung,read-strobe-delay: RCLK (Data strobe) delay to control HS400 mode
|
||||
(Latency value for delay line in Read path)
|
||||
|
||||
Required properties for a slot (Deprecated - Recommend to use one slot per host):
|
||||
|
||||
* gpios: specifies a list of gpios used for command, clock and data bus. The
|
||||
first gpio is the command line and the second gpio is the clock line. The
|
||||
rest of the gpios (depending on the bus-width property) are the data lines in
|
||||
no particular order. The format of the gpio specifier depends on the gpio
|
||||
controller.
|
||||
(Deprecated - Refer to Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt)
|
||||
|
||||
Example:
|
||||
|
||||
The MSHC controller node can be split into two portions, SoC specific and
|
||||
board specific portions as listed below.
|
||||
|
||||
dwmmc0@12200000 {
|
||||
compatible = "samsung,exynos5250-dw-mshc";
|
||||
reg = <0x12200000 0x1000>;
|
||||
interrupts = <0 75 0>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
};
|
||||
|
||||
dwmmc0@12200000 {
|
||||
cap-mmc-highspeed;
|
||||
cap-sd-highspeed;
|
||||
broken-cd;
|
||||
fifo-depth = <0x80>;
|
||||
card-detect-delay = <200>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <2 3>;
|
||||
samsung,dw-mshc-ddr-timing = <1 2>;
|
||||
samsung,dw-mshc-hs400-timing = <0 2>;
|
||||
samsung,read-strobe-delay = <90>;
|
||||
bus-width = <8>;
|
||||
};
|
@ -1,29 +0,0 @@
|
||||
MMC/SD/SDIO slot directly connected to a SPI bus
|
||||
|
||||
This file documents differences between the core properties described
|
||||
by mmc.txt and the properties used by the mmc_spi driver.
|
||||
|
||||
Required properties:
|
||||
- spi-max-frequency : maximum frequency for this device (Hz).
|
||||
|
||||
Optional properties:
|
||||
- voltage-ranges : two cells are required, first cell specifies minimum
|
||||
slot voltage (mV), second cell specifies maximum slot voltage (mV).
|
||||
Several ranges could be specified. If not provided, 3.2v..3.4v is assumed.
|
||||
- gpios : may specify GPIOs in this order: Card-Detect GPIO,
|
||||
Write-Protect GPIO. Note that this does not follow the
|
||||
binding from mmc.txt, for historical reasons.
|
||||
|
||||
Example:
|
||||
|
||||
mmc-slot@0 {
|
||||
compatible = "fsl,mpc8323rdb-mmc-slot",
|
||||
"mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&qe_pio_d 14 1
|
||||
&qe_pio_d 15 0>;
|
||||
voltage-ranges = <3300 3300>;
|
||||
spi-max-frequency = <50000000>;
|
||||
interrupts = <42>;
|
||||
interrupt-parent = <&PIC>;
|
||||
};
|
77
Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
Normal file
77
Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml
Normal file
@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/mmc-spi-slot.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MMC/SD/SDIO slot directly connected to a SPI bus
|
||||
|
||||
maintainers:
|
||||
- Ulf Hansson <ulf.hansson@linaro.org>
|
||||
|
||||
allOf:
|
||||
- $ref: "mmc-controller.yaml"
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml
|
||||
|
||||
description: |
|
||||
The extra properties used by an mmc connected via SPI.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mmc-spi-slot
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
spi-max-frequency: true
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
voltage-ranges:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
description: |
|
||||
Two cells are required, first cell specifies minimum slot voltage (mV),
|
||||
second cell specifies maximum slot voltage (mV).
|
||||
items:
|
||||
- description: |
|
||||
value for minimum slot voltage in mV
|
||||
default: 3200
|
||||
- description: |
|
||||
value for maximum slot voltage in mV
|
||||
default: 3400
|
||||
|
||||
gpios:
|
||||
description: |
|
||||
For historical reasons, this does not follow the generic mmc-controller
|
||||
binding.
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Card-Detect GPIO
|
||||
- description: Write-Protect GPIO
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- spi-max-frequency
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mmc@0 {
|
||||
compatible = "mmc-spi-slot";
|
||||
reg = <0>;
|
||||
gpios = <&gpio 14 GPIO_ACTIVE_LOW>, <&gpio 15 GPIO_ACTIVE_HIGH>;
|
||||
voltage-ranges = <3300 3300>;
|
||||
spi-max-frequency = <50000000>;
|
||||
interrupts = <42>;
|
||||
interrupt-parent = <&PIC>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
@ -30,13 +30,11 @@ properties:
|
||||
- const: mediatek,mt7623-mmc
|
||||
- const: mediatek,mt2701-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8186-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8192-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
- items:
|
||||
- const: mediatek,mt8195-mmc
|
||||
- enum:
|
||||
- mediatek,mt8186-mmc
|
||||
- mediatek,mt8188-mmc
|
||||
- mediatek,mt8192-mmc
|
||||
- mediatek,mt8195-mmc
|
||||
- const: mediatek,mt8183-mmc
|
||||
|
||||
reg:
|
||||
@ -72,12 +70,27 @@ properties:
|
||||
- const: ahb_cg
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description:
|
||||
Should at least contain MSDC GIC interrupt. To support SDIO in-band wakeup, an extended
|
||||
interrupt is required and be configured as wakeup source irq.
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: msdc
|
||||
- const: sdio_wakeup
|
||||
|
||||
pinctrl-names:
|
||||
description:
|
||||
Should at least contain default and state_uhs. To support SDIO in-band wakeup, dat1 pin
|
||||
will be switched between GPIO mode and SDIO DAT1 mode, state_eint is mandatory in this
|
||||
scenario.
|
||||
minItems: 2
|
||||
items:
|
||||
- const: default
|
||||
- const: state_uhs
|
||||
- const: state_eint
|
||||
|
||||
pinctrl-0:
|
||||
description:
|
||||
@ -89,6 +102,11 @@ properties:
|
||||
should contain uhs mode pin ctrl.
|
||||
maxItems: 1
|
||||
|
||||
pinctrl-2:
|
||||
description:
|
||||
should switch dat1 pin to GPIO mode.
|
||||
maxItems: 1
|
||||
|
||||
assigned-clocks:
|
||||
description:
|
||||
PLL of the source clock.
|
||||
@ -208,4 +226,32 @@ examples:
|
||||
mediatek,hs400-cmd-resp-sel-rising;
|
||||
};
|
||||
|
||||
mmc3: mmc@11260000 {
|
||||
compatible = "mediatek,mt8173-mmc";
|
||||
reg = <0x11260000 0x1000>;
|
||||
clock-names = "source", "hclk";
|
||||
clocks = <&pericfg CLK_PERI_MSDC30_3>,
|
||||
<&topckgen CLK_TOP_MSDC50_2_H_SEL>;
|
||||
interrupt-names = "msdc", "sdio_wakeup";
|
||||
interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_LOW 0>,
|
||||
<&pio 23 IRQ_TYPE_LEVEL_LOW>;
|
||||
pinctrl-names = "default", "state_uhs", "state_eint";
|
||||
pinctrl-0 = <&mmc2_pins_default>;
|
||||
pinctrl-1 = <&mmc2_pins_uhs>;
|
||||
pinctrl-2 = <&mmc2_pins_eint>;
|
||||
bus-width = <4>;
|
||||
max-frequency = <200000000>;
|
||||
cap-sd-highspeed;
|
||||
sd-uhs-sdr104;
|
||||
keep-power-in-suspend;
|
||||
wakeup-source;
|
||||
cap-sdio-irq;
|
||||
no-mmc;
|
||||
no-sd;
|
||||
non-removable;
|
||||
vmmc-supply = <&sdio_fixed_3v3>;
|
||||
vqmmc-supply = <&mt6397_vgp3_reg>;
|
||||
mmc-pwrseq = <&wifi_pwrseq>;
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -56,11 +56,15 @@ properties:
|
||||
- renesas,sdhi-r8a77980 # R-Car V3H
|
||||
- renesas,sdhi-r8a77990 # R-Car E3
|
||||
- renesas,sdhi-r8a77995 # R-Car D3
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- renesas,sdhi-r9a07g043 # RZ/G2UL
|
||||
- renesas,sdhi-r9a07g044 # RZ/G2{L,LC}
|
||||
- renesas,sdhi-r9a07g054 # RZ/V2L
|
||||
- const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,sdhi-r8a779a0 # R-Car V3U
|
||||
- renesas,sdhi-r8a779f0 # R-Car S4-8
|
||||
- const: renesas,rcar-gen4-sdhi # R-Car Gen4
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@ -141,6 +145,7 @@ allOf:
|
||||
enum:
|
||||
- renesas,rcar-gen2-sdhi
|
||||
- renesas,rcar-gen3-sdhi
|
||||
- renesas,rcar-gen4-sdhi
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
|
@ -39,6 +39,7 @@ properties:
|
||||
- rockchip,rk3399-dw-mshc
|
||||
- rockchip,rk3568-dw-mshc
|
||||
- rockchip,rv1108-dw-mshc
|
||||
- rockchip,rv1126-dw-mshc
|
||||
- const: rockchip,rk3288-dw-mshc
|
||||
|
||||
reg:
|
||||
|
@ -0,0 +1,160 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/samsung,exynos-dw-mshc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title:
|
||||
Samsung Exynos SoC specific extensions to the Synopsys Designware Mobile
|
||||
Storage Host Controller
|
||||
|
||||
maintainers:
|
||||
- Jaehoon Chung <jh80.chung@samsung.com>
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,exynos4210-dw-mshc
|
||||
- samsung,exynos4412-dw-mshc
|
||||
- samsung,exynos5250-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc-smu
|
||||
- samsung,exynos7-dw-mshc
|
||||
- samsung,exynos7-dw-mshc-smu
|
||||
- axis,artpec8-dw-mshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 2
|
||||
description:
|
||||
Handle to "biu" and "ciu" clocks for the
|
||||
bus interface unit clock and the card interface unit clock.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: biu
|
||||
- const: ciu
|
||||
|
||||
samsung,dw-mshc-ciu-div:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The divider value for the card interface unit (ciu) clock.
|
||||
|
||||
samsung,dw-mshc-ddr-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The value of CUI clock phase shift value in transmit mode and CIU clock
|
||||
phase shift value in receive mode for double data rate mode operation.
|
||||
See also samsung,dw-mshc-hs400-timing property.
|
||||
|
||||
samsung,dw-mshc-hs400-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description: |
|
||||
The value of CIU TX and RX clock phase shift value for HS400 mode
|
||||
operation.
|
||||
Valid values for SDR and DDR CIU clock timing::
|
||||
- valid value for tx phase shift and rx phase shift is 0 to 7.
|
||||
- when CIU clock divider value is set to 3, all possible 8 phase shift
|
||||
values can be used.
|
||||
- if CIU clock divider value is 0 (that is divide by 1), both tx and rx
|
||||
phase shift clocks should be 0.
|
||||
If missing, values from samsung,dw-mshc-ddr-timing property are used.
|
||||
|
||||
samsung,dw-mshc-sdr-timing:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
items:
|
||||
- description: CIU clock phase shift value for tx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
- description: CIU clock phase shift value for rx mode
|
||||
minimum: 0
|
||||
maximum: 7
|
||||
description:
|
||||
The value of CIU clock phase shift value in transmit mode and CIU clock
|
||||
phase shift value in receive mode for single data rate mode operation.
|
||||
See also samsung,dw-mshc-hs400-timing property.
|
||||
|
||||
samsung,read-strobe-delay:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
RCLK (Data strobe) delay to control HS400 mode (Latency value for delay
|
||||
line in Read path). If missing, default from hardware is used.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
- samsung,dw-mshc-ddr-timing
|
||||
- samsung,dw-mshc-sdr-timing
|
||||
|
||||
allOf:
|
||||
- $ref: "synopsys-dw-mshc-common.yaml#"
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos5250-dw-mshc
|
||||
- samsung,exynos5420-dw-mshc
|
||||
- samsung,exynos7-dw-mshc
|
||||
- samsung,exynos7-dw-mshc-smu
|
||||
- axis,artpec8-dw-mshc
|
||||
then:
|
||||
required:
|
||||
- samsung,dw-mshc-ciu-div
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos5420.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
mmc@12220000 {
|
||||
compatible = "samsung,exynos5420-dw-mshc";
|
||||
interrupts = <GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x12220000 0x1000>;
|
||||
clocks = <&clock CLK_MMC2>, <&clock CLK_SCLK_MMC2>;
|
||||
clock-names = "biu", "ciu";
|
||||
fifo-depth = <0x40>;
|
||||
card-detect-delay = <200>;
|
||||
samsung,dw-mshc-ciu-div = <3>;
|
||||
samsung,dw-mshc-sdr-timing = <0 4>;
|
||||
samsung,dw-mshc-ddr-timing = <0 2>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_wp &sd2_bus1 &sd2_bus4>;
|
||||
bus-width = <4>;
|
||||
cap-sd-highspeed;
|
||||
max-frequency = <200000000>;
|
||||
vmmc-supply = <&ldo19_reg>;
|
||||
vqmmc-supply = <&ldo13_reg>;
|
||||
sd-uhs-sdr50;
|
||||
sd-uhs-sdr104;
|
||||
sd-uhs-ddr50;
|
||||
};
|
@ -0,0 +1,81 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/mmc/samsung,s3c6410-sdhci.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Samsung SoC SDHCI Controller
|
||||
|
||||
maintainers:
|
||||
- Jaehoon Chung <jh80.chung@samsung.com>
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- samsung,s3c6410-sdhci
|
||||
- samsung,exynos4210-sdhci
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
minItems: 2
|
||||
maxItems: 5
|
||||
|
||||
clock-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: hsmmc
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
- pattern: "^mmc_busclk.[0-3]$"
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- samsung,exynos4210-sdhci
|
||||
then:
|
||||
properties:
|
||||
clocks:
|
||||
maxItems: 2
|
||||
clock-names:
|
||||
items:
|
||||
- const: hsmmc
|
||||
- const: mmc_busclk.2
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/exynos4.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
mmc@12510000 {
|
||||
compatible = "samsung,exynos4210-sdhci";
|
||||
reg = <0x12510000 0x100>;
|
||||
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>;
|
||||
clock-names = "hsmmc", "mmc_busclk.2";
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpx3 4 GPIO_ACTIVE_LOW>;
|
||||
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sdhci2_cd>;
|
||||
pinctrl-names = "default";
|
||||
vmmc-supply = <&ldo21_reg>;
|
||||
};
|
@ -1,32 +0,0 @@
|
||||
* Samsung's SDHCI Controller device tree bindings
|
||||
|
||||
Samsung's SDHCI controller is used as a connectivity interface with external
|
||||
MMC, SD and eMMC storage mediums. This file documents differences between the
|
||||
core mmc properties described by mmc.txt and the properties used by the
|
||||
Samsung implementation of the SDHCI controller.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci
|
||||
controller.
|
||||
- "samsung,exynos4210-sdhci": For controllers compatible with Exynos4 sdhci
|
||||
controller.
|
||||
|
||||
Required Board Specific Properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Example:
|
||||
sdhci@12530000 {
|
||||
compatible = "samsung,exynos4210-sdhci";
|
||||
reg = <0x12530000 0x100>;
|
||||
interrupts = <0 75 0>;
|
||||
bus-width = <4>;
|
||||
cd-gpios = <&gpk2 2 0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>;
|
||||
};
|
||||
|
||||
Note: This example shows both SoC specific and board specific properties
|
||||
in a single device node. The properties can be actually be separated
|
||||
into SoC specific node and board specific node.
|
@ -17,6 +17,9 @@ description:
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- enum:
|
||||
- qcom,sdhci-msm-v4
|
||||
deprecated: true
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,apq8084-sdhci
|
||||
@ -27,6 +30,10 @@ properties:
|
||||
- qcom,msm8992-sdhci
|
||||
- qcom,msm8994-sdhci
|
||||
- qcom,msm8996-sdhci
|
||||
- qcom,msm8998-sdhci
|
||||
- const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,qcs404-sdhci
|
||||
- qcom,sc7180-sdhci
|
||||
- qcom,sc7280-sdhci
|
||||
@ -38,20 +45,16 @@ properties:
|
||||
- qcom,sm6350-sdhci
|
||||
- qcom,sm8150-sdhci
|
||||
- qcom,sm8250-sdhci
|
||||
- enum:
|
||||
- qcom,sdhci-msm-v4 # for sdcc versions less than 5.0
|
||||
- qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||
- items:
|
||||
- const: qcom,sdhci-msm-v4 # Deprecated (only for backward compatibility)
|
||||
# for sdcc versions less than 5.0
|
||||
- qcom,sm8450-sdhci
|
||||
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: SD Core register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
maxItems: 4
|
||||
|
||||
reg-names:
|
||||
minItems: 1
|
||||
maxItems: 4
|
||||
|
||||
clocks:
|
||||
minItems: 3
|
||||
@ -93,6 +96,9 @@ properties:
|
||||
description:
|
||||
Should specify pin control groups used for this controller.
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
qcom,ddr-config:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: platform specific settings for DDR_CONFIG reg.
|
||||
@ -121,6 +127,16 @@ properties:
|
||||
description: A phandle to sdhci power domain node
|
||||
maxItems: 1
|
||||
|
||||
mmc-ddr-1_8v: true
|
||||
|
||||
mmc-hs200-1_8v: true
|
||||
|
||||
mmc-hs400-1_8v: true
|
||||
|
||||
bus-width: true
|
||||
|
||||
max-frequency: true
|
||||
|
||||
patternProperties:
|
||||
'^opp-table(-[a-z0-9]+)?$':
|
||||
if:
|
||||
@ -140,7 +156,47 @@ required:
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
additionalProperties: true
|
||||
allOf:
|
||||
- $ref: mmc-controller.yaml#
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdhci-msm-v4
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 2
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: SD Core register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
reg-names:
|
||||
minItems: 2
|
||||
items:
|
||||
- const: hc
|
||||
- const: core
|
||||
- const: cqhci
|
||||
- const: ice
|
||||
else:
|
||||
properties:
|
||||
reg:
|
||||
minItems: 1
|
||||
items:
|
||||
- description: Host controller register map
|
||||
- description: CQE register map
|
||||
- description: Inline Crypto Engine register map
|
||||
reg-names:
|
||||
minItems: 1
|
||||
items:
|
||||
- const: hc
|
||||
- const: cqhci
|
||||
- const: ice
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
@ -149,7 +205,7 @@ examples:
|
||||
#include <dt-bindings/clock/qcom,rpmh.h>
|
||||
#include <dt-bindings/power/qcom-rpmpd.h>
|
||||
|
||||
sdhc_2: sdhci@8804000 {
|
||||
sdhc_2: mmc@8804000 {
|
||||
compatible = "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5";
|
||||
reg = <0 0x08804000 0 0x1000>;
|
||||
|
||||
|
@ -1341,17 +1341,17 @@ static int msb_ftl_initialize(struct msb_data *msb)
|
||||
msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE;
|
||||
msb->logical_block_count = msb->zone_count * 496 - 2;
|
||||
|
||||
msb->used_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
|
||||
msb->erased_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL);
|
||||
msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
|
||||
msb->erased_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL);
|
||||
msb->lba_to_pba_table =
|
||||
kmalloc_array(msb->logical_block_count, sizeof(u16),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!msb->used_blocks_bitmap || !msb->lba_to_pba_table ||
|
||||
!msb->erased_blocks_bitmap) {
|
||||
kfree(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->erased_blocks_bitmap);
|
||||
kfree(msb->lba_to_pba_table);
|
||||
kfree(msb->erased_blocks_bitmap);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1946,7 +1946,8 @@ static DEFINE_MUTEX(msb_disk_lock); /* protects against races in open/release */
|
||||
static void msb_data_clear(struct msb_data *msb)
|
||||
{
|
||||
kfree(msb->boot_page);
|
||||
kfree(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->used_blocks_bitmap);
|
||||
bitmap_free(msb->erased_blocks_bitmap);
|
||||
kfree(msb->lba_to_pba_table);
|
||||
kfree(msb->cache);
|
||||
msb->card = NULL;
|
||||
@ -2243,8 +2244,8 @@ static int msb_resume(struct memstick_dev *card)
|
||||
goto out;
|
||||
|
||||
if (msb->block_count != new_msb->block_count ||
|
||||
memcmp(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap,
|
||||
msb->block_count / 8))
|
||||
!bitmap_equal(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap,
|
||||
msb->block_count))
|
||||
goto out;
|
||||
|
||||
card_dead = false;
|
||||
|
@ -176,7 +176,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card,
|
||||
unsigned int part_type);
|
||||
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_card *card,
|
||||
int disable_multi,
|
||||
int recovery_mode,
|
||||
struct mmc_queue *mq);
|
||||
static void mmc_blk_hsq_req_done(struct mmc_request *mrq);
|
||||
|
||||
@ -1302,7 +1302,7 @@ static void mmc_blk_eval_resp_error(struct mmc_blk_request *brq)
|
||||
}
|
||||
|
||||
static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
int disable_multi, bool *do_rel_wr_p,
|
||||
int recovery_mode, bool *do_rel_wr_p,
|
||||
bool *do_data_tag_p)
|
||||
{
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
@ -1368,12 +1368,12 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq,
|
||||
brq->data.blocks--;
|
||||
|
||||
/*
|
||||
* After a read error, we redo the request one sector
|
||||
* After a read error, we redo the request one (native) sector
|
||||
* at a time in order to accurately determine which
|
||||
* sectors can be read successfully.
|
||||
*/
|
||||
if (disable_multi)
|
||||
brq->data.blocks = 1;
|
||||
if (recovery_mode)
|
||||
brq->data.blocks = queue_physical_block_size(mq->queue) >> 9;
|
||||
|
||||
/*
|
||||
* Some controllers have HW issues while operating
|
||||
@ -1590,7 +1590,7 @@ static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req)
|
||||
|
||||
static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_card *card,
|
||||
int disable_multi,
|
||||
int recovery_mode,
|
||||
struct mmc_queue *mq)
|
||||
{
|
||||
u32 readcmd, writecmd;
|
||||
@ -1599,7 +1599,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
|
||||
struct mmc_blk_data *md = mq->blkdata;
|
||||
bool do_rel_wr, do_data_tag;
|
||||
|
||||
mmc_blk_data_prep(mq, mqrq, disable_multi, &do_rel_wr, &do_data_tag);
|
||||
mmc_blk_data_prep(mq, mqrq, recovery_mode, &do_rel_wr, &do_data_tag);
|
||||
|
||||
brq->mrq.cmd = &brq->cmd;
|
||||
|
||||
@ -1690,7 +1690,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req)
|
||||
|
||||
#define MMC_READ_SINGLE_RETRIES 2
|
||||
|
||||
/* Single sector read during recovery */
|
||||
/* Single (native) sector read during recovery */
|
||||
static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
{
|
||||
struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req);
|
||||
@ -1698,6 +1698,7 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
struct mmc_card *card = mq->card;
|
||||
struct mmc_host *host = card->host;
|
||||
blk_status_t error = BLK_STS_OK;
|
||||
size_t bytes_per_read = queue_physical_block_size(mq->queue);
|
||||
|
||||
do {
|
||||
u32 status;
|
||||
@ -1732,13 +1733,13 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req)
|
||||
else
|
||||
error = BLK_STS_OK;
|
||||
|
||||
} while (blk_update_request(req, error, 512));
|
||||
} while (blk_update_request(req, error, bytes_per_read));
|
||||
|
||||
return;
|
||||
|
||||
error_exit:
|
||||
mrq->data->bytes_xfered = 0;
|
||||
blk_update_request(req, BLK_STS_IOERR, 512);
|
||||
blk_update_request(req, BLK_STS_IOERR, bytes_per_read);
|
||||
/* Let it try the remaining request again */
|
||||
if (mqrq->retries > MMC_MAX_RETRIES - 1)
|
||||
mqrq->retries = MMC_MAX_RETRIES - 1;
|
||||
@ -1879,10 +1880,9 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req)
|
||||
return;
|
||||
}
|
||||
|
||||
/* FIXME: Missing single sector read for large sector size */
|
||||
if (!mmc_large_sector(card) && rq_data_dir(req) == READ &&
|
||||
brq->data.blocks > 1) {
|
||||
/* Read one sector at a time */
|
||||
if (rq_data_dir(req) == READ && brq->data.blocks >
|
||||
queue_physical_block_size(mq->queue) >> 9) {
|
||||
/* Read one (native) sector at a time */
|
||||
mmc_blk_read_single(mq, req);
|
||||
return;
|
||||
}
|
||||
@ -2988,7 +2988,7 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||
* Don't enable runtime PM for SD-combo cards here. Leave that
|
||||
* decision to be taken during the SDIO init sequence instead.
|
||||
*/
|
||||
if (card->type != MMC_TYPE_SD_COMBO) {
|
||||
if (!mmc_card_sd_combo(card)) {
|
||||
pm_runtime_set_active(&card->dev);
|
||||
pm_runtime_enable(&card->dev);
|
||||
}
|
||||
@ -3015,7 +3015,7 @@ static void mmc_blk_remove(struct mmc_card *card)
|
||||
mmc_blk_part_switch(card, md->part_type);
|
||||
mmc_release_host(card->host);
|
||||
}
|
||||
if (card->type != MMC_TYPE_SD_COMBO)
|
||||
if (!mmc_card_sd_combo(card))
|
||||
pm_runtime_disable(&card->dev);
|
||||
pm_runtime_put_noidle(&card->dev);
|
||||
mmc_blk_remove_req(md);
|
||||
|
@ -85,7 +85,7 @@ 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) {
|
||||
if (mmc_card_sdio(card) || mmc_card_sd_combo(card)) {
|
||||
retval = add_uevent_var(env, "SDIO_ID=%04X:%04X",
|
||||
card->cis.vendor, card->cis.device);
|
||||
if (retval)
|
||||
@ -107,7 +107,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
* 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)
|
||||
if (mmc_card_sdio(card))
|
||||
return 0;
|
||||
|
||||
retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card));
|
||||
|
@ -943,9 +943,11 @@ int mmc_execute_tuning(struct mmc_card *card)
|
||||
}
|
||||
|
||||
/* Only print error when we don't check for card removal */
|
||||
if (!host->detect_change)
|
||||
if (!host->detect_change) {
|
||||
pr_err("%s: tuning execution failed: %d\n",
|
||||
mmc_hostname(host), err);
|
||||
mmc_debugfs_err_stats_inc(host, MMC_ERR_TUNING);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
@ -2244,6 +2246,12 @@ void mmc_rescan(struct work_struct *work)
|
||||
if (freqs[i] <= host->f_min)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore the command timeout errors observed during
|
||||
* the card init as those are excepted.
|
||||
*/
|
||||
host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0;
|
||||
mmc_release_host(host);
|
||||
|
||||
out:
|
||||
|
@ -223,6 +223,81 @@ static int mmc_clock_opt_set(void *data, u64 val)
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set,
|
||||
"%llu\n");
|
||||
|
||||
static int mmc_err_state_get(void *data, u64 *val)
|
||||
{
|
||||
struct mmc_host *host = data;
|
||||
int i;
|
||||
|
||||
if (!host)
|
||||
return -EINVAL;
|
||||
|
||||
*val = 0;
|
||||
for (i = 0; i < MMC_ERR_MAX; i++) {
|
||||
if (host->err_stats[i]) {
|
||||
*val = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(mmc_err_state, mmc_err_state_get, NULL, "%llu\n");
|
||||
|
||||
static int mmc_err_stats_show(struct seq_file *file, void *data)
|
||||
{
|
||||
struct mmc_host *host = (struct mmc_host *)file->private;
|
||||
const char *desc[MMC_ERR_MAX] = {
|
||||
[MMC_ERR_CMD_TIMEOUT] = "Command Timeout Occurred",
|
||||
[MMC_ERR_CMD_CRC] = "Command CRC Errors Occurred",
|
||||
[MMC_ERR_DAT_TIMEOUT] = "Data Timeout Occurred",
|
||||
[MMC_ERR_DAT_CRC] = "Data CRC Errors Occurred",
|
||||
[MMC_ERR_AUTO_CMD] = "Auto-Cmd Error Occurred",
|
||||
[MMC_ERR_ADMA] = "ADMA Error Occurred",
|
||||
[MMC_ERR_TUNING] = "Tuning Error Occurred",
|
||||
[MMC_ERR_CMDQ_RED] = "CMDQ RED Errors",
|
||||
[MMC_ERR_CMDQ_GCE] = "CMDQ GCE Errors",
|
||||
[MMC_ERR_CMDQ_ICCE] = "CMDQ ICCE Errors",
|
||||
[MMC_ERR_REQ_TIMEOUT] = "Request Timedout",
|
||||
[MMC_ERR_CMDQ_REQ_TIMEOUT] = "CMDQ Request Timedout",
|
||||
[MMC_ERR_ICE_CFG] = "ICE Config Errors",
|
||||
[MMC_ERR_CTRL_TIMEOUT] = "Controller Timedout errors",
|
||||
[MMC_ERR_UNEXPECTED_IRQ] = "Unexpected IRQ errors",
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MMC_ERR_MAX; i++) {
|
||||
if (desc[i])
|
||||
seq_printf(file, "# %s:\t %d\n",
|
||||
desc[i], host->err_stats[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mmc_err_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, mmc_err_stats_show, inode->i_private);
|
||||
}
|
||||
|
||||
static ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct mmc_host *host = filp->f_mapping->host->i_private;
|
||||
|
||||
pr_debug("%s: Resetting MMC error statistics\n", __func__);
|
||||
memset(host->err_stats, 0, sizeof(host->err_stats));
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations mmc_err_stats_fops = {
|
||||
.open = mmc_err_stats_open,
|
||||
.read = seq_read,
|
||||
.write = mmc_err_stats_write,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
{
|
||||
struct dentry *root;
|
||||
@ -236,6 +311,11 @@ void mmc_add_host_debugfs(struct mmc_host *host)
|
||||
debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host,
|
||||
&mmc_clock_fops);
|
||||
|
||||
debugfs_create_file_unsafe("err_state", 0600, root, host,
|
||||
&mmc_err_state);
|
||||
debugfs_create_file("err_stats", 0600, root, host,
|
||||
&mmc_err_stats_fops);
|
||||
|
||||
#ifdef CONFIG_FAIL_MMC_REQUEST
|
||||
if (fail_request)
|
||||
setup_fault_attr(&fail_default_attr, fail_request);
|
||||
|
@ -599,7 +599,7 @@ static int mmc_validate_host_caps(struct mmc_host *host)
|
||||
}
|
||||
|
||||
if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) &&
|
||||
!(caps & MMC_CAP_8_BIT_DATA)) {
|
||||
!(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) {
|
||||
dev_warn(dev, "drop HS400 support since no 8-bit bus\n");
|
||||
host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400;
|
||||
}
|
||||
|
@ -163,8 +163,10 @@ static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card,
|
||||
struct device_node *np;
|
||||
|
||||
for_each_child_of_node(mmc_dev(card->host)->of_node, np) {
|
||||
if (of_device_is_compatible(np, compatible))
|
||||
if (of_device_is_compatible(np, compatible)) {
|
||||
of_node_put(np);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -793,7 +793,7 @@ static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
attr == &dev_attr_info2.attr ||
|
||||
attr == &dev_attr_info3.attr ||
|
||||
attr == &dev_attr_info4.attr
|
||||
) && card->type != MMC_TYPE_SD_COMBO)
|
||||
) &&!mmc_card_sd_combo(card))
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
@ -870,7 +870,7 @@ try_again:
|
||||
* the CCS bit is set as well. We deliberately deviate from the spec in
|
||||
* regards to this, which allows UHS-I to be supported for SDSC cards.
|
||||
*/
|
||||
if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x01000000)) {
|
||||
if (!mmc_host_is_spi(host) && rocr && (*rocr & SD_ROCR_S18A)) {
|
||||
err = mmc_set_uhs_voltage(host, pocr);
|
||||
if (err == -EAGAIN) {
|
||||
retries--;
|
||||
|
@ -226,6 +226,20 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C;
|
||||
if (data & SDIO_DRIVE_SDTD)
|
||||
card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D;
|
||||
|
||||
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTERRUPT_EXT, 0, &data);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (data & SDIO_INTERRUPT_EXT_SAI) {
|
||||
data |= SDIO_INTERRUPT_EXT_EAI;
|
||||
ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_INTERRUPT_EXT,
|
||||
data, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
card->cccr.enable_async_irq = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* if no uhs mode ensure we check for high speed */
|
||||
@ -335,7 +349,7 @@ static int sdio_disable_4bit_bus(struct mmc_card *card)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
if (mmc_card_sdio(card))
|
||||
goto out;
|
||||
|
||||
if (!(card->host->caps & MMC_CAP_4_BIT_DATA))
|
||||
@ -360,7 +374,7 @@ static int sdio_enable_4bit_bus(struct mmc_card *card)
|
||||
err = sdio_enable_wide(card);
|
||||
if (err <= 0)
|
||||
return err;
|
||||
if (card->type == MMC_TYPE_SDIO)
|
||||
if (mmc_card_sdio(card))
|
||||
goto out;
|
||||
|
||||
if (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) {
|
||||
@ -415,7 +429,7 @@ static int sdio_enable_hs(struct mmc_card *card)
|
||||
int ret;
|
||||
|
||||
ret = mmc_sdio_switch_hs(card, true);
|
||||
if (ret <= 0 || card->type == MMC_TYPE_SDIO)
|
||||
if (ret <= 0 || mmc_card_sdio(card))
|
||||
return ret;
|
||||
|
||||
ret = mmc_sd_switch_hs(card);
|
||||
@ -441,7 +455,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card)
|
||||
max_dtr = card->cis.max_dtr;
|
||||
}
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO)
|
||||
if (mmc_card_sd_combo(card))
|
||||
max_dtr = min(max_dtr, mmc_sd_get_max_clock(card));
|
||||
|
||||
return max_dtr;
|
||||
@ -689,7 +703,7 @@ try_again:
|
||||
mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) {
|
||||
card->type = MMC_TYPE_SD_COMBO;
|
||||
|
||||
if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO ||
|
||||
if (oldcard && (!mmc_card_sd_combo(oldcard) ||
|
||||
memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) {
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
@ -697,7 +711,7 @@ try_again:
|
||||
} else {
|
||||
card->type = MMC_TYPE_SDIO;
|
||||
|
||||
if (oldcard && oldcard->type != MMC_TYPE_SDIO) {
|
||||
if (oldcard && !mmc_card_sdio(oldcard)) {
|
||||
err = -ENOENT;
|
||||
goto mismatch;
|
||||
}
|
||||
@ -754,7 +768,7 @@ try_again:
|
||||
/*
|
||||
* Read CSD, before selecting the card
|
||||
*/
|
||||
if (!oldcard && card->type == MMC_TYPE_SD_COMBO) {
|
||||
if (!oldcard && mmc_card_sd_combo(card)) {
|
||||
err = mmc_sd_get_csd(card);
|
||||
if (err)
|
||||
goto remove;
|
||||
@ -827,7 +841,7 @@ try_again:
|
||||
|
||||
mmc_fixup_device(card, sdio_fixup_methods);
|
||||
|
||||
if (card->type == MMC_TYPE_SD_COMBO) {
|
||||
if (mmc_card_sd_combo(card)) {
|
||||
err = mmc_sd_setup_card(host, card, oldcard != NULL);
|
||||
/* handle as SDIO-only card if memory init failed */
|
||||
if (err) {
|
||||
|
@ -277,6 +277,7 @@ static int octeon_mmc_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error populating slots\n");
|
||||
octeon_mmc_set_shared_power(host, 0);
|
||||
of_node_put(cn);
|
||||
goto error;
|
||||
}
|
||||
i++;
|
||||
|
@ -142,8 +142,10 @@ static int thunder_mmc_probe(struct pci_dev *pdev,
|
||||
continue;
|
||||
|
||||
ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
of_node_put(child_node);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
@ -822,8 +822,15 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
|
||||
pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status);
|
||||
|
||||
if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) ||
|
||||
cmd_error || data_error)
|
||||
cmd_error || data_error) {
|
||||
if (status & CQHCI_IS_RED)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_RED);
|
||||
if (status & CQHCI_IS_GCE)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_GCE);
|
||||
if (status & CQHCI_IS_ICCE)
|
||||
mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_ICCE);
|
||||
cqhci_error_irq(mmc, status, cmd_error, data_error);
|
||||
}
|
||||
|
||||
if (status & CQHCI_IS_TCC) {
|
||||
/* read TCN and complete the request */
|
||||
|
@ -670,7 +670,9 @@ static int dw_mci_exynos_remove(struct platform_device *pdev)
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_exynos_pmops = {
|
||||
|
@ -179,7 +179,9 @@ static int dw_mci_hi3798cv200_remove(struct platform_device *pdev)
|
||||
clk_disable_unprepare(priv->drive_clk);
|
||||
clk_disable_unprepare(priv->sample_clk);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id dw_mci_hi3798cv200_match[] = {
|
||||
|
@ -377,7 +377,9 @@ static int dw_mci_rockchip_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
|
||||
return dw_mci_pltfm_remove(pdev);
|
||||
dw_mci_pltfm_remove(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = {
|
||||
|
@ -762,7 +762,7 @@ int mmci_dmae_setup(struct mmci_host *host)
|
||||
|
||||
/*
|
||||
* If only an RX channel is specified, the driver will
|
||||
* attempt to use it bidirectionally, however if it is
|
||||
* attempt to use it bidirectionally, however if it
|
||||
* is specified but cannot be located, DMA will be disabled.
|
||||
*/
|
||||
if (dmae->rx_channel && !dmae->tx_channel)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2014-2015 MediaTek Inc.
|
||||
* Copyright (c) 2014-2015, 2022 MediaTek Inc.
|
||||
* Author: Chaotian.Jing <chaotian.jing@mediatek.com>
|
||||
*/
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
@ -440,8 +441,10 @@ struct msdc_host {
|
||||
struct pinctrl *pinctrl;
|
||||
struct pinctrl_state *pins_default;
|
||||
struct pinctrl_state *pins_uhs;
|
||||
struct pinctrl_state *pins_eint;
|
||||
struct delayed_work req_timeout;
|
||||
int irq; /* host interrupt */
|
||||
int eint_irq; /* interrupt from sdio device for waking up system */
|
||||
struct reset_control *reset;
|
||||
|
||||
struct clk *src_clk; /* msdc source clock */
|
||||
@ -1521,17 +1524,46 @@ static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb)
|
||||
|
||||
static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&host->lock, flags);
|
||||
__msdc_enable_sdio_irq(host, enb);
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
if (enb)
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
else
|
||||
pm_runtime_put_noidle(host->dev);
|
||||
if (mmc_card_enable_async_irq(mmc->card) && host->pins_eint) {
|
||||
if (enb) {
|
||||
/*
|
||||
* In dev_pm_set_dedicated_wake_irq_reverse(), eint pin will be set to
|
||||
* GPIO mode. We need to restore it to SDIO DAT1 mode after that.
|
||||
* Since the current pinstate is pins_uhs, to ensure pinctrl select take
|
||||
* affect successfully, we change the pinstate to pins_eint firstly.
|
||||
*/
|
||||
pinctrl_select_state(host->pinctrl, host->pins_eint);
|
||||
ret = dev_pm_set_dedicated_wake_irq_reverse(host->dev, host->eint_irq);
|
||||
|
||||
if (ret) {
|
||||
dev_err(host->dev, "Failed to register SDIO wakeup irq!\n");
|
||||
host->pins_eint = NULL;
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
} else {
|
||||
dev_dbg(host->dev, "SDIO eint irq: %d!\n", host->eint_irq);
|
||||
}
|
||||
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
} else {
|
||||
dev_pm_clear_wake_irq(host->dev);
|
||||
}
|
||||
} else {
|
||||
if (enb) {
|
||||
/* Ensure host->pins_eint is NULL */
|
||||
host->pins_eint = NULL;
|
||||
pm_runtime_get_noresume(host->dev);
|
||||
} else {
|
||||
pm_runtime_put_noidle(host->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
|
||||
@ -2319,7 +2351,7 @@ static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card
|
||||
else
|
||||
val = readl(host->base + PAD_DS_TUNE);
|
||||
|
||||
dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val);
|
||||
dev_info(host->dev, "Final PAD_DS_TUNE: 0x%x\n", val);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -2635,6 +2667,20 @@ static int msdc_drv_probe(struct platform_device *pdev)
|
||||
goto host_free;
|
||||
}
|
||||
|
||||
/* Support for SDIO eint irq ? */
|
||||
if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) {
|
||||
host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup");
|
||||
if (host->eint_irq > 0) {
|
||||
host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint");
|
||||
if (IS_ERR(host->pins_eint)) {
|
||||
dev_err(&pdev->dev, "Cannot find pinctrl eint!\n");
|
||||
host->pins_eint = NULL;
|
||||
} else {
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msdc_of_property_parse(pdev, host);
|
||||
|
||||
host->dev = &pdev->dev;
|
||||
@ -2849,6 +2895,15 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev)
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
msdc_save_reg(host);
|
||||
|
||||
if (sdio_irq_claimed(mmc)) {
|
||||
if (host->pins_eint) {
|
||||
disable_irq(host->irq);
|
||||
pinctrl_select_state(host->pinctrl, host->pins_eint);
|
||||
}
|
||||
|
||||
__msdc_enable_sdio_irq(host, 0);
|
||||
}
|
||||
msdc_gate_clock(host);
|
||||
return 0;
|
||||
}
|
||||
@ -2864,12 +2919,18 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev)
|
||||
return ret;
|
||||
|
||||
msdc_restore_reg(host);
|
||||
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint) {
|
||||
pinctrl_select_state(host->pinctrl, host->pins_uhs);
|
||||
enable_irq(host->irq);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msdc_suspend(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
int ret;
|
||||
|
||||
if (mmc->caps2 & MMC_CAP2_CQE) {
|
||||
@ -2878,11 +2939,24 @@ static int __maybe_unused msdc_suspend(struct device *dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bump up runtime PM usage counter otherwise dev->power.needs_force_resume will
|
||||
* not be marked as 1, pm_runtime_force_resume() will go out directly.
|
||||
*/
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint)
|
||||
pm_runtime_get_noresume(dev);
|
||||
|
||||
return pm_runtime_force_suspend(dev);
|
||||
}
|
||||
|
||||
static int __maybe_unused msdc_resume(struct device *dev)
|
||||
{
|
||||
struct mmc_host *mmc = dev_get_drvdata(dev);
|
||||
struct msdc_host *host = mmc_priv(mmc);
|
||||
|
||||
if (sdio_irq_claimed(mmc) && host->pins_eint)
|
||||
pm_runtime_put_noidle(dev);
|
||||
|
||||
return pm_runtime_force_resume(dev);
|
||||
}
|
||||
|
||||
|
@ -923,7 +923,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card)
|
||||
* One way to prevent this is to only allow 1-bit transfers.
|
||||
*/
|
||||
|
||||
if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO)
|
||||
if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card))
|
||||
host->caps &= ~MMC_CAP_4_BIT_DATA;
|
||||
else
|
||||
host->caps |= MMC_CAP_4_BIT_DATA;
|
||||
@ -1025,7 +1025,7 @@ static int mxcmci_probe(struct platform_device *pdev)
|
||||
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
|
||||
mmc->max_seg_size = mmc->max_req_size;
|
||||
|
||||
host->devtype = (enum mxcmci_type)of_device_get_match_data(&pdev->dev);
|
||||
host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||
|
||||
/* adjust max_segs after devtype detection */
|
||||
if (!is_mpc512x_mmc(host))
|
||||
|
@ -43,6 +43,7 @@ struct renesas_sdhi_quirks {
|
||||
bool hs400_4taps;
|
||||
bool fixed_addr_mode;
|
||||
bool dma_one_rx_only;
|
||||
bool manual_tap_correction;
|
||||
u32 hs400_bad_taps;
|
||||
const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX];
|
||||
};
|
||||
|
@ -49,9 +49,6 @@
|
||||
#define HOST_MODE_GEN3_32BIT (HOST_MODE_GEN3_WMODE | HOST_MODE_GEN3_BUSWIDTH)
|
||||
#define HOST_MODE_GEN3_64BIT 0
|
||||
|
||||
#define CTL_SDIF_MODE 0xe6
|
||||
#define SDIF_MODE_HS400 BIT(0)
|
||||
|
||||
#define SDHI_VER_GEN2_SDR50 0x490c
|
||||
#define SDHI_VER_RZ_A1 0x820b
|
||||
/* very old datasheets said 0x490c for SDR104, too. They are wrong! */
|
||||
@ -383,8 +380,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF,
|
||||
priv->scc_tappos_hs400);
|
||||
|
||||
/* Gen3 can't do automatic tap correction with HS400, so disable it */
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC)
|
||||
if (priv->quirks && priv->quirks->manual_tap_correction)
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
|
||||
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
|
||||
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
|
||||
@ -562,23 +558,25 @@ static void renesas_sdhi_scc_reset(struct tmio_mmc_host *host, struct renesas_sd
|
||||
}
|
||||
|
||||
/* only populated for TMIO_MMC_MIN_RCAR2 */
|
||||
static void renesas_sdhi_reset(struct tmio_mmc_host *host)
|
||||
static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
struct renesas_sdhi *priv = host_to_priv(host);
|
||||
int ret;
|
||||
u16 val;
|
||||
|
||||
if (priv->rstc) {
|
||||
reset_control_reset(priv->rstc);
|
||||
/* Unknown why but without polling reset status, it will hang */
|
||||
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
|
||||
false, priv->rstc);
|
||||
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
priv->needs_adjust_hs400 = false;
|
||||
renesas_sdhi_set_clock(host, host->clk_cache);
|
||||
} else if (priv->scc_ctl) {
|
||||
renesas_sdhi_scc_reset(host, priv);
|
||||
if (!preserve) {
|
||||
if (priv->rstc) {
|
||||
reset_control_reset(priv->rstc);
|
||||
/* Unknown why but without polling reset status, it will hang */
|
||||
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
|
||||
false, priv->rstc);
|
||||
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
|
||||
priv->needs_adjust_hs400 = false;
|
||||
renesas_sdhi_set_clock(host, host->clk_cache);
|
||||
} else if (priv->scc_ctl) {
|
||||
renesas_sdhi_scc_reset(host, priv);
|
||||
}
|
||||
}
|
||||
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) >= SDHI_VER_GEN3_SD) {
|
||||
@ -719,7 +717,7 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
|
||||
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
|
||||
|
||||
/* Change TAP position according to correction status */
|
||||
if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC &&
|
||||
if (priv->quirks && priv->quirks->manual_tap_correction &&
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
|
||||
/*
|
||||
@ -938,6 +936,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
if (IS_ERR(priv->clk_cd))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
|
||||
|
||||
priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
priv->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||
if (!IS_ERR(priv->pinctrl)) {
|
||||
priv->pins_default = pinctrl_lookup_state(priv->pinctrl,
|
||||
@ -1030,10 +1032,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||
if (ret)
|
||||
goto efree;
|
||||
|
||||
priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
ver = sd_ctrl_read16(host, CTL_VERSION);
|
||||
/* GEN2_SDR104 is first known SDHI to use 32bit block count */
|
||||
if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
|
||||
|
@ -170,6 +170,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400_one_rx = {
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
|
||||
.hs400_4taps = true,
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = {
|
||||
@ -182,25 +183,30 @@ static const struct renesas_sdhi_quirks sdhi_quirks_fixed_addr = {
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = {
|
||||
.hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = {
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a7796_es13 = {
|
||||
.hs400_4taps = true,
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.hs400_calib_table = r8a7796_es13_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77965 = {
|
||||
.hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7),
|
||||
.hs400_calib_table = r8a77965_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = {
|
||||
.hs400_calib_table = r8a77990_calib_table,
|
||||
.manual_tap_correction = true,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -268,6 +274,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
|
||||
{ .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, },
|
||||
{ .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{ .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
|
||||
@ -321,7 +328,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
|
||||
}
|
||||
|
||||
/*
|
||||
* renesas_sdhi_internal_dmac_map() will be called with two difference
|
||||
* renesas_sdhi_internal_dmac_map() will be called with two different
|
||||
* sg pointers in two mmc_data by .pre_req(), but tmio host can have a single
|
||||
* sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg
|
||||
* pointer in a mmc_data instead of host->sg_ptr.
|
||||
@ -355,7 +362,7 @@ renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host,
|
||||
|
||||
data->host_cookie = cookie;
|
||||
|
||||
/* This DMAC cannot handle if buffer is not 128-bytes alignment */
|
||||
/* This DMAC needs buffers to be 128-byte aligned */
|
||||
if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) {
|
||||
renesas_sdhi_internal_dmac_unmap(host, data, cookie);
|
||||
return false;
|
||||
|
@ -31,6 +31,8 @@
|
||||
struct sdhci_brcmstb_priv {
|
||||
void __iomem *cfg_regs;
|
||||
unsigned int flags;
|
||||
struct clk *base_clk;
|
||||
u32 base_freq_hz;
|
||||
};
|
||||
|
||||
struct brcmstb_match_priv {
|
||||
@ -250,9 +252,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
const struct of_device_id *match;
|
||||
struct sdhci_brcmstb_priv *priv;
|
||||
u32 actual_clock_mhz;
|
||||
struct sdhci_host *host;
|
||||
struct resource *iomem;
|
||||
struct clk *clk;
|
||||
struct clk *base_clk = NULL;
|
||||
int res;
|
||||
|
||||
match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node);
|
||||
@ -330,6 +334,35 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT)
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
|
||||
/* Change the base clock frequency if the DT property exists */
|
||||
if (device_property_read_u32(&pdev->dev, "clock-frequency",
|
||||
&priv->base_freq_hz) != 0)
|
||||
goto add_host;
|
||||
|
||||
base_clk = devm_clk_get_optional(&pdev->dev, "sdio_freq");
|
||||
if (IS_ERR(base_clk)) {
|
||||
dev_warn(&pdev->dev, "Clock for \"sdio_freq\" not found\n");
|
||||
goto add_host;
|
||||
}
|
||||
|
||||
res = clk_prepare_enable(base_clk);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
/* set improved clock rate */
|
||||
clk_set_rate(base_clk, priv->base_freq_hz);
|
||||
actual_clock_mhz = clk_get_rate(base_clk) / 1000000;
|
||||
|
||||
host->caps &= ~SDHCI_CLOCK_V3_BASE_MASK;
|
||||
host->caps |= (actual_clock_mhz << SDHCI_CLOCK_BASE_SHIFT);
|
||||
/* Disable presets because they are now incorrect */
|
||||
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
|
||||
|
||||
dev_dbg(&pdev->dev, "Base Clock Frequency changed to %dMHz\n",
|
||||
actual_clock_mhz);
|
||||
priv->base_clk = base_clk;
|
||||
|
||||
add_host:
|
||||
res = sdhci_brcmstb_add_host(host, priv);
|
||||
if (res)
|
||||
goto err;
|
||||
@ -340,6 +373,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||
err:
|
||||
sdhci_pltfm_free(pdev);
|
||||
err_clk:
|
||||
clk_disable_unprepare(base_clk);
|
||||
clk_disable_unprepare(clk);
|
||||
return res;
|
||||
}
|
||||
@ -351,11 +385,51 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev)
|
||||
|
||||
MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_brcmstb_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
|
||||
clk_disable_unprepare(priv->base_clk);
|
||||
return sdhci_pltfm_suspend(dev);
|
||||
}
|
||||
|
||||
static int sdhci_brcmstb_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_resume(dev);
|
||||
if (!ret && priv->base_freq_hz) {
|
||||
ret = clk_prepare_enable(priv->base_clk);
|
||||
/*
|
||||
* Note: using clk_get_rate() below as clk_get_rate()
|
||||
* honors CLK_GET_RATE_NOCACHE attribute, but clk_set_rate()
|
||||
* may do implicit get_rate() calls that do not honor
|
||||
* CLK_GET_RATE_NOCACHE.
|
||||
*/
|
||||
if (!ret &&
|
||||
(clk_get_rate(priv->base_clk) != priv->base_freq_hz))
|
||||
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops sdhci_brcmstb_pmops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_brcmstb_driver = {
|
||||
.driver = {
|
||||
.name = "sdhci-brcmstb",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &sdhci_pltfm_pmops,
|
||||
.pm = &sdhci_brcmstb_pmops,
|
||||
.of_match_table = of_match_ptr(sdhci_brcm_of_match),
|
||||
},
|
||||
.probe = sdhci_brcmstb_probe,
|
||||
|
@ -2435,33 +2435,12 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = {
|
||||
};
|
||||
|
||||
static const struct of_device_id sdhci_msm_dt_match[] = {
|
||||
/* Following two entries are deprecated (kept only for backward compatibility) */
|
||||
/*
|
||||
* Do not add new variants to the driver which are compatible with
|
||||
* generic ones, unless they need customization.
|
||||
*/
|
||||
{.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var},
|
||||
/* Add entries for sdcc versions less than 5.0 here */
|
||||
{.compatible = "qcom,apq8084-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8226-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8916-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8953-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8974-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8992-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8994-sdhci", .data = &sdhci_msm_mci_var},
|
||||
{.compatible = "qcom,msm8996-sdhci", .data = &sdhci_msm_mci_var},
|
||||
/*
|
||||
* Add entries for sdcc version 5.0 here. For SDCC version 5.0.0,
|
||||
* MCI registers are removed from SDCC interface and some registers
|
||||
* are moved to HC.
|
||||
*/
|
||||
{.compatible = "qcom,qcs404-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdx55-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdx65-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sdm630-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm6125-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm6350-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm8150-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sm8250-sdhci", .data = &sdhci_msm_v5_var},
|
||||
{.compatible = "qcom,sc7280-sdhci", .data = &sdhci_msm_v5_var},
|
||||
/* Add entries where soc specific handling is required, here */
|
||||
{.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var},
|
||||
{.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var},
|
||||
{},
|
||||
|
@ -1733,7 +1733,6 @@ err_pltfm_free:
|
||||
|
||||
static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host);
|
||||
@ -1747,11 +1746,11 @@ static int sdhci_arasan_remove(struct platform_device *pdev)
|
||||
|
||||
sdhci_arasan_unregister_sdclk(&pdev->dev);
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(clk_ahb);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sdhci_arasan_driver = {
|
||||
|
@ -100,8 +100,13 @@ static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||
static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
if (timing == MMC_TIMING_MMC_DDR52)
|
||||
sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R);
|
||||
u8 mc1r;
|
||||
|
||||
if (timing == MMC_TIMING_MMC_DDR52) {
|
||||
mc1r = sdhci_readb(host, SDMMC_MC1R);
|
||||
mc1r |= SDMMC_MC1R_DDR;
|
||||
sdhci_writeb(host, mc1r, SDMMC_MC1R);
|
||||
}
|
||||
sdhci_set_uhs_signaling(host, timing);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
@ -30,6 +31,7 @@
|
||||
/* Offset inside the vendor area 1 */
|
||||
#define DWCMSHC_HOST_CTRL3 0x8
|
||||
#define DWCMSHC_EMMC_CONTROL 0x2c
|
||||
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
||||
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
||||
#define DWCMSHC_EMMC_ATCTRL 0x40
|
||||
|
||||
@ -38,7 +40,7 @@
|
||||
#define DWCMSHC_EMMC_DLL_RXCLK 0x804
|
||||
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
|
||||
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
|
||||
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
|
||||
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
|
||||
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
|
||||
#define DWCMSHC_EMMC_DLL_START BIT(0)
|
||||
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
|
||||
@ -47,22 +49,39 @@
|
||||
#define DWCMSHC_EMMC_DLL_START_POINT 16
|
||||
#define DWCMSHC_EMMC_DLL_INC 8
|
||||
#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
|
||||
#define DLL_TXCLK_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
|
||||
#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
|
||||
#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
|
||||
#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
|
||||
#define DLL_STRBIN_DELAY_NUM_OFFSET 16
|
||||
#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16
|
||||
#define DLL_RXCLK_NO_INVERTER 1
|
||||
#define DLL_RXCLK_INVERTER 0
|
||||
#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
|
||||
#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
|
||||
#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
|
||||
#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
|
||||
|
||||
#define DLL_LOCK_WO_TMOUT(x) \
|
||||
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
|
||||
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
|
||||
#define RK3568_MAX_CLKS 3
|
||||
#define RK35xx_MAX_CLKS 3
|
||||
|
||||
#define BOUNDARY_OK(addr, len) \
|
||||
((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
|
||||
|
||||
struct rk3568_priv {
|
||||
enum dwcmshc_rk_type {
|
||||
DWCMSHC_RK3568,
|
||||
DWCMSHC_RK3588,
|
||||
};
|
||||
|
||||
struct rk35xx_priv {
|
||||
/* Rockchip specified optional clocks */
|
||||
struct clk_bulk_data rockchip_clks[RK3568_MAX_CLKS];
|
||||
struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
|
||||
struct reset_control *reset;
|
||||
enum dwcmshc_rk_type devtype;
|
||||
u8 txclk_tapnum;
|
||||
};
|
||||
|
||||
@ -131,7 +150,9 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||
static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
|
||||
unsigned int timing)
|
||||
{
|
||||
u16 ctrl_2;
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
u16 ctrl, ctrl_2;
|
||||
|
||||
ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||
/* Select Bus Speed Mode for host */
|
||||
@ -149,8 +170,15 @@ static void dwcmshc_set_uhs_signaling(struct sdhci_host *host,
|
||||
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)
|
||||
else if (timing == MMC_TIMING_MMC_HS400) {
|
||||
/* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */
|
||||
ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
ctrl |= DWCMSHC_CARD_IS_EMMC;
|
||||
sdhci_writew(host, ctrl, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||
|
||||
ctrl_2 |= DWCMSHC_CTRL_HS400;
|
||||
}
|
||||
|
||||
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
|
||||
}
|
||||
|
||||
@ -176,24 +204,18 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *priv = dwc_priv->priv;
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT;
|
||||
u32 extra, reg;
|
||||
int err;
|
||||
|
||||
host->mmc->actual_clock = 0;
|
||||
|
||||
/*
|
||||
* DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled
|
||||
* by default, but it shouldn't be enabled. We should anyway
|
||||
* disable it before issuing any cmds.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
|
||||
if (clock == 0)
|
||||
if (clock == 0) {
|
||||
/* Disable interface clock at initial state. */
|
||||
sdhci_set_clock(host, clock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Rockchip platform only support 375KHz for identify mode */
|
||||
if (clock <= 400000)
|
||||
@ -211,9 +233,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
extra &= ~BIT(0);
|
||||
sdhci_writel(host, extra, reg);
|
||||
|
||||
if (clock <= 400000) {
|
||||
/* Disable DLL to reset sample clock */
|
||||
if (clock <= 52000000) {
|
||||
/* Disable DLL and reset both of sample and drive clock */
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL);
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK);
|
||||
sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT);
|
||||
/*
|
||||
* Before switching to hs400es mode, the driver will enable
|
||||
* enhanced strobe first. PHY needs to configure the parameters
|
||||
* of enhanced strobe first.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_STRBIN_DELAY_NUM_SEL |
|
||||
DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -222,6 +256,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
udelay(1);
|
||||
sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL);
|
||||
|
||||
/*
|
||||
* We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but
|
||||
* we must set it in higher speed mode.
|
||||
*/
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA;
|
||||
if (priv->devtype == DWCMSHC_RK3568)
|
||||
extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
|
||||
|
||||
/* Init DLL settings */
|
||||
extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
|
||||
0x2 << DWCMSHC_EMMC_DLL_INC |
|
||||
@ -244,8 +287,20 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
host->mmc->ios.timing == MMC_TIMING_MMC_HS400)
|
||||
txclk_tapnum = priv->txclk_tapnum;
|
||||
|
||||
if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
|
||||
txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES;
|
||||
|
||||
extra = DLL_CMDOUT_SRC_CLK_NEG |
|
||||
DLL_CMDOUT_EN_SRC_CLK_NEG |
|
||||
DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_CMDOUT_TAPNUM_90_DEGREES |
|
||||
DLL_CMDOUT_TAPNUM_FROM_SW;
|
||||
sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
|
||||
}
|
||||
|
||||
extra = DWCMSHC_EMMC_DLL_DLYENA |
|
||||
DLL_TXCLK_TAPNUM_FROM_SW |
|
||||
DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL |
|
||||
txclk_tapnum;
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK);
|
||||
|
||||
@ -255,6 +310,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||
sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN);
|
||||
}
|
||||
|
||||
static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
|
||||
if (mask & SDHCI_RESET_ALL && priv->reset) {
|
||||
reset_control_assert(priv->reset);
|
||||
udelay(1);
|
||||
reset_control_deassert(priv->reset);
|
||||
}
|
||||
|
||||
sdhci_reset(host, mask);
|
||||
}
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
@ -264,12 +334,12 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
||||
static const struct sdhci_ops sdhci_dwcmshc_rk3568_ops = {
|
||||
static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = {
|
||||
.set_clock = dwcmshc_rk3568_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
.set_uhs_signaling = dwcmshc_set_uhs_signaling,
|
||||
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||
.reset = sdhci_reset,
|
||||
.reset = rk35xx_sdhci_reset,
|
||||
.adma_write_desc = dwcmshc_adma_write_desc,
|
||||
};
|
||||
|
||||
@ -279,30 +349,37 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
|
||||
};
|
||||
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_rk3568_pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk3568_ops,
|
||||
static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
|
||||
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
|
||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
};
|
||||
|
||||
static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
int err;
|
||||
struct rk3568_priv *priv = dwc_priv->priv;
|
||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||
|
||||
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
|
||||
if (IS_ERR(priv->reset)) {
|
||||
err = PTR_ERR(priv->reset);
|
||||
dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
priv->rockchip_clks[0].id = "axi";
|
||||
priv->rockchip_clks[1].id = "block";
|
||||
priv->rockchip_clks[2].id = "timer";
|
||||
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK3568_MAX_CLKS,
|
||||
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS,
|
||||
priv->rockchip_clks);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = clk_bulk_prepare_enable(RK3568_MAX_CLKS, priv->rockchip_clks);
|
||||
err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks);
|
||||
if (err) {
|
||||
dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
|
||||
return err;
|
||||
@ -321,10 +398,28 @@ static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||
{
|
||||
/*
|
||||
* Don't support highspeed bus mode with low clk speed as we
|
||||
* cannot use DLL for this condition.
|
||||
*/
|
||||
if (host->mmc->f_max <= 52000000) {
|
||||
dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n",
|
||||
host->mmc->f_max);
|
||||
host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400);
|
||||
host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||
{
|
||||
.compatible = "rockchip,rk3588-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "rockchip,rk3568-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_rk3568_pdata,
|
||||
.data = &sdhci_dwcmshc_rk35xx_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "snps,dwcmshc-sdhci",
|
||||
@ -347,7 +442,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_host *host;
|
||||
struct dwcmshc_priv *priv;
|
||||
struct rk3568_priv *rk_priv = NULL;
|
||||
struct rk35xx_priv *rk_priv = NULL;
|
||||
const struct sdhci_pltfm_data *pltfm_data;
|
||||
int err;
|
||||
u32 extra;
|
||||
@ -402,33 +497,47 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||
host->mmc_host_ops.request = dwcmshc_request;
|
||||
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
|
||||
|
||||
if (pltfm_data == &sdhci_dwcmshc_rk3568_pdata) {
|
||||
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk3568_priv), GFP_KERNEL);
|
||||
if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) {
|
||||
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
|
||||
if (!rk_priv) {
|
||||
err = -ENOMEM;
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc"))
|
||||
rk_priv->devtype = DWCMSHC_RK3588;
|
||||
else
|
||||
rk_priv->devtype = DWCMSHC_RK3568;
|
||||
|
||||
priv->priv = rk_priv;
|
||||
|
||||
err = dwcmshc_rk3568_init(host, priv);
|
||||
err = dwcmshc_rk35xx_init(host, priv);
|
||||
if (err)
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
|
||||
|
||||
err = sdhci_add_host(host);
|
||||
err = sdhci_setup_host(host);
|
||||
if (err)
|
||||
goto err_clk;
|
||||
|
||||
if (rk_priv)
|
||||
dwcmshc_rk35xx_postinit(host, priv);
|
||||
|
||||
err = __sdhci_add_host(host);
|
||||
if (err)
|
||||
goto err_setup_host;
|
||||
|
||||
return 0;
|
||||
|
||||
err_setup_host:
|
||||
sdhci_cleanup_host(host);
|
||||
err_clk:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
free_pltfm:
|
||||
sdhci_pltfm_free(pdev);
|
||||
@ -440,14 +549,14 @@ static int dwcmshc_remove(struct platform_device *pdev)
|
||||
struct sdhci_host *host = platform_get_drvdata(pdev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
|
||||
sdhci_remove_host(host, 0);
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
@ -460,7 +569,7 @@ 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);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_suspend_host(host);
|
||||
@ -472,7 +581,7 @@ static int dwcmshc_suspend(struct device *dev)
|
||||
clk_disable_unprepare(priv->bus_clk);
|
||||
|
||||
if (rk_priv)
|
||||
clk_bulk_disable_unprepare(RK3568_MAX_CLKS,
|
||||
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
|
||||
return ret;
|
||||
@ -483,7 +592,7 @@ 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);
|
||||
struct rk3568_priv *rk_priv = priv->priv;
|
||||
struct rk35xx_priv *rk_priv = priv->priv;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
@ -497,7 +606,7 @@ static int dwcmshc_resume(struct device *dev)
|
||||
}
|
||||
|
||||
if (rk_priv) {
|
||||
ret = clk_bulk_prepare_enable(RK3568_MAX_CLKS,
|
||||
ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS,
|
||||
rk_priv->rockchip_clks);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -904,6 +904,7 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc,
|
||||
scfg_node = of_find_matching_node(NULL, scfg_device_ids);
|
||||
if (scfg_node)
|
||||
scfg_base = of_iomap(scfg_node, 0);
|
||||
of_node_put(scfg_node);
|
||||
if (scfg_base) {
|
||||
sdhciovselcr = SDHCIOVSELCR_TGLEN |
|
||||
SDHCIOVSELCR_VSELVAL;
|
||||
@ -1418,7 +1419,7 @@ static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc)
|
||||
static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sdhci_host *host;
|
||||
struct device_node *np;
|
||||
struct device_node *np, *tp;
|
||||
struct sdhci_pltfm_host *pltfm_host;
|
||||
struct sdhci_esdhc *esdhc;
|
||||
int ret;
|
||||
@ -1463,7 +1464,9 @@ static int sdhci_esdhc_probe(struct platform_device *pdev)
|
||||
if (esdhc->vendor_ver > VENDOR_V_22)
|
||||
host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ;
|
||||
|
||||
if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) {
|
||||
tp = of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc");
|
||||
if (tp) {
|
||||
of_node_put(tp);
|
||||
host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST;
|
||||
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
|
||||
}
|
||||
|
@ -95,6 +95,9 @@
|
||||
#define PCIE_GLI_9763E_SCR 0x8E0
|
||||
#define GLI_9763E_SCR_AXI_REQ BIT(9)
|
||||
|
||||
#define PCIE_GLI_9763E_CFG 0x8A0
|
||||
#define GLI_9763E_CFG_LPSN_DIS BIT(12)
|
||||
|
||||
#define PCIE_GLI_9763E_CFG2 0x8A4
|
||||
#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19)
|
||||
#define GLI_9763E_CFG2_L1DLY_MID 0x54
|
||||
@ -963,12 +966,40 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable)
|
||||
{
|
||||
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_CFG, &value);
|
||||
|
||||
if (enable)
|
||||
value &= ~GLI_9763E_CFG_LPSN_DIS;
|
||||
else
|
||||
value |= GLI_9763E_CFG_LPSN_DIS;
|
||||
|
||||
pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, 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 gl9763e_runtime_suspend(struct sdhci_pci_chip *chip)
|
||||
{
|
||||
struct sdhci_pci_slot *slot = chip->slots[0];
|
||||
struct sdhci_host *host = slot->host;
|
||||
u16 clock;
|
||||
|
||||
/* Enable LPM negotiation to allow entering L1 state */
|
||||
gl9763e_set_low_power_negotiation(slot, true);
|
||||
|
||||
clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||
clock &= ~(SDHCI_CLOCK_PLL_EN | SDHCI_CLOCK_CARD_EN);
|
||||
sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
|
||||
@ -1002,6 +1033,9 @@ static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip)
|
||||
clock |= SDHCI_CLOCK_CARD_EN;
|
||||
sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL);
|
||||
|
||||
/* Disable LPM negotiation to avoid entering L1 state. */
|
||||
gl9763e_set_low_power_negotiation(slot, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -440,15 +440,14 @@ static int sdhci_st_remove(struct platform_device *pdev)
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host);
|
||||
struct reset_control *rstc = pdata->rstc;
|
||||
int ret;
|
||||
|
||||
ret = sdhci_pltfm_unregister(pdev);
|
||||
sdhci_pltfm_unregister(pdev);
|
||||
|
||||
clk_disable_unprepare(pdata->icnclk);
|
||||
|
||||
reset_control_assert(rstc);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -224,6 +224,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||
if (timedout) {
|
||||
pr_err("%s: Reset 0x%x never completed.\n",
|
||||
mmc_hostname(host->mmc), (int)mask);
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@ -1716,6 +1717,7 @@ static bool sdhci_send_command_retry(struct sdhci_host *host,
|
||||
if (!timeout--) {
|
||||
pr_err("%s: Controller never released inhibit bit(s).\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
cmd->error = -EIO;
|
||||
return false;
|
||||
@ -1965,6 +1967,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
|
||||
if (timedout) {
|
||||
pr_err("%s: Internal clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@ -1987,6 +1990,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk)
|
||||
if (timedout) {
|
||||
pr_err("%s: PLL clock never stabilised.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, CTRL_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
@ -3161,6 +3165,7 @@ static void sdhci_timeout_timer(struct timer_list *t)
|
||||
if (host->cmd && !sdhci_data_line_cmd(host->cmd)) {
|
||||
pr_err("%s: Timeout waiting for hardware cmd interrupt.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, REQ_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
@ -3183,6 +3188,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t)
|
||||
(host->cmd && sdhci_data_line_cmd(host->cmd))) {
|
||||
pr_err("%s: Timeout waiting for hardware interrupt.\n",
|
||||
mmc_hostname(host->mmc));
|
||||
sdhci_err_stats_inc(host, REQ_TIMEOUT);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
if (host->data) {
|
||||
@ -3234,17 +3240,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
return;
|
||||
pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n",
|
||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
return;
|
||||
}
|
||||
|
||||
if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC |
|
||||
SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) {
|
||||
if (intmask & SDHCI_INT_TIMEOUT)
|
||||
if (intmask & SDHCI_INT_TIMEOUT) {
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
else
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
} else {
|
||||
host->cmd->error = -EILSEQ;
|
||||
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, CMD_CRC);
|
||||
}
|
||||
/* Treat data command CRC error the same as data CRC error */
|
||||
if (host->cmd->data &&
|
||||
(intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) ==
|
||||
@ -3266,6 +3276,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p)
|
||||
-ETIMEDOUT :
|
||||
-EILSEQ;
|
||||
|
||||
sdhci_err_stats_inc(host, AUTO_CMD);
|
||||
|
||||
if (sdhci_auto_cmd23(host, mrq)) {
|
||||
mrq->sbc->error = err;
|
||||
__sdhci_finish_mrq(host, mrq);
|
||||
@ -3342,6 +3354,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
host->data_cmd = NULL;
|
||||
data_cmd->error = -ETIMEDOUT;
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
__sdhci_finish_mrq(host, data_cmd->mrq);
|
||||
return;
|
||||
}
|
||||
@ -3370,23 +3383,30 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
|
||||
|
||||
pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n",
|
||||
mmc_hostname(host->mmc), (unsigned)intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
||||
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
host->data->error = -ETIMEDOUT;
|
||||
else if (intmask & SDHCI_INT_DATA_END_BIT)
|
||||
sdhci_err_stats_inc(host, DAT_TIMEOUT);
|
||||
} else if (intmask & SDHCI_INT_DATA_END_BIT) {
|
||||
host->data->error = -EILSEQ;
|
||||
else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if ((intmask & SDHCI_INT_DATA_CRC) &&
|
||||
SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))
|
||||
!= MMC_BUS_TEST_R)
|
||||
!= MMC_BUS_TEST_R) {
|
||||
host->data->error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND))))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc),
|
||||
intmask);
|
||||
sdhci_adma_show_error(host);
|
||||
sdhci_err_stats_inc(host, ADMA);
|
||||
host->data->error = -EIO;
|
||||
if (host->ops->adma_workaround)
|
||||
host->ops->adma_workaround(host, intmask);
|
||||
@ -3584,6 +3604,7 @@ out:
|
||||
if (unexpected) {
|
||||
pr_err("%s: Unexpected interrupt 0x%08x.\n",
|
||||
mmc_hostname(host->mmc), unexpected);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
}
|
||||
|
||||
@ -3905,20 +3926,27 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
|
||||
if (!host->cqe_on)
|
||||
return false;
|
||||
|
||||
if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC))
|
||||
if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)) {
|
||||
*cmd_error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_TIMEOUT)
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, CMD_CRC);
|
||||
} else if (intmask & SDHCI_INT_TIMEOUT) {
|
||||
*cmd_error = -ETIMEDOUT;
|
||||
else
|
||||
sdhci_err_stats_inc(host, CMD_TIMEOUT);
|
||||
} else
|
||||
*cmd_error = 0;
|
||||
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC))
|
||||
if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) {
|
||||
*data_error = -EILSEQ;
|
||||
else if (intmask & SDHCI_INT_DATA_TIMEOUT)
|
||||
if (!mmc_op_tuning(host->cmd->opcode))
|
||||
sdhci_err_stats_inc(host, DAT_CRC);
|
||||
} else if (intmask & SDHCI_INT_DATA_TIMEOUT) {
|
||||
*data_error = -ETIMEDOUT;
|
||||
else if (intmask & SDHCI_INT_ADMA_ERROR)
|
||||
sdhci_err_stats_inc(host, DAT_TIMEOUT);
|
||||
} else if (intmask & SDHCI_INT_ADMA_ERROR) {
|
||||
*data_error = -EIO;
|
||||
else
|
||||
sdhci_err_stats_inc(host, ADMA);
|
||||
} else
|
||||
*data_error = 0;
|
||||
|
||||
/* Clear selected interrupts. */
|
||||
@ -3934,6 +3962,7 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error,
|
||||
sdhci_writel(host, intmask, SDHCI_INT_STATUS);
|
||||
pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n",
|
||||
mmc_hostname(host->mmc), intmask);
|
||||
sdhci_err_stats_inc(host, UNEXPECTED_IRQ);
|
||||
sdhci_dumpregs(host);
|
||||
}
|
||||
|
||||
|
@ -356,6 +356,9 @@ struct sdhci_adma2_64_desc {
|
||||
*/
|
||||
#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */
|
||||
|
||||
#define sdhci_err_stats_inc(host, err_name) \
|
||||
mmc_debugfs_err_stats_inc((host)->mmc, MMC_ERR_##err_name)
|
||||
|
||||
enum sdhci_cookie {
|
||||
COOKIE_UNMAPPED,
|
||||
COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */
|
||||
|
@ -75,7 +75,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
|
||||
tmio_mmc_clk_start(host);
|
||||
}
|
||||
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
|
||||
usleep_range(10000, 11000);
|
||||
|
@ -42,6 +42,7 @@
|
||||
#define CTL_DMA_ENABLE 0xd8
|
||||
#define CTL_RESET_SD 0xe0
|
||||
#define CTL_VERSION 0xe2
|
||||
#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
|
||||
|
||||
/* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
|
||||
#define TMIO_STOP_STP BIT(0)
|
||||
@ -98,6 +99,9 @@
|
||||
/* Definitions for values the CTL_DMA_ENABLE register can take */
|
||||
#define DMA_ENABLE_DMASDRW BIT(1)
|
||||
|
||||
/* Definitions for values the CTL_SDIF_MODE register can take */
|
||||
#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */
|
||||
|
||||
/* Define some IRQ masks */
|
||||
/* This is the mask used at reset by the chip */
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
@ -181,7 +185,7 @@ struct tmio_mmc_host {
|
||||
int (*multi_io_quirk)(struct mmc_card *card,
|
||||
unsigned int direction, int blk_size);
|
||||
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
|
||||
void (*reset)(struct tmio_mmc_host *host);
|
||||
void (*reset)(struct tmio_mmc_host *host, bool preserve);
|
||||
bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq);
|
||||
unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host);
|
||||
|
@ -179,8 +179,17 @@ static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg);
|
||||
}
|
||||
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)
|
||||
{
|
||||
u16 card_opt, clk_ctrl, sdif_mode;
|
||||
|
||||
if (preserve) {
|
||||
card_opt = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT);
|
||||
clk_ctrl = sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL);
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
sdif_mode = sd_ctrl_read16(host, CTL_SDIF_MODE);
|
||||
}
|
||||
|
||||
/* FIXME - should we set stop clock reg here */
|
||||
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
|
||||
usleep_range(10000, 11000);
|
||||
@ -190,7 +199,7 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
tmio_mmc_abort_dma(host);
|
||||
|
||||
if (host->reset)
|
||||
host->reset(host);
|
||||
host->reset(host, preserve);
|
||||
|
||||
sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all);
|
||||
host->sdcard_irq_mask = host->sdcard_irq_mask_all;
|
||||
@ -206,6 +215,13 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host)
|
||||
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
|
||||
}
|
||||
|
||||
if (preserve) {
|
||||
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, card_opt);
|
||||
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk_ctrl);
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
sd_ctrl_write16(host, CTL_SDIF_MODE, sdif_mode);
|
||||
}
|
||||
|
||||
if (host->mmc->card)
|
||||
mmc_retune_needed(host->mmc);
|
||||
}
|
||||
@ -248,7 +264,7 @@ static void tmio_mmc_reset_work(struct work_struct *work)
|
||||
|
||||
spin_unlock_irqrestore(&host->lock, flags);
|
||||
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, true);
|
||||
|
||||
/* Ready for new calls */
|
||||
host->mrq = NULL;
|
||||
@ -961,7 +977,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||
tmio_mmc_power_off(host);
|
||||
/* For R-Car Gen2+, we need to reset SDHI specific SCC */
|
||||
if (host->pdata->flags & TMIO_MMC_MIN_RCAR2)
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, false);
|
||||
|
||||
host->set_clock(host, 0);
|
||||
break;
|
||||
@ -1189,7 +1205,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host)
|
||||
_host->sdcard_irq_mask_all = TMIO_MASK_ALL;
|
||||
|
||||
_host->set_clock(_host, 0);
|
||||
tmio_mmc_reset(_host);
|
||||
tmio_mmc_reset(_host, false);
|
||||
|
||||
spin_lock_init(&_host->lock);
|
||||
mutex_init(&_host->ios_lock);
|
||||
@ -1285,7 +1301,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
|
||||
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||
|
||||
tmio_mmc_clk_enable(host);
|
||||
tmio_mmc_reset(host);
|
||||
tmio_mmc_reset(host, false);
|
||||
|
||||
if (host->clk_cache)
|
||||
host->set_clock(host, host->clk_cache);
|
||||
|
@ -219,7 +219,8 @@ struct sdio_cccr {
|
||||
wide_bus:1,
|
||||
high_power:1,
|
||||
high_speed:1,
|
||||
disable_cd:1;
|
||||
disable_cd:1,
|
||||
enable_async_irq:1;
|
||||
};
|
||||
|
||||
struct sdio_cis {
|
||||
@ -343,10 +344,16 @@ static inline bool mmc_large_sector(struct mmc_card *card)
|
||||
return card->ext_csd.data_sector_size == 4096;
|
||||
}
|
||||
|
||||
static inline int mmc_card_enable_async_irq(struct mmc_card *card)
|
||||
{
|
||||
return card->cccr.enable_async_irq;
|
||||
}
|
||||
|
||||
bool mmc_card_is_blockaddr(struct mmc_card *card);
|
||||
|
||||
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
|
||||
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
|
||||
#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
|
||||
#define mmc_card_sd_combo(c) ((c)->type == MMC_TYPE_SD_COMBO)
|
||||
|
||||
#endif /* LINUX_MMC_CARD_H */
|
||||
|
@ -93,6 +93,25 @@ struct mmc_clk_phase_map {
|
||||
|
||||
struct mmc_host;
|
||||
|
||||
enum mmc_err_stat {
|
||||
MMC_ERR_CMD_TIMEOUT,
|
||||
MMC_ERR_CMD_CRC,
|
||||
MMC_ERR_DAT_TIMEOUT,
|
||||
MMC_ERR_DAT_CRC,
|
||||
MMC_ERR_AUTO_CMD,
|
||||
MMC_ERR_ADMA,
|
||||
MMC_ERR_TUNING,
|
||||
MMC_ERR_CMDQ_RED,
|
||||
MMC_ERR_CMDQ_GCE,
|
||||
MMC_ERR_CMDQ_ICCE,
|
||||
MMC_ERR_REQ_TIMEOUT,
|
||||
MMC_ERR_CMDQ_REQ_TIMEOUT,
|
||||
MMC_ERR_ICE_CFG,
|
||||
MMC_ERR_CTRL_TIMEOUT,
|
||||
MMC_ERR_UNEXPECTED_IRQ,
|
||||
MMC_ERR_MAX,
|
||||
};
|
||||
|
||||
struct mmc_host_ops {
|
||||
/*
|
||||
* It is optional for the host to implement pre_req and post_req in
|
||||
@ -501,6 +520,7 @@ struct mmc_host {
|
||||
/* Host Software Queue support */
|
||||
bool hsq_enabled;
|
||||
|
||||
u32 err_stats[MMC_ERR_MAX];
|
||||
unsigned long private[] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
@ -635,6 +655,12 @@ static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data)
|
||||
return data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||
}
|
||||
|
||||
static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host,
|
||||
enum mmc_err_stat stat)
|
||||
{
|
||||
host->err_stats[stat] += 1;
|
||||
}
|
||||
|
||||
int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error);
|
||||
int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode);
|
||||
int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd);
|
||||
|
@ -99,6 +99,12 @@ static inline bool mmc_op_multi(u32 opcode)
|
||||
opcode == MMC_READ_MULTIPLE_BLOCK;
|
||||
}
|
||||
|
||||
static inline bool mmc_op_tuning(u32 opcode)
|
||||
{
|
||||
return opcode == MMC_SEND_TUNING_BLOCK ||
|
||||
opcode == MMC_SEND_TUNING_BLOCK_HS200;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMC_SWITCH argument format:
|
||||
*
|
||||
|
@ -159,6 +159,11 @@
|
||||
#define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
#define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT)
|
||||
|
||||
#define SDIO_CCCR_INTERRUPT_EXT 0x16
|
||||
#define SDIO_INTERRUPT_EXT_SAI (1 << 0)
|
||||
#define SDIO_INTERRUPT_EXT_EAI (1 << 1)
|
||||
|
||||
/*
|
||||
* Function Basic Registers (FBR)
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user