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:
Linus Torvalds 2025-12-04 14:10:16 -08:00
commit dd463c51a3
36 changed files with 1219 additions and 334 deletions

View File

@ -21,9 +21,11 @@ properties:
- items: - items:
- enum: - enum:
- brcm,bcm2712-sdhci - brcm,bcm2712-sdhci
- brcm,bcm72116-sdhci
- brcm,bcm74165b0-sdhci - brcm,bcm74165b0-sdhci
- brcm,bcm7445-sdhci - brcm,bcm7445-sdhci
- brcm,bcm7425-sdhci - brcm,bcm7425-sdhci
- brcm,bcm74371-sdhci
- const: brcm,sdhci-brcmstb - const: brcm,sdhci-brcmstb
reg: reg:

View File

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

View File

@ -38,6 +38,7 @@ properties:
- rockchip,rk3328-dw-mshc - rockchip,rk3328-dw-mshc
- rockchip,rk3368-dw-mshc - rockchip,rk3368-dw-mshc
- rockchip,rk3399-dw-mshc - rockchip,rk3399-dw-mshc
- rockchip,rk3506-dw-mshc
- rockchip,rk3528-dw-mshc - rockchip,rk3528-dw-mshc
- rockchip,rk3562-dw-mshc - rockchip,rk3562-dw-mshc
- rockchip,rk3568-dw-mshc - rockchip,rk3568-dw-mshc

View File

@ -50,8 +50,7 @@ properties:
- const: clk_ahb - const: clk_ahb
- const: clk_xin - const: clk_xin
dma-coherent: dma-coherent: true
type: boolean
# PHY output tap delays: # PHY output tap delays:
# Used to delay the data valid window and align it to the sampling clock. # Used to delay the data valid window and align it to the sampling clock.

View File

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

View File

@ -42,6 +42,7 @@ properties:
- qcom,ipq5424-sdhci - qcom,ipq5424-sdhci
- qcom,ipq6018-sdhci - qcom,ipq6018-sdhci
- qcom,ipq9574-sdhci - qcom,ipq9574-sdhci
- qcom,kaanapali-sdhci
- qcom,milos-sdhci - qcom,milos-sdhci
- qcom,qcm2290-sdhci - qcom,qcm2290-sdhci
- qcom,qcs404-sdhci - qcom,qcs404-sdhci
@ -70,6 +71,7 @@ properties:
- qcom,sm8450-sdhci - qcom,sm8450-sdhci
- qcom,sm8550-sdhci - qcom,sm8550-sdhci
- qcom,sm8650-sdhci - qcom,sm8650-sdhci
- qcom,sm8750-sdhci
- qcom,x1e80100-sdhci - qcom,x1e80100-sdhci
- const: qcom,sdhci-msm-v5 # for sdcc version 5.0 - const: qcom,sdhci-msm-v5 # for sdcc version 5.0

View File

@ -30,6 +30,7 @@ properties:
- sophgo,sg2002-dwcmshc - sophgo,sg2002-dwcmshc
- sophgo,sg2042-dwcmshc - sophgo,sg2042-dwcmshc
- thead,th1520-dwcmshc - thead,th1520-dwcmshc
- eswin,eic7700-dwcmshc
reg: reg:
maxItems: 1 maxItems: 1
@ -52,17 +53,30 @@ properties:
maxItems: 5 maxItems: 5
reset-names: reset-names:
items: maxItems: 5
- const: core
- const: bus
- const: axi
- const: block
- const: timer
rockchip,txclk-tapnum: rockchip,txclk-tapnum:
description: Specify the number of delay for tx sampling. description: Specify the number of delay for tx sampling.
$ref: /schemas/types.yaml#/definitions/uint8 $ref: /schemas/types.yaml#/definitions/uint8
eswin,hsp-sp-csr:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
- items:
- description: Phandle to HSP(High-Speed Peripheral) device
- description: Offset of the stability status register for internal
clock.
- description: Offset of the stability register for host regulator
voltage.
description:
HSP CSR is to control and get status of different high-speed peripherals
(such as Ethernet, USB, SATA, etc.) via register, which can tune
board-level's parameters of PHY, etc.
eswin,drive-impedance-ohms:
description: Specifies the drive impedance in Ohm.
enum: [33, 40, 50, 66, 100]
required: required:
- compatible - compatible
- reg - reg
@ -110,6 +124,37 @@ allOf:
- const: block - const: block
- const: timer - const: timer
- if:
properties:
compatible:
contains:
const: eswin,eic7700-dwcmshc
then:
properties:
resets:
minItems: 4
maxItems: 4
reset-names:
items:
- const: axi
- const: phy
- const: prstn
- const: txrx
required:
- eswin,hsp-sp-csr
- eswin,drive-impedance-ohms
else:
properties:
resets:
maxItems: 5
reset-names:
items:
- const: core
- const: bus
- const: axi
- const: block
- const: timer
- if: - if:
properties: properties:
compatible: compatible:

View File

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

View File

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

View File

@ -25119,6 +25119,7 @@ F: include/linux/soc/amd/isp4_misc.h
SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER SYNOPSYS DESIGNWARE MMC/SD/SDIO DRIVER
M: Jaehoon Chung <jh80.chung@samsung.com> M: Jaehoon Chung <jh80.chung@samsung.com>
M: Shawn Lin <shawn.lin@rock-chips.com>
L: linux-mmc@vger.kernel.org L: linux-mmc@vger.kernel.org
S: Maintained S: Maintained
F: drivers/mmc/host/dw_mmc* F: drivers/mmc/host/dw_mmc*

View File

@ -349,10 +349,10 @@ static umode_t mmc_disk_attrs_is_visible(struct kobject *kobj,
if (a == &dev_attr_ro_lock_until_next_power_on.attr && if (a == &dev_attr_ro_lock_until_next_power_on.attr &&
(md->area_type & MMC_BLK_DATA_AREA_BOOT) && (md->area_type & MMC_BLK_DATA_AREA_BOOT) &&
md->queue.card->ext_csd.boot_ro_lockable) { md->queue.card->ext_csd.boot_ro_lockable) {
mode = S_IRUGO; mode = 0444;
if (!(md->queue.card->ext_csd.boot_ro_lock & if (!(md->queue.card->ext_csd.boot_ro_lock &
EXT_CSD_BOOT_WP_B_PWR_WP_DIS)) EXT_CSD_BOOT_WP_B_PWR_WP_DIS))
mode |= S_IWUSR; mode |= 0200;
} }
mmc_blk_put(md); mmc_blk_put(md);
@ -957,7 +957,6 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
u32 result; u32 result;
__be32 *blocks; __be32 *blocks;
u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4; u8 resp_sz = mmc_card_ult_capacity(card) ? 8 : 4;
unsigned int noio_flag;
struct mmc_request mrq = {}; struct mmc_request mrq = {};
struct mmc_command cmd = {}; struct mmc_command cmd = {};
@ -982,9 +981,7 @@ static int mmc_sd_num_wr_blocks(struct mmc_card *card, u32 *written_blocks)
mrq.cmd = &cmd; mrq.cmd = &cmd;
mrq.data = &data; mrq.data = &data;
noio_flag = memalloc_noio_save(); blocks = kmalloc(resp_sz, GFP_NOIO);
blocks = kmalloc(resp_sz, GFP_KERNEL);
memalloc_noio_restore(noio_flag);
if (!blocks) if (!blocks)
return -ENOMEM; return -ENOMEM;
@ -3194,7 +3191,7 @@ static void mmc_blk_add_debugfs(struct mmc_card *card, struct mmc_blk_data *md)
if (mmc_card_mmc(card)) { if (mmc_card_mmc(card)) {
md->ext_csd_dentry = md->ext_csd_dentry =
debugfs_create_file("ext_csd", S_IRUSR, root, card, debugfs_create_file("ext_csd", 0400, root, card,
&mmc_dbg_ext_csd_fops); &mmc_dbg_ext_csd_fops);
} }
} }
@ -3275,7 +3272,8 @@ static int mmc_blk_probe(struct mmc_card *card)
mmc_fixup_device(card, mmc_blk_fixups); mmc_fixup_device(card, mmc_blk_fixups);
card->complete_wq = alloc_workqueue("mmc_complete", card->complete_wq = alloc_workqueue("mmc_complete",
WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); WQ_MEM_RECLAIM | WQ_HIGHPRI | WQ_PERCPU,
0);
if (!card->complete_wq) { if (!card->complete_wq) {
pr_err("Failed to create mmc completion workqueue"); pr_err("Failed to create mmc completion workqueue");
return -ENOMEM; return -ENOMEM;

View File

@ -20,7 +20,7 @@ static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *a
struct mmc_card *card = mmc_dev_to_card(dev); \ struct mmc_card *card = mmc_dev_to_card(dev); \
return sysfs_emit(buf, fmt, args); \ return sysfs_emit(buf, fmt, args); \
} \ } \
static DEVICE_ATTR(name, S_IRUGO, mmc_##name##_show, NULL) static DEVICE_ATTR(name, 0444, mmc_##name##_show, NULL)
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct mmc_card *mmc_alloc_card(struct mmc_host *host,
const struct device_type *type); const struct device_type *type);

View File

@ -315,7 +315,10 @@ static int mmc_caps_set(void *data, u64 val)
MMC_CAP_SD_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_UHS | MMC_CAP_UHS |
MMC_CAP_DDR; MMC_CAP_DDR |
MMC_CAP_4_BIT_DATA |
MMC_CAP_8_BIT_DATA |
MMC_CAP_CMD23;
if (diff & ~allowed) if (diff & ~allowed)
return -EINVAL; return -EINVAL;
@ -327,7 +330,10 @@ static int mmc_caps_set(void *data, u64 val)
static int mmc_caps2_set(void *data, u64 val) static int mmc_caps2_set(void *data, u64 val)
{ {
u32 allowed = MMC_CAP2_HSX00_1_8V | MMC_CAP2_HSX00_1_2V; u32 allowed = MMC_CAP2_HSX00_1_8V |
MMC_CAP2_HSX00_1_2V |
MMC_CAP2_CQE |
MMC_CAP2_CQE_DCMD;
u32 *caps = data; u32 *caps = data;
u32 diff = *caps ^ val; u32 diff = *caps ^ val;

View File

@ -831,7 +831,7 @@ static ssize_t mmc_fwrev_show(struct device *dev,
card->ext_csd.fwrev); card->ext_csd.fwrev);
} }
static DEVICE_ATTR(fwrev, S_IRUGO, mmc_fwrev_show, NULL); static DEVICE_ATTR(fwrev, 0444, mmc_fwrev_show, NULL);
static ssize_t mmc_dsr_show(struct device *dev, static ssize_t mmc_dsr_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
@ -847,7 +847,7 @@ static ssize_t mmc_dsr_show(struct device *dev,
return sysfs_emit(buf, "0x%x\n", 0x404); return sysfs_emit(buf, "0x%x\n", 0x404);
} }
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
static struct attribute *mmc_std_attrs[] = { static struct attribute *mmc_std_attrs[] = {
&dev_attr_cid.attr, &dev_attr_cid.attr,

View File

@ -3208,12 +3208,12 @@ static int mmc_test_register_dbgfs_file(struct mmc_card *card)
mutex_lock(&mmc_test_lock); mutex_lock(&mmc_test_lock);
ret = __mmc_test_register_dbgfs_file(card, "test", S_IWUSR | S_IRUGO, ret = __mmc_test_register_dbgfs_file(card, "test", 0644,
&mmc_test_fops_test); &mmc_test_fops_test);
if (ret) if (ret)
goto err; goto err;
ret = __mmc_test_register_dbgfs_file(card, "testlist", S_IRUGO, ret = __mmc_test_register_dbgfs_file(card, "testlist", 0444,
&mtf_testlist_fops); &mtf_testlist_fops);
if (ret) if (ret)
goto err; goto err;

View File

@ -554,7 +554,7 @@ static u32 sd_get_host_max_current(struct mmc_host *host)
static int sd_set_current_limit(struct mmc_card *card, u8 *status) static int sd_set_current_limit(struct mmc_card *card, u8 *status)
{ {
int current_limit = SD_SET_CURRENT_NO_CHANGE; int current_limit = SD_SET_CURRENT_LIMIT_200;
int err; int err;
u32 max_current; u32 max_current;
@ -598,11 +598,8 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
else if (max_current >= 400 && else if (max_current >= 400 &&
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400) card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_400)
current_limit = SD_SET_CURRENT_LIMIT_400; current_limit = SD_SET_CURRENT_LIMIT_400;
else if (max_current >= 200 &&
card->sw_caps.sd3_curr_limit & SD_MAX_CURRENT_200)
current_limit = SD_SET_CURRENT_LIMIT_200;
if (current_limit != SD_SET_CURRENT_NO_CHANGE) { if (current_limit != SD_SET_CURRENT_LIMIT_200) {
err = mmc_sd_switch(card, SD_SWITCH_SET, 3, err = mmc_sd_switch(card, SD_SWITCH_SET, 3,
current_limit, status); current_limit, status);
if (err) if (err)
@ -744,7 +741,7 @@ static ssize_t mmc_dsr_show(struct device *dev, struct device_attribute *attr,
return sysfs_emit(buf, "0x%x\n", 0x404); return sysfs_emit(buf, "0x%x\n", 0x404);
} }
static DEVICE_ATTR(dsr, S_IRUGO, mmc_dsr_show, NULL); static DEVICE_ATTR(dsr, 0444, mmc_dsr_show, NULL);
MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor); MMC_DEV_ATTR(vendor, "0x%04x\n", card->cis.vendor);
MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device); MMC_DEV_ATTR(device, "0x%04x\n", card->cis.device);

View File

@ -504,6 +504,7 @@ config MMC_MESON_MX_SDIO
depends on ARCH_MESON || COMPILE_TEST depends on ARCH_MESON || COMPILE_TEST
depends on COMMON_CLK depends on COMMON_CLK
depends on OF_ADDRESS depends on OF_ADDRESS
select REGMAP_MMIO
help help
This selects support for the SD/MMC Host Controller on This selects support for the SD/MMC Host Controller on
Amlogic Meson6, Meson8 and Meson8b SoCs. Amlogic Meson6, Meson8 and Meson8b SoCs.

View File

@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
if (!root) if (!root)
return; return;
debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops); debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops);
debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); debugfs_create_file("req", 0400, root, slot, &atmci_req_fops);
debugfs_create_u32("state", S_IRUSR, root, &host->state); debugfs_create_u32("state", 0400, root, &host->state);
debugfs_create_xul("pending_events", S_IRUSR, root, debugfs_create_xul("pending_events", 0400, root,
&host->pending_events); &host->pending_events);
debugfs_create_xul("completed_events", S_IRUSR, root, debugfs_create_xul("completed_events", 0400, root,
&host->completed_events); &host->completed_events);
} }

View File

@ -93,6 +93,7 @@
/* send status config 1 */ /* send status config 1 */
#define CQHCI_SSC1 0x40 #define CQHCI_SSC1 0x40
#define CQHCI_SSC1_CBC_MASK GENMASK(19, 16) #define CQHCI_SSC1_CBC_MASK GENMASK(19, 16)
#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0)
/* send status config 2 */ /* send status config 2 */
#define CQHCI_SSC2 0x44 #define CQHCI_SSC2 0x44

View File

@ -145,17 +145,17 @@
#define MAX_NR_SG 16 #define MAX_NR_SG 16
static unsigned rw_threshold = 32; static unsigned rw_threshold = 32;
module_param(rw_threshold, uint, S_IRUGO); module_param(rw_threshold, uint, 0444);
MODULE_PARM_DESC(rw_threshold, MODULE_PARM_DESC(rw_threshold,
"Read/Write threshold. Default = 32"); "Read/Write threshold. Default = 32");
static unsigned poll_threshold = 128; static unsigned poll_threshold = 128;
module_param(poll_threshold, uint, S_IRUGO); module_param(poll_threshold, uint, 0444);
MODULE_PARM_DESC(poll_threshold, MODULE_PARM_DESC(poll_threshold,
"Polling transaction size threshold. Default = 128"); "Polling transaction size threshold. Default = 128");
static unsigned poll_loopcount = 32; static unsigned poll_loopcount = 32;
module_param(poll_loopcount, uint, S_IRUGO); module_param(poll_loopcount, uint, 0444);
MODULE_PARM_DESC(poll_loopcount, MODULE_PARM_DESC(poll_loopcount,
"Maximum polling loop count. Default = 32"); "Maximum polling loop count. Default = 32");

View File

@ -19,6 +19,8 @@
#define RK3288_CLKGEN_DIV 2 #define RK3288_CLKGEN_DIV 2
#define SDMMC_TIMING_CON0 0x130 #define SDMMC_TIMING_CON0 0x130
#define SDMMC_TIMING_CON1 0x134 #define SDMMC_TIMING_CON1 0x134
#define SDMMC_MISC_CON 0x138
#define MEM_CLK_AUTOGATE_ENABLE BIT(5)
#define ROCKCHIP_MMC_DELAY_SEL BIT(10) #define ROCKCHIP_MMC_DELAY_SEL BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3 #define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DEGREE_OFFSET 1 #define ROCKCHIP_MMC_DEGREE_OFFSET 1
@ -470,6 +472,7 @@ static int dw_mci_rk3576_parse_dt(struct dw_mci *host)
static int dw_mci_rockchip_init(struct dw_mci *host) static int dw_mci_rockchip_init(struct dw_mci *host)
{ {
struct dw_mci_rockchip_priv_data *priv = host->priv;
int ret, i; int ret, i;
/* It is slot 8 on Rockchip SoCs */ /* It is slot 8 on Rockchip SoCs */
@ -494,6 +497,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host)
dev_warn(host->dev, "no valid minimum freq: %d\n", ret); dev_warn(host->dev, "no valid minimum freq: %d\n", ret);
} }
if (priv->internal_phase)
mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE);
return 0; return 0;
} }

View File

@ -175,12 +175,12 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
if (!root) if (!root)
return; return;
debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops); debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops);
debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops); debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops);
debugfs_create_u32("state", S_IRUSR, root, &host->state); debugfs_create_u32("state", 0400, root, &host->state);
debugfs_create_xul("pending_events", S_IRUSR, root, debugfs_create_xul("pending_events", 0400, root,
&host->pending_events); &host->pending_events);
debugfs_create_xul("completed_events", S_IRUSR, root, debugfs_create_xul("completed_events", 0400, root,
&host->completed_events); &host->completed_events);
#ifdef CONFIG_FAULT_INJECTION #ifdef CONFIG_FAULT_INJECTION
fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc); fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc);
@ -3120,9 +3120,8 @@ static void dw_mci_init_dma(struct dw_mci *host)
host->dma_64bit_address = 1; host->dma_64bit_address = 1;
dev_info(host->dev, dev_info(host->dev,
"IDMAC supports 64-bit address mode.\n"); "IDMAC supports 64-bit address mode.\n");
if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64)))
dma_set_coherent_mask(host->dev, dev_info(host->dev, "Fail to set 64-bit DMA mask");
DMA_BIT_MASK(64));
} else { } else {
/* host supports IDMAC in 32-bit address mode */ /* host supports IDMAC in 32-bit address mode */
host->dma_64bit_address = 0; host->dma_64bit_address = 0;

View File

@ -19,6 +19,7 @@
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/types.h> #include <linux/types.h>
@ -98,17 +99,16 @@
#define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1) #define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1)
#define MESON_MX_SDIO_MAX_SLOTS 3 #define MESON_MX_SDIO_MAX_SLOTS 3
struct meson_mx_mmc_host_clkc {
struct clk_divider cfg_div;
struct clk_fixed_factor fixed_div2;
};
struct meson_mx_mmc_host { struct meson_mx_mmc_host {
struct device *controller_dev; struct device *controller_dev;
struct clk *parent_clk;
struct clk *core_clk;
struct clk_divider cfg_div;
struct clk *cfg_div_clk; struct clk *cfg_div_clk;
struct clk_fixed_factor fixed_factor; struct regmap *regmap;
struct clk *fixed_factor_clk;
void __iomem *base;
int irq; int irq;
spinlock_t irq_lock; spinlock_t irq_lock;
@ -122,22 +122,10 @@ struct meson_mx_mmc_host {
int error; int error;
}; };
static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask,
u32 val)
{
struct meson_mx_mmc_host *host = mmc_priv(mmc);
u32 regval;
regval = readl(host->base + reg);
regval &= ~mask;
regval |= (val & mask);
writel(regval, host->base + reg);
}
static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host) static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host)
{ {
writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC); regmap_write(host->regmap, MESON_MX_SDIO_IRQC,
MESON_MX_SDIO_IRQC_SOFT_RESET);
udelay(2); udelay(2);
} }
@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
struct meson_mx_mmc_host *host = mmc_priv(mmc); struct meson_mx_mmc_host *host = mmc_priv(mmc);
unsigned int pack_size; unsigned int pack_size;
unsigned long irqflags, timeout; unsigned long irqflags, timeout;
u32 mult, send = 0, ext = 0; u32 send = 0, ext = 0;
host->cmd = cmd; host->cmd = cmd;
@ -215,25 +203,22 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc,
spin_lock_irqsave(&host->irq_lock, irqflags); spin_lock_irqsave(&host->irq_lock, irqflags);
mult = readl(host->base + MESON_MX_SDIO_MULT); regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK; MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31),
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id); FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK,
mult |= BIT(31); host->slot_id) | BIT(31));
writel(mult, host->base + MESON_MX_SDIO_MULT);
/* enable the CMD done interrupt */ /* enable the CMD done interrupt */
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC, regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC,
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN,
MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN); MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
/* clear pending interrupts */ /* clear pending interrupts */
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS, regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS,
MESON_MX_SDIO_IRQS_CMD_INT,
MESON_MX_SDIO_IRQS_CMD_INT); MESON_MX_SDIO_IRQS_CMD_INT);
writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU); regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg);
writel(ext, host->base + MESON_MX_SDIO_EXT); regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext);
writel(send, host->base + MESON_MX_SDIO_SEND); regmap_write(host->regmap, MESON_MX_SDIO_SEND, send);
spin_unlock_irqrestore(&host->irq_lock, irqflags); spin_unlock_irqrestore(&host->irq_lock, irqflags);
@ -263,13 +248,12 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
switch (ios->bus_width) { switch (ios->bus_width) {
case MMC_BUS_WIDTH_1: case MMC_BUS_WIDTH_1:
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF,
MESON_MX_SDIO_CONF_BUS_WIDTH, 0); MESON_MX_SDIO_CONF_BUS_WIDTH);
break; break;
case MMC_BUS_WIDTH_4: case MMC_BUS_WIDTH_4:
meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF,
MESON_MX_SDIO_CONF_BUS_WIDTH,
MESON_MX_SDIO_CONF_BUS_WIDTH); MESON_MX_SDIO_CONF_BUS_WIDTH);
break; break;
@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
host->mrq = mrq; host->mrq = mrq;
if (mrq->data) if (mrq->data)
writel(sg_dma_address(mrq->data->sg), regmap_write(host->regmap, MESON_MX_SDIO_ADDR,
host->base + MESON_MX_SDIO_ADDR); sg_dma_address(mrq->data->sg));
if (mrq->sbc) if (mrq->sbc)
meson_mx_mmc_start_cmd(mmc, mrq->sbc); meson_mx_mmc_start_cmd(mmc, mrq->sbc);
@ -364,24 +348,26 @@ static void meson_mx_mmc_read_response(struct mmc_host *mmc,
struct mmc_command *cmd) struct mmc_command *cmd)
{ {
struct meson_mx_mmc_host *host = mmc_priv(mmc); struct meson_mx_mmc_host *host = mmc_priv(mmc);
u32 mult; unsigned int i, resp[4];
int i, resp[4];
mult = readl(host->base + MESON_MX_SDIO_MULT); regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT,
mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX; MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK; MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0); MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX |
writel(mult, host->base + MESON_MX_SDIO_MULT); FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK,
0));
if (cmd->flags & MMC_RSP_136) { if (cmd->flags & MMC_RSP_136) {
for (i = 0; i <= 3; i++) for (i = 0; i <= 3; i++)
resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU); regmap_read(host->regmap, MESON_MX_SDIO_ARGU,
&resp[3 - i]);
cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff); cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff);
cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff); cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff);
cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff); cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff);
cmd->resp[3] = (resp[3] << 8); cmd->resp[3] = (resp[3] << 8);
} else if (cmd->flags & MMC_RSP_PRESENT) { } else if (cmd->flags & MMC_RSP_PRESENT) {
cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU); regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &cmd->resp[0]);
} }
} }
@ -422,8 +408,8 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
spin_lock(&host->irq_lock); spin_lock(&host->irq_lock);
irqs = readl(host->base + MESON_MX_SDIO_IRQS); regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
send = readl(host->base + MESON_MX_SDIO_SEND); regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send);
if (irqs & MESON_MX_SDIO_IRQS_CMD_INT) if (irqs & MESON_MX_SDIO_IRQS_CMD_INT)
ret = meson_mx_mmc_process_cmd_irq(host, irqs, send); ret = meson_mx_mmc_process_cmd_irq(host, irqs, send);
@ -431,7 +417,7 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data)
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
/* finally ACK all pending interrupts */ /* finally ACK all pending interrupts */
writel(irqs, host->base + MESON_MX_SDIO_IRQS); regmap_write(host->regmap, MESON_MX_SDIO_IRQS, irqs);
spin_unlock(&host->irq_lock); spin_unlock(&host->irq_lock);
@ -450,8 +436,7 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data)
if (cmd->data) { if (cmd->data) {
dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg, dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg,
cmd->data->sg_len, cmd->data->sg_len, mmc_get_dma_dir(cmd->data));
mmc_get_dma_dir(cmd->data));
cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks; cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks;
} }
@ -470,14 +455,13 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
struct meson_mx_mmc_host *host = timer_container_of(host, t, struct meson_mx_mmc_host *host = timer_container_of(host, t,
cmd_timeout); cmd_timeout);
unsigned long irqflags; unsigned long irqflags;
u32 irqc; u32 irqs, argu;
spin_lock_irqsave(&host->irq_lock, irqflags); spin_lock_irqsave(&host->irq_lock, irqflags);
/* disable the CMD interrupt */ /* disable the CMD interrupt */
irqc = readl(host->base + MESON_MX_SDIO_IRQC); regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC,
irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN; MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN);
writel(irqc, host->base + MESON_MX_SDIO_IRQC);
spin_unlock_irqrestore(&host->irq_lock, irqflags); spin_unlock_irqrestore(&host->irq_lock, irqflags);
@ -488,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t)
if (!host->cmd) if (!host->cmd)
return; return;
regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs);
regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &argu);
dev_dbg(mmc_dev(host->mmc), dev_dbg(mmc_dev(host->mmc),
"Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n", "Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n",
host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS), host->cmd->opcode, irqs, argu);
readl(host->base + MESON_MX_SDIO_ARGU));
host->cmd->error = -ETIMEDOUT; host->cmd->error = -ETIMEDOUT;
@ -507,22 +493,29 @@ static struct mmc_host_ops meson_mx_mmc_ops = {
static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent) static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent)
{ {
struct device_node *slot_node; struct platform_device *pdev = NULL;
struct platform_device *pdev;
for_each_available_child_of_node_scoped(parent->of_node, slot_node) {
if (!of_device_is_compatible(slot_node, "mmc-slot"))
continue;
/* /*
* TODO: the MMC core framework currently does not support * TODO: the MMC core framework currently does not support
* controllers with multiple slots properly. So we only register * controllers with multiple slots properly. So we only
* the first slot for now * register the first slot for now.
*/ */
slot_node = of_get_compatible_child(parent->of_node, "mmc-slot"); if (pdev) {
if (!slot_node) { dev_warn(parent,
dev_warn(parent, "no 'mmc-slot' sub-node found\n"); "more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n");
return ERR_PTR(-ENOENT); break;
} }
pdev = of_platform_device_create(slot_node, NULL, parent); pdev = of_platform_device_create(slot_node, NULL, parent);
of_node_put(slot_node); if (!pdev)
dev_err(parent,
"Failed to create platform device for mmc-slot node '%pOF'\n",
slot_node);
}
return pdev; return pdev;
} }
@ -533,16 +526,14 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
struct device *slot_dev = mmc_dev(mmc); struct device *slot_dev = mmc_dev(mmc);
int ret; int ret;
if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) { if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id))
dev_err(slot_dev, "missing 'reg' property\n"); return dev_err_probe(slot_dev, -EINVAL,
return -EINVAL; "missing 'reg' property\n");
}
if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) { if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS)
dev_err(slot_dev, "invalid 'reg' property value %d\n", return dev_err_probe(slot_dev, -EINVAL,
"invalid 'reg' property value %d\n",
host->slot_id); host->slot_id);
return -EINVAL;
}
/* Get regulators and the supported OCR mask */ /* Get regulators and the supported OCR mask */
ret = mmc_regulator_get_supply(mmc); ret = mmc_regulator_get_supply(mmc);
@ -561,8 +552,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
/* Get the min and max supported clock rates */ /* Get the min and max supported clock rates */
mmc->f_min = clk_round_rate(host->cfg_div_clk, 1); mmc->f_min = clk_round_rate(host->cfg_div_clk, 1);
mmc->f_max = clk_round_rate(host->cfg_div_clk, mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX);
clk_get_rate(host->parent_clk));
mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY;
mmc->ops = &meson_mx_mmc_ops; mmc->ops = &meson_mx_mmc_ops;
@ -578,70 +568,89 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host)
return 0; return 0;
} }
static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host) static struct clk *meson_mx_mmc_register_clk(struct device *dev,
void __iomem *base)
{ {
struct clk_init_data init; const char *fixed_div2_name, *cfg_div_name;
const char *clk_div_parent, *clk_fixed_factor_parent; struct meson_mx_mmc_host_clkc *host_clkc;
struct clk *clk;
int ret;
clk_fixed_factor_parent = __clk_get_name(host->parent_clk); /* use a dedicated memory allocation for the clock controller to
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, * prevent use-after-free as meson_mx_mmc_host is free'd before
"%s#fixed_factor", * dev (controller dev, not mmc_host->dev) is free'd.
dev_name(host->controller_dev)); */
if (!init.name) host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL);
return -ENOMEM; if (!host_clkc)
return ERR_PTR(-ENOMEM);
init.ops = &clk_fixed_factor_ops; fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2",
init.flags = 0; dev_name(dev));
init.parent_names = &clk_fixed_factor_parent; if (!fixed_div2_name)
init.num_parents = 1; return ERR_PTR(-ENOMEM);
host->fixed_factor.div = 2;
host->fixed_factor.mult = 1;
host->fixed_factor.hw.init = &init;
host->fixed_factor_clk = devm_clk_register(host->controller_dev, host_clkc->fixed_div2.div = 2;
&host->fixed_factor.hw); host_clkc->fixed_div2.mult = 1;
if (WARN_ON(IS_ERR(host->fixed_factor_clk))) host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name,
return PTR_ERR(host->fixed_factor_clk); "clkin",
&clk_fixed_factor_ops,
0);
ret = devm_clk_hw_register(dev, &host_clkc->fixed_div2.hw);
if (ret)
return dev_err_ptr_probe(dev, ret,
"Failed to register %s clock\n",
fixed_div2_name);
clk_div_parent = __clk_get_name(host->fixed_factor_clk); cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev));
init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, if (!cfg_div_name)
"%s#div", dev_name(host->controller_dev)); return ERR_PTR(-ENOMEM);
if (!init.name)
return -ENOMEM;
init.ops = &clk_divider_ops; host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF;
init.flags = CLK_SET_RATE_PARENT; host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT;
init.parent_names = &clk_div_parent; host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH;
init.num_parents = 1; host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name,
host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF; &host_clkc->fixed_div2.hw,
host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT; &clk_divider_ops,
host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH; CLK_DIVIDER_ALLOW_ZERO);
host->cfg_div.hw.init = &init; ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw);
host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO; if (ret)
return dev_err_ptr_probe(dev, ret,
"Failed to register %s clock\n",
cfg_div_name);
host->cfg_div_clk = devm_clk_register(host->controller_dev, clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk");
&host->cfg_div.hw); if (IS_ERR(clk))
if (WARN_ON(IS_ERR(host->cfg_div_clk))) return dev_err_ptr_probe(dev, PTR_ERR(clk),
return PTR_ERR(host->cfg_div_clk); "Failed to get the cfg_div clock\n");
return 0; return clk;
} }
static int meson_mx_mmc_probe(struct platform_device *pdev) static int meson_mx_mmc_probe(struct platform_device *pdev)
{ {
const struct regmap_config meson_mx_sdio_regmap_config = {
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
.max_register = MESON_MX_SDIO_EXT,
};
struct platform_device *slot_pdev; struct platform_device *slot_pdev;
struct mmc_host *mmc; struct mmc_host *mmc;
struct meson_mx_mmc_host *host; struct meson_mx_mmc_host *host;
struct clk *core_clk;
void __iomem *base;
int ret, irq; int ret, irq;
u32 conf; u32 conf;
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev); slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev);
if (!slot_pdev) if (!slot_pdev)
return -ENODEV; return -ENODEV;
else if (IS_ERR(slot_pdev))
return PTR_ERR(slot_pdev);
mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev); mmc = devm_mmc_alloc_host(&slot_pdev->dev, sizeof(*host));
if (!mmc) { if (!mmc) {
ret = -ENOMEM; ret = -ENOMEM;
goto error_unregister_slot_pdev; goto error_unregister_slot_pdev;
@ -656,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
host->base = devm_platform_ioremap_resource(pdev, 0); host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
if (IS_ERR(host->base)) { &meson_mx_sdio_regmap_config);
ret = PTR_ERR(host->base); if (IS_ERR(host->regmap)) {
goto error_free_mmc; ret = dev_err_probe(host->controller_dev, PTR_ERR(host->regmap),
"Failed to initialize regmap\n");
goto error_unregister_slot_pdev;
} }
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq < 0) { if (irq < 0) {
ret = irq; ret = irq;
goto error_free_mmc; goto error_unregister_slot_pdev;
} }
ret = devm_request_threaded_irq(host->controller_dev, irq, ret = devm_request_threaded_irq(host->controller_dev, irq,
meson_mx_mmc_irq, meson_mx_mmc_irq,
meson_mx_mmc_irq_thread, IRQF_ONESHOT, meson_mx_mmc_irq_thread, IRQF_ONESHOT,
NULL, host); NULL, host);
if (ret)
goto error_free_mmc;
host->core_clk = devm_clk_get(host->controller_dev, "core");
if (IS_ERR(host->core_clk)) {
ret = PTR_ERR(host->core_clk);
goto error_free_mmc;
}
host->parent_clk = devm_clk_get(host->controller_dev, "clkin");
if (IS_ERR(host->parent_clk)) {
ret = PTR_ERR(host->parent_clk);
goto error_free_mmc;
}
ret = meson_mx_mmc_register_clks(host);
if (ret)
goto error_free_mmc;
ret = clk_prepare_enable(host->core_clk);
if (ret) { if (ret) {
dev_err(host->controller_dev, "Failed to enable core clock\n"); dev_err_probe(host->controller_dev, ret,
goto error_free_mmc; "Failed to request IRQ\n");
goto error_unregister_slot_pdev;
}
core_clk = devm_clk_get_enabled(host->controller_dev, "core");
if (IS_ERR(core_clk)) {
ret = dev_err_probe(host->controller_dev, PTR_ERR(core_clk),
"Failed to get and enable 'core' clock\n");
goto error_unregister_slot_pdev;
}
host->cfg_div_clk = meson_mx_mmc_register_clk(&pdev->dev, base);
if (IS_ERR(host->cfg_div_clk)) {
ret = PTR_ERR(host->cfg_div_clk);
goto error_unregister_slot_pdev;
} }
ret = clk_prepare_enable(host->cfg_div_clk); ret = clk_prepare_enable(host->cfg_div_clk);
if (ret) { if (ret) {
dev_err(host->controller_dev, "Failed to enable MMC clock\n"); dev_err_probe(host->controller_dev, ret,
goto error_disable_core_clk; "Failed to enable MMC (cfg div) clock\n");
goto error_unregister_slot_pdev;
} }
conf = 0; conf = 0;
@ -708,22 +714,18 @@ static int meson_mx_mmc_probe(struct platform_device *pdev)
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3); conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2); conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2);
conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2); conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2);
writel(conf, host->base + MESON_MX_SDIO_CONF); regmap_write(host->regmap, MESON_MX_SDIO_CONF, conf);
meson_mx_mmc_soft_reset(host); meson_mx_mmc_soft_reset(host);
ret = meson_mx_mmc_add_host(host); ret = meson_mx_mmc_add_host(host);
if (ret) if (ret)
goto error_disable_clks; goto error_disable_div_clk;
return 0; return 0;
error_disable_clks: error_disable_div_clk:
clk_disable_unprepare(host->cfg_div_clk); clk_disable_unprepare(host->cfg_div_clk);
error_disable_core_clk:
clk_disable_unprepare(host->core_clk);
error_free_mmc:
mmc_free_host(mmc);
error_unregister_slot_pdev: error_unregister_slot_pdev:
of_platform_device_destroy(&slot_pdev->dev, NULL); of_platform_device_destroy(&slot_pdev->dev, NULL);
return ret; return ret;
@ -741,9 +743,6 @@ static void meson_mx_mmc_remove(struct platform_device *pdev)
of_platform_device_destroy(slot_dev, NULL); of_platform_device_destroy(slot_dev, NULL);
clk_disable_unprepare(host->cfg_div_clk); clk_disable_unprepare(host->cfg_div_clk);
clk_disable_unprepare(host->core_clk);
mmc_free_host(host->mmc);
} }
static const struct of_device_id meson_mx_mmc_of_match[] = { static const struct of_device_id meson_mx_mmc_of_match[] = {

View File

@ -1214,7 +1214,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd,
host->data = data; host->data = data;
read = data->flags & MMC_DATA_READ; read = data->flags & MMC_DATA_READ;
mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
msdc_dma_setup(host, &host->dma, data); msdc_dma_setup(host, &host->dma, data);
sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask);
sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1);
@ -1444,7 +1444,7 @@ static void msdc_start_command(struct msdc_host *host,
WARN_ON(host->cmd); WARN_ON(host->cmd);
host->cmd = cmd; host->cmd = cmd;
mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT);
if (!msdc_cmd_is_ready(host, mrq, cmd)) if (!msdc_cmd_is_ready(host, mrq, cmd))
return; return;

View File

@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr,
"closed"); "closed");
} }
static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); static DEVICE_ATTR(cover_switch, 0444, mmc_omap_show_cover_switch, NULL);
static ssize_t static ssize_t
mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
@ -338,7 +338,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", slot->pdata->name); return sprintf(buf, "%s\n", slot->pdata->name);
} }
static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); static DEVICE_ATTR(slot_name, 0444, mmc_omap_show_slot_name, NULL);
static void static void
mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
@ -1477,7 +1477,7 @@ static int mmc_omap_probe(struct platform_device *pdev)
host->nr_slots = pdata->nr_slots; host->nr_slots = pdata->nr_slots;
host->reg_shift = (mmc_omap7xx() ? 1 : 2); host->reg_shift = (mmc_omap7xx() ? 1 : 2);
host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); host->mmc_omap_wq = alloc_workqueue("mmc_omap", WQ_PERCPU, 0);
if (!host->mmc_omap_wq) { if (!host->mmc_omap_wq) {
ret = -ENOMEM; ret = -ENOMEM;
goto err_plat_cleanup; goto err_plat_cleanup;

View File

@ -746,7 +746,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%s\n", mmc_pdata(host)->name); return sprintf(buf, "%s\n", mmc_pdata(host)->name);
} }
static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); static DEVICE_ATTR(slot_name, 0444, omap_hsmmc_show_slot_name, NULL);
/* /*
* Configure the response type and send the cmd. * Configure the response type and send the cmd.
@ -1672,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs);
static void omap_hsmmc_debugfs(struct mmc_host *mmc) static void omap_hsmmc_debugfs(struct mmc_host *mmc)
{ {
if (mmc->debugfs_root) if (mmc->debugfs_root)
debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root, debugfs_create_file("regs", 0400, mmc->debugfs_root,
mmc, &mmc_regs_fops); mmc, &mmc_regs_fops);
} }

View File

@ -9,6 +9,7 @@
#ifndef RENESAS_SDHI_H #ifndef RENESAS_SDHI_H
#define RENESAS_SDHI_H #define RENESAS_SDHI_H
#include <linux/device.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
@ -107,4 +108,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
const struct renesas_sdhi_of_data *of_data, const struct renesas_sdhi_of_data *of_data,
const struct renesas_sdhi_quirks *quirks); const struct renesas_sdhi_quirks *quirks);
void renesas_sdhi_remove(struct platform_device *pdev); void renesas_sdhi_remove(struct platform_device *pdev);
int renesas_sdhi_suspend(struct device *dev);
int renesas_sdhi_resume(struct device *dev);
#endif #endif

View File

@ -31,6 +31,7 @@
#include <linux/platform_data/tmio.h> #include <linux/platform_data/tmio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h> #include <linux/regulator/of_regulator.h>
@ -1103,7 +1104,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (IS_ERR(priv->clk_cd)) if (IS_ERR(priv->clk_cd))
return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock"); return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock");
priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL);
if (IS_ERR(priv->rstc)) if (IS_ERR(priv->rstc))
return PTR_ERR(priv->rstc); return PTR_ERR(priv->rstc);
@ -1317,5 +1318,41 @@ void renesas_sdhi_remove(struct platform_device *pdev)
} }
EXPORT_SYMBOL_GPL(renesas_sdhi_remove); EXPORT_SYMBOL_GPL(renesas_sdhi_remove);
int renesas_sdhi_suspend(struct device *dev)
{
struct tmio_mmc_host *host = dev_get_drvdata(dev);
struct renesas_sdhi *priv = host_to_priv(host);
int ret;
ret = pm_runtime_force_suspend(dev);
if (ret)
return ret;
ret = reset_control_assert(priv->rstc);
if (ret)
pm_runtime_force_resume(dev);
return ret;
}
EXPORT_SYMBOL_GPL(renesas_sdhi_suspend);
int renesas_sdhi_resume(struct device *dev)
{
struct tmio_mmc_host *host = dev_get_drvdata(dev);
struct renesas_sdhi *priv = host_to_priv(host);
int ret;
ret = reset_control_deassert(priv->rstc);
if (ret)
return ret;
ret = pm_runtime_force_resume(dev);
if (ret)
reset_control_assert(priv->rstc);
return ret;
}
EXPORT_SYMBOL_GPL(renesas_sdhi_resume);
MODULE_DESCRIPTION("Renesas SDHI core driver"); MODULE_DESCRIPTION("Renesas SDHI core driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -18,7 +18,6 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/platform_data/tmio.h> #include <linux/platform_data/tmio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/sys_soc.h> #include <linux/sys_soc.h>
@ -124,7 +123,8 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3 = {
static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = { static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
TMIO_MMC_64BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE, .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE,
@ -599,9 +599,8 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
} }
static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = { static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume)
pm_runtime_force_resume) RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
tmio_mmc_host_runtime_resume, tmio_mmc_host_runtime_resume,
NULL) NULL)
}; };
@ -610,7 +609,7 @@ static struct platform_driver renesas_internal_dmac_sdhi_driver = {
.driver = { .driver = {
.name = "renesas_sdhi_internal_dmac", .name = "renesas_sdhi_internal_dmac",
.probe_type = PROBE_PREFER_ASYNCHRONOUS, .probe_type = PROBE_PREFER_ASYNCHRONOUS,
.pm = &renesas_sdhi_internal_dmac_dev_pm_ops, .pm = pm_ptr(&renesas_sdhi_internal_dmac_dev_pm_ops),
.of_match_table = renesas_sdhi_internal_dmac_of_match, .of_match_table = renesas_sdhi_internal_dmac_of_match,
}, },
.probe = renesas_sdhi_internal_dmac_probe, .probe = renesas_sdhi_internal_dmac_probe,

View File

@ -60,7 +60,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = {
static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL |
TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 |
TMIO_MMC_32BIT_DATA_PORT,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY,
.capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT,

View File

@ -31,35 +31,116 @@
#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
#define SDIO_CFG_CQ_CAPABILITY 0x4c
#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
#define SDIO_CFG_CTRL 0x0 #define SDIO_CFG_CTRL 0x0
#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31) #define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30) #define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
#define SDIO_CFG_OP_DLY 0x34
#define SDIO_CFG_OP_DLY_DEFAULT 0x80000003
#define SDIO_CFG_CQ_CAPABILITY 0x4c
#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12)
#define SDIO_CFG_SD_PIN_SEL 0x44
#define SDIO_CFG_V1_SD_PIN_SEL 0x54
#define SDIO_CFG_PHY_SW_MODE_0_RX_CTRL 0x7C
#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac #define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31) #define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0) #define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
#define SDIO_BOOT_MAIN_CTL 0x0
#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V) #define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V)
/* Select all SD UHS type I SDR speed above 50MB/s */ /* Select all SD UHS type I SDR speed above 50MB/s */
#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104) #define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104)
struct sdhci_brcmstb_priv { enum cfg_core_ver {
void __iomem *cfg_regs; SDIO_CFG_CORE_V1 = 1,
unsigned int flags; SDIO_CFG_CORE_V2,
struct clk *base_clk; };
u32 base_freq_hz;
struct sdhci_brcmstb_saved_regs {
u32 sd_pin_sel;
u32 phy_sw_mode0_rxctrl;
u32 max_50mhz_mode;
u32 boot_main_ctl;
}; };
struct brcmstb_match_priv { struct brcmstb_match_priv {
void (*cfginit)(struct sdhci_host *host); void (*cfginit)(struct sdhci_host *host);
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
void (*save_restore_regs)(struct mmc_host *mmc, int save);
struct sdhci_ops *ops; struct sdhci_ops *ops;
const unsigned int flags; const unsigned int flags;
}; };
struct sdhci_brcmstb_priv {
void __iomem *cfg_regs;
void __iomem *boot_regs;
struct sdhci_brcmstb_saved_regs saved_regs;
unsigned int flags;
struct clk *base_clk;
u32 base_freq_hz;
const struct brcmstb_match_priv *match_priv;
};
static void sdhci_brcmstb_save_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
void __iomem *cr = priv->cfg_regs;
bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
if (is_emmc && priv->boot_regs)
sr->boot_main_ctl = readl(priv->boot_regs + SDIO_BOOT_MAIN_CTL);
if (ver == SDIO_CFG_CORE_V1) {
sr->sd_pin_sel = readl(cr + SDIO_CFG_V1_SD_PIN_SEL);
return;
}
sr->sd_pin_sel = readl(cr + SDIO_CFG_SD_PIN_SEL);
sr->phy_sw_mode0_rxctrl = readl(cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
sr->max_50mhz_mode = readl(cr + SDIO_CFG_MAX_50MHZ_MODE);
}
static void sdhci_brcmstb_restore_regs(struct mmc_host *mmc, enum cfg_core_ver ver)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs;
void __iomem *cr = priv->cfg_regs;
bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE;
if (is_emmc && priv->boot_regs)
writel(sr->boot_main_ctl, priv->boot_regs + SDIO_BOOT_MAIN_CTL);
if (ver == SDIO_CFG_CORE_V1) {
writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
return;
}
writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL);
writel(sr->phy_sw_mode0_rxctrl, cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL);
writel(sr->max_50mhz_mode, cr + SDIO_CFG_MAX_50MHZ_MODE);
}
static void sdhci_brcmstb_save_restore_regs_v1(struct mmc_host *mmc, int save)
{
if (save)
sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V1);
else
sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V1);
}
static void sdhci_brcmstb_save_restore_regs_v2(struct mmc_host *mmc, int save)
{
if (save)
sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V2);
else
sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V2);
}
static inline void enable_clock_gating(struct sdhci_host *host) static inline void enable_clock_gating(struct sdhci_host *host)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -212,6 +293,21 @@ static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
} }
} }
static void sdhci_brcmstb_set_72116_uhs_signaling(struct sdhci_host *host, unsigned int timing)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 reg;
/* no change to SDIO_CFG_OP_DLY_DEFAULT when using preset clk rate */
if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN))
return;
reg = (timing == MMC_TIMING_MMC_HS200) ? 0 : SDIO_CFG_OP_DLY_DEFAULT;
writel(reg, priv->cfg_regs + SDIO_CFG_OP_DLY);
sdhci_set_uhs_signaling(host, timing);
}
static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{ {
sdhci_dumpregs(mmc_priv(mmc)); sdhci_dumpregs(mmc_priv(mmc));
@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
.set_uhs_signaling = sdhci_set_uhs_signaling, .set_uhs_signaling = sdhci_set_uhs_signaling,
}; };
static struct sdhci_ops sdhci_brcmstb_ops_72116 = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_brcmstb_set_72116_uhs_signaling,
};
static struct sdhci_ops sdhci_brcmstb_ops_7216 = { static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock, .set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = {
.ops = &sdhci_brcmstb_ops, .ops = &sdhci_brcmstb_ops,
}; };
static struct brcmstb_match_priv match_priv_7445 = { static struct brcmstb_match_priv match_priv_74371 = {
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
.ops = &sdhci_brcmstb_ops, .ops = &sdhci_brcmstb_ops,
}; };
static struct brcmstb_match_priv match_priv_7445 = {
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
.ops = &sdhci_brcmstb_ops,
};
static struct brcmstb_match_priv match_priv_72116 = {
.flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT,
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v1,
.ops = &sdhci_brcmstb_ops_72116,
};
static const struct brcmstb_match_priv match_priv_7216 = { static const struct brcmstb_match_priv match_priv_7216 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es, .hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_7216, .ops = &sdhci_brcmstb_ops_7216,
}; };
static struct brcmstb_match_priv match_priv_74165b0 = { static struct brcmstb_match_priv match_priv_74165b0 = {
.flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE,
.save_restore_regs = sdhci_brcmstb_save_restore_regs_v2,
.hs400es = sdhci_brcmstb_hs400es, .hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_74165b0, .ops = &sdhci_brcmstb_ops_74165b0,
}; };
@ -297,7 +414,9 @@ static struct brcmstb_match_priv match_priv_74165b0 = {
static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 }, { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
{ .compatible = "brcm,bcm74371-sdhci", .data = &match_priv_74371 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
{ .compatible = "brcm,bcm72116-sdhci", .data = &match_priv_72116 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
{ .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 }, { .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 },
{}, {},
@ -395,6 +514,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
pltfm_host = sdhci_priv(host); pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host); priv = sdhci_pltfm_priv(pltfm_host);
priv->match_priv = match->data;
if (device_property_read_bool(&pdev->dev, "supports-cqe")) { if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE;
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
if (res) if (res)
goto err; goto err;
/* map non-standard BOOT registers if present */
if (host->mmc->caps & MMC_CAP_NONREMOVABLE) {
priv->boot_regs = devm_platform_get_and_ioremap_resource(pdev, 2, NULL);
if (IS_ERR(priv->boot_regs))
priv->boot_regs = NULL;
}
/* /*
* Automatic clock gating does not work for SD cards that may * Automatic clock gating does not work for SD cards that may
* voltage switch so only enable it for non-removable devices. * voltage switch so only enable it for non-removable devices.
@ -501,8 +628,13 @@ static int sdhci_brcmstb_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
const struct brcmstb_match_priv *match_priv = priv->match_priv;
int ret; int ret;
if (match_priv->save_restore_regs)
match_priv->save_restore_regs(host->mmc, 1);
clk_disable_unprepare(priv->base_clk); clk_disable_unprepare(priv->base_clk);
if (host->mmc->caps2 & MMC_CAP2_CQE) { if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_suspend(host->mmc); ret = cqhci_suspend(host->mmc);
@ -518,6 +650,7 @@ static int sdhci_brcmstb_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host);
const struct brcmstb_match_priv *match_priv = priv->match_priv;
int ret; int ret;
ret = sdhci_pltfm_resume(dev); ret = sdhci_pltfm_resume(dev);
@ -534,6 +667,9 @@ static int sdhci_brcmstb_resume(struct device *dev)
ret = clk_set_rate(priv->base_clk, priv->base_freq_hz); ret = clk_set_rate(priv->base_clk, priv->base_freq_hz);
} }
if (match_priv->save_restore_regs)
match_priv->save_restore_regs(host->mmc, 0);
if (host->mmc->caps2 & MMC_CAP2_CQE) if (host->mmc->caps2 & MMC_CAP2_CQE)
ret = cqhci_resume(host->mmc); ret = cqhci_resume(host->mmc);

View File

@ -344,41 +344,43 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val,
writel_relaxed(val, host->ioaddr + offset); writel_relaxed(val, host->ioaddr + offset);
} }
static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host) static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host,
unsigned int clock,
unsigned int timing)
{ {
struct mmc_ios ios = host->mmc->ios;
/* /*
* The SDHC requires internal clock frequency to be double the * The SDHC requires internal clock frequency to be double the
* actual clock that will be set for DDR mode. The controller * actual clock that will be set for DDR mode. The controller
* uses the faster clock(100/400MHz) for some of its parts and * uses the faster clock(100/400MHz) for some of its parts and
* send the actual required clock (50/200MHz) to the card. * send the actual required clock (50/200MHz) to the card.
*/ */
if (ios.timing == MMC_TIMING_UHS_DDR50 || if (timing == MMC_TIMING_UHS_DDR50 ||
ios.timing == MMC_TIMING_MMC_DDR52 || timing == MMC_TIMING_MMC_DDR52 ||
ios.timing == MMC_TIMING_MMC_HS400 || (timing == MMC_TIMING_MMC_HS400 &&
clock == MMC_HS200_MAX_DTR) ||
host->flags & SDHCI_HS400_TUNING) host->flags & SDHCI_HS400_TUNING)
return 2; return 2;
return 1; return 1;
} }
static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
unsigned int clock) unsigned int clock,
unsigned int timing)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct mmc_ios curr_ios = host->mmc->ios;
struct clk *core_clk = msm_host->bulk_clks[0].clk; struct clk *core_clk = msm_host->bulk_clks[0].clk;
unsigned long achieved_rate; unsigned long achieved_rate;
unsigned int desired_rate; unsigned int desired_rate;
unsigned int mult; unsigned int mult;
int rc; int rc;
mult = msm_get_clock_mult_for_bus_mode(host); mult = msm_get_clock_mult_for_bus_mode(host, clock, timing);
desired_rate = clock * mult; desired_rate = clock * mult;
rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate); rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate);
if (rc) { if (rc) {
pr_err("%s: Failed to set clock at rate %u at timing %d\n", pr_err("%s: Failed to set clock at rate %u at timing %d\n",
mmc_hostname(host->mmc), desired_rate, curr_ios.timing); mmc_hostname(host->mmc), desired_rate, timing);
return; return;
} }
@ -397,7 +399,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host,
msm_host->clk_rate = desired_rate; msm_host->clk_rate = desired_rate;
pr_debug("%s: Setting clock at rate %lu at timing %d\n", pr_debug("%s: Setting clock at rate %lu at timing %d\n",
mmc_hostname(host->mmc), achieved_rate, curr_ios.timing); mmc_hostname(host->mmc), achieved_rate, timing);
} }
/* Platform specific tuning */ /* Platform specific tuning */
@ -1239,7 +1241,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode)
*/ */
if (host->flags & SDHCI_HS400_TUNING) { if (host->flags & SDHCI_HS400_TUNING) {
sdhci_msm_hc_select_mode(host); sdhci_msm_hc_select_mode(host);
msm_set_clock_rate_for_bus_mode(host, ios.clock); msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
host->flags &= ~SDHCI_HS400_TUNING; host->flags &= ~SDHCI_HS400_TUNING;
} }
@ -1864,6 +1866,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
struct mmc_ios ios = host->mmc->ios;
if (!clock) { if (!clock) {
host->mmc->actual_clock = msm_host->clk_rate = 0; host->mmc->actual_clock = msm_host->clk_rate = 0;
@ -1872,7 +1875,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
sdhci_msm_hc_select_mode(host); sdhci_msm_hc_select_mode(host);
msm_set_clock_rate_for_bus_mode(host, clock); msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing);
out: out:
__sdhci_msm_set_clock(host, clock); __sdhci_msm_set_clock(host, clock);
} }

