[dm][sdio] Update for SDHCI- #11684 #11684

This commit is contained in:
GUI
2026-08-14 12:39:11 +08:00
committed by GitHub
parent 76b0267361
commit 4cf70e176c
10 changed files with 1258 additions and 458 deletions
File diff suppressed because it is too large Load Diff
+44 -14
View File
@@ -10,11 +10,11 @@
#include "dev_sdio_dm.h"
#define DBG_TAG "SDIO"
#define DBG_TAG "SDIO"
#ifdef RT_SDIO_DEBUG
#define DBG_LVL DBG_LOG
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_INFO
#define DBG_LVL DBG_INFO
#endif /* RT_SDIO_DEBUG */
#include <rtdbg.h>
@@ -44,7 +44,7 @@ static rt_err_t ocrbitnum_to_vdd(int vdd_bit, int *min_uvolt, int *max_uvolt)
}
rt_err_t sdio_regulator_set_ocr(struct rt_mmcsd_host *host,
struct rt_regulator *supply, rt_uint16_t vdd_bit)
struct rt_regulator *supply, rt_uint16_t vdd_bit)
{
rt_err_t err = RT_EOK;
@@ -94,10 +94,26 @@ rt_err_t sdio_regulator_set_ocr(struct rt_mmcsd_host *host,
return err;
}
static int regulator_set_voltage_if_supported(struct rt_regulator *regulator,
int min_uvolt, int target_uvolt, int max_uvolt)
rt_bool_t sdio_regulator_supports_vqmmc_voltage(struct rt_regulator *regulator,
int min_uvolt, int target_uvolt, int max_uvolt)
{
if (!rt_regulator_is_supported_voltage(regulator, min_uvolt, max_uvolt))
if (!regulator)
{
return RT_FALSE;
}
if (rt_regulator_is_supported_voltage(regulator, min_uvolt, max_uvolt))
{
return RT_TRUE;
}
return rt_regulator_is_supported_voltage(regulator, target_uvolt, target_uvolt);
}
static int regulator_set_voltage_if_supported(struct rt_regulator *regulator,
int min_uvolt, int target_uvolt, int max_uvolt)
{
if (!regulator)
{
return -RT_EINVAL;
}
@@ -107,12 +123,26 @@ static int regulator_set_voltage_if_supported(struct rt_regulator *regulator,
return RT_EOK;
}
return rt_regulator_set_voltage_triplet(regulator, min_uvolt, target_uvolt,
max_uvolt);
if (rt_regulator_is_supported_voltage(regulator, min_uvolt, max_uvolt))
{
return rt_regulator_set_voltage_triplet(regulator, min_uvolt, target_uvolt,
max_uvolt);
}
/*
* gpio regulator may declare min above the SDHCI nominal range
* (e.g. 1.8V only) while still supporting the target voltage.
*/
if (rt_regulator_is_supported_voltage(regulator, target_uvolt, target_uvolt))
{
return rt_regulator_set_voltage(regulator, target_uvolt, target_uvolt);
}
return -RT_EINVAL;
}
rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host,
struct rt_mmcsd_io_cfg *ios)
struct rt_mmcsd_io_cfg *ios)
{
rt_err_t err;
int uvolt, min_uvolt, max_uvolt;
@@ -126,11 +156,11 @@ rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host,
{
case MMCSD_SIGNAL_VOLTAGE_120:
return regulator_set_voltage_if_supported(host->supply.vqmmc,
1100000, 1200000, 1300000);
1100000, 1200000, 1300000);
case MMCSD_SIGNAL_VOLTAGE_180:
return regulator_set_voltage_if_supported(host->supply.vqmmc,
1700000, 1800000, 1950000);
1700000, 1800000, 1950000);
case MMCSD_SIGNAL_VOLTAGE_330:
err = ocrbitnum_to_vdd(host->io_cfg.vdd, &uvolt, &max_uvolt);
@@ -144,14 +174,14 @@ rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host,
max_uvolt = rt_min(max_uvolt + 200000, 3600000);
err = regulator_set_voltage_if_supported(host->supply.vqmmc,
min_uvolt, uvolt, max_uvolt);
min_uvolt, uvolt, max_uvolt);
if (err >= 0)
{
return err;
}
return regulator_set_voltage_if_supported(host->supply.vqmmc,
2700000, uvolt, 3600000);
2700000, uvolt, 3600000);
default:
return -RT_EINVAL;
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -19,9 +19,11 @@ int sdio_host_set_name(struct rt_mmcsd_host *host, char *out_devname);
#ifdef RT_USING_REGULATOR
rt_err_t sdio_regulator_set_ocr(struct rt_mmcsd_host *host,
struct rt_regulator *supply, rt_uint16_t vdd_bit);
struct rt_regulator *supply, rt_uint16_t vdd_bit);
rt_err_t sdio_regulator_set_vqmmc(struct rt_mmcsd_host *host,
struct rt_mmcsd_io_cfg *ios);
struct rt_mmcsd_io_cfg *ios);
rt_bool_t sdio_regulator_supports_vqmmc_voltage(struct rt_regulator *regulator,
int min_uvolt, int target_uvolt, int max_uvolt);
rt_err_t sdio_regulator_get_supply(struct rt_device *dev, struct rt_mmcsd_host *host);
rt_err_t sdio_regulator_enable_vqmmc(struct rt_mmcsd_host *host);
void sdio_regulator_disable_vqmmc(struct rt_mmcsd_host *host);
+6
View File
@@ -19,4 +19,10 @@ config RT_SDIO_DW_MMC_PCI
depends on RT_USING_PCI
default n
config RT_SDIO_SDHCI_DWCMSHC
bool "Synopsys DWC MSHC SDHCI"
depends on RT_USING_OFW
select RT_USING_SDHCI
default n
osource "$(SOC_DM_SDIO_DIR)/Kconfig"
+3
View File
@@ -19,6 +19,9 @@ if GetDepend(['RT_SDIO_DW_MMC']):
if GetDepend(['RT_SDIO_DW_MMC_PCI']):
src += ['sdio-dw-pci.c']
if GetDepend(['RT_SDIO_SDHCI_DWCMSHC']):
src += ['sdhci-dwcmshc.c', 'sdhci-dwcmshc-platform.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-30 RT-Thread first version
*/
#include "sdhci-dwcmshc-platform.h"
rt_err_t sdhci_dwcmshc_platform_register(struct rt_platform_device *pdev,
const struct sdhci_dwcmshc_drv_data *drv_data)
{
return sdhci_dwcmshc_probe(pdev, drv_data);
}
static rt_err_t sdhci_dwcmshc_platform_probe(struct rt_platform_device *pdev)
{
const struct sdhci_dwcmshc_drv_data *drv_data = RT_NULL;
if (pdev->parent.ofw_node)
{
drv_data = pdev->id->data;
}
if (!drv_data)
{
drv_data = &sdhci_dwcmshc_generic_drv_data;
}
return sdhci_dwcmshc_platform_register(pdev, drv_data);
}
static rt_err_t sdhci_dwcmshc_platform_remove(struct rt_platform_device *pdev)
{
return sdhci_dwcmshc_remove(pdev);
}
static const struct rt_ofw_node_id sdhci_dwcmshc_platform_ofw_ids[] = {
{
.compatible = "snps,dwcmshc-sdhci",
.data = &sdhci_dwcmshc_generic_drv_data,
},
{ /* sentinel */ }
};
static struct rt_platform_driver sdhci_dwcmshc_platform_driver = {
.name = "sdhci-dwcmshc",
.ids = sdhci_dwcmshc_platform_ofw_ids,
.probe = sdhci_dwcmshc_platform_probe,
.remove = sdhci_dwcmshc_platform_remove,
};
RT_PLATFORM_DRIVER_EXPORT(sdhci_dwcmshc_platform_driver);
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-30 RT-Thread first version
*/
#ifndef __SDHCI_DWCMSHC_PLATFORM_H__
#define __SDHCI_DWCMSHC_PLATFORM_H__
#include "sdhci-dwcmshc.h"
rt_err_t sdhci_dwcmshc_platform_register(struct rt_platform_device *pdev,
const struct sdhci_dwcmshc_drv_data *drv_data);
#endif /* __SDHCI_DWCMSHC_PLATFORM_H__ */
@@ -0,0 +1,359 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-30 RT-Thread first version
*/
#include "sdhci-dwcmshc.h"
#include <drivers/dev_sdhci_host.h>
#define DBG_TAG "sdhci.dwcmshc"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
static unsigned int sdhci_dwcmshc_get_max_clock(struct rt_sdhci_host *host)
{
struct rt_sdhci_pltfm_host *pltfm_host = rt_sdhci_priv(host);
if (pltfm_host->clk)
{
return rt_sdhci_pltfm_clk_get_max_clock(host);
}
return pltfm_host->clock;
}
static void sdhci_dwcmshc_check_auto_cmd23(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq)
{
struct rt_sdhci_host *host = rt_mmc_priv(mmc);
/*
* No matter V4 is enabled or not, ARGUMENT2 register is 32-bit
* block count register which doesn't support stuff bits of
* CMD23 argument on dwcmshc host controller.
*/
if (mrq->sbc && (mrq->sbc->arg & SDHCI_DWCMSHC_ARG2_STUFF))
{
host->flags &= ~RT_SDHCI_AUTO_CMD23;
}
else
{
host->flags |= RT_SDHCI_AUTO_CMD23;
}
}
static void sdhci_dwcmshc_request(struct rt_mmc_host *mmc, struct rt_mmcsd_req *mrq)
{
sdhci_dwcmshc_check_auto_cmd23(mmc, mrq);
rt_sdhci_start_request(mmc, mrq);
}
void sdhci_dwcmshc_set_uhs_signaling(struct rt_sdhci_host *host, unsigned int timing)
{
rt_uint16_t ctrl_2;
rt_uint32_t ctrl;
struct rt_sdhci_pltfm_host *pltfm_host = rt_sdhci_priv(host);
struct sdhci_dwcmshc *priv = rt_sdhci_pltfm_priv(pltfm_host);
ctrl_2 = rt_sdhci_readw(host, RT_SDHCI_HOST_CONTROL2);
/* Select Bus Speed Mode for host */
ctrl_2 &= ~RT_SDHCI_CTRL_UHS_MASK;
if (timing == MMC_TIMING_MMC_HS200 || timing == MMC_TIMING_UHS_SDR104)
{
ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR104;
}
else if (timing == MMC_TIMING_UHS_SDR12)
{
ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR12;
}
else if (timing == MMC_TIMING_UHS_SDR25 || timing == MMC_TIMING_MMC_HS)
{
ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR25;
}
else if (timing == MMC_TIMING_UHS_SDR50)
{
ctrl_2 |= RT_SDHCI_CTRL_UHS_SDR50;
}
else if (timing == MMC_TIMING_UHS_DDR50 || timing == MMC_TIMING_MMC_DDR52)
{
ctrl_2 |= RT_SDHCI_CTRL_UHS_DDR50;
}
else if (timing == MMC_TIMING_MMC_HS400)
{
/* Set CARD_IS_EMMC bit to enable Data Strobe for HS400 */
ctrl = rt_sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
ctrl |= DWCMSHC_CARD_IS_EMMC;
rt_sdhci_writel(host, ctrl, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL);
ctrl_2 |= DWCMSHC_CTRL_HS400;
}
rt_sdhci_writew(host, ctrl_2, RT_SDHCI_HOST_CONTROL2);
}
static void sdhci_dwcmshc_hs400_enhanced_strobe(struct rt_mmc_host *mmc, struct rt_mmcsd_io_cfg *ios)
{
int reg;
rt_uint32_t vendor;
struct rt_sdhci_host *host = rt_mmc_priv(mmc);
struct rt_sdhci_pltfm_host *pltfm_host = rt_sdhci_priv(host);
struct sdhci_dwcmshc *priv = rt_sdhci_pltfm_priv(pltfm_host);
reg = priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL;
vendor = rt_sdhci_readl(host, reg);
if (ios->enhanced_strobe)
{
vendor |= DWCMSHC_ENHANCED_STROBE;
}
else
{
vendor &= ~DWCMSHC_ENHANCED_STROBE;
}
rt_sdhci_writel(host, vendor, reg);
}
static int sdhci_dwcmshc_execute_tuning(struct rt_mmc_host *mmc, rt_uint32_t opcode)
{
int err;
struct rt_sdhci_host *host = rt_mmc_priv(mmc);
if ((err = rt_sdhci_execute_tuning(mmc, opcode)))
{
return err;
}
/*
* Tuning can leave the IP in an active state (Buffer Read Enable bit
* set) which prevents the entry to low power states (i.e. S0i3). Data
* reset will clear it.
*/
rt_sdhci_reset(host, RT_SDHCI_RESET_DATA);
return 0;
}
static void sdhci_dwcmshc_disable_card_clk(struct rt_sdhci_host *host)
{
rt_uint16_t ctrl;
ctrl = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
if (ctrl & RT_SDHCI_CLOCK_CARD_EN)
{
ctrl &= ~RT_SDHCI_CLOCK_CARD_EN;
rt_sdhci_writew(host, ctrl, RT_SDHCI_CLOCK_CONTROL);
}
}
static const struct rt_sdhci_ops sdhci_dwcmshc_ops = {
.set_clock = rt_sdhci_set_clock,
.set_bus_width = rt_sdhci_set_bus_width,
.set_uhs_signaling = sdhci_dwcmshc_set_uhs_signaling,
.get_max_clock = sdhci_dwcmshc_get_max_clock,
.reset = rt_sdhci_reset,
};
static const struct rt_sdhci_pltfm_data sdhci_dwcmshc_pdata = {
.ops = &sdhci_dwcmshc_ops,
.quirks = RT_SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = RT_SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
};
const struct sdhci_dwcmshc_drv_data sdhci_dwcmshc_generic_drv_data = {
.pdata = &sdhci_dwcmshc_pdata,
};
#ifdef RT_USING_PM
static rt_err_t sdhci_dwcmshc_pm_suspend(const struct rt_device *device, rt_uint8_t mode)
{
rt_uint16_t data;
struct rt_sdhci_host *host = device->user_data;
data = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
data &= ~RT_SDHCI_CLOCK_CARD_EN;
rt_sdhci_writew(host, data, RT_SDHCI_CLOCK_CONTROL);
return RT_EOK;
}
static void sdhci_dwcmshc_pm_resume(const struct rt_device *device, rt_uint8_t mode)
{
rt_uint16_t data;
struct rt_sdhci_host *host = device->user_data;
data = rt_sdhci_readw(host, RT_SDHCI_CLOCK_CONTROL);
data |= RT_SDHCI_CLOCK_CARD_EN;
rt_sdhci_writew(host, data, RT_SDHCI_CLOCK_CONTROL);
}
static const struct rt_device_pm_ops sdhci_dwcmshc_pm_ops = {
.suspend = sdhci_dwcmshc_pm_suspend,
.resume = sdhci_dwcmshc_pm_resume,
};
#endif /* RT_USING_PM */
rt_err_t sdhci_dwcmshc_probe(struct rt_platform_device *pdev,
const struct sdhci_dwcmshc_drv_data *drv_data)
{
rt_err_t err;
struct rt_device *dev = &pdev->parent;
struct rt_sdhci_pltfm_host *pltfm_host;
struct rt_sdhci_host *host;
struct sdhci_dwcmshc *priv;
if (!drv_data || !drv_data->pdata)
{
return -RT_EINVAL;
}
host = rt_sdhci_pltfm_init(pdev, drv_data->pdata, sizeof(struct sdhci_dwcmshc));
if (rt_is_err(host))
{
return rt_ptr_err(host);
}
pltfm_host = rt_sdhci_priv(host);
priv = rt_sdhci_pltfm_priv(pltfm_host);
priv->drv_data = drv_data;
pltfm_host->clk = rt_clk_get_by_name(dev, "core");
if (rt_is_err(pltfm_host->clk))
{
err = rt_ptr_err(pltfm_host->clk);
LOG_E("failed to get core clk %s", rt_strerror(err));
goto _free_pltfm;
}
err = rt_clk_prepare_enable(pltfm_host->clk);
if (err)
{
goto _err_clk;
}
priv->bus_clk = rt_clk_get_by_name(dev, "bus");
if (!rt_is_err(priv->bus_clk))
{
err = rt_clk_prepare_enable(priv->bus_clk);
if (err)
{
LOG_E("failed to enable bus clk %s", rt_strerror(err));
goto _err_core_clk;
}
}
err = rt_mmc_of_parse(host->mmc);
if (err)
{
goto _err_clk;
}
rt_sdhci_get_of_property(pdev);
priv->vendor_specific_area1 = rt_sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK;
host->mmc_host_ops.request = sdhci_dwcmshc_request;
host->mmc_host_ops.hs400_enhanced_strobe = sdhci_dwcmshc_hs400_enhanced_strobe;
host->mmc_host_ops.execute_tuning = sdhci_dwcmshc_execute_tuning;
if (drv_data->init)
{
if ((err = drv_data->init(host, priv)))
{
goto _err_clk;
}
}
if (rt_sdhci_readl(host, RT_SDHCI_CAPABILITIES) & RT_SDHCI_CAN_64BIT_V4)
{
rt_sdhci_enable_v4_mode(host);
}
err = rt_sdhci_setup_host(host);
if (err)
{
goto _err_setup_host;
}
if (drv_data->postinit)
{
drv_data->postinit(host, priv);
}
err = rt_sdhci_init_host(host);
if (err)
{
goto _err_setup_host;
}
#ifdef RT_USING_PM
dev->user_data = host;
rt_pm_device_register(dev, &sdhci_dwcmshc_pm_ops);
#endif
return RT_EOK;
_err_setup_host:
rt_sdhci_cleanup_host(host);
_err_clk:
if (drv_data->remove)
{
drv_data->remove(host, priv);
}
if (!rt_is_err(priv->bus_clk))
{
rt_clk_disable_unprepare(priv->bus_clk);
}
_err_core_clk:
if (!rt_is_err(pltfm_host->clk))
{
rt_clk_disable_unprepare(pltfm_host->clk);
}
_free_pltfm:
rt_sdhci_pltfm_free(pdev);
return err;
}
rt_err_t sdhci_dwcmshc_remove(struct rt_platform_device *pdev)
{
struct rt_sdhci_host *host = pdev->priv;
struct rt_sdhci_pltfm_host *pltfm_host = rt_sdhci_priv(host);
struct sdhci_dwcmshc *priv = rt_sdhci_pltfm_priv(pltfm_host);
#ifdef RT_USING_PM
rt_pm_device_unregister(&pdev->parent);
#endif
rt_sdhci_uninit_host(host, 0);
sdhci_dwcmshc_disable_card_clk(host);
if (priv->drv_data && priv->drv_data->remove)
{
priv->drv_data->remove(host, priv);
}
rt_clk_disable_unprepare(pltfm_host->clk);
if (!rt_is_err(priv->bus_clk))
{
rt_clk_disable_unprepare(priv->bus_clk);
}
rt_sdhci_pltfm_free(pdev);
return RT_EOK;
}
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-01-30 RT-Thread first version
*/
#ifndef __SDHCI_DWCMSHC_H__
#define __SDHCI_DWCMSHC_H__
#include "../dev_sdhci_dm.h"
#define SDHCI_DWCMSHC_ARG2_STUFF RT_GENMASK(31, 16)
/* DWCMSHC specific Mode Select value */
#define DWCMSHC_CTRL_HS400 0x7
/* DWC IP vendor area 1 pointer */
#define DWCMSHC_P_VENDOR_AREA1 0xe8
#define DWCMSHC_AREA1_MASK RT_GENMASK(11, 0)
/* Offset inside the vendor area 1 */
#define DWCMSHC_HOST_CTRL3 0x8
#define DWCMSHC_EMMC_CONTROL 0x2c
#define DWCMSHC_CARD_IS_EMMC RT_BIT(0)
#define DWCMSHC_ENHANCED_STROBE RT_BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40
/* DWC IP vendor area 2 pointer */
#define DWCMSHC_P_VENDOR_AREA2 0xea
struct sdhci_dwcmshc;
struct sdhci_dwcmshc_drv_data
{
const struct rt_sdhci_pltfm_data *pdata;
rt_err_t (*init)(struct rt_sdhci_host *host, struct sdhci_dwcmshc *priv);
void (*postinit)(struct rt_sdhci_host *host, struct sdhci_dwcmshc *priv);
void (*remove)(struct rt_sdhci_host *host, struct sdhci_dwcmshc *priv);
void *vendor_priv;
};
struct sdhci_dwcmshc
{
struct rt_clk *bus_clk;
/* P_VENDOR_SPECIFIC_AREA1/2 reg */
int vendor_specific_area1;
int vendor_specific_area2;
void *vendor_priv;
const struct sdhci_dwcmshc_drv_data *drv_data;
};
extern const struct sdhci_dwcmshc_drv_data sdhci_dwcmshc_generic_drv_data;
rt_err_t sdhci_dwcmshc_probe(struct rt_platform_device *pdev,
const struct sdhci_dwcmshc_drv_data *drv_data);
rt_err_t sdhci_dwcmshc_remove(struct rt_platform_device *pdev);
void sdhci_dwcmshc_set_uhs_signaling(struct rt_sdhci_host *host, unsigned int timing);
#endif /* __SDHCI_DWCMSHC_H__ */