drivers: sdhc: add cdns sdhc/combophy driver files

Add SDHC driver files with new SD framework changes
SDHC driver includes combophy configurations.

Added mmc binding yaml file

Signed-off-by: Murlidhar Roy <murlidhar.roy@intel.com>
This commit is contained in:
Murlidhar Roy 2023-10-17 14:41:17 +08:00 committed by Carles Cufí
parent 9a642caebb
commit eb006b992f
10 changed files with 3210 additions and 0 deletions

View file

@ -9,4 +9,5 @@ zephyr_library_sources_ifdef(CONFIG_MCUX_SDIF mcux_sdif.c)
zephyr_library_sources_ifdef(CONFIG_SAM_HSMCI sam_hsmci.c)
zephyr_library_sources_ifdef(CONFIG_INTEL_EMMC_HOST intel_emmc_host.c)
zephyr_library_sources_ifdef(CONFIG_SDHC_INFINEON_CAT1 ifx_cat1_sdio.c)
zephyr_library_sources_ifdef(CONFIG_CDNS_SDHC sdhc_cdns_ll.c sdhc_cdns.c)
endif()

View file

@ -14,6 +14,7 @@ source "drivers/sdhc/Kconfig.spi"
source "drivers/sdhc/Kconfig.mcux_sdif"
source "drivers/sdhc/Kconfig.sam_hsmci"
source "drivers/sdhc/Kconfig.intel"
source "drivers/sdhc/Kconfig.sdhc_cdns"
config SDHC_INIT_PRIORITY
int "SDHC driver init priority"

View file

@ -0,0 +1,26 @@
# Copyright (c) 2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
config CDNS_SDHC
bool "CDNS SDHC"
default y
depends on DT_HAS_CDNS_SDHC_ENABLED
select SDHC_SUPPORTS_NATIVE_MODE
help
Enable Cadence SDMMC Host Controller.
if CDNS_SDHC
# Cadence SDHC DMA needs 64 bit aligned buffers
config SDHC_BUFFER_ALIGNMENT
default 8
config CDNS_DESC_COUNT
int "Allocate number of descriptors"
default 8
help
SD host controllers require DMA preparation for read and write operation.
Creates static descriptors which can be used by ADMA. Devices should
configure this flag if they require to transfer more than 8*64Kb of data.
endif # CDNS_SDHC

301
drivers/sdhc/sdhc_cdns.c Normal file
View file

