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:
|
||||
- enum:
|
||||
- brcm,bcm2712-sdhci
|
||||
- brcm,bcm72116-sdhci
|
||||
- brcm,bcm74165b0-sdhci
|
||||
- brcm,bcm7445-sdhci
|
||||
- brcm,bcm7425-sdhci
|
||||
- brcm,bcm74371-sdhci
|
||||
- const: brcm,sdhci-brcmstb
|
||||
|
||||
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,rk3368-dw-mshc
|
||||
- rockchip,rk3399-dw-mshc
|
||||
- rockchip,rk3506-dw-mshc
|
||||
- rockchip,rk3528-dw-mshc
|
||||
- rockchip,rk3562-dw-mshc
|
||||
- rockchip,rk3568-dw-mshc
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ properties:
|
|||
- const: clk_ahb
|
||||
- const: clk_xin
|
||||
|
||||
dma-coherent:
|
||||
type: boolean
|
||||
dma-coherent: true
|
||||
|
||||
# PHY output tap delays:
|
||||
# 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,ipq6018-sdhci
|
||||
- qcom,ipq9574-sdhci
|
||||
- qcom,kaanapali-sdhci
|
||||
- qcom,milos-sdhci
|
||||
- qcom,qcm2290-sdhci
|
||||
- qcom,qcs404-sdhci
|
||||
|
|
@ -70,6 +71,7 @@ properties:
|
|||
- qcom,sm8450-sdhci
|
||||
- qcom,sm8550-sdhci
|
||||
- qcom,sm8650-sdhci
|
||||
- qcom,sm8750-sdhci
|
||||
- qcom,x1e80100-sdhci
|
||||
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ properties:
|
|||
- sophgo,sg2002-dwcmshc
|
||||
- sophgo,sg2042-dwcmshc
|
||||
- thead,th1520-dwcmshc
|
||||
- eswin,eic7700-dwcmshc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -52,17 +53,30 @@ properties:
|
|||
maxItems: 5
|
||||
|
||||
reset-names:
|
||||
items:
|
||||
- const: core
|
||||
- const: bus
|
||||
- const: axi
|
||||
- const: block
|
||||
- const: timer
|
||||
maxItems: 5
|
||||
|
||||
rockchip,txclk-tapnum:
|
||||
description: Specify the number of delay for tx sampling.
|
||||
$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:
|
||||
- compatible
|
||||
- reg
|
||||
|
|
@ -110,6 +124,37 @@ allOf:
|
|||
- const: block
|
||||
- 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:
|
||||
properties:
|
||||
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
|
||||
M: Jaehoon Chung <jh80.chung@samsung.com>
|
||||
M: Shawn Lin <shawn.lin@rock-chips.com>
|
||||
L: linux-mmc@vger.kernel.org
|
||||
S: Maintained
|
||||
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 &&
|
||||
(md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
|
||||
md->queue.card->ext_csd.boot_ro_lockable) {
|
||||
mode = S_IRUGO;
|
||||
mode = 0444;
|
||||
if (!(md->queue.card->ext_csd.boot_ro_lock &
|
||||
EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
|
||||
mode |= S_IWUSR;
|
||||
mode |= 0200;
|
||||
}
|
||||
|
||||
mmc_blk_put(md);
|
||||
|
|
@ -957,7 +957,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
|
|||
u32 result;
|
||||
__be32 *blocks;
|
||||
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
|
||||
unsigned int noio_flag;
|
||||
|
||||
struct mmc_request mrq = {};
|
||||
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.data = &data;
|
||||
|
||||
noio_flag = memalloc_noio_save();
|
||||
blocks = kmalloc(resp_sz, GFP_KERNEL);
|
||||
memalloc_noio_restore(noio_flag);
|
||||
blocks = kmalloc(resp_sz, GFP_NOIO);
|
||||
if (!blocks)
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -3275,7 +3272,8 @@ static int mmc_blk_probe(struct mmc_card *card)
|
|||
mmc_fixup_device(card, mmc_blk_fixups);
|
||||
|
||||
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) {
|
||||
pr_err("Failed to create mmc completion workqueue");
|
||||
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); \
|
||||
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,
|
||||
const struct device_type *type);
|
||||
|
|
|
|||
|
|
@ -315,7 +315,10 @@ static int mmc_caps_set(void *data, u64 val)
|
|||
MMC_CAP_SD_HIGHSPEED |
|
||||
MMC_CAP_MMC_HIGHSPEED |
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
|
@ -327,7 +330,10 @@ static int mmc_caps_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 diff = *caps ^ val;
|
||||
|
||||
|
|
|
|||
|
|
@ -831,7 +831,7 @@ static ssize_t mmc_fwrev_show(struct device *dev,
|
|||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
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[] = {
|
||||
&dev_attr_cid.attr,
|
||||
|
|
|
|||
|
|
@ -3208,12 +3208,12 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
|
|||
|
||||
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);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO,
|
||||
ret = __mmc_test_register_dbgfs_file(card, "testlist", 0444,
|
||||
&mtf_testlist_fops);
|
||||
if (ret)
|
||||
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)
|
||||
{
|
||||
int current_limit = SD_SET_CURRENT_NO_CHANGE;
|
||||
int current_limit = SD_SET_CURRENT_LIMIT_200;
|
||||
int err;
|
||||
u32 max_current;
|
||||
|
||||
|
|
@ -598,11 +598,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
|
|||
else if (max_current >= 400 &&
|
||||
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_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,
|
||||
current_limit, status);
|
||||
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);
|
||||
}
|
||||
|
||||
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(device, "0x%04x\n", card->cis.device);
|
||||
|
|
|
|||
|
|
@ -504,6 +504,7 @@ config MMC_MESON_MX_SDIO
|
|||
depends on ARCH_MESON || COMPILE_TEST
|
||||
depends on COMMON_CLK
|
||||
depends on OF_ADDRESS
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
This selects support for the SD/MMC Host Controller on
|
||||
Amlogic Meson6, Meson8 and Meson8b SoCs.
|
||||
|
|
|
|||
|
|
@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
|||
if (!root)
|
||||
return;
|
||||
|
||||
debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops);
|
||||
debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
|
||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
||||
debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops);
|
||||
debugfs_create_file("req", 0400, root, slot, &atmci_req_fops);
|
||||
debugfs_create_u32("state", 0400, root, &host->state);
|
||||
debugfs_create_xul("pending_events", 0400, root,
|
||||
&host->pending_events);
|
||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
||||
debugfs_create_xul("completed_events", 0400, root,
|
||||
&host->completed_events);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@
|
|||
/* send status config 1 */
|
||||
#define CQHCI_SSC1 0x40
|
||||
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
|
||||
#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0)
|
||||
|
||||
/* send status config 2 */
|
||||
#define CQHCI_SSC2 0x44
|
||||
|
|
|
|||
|
|
@ -145,17 +145,17 @@
|
|||
#define MAX_NR_SG 16
|
||||
|
||||
static unsigned rw_threshold = 32;
|
||||
module_param(rw_threshold, uint, S_IRUGO);
|
||||
module_param(rw_threshold, uint, 0444);
|
||||
MODULE_PARM_DESC(rw_threshold,
|
||||
"Read/Write threshold. Default = 32");
|
||||
|
||||
static unsigned poll_threshold = 128;
|
||||
module_param(poll_threshold, uint, S_IRUGO);
|
||||
module_param(poll_threshold, uint, 0444);
|
||||
MODULE_PARM_DESC(poll_threshold,
|
||||
"Polling transaction size threshold. Default = 128");
|
||||
|
||||
static unsigned poll_loopcount = 32;
|
||||
module_param(poll_loopcount, uint, S_IRUGO);
|
||||
module_param(poll_loopcount, uint, 0444);
|
||||
MODULE_PARM_DESC(poll_loopcount,
|
||||
"Maximum polling loop count. Default = 32");
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
#define RK3288_CLKGEN_DIV 2
|
||||
#define SDMMC_TIMING_CON0 0x130
|
||||
#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_DEGREE_MASK 0x3
|
||||
#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)
|
||||
{
|
||||
struct dw_mci_rockchip_priv_data *priv = host->priv;
|
||||
int ret, i;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
if (priv->internal_phase)
|
||||
mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -175,12 +175,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
|
|||
if (!root)
|
||||
return;
|
||||
|
||||
debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops);
|
||||
debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
|
||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
||||
debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
|
||||
debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
|
||||
debugfs_create_u32("state", 0400, root, &host->state);
|
||||
debugfs_create_xul("pending_events", 0400, root,
|
||||
&host->pending_events);
|
||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
||||
debugfs_create_xul("completed_events", 0400, root,
|
||||
&host->completed_events);
|
||||
#ifdef CONFIG_FAULT_INJECTION
|
||||
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;
|
||||
dev_info(host->dev,
|
||||
"IDMAC supports 64-bit address mode.\n");
|
||||
if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
|
||||
dma_set_coherent_mask(host->dev,
|
||||
DMA_BIT_MASK(64));
|
||||
if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64)))
|
||||
dev_info(host->dev, "Fail to set 64-bit DMA mask");
|
||||
} else {
|
||||
/* host supports IDMAC in 32-bit address mode */
|
||||
host->dma_64bit_address = 0;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
|
|
@ -98,17 +99,16 @@
|
|||
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
|
||||
#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 device *controller_dev;
|
||||
|
||||
struct clk *parent_clk;
|
||||
struct clk *core_clk;
|
||||
struct clk_divider cfg_div;
|
||||
struct clk *cfg_div_clk;
|
||||
struct clk_fixed_factor fixed_factor;
|
||||
struct clk *fixed_factor_clk;
|
||||
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
int irq;
|
||||
spinlock_t irq_lock;
|
||||
|
||||
|
|
@ -122,22 +122,10 @@ struct meson_mx_mmc_host {
|
|||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
|
|||
struct meson_mx_mmc_host *host = mmc_priv(mmc);
|
||||
unsigned int pack_size;
|
||||
unsigned long irqflags, timeout;
|
||||
u32 mult, send = 0, ext = 0;
|
||||
u32 send = 0, ext = 0;
|
||||
|
||||
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);
|
||||
|
||||
mult = readl(host->base + MESON_MX_SDIO_MULT);
|
||||
mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK;
|
||||
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id);
|
||||
mult |= BIT(31);
|
||||
writel(mult, host->base + MESON_MX_SDIO_MULT);
|
||||
regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
|
||||
MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31),
|
||||
FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK,
|
||||
host->slot_id) | BIT(31));
|
||||
|
||||
/* enable the CMD done interrupt */
|
||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC,
|
||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
|
||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
||||
regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC,
|
||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
||||
|
||||
/* clear pending interrupts */
|
||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS,
|
||||
MESON_MX_SDIO_IRQS_CMD_INT,
|
||||
MESON_MX_SDIO_IRQS_CMD_INT);
|
||||
regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS,
|
||||
MESON_MX_SDIO_IRQS_CMD_INT);
|
||||
|
||||
writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU);
|
||||
writel(ext, host->base + MESON_MX_SDIO_EXT);
|
||||
writel(send, host->base + MESON_MX_SDIO_SEND);
|
||||
regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg);
|
||||
regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext);
|
||||
regmap_write(host->regmap, MESON_MX_SDIO_SEND, send);
|
||||
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
|
||||
|
|
@ -263,14 +248,13 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
|
|||
|
||||
switch (ios->bus_width) {
|
||||
case MMC_BUS_WIDTH_1:
|
||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
|
||||
MESON_MX_SDIO_CONF_BUS_WIDTH, 0);
|
||||
regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF,
|
||||
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
||||
break;
|
||||
|
||||
case MMC_BUS_WIDTH_4:
|
||||
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF,
|
||||
MESON_MX_SDIO_CONF_BUS_WIDTH,
|
||||
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
||||
regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF,
|
||||
MESON_MX_SDIO_CONF_BUS_WIDTH);
|
||||
break;
|
||||
|
||||
case MMC_BUS_WIDTH_8:
|
||||
|
|
@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
|
|||
host->mrq = mrq;
|
||||
|
||||
if (mrq->data)
|
||||
writel(sg_dma_address(mrq->data->sg),
|
||||
host->base + MESON_MX_SDIO_ADDR);
|
||||
regmap_write(host->regmap, MESON_MX_SDIO_ADDR,
|
||||
sg_dma_address(mrq->data->sg));
|
||||
|
||||
if (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 meson_mx_mmc_host *host = mmc_priv(mmc);
|
||||
u32 mult;
|
||||
int i, resp[4];
|
||||
unsigned int i, resp[4];
|
||||
|
||||
mult = readl(host->base + MESON_MX_SDIO_MULT);
|
||||
mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX;
|
||||
mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK;
|
||||
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0);
|
||||
writel(mult, host->base + MESON_MX_SDIO_MULT);
|
||||
regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
|
||||
MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
|
||||
MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
|
||||
MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
|
||||
FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
|
||||
0));
|
||||
|
||||
if (cmd->flags & MMC_RSP_136) {
|
||||
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[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
|
||||
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
|
||||
cmd->resp[3] = (resp[3] << 8);
|
||||
} 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);
|
||||
|
||||
irqs = readl(host->base + MESON_MX_SDIO_IRQS);
|
||||
send = readl(host->base + MESON_MX_SDIO_SEND);
|
||||
regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
|
||||
regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send);
|
||||
|
||||
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
|
|
@ -450,8 +436,7 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
|
|||
|
||||
if (cmd->data) {
|
||||
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
|
||||
cmd->data->sg_len,
|
||||
mmc_get_dma_dir(cmd->data));
|
||||
cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
|
||||
|
||||
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,
|
||||
cmd_timeout);
|
||||
unsigned long irqflags;
|
||||
u32 irqc;
|
||||
u32 irqs, argu;
|
||||
|
||||
spin_lock_irqsave(&host->irq_lock, irqflags);
|
||||
|
||||
/* disable the CMD interrupt */
|
||||
irqc = readl(host->base + MESON_MX_SDIO_IRQC);
|
||||
irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN;
|
||||
writel(irqc, host->base + MESON_MX_SDIO_IRQC);
|
||||
regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC,
|
||||
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
|
||||
|
||||
spin_unlock_irqrestore(&host->irq_lock, irqflags);
|
||||
|
||||
|
|
@ -488,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
|
|||
if (!host->cmd)
|
||||
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),
|
||||
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
|
||||
host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS),
|
||||
readl(host->base + MESON_MX_SDIO_ARGU));
|
||||
host->cmd->opcode, irqs, argu);
|
||||
|
||||
host->cmd->error = -ETIMEDOUT;
|
||||
|
||||
|
|
@ -507,23 +493,30 @@ static struct mmc_host_ops meson_mx_mmc_ops = {
|
|||
|
||||
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
|
||||
{
|
||||
struct device_node *slot_node;
|
||||
struct platform_device *pdev;
|
||||
struct platform_device *pdev = NULL;
|
||||
|
||||
/*
|
||||
* TODO: the MMC core framework currently does not support
|
||||
* controllers with multiple slots properly. So we only register
|
||||
* the first slot for now
|
||||
*/
|
||||
slot_node = of_get_compatible_child(parent->of_node, "mmc-slot");
|
||||
if (!slot_node) {
|
||||
dev_warn(parent, "no 'mmc-slot' sub-node found\n");
|
||||
return ERR_PTR(-ENOENT);
|
||||
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
|
||||
* controllers with multiple slots properly. So we only
|
||||
* register the first slot for now.
|
||||
*/
|
||||
if (pdev) {
|
||||
dev_warn(parent,
|
||||
"more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n");
|
||||
break;
|
||||
}
|
||||
|
||||
pdev = of_platform_device_create(slot_node, NULL, parent);
|
||||
if (!pdev)
|
||||
dev_err(parent,
|
||||
"Failed to create platform device for mmc-slot node '%pOF'\n",
|
||||
slot_node);
|
||||
}
|
||||
|
||||
pdev = of_platform_device_create(slot_node, NULL, parent);
|
||||
of_node_put(slot_node);
|
||||
|
||||
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);
|
||||
int ret;
|
||||
|
||||
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) {
|
||||
dev_err(slot_dev, "missing 'reg' property\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id))
|
||||
return dev_err_probe(slot_dev, -EINVAL,
|
||||
"missing 'reg' property\n");
|
||||
|
||||
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) {
|
||||
dev_err(slot_dev, "invalid 'reg' property value %d\n",
|
||||
host->slot_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS)
|
||||
return dev_err_probe(slot_dev, -EINVAL,
|
||||
"invalid 'reg' property value %d\n",
|
||||
host->slot_id);
|
||||
|
||||
/* Get regulators and the supported OCR mask */
|
||||
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 */
|
||||
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
|
||||
mmc->f_max = clk_round_rate(host->cfg_div_clk,
|
||||
clk_get_rate(host->parent_clk));
|
||||
mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX);
|
||||
|
||||
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
|
||||
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;
|
||||
}
|
||||
|
||||
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 *clk_div_parent, *clk_fixed_factor_parent;
|
||||
const char *fixed_div2_name, *cfg_div_name;
|
||||
struct meson_mx_mmc_host_clkc *host_clkc;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk_fixed_factor_parent = __clk_get_name(host->parent_clk);
|
||||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
||||
"%s#fixed_factor",
|
||||
dev_name(host->controller_dev));
|
||||
if (!init.name)
|
||||
return -ENOMEM;
|
||||
/* use a dedicated memory allocation for the clock controller to
|
||||
* prevent use-after-free as meson_mx_mmc_host is free'd before
|
||||
* dev (controller dev, not mmc_host->dev) is free'd.
|
||||
*/
|
||||
host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL);
|
||||
if (!host_clkc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.ops = &clk_fixed_factor_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &clk_fixed_factor_parent;
|
||||
init.num_parents = 1;
|
||||
host->fixed_factor.div = 2;
|
||||
host->fixed_factor.mult = 1;
|
||||
host->fixed_factor.hw.init = &init;
|
||||
fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2",
|
||||
dev_name(dev));
|
||||
if (!fixed_div2_name)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
host->fixed_factor_clk = devm_clk_register(host->controller_dev,
|
||||
&host->fixed_factor.hw);
|
||||
if (WARN_ON(IS_ERR(host->fixed_factor_clk)))
|
||||
return PTR_ERR(host->fixed_factor_clk);
|
||||
host_clkc->fixed_div2.div = 2;
|
||||
host_clkc->fixed_div2.mult = 1;
|
||||
host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name,
|
||||
"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);
|
||||
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL,
|
||||
"%s#div", dev_name(host->controller_dev));
|
||||
if (!init.name)
|
||||
return -ENOMEM;
|
||||
cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev));
|
||||
if (!cfg_div_name)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.ops = &clk_divider_ops;
|
||||
init.flags = CLK_SET_RATE_PARENT;
|
||||
init.parent_names = &clk_div_parent;
|
||||
init.num_parents = 1;
|
||||
host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF;
|
||||
host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
|
||||
host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
|
||||
host->cfg_div.hw.init = &init;
|
||||
host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO;
|
||||
host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF;
|
||||
host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
|
||||
host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
|
||||
host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name,
|
||||
&host_clkc->fixed_div2.hw,
|
||||
&clk_divider_ops,
|
||||
CLK_DIVIDER_ALLOW_ZERO);
|
||||
ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw);
|
||||
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,
|
||||
&host->cfg_div.hw);
|
||||
if (WARN_ON(IS_ERR(host->cfg_div_clk)))
|
||||
return PTR_ERR(host->cfg_div_clk);
|
||||
clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk");
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_ptr_probe(dev, PTR_ERR(clk),
|
||||
"Failed to get the cfg_div clock\n");
|
||||
|
||||
return 0;
|
||||
return clk;
|
||||
}
|
||||
|
||||
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 mmc_host *mmc;
|
||||
struct meson_mx_mmc_host *host;
|
||||
struct clk *core_clk;
|
||||
void __iomem *base;
|
||||
int ret, irq;
|
||||
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);
|
||||
if (!slot_pdev)
|
||||
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) {
|
||||
ret = -ENOMEM;
|
||||
goto error_unregister_slot_pdev;
|
||||
|
|
@ -656,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, host);
|
||||
|
||||
host->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(host->base)) {
|
||||
ret = PTR_ERR(host->base);
|
||||
goto error_free_mmc;
|
||||
host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&meson_mx_sdio_regmap_config);
|
||||
if (IS_ERR(host->regmap)) {
|
||||
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);
|
||||
if (irq < 0) {
|
||||
ret = irq;
|
||||
goto error_free_mmc;
|
||||
goto error_unregister_slot_pdev;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(host->controller_dev, irq,
|
||||
meson_mx_mmc_irq,
|
||||
meson_mx_mmc_irq_thread, IRQF_ONESHOT,
|
||||
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) {
|
||||
dev_err(host->controller_dev, "Failed to enable core clock\n");
|
||||
goto error_free_mmc;
|
||||
dev_err_probe(host->controller_dev, ret,
|
||||
"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);
|
||||
if (ret) {
|
||||
dev_err(host->controller_dev, "Failed to enable MMC clock\n");
|
||||
goto error_disable_core_clk;
|
||||
dev_err_probe(host->controller_dev, ret,
|
||||
"Failed to enable MMC (cfg div) clock\n");
|
||||
goto error_unregister_slot_pdev;
|
||||
}
|
||||
|
||||
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_WRITE_NWR_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);
|
||||
|
||||
ret = meson_mx_mmc_add_host(host);
|
||||
if (ret)
|
||||
goto error_disable_clks;
|
||||
goto error_disable_div_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_clks:
|
||||
error_disable_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:
|
||||
of_platform_device_destroy(&slot_pdev->dev, NULL);
|
||||
return ret;
|
||||
|
|
@ -741,9 +743,6 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
|
|||
of_platform_device_destroy(slot_dev, NULL);
|
||||
|
||||
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[] = {
|
||||
|
|
|
|||
|
|
@ -1214,7 +1214,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
|
|||
host->data = data;
|
||||
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);
|
||||
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
|
||||
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);
|
||||
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))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
|
|||
"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
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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->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) {
|
||||
ret = -ENOMEM;
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
@ -1672,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs);
|
|||
static void omap_hsmmc_debugfs(struct mmc_host *mmc)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#ifndef RENESAS_SDHI_H
|
||||
#define RENESAS_SDHI_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/platform_device.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_quirks *quirks);
|
||||
void renesas_sdhi_remove(struct platform_device *pdev);
|
||||
int renesas_sdhi_suspend(struct device *dev);
|
||||
int renesas_sdhi_resume(struct device *dev);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/platform_data/tmio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regulator/driver.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))
|
||||
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))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
|
|
@ -1317,5 +1318,41 @@ void renesas_sdhi_remove(struct platform_device *pdev)
|
|||
}
|
||||
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_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include <linux/pagemap.h>
|
||||
#include <linux/platform_data/tmio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/scatterlist.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 = {
|
||||
.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 |
|
||||
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
|
||||
|
|
@ -599,18 +599,17 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
||||
tmio_mmc_host_runtime_resume,
|
||||
NULL)
|
||||
SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume)
|
||||
RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
|
||||
tmio_mmc_host_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver renesas_internal_dmac_sdhi_driver = {
|
||||
.driver = {
|
||||
.name = "renesas_sdhi_internal_dmac",
|
||||
.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,
|
||||
},
|
||||
.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 = {
|
||||
.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 |
|
||||
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
|
||||
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,
|
||||
|
|
|
|||
|
|
@ -31,35 +31,116 @@
|
|||
|
||||
#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_SDCD_N_TEST_EN BIT(31)
|
||||
#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_STRAP_OVERRIDE BIT(31)
|
||||
#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)
|
||||
/* 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)
|
||||
|
||||
struct sdhci_brcmstb_priv {
|
||||
void __iomem *cfg_regs;
|
||||
unsigned int flags;
|
||||
struct clk *base_clk;
|
||||
u32 base_freq_hz;
|
||||
enum cfg_core_ver {
|
||||
SDIO_CFG_CORE_V1 = 1,
|
||||
SDIO_CFG_CORE_V2,
|
||||
};
|
||||
|
||||
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 {
|
||||
void (*cfginit)(struct sdhci_host *host);
|
||||
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
|
||||
void (*save_restore_regs)(struct mmc_host *mmc, int save);
|
||||
struct sdhci_ops *ops;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
sdhci_dumpregs(mmc_priv(mmc));
|
||||
|
|
@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
|
|||
.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 = {
|
||||
.set_clock = sdhci_brcmstb_set_clock,
|
||||
.set_bus_width = sdhci_set_bus_width,
|
||||
|
|
@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = {
|
|||
.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,
|
||||
.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 = {
|
||||
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
||||
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
|
||||
.hs400es = sdhci_brcmstb_hs400es,
|
||||
.ops = &sdhci_brcmstb_ops_7216,
|
||||
};
|
||||
|
||||
static struct brcmstb_match_priv match_priv_74165b0 = {
|
||||
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
|
||||
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
|
||||
.hs400es = sdhci_brcmstb_hs400es,
|
||||
.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[] = {
|
||||
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
|
||||
{ .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,bcm72116-sdhci", .data = &match_priv_72116 },
|
||||
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
|
||||
{ .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);
|
||||
priv = sdhci_pltfm_priv(pltfm_host);
|
||||
priv->match_priv = match->data;
|
||||
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
|
||||
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
|
||||
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
|
||||
|
|
@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
|
|||
if (res)
|
||||
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
|
||||
* 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_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct brcmstb_match_priv *match_priv = priv->match_priv;
|
||||
|
||||
int ret;
|
||||
|
||||
if (match_priv->save_restore_regs)
|
||||
match_priv->save_restore_regs(host->mmc, 1);
|
||||
|
||||
clk_disable_unprepare(priv->base_clk);
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
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_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
|
||||
const struct brcmstb_match_priv *match_priv = priv->match_priv;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (match_priv->save_restore_regs)
|
||||
match_priv->save_restore_regs(host->mmc, 0);
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE)
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
* actual clock that will be set for DDR mode. The controller
|
||||
* uses the faster clock(100/400MHz) for some of its parts and
|
||||
* send the actual required clock (50/200MHz) to the card.
|
||||
*/
|
||||
if (ios.timing == MMC_TIMING_UHS_DDR50 ||
|
||||
ios.timing == MMC_TIMING_MMC_DDR52 ||
|
||||
ios.timing == MMC_TIMING_MMC_HS400 ||
|
||||
if (timing == MMC_TIMING_UHS_DDR50 ||
|
||||
timing == MMC_TIMING_MMC_DDR52 ||
|
||||
(timing == MMC_TIMING_MMC_HS400 &&
|
||||
clock == MMC_HS200_MAX_DTR) ||
|
||||
host->flags & SDHCI_HS400_TUNING)
|
||||
return 2;
|
||||
return 1;
|
||||
}
|
||||
|
||||
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_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;
|
||||
unsigned long achieved_rate;
|
||||
unsigned int desired_rate;
|
||||
unsigned int mult;
|
||||
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;
|
||||
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
|
||||
if (rc) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -397,7 +399,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
|
|||
msm_host->clk_rate = desired_rate;
|
||||
|
||||
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 */
|
||||
|
|
@ -1239,7 +1241,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
|
|||
*/
|
||||
if (host->flags & SDHCI_HS400_TUNING) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -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_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct mmc_ios ios = host->mmc->ios;
|
||||
|
||||
if (!clock) {
|
||||
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);
|
||||
|
||||
msm_set_clock_rate_for_bus_mode(host, clock);
|
||||
msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
|
||||
out:
|
||||
__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);
|
||||
if (ret) {
|
||||
ret = dev_err_probe(dev, ret, "parsing dt failed.\n");
|
||||
dev_err_probe(dev, ret, "parsing dt failed.\n");
|
||||
goto unreg_clk;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/arm-smccc.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -19,11 +20,15 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/units.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
#include "sdhci-cqhci.h"
|
||||
|
||||
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
|
||||
|
||||
|
|
@ -39,6 +44,7 @@
|
|||
#define DWCMSHC_CARD_IS_EMMC BIT(0)
|
||||
#define DWCMSHC_ENHANCED_STROBE BIT(8)
|
||||
#define DWCMSHC_EMMC_ATCTRL 0x40
|
||||
#define DWCMSHC_AT_STAT 0x44
|
||||
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
|
||||
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
|
||||
#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_STRBIN 0x80c
|
||||
#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_START BIT(0)
|
||||
#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 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 BOUNDARY_OK(addr, len) \
|
||||
|
|
@ -206,6 +227,31 @@
|
|||
/* SMC call for BlueField-3 eMMC RST_N */
|
||||
#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 {
|
||||
DWCMSHC_RK3568,
|
||||
DWCMSHC_RK3588,
|
||||
|
|
@ -217,6 +263,11 @@ struct rk35xx_priv {
|
|||
u8 txclk_tapnum;
|
||||
};
|
||||
|
||||
struct eic7700_priv {
|
||||
struct reset_control *reset;
|
||||
unsigned int drive_impedance;
|
||||
};
|
||||
|
||||
#define DWCMSHC_MAX_OTHER_CLKS 3
|
||||
|
||||
struct dwcmshc_priv {
|
||||
|
|
@ -234,10 +285,22 @@ struct dwcmshc_priv {
|
|||
|
||||
struct dwcmshc_pltfm_data {
|
||||
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);
|
||||
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,
|
||||
struct dwcmshc_priv *priv,
|
||||
int num_clks,
|
||||
|
|
@ -574,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *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)
|
||||
{
|
||||
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);
|
||||
|
||||
/* Disable cmd conflict check */
|
||||
/* Disable cmd conflict check and internal clock gate */
|
||||
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
|
||||
extra = sdhci_readl(host, reg);
|
||||
extra &= ~BIT(0);
|
||||
extra |= BIT(4);
|
||||
sdhci_writel(host, extra, reg);
|
||||
|
||||
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 dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
|
||||
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) {
|
||||
reset_control_assert(priv->reset);
|
||||
|
|
@ -700,6 +835,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 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,
|
||||
|
|
@ -1100,6 +1238,411 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
|
|||
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 = {
|
||||
.set_clock = sdhci_set_clock,
|
||||
.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,
|
||||
};
|
||||
|
||||
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 = {
|
||||
.pdata = {
|
||||
.ops = &sdhci_dwcmshc_ops,
|
||||
|
|
@ -1193,6 +1748,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
|
|||
};
|
||||
#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 = {
|
||||
.pdata = {
|
||||
.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 |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk35xx_postinit,
|
||||
};
|
||||
|
|
@ -1213,6 +1778,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
|
|||
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
|
||||
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
|
||||
},
|
||||
.cqhci_host_ops = &rk35xx_cqhci_ops,
|
||||
.init = dwcmshc_rk35xx_init,
|
||||
.postinit = dwcmshc_rk3576_postinit,
|
||||
};
|
||||
|
|
@ -1243,6 +1809,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
|
|||
.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 = {
|
||||
.enable = dwcmshc_sdhci_cqe_enable,
|
||||
.disable = sdhci_cqe_disable,
|
||||
|
|
@ -1250,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
|
|||
.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 sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
|
@ -1280,7 +1858,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
|
|||
}
|
||||
|
||||
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
|
||||
cq_host->ops = &dwcmshc_cqhci_ops;
|
||||
if (pltfm_data->cqhci_host_ops)
|
||||
cq_host->ops = pltfm_data->cqhci_host_ops;
|
||||
else
|
||||
cq_host->ops = &dwcmshc_cqhci_ops;
|
||||
|
||||
/* Enable using of 128-bit task descriptors */
|
||||
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
|
||||
|
|
@ -1343,6 +1924,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
|
|||
.compatible = "sophgo,sg2042-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_sg2042_pdata,
|
||||
},
|
||||
{
|
||||
.compatible = "eswin,eic7700-dwcmshc",
|
||||
.data = &sdhci_dwcmshc_eic7700_pdata,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
|
||||
|
|
@ -1448,7 +2033,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
|
|||
priv->vendor_specific_area2 =
|
||||
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
|
||||
|
||||
dwcmshc_cqhci_init(host, pdev);
|
||||
dwcmshc_cqhci_init(host, pdev, pltfm_data);
|
||||
}
|
||||
|
||||
if (pltfm_data->postinit)
|
||||
|
|
@ -1575,17 +2160,6 @@ static int dwcmshc_resume(struct device *dev)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
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_resume(struct device *dev);
|
||||
#endif
|
||||
|
||||
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_600 2
|
||||
#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_400 (1 << SD_SET_CURRENT_LIMIT_400)
|
||||
|
|
|
|||
Loading…
Reference in New Issue