diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index 431867fb2b..2675589bfc 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -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() diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index 7e75e03a02..13b63cfcc9 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -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" diff --git a/drivers/sdhc/Kconfig.sdhc_cdns b/drivers/sdhc/Kconfig.sdhc_cdns new file mode 100644 index 0000000000..3558763fbf --- /dev/null +++ b/drivers/sdhc/Kconfig.sdhc_cdns @@ -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 diff --git a/drivers/sdhc/sdhc_cdns.c b/drivers/sdhc/sdhc_cdns.c new file mode 100644 index 0000000000..a2a071d876 --- /dev/null +++ b/drivers/sdhc/sdhc_cdns.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT cdns_sdhc + +#include +#include +#include +#include + +#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) diff --git a/drivers/sdhc/sdhc_cdns/sdhc_cdns.c b/drivers/sdhc/sdhc_cdns/sdhc_cdns.c new file mode 100644 index 0000000000..a2a071d876 --- /dev/null +++ b/drivers/sdhc/sdhc_cdns/sdhc_cdns.c @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2023 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT cdns_sdhc + +#include +#include +#include +#include + +#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) diff --git a/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.c b/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.c new file mode 100644 index 0000000000..3d8e3f97e4 --- /dev/null +++ b/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.c @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdhc_cdns_ll.h" +#include + +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); +} diff --git a/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.h b/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.h new file mode 100644 index 0000000000..c07e446dad --- /dev/null +++ b/drivers/sdhc/sdhc_cdns/sdhc_cdns_ll.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* 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); diff --git a/drivers/sdhc/sdhc_cdns_ll.c b/drivers/sdhc/sdhc_cdns_ll.c new file mode 100644 index 0000000000..3d8e3f97e4 --- /dev/null +++ b/drivers/sdhc/sdhc_cdns_ll.c @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdhc_cdns_ll.h" +#include + +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); +} diff --git a/drivers/sdhc/sdhc_cdns_ll.h b/drivers/sdhc/sdhc_cdns_ll.h new file mode 100644 index 0000000000..c07e446dad --- /dev/null +++ b/drivers/sdhc/sdhc_cdns_ll.h @@ -0,0 +1,489 @@ +/* + * Copyright (C) 2023 Intel Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/* 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); diff --git a/dts/bindings/sdhc/cdns,sdhc.yaml b/dts/bindings/sdhc/cdns,sdhc.yaml new file mode 100644 index 0000000000..536b9c4bd5 --- /dev/null +++ b/dts/bindings/sdhc/cdns,sdhc.yaml @@ -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