@ -0,0 +1,301 @@
/*
* 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 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)

View file

@ -0,0 +1,301 @@
/*
* 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 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)

View file

@ -0,0 +1,791 @@
/*
* Copyright (C) 2023 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include "sdhc_cdns_ll.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sdhc_cdns_ll, CONFIG_SDHC_LOG_LEVEL);
/* card busy and present */
#define CARD_BUSY 1
#define CARD_NOT_BUSY 0
#define CARD_PRESENT 1
/* SRS12 error mask */
#define CDNS_SRS12_ERR_MASK 0xFFFF8000U
#define CDNS_CSD_BYTE_MASK 0x000000FFU
/* General define */
#define SDHC_REG_MASK 0xFFFFFFFFU
#define SD_HOST_BLOCK_SIZE 0x200
#define SDMMC_DMA_MAX_BUFFER_SIZE (64 * 1024)
#define CDNSMMC_ADDRESS_MASK (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)
#define SRS10_VAL_READ (ADMA2_32 | HS_EN | DT_WIDTH)
#define SRS10_VAL_SW (ADMA2_32 | DT_WIDTH)
#define SRS11_VAL_GEN (READ_CLK | CDNS_SRS11_ICE | CDNS_SRS11_ICS | CDNS_SRS11_SDCE)
#define SRS11_VAL_CID (CDNS_SRS11_ICE | CDNS_SRS11_ICS | CDNS_SRS11_SDCE)
#define SRS15_VAL_GEN (CDNS_SRS15_BIT_AD_64 | CDNS_SRS15_HV4E | CDNS_SRS15_V18SE)
#define SRS15_VAL_RD_WR (SRS15_VAL_GEN | CDNS_SRS15_SDR104 | CDNS_SRS15_PVE)
#define SRS15_VAL_CID (CDNS_SRS15_HV4E | CDNS_SRS15_V18SE)
#define CARD_REG_TIME_DELAY_US 100000
#define WAIT_ICS_TIME_DELAY_US 5000
#define RESET_SRS14 0x00000000
static struct sdhc_cdns_params cdns_params;
static struct sdhc_cdns_combo_phy sdhc_cdns_combo_phy_reg_info;
static struct sdhc_cdns_sdmmc sdhc_cdns_sdmmc_reg_info;
/* Function to write general phy registers */
static int sdhc_cdns_write_phy_reg(uint32_t phy_reg_addr, uint32_t phy_reg_addr_value,
uint32_t phy_reg_data, uint32_t phy_reg_data_value)
{
uint32_t data = 0;
/* Set PHY register address, write HRS04*/
sys_write32(phy_reg_addr_value, phy_reg_addr);
/* Set PHY register data, write HRS05 */
sys_write32(phy_reg_data_value, phy_reg_data);
data = sys_read32(phy_reg_data);
if (data != phy_reg_data_value) {
LOG_ERR("PHY_REG_DATA is not set properly");
return -ENXIO;
}
return 0;
}
int sdhc_cdns_wait_ics(uint16_t timeout, uint32_t cdn_srs_res)
{
/* Wait status command response ready */
if (!WAIT_FOR(((sys_read32(cdn_srs_res) & CDNS_SRS11_ICS)
== CDNS_SRS11_ICS), timeout, k_msleep(1))) {
LOG_ERR("Timed out waiting for ICS response");
return -ETIMEDOUT;
}
return 0;
}
static int sdhc_cdns_busy(void)
{
unsigned int data;
data = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS09);
return (data & CDNS_SRS09_STAT_DAT_BUSY) ? CARD_BUSY : CARD_NOT_BUSY;
}
static int sdhc_cdns_card_present(void)
{
uint32_t timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR((((sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS09))
& CDNS_SRS09_CI) == CDNS_SRS09_CI),
timeout, k_msleep(1))) {
LOG_ERR("Card detection timeout");
return -ETIMEDOUT;
}
return CARD_PRESENT;
}
static int sdhc_cdns_vol_reset(void)
{
/* Reset embedded card */
sys_write32((7 << CDNS_SRS10_BVS) | (0 << CDNS_SRS10_BP),
(cdns_params.reg_base + SDHC_CDNS_SRS10));
/*
* Turn on supply voltage
* CDNS_SRS10_BVS = 7, CDNS_SRS10_BP = 1, BP2 only in UHS2 mode
*/
sys_write32((7 << CDNS_SRS10_BVS) | (1 << CDNS_SRS10_BP),
(cdns_params.reg_base + SDHC_CDNS_SRS10));
return 0;
}
/*
* Values are taken from IP documents and calc_setting.py script
* with input value- mode sd_ds,
* sdmclk 5000,
* sdclk 10000,
* iocell_input_delay 2500,
* iocell_output_delay 2500 and
* delay_element 24
*/
void cdns_sdhc_set_sdmmc_params(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
/* Values are taken by the reference of cadence IP documents */
sdhc_cdns_combo_phy_reg->cp_clk_wr_delay = 0;
sdhc_cdns_combo_phy_reg->cp_clk_wrdqs_delay = 0;
sdhc_cdns_combo_phy_reg->cp_data_select_oe_end = 1;
sdhc_cdns_combo_phy_reg->cp_dll_bypass_mode = 1;
sdhc_cdns_combo_phy_reg->cp_dll_locked_mode = 3;
sdhc_cdns_combo_phy_reg->cp_dll_start_point = 4;
sdhc_cdns_combo_phy_reg->cp_gate_cfg_always_on = 1;
sdhc_cdns_combo_phy_reg->cp_io_mask_always_on = 0;
sdhc_cdns_combo_phy_reg->cp_io_mask_end = 2;
sdhc_cdns_combo_phy_reg->cp_io_mask_start = 0;
sdhc_cdns_combo_phy_reg->cp_rd_del_sel = 52;
sdhc_cdns_combo_phy_reg->cp_read_dqs_cmd_delay = 0;
sdhc_cdns_combo_phy_reg->cp_read_dqs_delay = 0;
sdhc_cdns_combo_phy_reg->cp_sw_half_cycle_shift = 0;
sdhc_cdns_combo_phy_reg->cp_sync_method = 1;
sdhc_cdns_combo_phy_reg->cp_underrun_suppress = 1;
sdhc_cdns_combo_phy_reg->cp_use_ext_lpbk_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_lpbk_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_phony_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_phony_dqs_cmd = 1;
sdhc_cdns_sdmmc_reg->sdhc_extended_rd_mode = 1;
sdhc_cdns_sdmmc_reg->sdhc_extended_wr_mode = 1;
sdhc_cdns_sdmmc_reg->sdhc_hcsdclkadj = 6;
sdhc_cdns_sdmmc_reg->sdhc_idelay_val = 1;
sdhc_cdns_sdmmc_reg->sdhc_rdcmd_en = 1;
sdhc_cdns_sdmmc_reg->sdhc_rddata_en = 1;
sdhc_cdns_sdmmc_reg->sdhc_rw_compensate = 10;
sdhc_cdns_sdmmc_reg->sdhc_sdcfsh = 0;
sdhc_cdns_sdmmc_reg->sdhc_sdcfsl = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_dly = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata0_dly = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrdata0_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata1_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata1_sdclk_dly = 0;
}
/* Phy register programing for phy init */
static int sdhc_cdns_program_phy_reg(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
uint32_t value = 0;
int ret = 0;
/*
* program PHY_DQS_TIMING_REG
* This register controls the DQS related timing
*/
value = (CP_USE_EXT_LPBK_DQS(sdhc_cdns_combo_phy_reg->cp_use_ext_lpbk_dqs))
| (CP_USE_LPBK_DQS(sdhc_cdns_combo_phy_reg->cp_use_lpbk_dqs))
| (CP_USE_PHONY_DQS(sdhc_cdns_combo_phy_reg->cp_use_phony_dqs))
| (CP_USE_PHONY_DQS_CMD(sdhc_cdns_combo_phy_reg->cp_use_phony_dqs_cmd));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DQS_TIMING_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DQS_TIMING_REG programming");
return ret;
}
/*
* program PHY_GATE_LPBK_CTRL_REG
* This register controls the gate and loopback control related timing.
*/
value = (CP_SYNC_METHOD(sdhc_cdns_combo_phy_reg->cp_sync_method))
| (CP_SW_HALF_CYCLE_SHIFT(sdhc_cdns_combo_phy_reg->cp_sw_half_cycle_shift))
| (CP_RD_DEL_SEL(sdhc_cdns_combo_phy_reg->cp_rd_del_sel))
| (CP_UNDERRUN_SUPPRESS(sdhc_cdns_combo_phy_reg->cp_underrun_suppress))
| (CP_GATE_CFG_ALWAYS_ON(sdhc_cdns_combo_phy_reg->cp_gate_cfg_always_on));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_GATE_LPBK_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_GATE_LPBK_CTRL_REG programming");
return -ret;
}
/*
* program PHY_DLL_MASTER_CTRL_REG
* This register holds the control for the Master DLL logic.
*/
value = (CP_DLL_BYPASS_MODE(sdhc_cdns_combo_phy_reg->cp_dll_bypass_mode))
| (CP_DLL_START_POINT(sdhc_cdns_combo_phy_reg->cp_dll_start_point));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DLL_MASTER_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DLL_MASTER_CTRL_REG programming");
return ret;
}
/*
* program PHY_DLL_SLAVE_CTRL_REG
* This register holds the control for the slave DLL logic.
*/
value = (CP_READ_DQS_CMD_DELAY(sdhc_cdns_combo_phy_reg->cp_read_dqs_cmd_delay))
| (CP_CLK_WRDQS_DELAY(sdhc_cdns_combo_phy_reg->cp_clk_wrdqs_delay))
| (CP_CLK_WR_DELAY(sdhc_cdns_combo_phy_reg->cp_clk_wr_delay))
| (CP_READ_DQS_DELAY(sdhc_cdns_combo_phy_reg->cp_read_dqs_delay));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DLL_SLAVE_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DLL_SLAVE_CTRL_REG programming");
return ret;
}
/*
* program PHY_CTRL_REG
* This register handles the global control settings for the PHY.
*/
sys_write32(cdns_params.combophy + PHY_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS04);
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS05);
/* phony_dqs_timing=0 */
value &= ~(CP_PHONY_DQS_TIMING_MASK << CP_PHONY_DQS_TIMING_SHIFT);
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS05);
/* switch off DLL_RESET */
do {
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09);
value |= CDNS_HRS09_PHY_SW_RESET;
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS09);
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09);
/* polling PHY_INIT_COMPLETE */
} while ((value & CDNS_HRS09_PHY_INIT_COMP) != CDNS_HRS09_PHY_INIT_COMP);
/*
* program PHY_DQ_TIMING_REG
* This register controls the DQ related timing.
*/
sdhc_cdns_combo_phy_reg->cp_io_mask_end = 0U;
value = (CP_IO_MASK_ALWAYS_ON(sdhc_cdns_combo_phy_reg->cp_io_mask_always_on))
| (CP_IO_MASK_END(sdhc_cdns_combo_phy_reg->cp_io_mask_end))
| (CP_IO_MASK_START(sdhc_cdns_combo_phy_reg->cp_io_mask_start))
| (CP_DATA_SELECT_OE_END(sdhc_cdns_combo_phy_reg->cp_data_select_oe_end));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DQ_TIMING_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DQ_TIMING_REG programming");
return ret;
}
return 0;
}
static int sdhc_cdns_cache_invd(int lba, uintptr_t buf, size_t size)
{
int ret = 0;
ret = arch_dcache_invd_range((void *)buf, size);
if (ret != 0) {
LOG_ERR("%s: error in invalidate dcache with ret %d", __func__, ret);
return ret;
}
return 0;
}
/* DMA preparation for the read and write operation */
static int sdhc_cdns_prepare(uint32_t dma_start_addr, uintptr_t dma_buff,
struct sdhc_data *data)
{
struct sdhc_cdns_desc *desc;
uint32_t desc_cnt, i;
uintptr_t base;
uint64_t desc_base;
uint32_t size = data->blocks * data->block_size;
__ASSERT_NO_MSG(((dma_buff & CDNSMMC_ADDRESS_MASK) == 0) &&
(cdns_params.desc_size > 0) &&
((cdns_params.desc_size & MMC_BLOCK_MASK) == 0));
arch_dcache_flush_range((void *)dma_buff, size);
desc_cnt = (size + (SDMMC_DMA_MAX_BUFFER_SIZE) - 1) /
(SDMMC_DMA_MAX_BUFFER_SIZE);
__ASSERT_NO_MSG(desc_cnt * sizeof(struct sdhc_cdns_desc) <
cdns_params.desc_size);
if (desc_cnt > CONFIG_CDNS_DESC_COUNT) {
LOG_ERR("Requested data transfer length %u greater than configured length %u",
size, (CONFIG_CDNS_DESC_COUNT * SDMMC_DMA_MAX_BUFFER_SIZE));
return -EINVAL;
}
/*
* Creating descriptor as per the desc_count and linked list of
* descriptor for contiguous desc alignment
*/
base = cdns_params.reg_base;
desc = (struct sdhc_cdns_desc *)cdns_params.desc_base;
desc_base = (uint64_t)desc;
i = 0;
while ((i+1) < desc_cnt) {
desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
desc->reserved = 0;
desc->len = MAX_64KB_PAGE;
desc->addr_lo = (dma_buff & 0xffffffff) + (SDMMC_DMA_MAX_BUFFER_SIZE * i);
desc->addr_hi = (dma_buff >> 32) & 0xffffffff;
size -= SDMMC_DMA_MAX_BUFFER_SIZE;
desc++;
i++;
}
desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA |
ADMA_DESC_ATTR_END;
desc->reserved = 0;
desc->len = size;
desc->addr_lo = (dma_buff & 0xffffffff) + (SDMMC_DMA_MAX_BUFFER_SIZE * i);
desc->addr_hi = (dma_buff >> 32) & 0xffffffff;
sys_write32((uint32_t)desc_base, cdns_params.reg_base + SDHC_CDNS_SRS22);
sys_write32((uint32_t)(desc_base >> 32), cdns_params.reg_base + SDHC_CDNS_SRS23);
arch_dcache_flush_range((void *)cdns_params.desc_base,
desc_cnt * sizeof(struct sdhc_cdns_desc));
sys_write32((data->block_size << CDNS_SRS01_BLK_SIZE |
data->blocks << CDNS_SRS01_BLK_COUNT_CT | CDNS_SRS01_SDMA_BUF),
cdns_params.reg_base + SDHC_CDNS_SRS01);
return 0;
}
static int sdhc_cdns_host_set_clk(int clk)
{
uint32_t sdclkfsval = 0;
uint32_t dtcvval = 0xe;
int ret = 0;
sdclkfsval = (cdns_params.clk_rate / 2000) / clk;
sys_write32(0, cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32(((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS) |
(1 << CDNS_SRS11_ICE)), cdns_params.reg_base + SDHC_CDNS_SRS11);
ret = sdhc_cdns_wait_ics(WAIT_ICS_TIME_DELAY_US, cdns_params.reg_base + SDHC_CDNS_SRS11);
if (ret != 0) {
return ret;
}
/* Enable DLL reset */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 0);
/* Set extended_wr_mode */
sys_write32(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09)
& 0xFFFFFFF7) | (1 << CDNS_HRS09_EXT_WR_MODE)), (cdns_params.reg_base
+ SDHC_CDNS_HRS09));
/* Release DLL reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 1);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_HRS09, (3 << CDNS_HRS09_RDCMD_EN));
sys_write32(((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS)
| (1 << CDNS_SRS11_ICE) | (1 << CDNS_SRS11_SDCE)),
cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32(0xFFFFFFFF, cdns_params.reg_base + SDHC_CDNS_SRS13);
return 0;
}
static int sdhc_cdns_set_ios(unsigned int clk, unsigned int width)
{
int ret = 0;
switch (width) {
case SDHC_BUS_WIDTH1BIT:
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT1);
break;
case SDHC_BUS_WIDTH4BIT:
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT4);
break;
case SDHC_BUS_WIDTH8BIT:
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT8);
break;
default:
__ASSERT_NO_MSG(0);
break;
}
/* Perform clock configuration when SD clock is not gated */
if (clk != 0) {
ret = sdhc_cdns_host_set_clk(clk);
if (ret != 0) {
LOG_ERR("%s: Clock configuration failed", __func__);
return ret;
}
}
return 0;
}
/* Programming HRS register for initialisation */
static int sdhc_cdns_init_hrs_io(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
uint32_t value = 0;
int ret = 0;
/*
* program HRS09, register 42
* PHY Control and Status Register
*/
value = (CDNS_HRS09_RDDATA_EN(sdhc_cdns_sdmmc_reg->sdhc_rddata_en))
| (CDNS_HRS09_RDCMD(sdhc_cdns_sdmmc_reg->sdhc_rdcmd_en))
| (CDNS_HRS09_EXTENDED_WR(sdhc_cdns_sdmmc_reg->sdhc_extended_wr_mode))
| (CDNS_HRS09_EXT_RD_MODE(sdhc_cdns_sdmmc_reg->sdhc_extended_rd_mode));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS09);
/*
* program HRS10, register 43
* Host Controller SDCLK start point adjustment
*/
value = (SDHC_HRS10_HCSDCLKADJ(sdhc_cdns_sdmmc_reg->sdhc_hcsdclkadj));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS10);
/*
* program HRS16, register 48
* CMD/DAT output delay
*/
value = (CDNS_HRS16_WRDATA1_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata1_sdclk_dly))
| (CDNS_HRS16_WRDATA0_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata0_sdclk_dly))
| (CDNS_HRS16_WRCMD1_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_sdclk_dly))
| (CDNS_HRS16_WRCMD0_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_sdclk_dly))
| (CDNS_HRS16_WRDATA1_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata1_dly))
| (CDNS_HRS16_WRDATA0_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata0_dly))
| (CDNS_HRS16_WRCMD1_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_dly))
| (CDNS_HRS16_WRCMD0_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_dly));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS16);
/*
* program HRS07, register 40
* IO Delay Information Register
*/
value = (CDNS_HRS07_RW_COMPENSATE(sdhc_cdns_sdmmc_reg->sdhc_rw_compensate))
| (CDNS_HRS07_IDELAY_VAL(sdhc_cdns_sdmmc_reg->sdhc_idelay_val));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS07);
return ret;
}
static int sdhc_cdns_set_clk(struct sdhc_cdns_params *cdn_sdmmc_dev_type_params)
{
uint32_t dtcvval, sdclkfsval;
int ret = 0;
dtcvval = DTC_VAL;
sdclkfsval = 0;
/* Condition for Default speed mode and SDR12 and SDR_BC */
if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_DS) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR12) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_SDR_BC)) {
sdclkfsval = 4;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_HS) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR25) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_DDR50) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_SDR)) {
sdclkfsval = 2;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR50) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_DDR) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS400) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS400ES)) {
sdclkfsval = 1;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR104) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS200)) {
sdclkfsval = 0;
}
/* Disabling SD clock enable */
sys_write32(0, cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32((dtcvval << CDNS_SRS11_DTCV) |
(sdclkfsval << CDNS_SRS11_SDCLKFS) | (1 << CDNS_SRS11_ICE),
cdns_params.reg_base + SDHC_CDNS_SRS11);
ret = sdhc_cdns_wait_ics(WAIT_ICS_TIME_DELAY_US, cdns_params.reg_base + SDHC_CDNS_SRS11);
if (ret != 0) {
return ret;
}
/* Enable DLL reset */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 0);
/* Set extended_wr_mode */
sys_write32(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09) &
0xFFFFFFF7) | (1 << CDNS_HRS09_EXT_WR_MODE)), (cdns_params.reg_base
+ SDHC_CDNS_HRS09));
/* Release DLL reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 1);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_HRS09, (3 << CDNS_HRS09_RDCMD_EN));
sys_write32((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS) |
(1 << CDNS_SRS11_ICE) | (1 << CDNS_SRS11_SDCE), cdns_params.reg_base
+ SDHC_CDNS_SRS11);
sys_write32(0xFFFFFFFF, cdns_params.reg_base + SDHC_CDNS_SRS13);
return 0;
}
static int sdhc_cdns_reset(void)
{
int32_t timeout;
sys_clear_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, 0xFFFF);
/* Software reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS00, CDNS_HRS00_SWR);
/* Wait status command response ready */
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS00) &
CDNS_HRS00_SWR) == 0), timeout, k_msleep(1))) {
LOG_ERR("Software reset is not completed...timedout");
return -ETIMEDOUT;
}
/* Step 1, switch on DLL_RESET */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, CDNS_HRS09_PHY_SW_RESET);
return 0;
}
static int sdhc_cdns_init(void)
{
int ret = 0;
ret = sdhc_cdns_program_phy_reg(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
if (ret != 0U) {
LOG_ERR("SoftPhy register configuration failed");
return ret;
}
ret = sdhc_cdns_init_hrs_io(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
if (ret != 0U) {
LOG_ERR("Configuration for HRS IO reg failed");
return ret;
}
ret = sdhc_cdns_card_present();
if (ret != CARD_PRESENT) {
LOG_ERR("SD card does not detect");
return -ETIMEDOUT;
}
ret = sdhc_cdns_vol_reset();
if (ret != 0U) {
LOG_ERR("SD/MMC card reset failed");
return ret;
}
ret = sdhc_cdns_set_clk(&cdns_params);
if (ret != 0U) {
LOG_ERR("Host controller set clk failed");
return ret;
}
return 0;
}
static int sdhc_cdns_send_cmd(struct sdmmc_cmd *cmd, struct sdhc_data *data)
{
uint32_t op = 0;
uint32_t value;
uintptr_t base;
int32_t timeout;
uint32_t cmd_indx;
uint32_t status_check = 0;
__ASSERT(cmd, "Assert %s function call", __func__);
base = cdns_params.reg_base;
cmd_indx = (cmd->cmd_idx) << CDNS_SRS03_COM_IDX;
if (data) {
switch (cmd->cmd_idx) {
case SD_SWITCH:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_SW);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_GEN);
break;
case SD_WRITE_SINGLE_BLOCK:
case SD_READ_SINGLE_BLOCK:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_READ);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_RD_WR);
sys_write32(CDNS_SRS00_SAAR, cdns_params.reg_base + SDHC_CDNS_SRS00);
break;
case SD_WRITE_MULTIPLE_BLOCK:
case SD_READ_MULTIPLE_BLOCK:
op = CDNS_SRS03_DATA_PRSNT | AUTO_CMD23 | CDNS_SRS03_MULTI_BLK_READ;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_READ);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_RD_WR);
sys_write32(CDNS_SRS00_SAAR, cdns_params.reg_base + SDHC_CDNS_SRS00);
break;
case SD_APP_SEND_SCR:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, ADMA2_32);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_GEN);
break;
default:
op = 0;
break;
}
} else {
switch (cmd->cmd_idx) {
case SD_GO_IDLE_STATE:
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_CID);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, CDNS_SRS15_HV4E);
break;
case SD_ALL_SEND_CID:
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_CID);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_CID);
break;
case SD_SEND_IF_COND:
op = CDNS_SRS03_CMD_IDX_CHK_EN;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, CDNS_SRS15_HV4E);
break;
case SD_STOP_TRANSMISSION:
op = CMD_STOP_ABORT_CMD;
break;
case SD_SEND_STATUS:
break;
case SD_SELECT_CARD:
op = CDNS_SRS03_MULTI_BLK_READ;
break;
default:
op = 0;
break;
}
}
switch (cmd->resp_type) {
case SD_RSP_TYPE_NONE:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN);
break;
case SD_RSP_TYPE_R2:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN |
RES_TYPE_SEL_136 | CDNS_SRS03_RESP_CRC);
break;
case SD_RSP_TYPE_R3:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | RES_TYPE_SEL_48);
break;
case SD_RSP_TYPE_R1:
if ((cmd->cmd_idx == SD_WRITE_SINGLE_BLOCK) || (cmd->cmd_idx
== SD_WRITE_MULTIPLE_BLOCK)) {
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | RES_TYPE_SEL_48
| CDNS_SRS03_RESP_CRC | CDNS_SRS03_CMD_IDX_CHK_EN);
} else {
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | CDNS_SRS03_CMD_READ
| RES_TYPE_SEL_48 | CDNS_SRS03_RESP_CRC | CDNS_SRS03_CMD_IDX_CHK_EN);
}
break;
default:
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | CDNS_SRS03_CMD_READ |
CDNS_SRS03_MULTI_BLK_READ | RES_TYPE_SEL_48 | CDNS_SRS03_RESP_CRC |
CDNS_SRS03_CMD_IDX_CHK_EN);
break;
}
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR((sdhc_cdns_busy() == 0), timeout, k_msleep(1))) {
k_panic();
}
sys_write32(~0, cdns_params.reg_base + SDHC_CDNS_SRS12);
sys_write32(cmd->cmd_arg, cdns_params.reg_base + SDHC_CDNS_SRS02);
sys_write32(RESET_SRS14, cdns_params.reg_base + SDHC_CDNS_SRS14);
sys_write32(op | cmd_indx, cdns_params.reg_base + SDHC_CDNS_SRS03);
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR(((((sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS12)) &
CDNS_SRS03_CMD_DONE) == 1) | (((sys_read32(cdns_params.reg_base +
SDHC_CDNS_SRS12)) & ERROR_INT) == ERROR_INT)), timeout, k_busy_wait(1))) {
LOG_ERR("Response timeout SRS12");
return -ETIMEDOUT;
}
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS12);
status_check = value & CDNS_SRS12_ERR_MASK;
if (status_check != 0U) {
LOG_ERR("SD host controller send command failed, SRS12 = %X", status_check);
return -EIO;
}
if ((op & RES_TYPE_SEL_48) || (op & RES_TYPE_SEL_136)) {
cmd->resp_data[0] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS04);
if (op & RES_TYPE_SEL_136) {
cmd->resp_data[1] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS05);
cmd->resp_data[2] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS06);
cmd->resp_data[3] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS07);
/* 136-bit: RTS=01b, Response field R[127:8] - RESP3[23:0],
* RESP2[31:0], RESP1[31:0], RESP0[31:0]
* Subsystem expects 128 bits response but cadence SDHC sends
* 120 bits response from R[127:8]. Bits manupulation to address
* the correct responses for the 136 bit response type.
*/
cmd->resp_data[3] = ((cmd->resp_data[3] << 8) | ((cmd->resp_data[2] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[2] = ((cmd->resp_data[2] << 8) | ((cmd->resp_data[1] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[1] = ((cmd->resp_data[1] << 8) | ((cmd->resp_data[0] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[0] = (cmd->resp_data[0] << 8);
}
}
return 0;
}
static const struct sdhc_cdns_ops cdns_sdmmc_ops = {
.init = sdhc_cdns_init,
.send_cmd = sdhc_cdns_send_cmd,
.card_present = sdhc_cdns_card_present,
.set_ios = sdhc_cdns_set_ios,
.prepare = sdhc_cdns_prepare,
.cache_invd = sdhc_cdns_cache_invd,
.busy = sdhc_cdns_busy,
.reset = sdhc_cdns_reset,
};
void sdhc_cdns_sdmmc_init(struct sdhc_cdns_params *params, struct sdmmc_device_info *info,
const struct sdhc_cdns_ops **cb_sdmmc_ops)
{
__ASSERT_NO_MSG((params != NULL) &&
((params->reg_base & MMC_BLOCK_MASK) == 0) &&
((params->desc_size & MMC_BLOCK_MASK) == 0) &&
((params->reg_phy & MMC_BLOCK_MASK) == 0) &&
(params->desc_size > 0) &&
(params->clk_rate > 0) &&
((params->bus_width == MMC_BUS_WIDTH_1) ||
(params->bus_width == MMC_BUS_WIDTH_4) ||
(params->bus_width == MMC_BUS_WIDTH_8)));
memcpy(&cdns_params, params, sizeof(struct sdhc_cdns_params));
cdns_params.cdn_sdmmc_dev_type = info->cdn_sdmmc_dev_type;
*cb_sdmmc_ops = &cdns_sdmmc_ops;
cdns_sdhc_set_sdmmc_params(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
}

View file

@ -0,0 +1,489 @@
/*
* Copyright (C) 2023 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/cache.h>
#include <zephyr/drivers/sdhc.h>
/* HRS09 */
#define CDNS_HRS09_PHY_SW_RESET BIT(0)
#define CDNS_HRS09_PHY_INIT_COMP BIT(1)
#define CDNS_HRS09_EXT_RD_MODE(x) ((x) << 2)
#define CDNS_HRS09_EXT_WR_MODE 3
#define CDNS_HRS09_EXTENDED_WR(x) ((x) << 3)
#define CDNS_HRS09_RDCMD_EN 15
#define CDNS_HRS09_RDCMD(x) ((x) << 15)
#define CDNS_HRS09_RDDATA_EN(x) ((x) << 16)
/* HRS00 */
#define CDNS_HRS00_SWR BIT(0)
/* CMD_DATA_OUTPUT */
#define SDHC_CDNS_HRS16 0x40
/* SRS09 */
#define CDNS_SRS09_STAT_DAT_BUSY BIT(2)
#define CDNS_SRS09_CI BIT(16)
/* SRS10 */
#define CDNS_SRS10_DTW 1
#define CDNS_SRS10_EDTW 5
#define CDNS_SRS10_BP 8
#define CDNS_SRS10_BVS 9
/* data bus width */
#define WIDTH_BIT1 CDNS_SRS10_DTW
#define WIDTH_BIT4 CDNS_SRS10_DTW
#define WIDTH_BIT8 CDNS_SRS10_EDTW
/* SRS11 */
#define CDNS_SRS11_ICE BIT(0)
#define CDNS_SRS11_ICS BIT(1)
#define CDNS_SRS11_SDCE BIT(2)
#define CDNS_SRS11_USDCLKFS 6
#define CDNS_SRS11_SDCLKFS 8
#define CDNS_SRS11_DTCV 16
#define CDNS_SRS11_SRFA BIT(24)
#define CDNS_SRS11_SRCMD BIT(25)
#define CDNS_SRS11_SRDAT BIT(26)
/*
* This value determines the interval by which DAT line timeouts are detected
* The interval can be computed as below:
* 1111b - Reserved
* 1110b - t_sdmclk*2(27+2)
* 1101b - t_sdmclk*2(26+2)
*/
#define READ_CLK (0xa << 16)
#define WRITE_CLK (0xe << 16)
#define DTC_VAL 0xE
/* SRS12 */
#define CDNS_SRS12_CC 0U
#define CDNS_SRS12_TC 1
#define CDNS_SRS12_EINT 15
/* SRS01 */
#define CDNS_SRS01_BLK_SIZE 0U
#define CDNS_SRS01_SDMA_BUF 7 << 12
#define CDNS_SRS01_BLK_COUNT_CT 16
/* SRS15 Registers */
#define CDNS_SRS15_SDR12 0U
#define CDNS_SRS15_SDR25 BIT(16)
#define CDNS_SRS15_SDR50 (2 << 16)
#define CDNS_SRS15_SDR104 (3 << 16)
#define CDNS_SRS15_DDR50 (4 << 16)
/* V18SE is 0 for DS and HS, 1 for UHS-I */
#define CDNS_SRS15_V18SE BIT(19)
#define CDNS_SRS15_CMD23_EN BIT(27)
/* HC4E is 0 means version 3.0 and 1 means v 4.0 */
#define CDNS_SRS15_HV4E BIT(28)
#define CDNS_SRS15_BIT_AD_32 0U
#define CDNS_SRS15_BIT_AD_64 BIT(29)
#define CDNS_SRS15_PVE BIT(31)
/* Combo PHY */
#define PHY_DQ_TIMING_REG 0x0
#define PHY_DQS_TIMING_REG 0x04
#define PHY_GATE_LPBK_CTRL_REG 0x08
#define PHY_DLL_MASTER_CTRL_REG 0x0C
#define PHY_DLL_SLAVE_CTRL_REG 0x10
#define PHY_CTRL_REG 0x80
#define PERIPHERAL_SDMMC_MASK 0x60
#define PERIPHERAL_SDMMC_OFFSET 6
#define DFI_INTF_MASK 0x1
/* PHY_DQS_TIMING_REG */
#define CP_USE_EXT_LPBK_DQS(x) (x << 22)
#define CP_USE_LPBK_DQS(x) (x << 21)
#define CP_USE_PHONY_DQS(x) (x << 20)
#define CP_USE_PHONY_DQS_CMD(x) (x << 19)
/* PHY_GATE_LPBK_CTRL_REG */
#define CP_SYNC_METHOD(x) ((x) << 31)
#define CP_SW_HALF_CYCLE_SHIFT(x) ((x) << 28)
#define CP_RD_DEL_SEL(x) ((x) << 19)
#define CP_UNDERRUN_SUPPRESS(x) ((x) << 18)
#define CP_GATE_CFG_ALWAYS_ON(x) ((x) << 6)
/* PHY_DLL_MASTER_CTRL_REG */
#define CP_DLL_BYPASS_MODE(x) ((x) << 23)
#define CP_DLL_START_POINT(x) ((x) << 0)
/* PHY_DLL_SLAVE_CTRL_REG */
#define CP_READ_DQS_CMD_DELAY(x) ((x) << 24)
#define CP_CLK_WRDQS_DELAY(x) ((x) << 16)
#define CP_CLK_WR_DELAY(x) ((x) << 8)
#define CP_READ_DQS_DELAY(x) (x)
/* PHY_DQ_TIMING_REG */
#define CP_IO_MASK_ALWAYS_ON(x) ((x) << 31)
#define CP_IO_MASK_END(x) ((x) << 27)
#define CP_IO_MASK_START(x) ((x) << 24)
#define CP_DATA_SELECT_OE_END(x) (x)
/* SW RESET REG */
#define SDHC_CDNS_HRS00 (0x00)
#define CDNS_HRS00_SWR BIT(0)
/* PHY access port */
#define SDHC_CDNS_HRS04 0x10
#define CDNS_HRS04_ADDR GENMASK(5, 0)
/* PHY data access port */
#define SDHC_CDNS_HRS05 0x14
/* eMMC control registers */
#define SDHC_CDNS_HRS06 0x18
/* PHY_CTRL_REG */
#define CP_PHONY_DQS_TIMING_MASK 0x3F
#define CP_PHONY_DQS_TIMING_SHIFT 4
/* SRS */
#define SDHC_CDNS_SRS00 0x200
#define SDHC_CDNS_SRS01 0x204
#define SDHC_CDNS_SRS02 0x208
#define SDHC_CDNS_SRS03 0x20c
#define SDHC_CDNS_SRS04 0x210
#define SDHC_CDNS_SRS05 0x214
#define SDHC_CDNS_SRS06 0x218
#define SDHC_CDNS_SRS07 0x21C
#define SDHC_CDNS_SRS08 0x220
#define SDHC_CDNS_SRS09 0x224
#define SDHC_CDNS_SRS10 0x228
#define SDHC_CDNS_SRS11 0x22C
#define SDHC_CDNS_SRS12 0x230
#define SDHC_CDNS_SRS13 0x234
#define SDHC_CDNS_SRS14 0x238
#define SDHC_CDNS_SRS15 0x23c
#define SDHC_CDNS_SRS21 0x254
#define SDHC_CDNS_SRS22 0x258
#define SDHC_CDNS_SRS23 0x25c
/* SRS00 */
#define CDNS_SRS00_SAAR 1
/* SRS03 */
#define CDNS_SRS03_CMD_START BIT(31)
#define CDNS_SRS03_CMD_USE_HOLD_REG BIT(29)
#define CDNS_SRS03_CMD_UPDATE_CLK_ONLY BIT(21)
#define CDNS_SRS03_CMD_SEND_INIT BIT(15)
/* Command type */
#define CDNS_SRS03_CMD_TYPE BIT(22)
#define CMD_STOP_ABORT_CMD (3 << 22)
#define CMD_RESUME_CMD (2 << 22)
#define CMD_SUSPEND_CMD BIT(22)
#define CDNS_SRS03_DATA_PRSNT BIT(21)
#define CDNS_SRS03_CMD_IDX_CHK_EN BIT(20)
#define CDNS_SRS03_CMD_READ BIT(4)
#define CDNS_SRS03_MULTI_BLK_READ BIT(5)
#define CDNS_SRS03_RESP_ERR BIT(7)
#define CDNS_SRS03_RESP_CRC BIT(19)
#define CDNS_SRS03_CMD_DONE BIT(0)
/* Response type select */
#define CDNS_SRS03_RES_TYPE_SEL BIT(16)
#define RES_TYPE_SEL_48 (2 << 16)
#define RES_TYPE_SEL_136 (1 << 16)
#define RES_TYPE_SEL_48_B (3 << 16)
#define RES_TYPE_SEL_NO (0 << 16)
/* Auto CMD Enable */
#define CDNS_SRS03_AUTO_CMD_EN BIT(2)
#define AUTO_CMD23 (2 << 2)
#define AUTO_CMD12 BIT(2)
#define AUTO_CMD_AUTO (3 << 2)
#define CDNS_SRS03_COM_IDX 24
#define ERROR_INT BIT(15)
#define CDNS_SRS03_DMA_EN BIT(0)
#define CDNS_SRS03_BLK_CNT_EN BIT(1)
/* HRS07 */
#define SDHC_CDNS_HRS07 0x1c
#define CDNS_HRS07_IDELAY_VAL(x) (x)
#define CDNS_HRS07_RW_COMPENSATE(x) ((x) << 16)
/* PHY reset port */
#define SDHC_CDNS_HRS09 0x24
/* HRS10 */
/* PHY reset port */
#define SDHC_CDNS_HRS10 0x28
/* HCSDCLKADJ DATA; DDR Mode */
#define SDHC_HRS10_HCSDCLKADJ(x) ((x) << 16)
/* HRS16 */
#define CDNS_HRS16_WRCMD0_DLY(x) (x)
#define CDNS_HRS16_WRCMD1_DLY(x) ((x) << 4)
#define CDNS_HRS16_WRDATA0_DLY(x) ((x) << 8)
#define CDNS_HRS16_WRDATA1_DLY(x) ((x) << 12)
#define CDNS_HRS16_WRCMD0_SDCLK_DLY(x) ((x) << 16)
#define CDNS_HRS16_WRCMD1_SDCLK_DLY(x) ((x) << 20)
#define CDNS_HRS16_WRDATA0_SDCLK_DLY(x) ((x) << 24)
#define CDNS_HRS16_WRDATA1_SDCLK_DLY(x) ((x) << 28)
/* Shared Macros */
#define SDMMC_CDN(_reg) (SDMMC_CDN_REG_BASE + \
(SDMMC_CDN_##_reg))
/* MMC Peripheral Definition */
#define MMC_BLOCK_SIZE 512U
#define MMC_BLOCK_MASK (MMC_BLOCK_SIZE - 1)
#define MMC_BOOT_CLK_RATE (400 * 1000)
#define OCR_POWERUP BIT(31)
#define OCR_HCS BIT(30)
#define OCR_3_5_3_6 BIT(23)
#define OCR_3_4_3_5 BIT(22)
#define OCR_3_3_3_4 BIT(21)
#define OCR_3_2_3_3 BIT(20)
#define OCR_3_1_3_2 BIT(19)
#define OCR_3_0_3_1 BIT(18)
#define OCR_2_9_3_0 BIT(17)
#define OCR_2_8_2_9 BIT(16)
#define OCR_2_7_2_8 BIT(15)
#define OCR_VDD_MIN_2V7 GENMASK(23, 15)
#define OCR_VDD_MIN_2V0 GENMASK(14, 8)
#define OCR_VDD_MIN_1V7 BIT(7)
#define MMC_RSP_48 BIT(0)
#define MMC_RSP_136 BIT(1) /* 136 bit response */
#define MMC_RSP_CRC BIT(2) /* expect valid crc */
#define MMC_RSP_CMD_IDX BIT(3) /* response contains cmd idx */
#define MMC_RSP_BUSY BIT(4) /* device may be busy */
/* JEDEC 4.51 chapter 6.12 */
#define MMC_RESPONSE_R1 (MMC_RSP_48 | MMC_RSP_CMD_IDX | MMC_RSP_CRC)
#define MMC_RESPONSE_R1B (MMC_RESPONSE_R1 | MMC_RSP_BUSY)
#define MMC_RESPONSE_R2 (MMC_RSP_48 | MMC_RSP_136 | MMC_RSP_CRC)
#define MMC_RESPONSE_R3 (MMC_RSP_48)
#define MMC_RESPONSE_R4 (MMC_RSP_48)
#define MMC_RESPONSE_R5 (MMC_RSP_48 | MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R6 (MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R7 (MMC_RSP_48 | MMC_RSP_CRC)
#define MMC_RESPONSE_NONE 0
/* Value randomly chosen for eMMC RCA, it should be > 1 */
#define MMC_FIX_RCA 6
#define RCA_SHIFT_OFFSET 16
#define CMD_EXTCSD_PARTITION_CONFIG 179
#define CMD_EXTCSD_BUS_WIDTH 183
#define CMD_EXTCSD_HS_TIMING 185
#define CMD_EXTCSD_SEC_CNT 212
#define PART_CFG_BOOT_PARTITION1_ENABLE BIT(3)
#define PART_CFG_PARTITION1_ACCESS 1
/* Values in EXT CSD register */
#define MMC_BUS_WIDTH_1 0
#define MMC_BUS_WIDTH_4 1
#define MMC_BUS_WIDTH_8 2
#define MMC_BUS_WIDTH_DDR_4 5
#define MMC_BUS_WIDTH_DDR_8 6
#define MMC_BOOT_MODE_BACKWARD 0
#define MMC_BOOT_MODE_HS_TIMING BIT(3)
#define MMC_BOOT_MODE_DDR (2 << 3)
#define EXTCSD_SET_CMD 0
#define EXTCSD_SET_BITS BIT(24)
#define EXTCSD_CLR_BITS (2 << 24)
#define EXTCSD_WRITE_BYTES (3 << 24)
#define EXTCSD_CMD(x) (((x) & 0xff) << 16)
#define EXTCSD_VALUE(x) (((x) & 0xff) << 8)
#define EXTCSD_CMD_SET_NORMAL 1
#define CSD_TRAN_SPEED_UNIT_MASK GENMASK(2, 0)
#define CSD_TRAN_SPEED_MULT_MASK GENMASK(6, 3)
#define CSD_TRAN_SPEED_MULT_SHIFT 3
#define STATUS_CURRENT_STATE(x) (((x) & 0xf) << 9)
#define STATUS_READY_FOR_DATA BIT(8)
#define STATUS_SWITCH_ERROR BIT(7)
#define MMC_GET_STATE(x) (((x) >> 9) & 0xf)
#define MMC_STATE_IDLE 0
#define MMC_STATE_READY 1
#define MMC_STATE_IDENT 2
#define MMC_STATE_STBY 3
#define MMC_STATE_TRAN 4
#define MMC_STATE_DATA 5
#define MMC_STATE_RCV 6
#define MMC_STATE_PRG 7
#define MMC_STATE_DIS 8
#define MMC_STATE_BTST 9
#define MMC_STATE_SLP 10
#define MMC_FLAG_CMD23 1
#define CMD8_CHECK_PATTERN 0xAA
#define VHS_2_7_3_6_V BIT(8)
#define SD_SCR_BUS_WIDTH_1 BIT(8)
#define SD_SCR_BUS_WIDTH_4 BIT(10)
/* ADMA table component */
#define ADMA_DESC_ATTR_VALID BIT(0)
#define ADMA_DESC_ATTR_END BIT(1)
#define ADMA_DESC_ATTR_INT BIT(2)
#define ADMA_DESC_ATTR_ACT1 BIT(4)
#define ADMA_DESC_ATTR_ACT2 BIT(5)
#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2
/* SRS10 Register */
#define LEDC BIT(0)
#define DT_WIDTH BIT(1)
#define HS_EN BIT(2)
/* Conf depends on SRS15.HV4E */
#define SDMA 0
#define ADMA2_32 (2 << 3)
#define ADMA2_64 (3 << 3)
/* here 0 defines the 64 Kb size */
#define MAX_64KB_PAGE 0
struct sdmmc_cmd {
unsigned int cmd_idx;
unsigned int cmd_arg;
unsigned int resp_type;
unsigned int resp_data[4];
};
struct sdhc_cdns_ops {
/* init function for card */
int (*init)(void);
/* busy check function for card */
int (*busy)(void);
/* card_present function check for card */
int (*card_present)(void);
/* reset the card */
int (*reset)(void);
/* send command and respective argument */
int (*send_cmd)(struct sdmmc_cmd *cmd, struct sdhc_data *data);
/* io set up for card */
int (*set_ios)(unsigned int clk, unsigned int width);
/* prepare dma descriptors */
int (*prepare)(uint32_t lba, uintptr_t buf, struct sdhc_data *data);
/* cache invd api */
int (*cache_invd)(int lba, uintptr_t buf, size_t size);
};
/* Combo Phy reg */
struct sdhc_cdns_combo_phy {
uint32_t cp_clk_wr_delay;
uint32_t cp_clk_wrdqs_delay;
uint32_t cp_data_select_oe_end;
uint32_t cp_dll_bypass_mode;
uint32_t cp_dll_locked_mode;
uint32_t cp_dll_start_point;
uint32_t cp_gate_cfg_always_on;
uint32_t cp_io_mask_always_on;
uint32_t cp_io_mask_end;
uint32_t cp_io_mask_start;
uint32_t cp_rd_del_sel;
uint32_t cp_read_dqs_cmd_delay;
uint32_t cp_read_dqs_delay;
uint32_t cp_sw_half_cycle_shift;
uint32_t cp_sync_method;
uint32_t cp_underrun_suppress;
uint32_t cp_use_ext_lpbk_dqs;
uint32_t cp_use_lpbk_dqs;
uint32_t cp_use_phony_dqs;
uint32_t cp_use_phony_dqs_cmd;
};
/* sdmmc reg */
struct sdhc_cdns_sdmmc {
uint32_t sdhc_extended_rd_mode;
uint32_t sdhc_extended_wr_mode;
uint32_t sdhc_hcsdclkadj;
uint32_t sdhc_idelay_val;
uint32_t sdhc_rdcmd_en;
uint32_t sdhc_rddata_en;
uint32_t sdhc_rw_compensate;
uint32_t sdhc_sdcfsh;
uint32_t sdhc_sdcfsl;
uint32_t sdhc_wrcmd0_dly;
uint32_t sdhc_wrcmd0_sdclk_dly;
uint32_t sdhc_wrcmd1_dly;
uint32_t sdhc_wrcmd1_sdclk_dly;
uint32_t sdhc_wrdata0_dly;
uint32_t sdhc_wrdata0_sdclk_dly;
uint32_t sdhc_wrdata1_dly;
uint32_t sdhc_wrdata1_sdclk_dly;
};
enum sdmmc_device_mode {
/* Identification */
SD_DS_ID,
/* Default speed */
SD_DS,
/* High speed */
SD_HS,
/* Ultra high speed SDR12 */
SD_UHS_SDR12,
/* Ultra high speed SDR25 */
SD_UHS_SDR25,
/* Ultra high speed SDR`50 */
SD_UHS_SDR50,
/* Ultra high speed SDR104 */
SD_UHS_SDR104,
/* Ultra high speed DDR50 */
SD_UHS_DDR50,
/* SDR backward compatible */
EMMC_SDR_BC,
/* SDR */
EMMC_SDR,
/* DDR */
EMMC_DDR,
/* High speed 200Mhz in SDR */
EMMC_HS200,
/* High speed 200Mhz in DDR */
EMMC_HS400,
/* High speed 200Mhz in SDR with enhanced strobe */
EMMC_HS400ES,
};
struct sdhc_cdns_params {
uintptr_t reg_base;
uintptr_t reg_phy;
uintptr_t desc_base;
size_t desc_size;
int clk_rate;
int bus_width;
unsigned int flags;
enum sdmmc_device_mode cdn_sdmmc_dev_type;
uint32_t combophy;
};
struct sdmmc_device_info {
/* Size of device in bytes */
unsigned long long device_size;
/* Block size in bytes */
unsigned int block_size;
/* Max bus freq in Hz */
unsigned int max_bus_freq;
/* OCR voltage */
unsigned int ocr_voltage;
/* Type of MMC */
enum sdmmc_device_mode cdn_sdmmc_dev_type;
};
/*descriptor structure with 8 byte alignment*/
struct sdhc_cdns_desc {
/* 8 bit attribute */
uint8_t attr;
/* reserved bits in desc */
uint8_t reserved;
/* page length for the descriptor */
uint16_t len;
/* lower 32 bits for buffer (64 bit addressing) */
uint32_t addr_lo;
/* higher 32 bits for buffer (64 bit addressing) */
uint32_t addr_hi;
} __aligned(8);
void sdhc_cdns_sdmmc_init(struct sdhc_cdns_params *params, struct sdmmc_device_info *info,
const struct sdhc_cdns_ops **cb_sdmmc_ops);

791
drivers/sdhc/sdhc_cdns_ll.c Normal file
View file

@ -0,0 +1,791 @@
/*
* Copyright (C) 2023 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <stddef.h>
#include "sdhc_cdns_ll.h"
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(sdhc_cdns_ll, CONFIG_SDHC_LOG_LEVEL);
/* card busy and present */
#define CARD_BUSY 1
#define CARD_NOT_BUSY 0
#define CARD_PRESENT 1
/* SRS12 error mask */
#define CDNS_SRS12_ERR_MASK 0xFFFF8000U
#define CDNS_CSD_BYTE_MASK 0x000000FFU
/* General define */
#define SDHC_REG_MASK 0xFFFFFFFFU
#define SD_HOST_BLOCK_SIZE 0x200
#define SDMMC_DMA_MAX_BUFFER_SIZE (64 * 1024)
#define CDNSMMC_ADDRESS_MASK (CONFIG_SDHC_BUFFER_ALIGNMENT - 1)
#define SRS10_VAL_READ (ADMA2_32 | HS_EN | DT_WIDTH)
#define SRS10_VAL_SW (ADMA2_32 | DT_WIDTH)
#define SRS11_VAL_GEN (READ_CLK | CDNS_SRS11_ICE | CDNS_SRS11_ICS | CDNS_SRS11_SDCE)
#define SRS11_VAL_CID (CDNS_SRS11_ICE | CDNS_SRS11_ICS | CDNS_SRS11_SDCE)
#define SRS15_VAL_GEN (CDNS_SRS15_BIT_AD_64 | CDNS_SRS15_HV4E | CDNS_SRS15_V18SE)
#define SRS15_VAL_RD_WR (SRS15_VAL_GEN | CDNS_SRS15_SDR104 | CDNS_SRS15_PVE)
#define SRS15_VAL_CID (CDNS_SRS15_HV4E | CDNS_SRS15_V18SE)
#define CARD_REG_TIME_DELAY_US 100000
#define WAIT_ICS_TIME_DELAY_US 5000
#define RESET_SRS14 0x00000000
static struct sdhc_cdns_params cdns_params;
static struct sdhc_cdns_combo_phy sdhc_cdns_combo_phy_reg_info;
static struct sdhc_cdns_sdmmc sdhc_cdns_sdmmc_reg_info;
/* Function to write general phy registers */
static int sdhc_cdns_write_phy_reg(uint32_t phy_reg_addr, uint32_t phy_reg_addr_value,
uint32_t phy_reg_data, uint32_t phy_reg_data_value)
{
uint32_t data = 0;
/* Set PHY register address, write HRS04*/
sys_write32(phy_reg_addr_value, phy_reg_addr);
/* Set PHY register data, write HRS05 */
sys_write32(phy_reg_data_value, phy_reg_data);
data = sys_read32(phy_reg_data);
if (data != phy_reg_data_value) {
LOG_ERR("PHY_REG_DATA is not set properly");
return -ENXIO;
}
return 0;
}
int sdhc_cdns_wait_ics(uint16_t timeout, uint32_t cdn_srs_res)
{
/* Wait status command response ready */
if (!WAIT_FOR(((sys_read32(cdn_srs_res) & CDNS_SRS11_ICS)
== CDNS_SRS11_ICS), timeout, k_msleep(1))) {
LOG_ERR("Timed out waiting for ICS response");
return -ETIMEDOUT;
}
return 0;
}
static int sdhc_cdns_busy(void)
{
unsigned int data;
data = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS09);
return (data & CDNS_SRS09_STAT_DAT_BUSY) ? CARD_BUSY : CARD_NOT_BUSY;
}
static int sdhc_cdns_card_present(void)
{
uint32_t timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR((((sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS09))
& CDNS_SRS09_CI) == CDNS_SRS09_CI),
timeout, k_msleep(1))) {
LOG_ERR("Card detection timeout");
return -ETIMEDOUT;
}
return CARD_PRESENT;
}
static int sdhc_cdns_vol_reset(void)
{
/* Reset embedded card */
sys_write32((7 << CDNS_SRS10_BVS) | (0 << CDNS_SRS10_BP),
(cdns_params.reg_base + SDHC_CDNS_SRS10));
/*
* Turn on supply voltage
* CDNS_SRS10_BVS = 7, CDNS_SRS10_BP = 1, BP2 only in UHS2 mode
*/
sys_write32((7 << CDNS_SRS10_BVS) | (1 << CDNS_SRS10_BP),
(cdns_params.reg_base + SDHC_CDNS_SRS10));
return 0;
}
/*
* Values are taken from IP documents and calc_setting.py script
* with input value- mode sd_ds,
* sdmclk 5000,
* sdclk 10000,
* iocell_input_delay 2500,
* iocell_output_delay 2500 and
* delay_element 24
*/
void cdns_sdhc_set_sdmmc_params(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
/* Values are taken by the reference of cadence IP documents */
sdhc_cdns_combo_phy_reg->cp_clk_wr_delay = 0;
sdhc_cdns_combo_phy_reg->cp_clk_wrdqs_delay = 0;
sdhc_cdns_combo_phy_reg->cp_data_select_oe_end = 1;
sdhc_cdns_combo_phy_reg->cp_dll_bypass_mode = 1;
sdhc_cdns_combo_phy_reg->cp_dll_locked_mode = 3;
sdhc_cdns_combo_phy_reg->cp_dll_start_point = 4;
sdhc_cdns_combo_phy_reg->cp_gate_cfg_always_on = 1;
sdhc_cdns_combo_phy_reg->cp_io_mask_always_on = 0;
sdhc_cdns_combo_phy_reg->cp_io_mask_end = 2;
sdhc_cdns_combo_phy_reg->cp_io_mask_start = 0;
sdhc_cdns_combo_phy_reg->cp_rd_del_sel = 52;
sdhc_cdns_combo_phy_reg->cp_read_dqs_cmd_delay = 0;
sdhc_cdns_combo_phy_reg->cp_read_dqs_delay = 0;
sdhc_cdns_combo_phy_reg->cp_sw_half_cycle_shift = 0;
sdhc_cdns_combo_phy_reg->cp_sync_method = 1;
sdhc_cdns_combo_phy_reg->cp_underrun_suppress = 1;
sdhc_cdns_combo_phy_reg->cp_use_ext_lpbk_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_lpbk_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_phony_dqs = 1;
sdhc_cdns_combo_phy_reg->cp_use_phony_dqs_cmd = 1;
sdhc_cdns_sdmmc_reg->sdhc_extended_rd_mode = 1;
sdhc_cdns_sdmmc_reg->sdhc_extended_wr_mode = 1;
sdhc_cdns_sdmmc_reg->sdhc_hcsdclkadj = 6;
sdhc_cdns_sdmmc_reg->sdhc_idelay_val = 1;
sdhc_cdns_sdmmc_reg->sdhc_rdcmd_en = 1;
sdhc_cdns_sdmmc_reg->sdhc_rddata_en = 1;
sdhc_cdns_sdmmc_reg->sdhc_rw_compensate = 10;
sdhc_cdns_sdmmc_reg->sdhc_sdcfsh = 0;
sdhc_cdns_sdmmc_reg->sdhc_sdcfsl = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_dly = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata0_dly = 1;
sdhc_cdns_sdmmc_reg->sdhc_wrdata0_sdclk_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata1_dly = 0;
sdhc_cdns_sdmmc_reg->sdhc_wrdata1_sdclk_dly = 0;
}
/* Phy register programing for phy init */
static int sdhc_cdns_program_phy_reg(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
uint32_t value = 0;
int ret = 0;
/*
* program PHY_DQS_TIMING_REG
* This register controls the DQS related timing
*/
value = (CP_USE_EXT_LPBK_DQS(sdhc_cdns_combo_phy_reg->cp_use_ext_lpbk_dqs))
| (CP_USE_LPBK_DQS(sdhc_cdns_combo_phy_reg->cp_use_lpbk_dqs))
| (CP_USE_PHONY_DQS(sdhc_cdns_combo_phy_reg->cp_use_phony_dqs))
| (CP_USE_PHONY_DQS_CMD(sdhc_cdns_combo_phy_reg->cp_use_phony_dqs_cmd));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DQS_TIMING_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DQS_TIMING_REG programming");
return ret;
}
/*
* program PHY_GATE_LPBK_CTRL_REG
* This register controls the gate and loopback control related timing.
*/
value = (CP_SYNC_METHOD(sdhc_cdns_combo_phy_reg->cp_sync_method))
| (CP_SW_HALF_CYCLE_SHIFT(sdhc_cdns_combo_phy_reg->cp_sw_half_cycle_shift))
| (CP_RD_DEL_SEL(sdhc_cdns_combo_phy_reg->cp_rd_del_sel))
| (CP_UNDERRUN_SUPPRESS(sdhc_cdns_combo_phy_reg->cp_underrun_suppress))
| (CP_GATE_CFG_ALWAYS_ON(sdhc_cdns_combo_phy_reg->cp_gate_cfg_always_on));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_GATE_LPBK_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_GATE_LPBK_CTRL_REG programming");
return -ret;
}
/*
* program PHY_DLL_MASTER_CTRL_REG
* This register holds the control for the Master DLL logic.
*/
value = (CP_DLL_BYPASS_MODE(sdhc_cdns_combo_phy_reg->cp_dll_bypass_mode))
| (CP_DLL_START_POINT(sdhc_cdns_combo_phy_reg->cp_dll_start_point));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DLL_MASTER_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DLL_MASTER_CTRL_REG programming");
return ret;
}
/*
* program PHY_DLL_SLAVE_CTRL_REG
* This register holds the control for the slave DLL logic.
*/
value = (CP_READ_DQS_CMD_DELAY(sdhc_cdns_combo_phy_reg->cp_read_dqs_cmd_delay))
| (CP_CLK_WRDQS_DELAY(sdhc_cdns_combo_phy_reg->cp_clk_wrdqs_delay))
| (CP_CLK_WR_DELAY(sdhc_cdns_combo_phy_reg->cp_clk_wr_delay))
| (CP_READ_DQS_DELAY(sdhc_cdns_combo_phy_reg->cp_read_dqs_delay));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DLL_SLAVE_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DLL_SLAVE_CTRL_REG programming");
return ret;
}
/*
* program PHY_CTRL_REG
* This register handles the global control settings for the PHY.
*/
sys_write32(cdns_params.combophy + PHY_CTRL_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS04);
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS05);
/* phony_dqs_timing=0 */
value &= ~(CP_PHONY_DQS_TIMING_MASK << CP_PHONY_DQS_TIMING_SHIFT);
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS05);
/* switch off DLL_RESET */
do {
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09);
value |= CDNS_HRS09_PHY_SW_RESET;
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS09);
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09);
/* polling PHY_INIT_COMPLETE */
} while ((value & CDNS_HRS09_PHY_INIT_COMP) != CDNS_HRS09_PHY_INIT_COMP);
/*
* program PHY_DQ_TIMING_REG
* This register controls the DQ related timing.
*/
sdhc_cdns_combo_phy_reg->cp_io_mask_end = 0U;
value = (CP_IO_MASK_ALWAYS_ON(sdhc_cdns_combo_phy_reg->cp_io_mask_always_on))
| (CP_IO_MASK_END(sdhc_cdns_combo_phy_reg->cp_io_mask_end))
| (CP_IO_MASK_START(sdhc_cdns_combo_phy_reg->cp_io_mask_start))
| (CP_DATA_SELECT_OE_END(sdhc_cdns_combo_phy_reg->cp_data_select_oe_end));
ret = sdhc_cdns_write_phy_reg(cdns_params.reg_base + SDHC_CDNS_HRS04,
cdns_params.combophy + PHY_DQ_TIMING_REG, cdns_params.reg_base
+ SDHC_CDNS_HRS05, value);
if (ret != 0U) {
LOG_ERR("Error in PHY_DQ_TIMING_REG programming");
return ret;
}
return 0;
}
static int sdhc_cdns_cache_invd(int lba, uintptr_t buf, size_t size)
{
int ret = 0;
ret = arch_dcache_invd_range((void *)buf, size);
if (ret != 0) {
LOG_ERR("%s: error in invalidate dcache with ret %d", __func__, ret);
return ret;
}
return 0;
}
/* DMA preparation for the read and write operation */
static int sdhc_cdns_prepare(uint32_t dma_start_addr, uintptr_t dma_buff,
struct sdhc_data *data)
{
struct sdhc_cdns_desc *desc;
uint32_t desc_cnt, i;
uintptr_t base;
uint64_t desc_base;
uint32_t size = data->blocks * data->block_size;
__ASSERT_NO_MSG(((dma_buff & CDNSMMC_ADDRESS_MASK) == 0) &&
(cdns_params.desc_size > 0) &&
((cdns_params.desc_size & MMC_BLOCK_MASK) == 0));
arch_dcache_flush_range((void *)dma_buff, size);
desc_cnt = (size + (SDMMC_DMA_MAX_BUFFER_SIZE) - 1) /
(SDMMC_DMA_MAX_BUFFER_SIZE);
__ASSERT_NO_MSG(desc_cnt * sizeof(struct sdhc_cdns_desc) <
cdns_params.desc_size);
if (desc_cnt > CONFIG_CDNS_DESC_COUNT) {
LOG_ERR("Requested data transfer length %u greater than configured length %u",
size, (CONFIG_CDNS_DESC_COUNT * SDMMC_DMA_MAX_BUFFER_SIZE));
return -EINVAL;
}
/*
* Creating descriptor as per the desc_count and linked list of
* descriptor for contiguous desc alignment
*/
base = cdns_params.reg_base;
desc = (struct sdhc_cdns_desc *)cdns_params.desc_base;
desc_base = (uint64_t)desc;
i = 0;
while ((i+1) < desc_cnt) {
desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
desc->reserved = 0;
desc->len = MAX_64KB_PAGE;
desc->addr_lo = (dma_buff & 0xffffffff) + (SDMMC_DMA_MAX_BUFFER_SIZE * i);
desc->addr_hi = (dma_buff >> 32) & 0xffffffff;
size -= SDMMC_DMA_MAX_BUFFER_SIZE;
desc++;
i++;
}
desc->attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA |
ADMA_DESC_ATTR_END;
desc->reserved = 0;
desc->len = size;
desc->addr_lo = (dma_buff & 0xffffffff) + (SDMMC_DMA_MAX_BUFFER_SIZE * i);
desc->addr_hi = (dma_buff >> 32) & 0xffffffff;
sys_write32((uint32_t)desc_base, cdns_params.reg_base + SDHC_CDNS_SRS22);
sys_write32((uint32_t)(desc_base >> 32), cdns_params.reg_base + SDHC_CDNS_SRS23);
arch_dcache_flush_range((void *)cdns_params.desc_base,
desc_cnt * sizeof(struct sdhc_cdns_desc));
sys_write32((data->block_size << CDNS_SRS01_BLK_SIZE |
data->blocks << CDNS_SRS01_BLK_COUNT_CT | CDNS_SRS01_SDMA_BUF),
cdns_params.reg_base + SDHC_CDNS_SRS01);
return 0;
}
static int sdhc_cdns_host_set_clk(int clk)
{
uint32_t sdclkfsval = 0;
uint32_t dtcvval = 0xe;
int ret = 0;
sdclkfsval = (cdns_params.clk_rate / 2000) / clk;
sys_write32(0, cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32(((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS) |
(1 << CDNS_SRS11_ICE)), cdns_params.reg_base + SDHC_CDNS_SRS11);
ret = sdhc_cdns_wait_ics(WAIT_ICS_TIME_DELAY_US, cdns_params.reg_base + SDHC_CDNS_SRS11);
if (ret != 0) {
return ret;
}
/* Enable DLL reset */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 0);
/* Set extended_wr_mode */
sys_write32(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09)
& 0xFFFFFFF7) | (1 << CDNS_HRS09_EXT_WR_MODE)), (cdns_params.reg_base
+ SDHC_CDNS_HRS09));
/* Release DLL reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 1);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_HRS09, (3 << CDNS_HRS09_RDCMD_EN));
sys_write32(((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS)
| (1 << CDNS_SRS11_ICE) | (1 << CDNS_SRS11_SDCE)),
cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32(0xFFFFFFFF, cdns_params.reg_base + SDHC_CDNS_SRS13);
return 0;
}
static int sdhc_cdns_set_ios(unsigned int clk, unsigned int width)
{
int ret = 0;
switch (width) {
case SDHC_BUS_WIDTH1BIT:
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT1);
break;
case SDHC_BUS_WIDTH4BIT:
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT4);
break;
case SDHC_BUS_WIDTH8BIT:
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_SRS10, WIDTH_BIT8);
break;
default:
__ASSERT_NO_MSG(0);
break;
}
/* Perform clock configuration when SD clock is not gated */
if (clk != 0) {
ret = sdhc_cdns_host_set_clk(clk);
if (ret != 0) {
LOG_ERR("%s: Clock configuration failed", __func__);
return ret;
}
}
return 0;
}
/* Programming HRS register for initialisation */
static int sdhc_cdns_init_hrs_io(struct sdhc_cdns_combo_phy *sdhc_cdns_combo_phy_reg,
struct sdhc_cdns_sdmmc *sdhc_cdns_sdmmc_reg)
{
uint32_t value = 0;
int ret = 0;
/*
* program HRS09, register 42
* PHY Control and Status Register
*/
value = (CDNS_HRS09_RDDATA_EN(sdhc_cdns_sdmmc_reg->sdhc_rddata_en))
| (CDNS_HRS09_RDCMD(sdhc_cdns_sdmmc_reg->sdhc_rdcmd_en))
| (CDNS_HRS09_EXTENDED_WR(sdhc_cdns_sdmmc_reg->sdhc_extended_wr_mode))
| (CDNS_HRS09_EXT_RD_MODE(sdhc_cdns_sdmmc_reg->sdhc_extended_rd_mode));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS09);
/*
* program HRS10, register 43
* Host Controller SDCLK start point adjustment
*/
value = (SDHC_HRS10_HCSDCLKADJ(sdhc_cdns_sdmmc_reg->sdhc_hcsdclkadj));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS10);
/*
* program HRS16, register 48
* CMD/DAT output delay
*/
value = (CDNS_HRS16_WRDATA1_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata1_sdclk_dly))
| (CDNS_HRS16_WRDATA0_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata0_sdclk_dly))
| (CDNS_HRS16_WRCMD1_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_sdclk_dly))
| (CDNS_HRS16_WRCMD0_SDCLK_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_sdclk_dly))
| (CDNS_HRS16_WRDATA1_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata1_dly))
| (CDNS_HRS16_WRDATA0_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrdata0_dly))
| (CDNS_HRS16_WRCMD1_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd1_dly))
| (CDNS_HRS16_WRCMD0_DLY(sdhc_cdns_sdmmc_reg->sdhc_wrcmd0_dly));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS16);
/*
* program HRS07, register 40
* IO Delay Information Register
*/
value = (CDNS_HRS07_RW_COMPENSATE(sdhc_cdns_sdmmc_reg->sdhc_rw_compensate))
| (CDNS_HRS07_IDELAY_VAL(sdhc_cdns_sdmmc_reg->sdhc_idelay_val));
sys_write32(value, cdns_params.reg_base + SDHC_CDNS_HRS07);
return ret;
}
static int sdhc_cdns_set_clk(struct sdhc_cdns_params *cdn_sdmmc_dev_type_params)
{
uint32_t dtcvval, sdclkfsval;
int ret = 0;
dtcvval = DTC_VAL;
sdclkfsval = 0;
/* Condition for Default speed mode and SDR12 and SDR_BC */
if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_DS) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR12) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_SDR_BC)) {
sdclkfsval = 4;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_HS) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR25) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_DDR50) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_SDR)) {
sdclkfsval = 2;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR50) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_DDR) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS400) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS400ES)) {
sdclkfsval = 1;
} else if ((cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == SD_UHS_SDR104) ||
(cdn_sdmmc_dev_type_params->cdn_sdmmc_dev_type == EMMC_HS200)) {
sdclkfsval = 0;
}
/* Disabling SD clock enable */
sys_write32(0, cdns_params.reg_base + SDHC_CDNS_SRS11);
sys_write32((dtcvval << CDNS_SRS11_DTCV) |
(sdclkfsval << CDNS_SRS11_SDCLKFS) | (1 << CDNS_SRS11_ICE),
cdns_params.reg_base + SDHC_CDNS_SRS11);
ret = sdhc_cdns_wait_ics(WAIT_ICS_TIME_DELAY_US, cdns_params.reg_base + SDHC_CDNS_SRS11);
if (ret != 0) {
return ret;
}
/* Enable DLL reset */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 0);
/* Set extended_wr_mode */
sys_write32(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS09) &
0xFFFFFFF7) | (1 << CDNS_HRS09_EXT_WR_MODE)), (cdns_params.reg_base
+ SDHC_CDNS_HRS09));
/* Release DLL reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, 1);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_HRS09, (3 << CDNS_HRS09_RDCMD_EN));
sys_write32((dtcvval << CDNS_SRS11_DTCV) | (sdclkfsval << CDNS_SRS11_SDCLKFS) |
(1 << CDNS_SRS11_ICE) | (1 << CDNS_SRS11_SDCE), cdns_params.reg_base
+ SDHC_CDNS_SRS11);
sys_write32(0xFFFFFFFF, cdns_params.reg_base + SDHC_CDNS_SRS13);
return 0;
}
static int sdhc_cdns_reset(void)
{
int32_t timeout;
sys_clear_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, 0xFFFF);
/* Software reset */
sys_set_bit(cdns_params.reg_base + SDHC_CDNS_HRS00, CDNS_HRS00_SWR);
/* Wait status command response ready */
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR(((sys_read32(cdns_params.reg_base + SDHC_CDNS_HRS00) &
CDNS_HRS00_SWR) == 0), timeout, k_msleep(1))) {
LOG_ERR("Software reset is not completed...timedout");
return -ETIMEDOUT;
}
/* Step 1, switch on DLL_RESET */
sys_clear_bit(cdns_params.reg_base + SDHC_CDNS_HRS09, CDNS_HRS09_PHY_SW_RESET);
return 0;
}
static int sdhc_cdns_init(void)
{
int ret = 0;
ret = sdhc_cdns_program_phy_reg(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
if (ret != 0U) {
LOG_ERR("SoftPhy register configuration failed");
return ret;
}
ret = sdhc_cdns_init_hrs_io(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
if (ret != 0U) {
LOG_ERR("Configuration for HRS IO reg failed");
return ret;
}
ret = sdhc_cdns_card_present();
if (ret != CARD_PRESENT) {
LOG_ERR("SD card does not detect");
return -ETIMEDOUT;
}
ret = sdhc_cdns_vol_reset();
if (ret != 0U) {
LOG_ERR("SD/MMC card reset failed");
return ret;
}
ret = sdhc_cdns_set_clk(&cdns_params);
if (ret != 0U) {
LOG_ERR("Host controller set clk failed");
return ret;
}
return 0;
}
static int sdhc_cdns_send_cmd(struct sdmmc_cmd *cmd, struct sdhc_data *data)
{
uint32_t op = 0;
uint32_t value;
uintptr_t base;
int32_t timeout;
uint32_t cmd_indx;
uint32_t status_check = 0;
__ASSERT(cmd, "Assert %s function call", __func__);
base = cdns_params.reg_base;
cmd_indx = (cmd->cmd_idx) << CDNS_SRS03_COM_IDX;
if (data) {
switch (cmd->cmd_idx) {
case SD_SWITCH:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_SW);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_GEN);
break;
case SD_WRITE_SINGLE_BLOCK:
case SD_READ_SINGLE_BLOCK:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_READ);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_RD_WR);
sys_write32(CDNS_SRS00_SAAR, cdns_params.reg_base + SDHC_CDNS_SRS00);
break;
case SD_WRITE_MULTIPLE_BLOCK:
case SD_READ_MULTIPLE_BLOCK:
op = CDNS_SRS03_DATA_PRSNT | AUTO_CMD23 | CDNS_SRS03_MULTI_BLK_READ;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, SRS10_VAL_READ);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_RD_WR);
sys_write32(CDNS_SRS00_SAAR, cdns_params.reg_base + SDHC_CDNS_SRS00);
break;
case SD_APP_SEND_SCR:
op = CDNS_SRS03_DATA_PRSNT;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS10, ADMA2_32);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_GEN);
break;
default:
op = 0;
break;
}
} else {
switch (cmd->cmd_idx) {
case SD_GO_IDLE_STATE:
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_CID);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, CDNS_SRS15_HV4E);
break;
case SD_ALL_SEND_CID:
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_CID);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, SRS15_VAL_CID);
break;
case SD_SEND_IF_COND:
op = CDNS_SRS03_CMD_IDX_CHK_EN;
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS11, SRS11_VAL_GEN);
sys_set_bits(cdns_params.reg_base + SDHC_CDNS_SRS15, CDNS_SRS15_HV4E);
break;
case SD_STOP_TRANSMISSION:
op = CMD_STOP_ABORT_CMD;
break;
case SD_SEND_STATUS:
break;
case SD_SELECT_CARD:
op = CDNS_SRS03_MULTI_BLK_READ;
break;
default:
op = 0;
break;
}
}
switch (cmd->resp_type) {
case SD_RSP_TYPE_NONE:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN);
break;
case SD_RSP_TYPE_R2:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN |
RES_TYPE_SEL_136 | CDNS_SRS03_RESP_CRC);
break;
case SD_RSP_TYPE_R3:
op |= (CDNS_SRS03_CMD_READ | CDNS_SRS03_MULTI_BLK_READ |
CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | RES_TYPE_SEL_48);
break;
case SD_RSP_TYPE_R1:
if ((cmd->cmd_idx == SD_WRITE_SINGLE_BLOCK) || (cmd->cmd_idx
== SD_WRITE_MULTIPLE_BLOCK)) {
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | RES_TYPE_SEL_48
| CDNS_SRS03_RESP_CRC | CDNS_SRS03_CMD_IDX_CHK_EN);
} else {
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | CDNS_SRS03_CMD_READ
| RES_TYPE_SEL_48 | CDNS_SRS03_RESP_CRC | CDNS_SRS03_CMD_IDX_CHK_EN);
}
break;
default:
op |= (CDNS_SRS03_DMA_EN | CDNS_SRS03_BLK_CNT_EN | CDNS_SRS03_CMD_READ |
CDNS_SRS03_MULTI_BLK_READ | RES_TYPE_SEL_48 | CDNS_SRS03_RESP_CRC |
CDNS_SRS03_CMD_IDX_CHK_EN);
break;
}
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR((sdhc_cdns_busy() == 0), timeout, k_msleep(1))) {
k_panic();
}
sys_write32(~0, cdns_params.reg_base + SDHC_CDNS_SRS12);
sys_write32(cmd->cmd_arg, cdns_params.reg_base + SDHC_CDNS_SRS02);
sys_write32(RESET_SRS14, cdns_params.reg_base + SDHC_CDNS_SRS14);
sys_write32(op | cmd_indx, cdns_params.reg_base + SDHC_CDNS_SRS03);
timeout = CARD_REG_TIME_DELAY_US;
if (!WAIT_FOR(((((sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS12)) &
CDNS_SRS03_CMD_DONE) == 1) | (((sys_read32(cdns_params.reg_base +
SDHC_CDNS_SRS12)) & ERROR_INT) == ERROR_INT)), timeout, k_busy_wait(1))) {
LOG_ERR("Response timeout SRS12");
return -ETIMEDOUT;
}
value = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS12);
status_check = value & CDNS_SRS12_ERR_MASK;
if (status_check != 0U) {
LOG_ERR("SD host controller send command failed, SRS12 = %X", status_check);
return -EIO;
}
if ((op & RES_TYPE_SEL_48) || (op & RES_TYPE_SEL_136)) {
cmd->resp_data[0] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS04);
if (op & RES_TYPE_SEL_136) {
cmd->resp_data[1] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS05);
cmd->resp_data[2] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS06);
cmd->resp_data[3] = sys_read32(cdns_params.reg_base + SDHC_CDNS_SRS07);
/* 136-bit: RTS=01b, Response field R[127:8] - RESP3[23:0],
* RESP2[31:0], RESP1[31:0], RESP0[31:0]
* Subsystem expects 128 bits response but cadence SDHC sends
* 120 bits response from R[127:8]. Bits manupulation to address
* the correct responses for the 136 bit response type.
*/
cmd->resp_data[3] = ((cmd->resp_data[3] << 8) | ((cmd->resp_data[2] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[2] = ((cmd->resp_data[2] << 8) | ((cmd->resp_data[1] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[1] = ((cmd->resp_data[1] << 8) | ((cmd->resp_data[0] >> 24)
& CDNS_CSD_BYTE_MASK));
cmd->resp_data[0] = (cmd->resp_data[0] << 8);
}
}
return 0;
}
static const struct sdhc_cdns_ops cdns_sdmmc_ops = {
.init = sdhc_cdns_init,
.send_cmd = sdhc_cdns_send_cmd,
.card_present = sdhc_cdns_card_present,
.set_ios = sdhc_cdns_set_ios,
.prepare = sdhc_cdns_prepare,
.cache_invd = sdhc_cdns_cache_invd,
.busy = sdhc_cdns_busy,
.reset = sdhc_cdns_reset,
};
void sdhc_cdns_sdmmc_init(struct sdhc_cdns_params *params, struct sdmmc_device_info *info,
const struct sdhc_cdns_ops **cb_sdmmc_ops)
{
__ASSERT_NO_MSG((params != NULL) &&
((params->reg_base & MMC_BLOCK_MASK) == 0) &&
((params->desc_size & MMC_BLOCK_MASK) == 0) &&
((params->reg_phy & MMC_BLOCK_MASK) == 0) &&
(params->desc_size > 0) &&
(params->clk_rate > 0) &&
((params->bus_width == MMC_BUS_WIDTH_1) ||
(params->bus_width == MMC_BUS_WIDTH_4) ||
(params->bus_width == MMC_BUS_WIDTH_8)));
memcpy(&cdns_params, params, sizeof(struct sdhc_cdns_params));
cdns_params.cdn_sdmmc_dev_type = info->cdn_sdmmc_dev_type;
*cb_sdmmc_ops = &cdns_sdmmc_ops;
cdns_sdhc_set_sdmmc_params(&sdhc_cdns_combo_phy_reg_info, &sdhc_cdns_sdmmc_reg_info);
}

489
drivers/sdhc/sdhc_cdns_ll.h Normal file
View file

@ -0,0 +1,489 @@
/*
* Copyright (C) 2023 Intel Corporation
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/cache.h>
#include <zephyr/drivers/sdhc.h>
/* HRS09 */
#define CDNS_HRS09_PHY_SW_RESET BIT(0)
#define CDNS_HRS09_PHY_INIT_COMP BIT(1)
#define CDNS_HRS09_EXT_RD_MODE(x) ((x) << 2)
#define CDNS_HRS09_EXT_WR_MODE 3
#define CDNS_HRS09_EXTENDED_WR(x) ((x) << 3)
#define CDNS_HRS09_RDCMD_EN 15
#define CDNS_HRS09_RDCMD(x) ((x) << 15)
#define CDNS_HRS09_RDDATA_EN(x) ((x) << 16)
/* HRS00 */
#define CDNS_HRS00_SWR BIT(0)
/* CMD_DATA_OUTPUT */
#define SDHC_CDNS_HRS16 0x40
/* SRS09 */
#define CDNS_SRS09_STAT_DAT_BUSY BIT(2)
#define CDNS_SRS09_CI BIT(16)
/* SRS10 */
#define CDNS_SRS10_DTW 1
#define CDNS_SRS10_EDTW 5
#define CDNS_SRS10_BP 8
#define CDNS_SRS10_BVS 9
/* data bus width */
#define WIDTH_BIT1 CDNS_SRS10_DTW
#define WIDTH_BIT4 CDNS_SRS10_DTW
#define WIDTH_BIT8 CDNS_SRS10_EDTW
/* SRS11 */
#define CDNS_SRS11_ICE BIT(0)
#define CDNS_SRS11_ICS BIT(1)
#define CDNS_SRS11_SDCE BIT(2)
#define CDNS_SRS11_USDCLKFS 6
#define CDNS_SRS11_SDCLKFS 8
#define CDNS_SRS11_DTCV 16
#define CDNS_SRS11_SRFA BIT(24)
#define CDNS_SRS11_SRCMD BIT(25)
#define CDNS_SRS11_SRDAT BIT(26)
/*
* This value determines the interval by which DAT line timeouts are detected
* The interval can be computed as below:
* 1111b - Reserved
* 1110b - t_sdmclk*2(27+2)
* 1101b - t_sdmclk*2(26+2)
*/
#define READ_CLK (0xa << 16)
#define WRITE_CLK (0xe << 16)
#define DTC_VAL 0xE
/* SRS12 */
#define CDNS_SRS12_CC 0U
#define CDNS_SRS12_TC 1
#define CDNS_SRS12_EINT 15
/* SRS01 */
#define CDNS_SRS01_BLK_SIZE 0U
#define CDNS_SRS01_SDMA_BUF 7 << 12
#define CDNS_SRS01_BLK_COUNT_CT 16
/* SRS15 Registers */
#define CDNS_SRS15_SDR12 0U
#define CDNS_SRS15_SDR25 BIT(16)
#define CDNS_SRS15_SDR50 (2 << 16)
#define CDNS_SRS15_SDR104 (3 << 16)
#define CDNS_SRS15_DDR50 (4 << 16)
/* V18SE is 0 for DS and HS, 1 for UHS-I */
#define CDNS_SRS15_V18SE BIT(19)
#define CDNS_SRS15_CMD23_EN BIT(27)
/* HC4E is 0 means version 3.0 and 1 means v 4.0 */
#define CDNS_SRS15_HV4E BIT(28)
#define CDNS_SRS15_BIT_AD_32 0U
#define CDNS_SRS15_BIT_AD_64 BIT(29)
#define CDNS_SRS15_PVE BIT(31)
/* Combo PHY */
#define PHY_DQ_TIMING_REG 0x0
#define PHY_DQS_TIMING_REG 0x04
#define PHY_GATE_LPBK_CTRL_REG 0x08
#define PHY_DLL_MASTER_CTRL_REG 0x0C
#define PHY_DLL_SLAVE_CTRL_REG 0x10
#define PHY_CTRL_REG 0x80
#define PERIPHERAL_SDMMC_MASK 0x60
#define PERIPHERAL_SDMMC_OFFSET 6
#define DFI_INTF_MASK 0x1
/* PHY_DQS_TIMING_REG */
#define CP_USE_EXT_LPBK_DQS(x) (x << 22)
#define CP_USE_LPBK_DQS(x) (x << 21)
#define CP_USE_PHONY_DQS(x) (x << 20)
#define CP_USE_PHONY_DQS_CMD(x) (x << 19)
/* PHY_GATE_LPBK_CTRL_REG */
#define CP_SYNC_METHOD(x) ((x) << 31)
#define CP_SW_HALF_CYCLE_SHIFT(x) ((x) << 28)
#define CP_RD_DEL_SEL(x) ((x) << 19)
#define CP_UNDERRUN_SUPPRESS(x) ((x) << 18)
#define CP_GATE_CFG_ALWAYS_ON(x) ((x) << 6)
/* PHY_DLL_MASTER_CTRL_REG */
#define CP_DLL_BYPASS_MODE(x) ((x) << 23)
#define CP_DLL_START_POINT(x) ((x) << 0)
/* PHY_DLL_SLAVE_CTRL_REG */
#define CP_READ_DQS_CMD_DELAY(x) ((x) << 24)
#define CP_CLK_WRDQS_DELAY(x) ((x) << 16)
#define CP_CLK_WR_DELAY(x) ((x) << 8)
#define CP_READ_DQS_DELAY(x) (x)
/* PHY_DQ_TIMING_REG */
#define CP_IO_MASK_ALWAYS_ON(x) ((x) << 31)
#define CP_IO_MASK_END(x) ((x) << 27)
#define CP_IO_MASK_START(x) ((x) << 24)
#define CP_DATA_SELECT_OE_END(x) (x)
/* SW RESET REG */
#define SDHC_CDNS_HRS00 (0x00)
#define CDNS_HRS00_SWR BIT(0)
/* PHY access port */
#define SDHC_CDNS_HRS04 0x10
#define CDNS_HRS04_ADDR GENMASK(5, 0)
/* PHY data access port */
#define SDHC_CDNS_HRS05 0x14
/* eMMC control registers */
#define SDHC_CDNS_HRS06 0x18
/* PHY_CTRL_REG */
#define CP_PHONY_DQS_TIMING_MASK 0x3F
#define CP_PHONY_DQS_TIMING_SHIFT 4
/* SRS */
#define SDHC_CDNS_SRS00 0x200
#define SDHC_CDNS_SRS01 0x204
#define SDHC_CDNS_SRS02 0x208
#define SDHC_CDNS_SRS03 0x20c
#define SDHC_CDNS_SRS04 0x210
#define SDHC_CDNS_SRS05 0x214
#define SDHC_CDNS_SRS06 0x218
#define SDHC_CDNS_SRS07 0x21C
#define SDHC_CDNS_SRS08 0x220
#define SDHC_CDNS_SRS09 0x224
#define SDHC_CDNS_SRS10 0x228
#define SDHC_CDNS_SRS11 0x22C
#define SDHC_CDNS_SRS12 0x230
#define SDHC_CDNS_SRS13 0x234
#define SDHC_CDNS_SRS14 0x238
#define SDHC_CDNS_SRS15 0x23c
#define SDHC_CDNS_SRS21 0x254
#define SDHC_CDNS_SRS22 0x258
#define SDHC_CDNS_SRS23 0x25c
/* SRS00 */
#define CDNS_SRS00_SAAR 1
/* SRS03 */
#define CDNS_SRS03_CMD_START BIT(31)
#define CDNS_SRS03_CMD_USE_HOLD_REG BIT(29)
#define CDNS_SRS03_CMD_UPDATE_CLK_ONLY BIT(21)
#define CDNS_SRS03_CMD_SEND_INIT BIT(15)
/* Command type */
#define CDNS_SRS03_CMD_TYPE BIT(22)
#define CMD_STOP_ABORT_CMD (3 << 22)
#define CMD_RESUME_CMD (2 << 22)
#define CMD_SUSPEND_CMD BIT(22)
#define CDNS_SRS03_DATA_PRSNT BIT(21)
#define CDNS_SRS03_CMD_IDX_CHK_EN BIT(20)
#define CDNS_SRS03_CMD_READ BIT(4)
#define CDNS_SRS03_MULTI_BLK_READ BIT(5)
#define CDNS_SRS03_RESP_ERR BIT(7)
#define CDNS_SRS03_RESP_CRC BIT(19)
#define CDNS_SRS03_CMD_DONE BIT(0)
/* Response type select */
#define CDNS_SRS03_RES_TYPE_SEL BIT(16)
#define RES_TYPE_SEL_48 (2 << 16)
#define RES_TYPE_SEL_136 (1 << 16)
#define RES_TYPE_SEL_48_B (3 << 16)
#define RES_TYPE_SEL_NO (0 << 16)
/* Auto CMD Enable */
#define CDNS_SRS03_AUTO_CMD_EN BIT(2)
#define AUTO_CMD23 (2 << 2)
#define AUTO_CMD12 BIT(2)
#define AUTO_CMD_AUTO (3 << 2)
#define CDNS_SRS03_COM_IDX 24
#define ERROR_INT BIT(15)
#define CDNS_SRS03_DMA_EN BIT(0)
#define CDNS_SRS03_BLK_CNT_EN BIT(1)
/* HRS07 */
#define SDHC_CDNS_HRS07 0x1c
#define CDNS_HRS07_IDELAY_VAL(x) (x)
#define CDNS_HRS07_RW_COMPENSATE(x) ((x) << 16)
/* PHY reset port */
#define SDHC_CDNS_HRS09 0x24
/* HRS10 */
/* PHY reset port */
#define SDHC_CDNS_HRS10 0x28
/* HCSDCLKADJ DATA; DDR Mode */
#define SDHC_HRS10_HCSDCLKADJ(x) ((x) << 16)
/* HRS16 */
#define CDNS_HRS16_WRCMD0_DLY(x) (x)
#define CDNS_HRS16_WRCMD1_DLY(x) ((x) << 4)
#define CDNS_HRS16_WRDATA0_DLY(x) ((x) << 8)
#define CDNS_HRS16_WRDATA1_DLY(x) ((x) << 12)
#define CDNS_HRS16_WRCMD0_SDCLK_DLY(x) ((x) << 16)
#define CDNS_HRS16_WRCMD1_SDCLK_DLY(x) ((x) << 20)
#define CDNS_HRS16_WRDATA0_SDCLK_DLY(x) ((x) << 24)
#define CDNS_HRS16_WRDATA1_SDCLK_DLY(x) ((x) << 28)
/* Shared Macros */
#define SDMMC_CDN(_reg) (SDMMC_CDN_REG_BASE + \
(SDMMC_CDN_##_reg))
/* MMC Peripheral Definition */
#define MMC_BLOCK_SIZE 512U
#define MMC_BLOCK_MASK (MMC_BLOCK_SIZE - 1)
#define MMC_BOOT_CLK_RATE (400 * 1000)
#define OCR_POWERUP BIT(31)
#define OCR_HCS BIT(30)
#define OCR_3_5_3_6 BIT(23)
#define OCR_3_4_3_5 BIT(22)
#define OCR_3_3_3_4 BIT(21)
#define OCR_3_2_3_3 BIT(20)
#define OCR_3_1_3_2 BIT(19)
#define OCR_3_0_3_1 BIT(18)
#define OCR_2_9_3_0 BIT(17)
#define OCR_2_8_2_9 BIT(16)
#define OCR_2_7_2_8 BIT(15)
#define OCR_VDD_MIN_2V7 GENMASK(23, 15)
#define OCR_VDD_MIN_2V0 GENMASK(14, 8)
#define OCR_VDD_MIN_1V7 BIT(7)
#define MMC_RSP_48 BIT(0)
#define MMC_RSP_136 BIT(1) /* 136 bit response */
#define MMC_RSP_CRC BIT(2) /* expect valid crc */
#define MMC_RSP_CMD_IDX BIT(3) /* response contains cmd idx */
#define MMC_RSP_BUSY BIT(4) /* device may be busy */
/* JEDEC 4.51 chapter 6.12 */
#define MMC_RESPONSE_R1 (MMC_RSP_48 | MMC_RSP_CMD_IDX | MMC_RSP_CRC)
#define MMC_RESPONSE_R1B (MMC_RESPONSE_R1 | MMC_RSP_BUSY)
#define MMC_RESPONSE_R2 (MMC_RSP_48 | MMC_RSP_136 | MMC_RSP_CRC)
#define MMC_RESPONSE_R3 (MMC_RSP_48)
#define MMC_RESPONSE_R4 (MMC_RSP_48)
#define MMC_RESPONSE_R5 (MMC_RSP_48 | MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R6 (MMC_RSP_CRC | MMC_RSP_CMD_IDX)
#define MMC_RESPONSE_R7 (MMC_RSP_48 | MMC_RSP_CRC)
#define MMC_RESPONSE_NONE 0
/* Value randomly chosen for eMMC RCA, it should be > 1 */
#define MMC_FIX_RCA 6
#define RCA_SHIFT_OFFSET 16
#define CMD_EXTCSD_PARTITION_CONFIG 179
#define CMD_EXTCSD_BUS_WIDTH 183
#define CMD_EXTCSD_HS_TIMING 185
#define CMD_EXTCSD_SEC_CNT 212
#define PART_CFG_BOOT_PARTITION1_ENABLE BIT(3)
#define PART_CFG_PARTITION1_ACCESS 1
/* Values in EXT CSD register */
#define MMC_BUS_WIDTH_1 0
#define MMC_BUS_WIDTH_4 1
#define MMC_BUS_WIDTH_8 2
#define MMC_BUS_WIDTH_DDR_4 5
#define MMC_BUS_WIDTH_DDR_8 6
#define MMC_BOOT_MODE_BACKWARD 0
#define MMC_BOOT_MODE_HS_TIMING BIT(3)
#define MMC_BOOT_MODE_DDR (2 << 3)
#define EXTCSD_SET_CMD 0
#define EXTCSD_SET_BITS BIT(24)
#define EXTCSD_CLR_BITS (2 << 24)
#define EXTCSD_WRITE_BYTES (3 << 24)
#define EXTCSD_CMD(x) (((x) & 0xff) << 16)
#define EXTCSD_VALUE(x) (((x) & 0xff) << 8)
#define EXTCSD_CMD_SET_NORMAL 1
#define CSD_TRAN_SPEED_UNIT_MASK GENMASK(2, 0)
#define CSD_TRAN_SPEED_MULT_MASK GENMASK(6, 3)
#define CSD_TRAN_SPEED_MULT_SHIFT 3
#define STATUS_CURRENT_STATE(x) (((x) & 0xf) << 9)
#define STATUS_READY_FOR_DATA BIT(8)
#define STATUS_SWITCH_ERROR BIT(7)
#define MMC_GET_STATE(x) (((x) >> 9) & 0xf)
#define MMC_STATE_IDLE 0
#define MMC_STATE_READY 1
#define MMC_STATE_IDENT 2
#define MMC_STATE_STBY 3
#define MMC_STATE_TRAN 4
#define MMC_STATE_DATA 5
#define MMC_STATE_RCV 6
#define MMC_STATE_PRG 7
#define MMC_STATE_DIS 8
#define MMC_STATE_BTST 9
#define MMC_STATE_SLP 10
#define MMC_FLAG_CMD23 1
#define CMD8_CHECK_PATTERN 0xAA
#define VHS_2_7_3_6_V BIT(8)
#define SD_SCR_BUS_WIDTH_1 BIT(8)
#define SD_SCR_BUS_WIDTH_4 BIT(10)
/* ADMA table component */
#define ADMA_DESC_ATTR_VALID BIT(0)
#define ADMA_DESC_ATTR_END BIT(1)
#define ADMA_DESC_ATTR_INT BIT(2)
#define ADMA_DESC_ATTR_ACT1 BIT(4)
#define ADMA_DESC_ATTR_ACT2 BIT(5)
#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2
/* SRS10 Register */
#define LEDC BIT(0)
#define DT_WIDTH BIT(1)
#define HS_EN BIT(2)
/* Conf depends on SRS15.HV4E */
#define SDMA 0
#define ADMA2_32 (2 << 3)
#define ADMA2_64 (3 << 3)
/* here 0 defines the 64 Kb size */
#define MAX_64KB_PAGE 0
struct sdmmc_cmd {
unsigned int cmd_idx;
unsigned int cmd_arg;
unsigned int resp_type;
unsigned int resp_data[4];
};
struct sdhc_cdns_ops {
/* init function for card */
int (*init)(void);
/* busy check function for card */
int (*busy)(void);
/* card_present function check for card */
int (*card_present)(void);
/* reset the card */
int (*reset)(void);
/* send command and respective argument */
int (*send_cmd)(struct sdmmc_cmd *cmd, struct sdhc_data *data);
/* io set up for card */
int (*set_ios)(unsigned int clk, unsigned int width);
/* prepare dma descriptors */
int (*prepare)(uint32_t lba, uintptr_t buf, struct sdhc_data *data);
/* cache invd api */
int (*cache_invd)(int lba, uintptr_t buf, size_t size);
};
/* Combo Phy reg */
struct sdhc_cdns_combo_phy {
uint32_t cp_clk_wr_delay;
uint32_t cp_clk_wrdqs_delay;
uint32_t cp_data_select_oe_end;
uint32_t cp_dll_bypass_mode;
uint32_t cp_dll_locked_mode;
uint32_t cp_dll_start_point;
uint32_t cp_gate_cfg_always_on;
uint32_t cp_io_mask_always_on;
uint32_t cp_io_mask_end;
uint32_t cp_io_mask_start;
uint32_t cp_rd_del_sel;
uint32_t cp_read_dqs_cmd_delay;
uint32_t cp_read_dqs_delay;
uint32_t cp_sw_half_cycle_shift;
uint32_t cp_sync_method;
uint32_t cp_underrun_suppress;
uint32_t cp_use_ext_lpbk_dqs;
uint32_t cp_use_lpbk_dqs;
uint32_t cp_use_phony_dqs;
uint32_t cp_use_phony_dqs_cmd;
};
/* sdmmc reg */
struct sdhc_cdns_sdmmc {
uint32_t sdhc_extended_rd_mode;
uint32_t sdhc_extended_wr_mode;
uint32_t sdhc_hcsdclkadj;
uint32_t sdhc_idelay_val;
uint32_t sdhc_rdcmd_en;
uint32_t sdhc_rddata_en;
uint32_t sdhc_rw_compensate;
uint32_t sdhc_sdcfsh;
uint32_t sdhc_sdcfsl;
uint32_t sdhc_wrcmd0_dly;
uint32_t sdhc_wrcmd0_sdclk_dly;
uint32_t sdhc_wrcmd1_dly;
uint32_t sdhc_wrcmd1_sdclk_dly;
uint32_t sdhc_wrdata0_dly;
uint32_t sdhc_wrdata0_sdclk_dly;
uint32_t sdhc_wrdata1_dly;
uint32_t sdhc_wrdata1_sdclk_dly;
};
enum sdmmc_device_mode {
/* Identification */
SD_DS_ID,
/* Default speed */
SD_DS,
/* High speed */
SD_HS,
/* Ultra high speed SDR12 */
SD_UHS_SDR12,
/* Ultra high speed SDR25 */
SD_UHS_SDR25,
/* Ultra high speed SDR`50 */
SD_UHS_SDR50,
/* Ultra high speed SDR104 */
SD_UHS_SDR104,
/* Ultra high speed DDR50 */
SD_UHS_DDR50,
/* SDR backward compatible */
EMMC_SDR_BC,
/* SDR */
EMMC_SDR,
/* DDR */
EMMC_DDR,
/* High speed 200Mhz in SDR */
EMMC_HS200,
/* High speed 200Mhz in DDR */
EMMC_HS400,
/* High speed 200Mhz in SDR with enhanced strobe */
EMMC_HS400ES,
};
struct sdhc_cdns_params {
uintptr_t reg_base;
uintptr_t reg_phy;
uintptr_t desc_base;
size_t desc_size;
int clk_rate;
int bus_width;
unsigned int flags;
enum sdmmc_device_mode cdn_sdmmc_dev_type;
uint32_t combophy;
};
struct sdmmc_device_info {
/* Size of device in bytes */
unsigned long long device_size;
/* Block size in bytes */
unsigned int block_size;
/* Max bus freq in Hz */
unsigned int max_bus_freq;
/* OCR voltage */
unsigned int ocr_voltage;
/* Type of MMC */
enum sdmmc_device_mode cdn_sdmmc_dev_type;
};
/*descriptor structure with 8 byte alignment*/
struct sdhc_cdns_desc {
/* 8 bit attribute */
uint8_t attr;
/* reserved bits in desc */
uint8_t reserved;
/* page length for the descriptor */
uint16_t len;
/* lower 32 bits for buffer (64 bit addressing) */
uint32_t addr_lo;
/* higher 32 bits for buffer (64 bit addressing) */
uint32_t addr_hi;
} __aligned(8);
void sdhc_cdns_sdmmc_init(struct sdhc_cdns_params *params, struct sdmmc_device_info *info,
const struct sdhc_cdns_ops **cb_sdmmc_ops);

View file

@ -0,0 +1,20 @@
# Copyright (c) 2023 Intel Corporation
# SPDX-License-Identifier: Apache-2.0
description: Cadence SDHC Controller node
compatible: "cdns,sdhc"
include: [sdhc.yaml, reset-device.yaml]
properties:
clock-frequency:
type: int
description: clock-frequency for SDHC
reg:
required: true
description: register space
power_delay_ms:
type: int
required: true
description: delay required to switch on the SDHC