mirror of https://github.com/torvalds/linux.git
MMC core:
- Allow more host caps to be modified through debugfs
- Skip to set the default 200mA SD current limit
MMC host:
- Convert a few more DT bindings to the DT schema
- dw_mmc: Add Shawn Lin as co-maintainer for the dw_mmc drivers
- dw_mmc-rockchip: Add memory clock auto-gating support
- dw_mmc-rockchip: Add support for the RK3506 variant
- meson-mx-sdio: Ignore disabled "mmc-slot" child-nodes
- meson-mx-sdio: Refactoring and general code improvements
- renesas_sdhi: Enable bigger data ports where available
- renesas_sdhi: Manage reset in probe and during system-wide suspend/resume
- sdhci-brcmstb: Add support for the BCM72116 and BCM74371 variants
- sdhci-brcmstb: Save/restore registers during system-wide suspend/resume
- sdhci-msm: Add support for the sm8750 and the Kaanapali variants
- sdhci-msm: Avoid early clock doubling during HS400 transition
- sdhci-of-dwcmshc: Add command queue support for Rockchip SOCs
- sdhci-of-dwcmshc: Add support for the Eswin EIC7700 variant
-----BEGIN PGP SIGNATURE-----
iQJLBAABCgA1FiEEugLDXPmKSktSkQsV/iaEJXNYjCkFAmkutuAXHHVsZi5oYW5z
c29uQGxpbmFyby5vcmcACgkQ/iaEJXNYjCn30xAAqG+pVwtJLiAP26oKa43tvnHy
fwJRCKxLuh1S2JM95X5Q8E0bgFJfOoEM280DDZTxeJcVesIeZovLEVYfzGv53wkw
4QVbWrixzSww5PhGN25SbxswzqaYRuZt7BPeER2n1f5swgNAuHjnIAkOLw2sR7BF
gaCu/2/m5D0mgIQMUjmNe0bVYNWC1uuAI9GIamwKfbN+0tgwwruV6H6WUtvU+0wf
xXv3RC7R4kyI1fBG0CqC6MyGsdcj2mQC9WG92ZlS+V9vnY3wDcwnD8Kcb9IKehBH
sOBwPGpAPkTmvIwkmRaaiJzGHafdomCg73t7kLLeh642MKsmp/oN7XQwXxm6AM1j
WoCjA/cIAKjSI9vskjdd1LCUYs9Ja+b+LtrqfdcZvh1+MgpsJnpXIMm6npiwbqHv
MYnsLcx2NLfIU8jZnRePy5U/MCRvz5Ow5ADZDrwV2HQ31J7Y7FxHSN+lTPIWC7Fc
B4Gy6SPYIDQUP4aolEufbqE8UDqHXhZhVGGKKHPxpFWjkTfCRM99zsUONs9PBzrW
lkMMA6VKme2F66b2SDI65kZeFNUqtb+skmrzDnq3+v+SaPU14ToXBeY3dY42Ee15
nn4UcyGjQHh0wjQoeBI4gvkoiq9MIwJMmJwmxn3Z+NkrhO7V5ecrL7uMUGIYzxjY
YvDMdHmPmqjPZG8cQaU=
=Gf5H
-----END PGP SIGNATURE-----
Merge tag 'mmc-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc
Pull MMC updates from Ulf Hansson:
"MMC core:
- Allow more host caps to be modified through debugfs
- Skip to set the default 200mA SD current limit
MMC host:
- Convert a few more DT bindings to the DT schema
- dw_mmc: Add Shawn Lin as co-maintainer for the dw_mmc drivers
- dw_mmc-rockchip:
- Add memory clock auto-gating support
- Add support for the RK3506 variant
- meson-mx-sdio:
- Ignore disabled "mmc-slot" child-nodes
- Refactoring and general code improvements
- renesas_sdhi:
- Enable bigger data ports where available
- Manage reset in probe and during system-wide suspend/resume
- sdhci-brcmstb:
- Add support for the BCM72116 and BCM74371 variants
- Save/restore registers during system-wide suspend/resume
- sdhci-msm:
- Add support for the sm8750 and the Kaanapali variants
- Avoid early clock doubling during HS400 transition
- sdhci-of-dwcmshc:
- Add command queue support for Rockchip SOCs
- Add support for the Eswin EIC7700 variant"
* tag 'mmc-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (40 commits)
mmc: sdhci-of-dwcmshc: reduce CIT for better performance
mmc: sdhci-of-dwcmshc: Disable internal clock auto gate for Rockchip SOCs
mmc: sdhci-msm: Avoid early clock doubling during HS400 transition
MAINTAINERS: Add Shawn Lin as co-maintainer for dw_mmc drivers
mmc: sdhci-of-dwcmshc: Fix command queue support for RK3576
mmc: renesas_sdhi: Add suspend/resume hooks
mmc: renesas_sdhi: Switch to SYSTEM_SLEEP_PM_OPS()/RUNTIME_PM_OPS() and pm_ptr()
mmc: renesas_sdhi: Deassert the reset signal on probe
dt-bindings: mmc: am654: Simplify dma-coherent property
mmc: meson-mx-sdio: Ignore disabled "mmc-slot" child-nodes
mmc: meson-mx-sdio: Fix indentation in meson_mx_mmc_irq_thread()
mmc: meson-mx-sdio: Use dev_err_probe() where appropriate
mmc: meson-mx-sdio: Use devm_mmc_alloc_host() helper
mmc: meson-mx-sdio: Refactor internal clock initialization
mmc: meson-mx-sdio: Use devm_clk_get_enabled()
mmc: meson-mx-sdio: Switch to regmap for register access
mmc: core: add WQ_PERCPU to alloc_workqueue users
mmc: dw_mmc-rockchip: Add memory clock auto-gating support
mmc: omap: add WQ_PERCPU to alloc_workqueue users
mmc: mtk-sd: replace use of system_wq with system_percpu_wq
...
This commit is contained in:
commit
dd463c51a3
|
|
@ -21,9 +21,11 @@ properties:
|
||||||
- items:
|
- items:
|
||||||
- enum:
|
- enum:
|
||||||
- brcm,bcm2712-sdhci
|
- brcm,bcm2712-sdhci
|
||||||
|
- brcm,bcm72116-sdhci
|
||||||
- brcm,bcm74165b0-sdhci
|
- brcm,bcm74165b0-sdhci
|
||||||
- brcm,bcm7445-sdhci
|
- brcm,bcm7445-sdhci
|
||||||
- brcm,bcm7425-sdhci
|
- brcm,bcm7425-sdhci
|
||||||
|
- brcm,bcm74371-sdhci
|
||||||
- const: brcm,sdhci-brcmstb
|
- const: brcm,sdhci-brcmstb
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
|
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
* TI Highspeed MMC host controller for DaVinci
|
|
||||||
|
|
||||||
The Highspeed MMC Host Controller on TI DaVinci family
|
|
||||||
provides an interface for MMC, SD and SDIO types of memory cards.
|
|
||||||
|
|
||||||
This file documents the properties used by the davinci_mmc driver.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible:
|
|
||||||
Should be "ti,da830-mmc": for da830, da850, dm365
|
|
||||||
Should be "ti,dm355-mmc": for dm355, dm644x
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- bus-width: Number of data lines, can be <1>, <4>, or <8>, default <1>
|
|
||||||
- max-frequency: Maximum operating clock frequency, default 25MHz.
|
|
||||||
- dmas: List of DMA specifiers with the controller specific format
|
|
||||||
as described in the generic DMA client binding. A tx and rx
|
|
||||||
specifier is required.
|
|
||||||
- dma-names: RX and TX DMA request names. These strings correspond
|
|
||||||
1:1 with the DMA specifiers listed in dmas.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
mmc0: mmc@1c40000 {
|
|
||||||
compatible = "ti,da830-mmc",
|
|
||||||
reg = <0x40000 0x1000>;
|
|
||||||
interrupts = <16>;
|
|
||||||
bus-width = <4>;
|
|
||||||
max-frequency = <50000000>;
|
|
||||||
dmas = <&edma 16
|
|
||||||
&edma 17>;
|
|
||||||
dma-names = "rx", "tx";
|
|
||||||
};
|
|
||||||
|
|
@ -38,6 +38,7 @@ properties:
|
||||||
- rockchip,rk3328-dw-mshc
|
- rockchip,rk3328-dw-mshc
|
||||||
- rockchip,rk3368-dw-mshc
|
- rockchip,rk3368-dw-mshc
|
||||||
- rockchip,rk3399-dw-mshc
|
- rockchip,rk3399-dw-mshc
|
||||||
|
- rockchip,rk3506-dw-mshc
|
||||||
- rockchip,rk3528-dw-mshc
|
- rockchip,rk3528-dw-mshc
|
||||||
- rockchip,rk3562-dw-mshc
|
- rockchip,rk3562-dw-mshc
|
||||||
- rockchip,rk3568-dw-mshc
|
- rockchip,rk3568-dw-mshc
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,7 @@ properties:
|
||||||
- const: clk_ahb
|
- const: clk_ahb
|
||||||
- const: clk_xin
|
- const: clk_xin
|
||||||
|
|
||||||
dma-coherent:
|
dma-coherent: true
|
||||||
type: boolean
|
|
||||||
|
|
||||||
# PHY output tap delays:
|
# PHY output tap delays:
|
||||||
# Used to delay the data valid window and align it to the sampling clock.
|
# Used to delay the data valid window and align it to the sampling clock.
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
* SOCIONEXT Milbeaut SDHCI controller
|
|
||||||
|
|
||||||
This file documents differences between the core properties in mmc.txt
|
|
||||||
and the properties used by the sdhci_milbeaut driver.
|
|
||||||
|
|
||||||
Required properties:
|
|
||||||
- compatible: "socionext,milbeaut-m10v-sdhci-3.0"
|
|
||||||
- clocks: Must contain an entry for each entry in clock-names. It is a
|
|
||||||
list of phandles and clock-specifier pairs.
|
|
||||||
See ../clocks/clock-bindings.txt for details.
|
|
||||||
- clock-names: Should contain the following two entries:
|
|
||||||
"iface" - clock used for sdhci interface
|
|
||||||
"core" - core clock for sdhci controller
|
|
||||||
|
|
||||||
Optional properties:
|
|
||||||
- fujitsu,cmd-dat-delay-select: boolean property indicating that this host
|
|
||||||
requires the CMD_DAT_DELAY control to be enabled.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
sdhci3: mmc@1b010000 {
|
|
||||||
compatible = "socionext,milbeaut-m10v-sdhci-3.0";
|
|
||||||
reg = <0x1b010000 0x10000>;
|
|
||||||
interrupts = <0 265 0x4>;
|
|
||||||
voltage-ranges = <3300 3300>;
|
|
||||||
bus-width = <4>;
|
|
||||||
clocks = <&clk 7>, <&ahb_clk>;
|
|
||||||
clock-names = "core", "iface";
|
|
||||||
cap-sdio-irq;
|
|
||||||
fujitsu,cmd-dat-delay-select;
|
|
||||||
};
|
|
||||||
|
|
@ -42,6 +42,7 @@ properties:
|
||||||
- qcom,ipq5424-sdhci
|
- qcom,ipq5424-sdhci
|
||||||
- qcom,ipq6018-sdhci
|
- qcom,ipq6018-sdhci
|
||||||
- qcom,ipq9574-sdhci
|
- qcom,ipq9574-sdhci
|
||||||
|
- qcom,kaanapali-sdhci
|
||||||
- qcom,milos-sdhci
|
- qcom,milos-sdhci
|
||||||
- qcom,qcm2290-sdhci
|
- qcom,qcm2290-sdhci
|
||||||
- qcom,qcs404-sdhci
|
- qcom,qcs404-sdhci
|
||||||
|
|
@ -70,6 +71,7 @@ properties:
|
||||||
- qcom,sm8450-sdhci
|
- qcom,sm8450-sdhci
|
||||||
- qcom,sm8550-sdhci
|
- qcom,sm8550-sdhci
|
||||||
- qcom,sm8650-sdhci
|
- qcom,sm8650-sdhci
|
||||||
|
- qcom,sm8750-sdhci
|
||||||
- qcom,x1e80100-sdhci
|
- qcom,x1e80100-sdhci
|
||||||
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
|
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ properties:
|
||||||
- sophgo,sg2002-dwcmshc
|
- sophgo,sg2002-dwcmshc
|
||||||
- sophgo,sg2042-dwcmshc
|
- sophgo,sg2042-dwcmshc
|
||||||
- thead,th1520-dwcmshc
|
- thead,th1520-dwcmshc
|
||||||
|
- eswin,eic7700-dwcmshc
|
||||||
|
|
||||||
reg:
|
reg:
|
||||||
maxItems: 1
|
maxItems: 1
|
||||||
|
|
@ -52,17 +53,30 @@ properties:
|
||||||
maxItems: 5
|
maxItems: 5
|
||||||
|
|
||||||
reset-names:
|
reset-names:
|
||||||
items:
|
maxItems: 5
|
||||||
- const: core
|
|
||||||
- const: bus
|
|
||||||
- const: axi
|
|
||||||
- const: block
|
|
||||||
- const: timer
|
|
||||||
|
|
||||||
rockchip,txclk-tapnum:
|
rockchip,txclk-tapnum:
|
||||||
description: Specify the number of delay for tx sampling.
|
description: Specify the number of delay for tx sampling.
|
||||||
$ref: /schemas/types.yaml#/definitions/uint8
|
$ref: /schemas/types.yaml#/definitions/uint8
|
||||||
|
|
||||||
|
eswin,hsp-sp-csr:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/phandle-array
|
||||||
|
items:
|
||||||
|
- items:
|
||||||
|
- description: Phandle to HSP(High-Speed Peripheral) device
|
||||||
|
- description: Offset of the stability status register for internal
|
||||||
|
clock.
|
||||||
|
- description: Offset of the stability register for host regulator
|
||||||
|
voltage.
|
||||||
|
description:
|
||||||
|
HSP CSR is to control and get status of different high-speed peripherals
|
||||||
|
(such as Ethernet, USB, SATA, etc.) via register, which can tune
|
||||||
|
board-level's parameters of PHY, etc.
|
||||||
|
|
||||||
|
eswin,drive-impedance-ohms:
|
||||||
|
description: Specifies the drive impedance in Ohm.
|
||||||
|
enum: [33, 40, 50, 66, 100]
|
||||||
|
|
||||||
required:
|
required:
|
||||||
- compatible
|
- compatible
|
||||||
- reg
|
- reg
|
||||||
|
|
@ -110,6 +124,37 @@ allOf:
|
||||||
- const: block
|
- const: block
|
||||||
- const: timer
|
- const: timer
|
||||||
|
|
||||||
|
- if:
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
contains:
|
||||||
|
const: eswin,eic7700-dwcmshc
|
||||||
|
then:
|
||||||
|
properties:
|
||||||
|
resets:
|
||||||
|
minItems: 4
|
||||||
|
maxItems: 4
|
||||||
|
reset-names:
|
||||||
|
items:
|
||||||
|
- const: axi
|
||||||
|
- const: phy
|
||||||
|
- const: prstn
|
||||||
|
- const: txrx
|
||||||
|
required:
|
||||||
|
- eswin,hsp-sp-csr
|
||||||
|
- eswin,drive-impedance-ohms
|
||||||
|
else:
|
||||||
|
properties:
|
||||||
|
resets:
|
||||||
|
maxItems: 5
|
||||||
|
reset-names:
|
||||||
|
items:
|
||||||
|
- const: core
|
||||||
|
- const: bus
|
||||||
|
- const: axi
|
||||||
|
- const: block
|
||||||
|
- const: timer
|
||||||
|
|
||||||
- if:
|
- if:
|
||||||
properties:
|
properties:
|
||||||
compatible:
|
compatible:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mmc/socionext,milbeaut-m10v-sdhci-3.0.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: SOCIONEXT Milbeaut SDHCI controller
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Taichi Sugaya <sugaya.taichi@socionext.com>
|
||||||
|
- Takao Orito <orito.takao@socionext.com>
|
||||||
|
|
||||||
|
description:
|
||||||
|
The SOCIONEXT Milbeaut SDHCI controller is a specialized SD Host
|
||||||
|
Controller found in some of Socionext's Milbeaut image processing SoCs.
|
||||||
|
It features a dedicated "bridge controller." This bridge controller
|
||||||
|
implements special functions like reset control, clock management for
|
||||||
|
various SDR modes (SDR12, SDR25, SDR50) and physical pin property settings.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: sdhci-common.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
const: socionext,milbeaut-m10v-sdhci-3.0
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
items:
|
||||||
|
- const: core
|
||||||
|
- const: iface
|
||||||
|
|
||||||
|
fujitsu,cmd-dat-delay-select:
|
||||||
|
description:
|
||||||
|
Its presence indicates that the controller requires a specific command
|
||||||
|
and data line delay selection mechanism for proper operation, particularly
|
||||||
|
when dealing with high-speed SD/eMMC modes.
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
voltage-ranges:
|
||||||
|
$ref: /schemas/types.yaml#/definitions/uint32-matrix
|
||||||
|
items:
|
||||||
|
items:
|
||||||
|
- description: minimum slot voltage (mV).
|
||||||
|
- description: maximum slot voltage (mV).
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
mmc@1b010000 {
|
||||||
|
compatible = "socionext,milbeaut-m10v-sdhci-3.0";
|
||||||
|
reg = <0x1b010000 0x10000>;
|
||||||
|
interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
voltage-ranges = <3300 3300>;
|
||||||
|
bus-width = <4>;
|
||||||
|
clocks = <&clk 7>, <&ahb_clk>;
|
||||||
|
clock-names = "core", "iface";
|
||||||
|
cap-sdio-irq;
|
||||||
|
fujitsu,cmd-dat-delay-select;
|
||||||
|
};
|
||||||
|
...
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/mmc/ti,da830-mmc.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: TI Highspeed MMC host controller for DaVinci
|
||||||
|
|
||||||
|
description:
|
||||||
|
The Highspeed MMC Host Controller on TI DaVinci family
|
||||||
|
provides an interface for MMC, SD and SDIO types of memory cards.
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: mmc-controller.yaml
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Kishon Vijay Abraham I <kishon@kernel.org>
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
enum:
|
||||||
|
- ti,da830-mmc
|
||||||
|
- ti,dm355-mmc
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
dmas:
|
||||||
|
maxItems: 2
|
||||||
|
|
||||||
|
dma-names:
|
||||||
|
items:
|
||||||
|
- const: rx
|
||||||
|
- const: tx
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/irq.h>
|
||||||
|
mmc@1c40000 {
|
||||||
|
compatible = "ti,da830-mmc";
|
||||||
|
reg = <0x40000 0x1000>;
|
||||||
|
interrupts = <16 IRQ_TYPE_LEVEL_HIGH>,
|
||||||
|
<17 IRQ_TYPE_LEVEL_HIGH>;
|
||||||
|
bus-width = <4>;
|
||||||
|
max-frequency = <50000000>;
|
||||||
|
dmas = <&edma 16>, <&edma 17>;
|
||||||
|
dma-names = "rx", "tx";
|
||||||
|
};
|
||||||
|
...
|
||||||
|
|
@ -25119,6 +25119,7 @@ F: include/linux/soc/amd/isp4_misc.h
|
||||||
|
|
||||||
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
|
||||||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||||
|
M: Shawn Lin <shawn.lin@rock-chips.com>
|
||||||
L: linux-mmc@vger.kernel.org
|
L: linux-mmc@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mmc/host/dw_mmc*
|
F: drivers/mmc/host/dw_mmc*
|
||||||
|
|
|
||||||
|
|
@ -349,10 +349,10 @@ static umode_t mmc_disk_attrs_is_visible(struct kobject *kobj,
|
||||||
if (a == &dev_attr_ro_lock_until_next_power_on.attr &&
|
if (a == &dev_attr_ro_lock_until_next_power_on.attr &&
|
||||||
(md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
(md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
||||||
md->queue.card->ext_csd.boot_ro_lockable) {
|
md->queue.card->ext_csd.boot_ro_lockable) {
|
||||||
mode = S_IRUGO;
|
mode = 0444;
|
||||||
if (!(md->queue.card->ext_csd.boot_ro_lock &
|
if (!(md->queue.card->ext_csd.boot_ro_lock &
|
||||||
EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
|
EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
|
||||||
mode |= S_IWUSR;
|
mode |= 0200;
|
||||||
}
|
}
|
||||||
|
|
||||||
mmc_blk_put(md);
|
mmc_blk_put(md);
|
||||||
|
|
@ -957,7 +957,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
|
||||||
u32 result;
|
u32 result;
|
||||||
__be32 *blocks;
|
__be32 *blocks;
|
||||||
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
|
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
|
||||||
unsigned int noio_flag;
|
|
||||||
|
|
||||||
struct mmc_request mrq = {};
|
struct mmc_request mrq = {};
|
||||||
struct mmc_command cmd = {};
|
struct mmc_command cmd = {};
|
||||||
|
|
@ -982,9 +981,7 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
|
||||||
mrq.cmd = &cmd;
|
mrq.cmd = &cmd;
|
||||||
mrq.data = &data;
|
mrq.data = &data;
|
||||||
|
|
||||||
noio_flag = memalloc_noio_save();
|
blocks = kmalloc(resp_sz, GFP_NOIO);
|
||||||
blocks = kmalloc(resp_sz, GFP_KERNEL);
|
|
||||||
memalloc_noio_restore(noio_flag);
|
|
||||||
if (!blocks)
|
if (!blocks)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
@ -3194,7 +3191,7 @@ static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
|
||||||
|
|
||||||
if (mmc_card_mmc(card)) {
|
if (mmc_card_mmc(card)) {
|
||||||
md->ext_csd_dentry =
|
md->ext_csd_dentry =
|
||||||
debugfs_create_file("ext_csd", S_IRUSR, root, card,
|
debugfs_create_file("ext_csd", 0400, root, card,
|
||||||
&mmc_dbg_ext_csd_fops);
|
&mmc_dbg_ext_csd_fops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3275,7 +3272,8 @@ static int mmc_blk_probe(struct mmc_card *card)
|
||||||
mmc_fixup_device(card, mmc_blk_fixups);
|
mmc_fixup_device(card, mmc_blk_fixups);
|
||||||
|
|
||||||
card->complete_wq = alloc_workqueue("mmc_complete",
|
card->complete_wq = alloc_workqueue("mmc_complete",
|
||||||
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
|
WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
|
||||||
|
0);
|
||||||
if (!card->complete_wq) {
|
if (!card->complete_wq) {
|
||||||
pr_err("Failed to create mmc completion workqueue");
|
pr_err("Failed to create mmc completion workqueue");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a
|
||||||
struct mmc_card *card = mmc_dev_to_card(dev); \
|
struct mmc_card *card = mmc_dev_to_card(dev); \
|
||||||
return sysfs_emit(buf, fmt, args); \
|
return sysfs_emit(buf, fmt, args); \
|
||||||
} \
|
} \
|
||||||
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
|
static DEVICE_ATTR(name, 0444, mmc_##name##_show, NULL)
|
||||||
|
|
||||||
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
|
struct mmc_card *mmc_alloc_card(struct mmc_host *host,
|
||||||
const struct device_type *type);
|
const struct device_type *type);
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,10 @@ static int mmc_caps_set(void *data, u64 val)
|
||||||
MMC_CAP_SD_HIGHSPEED |
|
MMC_CAP_SD_HIGHSPEED |
|
||||||
MMC_CAP_MMC_HIGHSPEED |
|
MMC_CAP_MMC_HIGHSPEED |
|
||||||
MMC_CAP_UHS |
|
MMC_CAP_UHS |
|
||||||
MMC_CAP_DDR;
|
MMC_CAP_DDR |
|
||||||
|
MMC_CAP_4_BIT_DATA |
|
||||||
|
MMC_CAP_8_BIT_DATA |
|
||||||
|
MMC_CAP_CMD23;
|
||||||
|
|
||||||
if (diff & ~allowed)
|
if (diff & ~allowed)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
@ -327,7 +330,10 @@ static int mmc_caps_set(void *data, u64 val)
|
||||||
|
|
||||||
static int mmc_caps2_set(void *data, u64 val)
|
static int mmc_caps2_set(void *data, u64 val)
|
||||||
{
|
{
|
||||||
u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V;
|
u32 allowed = MMC_CAP2_HSX00_1_8V |
|
||||||
|
MMC_CAP2_HSX00_1_2V |
|
||||||
|
MMC_CAP2_CQE |
|
||||||
|
MMC_CAP2_CQE_DCMD;
|
||||||
u32 *caps = data;
|
u32 *caps = data;
|
||||||
u32 diff = *caps ^ val;
|
u32 diff = *caps ^ val;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -831,7 +831,7 @@ static ssize_t mmc_fwrev_show(struct device *dev,
|
||||||
card->ext_csd.fwrev);
|
card->ext_csd.fwrev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL);
|
static DEVICE_ATTR(fwrev, 0444, mmc_fwrev_show, NULL);
|
||||||
|
|
||||||
static ssize_t mmc_dsr_show(struct device *dev,
|
static ssize_t mmc_dsr_show(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
|
|
@ -847,7 +847,7 @@ static ssize_t mmc_dsr_show(struct device *dev,
|
||||||
return sysfs_emit(buf, "0x%x\n", 0x404);
|
return sysfs_emit(buf, "0x%x\n", 0x404);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
|
||||||
|
|
||||||
static struct attribute *mmc_std_attrs[] = {
|
static struct attribute *mmc_std_attrs[] = {
|
||||||
&dev_attr_cid.attr,
|
&dev_attr_cid.attr,
|
||||||
|
|
|
||||||
|
|
@ -3208,12 +3208,12 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
|
||||||
|
|
||||||
mutex_lock(&mmc_test_lock);
|
mutex_lock(&mmc_test_lock);
|
||||||
|
|
||||||
ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO,
|
ret = __mmc_test_register_dbgfs_file(card, "test", 0644,
|
||||||
&mmc_test_fops_test);
|
&mmc_test_fops_test);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
|
ret = __mmc_test_register_dbgfs_file(card, "testlist", 0444,
|
||||||
&mtf_testlist_fops);
|
&mtf_testlist_fops);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ static u32 sd_get_host_max_current(struct mmc_host *host)
|
||||||
|
|
||||||
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||||
{
|
{
|
||||||
int current_limit = SD_SET_CURRENT_NO_CHANGE;
|
int current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||||
int err;
|
int err;
|
||||||
u32 max_current;
|
u32 max_current;
|
||||||
|
|
||||||
|
|
@ -598,11 +598,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
||||||
else if (max_current >= 400 &&
|
else if (max_current >= 400 &&
|
||||||
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
|
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
|
||||||
current_limit = SD_SET_CURRENT_LIMIT_400;
|
current_limit = SD_SET_CURRENT_LIMIT_400;
|
||||||
else if (max_current >= 200 &&
|
|
||||||
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
|
|
||||||
current_limit = SD_SET_CURRENT_LIMIT_200;
|
|
||||||
|
|
||||||
if (current_limit != SD_SET_CURRENT_NO_CHANGE) {
|
if (current_limit != SD_SET_CURRENT_LIMIT_200) {
|
||||||
err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
|
err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
|
||||||
current_limit, status);
|
current_limit, status);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -744,7 +741,7 @@ static ssize_t mmc_dsr_show(struct device *dev, struct device_attribute *attr,
|
||||||
return sysfs_emit(buf, "0x%x\n", 0x404);
|
return sysfs_emit(buf, "0x%x\n", 0x404);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL);
|
static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
|
||||||
|
|
||||||
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
|
||||||
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);
|
||||||
|
|
|
||||||
|
|
@ -504,6 +504,7 @@ config MMC_MESON_MX_SDIO
|
||||||
depends on ARCH_MESON || COMPILE_TEST
|
depends on ARCH_MESON || COMPILE_TEST
|
||||||
depends on COMMON_CLK
|
depends on COMMON_CLK
|
||||||
depends on OF_ADDRESS
|
depends on OF_ADDRESS
|
||||||
|
select REGMAP_MMIO
|
||||||
help
|
help
|
||||||
This selects support for the SD/MMC Host Controller on
|
This selects support for the SD/MMC Host Controller on
|
||||||
Amlogic Meson6, Meson8 and Meson8b SoCs.
|
Amlogic Meson6, Meson8 and Meson8b SoCs.
|
||||||
|
|
|
||||||
|
|
@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops);
|
debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops);
|
||||||
debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
|
debugfs_create_file("req", 0400, root, slot, &atmci_req_fops);
|
||||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
debugfs_create_u32("state", 0400, root, &host->state);
|
||||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
debugfs_create_xul("pending_events", 0400, root,
|
||||||
&host->pending_events);
|
&host->pending_events);
|
||||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
debugfs_create_xul("completed_events", 0400, root,
|
||||||
&host->completed_events);
|
&host->completed_events);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
/* send status config 1 */
|
/* send status config 1 */
|
||||||
#define CQHCI_SSC1 0x40
|
#define CQHCI_SSC1 0x40
|
||||||
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
|
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
|
||||||
|
#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0)
|
||||||
|
|
||||||
/* send status config 2 */
|
/* send status config 2 */
|
||||||
#define CQHCI_SSC2 0x44
|
#define CQHCI_SSC2 0x44
|
||||||
|
|
|
||||||
|
|
@ -145,17 +145,17 @@
|
||||||
#define MAX_NR_SG 16
|
#define MAX_NR_SG 16
|
||||||
|
|
||||||
static unsigned rw_threshold = 32;
|
static unsigned rw_threshold = 32;
|
||||||
module_param(rw_threshold, uint, S_IRUGO);
|
module_param(rw_threshold, uint, 0444);
|
||||||
MODULE_PARM_DESC(rw_threshold,
|
MODULE_PARM_DESC(rw_threshold,
|
||||||
"Read/Write threshold. Default = 32");
|
"Read/Write threshold. Default = 32");
|
||||||
|
|
||||||
static unsigned poll_threshold = 128;
|
static unsigned poll_threshold = 128;
|
||||||
module_param(poll_threshold, uint, S_IRUGO);
|
module_param(poll_threshold, uint, 0444);
|
||||||
MODULE_PARM_DESC(poll_threshold,
|
MODULE_PARM_DESC(poll_threshold,
|
||||||
"Polling transaction size threshold. Default = 128");
|
"Polling transaction size threshold. Default = 128");
|
||||||
|
|
||||||
static unsigned poll_loopcount = 32;
|
static unsigned poll_loopcount = 32;
|
||||||
module_param(poll_loopcount, uint, S_IRUGO);
|
module_param(poll_loopcount, uint, 0444);
|
||||||
MODULE_PARM_DESC(poll_loopcount,
|
MODULE_PARM_DESC(poll_loopcount,
|
||||||
"Maximum polling loop count. Default = 32");
|
"Maximum polling loop count. Default = 32");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
#define RK3288_CLKGEN_DIV 2
|
#define RK3288_CLKGEN_DIV 2
|
||||||
#define SDMMC_TIMING_CON0 0x130
|
#define SDMMC_TIMING_CON0 0x130
|
||||||
#define SDMMC_TIMING_CON1 0x134
|
#define SDMMC_TIMING_CON1 0x134
|
||||||
|
#define SDMMC_MISC_CON 0x138
|
||||||
|
#define MEM_CLK_AUTOGATE_ENABLE BIT(5)
|
||||||
#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
|
#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
|
||||||
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
|
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
|
||||||
#define ROCKCHIP_MMC_DEGREE_OFFSET 1
|
#define ROCKCHIP_MMC_DEGREE_OFFSET 1
|
||||||
|
|
@ -470,6 +472,7 @@ static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
|
||||||
|
|
||||||
static int dw_mci_rockchip_init(struct dw_mci *host)
|
static int dw_mci_rockchip_init(struct dw_mci *host)
|
||||||
{
|
{
|
||||||
|
struct dw_mci_rockchip_priv_data *priv = host->priv;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
/* It is slot 8 on Rockchip SoCs */
|
/* It is slot 8 on Rockchip SoCs */
|
||||||
|
|
@ -494,6 +497,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
|
||||||
dev_warn(host->dev, "no valid minimum freq: %d\n", ret);
|
dev_warn(host->dev, "no valid minimum freq: %d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->internal_phase)
|
||||||
|
mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -175,12 +175,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
|
||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops);
|
debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
|
||||||
debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
|
debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
|
||||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
debugfs_create_u32("state", 0400, root, &host->state);
|
||||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
debugfs_create_xul("pending_events", 0400, root,
|
||||||
&host->pending_events);
|
&host->pending_events);
|
||||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
debugfs_create_xul("completed_events", 0400, root,
|
||||||
&host->completed_events);
|
&host->completed_events);
|
||||||
#ifdef CONFIG_FAULT_INJECTION
|
#ifdef CONFIG_FAULT_INJECTION
|
||||||
fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc);
|
fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc);
|
||||||
|
|
@ -3120,9 +3120,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
|
||||||
host->dma_64bit_address = 1;
|
host->dma_64bit_address = 1;
|
||||||
dev_info(host->dev,
|
dev_info(host->dev,
|
||||||
"IDMAC supports 64-bit address mode.\n");
|
"IDMAC supports 64-bit address mode.\n");
|
||||||
if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
|
if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64)))
|
||||||
dma_set_coherent_mask(host->dev,
|
dev_info(host->dev, "Fail to set 64-bit DMA mask");
|
||||||
DMA_BIT_MASK(64));
|
|
||||||
} else {
|
} else {
|
||||||
/* host supports IDMAC in 32-bit address mode */
|
/* host supports IDMAC in 32-bit address mode */
|
||||||
host->dma_64bit_address = 0;
|
host->dma_64bit_address = 0;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
#include <linux/ioport.h>
|
#include <linux/ioport.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
|
@ -98,17 +99,16 @@
|
||||||
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
|
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
|
||||||
#define MESON_MX_SDIO_MAX_SLOTS 3
|
#define MESON_MX_SDIO_MAX_SLOTS 3
|
||||||
|
|
||||||
|
struct meson_mx_mmc_host_clkc {
|
||||||
|
struct clk_divider cfg_div;
|
||||||
|
struct clk_fixed_factor fixed_div2;
|
||||||
|
};
|
||||||
|
|
||||||
struct meson_mx_mmc_host {
|
struct meson_mx_mmc_host {
|
||||||
struct device *controller_dev;
|
struct device *controller_dev;
|
||||||
|
|
||||||
struct clk *parent_clk;
|
|
||||||
struct clk *core_clk;
|
|
||||||
struct clk_divider cfg_div;
|
|
||||||
struct clk *cfg_div_clk;
|
struct clk *cfg_div_clk;
|
||||||
struct clk_fixed_factor fixed_factor;
|
struct regmap *regmap;
|
||||||
struct clk *fixed_factor_clk;
|
|
||||||
|
|
||||||
void __iomem *base;
|
|
||||||
int irq;
|
int irq;
|
||||||
spinlock_t irq_lock;
|
spinlock_t irq_lock;
|
||||||
|
|
||||||
|
|
@ -122,22 +122,10 @@ struct meson_mx_mmc_host {
|
||||||
int error;
|
int error;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask,
|
|
||||||
u32 val)
|
|
||||||
{
|
|
||||||
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
|
||||||
u32 regval;
|
|
||||||
|
|
||||||
regval = readl(host->base + reg);
|
|
||||||
regval &= ~mask;
|
|
||||||
regval |= (val & mask);
|
|
||||||
|
|
||||||
writel(regval, host->base + reg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
|
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
|
||||||
{
|
{
|
||||||
writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC);
|
regmap_write(host->regmap, MESON_MX_SDIO_IRQC,
|
||||||
|
MESON_MX_SDIO_IRQC_SOFT_RESET);
|
||||||
udelay(2);
|
udelay(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
|
||||||
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
||||||
unsigned int pack_size;
|
unsigned int pack_size;
|
||||||
unsigned long irqflags, timeout;
|
unsigned long irqflags, timeout;
|
||||||
u32 mult, send = 0, ext = 0;
|
u32 send = 0, ext = 0;
|
||||||
|
|
||||||
host->cmd = cmd;
|
host->cmd = cmd;
|
||||||
|
|
||||||
|
|
@ -215,25 +203,22 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
|
||||||
|
|
||||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
mult = readl(host->base + MESON_MX_SDIO_MULT);
|
regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
|
||||||
mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK;
|
MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31),
|
||||||
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id);
|
FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK,
|
||||||
mult |= BIT(31);
|
host->slot_id) | BIT(31));
|
||||||
writel(mult, host->base + MESON_MX_SDIO_MULT);
|
|
||||||
|
|
||||||
/* enable the CMD done interrupt */
|
/* enable the CMD done interrupt */
|
||||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC,
|
regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC,
|
||||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
|
|
||||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
||||||
|
|
||||||
/* clear pending interrupts */
|
/* clear pending interrupts */
|
||||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS,
|
regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS,
|
||||||
MESON_MX_SDIO_IRQS_CMD_INT,
|
|
||||||
MESON_MX_SDIO_IRQS_CMD_INT);
|
MESON_MX_SDIO_IRQS_CMD_INT);
|
||||||
|
|
||||||
writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU);
|
regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg);
|
||||||
writel(ext, host->base + MESON_MX_SDIO_EXT);
|
regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext);
|
||||||
writel(send, host->base + MESON_MX_SDIO_SEND);
|
regmap_write(host->regmap, MESON_MX_SDIO_SEND, send);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
|
|
@ -263,13 +248,12 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
||||||
|
|
||||||
switch (ios->bus_width) {
|
switch (ios->bus_width) {
|
||||||
case MMC_BUS_WIDTH_1:
|
case MMC_BUS_WIDTH_1:
|
||||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
|
regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF,
|
||||||
MESON_MX_SDIO_CONF_BUS_WIDTH, 0);
|
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MMC_BUS_WIDTH_4:
|
case MMC_BUS_WIDTH_4:
|
||||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
|
regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF,
|
||||||
MESON_MX_SDIO_CONF_BUS_WIDTH,
|
|
||||||
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
||||||
host->mrq = mrq;
|
host->mrq = mrq;
|
||||||
|
|
||||||
if (mrq->data)
|
if (mrq->data)
|
||||||
writel(sg_dma_address(mrq->data->sg),
|
regmap_write(host->regmap, MESON_MX_SDIO_ADDR,
|
||||||
host->base + MESON_MX_SDIO_ADDR);
|
sg_dma_address(mrq->data->sg));
|
||||||
|
|
||||||
if (mrq->sbc)
|
if (mrq->sbc)
|
||||||
meson_mx_mmc_start_cmd(mmc, mrq->sbc);
|
meson_mx_mmc_start_cmd(mmc, mrq->sbc);
|
||||||
|
|
@ -364,24 +348,26 @@ static void meson_mx_mmc_read_response(struct mmc_host *mmc,
|
||||||
struct mmc_command *cmd)
|
struct mmc_command *cmd)
|
||||||
{
|
{
|
||||||
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
||||||
u32 mult;
|
unsigned int i, resp[4];
|
||||||
int i, resp[4];
|
|
||||||
|
|
||||||
mult = readl(host->base + MESON_MX_SDIO_MULT);
|
regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
|
||||||
mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX;
|
MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
|
||||||
mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK;
|
MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
|
||||||
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0);
|
MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
|
||||||
writel(mult, host->base + MESON_MX_SDIO_MULT);
|
FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
|
||||||
|
0));
|
||||||
|
|
||||||
if (cmd->flags & MMC_RSP_136) {
|
if (cmd->flags & MMC_RSP_136) {
|
||||||
for (i = 0; i <= 3; i++)
|
for (i = 0; i <= 3; i++)
|
||||||
resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU);
|
regmap_read(host->regmap, MESON_MX_SDIO_ARGU,
|
||||||
|
&resp[3 - i]);
|
||||||
|
|
||||||
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
|
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
|
||||||
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
|
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
|
||||||
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
|
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
|
||||||
cmd->resp[3] = (resp[3] << 8);
|
cmd->resp[3] = (resp[3] << 8);
|
||||||
} else if (cmd->flags & MMC_RSP_PRESENT) {
|
} else if (cmd->flags & MMC_RSP_PRESENT) {
|
||||||
cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU);
|
regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &cmd->resp[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -422,8 +408,8 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
|
||||||
|
|
||||||
spin_lock(&host->irq_lock);
|
spin_lock(&host->irq_lock);
|
||||||
|
|
||||||
irqs = readl(host->base + MESON_MX_SDIO_IRQS);
|
regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
|
||||||
send = readl(host->base + MESON_MX_SDIO_SEND);
|
regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send);
|
||||||
|
|
||||||
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
|
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
|
||||||
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
|
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
|
||||||
|
|
@ -431,7 +417,7 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
|
||||||
ret = IRQ_HANDLED;
|
ret = IRQ_HANDLED;
|
||||||
|
|
||||||
/* finally ACK all pending interrupts */
|
/* finally ACK all pending interrupts */
|
||||||
writel(irqs, host->base + MESON_MX_SDIO_IRQS);
|
regmap_write(host->regmap, MESON_MX_SDIO_IRQS, irqs);
|
||||||
|
|
||||||
spin_unlock(&host->irq_lock);
|
spin_unlock(&host->irq_lock);
|
||||||
|
|
||||||
|
|
@ -450,8 +436,7 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
|
||||||
|
|
||||||
if (cmd->data) {
|
if (cmd->data) {
|
||||||
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
|
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
|
||||||
cmd->data->sg_len,
|
cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
|
||||||
mmc_get_dma_dir(cmd->data));
|
|
||||||
|
|
||||||
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
|
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
|
||||||
}
|
}
|
||||||
|
|
@ -470,14 +455,13 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
|
||||||
struct meson_mx_mmc_host *host = timer_container_of(host, t,
|
struct meson_mx_mmc_host *host = timer_container_of(host, t,
|
||||||
cmd_timeout);
|
cmd_timeout);
|
||||||
unsigned long irqflags;
|
unsigned long irqflags;
|
||||||
u32 irqc;
|
u32 irqs, argu;
|
||||||
|
|
||||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
/* disable the CMD interrupt */
|
/* disable the CMD interrupt */
|
||||||
irqc = readl(host->base + MESON_MX_SDIO_IRQC);
|
regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC,
|
||||||
irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN;
|
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
||||||
writel(irqc, host->base + MESON_MX_SDIO_IRQC);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||||
|
|
||||||
|
|
@ -488,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
|
||||||
if (!host->cmd)
|
if (!host->cmd)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
|
||||||
|
regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &argu);
|
||||||
|
|
||||||
dev_dbg(mmc_dev(host->mmc),
|
dev_dbg(mmc_dev(host->mmc),
|
||||||
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
|
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
|
||||||
host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS),
|
host->cmd->opcode, irqs, argu);
|
||||||
readl(host->base + MESON_MX_SDIO_ARGU));
|
|
||||||
|
|
||||||
host->cmd->error = -ETIMEDOUT;
|
host->cmd->error = -ETIMEDOUT;
|
||||||
|
|
||||||
|
|
@ -507,22 +493,29 @@ static struct mmc_host_ops meson_mx_mmc_ops = {
|
||||||
|
|
||||||
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
|
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
|
||||||
{
|
{
|
||||||
struct device_node *slot_node;
|
struct platform_device *pdev = NULL;
|
||||||
struct platform_device *pdev;
|
|
||||||
|
for_each_available_child_of_node_scoped(parent->of_node, slot_node) {
|
||||||
|
if (!of_device_is_compatible(slot_node, "mmc-slot"))
|
||||||
|
continue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* TODO: the MMC core framework currently does not support
|
* TODO: the MMC core framework currently does not support
|
||||||
* controllers with multiple slots properly. So we only register
|
* controllers with multiple slots properly. So we only
|
||||||
* the first slot for now
|
* register the first slot for now.
|
||||||
*/
|
*/
|
||||||
slot_node = of_get_compatible_child(parent->of_node, "mmc-slot");
|
if (pdev) {
|
||||||
if (!slot_node) {
|
dev_warn(parent,
|
||||||
dev_warn(parent, "no 'mmc-slot' sub-node found\n");
|
"more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n");
|
||||||
return ERR_PTR(-ENOENT);
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pdev = of_platform_device_create(slot_node, NULL, parent);
|
pdev = of_platform_device_create(slot_node, NULL, parent);
|
||||||
of_node_put(slot_node);
|
if (!pdev)
|
||||||
|
dev_err(parent,
|
||||||
|
"Failed to create platform device for mmc-slot node '%pOF'\n",
|
||||||
|
slot_node);
|
||||||
|
}
|
||||||
|
|
||||||
return pdev;
|
return pdev;
|
||||||
}
|
}
|
||||||
|
|
@ -533,16 +526,14 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
|
||||||
struct device *slot_dev = mmc_dev(mmc);
|
struct device *slot_dev = mmc_dev(mmc);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) {
|
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id))
|
||||||
dev_err(slot_dev, "missing 'reg' property\n");
|
return dev_err_probe(slot_dev, -EINVAL,
|
||||||
return -EINVAL;
|
"missing 'reg' property\n");
|
||||||
}
|
|
||||||
|
|
||||||
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) {
|
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS)
|
||||||
dev_err(slot_dev, "invalid 'reg' property value %d\n",
|
return dev_err_probe(slot_dev, -EINVAL,
|
||||||
|
"invalid 'reg' property value %d\n",
|
||||||
host->slot_id);
|
host->slot_id);
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get regulators and the supported OCR mask */
|
/* Get regulators and the supported OCR mask */
|
||||||
ret = mmc_regulator_get_supply(mmc);
|
ret = mmc_regulator_get_supply(mmc);
|
||||||
|
|
@ -561,8 +552,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
|
||||||
|
|
||||||
/* Get the min and max supported clock rates */
|
/* Get the min and max supported clock rates */
|
||||||
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
|
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
|
||||||
mmc->f_max = clk_round_rate(host->cfg_div_clk,
|
mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX);
|
||||||
clk_get_rate(host->parent_clk));
|
|
||||||
|
|
||||||
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
||||||
mmc->ops = &meson_mx_mmc_ops;
|
mmc->ops = &meson_mx_mmc_ops;
|
||||||
|
|
@ -578,70 +568,89 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host)
|
static struct clk *meson_mx_mmc_register_clk(struct device *dev,
|
||||||
|
void __iomem *base)
|
||||||
{
|
{
|
||||||
struct clk_init_data init;
|
const char *fixed_div2_name, *cfg_div_name;
|
||||||
const char *clk_div_parent, *clk_fixed_factor_parent;
|
struct meson_mx_mmc_host_clkc *host_clkc;
|
||||||
|
struct clk *clk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
clk_fixed_factor_parent = __clk_get_name(host->parent_clk);
|
/* use a dedicated memory allocation for the clock controller to
|
||||||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
* prevent use-after-free as meson_mx_mmc_host is free'd before
|
||||||
"%s#fixed_factor",
|
* dev (controller dev, not mmc_host->dev) is free'd.
|
||||||
dev_name(host->controller_dev));
|
*/
|
||||||
if (!init.name)
|
host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL);
|
||||||
return -ENOMEM;
|
if (!host_clkc)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
init.ops = &clk_fixed_factor_ops;
|
fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2",
|
||||||
init.flags = 0;
|
dev_name(dev));
|
||||||
init.parent_names = &clk_fixed_factor_parent;
|
if (!fixed_div2_name)
|
||||||
init.num_parents = 1;
|
return ERR_PTR(-ENOMEM);
|
||||||
host->fixed_factor.div = 2;
|
|
||||||
host->fixed_factor.mult = 1;
|
|
||||||
host->fixed_factor.hw.init = &init;
|
|
||||||
|
|
||||||
host->fixed_factor_clk = devm_clk_register(host->controller_dev,
|
host_clkc->fixed_div2.div = 2;
|
||||||
&host->fixed_factor.hw);
|
host_clkc->fixed_div2.mult = 1;
|
||||||
if (WARN_ON(IS_ERR(host->fixed_factor_clk)))
|
host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name,
|
||||||
return PTR_ERR(host->fixed_factor_clk);
|
"clkin",
|
||||||
|
&clk_fixed_factor_ops,
|
||||||
|
0);
|
||||||
|
ret = devm_clk_hw_register(dev, &host_clkc->fixed_div2.hw);
|
||||||
|
if (ret)
|
||||||
|
return dev_err_ptr_probe(dev, ret,
|
||||||
|
"Failed to register %s clock\n",
|
||||||
|
fixed_div2_name);
|
||||||
|
|
||||||
clk_div_parent = __clk_get_name(host->fixed_factor_clk);
|
cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev));
|
||||||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
if (!cfg_div_name)
|
||||||
"%s#div", dev_name(host->controller_dev));
|
return ERR_PTR(-ENOMEM);
|
||||||
if (!init.name)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
init.ops = &clk_divider_ops;
|
host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF;
|
||||||
init.flags = CLK_SET_RATE_PARENT;
|
host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
|
||||||
init.parent_names = &clk_div_parent;
|
host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
|
||||||
init.num_parents = 1;
|
host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name,
|
||||||
host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF;
|
&host_clkc->fixed_div2.hw,
|
||||||
host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
|
&clk_divider_ops,
|
||||||
host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
|
CLK_DIVIDER_ALLOW_ZERO);
|
||||||
host->cfg_div.hw.init = &init;
|
ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw);
|
||||||
host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO;
|
if (ret)
|
||||||
|
return dev_err_ptr_probe(dev, ret,
|
||||||
|
"Failed to register %s clock\n",
|
||||||
|
cfg_div_name);
|
||||||
|
|
||||||
host->cfg_div_clk = devm_clk_register(host->controller_dev,
|
clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk");
|
||||||
&host->cfg_div.hw);
|
if (IS_ERR(clk))
|
||||||
if (WARN_ON(IS_ERR(host->cfg_div_clk)))
|
return dev_err_ptr_probe(dev, PTR_ERR(clk),
|
||||||
return PTR_ERR(host->cfg_div_clk);
|
"Failed to get the cfg_div clock\n");
|
||||||
|
|
||||||
return 0;
|
return clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int meson_mx_mmc_probe(struct platform_device *pdev)
|
static int meson_mx_mmc_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
const struct regmap_config meson_mx_sdio_regmap_config = {
|
||||||
|
.reg_bits = 8,
|
||||||
|
.val_bits = 32,
|
||||||
|
.reg_stride = 4,
|
||||||
|
.max_register = MESON_MX_SDIO_EXT,
|
||||||
|
};
|
||||||
struct platform_device *slot_pdev;
|
struct platform_device *slot_pdev;
|
||||||
struct mmc_host *mmc;
|
struct mmc_host *mmc;
|
||||||
struct meson_mx_mmc_host *host;
|
struct meson_mx_mmc_host *host;
|
||||||
|
struct clk *core_clk;
|
||||||
|
void __iomem *base;
|
||||||
int ret, irq;
|
int ret, irq;
|
||||||
u32 conf;
|
u32 conf;
|
||||||
|
|
||||||
|
base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(base))
|
||||||
|
return PTR_ERR(base);
|
||||||
|
|
||||||
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
|
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
|
||||||
if (!slot_pdev)
|
if (!slot_pdev)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
else if (IS_ERR(slot_pdev))
|
|
||||||
return PTR_ERR(slot_pdev);
|
|
||||||
|
|
||||||
mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev);
|
mmc = devm_mmc_alloc_host(&slot_pdev->dev, sizeof(*host));
|
||||||
if (!mmc) {
|
if (!mmc) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto error_unregister_slot_pdev;
|
goto error_unregister_slot_pdev;
|
||||||
|
|
@ -656,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
platform_set_drvdata(pdev, host);
|
platform_set_drvdata(pdev, host);
|
||||||
|
|
||||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||||
if (IS_ERR(host->base)) {
|
&meson_mx_sdio_regmap_config);
|
||||||
ret = PTR_ERR(host->base);
|
if (IS_ERR(host->regmap)) {
|
||||||
goto error_free_mmc;
|
ret = dev_err_probe(host->controller_dev, PTR_ERR(host->regmap),
|
||||||
|
"Failed to initialize regmap\n");
|
||||||
|
goto error_unregister_slot_pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
ret = irq;
|
ret = irq;
|
||||||
goto error_free_mmc;
|
goto error_unregister_slot_pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(host->controller_dev, irq,
|
ret = devm_request_threaded_irq(host->controller_dev, irq,
|
||||||
meson_mx_mmc_irq,
|
meson_mx_mmc_irq,
|
||||||
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
|
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
|
||||||
NULL, host);
|
NULL, host);
|
||||||
if (ret)
|
|
||||||
goto error_free_mmc;
|
|
||||||
|
|
||||||
host->core_clk = devm_clk_get(host->controller_dev, "core");
|
|
||||||
if (IS_ERR(host->core_clk)) {
|
|
||||||
ret = PTR_ERR(host->core_clk);
|
|
||||||
goto error_free_mmc;
|
|
||||||
}
|
|
||||||
|
|
||||||
host->parent_clk = devm_clk_get(host->controller_dev, "clkin");
|
|
||||||
if (IS_ERR(host->parent_clk)) {
|
|
||||||
ret = PTR_ERR(host->parent_clk);
|
|
||||||
goto error_free_mmc;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = meson_mx_mmc_register_clks(host);
|
|
||||||
if (ret)
|
|
||||||
goto error_free_mmc;
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(host->core_clk);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(host->controller_dev, "Failed to enable core clock\n");
|
dev_err_probe(host->controller_dev, ret,
|
||||||
goto error_free_mmc;
|
"Failed to request IRQ\n");
|
||||||
|
goto error_unregister_slot_pdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
core_clk = devm_clk_get_enabled(host->controller_dev, "core");
|
||||||
|
if (IS_ERR(core_clk)) {
|
||||||
|
ret = dev_err_probe(host->controller_dev, PTR_ERR(core_clk),
|
||||||
|
"Failed to get and enable 'core' clock\n");
|
||||||
|
goto error_unregister_slot_pdev;
|
||||||
|
}
|
||||||
|
|
||||||
|
host->cfg_div_clk = meson_mx_mmc_register_clk(&pdev->dev, base);
|
||||||
|
if (IS_ERR(host->cfg_div_clk)) {
|
||||||
|
ret = PTR_ERR(host->cfg_div_clk);
|
||||||
|
goto error_unregister_slot_pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = clk_prepare_enable(host->cfg_div_clk);
|
ret = clk_prepare_enable(host->cfg_div_clk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(host->controller_dev, "Failed to enable MMC clock\n");
|
dev_err_probe(host->controller_dev, ret,
|
||||||
goto error_disable_core_clk;
|
"Failed to enable MMC (cfg div) clock\n");
|
||||||
|
goto error_unregister_slot_pdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = 0;
|
conf = 0;
|
||||||
|
|
@ -708,22 +714,18 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
|
||||||
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
|
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
|
||||||
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
|
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
|
||||||
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
|
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
|
||||||
writel(conf, host->base + MESON_MX_SDIO_CONF);
|
regmap_write(host->regmap, MESON_MX_SDIO_CONF, conf);
|
||||||
|
|
||||||
meson_mx_mmc_soft_reset(host);
|
meson_mx_mmc_soft_reset(host);
|
||||||
|
|
||||||
ret = meson_mx_mmc_add_host(host);
|
ret = meson_mx_mmc_add_host(host);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto error_disable_clks;
|
goto error_disable_div_clk;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error_disable_clks:
|
error_disable_div_clk:
|
||||||
clk_disable_unprepare(host->cfg_div_clk);
|
clk_disable_unprepare(host->cfg_div_clk);
|
||||||
error_disable_core_clk:
|
|
||||||
clk_disable_unprepare(host->core_clk);
|
|
||||||
error_free_mmc:
|
|
||||||
mmc_free_host(mmc);
|
|
||||||
error_unregister_slot_pdev:
|
error_unregister_slot_pdev:
|
||||||
of_platform_device_destroy(&slot_pdev->dev, NULL);
|
of_platform_device_destroy(&slot_pdev->dev, NULL);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -741,9 +743,6 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
|
||||||
of_platform_device_destroy(slot_dev, NULL);
|
of_platform_device_destroy(slot_dev, NULL);
|
||||||
|
|
||||||
clk_disable_unprepare(host->cfg_div_clk);
|
clk_disable_unprepare(host->cfg_div_clk);
|
||||||
clk_disable_unprepare(host->core_clk);
|
|
||||||
|
|
||||||
mmc_free_host(host->mmc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id meson_mx_mmc_of_match[] = {
|
static const struct of_device_id meson_mx_mmc_of_match[] = {
|
||||||
|
|
|
||||||
|
|
@ -1214,7 +1214,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
|
||||||
host->data = data;
|
host->data = data;
|
||||||
read = data->flags & MMC_DATA_READ;
|
read = data->flags & MMC_DATA_READ;
|
||||||
|
|
||||||
mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
|
mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
|
||||||
msdc_dma_setup(host, &host->dma, data);
|
msdc_dma_setup(host, &host->dma, data);
|
||||||
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
|
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
|
||||||
sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
|
sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
|
||||||
|
|
@ -1444,7 +1444,7 @@ static void msdc_start_command(struct msdc_host *host,
|
||||||
WARN_ON(host->cmd);
|
WARN_ON(host->cmd);
|
||||||
host->cmd = cmd;
|
host->cmd = cmd;
|
||||||
|
|
||||||
mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT);
|
mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
|
||||||
if (!msdc_cmd_is_ready(host, mrq, cmd))
|
if (!msdc_cmd_is_ready(host, mrq, cmd))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
|
||||||
"closed");
|
"closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
|
static DEVICE_ATTR(cover_switch, 0444, mmc_omap_show_cover_switch, NULL);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
|
mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
|
||||||
|
|
@ -338,7 +338,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
|
||||||
return sprintf(buf, "%s\n", slot->pdata->name);
|
return sprintf(buf, "%s\n", slot->pdata->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL);
|
static DEVICE_ATTR(slot_name, 0444, mmc_omap_show_slot_name, NULL);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
|
||||||
|
|
@ -1477,7 +1477,7 @@ static int mmc_omap_probe(struct platform_device *pdev)
|
||||||
host->nr_slots = pdata->nr_slots;
|
host->nr_slots = pdata->nr_slots;
|
||||||
host->reg_shift = (mmc_omap7xx() ? 1 : 2);
|
host->reg_shift = (mmc_omap7xx() ? 1 : 2);
|
||||||
|
|
||||||
host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0);
|
host->mmc_omap_wq = alloc_workqueue("mmc_omap", WQ_PERCPU, 0);
|
||||||
if (!host->mmc_omap_wq) {
|
if (!host->mmc_omap_wq) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_plat_cleanup;
|
goto err_plat_cleanup;
|
||||||
|
|
|
||||||
|
|
@ -746,7 +746,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
|
||||||
return sprintf(buf, "%s\n", mmc_pdata(host)->name);
|
return sprintf(buf, "%s\n", mmc_pdata(host)->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL);
|
static DEVICE_ATTR(slot_name, 0444, omap_hsmmc_show_slot_name, NULL);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configure the response type and send the cmd.
|
* Configure the response type and send the cmd.
|
||||||
|
|
@ -1672,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs);
|
||||||
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
|
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
if (mmc->debugfs_root)
|
if (mmc->debugfs_root)
|
||||||
debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root,
|
debugfs_create_file("regs", 0400, mmc->debugfs_root,
|
||||||
mmc, &mmc_regs_fops);
|
mmc, &mmc_regs_fops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#ifndef RENESAS_SDHI_H
|
#ifndef RENESAS_SDHI_H
|
||||||
#define RENESAS_SDHI_H
|
#define RENESAS_SDHI_H
|
||||||
|
|
||||||
|
#include <linux/device.h>
|
||||||
#include <linux/dmaengine.h>
|
#include <linux/dmaengine.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
@ -107,4 +108,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
const struct renesas_sdhi_of_data *of_data,
|
const struct renesas_sdhi_of_data *of_data,
|
||||||
const struct renesas_sdhi_quirks *quirks);
|
const struct renesas_sdhi_quirks *quirks);
|
||||||
void renesas_sdhi_remove(struct platform_device *pdev);
|
void renesas_sdhi_remove(struct platform_device *pdev);
|
||||||
|
int renesas_sdhi_suspend(struct device *dev);
|
||||||
|
int renesas_sdhi_resume(struct device *dev);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@
|
||||||
#include <linux/platform_data/tmio.h>
|
#include <linux/platform_data/tmio.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/regulator/consumer.h>
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/regulator/driver.h>
|
#include <linux/regulator/driver.h>
|
||||||
#include <linux/regulator/of_regulator.h>
|
#include <linux/regulator/of_regulator.h>
|
||||||
|
|
@ -1103,7 +1104,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
|
||||||
if (IS_ERR(priv->clk_cd))
|
if (IS_ERR(priv->clk_cd))
|
||||||
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
|
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);
|
priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
|
||||||
if (IS_ERR(priv->rstc))
|
if (IS_ERR(priv->rstc))
|
||||||
return PTR_ERR(priv->rstc);
|
return PTR_ERR(priv->rstc);
|
||||||
|
|
||||||
|
|
@ -1317,5 +1318,41 @@ void renesas_sdhi_remove(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
|
EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
|
||||||
|
|
||||||
|
int renesas_sdhi_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_force_suspend(dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = reset_control_assert(priv->rstc);
|
||||||
|
if (ret)
|
||||||
|
pm_runtime_force_resume(dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(renesas_sdhi_suspend);
|
||||||
|
|
||||||
|
int renesas_sdhi_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct tmio_mmc_host *host = dev_get_drvdata(dev);
|
||||||
|
struct renesas_sdhi *priv = host_to_priv(host);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = reset_control_deassert(priv->rstc);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = pm_runtime_force_resume(dev);
|
||||||
|
if (ret)
|
||||||
|
reset_control_assert(priv->rstc);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(renesas_sdhi_resume);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Renesas SDHI core driver");
|
MODULE_DESCRIPTION("Renesas SDHI core driver");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/platform_data/tmio.h>
|
#include <linux/platform_data/tmio.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_runtime.h>
|
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/sys_soc.h>
|
#include <linux/sys_soc.h>
|
||||||
|
|
||||||
|
|
@ -124,7 +123,8 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
|
||||||
|
|
||||||
static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
|
static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
|
||||||
|
TMIO_MMC_64BIT_DATA_PORT,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
||||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
|
||||||
|
|
@ -599,9 +599,8 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
|
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
|
||||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume)
|
||||||
pm_runtime_force_resume)
|
RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
||||||
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
|
||||||
tmio_mmc_host_runtime_resume,
|
tmio_mmc_host_runtime_resume,
|
||||||
NULL)
|
NULL)
|
||||||
};
|
};
|
||||||
|
|
@ -610,7 +609,7 @@ static struct platform_driver renesas_internal_dmac_sdhi_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "renesas_sdhi_internal_dmac",
|
.name = "renesas_sdhi_internal_dmac",
|
||||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||||
.pm = &renesas_sdhi_internal_dmac_dev_pm_ops,
|
.pm = pm_ptr(&renesas_sdhi_internal_dmac_dev_pm_ops),
|
||||||
.of_match_table = renesas_sdhi_internal_dmac_of_match,
|
.of_match_table = renesas_sdhi_internal_dmac_of_match,
|
||||||
},
|
},
|
||||||
.probe = renesas_sdhi_internal_dmac_probe,
|
.probe = renesas_sdhi_internal_dmac_probe,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
|
||||||
|
|
||||||
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
|
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
|
||||||
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
|
||||||
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2,
|
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
|
||||||
|
TMIO_MMC_32BIT_DATA_PORT,
|
||||||
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
|
||||||
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
||||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||||
|
|
|
||||||
|
|
@ -31,35 +31,116 @@
|
||||||
|
|
||||||
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
|
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
|
||||||
|
|
||||||
#define SDIO_CFG_CQ_CAPABILITY 0x4c
|
|
||||||
#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
|
|
||||||
|
|
||||||
#define SDIO_CFG_CTRL 0x0
|
#define SDIO_CFG_CTRL 0x0
|
||||||
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
|
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
|
||||||
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
|
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
|
||||||
|
#define SDIO_CFG_OP_DLY 0x34
|
||||||
|
#define SDIO_CFG_OP_DLY_DEFAULT 0x80000003
|
||||||
|
#define SDIO_CFG_CQ_CAPABILITY 0x4c
|
||||||
|
#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
|
||||||
|
#define SDIO_CFG_SD_PIN_SEL 0x44
|
||||||
|
#define SDIO_CFG_V1_SD_PIN_SEL 0x54
|
||||||
|
#define SDIO_CFG_PHY_SW_MODE_0_RX_CTRL 0x7C
|
||||||
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
|
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
|
||||||
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
|
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
|
||||||
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
|
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
|
||||||
|
|
||||||
|
#define SDIO_BOOT_MAIN_CTL 0x0
|
||||||
|
|
||||||
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
|
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
|
||||||
/* Select all SD UHS type I SDR speed above 50MB/s */
|
/* Select all SD UHS type I SDR speed above 50MB/s */
|
||||||
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
|
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
|
||||||
|
|
||||||
struct sdhci_brcmstb_priv {
|
enum cfg_core_ver {
|
||||||
void __iomem *cfg_regs;
|
SDIO_CFG_CORE_V1 = 1,
|
||||||
unsigned int flags;
|
SDIO_CFG_CORE_V2,
|
||||||
struct clk *base_clk;
|
};
|
||||||
u32 base_freq_hz;
|
|
||||||
|
struct sdhci_brcmstb_saved_regs {
|
||||||
|
u32 sd_pin_sel;
|
||||||
|
u32 phy_sw_mode0_rxctrl;
|
||||||
|
u32 max_50mhz_mode;
|
||||||
|
u32 boot_main_ctl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct brcmstb_match_priv {
|
struct brcmstb_match_priv {
|
||||||
void (*cfginit)(struct sdhci_host *host);
|
void (*cfginit)(struct sdhci_host *host);
|
||||||
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
|
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
|
||||||
|
void (*save_restore_regs)(struct mmc_host *mmc, int save);
|
||||||
struct sdhci_ops *ops;
|
struct sdhci_ops *ops;
|
||||||
const unsigned int flags;
|
const unsigned int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sdhci_brcmstb_priv {
|
||||||
|
void __iomem *cfg_regs;
|
||||||
|
void __iomem *boot_regs;
|
||||||
|
struct sdhci_brcmstb_saved_regs saved_regs;
|
||||||
|
unsigned int flags;
|
||||||
|
struct clk *base_clk;
|
||||||
|
u32 base_freq_hz;
|
||||||
|
const struct brcmstb_match_priv *match_priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void sdhci_brcmstb_save_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
|
||||||
|
void __iomem *cr = priv->cfg_regs;
|
||||||
|
bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
|
||||||
|
|
||||||
|
if (is_emmc && priv->boot_regs)
|
||||||
|
sr->boot_main_ctl = readl(priv->boot_regs + SDIO_BOOT_MAIN_CTL);
|
||||||
|
|
||||||
|
if (ver == SDIO_CFG_CORE_V1) {
|
||||||
|
sr->sd_pin_sel = readl(cr + SDIO_CFG_V1_SD_PIN_SEL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sr->sd_pin_sel = readl(cr + SDIO_CFG_SD_PIN_SEL);
|
||||||
|
sr->phy_sw_mode0_rxctrl = readl(cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
|
||||||
|
sr->max_50mhz_mode = readl(cr + SDIO_CFG_MAX_50MHZ_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_brcmstb_restore_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
|
||||||
|
void __iomem *cr = priv->cfg_regs;
|
||||||
|
bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
|
||||||
|
|
||||||
|
if (is_emmc && priv->boot_regs)
|
||||||
|
writel(sr->boot_main_ctl, priv->boot_regs + SDIO_BOOT_MAIN_CTL);
|
||||||
|
|
||||||
|
if (ver == SDIO_CFG_CORE_V1) {
|
||||||
|
writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
|
||||||
|
writel(sr->phy_sw_mode0_rxctrl, cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
|
||||||
|
writel(sr->max_50mhz_mode, cr + SDIO_CFG_MAX_50MHZ_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_brcmstb_save_restore_regs_v1(struct mmc_host *mmc, int save)
|
||||||
|
{
|
||||||
|
if (save)
|
||||||
|
sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V1);
|
||||||
|
else
|
||||||
|
sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_brcmstb_save_restore_regs_v2(struct mmc_host *mmc, int save)
|
||||||
|
{
|
||||||
|
if (save)
|
||||||
|
sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V2);
|
||||||
|
else
|
||||||
|
sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V2);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void enable_clock_gating(struct sdhci_host *host)
|
static inline void enable_clock_gating(struct sdhci_host *host)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
|
@ -212,6 +293,21 @@ static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_brcmstb_set_72116_uhs_signaling(struct sdhci_host *host, unsigned int timing)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* no change to SDIO_CFG_OP_DLY_DEFAULT when using preset clk rate */
|
||||||
|
if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
|
||||||
|
return;
|
||||||
|
|
||||||
|
reg = (timing == MMC_TIMING_MMC_HS200) ? 0 : SDIO_CFG_OP_DLY_DEFAULT;
|
||||||
|
writel(reg, priv->cfg_regs + SDIO_CFG_OP_DLY);
|
||||||
|
sdhci_set_uhs_signaling(host, timing);
|
||||||
|
}
|
||||||
|
|
||||||
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
|
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
|
||||||
{
|
{
|
||||||
sdhci_dumpregs(mmc_priv(mmc));
|
sdhci_dumpregs(mmc_priv(mmc));
|
||||||
|
|
@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
|
||||||
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
.set_uhs_signaling = sdhci_set_uhs_signaling,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct sdhci_ops sdhci_brcmstb_ops_72116 = {
|
||||||
|
.set_clock = sdhci_set_clock,
|
||||||
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
.reset = sdhci_reset,
|
||||||
|
.set_uhs_signaling = sdhci_brcmstb_set_72116_uhs_signaling,
|
||||||
|
};
|
||||||
|
|
||||||
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
|
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
|
||||||
.set_clock = sdhci_brcmstb_set_clock,
|
.set_clock = sdhci_brcmstb_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
|
@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = {
|
||||||
.ops = &sdhci_brcmstb_ops,
|
.ops = &sdhci_brcmstb_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct brcmstb_match_priv match_priv_7445 = {
|
static struct brcmstb_match_priv match_priv_74371 = {
|
||||||
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
|
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
|
||||||
.ops = &sdhci_brcmstb_ops,
|
.ops = &sdhci_brcmstb_ops,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct brcmstb_match_priv match_priv_7445 = {
|
||||||
|
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
|
||||||
|
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
|
||||||
|
.ops = &sdhci_brcmstb_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct brcmstb_match_priv match_priv_72116 = {
|
||||||
|
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
|
||||||
|
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
|
||||||
|
.ops = &sdhci_brcmstb_ops_72116,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct brcmstb_match_priv match_priv_7216 = {
|
static const struct brcmstb_match_priv match_priv_7216 = {
|
||||||
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
||||||
|
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
|
||||||
.hs400es = sdhci_brcmstb_hs400es,
|
.hs400es = sdhci_brcmstb_hs400es,
|
||||||
.ops = &sdhci_brcmstb_ops_7216,
|
.ops = &sdhci_brcmstb_ops_7216,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct brcmstb_match_priv match_priv_74165b0 = {
|
static struct brcmstb_match_priv match_priv_74165b0 = {
|
||||||
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
||||||
|
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
|
||||||
.hs400es = sdhci_brcmstb_hs400es,
|
.hs400es = sdhci_brcmstb_hs400es,
|
||||||
.ops = &sdhci_brcmstb_ops_74165b0,
|
.ops = &sdhci_brcmstb_ops_74165b0,
|
||||||
};
|
};
|
||||||
|
|
@ -297,7 +414,9 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
|
||||||
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
|
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
|
||||||
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
|
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
|
||||||
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
|
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
|
||||||
|
{ .compatible = "brcm,bcm74371-sdhci", .data = &match_priv_74371 },
|
||||||
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
|
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
|
||||||
|
{ .compatible = "brcm,bcm72116-sdhci", .data = &match_priv_72116 },
|
||||||
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
|
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
|
||||||
{ .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 },
|
{ .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 },
|
||||||
{},
|
{},
|
||||||
|
|
@ -395,6 +514,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
pltfm_host = sdhci_priv(host);
|
pltfm_host = sdhci_priv(host);
|
||||||
priv = sdhci_pltfm_priv(pltfm_host);
|
priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
priv->match_priv = match->data;
|
||||||
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
|
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
|
||||||
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
|
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
|
||||||
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
|
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
|
||||||
|
|
@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
||||||
if (res)
|
if (res)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
/* map non-standard BOOT registers if present */
|
||||||
|
if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
|
||||||
|
priv->boot_regs = devm_platform_get_and_ioremap_resource(pdev, 2, NULL);
|
||||||
|
if (IS_ERR(priv->boot_regs))
|
||||||
|
priv->boot_regs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Automatic clock gating does not work for SD cards that may
|
* Automatic clock gating does not work for SD cards that may
|
||||||
* voltage switch so only enable it for non-removable devices.
|
* voltage switch so only enable it for non-removable devices.
|
||||||
|
|
@ -501,8 +628,13 @@ static int sdhci_brcmstb_suspend(struct device *dev)
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
const struct brcmstb_match_priv *match_priv = priv->match_priv;
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
if (match_priv->save_restore_regs)
|
||||||
|
match_priv->save_restore_regs(host->mmc, 1);
|
||||||
|
|
||||||
clk_disable_unprepare(priv->base_clk);
|
clk_disable_unprepare(priv->base_clk);
|
||||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||||
ret = cqhci_suspend(host->mmc);
|
ret = cqhci_suspend(host->mmc);
|
||||||
|
|
@ -518,6 +650,7 @@ static int sdhci_brcmstb_resume(struct device *dev)
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
const struct brcmstb_match_priv *match_priv = priv->match_priv;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = sdhci_pltfm_resume(dev);
|
ret = sdhci_pltfm_resume(dev);
|
||||||
|
|
@ -534,6 +667,9 @@ static int sdhci_brcmstb_resume(struct device *dev)
|
||||||
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
|
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (match_priv->save_restore_regs)
|
||||||
|
match_priv->save_restore_regs(host->mmc, 0);
|
||||||
|
|
||||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||||
ret = cqhci_resume(host->mmc);
|
ret = cqhci_resume(host->mmc);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -344,41 +344,43 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
|
||||||
writel_relaxed(val, host->ioaddr + offset);
|
writel_relaxed(val, host->ioaddr + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host)
|
static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host,
|
||||||
|
unsigned int clock,
|
||||||
|
unsigned int timing)
|
||||||
{
|
{
|
||||||
struct mmc_ios ios = host->mmc->ios;
|
|
||||||
/*
|
/*
|
||||||
* The SDHC requires internal clock frequency to be double the
|
* The SDHC requires internal clock frequency to be double the
|
||||||
* actual clock that will be set for DDR mode. The controller
|
* actual clock that will be set for DDR mode. The controller
|
||||||
* uses the faster clock(100/400MHz) for some of its parts and
|
* uses the faster clock(100/400MHz) for some of its parts and
|
||||||
* send the actual required clock (50/200MHz) to the card.
|
* send the actual required clock (50/200MHz) to the card.
|
||||||
*/
|
*/
|
||||||
if (ios.timing == MMC_TIMING_UHS_DDR50 ||
|
if (timing == MMC_TIMING_UHS_DDR50 ||
|
||||||
ios.timing == MMC_TIMING_MMC_DDR52 ||
|
timing == MMC_TIMING_MMC_DDR52 ||
|
||||||
ios.timing == MMC_TIMING_MMC_HS400 ||
|
(timing == MMC_TIMING_MMC_HS400 &&
|
||||||
|
clock == MMC_HS200_MAX_DTR) ||
|
||||||
host->flags & SDHCI_HS400_TUNING)
|
host->flags & SDHCI_HS400_TUNING)
|
||||||
return 2;
|
return 2;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||||
unsigned int clock)
|
unsigned int clock,
|
||||||
|
unsigned int timing)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
struct mmc_ios curr_ios = host->mmc->ios;
|
|
||||||
struct clk *core_clk = msm_host->bulk_clks[0].clk;
|
struct clk *core_clk = msm_host->bulk_clks[0].clk;
|
||||||
unsigned long achieved_rate;
|
unsigned long achieved_rate;
|
||||||
unsigned int desired_rate;
|
unsigned int desired_rate;
|
||||||
unsigned int mult;
|
unsigned int mult;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
mult = msm_get_clock_mult_for_bus_mode(host);
|
mult = msm_get_clock_mult_for_bus_mode(host, clock, timing);
|
||||||
desired_rate = clock * mult;
|
desired_rate = clock * mult;
|
||||||
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
|
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
pr_err("%s: Failed to set clock at rate %u at timing %d\n",
|
||||||
mmc_hostname(host->mmc), desired_rate, curr_ios.timing);
|
mmc_hostname(host->mmc), desired_rate, timing);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,7 +399,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
||||||
msm_host->clk_rate = desired_rate;
|
msm_host->clk_rate = desired_rate;
|
||||||
|
|
||||||
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
|
pr_debug("%s: Setting clock at rate %lu at timing %d\n",
|
||||||
mmc_hostname(host->mmc), achieved_rate, curr_ios.timing);
|
mmc_hostname(host->mmc), achieved_rate, timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Platform specific tuning */
|
/* Platform specific tuning */
|
||||||
|
|
@ -1239,7 +1241,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
||||||
*/
|
*/
|
||||||
if (host->flags & SDHCI_HS400_TUNING) {
|
if (host->flags & SDHCI_HS400_TUNING) {
|
||||||
sdhci_msm_hc_select_mode(host);
|
sdhci_msm_hc_select_mode(host);
|
||||||
msm_set_clock_rate_for_bus_mode(host, ios.clock);
|
msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
|
||||||
host->flags &= ~SDHCI_HS400_TUNING;
|
host->flags &= ~SDHCI_HS400_TUNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1864,6 +1866,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
struct mmc_ios ios = host->mmc->ios;
|
||||||
|
|
||||||
if (!clock) {
|
if (!clock) {
|
||||||
host->mmc->actual_clock = msm_host->clk_rate = 0;
|
host->mmc->actual_clock = msm_host->clk_rate = 0;
|
||||||
|
|
@ -1872,7 +1875,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
|
||||||
sdhci_msm_hc_select_mode(host);
|
sdhci_msm_hc_select_mode(host);
|
||||||
|
|
||||||
msm_set_clock_rate_for_bus_mode(host, clock);
|
msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
|
||||||
out:
|
out:
|
||||||
__sdhci_msm_set_clock(host, clock);
|
__sdhci_msm_set_clock(host, clock);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1991,7 +1991,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
ret = mmc_of_parse(host->mmc);
|
ret = mmc_of_parse(host->mmc);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
|
dev_err_probe(dev, ret, "parsing dt failed.\n");
|
||||||
goto unreg_clk;
|
goto unreg_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <linux/arm-smccc.h>
|
#include <linux/arm-smccc.h>
|
||||||
#include <linux/bitfield.h>
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/iopoll.h>
|
#include <linux/iopoll.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
|
@ -19,11 +20,15 @@
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/pm_domain.h>
|
#include <linux/pm_domain.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/reset.h>
|
#include <linux/reset.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
|
#include <linux/mfd/syscon.h>
|
||||||
|
#include <linux/units.h>
|
||||||
|
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
#include "cqhci.h"
|
#include "cqhci.h"
|
||||||
|
#include "sdhci-cqhci.h"
|
||||||
|
|
||||||
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
|
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
|
||||||
|
|
||||||
|
|
@ -39,6 +44,7 @@
|
||||||
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
||||||
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
||||||
#define DWCMSHC_EMMC_ATCTRL 0x40
|
#define DWCMSHC_EMMC_ATCTRL 0x40
|
||||||
|
#define DWCMSHC_AT_STAT 0x44
|
||||||
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
|
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
|
||||||
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
|
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
|
||||||
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
|
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
|
||||||
|
|
@ -82,6 +88,8 @@
|
||||||
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
|
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
|
||||||
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
|
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
|
||||||
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
|
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
|
||||||
|
#define DECMSHC_EMMC_MISC_CON 0x81C
|
||||||
|
#define MISC_INTCLK_EN BIT(1)
|
||||||
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
|
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
|
||||||
#define DWCMSHC_EMMC_DLL_START BIT(0)
|
#define DWCMSHC_EMMC_DLL_START BIT(0)
|
||||||
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
|
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
|
||||||
|
|
@ -194,6 +202,19 @@
|
||||||
#define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */
|
#define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */
|
||||||
#define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */
|
#define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */
|
||||||
|
|
||||||
|
/* PHY DLL offset setting register */
|
||||||
|
#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
|
||||||
|
/* DLL LBT setting register */
|
||||||
|
#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
|
||||||
|
/* DLL Status register */
|
||||||
|
#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
|
||||||
|
#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */
|
||||||
|
/*
|
||||||
|
* Captures the value of DLL's lock error status information. Value is valid
|
||||||
|
* only when LOCK_STS is set.
|
||||||
|
*/
|
||||||
|
#define DLL_ERROR_STS BIT(1)
|
||||||
|
|
||||||
#define FLAG_IO_FIXED_1V8 BIT(0)
|
#define FLAG_IO_FIXED_1V8 BIT(0)
|
||||||
|
|
||||||
#define BOUNDARY_OK(addr, len) \
|
#define BOUNDARY_OK(addr, len) \
|
||||||
|
|
@ -206,6 +227,31 @@
|
||||||
/* SMC call for BlueField-3 eMMC RST_N */
|
/* SMC call for BlueField-3 eMMC RST_N */
|
||||||
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
|
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
|
||||||
|
|
||||||
|
/* Eswin specific Registers */
|
||||||
|
#define EIC7700_CARD_CLK_STABLE BIT(28)
|
||||||
|
#define EIC7700_INT_BCLK_STABLE BIT(16)
|
||||||
|
#define EIC7700_INT_ACLK_STABLE BIT(8)
|
||||||
|
#define EIC7700_INT_TMCLK_STABLE BIT(0)
|
||||||
|
#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \
|
||||||
|
EIC7700_INT_ACLK_STABLE | \
|
||||||
|
EIC7700_INT_BCLK_STABLE | \
|
||||||
|
EIC7700_INT_TMCLK_STABLE)
|
||||||
|
#define EIC7700_HOST_VAL_STABLE BIT(0)
|
||||||
|
|
||||||
|
/* strength definition */
|
||||||
|
#define PHYCTRL_DR_33OHM 0xee
|
||||||
|
#define PHYCTRL_DR_40OHM 0xcc
|
||||||
|
#define PHYCTRL_DR_50OHM 0x88
|
||||||
|
#define PHYCTRL_DR_66OHM 0x44
|
||||||
|
#define PHYCTRL_DR_100OHM 0x00
|
||||||
|
|
||||||
|
#define MAX_PHASE_CODE 0xff
|
||||||
|
#define TUNING_RANGE_THRESHOLD 40
|
||||||
|
#define PHY_CLK_MAX_DELAY_MASK 0x7f
|
||||||
|
#define PHY_DELAY_CODE_MAX 0x7f
|
||||||
|
#define PHY_DELAY_CODE_EMMC 0x17
|
||||||
|
#define PHY_DELAY_CODE_SD 0x55
|
||||||
|
|
||||||
enum dwcmshc_rk_type {
|
enum dwcmshc_rk_type {
|
||||||
DWCMSHC_RK3568,
|
DWCMSHC_RK3568,
|
||||||
DWCMSHC_RK3588,
|
DWCMSHC_RK3588,
|
||||||
|
|
@ -217,6 +263,11 @@ struct rk35xx_priv {
|
||||||
u8 txclk_tapnum;
|
u8 txclk_tapnum;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct eic7700_priv {
|
||||||
|
struct reset_control *reset;
|
||||||
|
unsigned int drive_impedance;
|
||||||
|
};
|
||||||
|
|
||||||
#define DWCMSHC_MAX_OTHER_CLKS 3
|
#define DWCMSHC_MAX_OTHER_CLKS 3
|
||||||
|
|
||||||
struct dwcmshc_priv {
|
struct dwcmshc_priv {
|
||||||
|
|
@ -234,10 +285,22 @@ struct dwcmshc_priv {
|
||||||
|
|
||||||
struct dwcmshc_pltfm_data {
|
struct dwcmshc_pltfm_data {
|
||||||
const struct sdhci_pltfm_data pdata;
|
const struct sdhci_pltfm_data pdata;
|
||||||
|
const struct cqhci_host_ops *cqhci_host_ops;
|
||||||
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
|
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
|
||||||
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
|
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
u16 ctrl;
|
||||||
|
|
||||||
|
ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||||
|
if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
|
||||||
|
ctrl |= SDHCI_CLOCK_CARD_EN;
|
||||||
|
sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int dwcmshc_get_enable_other_clks(struct device *dev,
|
static int dwcmshc_get_enable_other_clks(struct device *dev,
|
||||||
struct dwcmshc_priv *priv,
|
struct dwcmshc_priv *priv,
|
||||||
int num_clks,
|
int num_clks,
|
||||||
|
|
@ -574,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
|
||||||
sdhci_dumpregs(mmc_priv(mmc));
|
sdhci_dumpregs(mmc_priv(mmc));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
/* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */
|
||||||
|
reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
|
||||||
|
reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100;
|
||||||
|
sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
|
||||||
|
|
||||||
|
reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
|
||||||
|
reg |= CQHCI_ENABLE;
|
||||||
|
sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
u32 reg;
|
||||||
|
|
||||||
|
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||||
|
while (reg & SDHCI_DATA_AVAILABLE) {
|
||||||
|
sdhci_readl(host, SDHCI_BUFFER);
|
||||||
|
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
|
||||||
|
|
||||||
|
sdhci_cqe_enable(mmc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
unsigned long flags;
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* During CQE command transfers, command complete bit gets latched.
|
||||||
|
* So s/w should clear command complete interrupt status when CQE is
|
||||||
|
* either halted or disabled. Otherwise unexpected SDCHI legacy
|
||||||
|
* interrupt gets triggered when CQE is halted/disabled.
|
||||||
|
*/
|
||||||
|
spin_lock_irqsave(&host->lock, flags);
|
||||||
|
ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
|
||||||
|
ctrl |= SDHCI_INT_RESPONSE;
|
||||||
|
sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
|
||||||
|
sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
|
||||||
|
spin_unlock_irqrestore(&host->lock, flags);
|
||||||
|
|
||||||
|
sdhci_cqe_disable(mmc, recovery);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
|
||||||
|
{
|
||||||
|
struct sdhci_host *host = mmc_priv(mmc);
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 ctrl;
|
||||||
|
|
||||||
|
ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
|
||||||
|
ctrl &= ~CQHCI_ENABLE;
|
||||||
|
sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
|
||||||
|
}
|
||||||
|
|
||||||
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
|
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
{
|
{
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
|
@ -601,10 +731,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
|
||||||
|
|
||||||
sdhci_set_clock(host, clock);
|
sdhci_set_clock(host, clock);
|
||||||
|
|
||||||
/* Disable cmd conflict check */
|
/* Disable cmd conflict check and internal clock gate */
|
||||||
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
|
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
|
||||||
extra = sdhci_readl(host, reg);
|
extra = sdhci_readl(host, reg);
|
||||||
extra &= ~BIT(0);
|
extra &= ~BIT(0);
|
||||||
|
extra |= BIT(4);
|
||||||
sdhci_writel(host, extra, reg);
|
sdhci_writel(host, extra, reg);
|
||||||
|
|
||||||
if (clock <= 52000000) {
|
if (clock <= 52000000) {
|
||||||
|
|
@ -692,6 +823,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
struct rk35xx_priv *priv = dwc_priv->priv;
|
struct rk35xx_priv *priv = dwc_priv->priv;
|
||||||
|
u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
|
||||||
|
cqhci_deactivate(host->mmc);
|
||||||
|
|
||||||
if (mask & SDHCI_RESET_ALL && priv->reset) {
|
if (mask & SDHCI_RESET_ALL && priv->reset) {
|
||||||
reset_control_assert(priv->reset);
|
reset_control_assert(priv->reset);
|
||||||
|
|
@ -700,6 +835,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
sdhci_reset(host, mask);
|
sdhci_reset(host, mask);
|
||||||
|
|
||||||
|
/* Enable INTERNAL CLOCK */
|
||||||
|
sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
|
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
|
||||||
|
|
@ -1100,6 +1238,411 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
|
||||||
ARRAY_SIZE(clk_ids), clk_ids);
|
ARRAY_SIZE(clk_ids), clk_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
u16 clk;
|
||||||
|
|
||||||
|
host->mmc->actual_clock = clock;
|
||||||
|
|
||||||
|
if (clock == 0) {
|
||||||
|
sdhci_set_clock(host, clock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clk_set_rate(pltfm_host->clk, clock);
|
||||||
|
|
||||||
|
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
||||||
|
clk |= SDHCI_CLOCK_INT_EN;
|
||||||
|
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
|
||||||
|
|
||||||
|
dwcmshc_enable_card_clk(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
|
||||||
|
{
|
||||||
|
delay &= PHY_CLK_MAX_DELAY_MASK;
|
||||||
|
|
||||||
|
/* phy clk delay line config */
|
||||||
|
sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
|
||||||
|
sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
|
||||||
|
sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_config_phy(struct sdhci_host *host)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
|
||||||
|
struct eic7700_priv *priv = dwc_priv->priv;
|
||||||
|
unsigned int val, drv;
|
||||||
|
|
||||||
|
drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
|
||||||
|
drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
|
||||||
|
val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||||
|
val |= DWCMSHC_CARD_IS_EMMC;
|
||||||
|
sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* reset phy, config phy's pad */
|
||||||
|
sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
|
||||||
|
|
||||||
|
/* configure phy pads */
|
||||||
|
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
|
||||||
|
val |= PHY_PAD_RXSEL_1V8;
|
||||||
|
sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
|
||||||
|
sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
|
||||||
|
sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
|
||||||
|
|
||||||
|
/* Clock PAD Setting */
|
||||||
|
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
|
||||||
|
|
||||||
|
/* PHY strobe PAD setting (EMMC only) */
|
||||||
|
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
|
||||||
|
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
|
||||||
|
val |= PHY_PAD_RXSEL_1V8;
|
||||||
|
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
|
||||||
|
}
|
||||||
|
usleep_range(2000, 3000);
|
||||||
|
sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
|
||||||
|
sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
|
||||||
|
{
|
||||||
|
sdhci_reset(host, mask);
|
||||||
|
|
||||||
|
/* after reset all, the phy's config will be clear */
|
||||||
|
if (mask == SDHCI_RESET_ALL)
|
||||||
|
sdhci_eic7700_config_phy(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
|
||||||
|
if (IS_ERR(priv->reset)) {
|
||||||
|
ret = PTR_ERR(priv->reset);
|
||||||
|
dev_err(dev, "failed to get reset control %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = reset_control_assert(priv->reset);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to assert reset signals: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
usleep_range(2000, 2100);
|
||||||
|
ret = reset_control_deassert(priv->reset);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm)
|
||||||
|
{
|
||||||
|
switch (dr_ohm) {
|
||||||
|
case 100:
|
||||||
|
return PHYCTRL_DR_100OHM;
|
||||||
|
case 66:
|
||||||
|
return PHYCTRL_DR_66OHM;
|
||||||
|
case 50:
|
||||||
|
return PHYCTRL_DR_50OHM;
|
||||||
|
case 40:
|
||||||
|
return PHYCTRL_DR_40OHM;
|
||||||
|
case 33:
|
||||||
|
return PHYCTRL_DR_33OHM;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
|
||||||
|
return PHYCTRL_DR_50OHM;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
int delay_min = -1;
|
||||||
|
int delay_max = -1;
|
||||||
|
int cmd_error = 0;
|
||||||
|
int delay = 0;
|
||||||
|
int i = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
|
||||||
|
sdhci_eic7700_config_phy_delay(host, i);
|
||||||
|
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
|
||||||
|
if (ret) {
|
||||||
|
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||||
|
usleep_range(200, 210);
|
||||||
|
if (delay_min != -1 && delay_max != -1)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (delay_min == -1) {
|
||||||
|
delay_min = i;
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
delay_max = i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (delay_min == -1 && delay_max == -1) {
|
||||||
|
pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc));
|
||||||
|
sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
delay = (delay_min + delay_max) / 2;
|
||||||
|
sdhci_eic7700_config_phy_delay(host, delay);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
|
||||||
|
int phase_code = -1;
|
||||||
|
int code_range = -1;
|
||||||
|
bool is_sd = false;
|
||||||
|
int code_min = -1;
|
||||||
|
int code_max = -1;
|
||||||
|
int cmd_error = 0;
|
||||||
|
int ret = 0;
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & sd_caps) == sd_caps)
|
||||||
|
is_sd = true;
|
||||||
|
|
||||||
|
for (i = 0; i <= MAX_PHASE_CODE; i++) {
|
||||||
|
/* Centered Phase code */
|
||||||
|
sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
|
||||||
|
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
|
||||||
|
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
/* SD specific range tracking */
|
||||||
|
if (is_sd && code_min != -1 && code_max != -1) {
|
||||||
|
if (code_max - code_min > code_range) {
|
||||||
|
code_range = code_max - code_min;
|
||||||
|
phase_code = (code_min + code_max) / 2;
|
||||||
|
if (code_range > TUNING_RANGE_THRESHOLD)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
code_min = -1;
|
||||||
|
code_max = -1;
|
||||||
|
}
|
||||||
|
/* EMMC breaks after first valid range */
|
||||||
|
if (!is_sd && code_min != -1 && code_max != -1)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* Track valid phase code range */
|
||||||
|
if (code_min == -1) {
|
||||||
|
code_min = i;
|
||||||
|
if (!is_sd)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
code_max = i;
|
||||||
|
if (is_sd && i == MAX_PHASE_CODE) {
|
||||||
|
if (code_max - code_min > code_range) {
|
||||||
|
code_range = code_max - code_min;
|
||||||
|
phase_code = (code_min + code_max) / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle tuning failure case */
|
||||||
|
if ((is_sd && phase_code == -1) ||
|
||||||
|
(!is_sd && code_min == -1 && code_max == -1)) {
|
||||||
|
pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
|
||||||
|
sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
if (!is_sd)
|
||||||
|
phase_code = (code_min + code_max) / 2;
|
||||||
|
|
||||||
|
sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
|
||||||
|
|
||||||
|
/* SD specific final verification */
|
||||||
|
if (is_sd) {
|
||||||
|
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
|
||||||
|
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: Final phase code 0x%x verification failed!\n",
|
||||||
|
mmc_hostname(host->mmc), phase_code);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
|
||||||
|
int ret = 0;
|
||||||
|
u16 ctrl;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
|
||||||
|
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
|
||||||
|
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
|
||||||
|
|
||||||
|
val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||||
|
val |= AT_CTRL_SW_TUNE_EN;
|
||||||
|
sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||||
|
|
||||||
|
sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
|
||||||
|
sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
|
||||||
|
ret = sdhci_eic7700_delay_tuning(host, opcode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sdhci_eic7700_phase_code_tuning(host, opcode);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
|
||||||
|
{
|
||||||
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||||
|
u8 status;
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dwcmshc_set_uhs_signaling(host, timing);
|
||||||
|
|
||||||
|
/* here need make dll locked when in hs400 at 200MHz */
|
||||||
|
if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
|
||||||
|
val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||||
|
val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
|
||||||
|
/* 2-cycle latency */
|
||||||
|
val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
|
||||||
|
sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
|
||||||
|
|
||||||
|
sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
|
||||||
|
0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
|
||||||
|
/* DLL jump step input */
|
||||||
|
sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
|
||||||
|
sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
|
||||||
|
PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
|
||||||
|
/* Sets the value of DLL's offset input */
|
||||||
|
sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
|
||||||
|
/*
|
||||||
|
* Sets the value of DLL's olbt loadval input. Controls the Ibt
|
||||||
|
* timer's timeout value at which DLL runs a revalidation cycle.
|
||||||
|
*/
|
||||||
|
sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
|
||||||
|
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
|
||||||
|
usleep_range(100, 110);
|
||||||
|
|
||||||
|
ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
|
||||||
|
false, host, PHY_DLL_STATUS_R);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: DLL lock timeout! status: 0x%x\n",
|
||||||
|
mmc_hostname(host->mmc), status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = sdhci_readb(host, PHY_DLL_STATUS_R);
|
||||||
|
if (status & DLL_ERROR_STS) {
|
||||||
|
pr_err("%s: DLL lock failed!err_status:0x%x\n",
|
||||||
|
mmc_hostname(host->mmc), status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
|
||||||
|
{
|
||||||
|
u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & sd_caps) == sd_caps)
|
||||||
|
sdhci_set_uhs_signaling(host, timing);
|
||||||
|
else
|
||||||
|
sdhci_eic7700_set_uhs_signaling(host, timing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
|
||||||
|
{
|
||||||
|
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
|
||||||
|
unsigned int val, hsp_int_status, hsp_pwr_ctrl;
|
||||||
|
struct of_phandle_args args;
|
||||||
|
struct eic7700_priv *priv;
|
||||||
|
struct regmap *hsp_regmap;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dwc_priv->priv = priv;
|
||||||
|
|
||||||
|
ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "failed to reset\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
hsp_regmap = syscon_node_to_regmap(args.np);
|
||||||
|
if (IS_ERR(hsp_regmap)) {
|
||||||
|
dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
|
||||||
|
of_node_put(args.np);
|
||||||
|
return PTR_ERR(hsp_regmap);
|
||||||
|
}
|
||||||
|
hsp_int_status = args.args[0];
|
||||||
|
hsp_pwr_ctrl = args.args[1];
|
||||||
|
of_node_put(args.np);
|
||||||
|
/*
|
||||||
|
* Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
|
||||||
|
* This signals to the eMMC controller that platform clocks (card, ACLK,
|
||||||
|
* BCLK, TMCLK) are enabled and stable.
|
||||||
|
*/
|
||||||
|
regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
|
||||||
|
/*
|
||||||
|
* Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
|
||||||
|
* This signals that VDD is stable and permits transition to high-speed
|
||||||
|
* modes (e.g., UHS-I).
|
||||||
|
*/
|
||||||
|
regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
|
||||||
|
|
||||||
|
if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
|
||||||
|
dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
|
||||||
|
else
|
||||||
|
dwc_priv->delay_line = PHY_DELAY_CODE_SD;
|
||||||
|
|
||||||
|
if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
|
||||||
|
priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
static const struct sdhci_ops sdhci_dwcmshc_ops = {
|
||||||
.set_clock = sdhci_set_clock,
|
.set_clock = sdhci_set_clock,
|
||||||
.set_bus_width = sdhci_set_bus_width,
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
|
@ -1174,6 +1717,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
|
||||||
.platform_execute_tuning = th1520_execute_tuning,
|
.platform_execute_tuning = th1520_execute_tuning,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
|
||||||
|
.set_clock = sdhci_eic7700_set_clock,
|
||||||
|
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
|
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
|
||||||
|
.set_bus_width = sdhci_set_bus_width,
|
||||||
|
.reset = sdhci_eic7700_reset,
|
||||||
|
.set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
|
||||||
|
.set_power = sdhci_set_power_and_bus_voltage,
|
||||||
|
.irq = dwcmshc_cqe_irq_handler,
|
||||||
|
.platform_execute_tuning = sdhci_eic7700_executing_tuning,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
|
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
|
||||||
.pdata = {
|
.pdata = {
|
||||||
.ops = &sdhci_dwcmshc_ops,
|
.ops = &sdhci_dwcmshc_ops,
|
||||||
|
|
@ -1193,6 +1748,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static const struct cqhci_host_ops rk35xx_cqhci_ops = {
|
||||||
|
.pre_enable = rk35xx_sdhci_cqe_pre_enable,
|
||||||
|
.enable = rk35xx_sdhci_cqe_enable,
|
||||||
|
.disable = rk35xx_sdhci_cqe_disable,
|
||||||
|
.post_disable = rk35xx_sdhci_cqe_post_disable,
|
||||||
|
.dumpregs = dwcmshc_cqhci_dumpregs,
|
||||||
|
.set_tran_desc = dwcmshc_set_tran_desc,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
||||||
.pdata = {
|
.pdata = {
|
||||||
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
.ops = &sdhci_dwcmshc_rk35xx_ops,
|
||||||
|
|
@ -1201,6 +1765,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||||
},
|
},
|
||||||
|
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||||
.init = dwcmshc_rk35xx_init,
|
.init = dwcmshc_rk35xx_init,
|
||||||
.postinit = dwcmshc_rk35xx_postinit,
|
.postinit = dwcmshc_rk35xx_postinit,
|
||||||
};
|
};
|
||||||
|
|
@ -1213,6 +1778,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
|
||||||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||||
},
|
},
|
||||||
|
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||||
.init = dwcmshc_rk35xx_init,
|
.init = dwcmshc_rk35xx_init,
|
||||||
.postinit = dwcmshc_rk3576_postinit,
|
.postinit = dwcmshc_rk3576_postinit,
|
||||||
};
|
};
|
||||||
|
|
@ -1243,6 +1809,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
|
||||||
.init = sg2042_init,
|
.init = sg2042_init,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
|
||||||
|
.pdata = {
|
||||||
|
.ops = &sdhci_dwcmshc_eic7700_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,
|
||||||
|
},
|
||||||
|
.init = eic7700_init,
|
||||||
|
};
|
||||||
|
|
||||||
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
||||||
.enable = dwcmshc_sdhci_cqe_enable,
|
.enable = dwcmshc_sdhci_cqe_enable,
|
||||||
.disable = sdhci_cqe_disable,
|
.disable = sdhci_cqe_disable,
|
||||||
|
|
@ -1250,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
||||||
.set_tran_desc = dwcmshc_set_tran_desc,
|
.set_tran_desc = dwcmshc_set_tran_desc,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
|
static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
|
||||||
|
const struct dwcmshc_pltfm_data *pltfm_data)
|
||||||
{
|
{
|
||||||
struct cqhci_host *cq_host;
|
struct cqhci_host *cq_host;
|
||||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||||
|
|
@ -1280,6 +1858,9 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
|
||||||
}
|
}
|
||||||
|
|
||||||
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
|
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
|
||||||
|
if (pltfm_data->cqhci_host_ops)
|
||||||
|
cq_host->ops = pltfm_data->cqhci_host_ops;
|
||||||
|
else
|
||||||
cq_host->ops = &dwcmshc_cqhci_ops;
|
cq_host->ops = &dwcmshc_cqhci_ops;
|
||||||
|
|
||||||
/* Enable using of 128-bit task descriptors */
|
/* Enable using of 128-bit task descriptors */
|
||||||
|
|
@ -1343,6 +1924,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
||||||
.compatible = "sophgo,sg2042-dwcmshc",
|
.compatible = "sophgo,sg2042-dwcmshc",
|
||||||
.data = &sdhci_dwcmshc_sg2042_pdata,
|
.data = &sdhci_dwcmshc_sg2042_pdata,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "eswin,eic7700-dwcmshc",
|
||||||
|
.data = &sdhci_dwcmshc_eic7700_pdata,
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
|
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
|
||||||
|
|
@ -1448,7 +2033,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
||||||
priv->vendor_specific_area2 =
|
priv->vendor_specific_area2 =
|
||||||
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
|
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
|
||||||
|
|
||||||
dwcmshc_cqhci_init(host, pdev);
|
dwcmshc_cqhci_init(host, pdev, pltfm_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pltfm_data->postinit)
|
if (pltfm_data->postinit)
|
||||||
|
|
@ -1575,17 +2160,6 @@ static int dwcmshc_resume(struct device *dev)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
|
|
||||||
{
|
|
||||||
u16 ctrl;
|
|
||||||
|
|
||||||
ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
|
|
||||||
if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
|
|
||||||
ctrl |= SDHCI_CLOCK_CARD_EN;
|
|
||||||
sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dwcmshc_runtime_suspend(struct device *dev)
|
static int dwcmshc_runtime_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||||
|
|
|
||||||
|
|
@ -209,10 +209,8 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||||
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
|
||||||
irqreturn_t tmio_mmc_irq(int irq, void *devid);
|
irqreturn_t tmio_mmc_irq(int irq, void *devid);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
int tmio_mmc_host_runtime_suspend(struct device *dev);
|
int tmio_mmc_host_runtime_suspend(struct device *dev);
|
||||||
int tmio_mmc_host_runtime_resume(struct device *dev);
|
int tmio_mmc_host_runtime_resume(struct device *dev);
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
|
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,6 @@ struct sd_switch_caps {
|
||||||
#define SD_SET_CURRENT_LIMIT_400 1
|
#define SD_SET_CURRENT_LIMIT_400 1
|
||||||
#define SD_SET_CURRENT_LIMIT_600 2
|
#define SD_SET_CURRENT_LIMIT_600 2
|
||||||
#define SD_SET_CURRENT_LIMIT_800 3
|
#define SD_SET_CURRENT_LIMIT_800 3
|
||||||
#define SD_SET_CURRENT_NO_CHANGE (-1)
|
|
||||||
|
|
||||||
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
|
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
|
||||||
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue