Merge tag 'mmc-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc

Pull MMC updates from Ulf Hansson:
 "MMC host:
   - atmel-mci: Convert DT bindings to json schema
   - dw_mmc: Add support for the Exynos7870 variant
   - dw_mmc-rockchip: Add support for the RK3562/3528 variants
   - omap: Fix potential memory leak in the probe error path
   - renesas_sdhi: Add support for RZ/G3E variants
   - sdhci: Disable SD card clock before changing parameters
   - sdhci-esdhc-imx: Add support for the i.MX94 variant
   - sdhci-of-dwcmshc: Add support for the RK3562/RK3528 variants
   - sdhci-omap: Disable aggressive PM for eMMC/SD-cards
   - sdhci-pci-core: Wait for VDD to settle on card power off
   - sdhci-pxav3: Fix busy-signalling by using MMC_CAP_NEED_RSP_BUSY
   - sunxi-mmc: Add support for the A523 variant

  MEMSTICK:
   - rtsx_usb_ms: Fix potential use-after-free during remove"

* tag 'mmc-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/mmc: (27 commits)
  mmc: core: Remove redundant null check
  mmc: host: Wait for Vdd to settle on card power off
  mmc: omap: Fix memory leak in mmc_omap_new_slot
  memstick: rtsx_usb_ms: Fix slab-use-after-free in rtsx_usb_ms_drv_remove
  mmc: renesas_sdhi: fix error code in renesas_sdhi_probe()
  mmc: sdhci-pxav3: set NEED_RSP_BUSY capability
  mmc: sdhci-omap: Disable MMC_CAP_AGGRESSIVE_PM for eMMC/SD
  tty: mmc: sdio: use bool for cts and remove parentheses
  dt-bindings: mmc: sunxi: add compatible strings for Allwinner A523
  dt-bindings: mmc: sunxi: Simplify compatible string listing
  dt-bindings: mmc: sdhci-of-dwcmhsc: Add compatible string for RK3528
  dt-bindings: mmc: rockchip-dw-mshc: Add compatible string for RK3528
  mmc: renesas_sdhi: Add support for RZ/G3E SoC
  dt-bindings: mmc: renesas,sdhi: Document RZ/G3E support
  dt-bindings: mmc: rockchip-dw-mshc: Add support for rk3562
  dt-bindings: mmc: Add support for rk3562 eMMC
  mmc: core: Trim trailing whitespace from card product names
  dt-bindings: mmc: atmel,hsmci: Convert to json schema
  dt-bindings: mmc: mmc-slot: Make compatible property optional
  dt-bindings: mmc: fsl-imx-esdhc: Add i.MX94 support
  ...
