blob: f5a880e5fb4e6ec28b0628f006aaaf93adce26d3 [file] [log] [blame]
/*
* 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;
}
static const struct sdhc_driver_api sdhc_cdns_api = {
.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)