/* * 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 const struct sdhc_driver_api sdhc_cdns_api = { .request = sdhc_cdns_request, .set_io = sdhc_cdns_set_io, .get_host_props = sdhc_cdns_get_host_props, .get_card_present = sdhc_cdns_get_card_present, .reset = sdhc_cdns_reset, .card_busy = sdhc_cdns_card_busy, }; #define SDHC_CDNS_CLOCK_RATE_INIT(inst) \ COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clock_frequency), \ ( \ .clk_rate = DT_INST_PROP(inst, clock_frequency), \ .cdns_clk_dev = NULL, \ .clkid = (clock_control_subsys_t)0, \ ), \ ( \ .clk_rate = 0, \ .cdns_clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \ .clkid = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(inst, clkid), \ ) \ ) #define SDHC_CDNS_RESET_SPEC_INIT(inst) \ .reset_sdmmc = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 0), \ .reset_sdmmcocp = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 1),\ .reset_softphy = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 2), #define SDHC_CDNS_INIT(inst) \ static struct sdhc_cdns_desc cdns_desc \ [CONFIG_CDNS_DESC_COUNT]; \ \ static const struct sdhc_cdns_config sdhc_cdns_config_##inst = {\ DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME( \ reg_base, DT_DRV_INST(inst)), \ DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME( \ combo_phy, DT_DRV_INST(inst)), \ SDHC_CDNS_CLOCK_RATE_INIT(inst) \ IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, resets), \ (SDHC_CDNS_RESET_SPEC_INIT(inst))) \ .power_delay_ms = DT_INST_PROP(inst, power_delay_ms), \ }; \ static struct sdhc_cdns_data sdhc_cdns_data_##inst = { \ .params = { \ .bus_width = SDHC_BUS_WIDTH1BIT, \ .desc_base = (uintptr_t) &cdns_desc, \ .desc_size = SDHC_CDNS_DESC_SIZE, \ .flags = 0, \ }, \ .info = { \ .cdn_sdmmc_dev_type = SD_DS, \ .ocr_voltage = OCR_3_3_3_4 | OCR_3_2_3_3, \ }, \ }; \ DEVICE_DT_INST_DEFINE(inst, \ &sdhc_cdns_init, \ NULL, \ &sdhc_cdns_data_##inst, \ &sdhc_cdns_config_##inst, \ POST_KERNEL, \ CONFIG_SDHC_INIT_PRIORITY, \ &sdhc_cdns_api); DT_INST_FOREACH_STATUS_OKAY(SDHC_CDNS_INIT)