mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-08-18 02:58:57 +08:00
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
+375
-119
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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__ */
|
||||
Reference in New Issue
Block a user