View File

@ -1991,7 +1991,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc); ret = mmc_of_parse(host->mmc);
if (ret) { if (ret) {
ret = dev_err_probe(dev, ret, "parsing dt failed.\n"); dev_err_probe(dev, ret, "parsing dt failed.\n");
goto unreg_clk; goto unreg_clk;
} }

View File

@ -11,6 +11,7 @@
#include <linux/arm-smccc.h> #include <linux/arm-smccc.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/iopoll.h> #include <linux/iopoll.h>
#include <linux/kernel.h> #include <linux/kernel.h>
@ -19,11 +20,15 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/sizes.h> #include <linux/sizes.h>
#include <linux/mfd/syscon.h>
#include <linux/units.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#include "cqhci.h" #include "cqhci.h"
#include "sdhci-cqhci.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@ -39,6 +44,7 @@
#define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_CARD_IS_EMMC BIT(0)
#define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_ENHANCED_STROBE BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40 #define DWCMSHC_EMMC_ATCTRL 0x40
#define DWCMSHC_AT_STAT 0x44
/* Tuning and auto-tuning fields in AT_CTRL_R control register */ /* Tuning and auto-tuning fields in AT_CTRL_R control register */
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */ #define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */ #define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
@ -82,6 +88,8 @@
#define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c #define DWCMSHC_EMMC_DLL_STRBIN 0x80c
#define DECMSHC_EMMC_DLL_CMDOUT 0x810 #define DECMSHC_EMMC_DLL_CMDOUT 0x810
#define DECMSHC_EMMC_MISC_CON 0x81C
#define MISC_INTCLK_EN BIT(1)
#define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_START BIT(0) #define DWCMSHC_EMMC_DLL_START BIT(0)
#define DWCMSHC_EMMC_DLL_LOCKED BIT(8) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
@ -194,6 +202,19 @@
#define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */ #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */
#define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */ #define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */
/* PHY DLL offset setting register */
#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29)
/* DLL LBT setting register */
#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c)
/* DLL Status register */
#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e)
#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */
/*
* Captures the value of DLL's lock error status information. Value is valid
* only when LOCK_STS is set.
*/
#define DLL_ERROR_STS BIT(1)
#define FLAG_IO_FIXED_1V8 BIT(0) #define FLAG_IO_FIXED_1V8 BIT(0)
#define BOUNDARY_OK(addr, len) \ #define BOUNDARY_OK(addr, len) \
@ -206,6 +227,31 @@
/* SMC call for BlueField-3 eMMC RST_N */ /* SMC call for BlueField-3 eMMC RST_N */
#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007
/* Eswin specific Registers */
#define EIC7700_CARD_CLK_STABLE BIT(28)
#define EIC7700_INT_BCLK_STABLE BIT(16)
#define EIC7700_INT_ACLK_STABLE BIT(8)
#define EIC7700_INT_TMCLK_STABLE BIT(0)
#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \
EIC7700_INT_ACLK_STABLE | \
EIC7700_INT_BCLK_STABLE | \
EIC7700_INT_TMCLK_STABLE)
#define EIC7700_HOST_VAL_STABLE BIT(0)
/* strength definition */
#define PHYCTRL_DR_33OHM 0xee
#define PHYCTRL_DR_40OHM 0xcc
#define PHYCTRL_DR_50OHM 0x88
#define PHYCTRL_DR_66OHM 0x44
#define PHYCTRL_DR_100OHM 0x00
#define MAX_PHASE_CODE 0xff
#define TUNING_RANGE_THRESHOLD 40
#define PHY_CLK_MAX_DELAY_MASK 0x7f
#define PHY_DELAY_CODE_MAX 0x7f
#define PHY_DELAY_CODE_EMMC 0x17
#define PHY_DELAY_CODE_SD 0x55
enum dwcmshc_rk_type { enum dwcmshc_rk_type {
DWCMSHC_RK3568, DWCMSHC_RK3568,
DWCMSHC_RK3588, DWCMSHC_RK3588,
@ -217,6 +263,11 @@ struct rk35xx_priv {
u8 txclk_tapnum; u8 txclk_tapnum;
}; };
struct eic7700_priv {
struct reset_control *reset;
unsigned int drive_impedance;
};
#define DWCMSHC_MAX_OTHER_CLKS 3 #define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv { struct dwcmshc_priv {
@ -234,10 +285,22 @@ struct dwcmshc_priv {
struct dwcmshc_pltfm_data { struct dwcmshc_pltfm_data {
const struct sdhci_pltfm_data pdata; const struct sdhci_pltfm_data pdata;
const struct cqhci_host_ops *cqhci_host_ops;
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
}; };
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
{
u16 ctrl;
ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
ctrl |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
}
}
static int dwcmshc_get_enable_other_clks(struct device *dev, static int dwcmshc_get_enable_other_clks(struct device *dev,
struct dwcmshc_priv *priv, struct dwcmshc_priv *priv,
int num_clks, int num_clks,
@ -574,6 +637,73 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc)
sdhci_dumpregs(mmc_priv(mmc)); sdhci_dumpregs(mmc_priv(mmc));
} }
static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
u32 reg;
/* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */
reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100;
sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1);
reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
reg |= CQHCI_ENABLE;
sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
}
static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
u32 reg;
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
while (reg & SDHCI_DATA_AVAILABLE) {
sdhci_readl(host, SDHCI_BUFFER);
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
}
sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
sdhci_cqe_enable(mmc);
}
static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery)
{
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
u32 ctrl;
/*
* During CQE command transfers, command complete bit gets latched.
* So s/w should clear command complete interrupt status when CQE is
* either halted or disabled. Otherwise unexpected SDCHI legacy
* interrupt gets triggered when CQE is halted/disabled.
*/
spin_lock_irqsave(&host->lock, flags);
ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
ctrl |= SDHCI_INT_RESPONSE;
sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
spin_unlock_irqrestore(&host->lock, flags);
sdhci_cqe_disable(mmc, recovery);
}
static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
u32 ctrl;
ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
ctrl &= ~CQHCI_ENABLE;
sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
}
static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock)
{ {
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -601,10 +731,11 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock
sdhci_set_clock(host, clock); sdhci_set_clock(host, clock);
/* Disable cmd conflict check */ /* Disable cmd conflict check and internal clock gate */
reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3; reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3;
extra = sdhci_readl(host, reg); extra = sdhci_readl(host, reg);
extra &= ~BIT(0); extra &= ~BIT(0);
extra |= BIT(4);
sdhci_writel(host, extra, reg); sdhci_writel(host, extra, reg);
if (clock <= 52000000) { if (clock <= 52000000) {
@ -692,6 +823,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv; struct rk35xx_priv *priv = dwc_priv->priv;
u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
cqhci_deactivate(host->mmc);
if (mask & SDHCI_RESET_ALL && priv->reset) { if (mask & SDHCI_RESET_ALL && priv->reset) {
reset_control_assert(priv->reset); reset_control_assert(priv->reset);
@ -700,6 +835,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
} }
sdhci_reset(host, mask); sdhci_reset(host, mask);
/* Enable INTERNAL CLOCK */
sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
} }
static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host,
@ -1100,6 +1238,411 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host,
ARRAY_SIZE(clk_ids), clk_ids); ARRAY_SIZE(clk_ids), clk_ids);
} }
static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
u16 clk;
host->mmc->actual_clock = clock;
if (clock == 0) {
sdhci_set_clock(host, clock);
return;
}
clk_set_rate(pltfm_host->clk, clock);
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
clk |= SDHCI_CLOCK_INT_EN;
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
dwcmshc_enable_card_clk(host);
}
static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay)
{
delay &= PHY_CLK_MAX_DELAY_MASK;
/* phy clk delay line config */
sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R);
sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R);
}
static void sdhci_eic7700_config_phy(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
struct eic7700_priv *priv = dwc_priv->priv;
unsigned int val, drv;
drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF);
drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
val |= DWCMSHC_CARD_IS_EMMC;
sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
}
/* reset phy, config phy's pad */
sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
/* configure phy pads */
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= PHY_PAD_RXSEL_1V8;
sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
/* Clock PAD Setting */
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
/* PHY strobe PAD setting (EMMC only) */
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042);
val |= PHY_PAD_RXSEL_1V8;
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
}
usleep_range(2000, 3000);
sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R);
sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
}
static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask)
{
sdhci_reset(host, mask);
/* after reset all, the phy's config will be clear */
if (mask == SDHCI_RESET_ALL)
sdhci_eic7700_config_phy(host);
}
static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv)
{
int ret;
priv->reset = devm_reset_control_array_get_optional_exclusive(dev);
if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
dev_err(dev, "failed to get reset control %d\n", ret);
return ret;
}
ret = reset_control_assert(priv->reset);
if (ret) {
dev_err(dev, "Failed to assert reset signals: %d\n", ret);
return ret;
}
usleep_range(2000, 2100);
ret = reset_control_deassert(priv->reset);
if (ret) {
dev_err(dev, "Failed to deassert reset signals: %d\n", ret);
return ret;
}
return ret;
}
static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm)
{
switch (dr_ohm) {
case 100:
return PHYCTRL_DR_100OHM;
case 66:
return PHYCTRL_DR_66OHM;
case 50:
return PHYCTRL_DR_50OHM;
case 40:
return PHYCTRL_DR_40OHM;
case 33:
return PHYCTRL_DR_33OHM;
}
dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm);
return PHYCTRL_DR_50OHM;
}
static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
int delay_min = -1;
int delay_max = -1;
int cmd_error = 0;
int delay = 0;
int i = 0;
int ret;
for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) {
sdhci_eic7700_config_phy_delay(host, i);
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
if (ret) {
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
usleep_range(200, 210);
if (delay_min != -1 && delay_max != -1)
break;
} else {
if (delay_min == -1) {
delay_min = i;
continue;
} else {
delay_max = i;
continue;
}
}
}
if (delay_min == -1 && delay_max == -1) {
pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc));
sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line);
return ret;
}
delay = (delay_min + delay_max) / 2;
sdhci_eic7700_config_phy_delay(host, delay);
return 0;
}
static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
int phase_code = -1;
int code_range = -1;
bool is_sd = false;
int code_min = -1;
int code_max = -1;
int cmd_error = 0;
int ret = 0;
int i = 0;
if ((host->mmc->caps2 & sd_caps) == sd_caps)
is_sd = true;
for (i = 0; i <= MAX_PHASE_CODE; i++) {
/* Centered Phase code */
sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
if (ret) {
/* SD specific range tracking */
if (is_sd && code_min != -1 && code_max != -1) {
if (code_max - code_min > code_range) {
code_range = code_max - code_min;
phase_code = (code_min + code_max) / 2;
if (code_range > TUNING_RANGE_THRESHOLD)
break;
}
code_min = -1;
code_max = -1;
}
/* EMMC breaks after first valid range */
if (!is_sd && code_min != -1 && code_max != -1)
break;
} else {
/* Track valid phase code range */
if (code_min == -1) {
code_min = i;
if (!is_sd)
continue;
}
code_max = i;
if (is_sd && i == MAX_PHASE_CODE) {
if (code_max - code_min > code_range) {
code_range = code_max - code_min;
phase_code = (code_min + code_max) / 2;
}
}
}
}
/* Handle tuning failure case */
if ((is_sd && phase_code == -1) ||
(!is_sd && code_min == -1 && code_max == -1)) {
pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc));
sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
return -EIO;
}
if (!is_sd)
phase_code = (code_min + code_max) / 2;
sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
/* SD specific final verification */
if (is_sd) {
ret = mmc_send_tuning(host->mmc, opcode, &cmd_error);
host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
if (ret) {
pr_err("%s: Final phase code 0x%x verification failed!\n",
mmc_hostname(host->mmc), phase_code);
return ret;
}
}
return 0;
}
static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
int ret = 0;
u16 ctrl;
u32 val;
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
ctrl &= ~SDHCI_CTRL_TUNED_CLK;
sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
val |= AT_CTRL_SW_TUNE_EN;
sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT);
sdhci_writew(host, 0x0, SDHCI_CMD_DATA);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
ret = sdhci_eic7700_delay_tuning(host, opcode);
if (ret)
return ret;
}
ret = sdhci_eic7700_phase_code_tuning(host, opcode);
if (ret)
return ret;
return 0;
}
static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u8 status;
u32 val;
int ret;
dwcmshc_set_uhs_signaling(host, timing);
/* here need make dll locked when in hs400 at 200MHz */
if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) {
val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY));
/* 2-cycle latency */
val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2);
sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL);
sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) |
0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */
/* DLL jump step input */
sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R);
sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK,
PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R);
/* Sets the value of DLL's offset input */
sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R);
/*
* Sets the value of DLL's olbt loadval input. Controls the Ibt
* timer's timeout value at which DLL runs a revalidation cycle.
*/
sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R);
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
usleep_range(100, 110);
ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000,
false, host, PHY_DLL_STATUS_R);
if (ret) {
pr_err("%s: DLL lock timeout! status: 0x%x\n",
mmc_hostname(host->mmc), status);
return;
}
status = sdhci_readb(host, PHY_DLL_STATUS_R);
if (status & DLL_ERROR_STS) {
pr_err("%s: DLL lock failed!err_status:0x%x\n",
mmc_hostname(host->mmc), status);
}
}
}
static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing)
{
u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO;
if ((host->mmc->caps2 & sd_caps) == sd_caps)
sdhci_set_uhs_signaling(host, timing);
else
sdhci_eic7700_set_uhs_signaling(host, timing);
}
static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
unsigned int val, hsp_int_status, hsp_pwr_ctrl;
struct of_phandle_args args;
struct eic7700_priv *priv;
struct regmap *hsp_regmap;
int ret;
priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dwc_priv->priv = priv;
ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv);
if (ret) {
dev_err(dev, "failed to reset\n");
return ret;
}
ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args);
if (ret) {
dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret);
return ret;
}
hsp_regmap = syscon_node_to_regmap(args.np);
if (IS_ERR(hsp_regmap)) {
dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n");
of_node_put(args.np);
return PTR_ERR(hsp_regmap);
}
hsp_int_status = args.args[0];
hsp_pwr_ctrl = args.args[1];
of_node_put(args.np);
/*
* Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status.
* This signals to the eMMC controller that platform clocks (card, ACLK,
* BCLK, TMCLK) are enabled and stable.
*/
regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE);
/*
* Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl.
* This signals that VDD is stable and permits transition to high-speed
* modes (e.g., UHS-I).
*/
regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps)
dwc_priv->delay_line = PHY_DELAY_CODE_EMMC;
else
dwc_priv->delay_line = PHY_DELAY_CODE_SD;
if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val))
priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val);
return 0;
}
static const struct sdhci_ops sdhci_dwcmshc_ops = { static const struct sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = sdhci_set_clock, .set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
@ -1174,6 +1717,18 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = {
.platform_execute_tuning = th1520_execute_tuning, .platform_execute_tuning = th1520_execute_tuning,
}; };
static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = {
.set_clock = sdhci_eic7700_set_clock,
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_timeout_clock = sdhci_pltfm_clk_get_max_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_eic7700_reset,
.set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper,
.set_power = sdhci_set_power_and_bus_voltage,
.irq = dwcmshc_cqe_irq_handler,
.platform_execute_tuning = sdhci_eic7700_executing_tuning,
};
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = {
.pdata = { .pdata = {
.ops = &sdhci_dwcmshc_ops, .ops = &sdhci_dwcmshc_ops,
@ -1193,6 +1748,15 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = {
}; };
#endif #endif
static const struct cqhci_host_ops rk35xx_cqhci_ops = {
.pre_enable = rk35xx_sdhci_cqe_pre_enable,
.enable = rk35xx_sdhci_cqe_enable,
.disable = rk35xx_sdhci_cqe_disable,
.post_disable = rk35xx_sdhci_cqe_post_disable,
.dumpregs = dwcmshc_cqhci_dumpregs,
.set_tran_desc = dwcmshc_set_tran_desc,
};
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.pdata = { .pdata = {
.ops = &sdhci_dwcmshc_rk35xx_ops, .ops = &sdhci_dwcmshc_rk35xx_ops,
@ -1201,6 +1765,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
}, },
.cqhci_host_ops = &rk35xx_cqhci_ops,
.init = dwcmshc_rk35xx_init, .init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk35xx_postinit, .postinit = dwcmshc_rk35xx_postinit,
}; };
@ -1213,6 +1778,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = {
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
}, },
.cqhci_host_ops = &rk35xx_cqhci_ops,
.init = dwcmshc_rk35xx_init, .init = dwcmshc_rk35xx_init,
.postinit = dwcmshc_rk3576_postinit, .postinit = dwcmshc_rk3576_postinit,
}; };
@ -1243,6 +1809,17 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = {
.init = sg2042_init, .init = sg2042_init,
}; };
static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = {
.pdata = {
.ops = &sdhci_dwcmshc_eic7700_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
},
.init = eic7700_init,
};
static const struct cqhci_host_ops dwcmshc_cqhci_ops = { static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.enable = dwcmshc_sdhci_cqe_enable, .enable = dwcmshc_sdhci_cqe_enable,
.disable = sdhci_cqe_disable, .disable = sdhci_cqe_disable,
@ -1250,7 +1827,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc, .set_tran_desc = dwcmshc_set_tran_desc,
}; };
static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev,
const struct dwcmshc_pltfm_data *pltfm_data)
{ {
struct cqhci_host *cq_host; struct cqhci_host *cq_host;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@ -1280,6 +1858,9 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
} }
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
if (pltfm_data->cqhci_host_ops)
cq_host->ops = pltfm_data->cqhci_host_ops;
else
cq_host->ops = &dwcmshc_cqhci_ops; cq_host->ops = &dwcmshc_cqhci_ops;
/* Enable using of 128-bit task descriptors */ /* Enable using of 128-bit task descriptors */
@ -1343,6 +1924,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "sophgo,sg2042-dwcmshc", .compatible = "sophgo,sg2042-dwcmshc",
.data = &sdhci_dwcmshc_sg2042_pdata, .data = &sdhci_dwcmshc_sg2042_pdata,
}, },
{
.compatible = "eswin,eic7700-dwcmshc",
.data = &sdhci_dwcmshc_eic7700_pdata,
},
{}, {},
}; };
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@ -1448,7 +2033,7 @@ static int dwcmshc_probe(struct platform_device *pdev)
priv->vendor_specific_area2 = priv->vendor_specific_area2 =
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
dwcmshc_cqhci_init(host, pdev); dwcmshc_cqhci_init(host, pdev, pltfm_data);
} }
if (pltfm_data->postinit) if (pltfm_data->postinit)
@ -1575,17 +2160,6 @@ static int dwcmshc_resume(struct device *dev)
return ret; return ret;
} }
static void dwcmshc_enable_card_clk(struct sdhci_host *host)
{
u16 ctrl;
ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) {
ctrl |= SDHCI_CLOCK_CARD_EN;
sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL);
}
}
static int dwcmshc_runtime_suspend(struct device *dev) static int dwcmshc_runtime_suspend(struct device *dev)
{ {
struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_host *host = dev_get_drvdata(dev);

View File

@ -209,10 +209,8 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i); void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid); irqreturn_t tmio_mmc_irq(int irq, void *devid);
#ifdef CONFIG_PM
int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev);
#endif
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{ {

View File

@ -182,7 +182,6 @@ struct sd_switch_caps {
#define SD_SET_CURRENT_LIMIT_400 1 #define SD_SET_CURRENT_LIMIT_400 1
#define SD_SET_CURRENT_LIMIT_600 2 #define SD_SET_CURRENT_LIMIT_600 2
#define SD_SET_CURRENT_LIMIT_800 3 #define SD_SET_CURRENT_LIMIT_800 3
#define SD_SET_CURRENT_NO_CHANGE (-1)
#define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200) #define SD_MAX_CURRENT_200 (1 << SD_SET_CURRENT_LIMIT_200)
#define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400) #define SD_MAX_CURRENT_400 (1 << SD_SET_CURRENT_LIMIT_400)