2023-10-17 14:41:17 +08:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2023 Intel Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT cdns_sdhc
|
|
|
|
|
|
|
|
#include <zephyr/drivers/sdhc.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
#include <zephyr/drivers/clock_control.h>
|
|
|
|
#include <zephyr/drivers/reset.h>
|
|
|
|
|
|
|
|
#include "sdhc_cdns_ll.h"
|
|
|
|
|
|
|
|
#define SDHC_CDNS_DESC_SIZE (1<<20)
|
|
|
|
#define COMBOPHY_ADDR_MASK 0x0000FFFFU
|
|
|
|
|
|
|
|
#define DEV_CFG(_dev) ((const struct sdhc_cdns_config *)(_dev)->config)
|
|
|
|
#define DEV_DATA(_dev) ((struct sdhc_cdns_data *const)(_dev)->data)
|
|
|
|
|
|
|
|
LOG_MODULE_REGISTER(sdhc_cdns, CONFIG_SDHC_LOG_LEVEL);
|
|
|
|
|
|
|
|
/* SDMMC operations FPs are the element of structure*/
|
|
|
|
static const struct sdhc_cdns_ops *cdns_sdmmc_ops;
|
|
|
|
|
|
|
|
struct sdhc_cdns_config {
|
|
|
|
DEVICE_MMIO_NAMED_ROM(reg_base);
|
|
|
|
DEVICE_MMIO_NAMED_ROM(combo_phy);
|
|
|
|
/* Clock rate for host */
|
|
|
|
uint32_t clk_rate;
|
|
|
|
/* power delay prop for host */
|
|
|
|
uint32_t power_delay_ms;
|
|
|
|
/* run time device structure */
|
|
|
|
const struct device *cdns_clk_dev;
|
|
|
|
/* type to identify a clock controller sub-system */
|
|
|
|
clock_control_subsys_t clkid;
|
|
|
|
/* Reset controller device configuration. */
|
|
|
|
const struct reset_dt_spec reset_sdmmc;
|
|
|
|
const struct reset_dt_spec reset_sdmmcocp;
|
|
|
|
const struct reset_dt_spec reset_softphy;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct sdhc_cdns_data {
|
|
|
|
DEVICE_MMIO_NAMED_RAM(reg_base);
|
|
|
|
DEVICE_MMIO_NAMED_RAM(combo_phy);
|
|
|
|
/* Host controller parameters */
|
|
|
|
struct sdhc_cdns_params params;
|
|
|
|
/* sdmmc device informartaion for host */
|
|
|
|
struct sdmmc_device_info info;
|
|
|
|
/* Input/Output configuration */
|
|
|
|
struct sdhc_io host_io;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int sdhc_cdns_request(const struct device *dev,
|
|
|
|
struct sdhc_command *cmd,
|
|
|
|
struct sdhc_data *data)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
struct sdmmc_cmd cdns_sdmmc_cmd;
|
|
|
|
|
|
|
|
if (cmd == NULL) {
|
|
|
|
LOG_ERR("Wrong CMD parameter");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialization of command structure */
|
|
|
|
cdns_sdmmc_cmd.cmd_idx = cmd->opcode;
|
|
|
|
cdns_sdmmc_cmd.cmd_arg = cmd->arg;
|
|
|
|
cdns_sdmmc_cmd.resp_type = (cmd->response_type & SDHC_NATIVE_RESPONSE_MASK);
|
|
|
|
|
|
|
|
/* Sending command as per the data or non data */
|
|
|
|
if (data) {
|
|
|
|
ret = cdns_sdmmc_ops->prepare(data->block_addr, (uintptr_t)data->data,
|
|
|
|
data);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("DMA Prepare failed");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = cdns_sdmmc_ops->send_cmd(&cdns_sdmmc_cmd, data);
|
|
|
|
|
|
|
|
if (ret == 0) {
|
|
|
|
if (cmd->opcode == SD_READ_SINGLE_BLOCK || cmd->opcode == SD_APP_SEND_SCR ||
|
|
|
|
cmd->opcode == SD_READ_MULTIPLE_BLOCK) {
|
|
|
|
|
|
|
|
if (data == NULL) {
|
|
|
|
LOG_ERR("Invalid data parameter");
|
|
|
|
return -ENODATA;
|
|
|
|
}
|
|
|
|
ret = cdns_sdmmc_ops->cache_invd(data->block_addr, (uintptr_t)data->data,
|
|
|
|
data->block_size);
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* copying all responses as per response type */
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
cmd->response[i] = cdns_sdmmc_cmd.resp_data[i];
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_get_card_present(const struct device *dev)
|
|
|
|
{
|
|
|
|
return cdns_sdmmc_ops->card_present();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_card_busy(const struct device *dev)
|
|
|
|
{
|
|
|
|
return cdns_sdmmc_ops->busy();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_get_host_props(const struct device *dev,
|
|
|
|
struct sdhc_host_props *props)
|
|
|
|
{
|
|
|
|
const struct sdhc_cdns_config *sdhc_config = DEV_CFG(dev);
|
|
|
|
|
|
|
|
memset(props, 0, sizeof(struct sdhc_host_props));
|
|
|
|
props->f_min = SDMMC_CLOCK_400KHZ;
|
|
|
|
/*
|
|
|
|
* default max speed is 25MHZ, as per SCR register
|
|
|
|
* it will switch accordingly
|
|
|
|
*/
|
|
|
|
props->f_max = SD_CLOCK_25MHZ;
|
|
|
|
props->power_delay = sdhc_config->power_delay_ms;
|
|
|
|
props->host_caps.vol_330_support = true;
|
|
|
|
props->is_spi = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_reset(const struct device *dev)
|
|
|
|
{
|
|
|
|
return cdns_sdmmc_ops->reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_init(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct sdhc_cdns_data *const data = DEV_DATA(dev);
|
|
|
|
const struct sdhc_cdns_config *sdhc_config = DEV_CFG(dev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* SDHC reg base */
|
|
|
|
DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE);
|
|
|
|
/* ComboPhy reg base */
|
|
|
|
DEVICE_MMIO_NAMED_MAP(dev, combo_phy, K_MEM_CACHE_NONE);
|
|
|
|
|
|
|
|
/* clock setting */
|
|
|
|
if (sdhc_config->clk_rate == 0U) {
|
|
|
|
if (!device_is_ready(sdhc_config->cdns_clk_dev)) {
|
|
|
|
LOG_ERR("Clock controller device is not ready");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = clock_control_get_rate(sdhc_config->cdns_clk_dev,
|
|
|
|
sdhc_config->clkid, &data->params.clk_rate);
|
|
|
|
|
|
|
|
if (ret != 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
data->params.clk_rate = sdhc_config->clk_rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Setting regbase */
|
|
|
|
data->params.reg_base = DEVICE_MMIO_NAMED_GET(dev, reg_base);
|
|
|
|
data->params.reg_phy = DEVICE_MMIO_NAMED_GET(dev, combo_phy);
|
|
|
|
data->params.combophy = (DEVICE_MMIO_NAMED_ROM_PTR((dev),
|
|
|
|
combo_phy)->phys_addr);
|
|
|
|
data->params.combophy = (data->params.combophy & COMBOPHY_ADDR_MASK);
|
|
|
|
|
|
|
|
/* resetting the lines */
|
|
|
|
if (sdhc_config->reset_sdmmc.dev != NULL) {
|
|
|
|
if (!device_is_ready(sdhc_config->reset_sdmmc.dev) ||
|
|
|
|
!device_is_ready(sdhc_config->reset_sdmmcocp.dev) ||
|
|
|
|
!device_is_ready(sdhc_config->reset_softphy.dev)) {
|
|
|
|
LOG_ERR("Reset device not found");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = reset_line_toggle(sdhc_config->reset_softphy.dev,
|
|
|
|
sdhc_config->reset_softphy.id);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("Softphy Reset failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = reset_line_toggle(sdhc_config->reset_sdmmc.dev,
|
|
|
|
sdhc_config->reset_sdmmc.id);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("sdmmc Reset failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = reset_line_toggle(sdhc_config->reset_sdmmcocp.dev,
|
|
|
|
sdhc_config->reset_sdmmcocp.id);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("sdmmcocp Reset failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init function to call lower layer file */
|
|
|
|
sdhc_cdns_sdmmc_init(&data->params, &data->info, &cdns_sdmmc_ops);
|
|
|
|
|
|
|
|
ret = sdhc_cdns_reset(dev);
|
|
|
|
if (ret != 0U) {
|
|
|
|
LOG_ERR("Card reset failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init operation called for register initialisation */
|
|
|
|
ret = cdns_sdmmc_ops->init();
|
|
|
|
if (ret != 0U) {
|
|
|
|
LOG_ERR("Card initialization failed");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int sdhc_cdns_set_io(const struct device *dev, struct sdhc_io *ios)
|
|
|
|
{
|
|
|
|
struct sdhc_cdns_data *data = dev->data;
|
|
|
|
struct sdhc_io *host_io = &data->host_io;
|
|
|
|
|
|
|
|
if (host_io->bus_width != ios->bus_width || host_io->clock !=
|
|
|
|
ios->clock) {
|
|
|
|
host_io->bus_width = ios->bus_width;
|
|
|
|
host_io->clock = ios->clock;
|
|
|
|
return cdns_sdmmc_ops->set_ios(ios->clock, ios->bus_width);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-01-17 20:15:32 +08:00
|
|
|
static const struct sdhc_driver_api sdhc_cdns_api = {
|
2023-10-17 14:41:17 +08:00
|
|
|
.request = sdhc_cdns_request,
|
|
|
|
.set_io = sdhc_cdns_set_io,
|
|
|
|
.get_host_props = sdhc_cdns_get_host_props,
|
|
|
|
.get_card_present = sdhc_cdns_get_card_present,
|
|
|
|
.reset = sdhc_cdns_reset,
|
|
|
|
.card_busy = sdhc_cdns_card_busy,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define SDHC_CDNS_CLOCK_RATE_INIT(inst) \
|
|
|
|
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clock_frequency), \
|
|
|
|
( \
|
|
|
|
.clk_rate = DT_INST_PROP(inst, clock_frequency), \
|
|
|
|
.cdns_clk_dev = NULL, \
|
|
|
|
.clkid = (clock_control_subsys_t)0, \
|
|
|
|
), \
|
|
|
|
( \
|
|
|
|
.clk_rate = 0, \
|
|
|
|
.cdns_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \
|
|
|
|
.clkid = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(inst, clkid), \
|
|
|
|
) \
|
|
|
|
)
|
|
|
|
|
|
|
|
#define SDHC_CDNS_RESET_SPEC_INIT(inst) \
|
|
|
|
.reset_sdmmc = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 0), \
|
|
|
|
.reset_sdmmcocp = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 1),\
|
|
|
|
.reset_softphy = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 2),
|
|
|
|
|
|
|
|
#define SDHC_CDNS_INIT(inst) \
|
|
|
|
static struct sdhc_cdns_desc cdns_desc \
|
|
|
|
[CONFIG_CDNS_DESC_COUNT]; \
|
|
|
|
\
|
|
|
|
static const struct sdhc_cdns_config sdhc_cdns_config_##inst = {\
|
|
|
|
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME( \
|
|
|
|
reg_base, DT_DRV_INST(inst)), \
|
|
|
|
DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME( \
|
|
|
|
combo_phy, DT_DRV_INST(inst)), \
|
|
|
|
SDHC_CDNS_CLOCK_RATE_INIT(inst) \
|
|
|
|
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, resets), \
|
|
|
|
(SDHC_CDNS_RESET_SPEC_INIT(inst))) \
|
|
|
|
.power_delay_ms = DT_INST_PROP(inst, power_delay_ms), \
|
|
|
|
}; \
|
|
|
|
static struct sdhc_cdns_data sdhc_cdns_data_##inst = { \
|
|
|
|
.params = { \
|
|
|
|
.bus_width = SDHC_BUS_WIDTH1BIT, \
|
|
|
|
.desc_base = (uintptr_t) &cdns_desc, \
|
|
|
|
.desc_size = SDHC_CDNS_DESC_SIZE, \
|
|
|
|
.flags = 0, \
|
|
|
|
}, \
|
|
|
|
.info = { \
|
|
|
|
.cdn_sdmmc_dev_type = SD_DS, \
|
|
|
|
.ocr_voltage = OCR_3_3_3_4 | OCR_3_2_3_3, \
|
|
|
|
}, \
|
|
|
|
}; \
|
|
|
|
DEVICE_DT_INST_DEFINE(inst, \
|
|
|
|
&sdhc_cdns_init, \
|
|
|
|
NULL, \
|
|
|
|
&sdhc_cdns_data_##inst, \
|
|
|
|
&sdhc_cdns_config_##inst, \
|
|
|
|
POST_KERNEL, \
|
|
|
|
CONFIG_SDHC_INIT_PRIORITY, \
|
|
|
|
&sdhc_cdns_api);
|
|
|
|
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(SDHC_CDNS_INIT)
|