This commit is contained in:
Linus Torvalds
2025-03-25 20:36:29 -07:00
30 changed files with 508 additions and 184 deletions
@@ -30,38 +30,34 @@ properties:
- const: allwinner,sun50i-a100-emmc
- const: allwinner,sun50i-a100-mmc
- items:
- const: allwinner,sun8i-a83t-mmc
- enum:
- allwinner,sun8i-a83t-mmc
- allwinner,suniv-f1c100s-mmc
- const: allwinner,sun7i-a20-mmc
- items:
- const: allwinner,sun8i-r40-emmc
- enum:
- allwinner,sun8i-r40-emmc
- allwinner,sun50i-h5-emmc
- allwinner,sun50i-h6-emmc
- const: allwinner,sun50i-a64-emmc
- items:
- const: allwinner,sun8i-r40-mmc
- enum:
- allwinner,sun8i-r40-mmc
- allwinner,sun50i-h5-mmc
- allwinner,sun50i-h6-mmc
- const: allwinner,sun50i-a64-mmc
- items:
- const: allwinner,sun50i-h5-emmc
- const: allwinner,sun50i-a64-emmc
- items:
- const: allwinner,sun50i-h5-mmc
- const: allwinner,sun50i-a64-mmc
- items:
- const: allwinner,sun50i-h6-emmc
- const: allwinner,sun50i-a64-emmc
- items:
- const: allwinner,sun50i-h6-mmc
- const: allwinner,sun50i-a64-mmc
- items:
- const: allwinner,sun20i-d1-emmc
- const: allwinner,sun50i-a100-emmc
- items:
- const: allwinner,sun50i-h616-emmc
- enum:
- allwinner,sun20i-d1-emmc
- allwinner,sun50i-h616-emmc
- allwinner,sun55i-a523-emmc
- const: allwinner,sun50i-a100-emmc
- items:
- const: allwinner,sun50i-h616-mmc
- const: allwinner,sun50i-a100-mmc
- items:
- const: allwinner,suniv-f1c100s-mmc
- const: allwinner,sun7i-a20-mmc
- const: allwinner,sun55i-a523-mmc
- const: allwinner,sun20i-d1-mmc
reg:
maxItems: 1
@@ -60,6 +60,9 @@ patternProperties:
bus-width:
enum: [1, 4]
required:
- compatible
unevaluatedProperties: false
required:
@@ -0,0 +1,106 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mmc/atmel,hsmci.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Atmel High-Speed MultiMedia Card Interface (HSMCI)
description:
The Atmel HSMCI controller provides an interface for MMC, SD, and SDIO memory
cards.
maintainers:
- Nicolas Ferre <nicolas.ferre@microchip.com>
- Aubin Constans <aubin.constans@microchip.com>
allOf:
- $ref: mmc-controller.yaml
properties:
compatible:
const: atmel,hsmci
reg:
maxItems: 1
interrupts:
maxItems: 1
dmas:
maxItems: 1
dma-names:
const: rxtx
clocks:
maxItems: 1
clock-names:
const: mci_clk
"#address-cells":
const: 1
description: Used for slot IDs.
"#size-cells":
const: 0
patternProperties:
"slot@[0-2]$":
$ref: mmc-slot.yaml
description: A slot node representing an MMC, SD, or SDIO slot.
properties:
reg:
enum: [0, 1]
required:
- reg
- bus-width
unevaluatedProperties: false
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- "#address-cells"
- "#size-cells"
anyOf:
- required:
- slot@0
- required:
- slot@1
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/clock/at91.h>
mmc@f0008000 {
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&mci0_clk>;
clock-names = "mci_clk";
#address-cells = <1>;
#size-cells = <0>;
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 0>;
cd-inverted;
};
slot@1 {
reg = <1>;
bus-width = <4>;
};
};
...
@@ -1,73 +0,0 @@
* Atmel High Speed MultiMedia Card Interface
This controller on atmel products provides an interface for MMC, SD and SDIO
types of memory cards.
This file documents differences between the core properties described
by mmc.txt and the properties used by the atmel-mci driver.
1) MCI node
Required properties:
- compatible: should be "atmel,hsmci"
- #address-cells: should be one. The cell is the slot id.
- #size-cells: should be zero.
- at least one slot node
- clock-names: tuple listing input clock names.
Required elements: "mci_clk"
- clocks: phandles to input clocks.
The node contains child nodes for each slot that the platform uses
Example MCI node:
mmc0: mmc@f0008000 {
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4>;
#address-cells = <1>;
#size-cells = <0>;
clock-names = "mci_clk";
clocks = <&mci0_clk>;
[ child node definitions...]
};
2) slot nodes
Required properties:
- reg: should contain the slot id.
- bus-width: number of data lines connected to the controller
Optional properties:
- cd-gpios: specify GPIOs for card detection
- cd-inverted: invert the value of external card detect gpio line
- wp-gpios: specify GPIOs for write protection
Example slot node:
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 0>
cd-inverted;
};
Example full MCI node:
mmc0: mmc@f0008000 {
compatible = "atmel,hsmci";
reg = <0xf0008000 0x600>;
interrupts = <12 4>;
#address-cells = <1>;
#size-cells = <0>;
slot@0 {
reg = <0>;
bus-width = <4>;
cd-gpios = <&pioD 15 0>
cd-inverted;
};
slot@1 {
reg = <1>;
bus-width = <4>;
};
};
@@ -57,6 +57,7 @@ properties:
- fsl,imx8mp-usdhc
- fsl,imx8ulp-usdhc
- fsl,imx93-usdhc
- fsl,imx94-usdhc
- fsl,imx95-usdhc
- const: fsl,imx8mm-usdhc
- items:
@@ -24,7 +24,7 @@ properties:
$nodename:
pattern: "^mmc(@.*)?$"
unevaluatedProperties: true
additionalProperties: true
examples:
- |
@@ -29,7 +29,6 @@ properties:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
@@ -68,6 +68,9 @@ properties:
- renesas,sdhi-r9a08g045 # RZ/G3S
- renesas,sdhi-r9a09g011 # RZ/V2M
- const: renesas,rzg2l-sdhi
- items:
- const: renesas,sdhi-r9a09g047 # RZ/G3E
- const: renesas,sdhi-r9a09g057 # RZ/V2H(P)
reg:
maxItems: 1
@@ -211,6 +214,19 @@ allOf:
sectioned off to be run by a separate second clock source to allow
the main core clock to be turned off to save power.
- if:
properties:
compatible:
contains:
const: renesas,sdhi-r9a09g057
then:
properties:
vqmmc-regulator:
type: object
description: VQMMC SD regulator
$ref: /schemas/regulator/regulator.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
@@ -38,6 +38,8 @@ properties:
- rockchip,rk3328-dw-mshc
- rockchip,rk3368-dw-mshc
- rockchip,rk3399-dw-mshc
- rockchip,rk3528-dw-mshc
- rockchip,rk3562-dw-mshc
- rockchip,rk3568-dw-mshc
- rockchip,rk3588-dw-mshc
- rockchip,rv1108-dw-mshc
@@ -24,6 +24,8 @@ properties:
- samsung,exynos5420-dw-mshc-smu
- samsung,exynos7-dw-mshc
- samsung,exynos7-dw-mshc-smu
- samsung,exynos7870-dw-mshc
- samsung,exynos7870-dw-mshc-smu
- items:
- enum:
- samsung,exynos5433-dw-mshc-smu
@@ -14,7 +14,10 @@ properties:
compatible:
oneOf:
- items:
- const: rockchip,rk3576-dwcmshc
- enum:
- rockchip,rk3528-dwcmshc
- rockchip,rk3562-dwcmshc
- rockchip,rk3576-dwcmshc
- const: rockchip,rk3588-dwcmshc
- enum:
- rockchip,rk3568-dwcmshc
+1
View File
@@ -813,6 +813,7 @@ static void rtsx_usb_ms_drv_remove(struct platform_device *pdev)
host->eject = true;
cancel_work_sync(&host->handle_req);
cancel_delayed_work_sync(&host->poll_card);
mutex_lock(&host->host_mutex);
if (host->req) {
+1 -1
View File
@@ -335,7 +335,7 @@ int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
if (mrq->cmd && mrq->cmd->has_ext_addr)
if (mrq->cmd->has_ext_addr)
mmc_send_ext_addr(host, mrq->cmd->ext_addr);
init_completion(&mrq->cmd_completion);
+5 -1
View File
@@ -11,6 +11,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/sysfs.h>
@@ -66,7 +67,7 @@ static int mmc_decode_cid(struct mmc_card *card)
/*
* The selection of the format here is based upon published
* specs from sandisk and from what people have reported.
* specs from SanDisk and from what people have reported.
*/
switch (card->csd.mmca_vsn) {
case 0: /* MMC v1.0 - v1.2 */
@@ -109,6 +110,9 @@ static int mmc_decode_cid(struct mmc_card *card)
return -EINVAL;
}
/* some product names include trailing whitespace */
strim(card->cid.prod_name);
return 0;
}
+4
View File
@@ -11,6 +11,7 @@
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/string.h>
#include <linux/pm_runtime.h>
#include <linux/random.h>
#include <linux/scatterlist.h>
@@ -95,6 +96,9 @@ void mmc_decode_cid(struct mmc_card *card)
card->cid.month = unstuff_bits(resp, 8, 4);
card->cid.year += 2000; /* SD cards year offset */
/* some product names may include trailing whitespace */
strim(card->cid.prod_name);
}
/*
+1 -1
View File
@@ -471,7 +471,7 @@ static void sdio_uart_check_modem_status(struct sdio_uart_port *port)
port->icount.cts++;
tty = tty_port_tty_get(&port->port);
if (tty && C_CRTSCTS(tty)) {
int cts = (status & UART_MSR_CTS);
bool cts = status & UART_MSR_CTS;
if (tty->hw_stopped) {
if (cts) {
tty->hw_stopped = false;
-12
View File
@@ -159,18 +159,6 @@ int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on)
}
EXPORT_SYMBOL(mmc_gpio_set_cd_wake);
/* Register an alternate interrupt service routine for
* the card-detect GPIO.
*/
void mmc_gpio_set_cd_isr(struct mmc_host *host, irq_handler_t isr)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
WARN_ON(ctx->cd_gpio_isr);
ctx->cd_gpio_isr = isr;
}
EXPORT_SYMBOL(mmc_gpio_set_cd_isr);
/**
* mmc_gpiod_request_cd - request a gpio descriptor for card-detection
* @host: mmc host
+40 -1
View File
@@ -27,6 +27,8 @@ enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS5420_SMU,
DW_MCI_TYPE_EXYNOS7,
DW_MCI_TYPE_EXYNOS7_SMU,
DW_MCI_TYPE_EXYNOS7870,
DW_MCI_TYPE_EXYNOS7870_SMU,
DW_MCI_TYPE_ARTPEC8,
};
@@ -69,6 +71,12 @@ static struct dw_mci_exynos_compatible {
}, {
.compatible = "samsung,exynos7-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
}, {
.compatible = "samsung,exynos7870-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS7870,
}, {
.compatible = "samsung,exynos7870-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS7870_SMU,
}, {
.compatible = "axis,artpec8-dw-mshc",
.ctrl_type = DW_MCI_TYPE_ARTPEC8,
@@ -85,6 +93,8 @@ static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host)
return EXYNOS4210_FIXED_CIU_CLK_DIV;
else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1;
else
@@ -100,7 +110,8 @@ static void dw_mci_exynos_config_smu(struct dw_mci *host)
* set for non-ecryption mode at this time.
*/
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX);
mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT |
@@ -126,6 +137,12 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl);
}
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) {
/* Quirk needed for certain Exynos SoCs */
host->quirks |= DW_MMC_QUIRK_FIFO64_32;
}
if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) {
/* Quirk needed for the ARTPEC-8 SoC */
host->quirks |= DW_MMC_QUIRK_EXTENDED_TMOUT;
@@ -143,6 +160,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -152,6 +171,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -222,6 +243,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -230,6 +253,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -409,6 +434,8 @@ static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
else
@@ -422,6 +449,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -429,6 +458,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample);
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -443,6 +474,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
clksel = mci_readl(host, CLKSEL64);
else
@@ -453,6 +486,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU ||
priv->ctrl_type == DW_MCI_TYPE_ARTPEC8)
mci_writel(host, CLKSEL64, clksel);
else
@@ -632,6 +667,10 @@ static const struct of_device_id dw_mci_exynos_match[] = {
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7-dw-mshc-smu",
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7870-dw-mshc",
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7870-dw-mshc-smu",
.data = &exynos_drv_data, },
{ .compatible = "axis,artpec8-dw-mshc",
.data = &artpec_drv_data, },
{},
+92 -2
View File
@@ -2578,6 +2578,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt)
}
}
static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt)
{
struct mmc_data *data = host->data;
int init_cnt = cnt;
/* try and push anything in the part_buf */
if (unlikely(host->part_buf_count)) {
int len = dw_mci_push_part_bytes(host, buf, cnt);
buf += len;
cnt -= len;
if (host->part_buf_count == 8) {
mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
host->part_buf_count = 0;
}
}
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
if (unlikely((unsigned long)buf & 0x7)) {
while (cnt >= 8) {
u64 aligned_buf[16];
int len = min(cnt & -8, (int)sizeof(aligned_buf));
int items = len >> 3;
int i;
/* memcpy from input buffer into aligned buffer */
memcpy(aligned_buf, buf, len);
buf += len;
cnt -= len;
/* push data from aligned buffer into fifo */
for (i = 0; i < items; ++i)
mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]);
}
} else
#endif
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
mci_fifo_l_writeq(host->fifo_reg, *pdata++);
buf = pdata;
}
/* put anything remaining in the part_buf */
if (cnt) {
dw_mci_set_part_bytes(host, buf, cnt);
/* Push data if we have reached the expected data length */
if ((data->bytes_xfered + init_cnt) ==
(data->blksz * data->blocks))
mci_fifo_l_writeq(host->fifo_reg, host->part_buf);
}
}
static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt)
{
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
if (unlikely((unsigned long)buf & 0x7)) {
while (cnt >= 8) {
/* pull data from fifo into aligned buffer */
u64 aligned_buf[16];
int len = min(cnt & -8, (int)sizeof(aligned_buf));
int items = len >> 3;
int i;
for (i = 0; i < items; ++i)
aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg);
/* memcpy from aligned buffer into output buffer */
memcpy(buf, aligned_buf, len);
buf += len;
cnt -= len;
}
} else
#endif
{
u64 *pdata = buf;
for (; cnt >= 8; cnt -= 8)
*pdata++ = mci_fifo_l_readq(host->fifo_reg);
buf = pdata;
}
if (cnt) {
host->part_buf = mci_fifo_l_readq(host->fifo_reg);
dw_mci_pull_final_bytes(host, buf, cnt);
}
}
static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt)
{
int len;
@@ -3378,8 +3463,13 @@ int dw_mci_probe(struct dw_mci *host)
width = 16;
host->data_shift = 1;
} else if (i == 2) {
host->push_data = dw_mci_push_data64;
host->pull_data = dw_mci_pull_data64;
if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) {
host->push_data = dw_mci_push_data64_32;
host->pull_data = dw_mci_pull_data64_32;
} else {
host->push_data = dw_mci_push_data64;
host->pull_data = dw_mci_pull_data64;
}
width = 64;
host->data_shift = 3;
} else {
+27
View File
@@ -281,6 +281,8 @@ struct dw_mci_board {
/* Support for longer data read timeout */
#define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0)
/* Force 32-bit access to the FIFO */
#define DW_MMC_QUIRK_FIFO64_32 BIT(1)
#define DW_MMC_240A 0x240a
#define DW_MMC_280A 0x280a
@@ -472,6 +474,31 @@ struct dw_mci_board {
#define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value)
#define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value)
/*
* Some dw_mmc devices have 64-bit FIFOs, but expect them to be
* accessed using two 32-bit accesses. If such controller is used
* with a 64-bit kernel, this has to be done explicitly.
*/
static inline u64 mci_fifo_l_readq(void __iomem *addr)
{
u64 ans;
u32 proxy[2];
proxy[0] = mci_fifo_readl(addr);
proxy[1] = mci_fifo_readl(addr + 4);
memcpy(&ans, proxy, 8);
return ans;
}
static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value)
{
u32 proxy[2];
memcpy(proxy, &value, 8);
mci_fifo_writel(addr, proxy[0]);
mci_fifo_writel(addr + 4, proxy[1]);
}
/* Register access macros */
#define mci_readl(dev, reg) \
readl_relaxed((dev)->regs + SDMMC_##reg)
+13 -6
View File
@@ -1272,19 +1272,25 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
/* Check for some optional GPIO controls */
slot->vsd = devm_gpiod_get_index_optional(host->dev, "vsd",
id, GPIOD_OUT_LOW);
if (IS_ERR(slot->vsd))
return dev_err_probe(host->dev, PTR_ERR(slot->vsd),
if (IS_ERR(slot->vsd)) {
r = dev_err_probe(host->dev, PTR_ERR(slot->vsd),
"error looking up VSD GPIO\n");
goto err_free_host;
}
slot->vio = devm_gpiod_get_index_optional(host->dev, "vio",
id, GPIOD_OUT_LOW);
if (IS_ERR(slot->vio))
return dev_err_probe(host->dev, PTR_ERR(slot->vio),
if (IS_ERR(slot->vio)) {
r = dev_err_probe(host->dev, PTR_ERR(slot->vio),
"error looking up VIO GPIO\n");
goto err_free_host;
}
slot->cover = devm_gpiod_get_index_optional(host->dev, "cover",
id, GPIOD_IN);
if (IS_ERR(slot->cover))
return dev_err_probe(host->dev, PTR_ERR(slot->cover),
if (IS_ERR(slot->cover)) {
r = dev_err_probe(host->dev, PTR_ERR(slot->cover),
"error looking up cover switch GPIO\n");
goto err_free_host;
}
host->slots[id] = slot;
@@ -1344,6 +1350,7 @@ err_remove_slot_name:
device_remove_file(&mmc->class_dev, &dev_attr_slot_name);
err_remove_host:
mmc_remove_host(mmc);
err_free_host:
mmc_free_host(mmc);
return r;
}
+1
View File
@@ -95,6 +95,7 @@ struct renesas_sdhi {
struct reset_control *rstc;
struct tmio_mmc_host *host;
struct regulator_dev *rdev;
};
#define host_to_priv(host) \
+131
View File
@@ -32,6 +32,8 @@
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/reset.h>
#include <linux/sh_dma.h>
#include <linux/slab.h>
@@ -581,12 +583,24 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve)
if (!preserve) {
if (priv->rstc) {
u32 sd_status;
/*
* HW reset might have toggled the regulator state in
* HW which regulator core might be unaware of so save
* and restore the regulator state during HW reset.
*/
if (priv->rdev)
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
reset_control_reset(priv->rstc);
/* Unknown why but without polling reset status, it will hang */
read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100,
false, priv->rstc);
/* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
if (priv->rdev)
sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
priv->needs_adjust_hs400 = false;
renesas_sdhi_set_clock(host, host->clk_cache);
@@ -904,6 +918,102 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable)
renesas_sdhi_sdbuf_width(host, enable ? width : 16);
}
static const unsigned int renesas_sdhi_vqmmc_voltages[] = {
3300000, 1800000
};
static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev)
{
struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
u32 sd_status;
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
sd_status &= ~SD_STATUS_PWEN;
sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
return 0;
}
static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev)
{
struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
u32 sd_status;
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
sd_status |= SD_STATUS_PWEN;
sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
return 0;
}
static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev)
{
struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
u32 sd_status;
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
return (sd_status & SD_STATUS_PWEN) ? 1 : 0;
}
static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev)
{
struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
u32 sd_status;
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000;
}
static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV,
unsigned int *selector)
{
struct tmio_mmc_host *host = rdev_get_drvdata(rdev);
u32 sd_status;
sd_status = sd_ctrl_read32(host, CTL_SD_STATUS);
if (min_uV >= 1700000 && max_uV <= 1950000) {
sd_status |= SD_STATUS_IOVS;
*selector = 1;
} else {
sd_status &= ~SD_STATUS_IOVS;
*selector = 0;
}
sd_ctrl_write32(host, CTL_SD_STATUS, sd_status);
return 0;
}
static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev,
unsigned int selector)
{
if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages))
return -EINVAL;
return renesas_sdhi_vqmmc_voltages[selector];
}
static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = {
.enable = renesas_sdhi_regulator_enable,
.disable = renesas_sdhi_regulator_disable,
.is_enabled = renesas_sdhi_regulator_is_enabled,
.list_voltage = renesas_sdhi_regulator_list_voltage,
.get_voltage = renesas_sdhi_regulator_get_voltage,
.set_voltage = renesas_sdhi_regulator_set_voltage,
};
static const struct regulator_desc renesas_sdhi_vqmmc_regulator = {
.name = "sdhi-vqmmc-regulator",
.of_match = of_match_ptr("vqmmc-regulator"),
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.ops = &renesas_sdhi_regulator_voltage_ops,
.volt_table = renesas_sdhi_vqmmc_voltages,
.n_voltages = ARRAY_SIZE(renesas_sdhi_vqmmc_voltages),
};
int renesas_sdhi_probe(struct platform_device *pdev,
const struct tmio_mmc_dma_ops *dma_ops,
const struct renesas_sdhi_of_data *of_data,
@@ -911,7 +1021,10 @@ int renesas_sdhi_probe(struct platform_device *pdev,
{
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
struct tmio_mmc_data *mmc_data;
struct regulator_config rcfg = { .dev = &pdev->dev, };
struct regulator_dev *rdev;
struct renesas_sdhi_dma *dma_priv;
struct device *dev = &pdev->dev;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -1053,6 +1166,24 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ret)
goto efree;
rcfg.of_node = of_get_child_by_name(dev->of_node, "vqmmc-regulator");
if (!of_device_is_available(rcfg.of_node)) {
of_node_put(rcfg.of_node);
rcfg.of_node = NULL;
}
if (rcfg.of_node) {
rcfg.driver_data = priv->host;
rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg);
of_node_put(rcfg.of_node);
if (IS_ERR(rdev)) {
dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev));
ret = PTR_ERR(rdev);
goto efree;
}
priv->rdev = rdev;
}
ver = sd_ctrl_read16(host, CTL_VERSION);
/* GEN2_SDR104 is first known SDHI to use 32bit block count */
if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX)
+15 -57
View File
@@ -328,12 +328,17 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq)
sdhci_request(mmc, mrq);
}
static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
static void dwcmshc_phy_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 rxsel = PHY_PAD_RXSEL_3V3;
u32 val;
if (priv->flags & FLAG_IO_FIXED_1V8 ||
host->mmc->ios.timing & MMC_SIGNAL_VOLTAGE_180)
rxsel = PHY_PAD_RXSEL_1V8;
/* deassert phy reset & set tx drive strength */
val = PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
@@ -353,7 +358,7 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* configure phy pads */
val = PHY_PAD_RXSEL_1V8;
val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
@@ -365,65 +370,22 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host)
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
val = PHY_PAD_RXSEL_1V8;
val = rxsel;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* enable data strobe mode */
sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL),
PHY_DLLDL_CNFG_R);
if (rxsel == PHY_PAD_RXSEL_1V8) {
u8 sel = FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL);
sdhci_writeb(host, sel, PHY_DLLDL_CNFG_R);
}
/* enable phy dll */
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
}
static void dwcmshc_phy_3_3v_init(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
u32 val;
/* deassert phy reset & set tx drive strength */
val = PHY_CNFG_RSTN_DEASSERT;
val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
sdhci_writel(host, val, PHY_CNFG_R);
/* disable delay line */
sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
/* set delay line */
sdhci_writeb(host, priv->delay_line, PHY_SDCLKDL_DC_R);
sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
/* enable delay lane */
val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
/* configure phy pads */
val = PHY_PAD_RXSEL_3V3;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
val = PHY_PAD_RXSEL_3V3;
val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
/* enable phy dll */
sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
}
static void th1520_sdhci_set_phy(struct sdhci_host *host)
@@ -433,11 +395,7 @@ static void th1520_sdhci_set_phy(struct sdhci_host *host)
u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO;
u16 emmc_ctrl;
/* Before power on, set PHY configs */
if (priv->flags & FLAG_IO_FIXED_1V8)
dwcmshc_phy_1_8v_init(host);
else
dwcmshc_phy_3_3v_init(host);
dwcmshc_phy_init(host);
if ((host->mmc->caps2 & emmc_caps) == emmc_caps) {
emmc_ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
@@ -1163,7 +1121,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = {
.get_max_clock = dwcmshc_get_max_clock,
.reset = th1520_sdhci_reset,
.adma_write_desc = dwcmshc_adma_write_desc,
.voltage_switch = dwcmshc_phy_1_8v_init,
.voltage_switch = dwcmshc_phy_init,
.platform_execute_tuning = th1520_execute_tuning,
};
+2 -2
View File
@@ -1339,8 +1339,8 @@ static int sdhci_omap_probe(struct platform_device *pdev)
/* R1B responses is required to properly manage HW busy detection. */
mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
/* Allow card power off and runtime PM for eMMC/SD card devices */
mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM;
/* Enable SDIO card power off. */
mmc->caps |= MMC_CAP_POWER_OFF_CARD;
ret = sdhci_setup_host(host);
if (ret)
+5 -1
View File
@@ -610,8 +610,12 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode,
sdhci_set_power(host, mode, vdd);
if (mode == MMC_POWER_OFF)
if (mode == MMC_POWER_OFF) {
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD ||
slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD)
usleep_range(15000, 17500);
return;
}
/*
* Bus power might not enable after D3 -> D0 transition due to the
+1
View File
@@ -399,6 +399,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
if (!IS_ERR(pxa->clk_core))
clk_prepare_enable(pxa->clk_core);
host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY;
/* enable 1/8V DDR capable */
host->mmc->caps |= MMC_CAP_1_8V_DDR;
+7 -2
View File
@@ -2065,10 +2065,15 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
host->mmc->actual_clock = 0;
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
if (clk & SDHCI_CLOCK_CARD_EN)
sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN,
SDHCI_CLOCK_CONTROL);
if (clock == 0)
if (clock == 0) {
sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
return;
}
clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
sdhci_enable_clk(host, clk);
+10
View File
@@ -44,6 +44,7 @@
#define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2
#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */
#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */
/* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */
#define TMIO_STOP_STP BIT(0)
@@ -103,6 +104,10 @@
/* Definitions for values the CTL_SDIF_MODE register can take */
#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */
/* Definitions for values the CTL_SD_STATUS register can take */
#define SD_STATUS_PWEN BIT(0) /* only known on RZ/{G3E,V2H} */
#define SD_STATUS_IOVS BIT(16) /* only known on RZ/{G3E,V2H} */
/* Define some IRQ masks */
/* This is the mask used at reset by the chip */
#define TMIO_MASK_ALL 0x837f031d
@@ -226,6 +231,11 @@ static inline u32 sd_ctrl_read16_and_16_as_32(struct tmio_mmc_host *host,
ioread16(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
}
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
{
return ioread32(host->ctl + (addr << host->bus_shift));
}
static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr,
u32 *buf, int count)
{
-1
View File
@@ -22,7 +22,6 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int idx, unsigned int debounce);
int mmc_gpiod_set_cd_config(struct mmc_host *host, unsigned long config);
void mmc_gpio_set_cd_isr(struct mmc_host *host, irq_handler_t isr);
int mmc_gpio_set_cd_wake(struct mmc_host *host, bool on);
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
bool mmc_can_gpio_cd(struct mmc_host *host);