diff --git a/drivers/sdhc/CMakeLists.txt b/drivers/sdhc/CMakeLists.txt index a43e3f5480..431867fb2b 100644 --- a/drivers/sdhc/CMakeLists.txt +++ b/drivers/sdhc/CMakeLists.txt @@ -8,4 +8,5 @@ zephyr_library_sources_ifdef(CONFIG_SPI_SDHC sdhc_spi.c) 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) endif() diff --git a/drivers/sdhc/Kconfig b/drivers/sdhc/Kconfig index 69a942238e..7e75e03a02 100644 --- a/drivers/sdhc/Kconfig +++ b/drivers/sdhc/Kconfig @@ -8,6 +8,7 @@ menuconfig SDHC if SDHC +source "drivers/sdhc/Kconfig.ifx_cat1" source "drivers/sdhc/Kconfig.imx" source "drivers/sdhc/Kconfig.spi" source "drivers/sdhc/Kconfig.mcux_sdif" diff --git a/drivers/sdhc/Kconfig.ifx_cat1 b/drivers/sdhc/Kconfig.ifx_cat1 new file mode 100644 index 0000000000..13dde4cf18 --- /dev/null +++ b/drivers/sdhc/Kconfig.ifx_cat1 @@ -0,0 +1,22 @@ +# Infineon CAT1 SDHC configuration options + +# Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +config SDHC_INFINEON_CAT1 + bool "Infineon CAT1 SDHC driver" + default y + depends on DT_HAS_INFINEON_CAT1_SDHC_SDIO_ENABLED + select USE_INFINEON_SDIO + select SDHC_SUPPORTS_NATIVE_MODE + help + This option enables the SDHC driver for Infineon CAT1 family. + +if SDHC_INFINEON_CAT1 + +config SDHC_INIT_PRIORITY + default 70 + +endif diff --git a/drivers/sdhc/ifx_cat1_sdio.c b/drivers/sdhc/ifx_cat1_sdio.c new file mode 100644 index 0000000000..d298e1d7b6 --- /dev/null +++ b/drivers/sdhc/ifx_cat1_sdio.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or + * an affiliate of Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + * + */ + +/** + * @brief SDIO driver for Infineon CAT1 MCU family. + * + * This driver support only SDIO protocol of the SD interface for general + * I/O functions. + * + * Refer to the SD Specifications Part 1 SDIO Specifications Version 4.10 for more + * information on the SDIO protocol and specifications. + * + * Features + * - Supports 4-bit interface + * - Supports Ultra High Speed (UHS-I) mode + * - Supports Default Speed (DS), High Speed (HS), SDR12, SDR25 and SDR50 speed modes + * - Supports SDIO card interrupts in both 1-bit SD and 4-bit SD modes + * - Supports Standard capacity (SDSC), High capacity (SDHC) and + * Extended capacity (SDXC) memory + * + * Note (limitations): + * - current version of ifx_cat1_sdio supports only following set of commands: + * > GO_IDLE_STATE (CMD0) + * > SEND_RELATIVE_ADDR (CMD3) + * > IO_SEND_OP_COND (CMD5) + * > SELECT_CARD (CMD7) + * > VOLTAGE_SWITCH (CMD11) + * > GO_INACTIVE_STATE (CMD15) + * > IO_RW_DIRECT (CMD52) + * > IO_RW_EXTENDED (CMD53) + */ + +#define DT_DRV_COMPAT infineon_cat1_sdhc_sdio + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_REGISTER(ifx_cat1_sdio, CONFIG_SDHC_LOG_LEVEL); + +#include + +#define IFX_CAT1_SDIO_F_MIN (SDMMC_CLOCK_400KHZ) +#define IFX_CAT1_SDIO_F_MAX (SD_CLOCK_50MHZ) + +struct ifx_cat1_sdio_config { + const struct pinctrl_dev_config *pincfg; + SDHC_Type *reg_addr; + uint8_t irq_priority; +}; + +struct ifx_cat1_sdio_data { + cyhal_sdio_t sdio_obj; + cyhal_resource_inst_t hw_resource; + cyhal_sdio_configurator_t cyhal_sdio_config; + enum sdhc_clock_speed clock_speed; + enum sdhc_bus_width bus_width; + + void *sdio_cb_user_data; + sdhc_interrupt_cb_t sdio_cb; +}; + +static uint32_t sdio_rca; +static const cy_stc_sd_host_init_config_t host_config = {false, CY_SD_HOST_DMA_ADMA2, false}; +static cy_en_sd_host_card_capacity_t sd_host_card_capacity = CY_SD_HOST_SDSC; +static cy_en_sd_host_card_type_t sd_host_card_type = CY_SD_HOST_NOT_EMMC; +static cy_stc_sd_host_sd_card_config_t sd_host_sd_card_config = { + .lowVoltageSignaling = false, + .busWidth = CY_SD_HOST_BUS_WIDTH_4_BIT, + .cardType = &sd_host_card_type, + .rca = &sdio_rca, + .cardCapacity = &sd_host_card_capacity, +}; + +/* List of available SDHC instances */ +static SDHC_Type *const IFX_CAT1_SDHC_BASE_ADDRESSES[CY_IP_MXSDHC_INSTANCES] = { +#ifdef SDHC0 + SDHC0, +#endif /* ifdef SDHC0 */ + +#ifdef SDHC1 + SDHC1, +#endif /* ifdef SDHC1 */ +}; + +static int32_t _get_hw_block_num(SDHC_Type *reg_addr) +{ + uint32_t i; + + for (i = 0u; i < CY_IP_MXSDHC_INSTANCES; i++) { + if (IFX_CAT1_SDHC_BASE_ADDRESSES[i] == reg_addr) { + return i; + } + } + + return -EINVAL; +} + +static int ifx_cat1_sdio_reset(const struct device *dev) +{ + struct ifx_cat1_sdio_data *dev_data = dev->data; + + cyhal_sdhc_software_reset((cyhal_sdhc_t *)&dev_data->sdio_obj); + + return 0; +} + +static int ifx_cat1_sdio_set_io(const struct device *dev, struct sdhc_io *ios) +{ + cy_rslt_t ret; + struct ifx_cat1_sdio_data *dev_data = dev->data; + cyhal_sdio_cfg_t config = {.frequencyhal_hz = ios->clock}; + + /* NOTE: Set bus width, set card power, set host signal voltage, + * set I/O timing does not support in current version of driver + */ + + /* Set host clock */ + if ((dev_data->clock_speed != ios->clock) && (ios->clock != 0)) { + + if ((ios->clock > IFX_CAT1_SDIO_F_MAX) || (ios->clock < IFX_CAT1_SDIO_F_MIN)) { + return -EINVAL; + } + + ret = cyhal_sdio_configure(&dev_data->sdio_obj, &config); + if (ret != CY_RSLT_SUCCESS) { + return -ENOTSUP; + } + + dev_data->clock_speed = ios->clock; + } + + return 0; +} + +static int ifx_cat1_sdio_card_busy(const struct device *dev) +{ + struct ifx_cat1_sdio_data *dev_data = dev->data; + + return cyhal_sdio_is_busy(&dev_data->sdio_obj) ? 1 : 0; +} + +static int ifx_cat1_sdio_request(const struct device *dev, struct sdhc_command *cmd, + struct sdhc_data *data) +{ + struct ifx_cat1_sdio_data *dev_data = dev->data; + int ret; + + switch (cmd->opcode) { + case CYHAL_SDIO_CMD_GO_IDLE_STATE: + case CYHAL_SDIO_CMD_SEND_RELATIVE_ADDR: + case CYHAL_SDIO_CMD_IO_SEND_OP_COND: + case CYHAL_SDIO_CMD_SELECT_CARD: + case CYHAL_SDIO_CMD_VOLTAGE_SWITCH: + case CYHAL_SDIO_CMD_GO_INACTIVE_STATE: + case CYHAL_SDIO_CMD_IO_RW_DIRECT: + ret = cyhal_sdio_send_cmd(&dev_data->sdio_obj, CYHAL_SDIO_XFER_TYPE_READ, + cmd->opcode, cmd->arg, cmd->response); + if (ret != CY_RSLT_SUCCESS) { + LOG_ERR("cyhal_sdio_send_cmd failed ret = %d \r\n", ret); + } + break; + + case CYHAL_SDIO_CMD_IO_RW_EXTENDED: + cyhal_sdio_transfer_type_t direction; + + direction = (cmd->arg & BIT(SDIO_CMD_ARG_RW_SHIFT)) ? CYHAL_SDIO_XFER_TYPE_WRITE + : CYHAL_SDIO_XFER_TYPE_READ; + + ret = cyhal_sdio_bulk_transfer(&dev_data->sdio_obj, direction, cmd->arg, data->data, + data->blocks * data->block_size, cmd->response); + + if (ret != CY_RSLT_SUCCESS) { + LOG_ERR("cyhal_sdio_bulk_transfer failed ret = %d \r\n", ret); + } + break; + + default: + ret = -ENOTSUP; + } + + return ret; +} + +static int ifx_cat1_sdio_get_card_present(const struct device *dev) +{ + return 1; +} + +static int ifx_cat1_sdio_get_host_props(const struct device *dev, struct sdhc_host_props *props) +{ + memset(props, 0, sizeof(*props)); + props->f_max = IFX_CAT1_SDIO_F_MAX; + props->f_min = IFX_CAT1_SDIO_F_MIN; + props->host_caps.bus_4_bit_support = true; + props->host_caps.high_spd_support = true; + props->host_caps.sdr50_support = true; + props->host_caps.sdio_async_interrupt_support = true; + props->host_caps.vol_330_support = true; + + return 0; +} + +static int ifx_cat1_sdio_enable_interrupt(const struct device *dev, sdhc_interrupt_cb_t callback, + int sources, void *user_data) +{ + struct ifx_cat1_sdio_data *data = dev->data; + const struct ifx_cat1_sdio_config *cfg = dev->config; + + if (sources != SDHC_INT_SDIO) { + return -ENOTSUP; + } + + if (callback == NULL) { + return -EINVAL; + } + + /* Record SDIO callback parameters */ + data->sdio_cb = callback; + data->sdio_cb_user_data = user_data; + + /* Enable CARD INTERRUPT event */ + cyhal_sdio_enable_event(&data->sdio_obj, CYHAL_SDIO_CARD_INTERRUPT, + cfg->irq_priority, true); + + return 0; +} + +static int ifx_cat1_sdio_disable_interrupt(const struct device *dev, int sources) +{ + struct ifx_cat1_sdio_data *data = dev->data; + const struct ifx_cat1_sdio_config *cfg = dev->config; + + if (sources != SDHC_INT_SDIO) { + return -ENOTSUP; + } + + data->sdio_cb = NULL; + data->sdio_cb_user_data = NULL; + + /* Disable CARD INTERRUPT event */ + cyhal_sdio_enable_event(&data->sdio_obj, CYHAL_SDIO_CARD_INTERRUPT, + cfg->irq_priority, false); + + return 0; +} + +static void ifx_cat1_sdio_event_callback(void *callback_arg, cyhal_sdio_event_t event) +{ + const struct device *dev = callback_arg; + struct ifx_cat1_sdio_data *data = dev->data; + + if ((event == CYHAL_SDIO_CARD_INTERRUPT) && (data->sdio_cb != NULL)) { + data->sdio_cb(dev, SDHC_INT_SDIO, data->sdio_cb_user_data); + } +} + +static int ifx_cat1_sdio_init(const struct device *dev) +{ + cy_rslt_t ret; + struct ifx_cat1_sdio_data *data = dev->data; + const struct ifx_cat1_sdio_config *config = dev->config; + + /* Configure dt provided device signals when available */ + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret) { + return ret; + } + + /* Dedicate SDIO HW resource */ + data->hw_resource.type = CYHAL_RSC_SDIODEV; + data->hw_resource.block_num = _get_hw_block_num(config->reg_addr); + + /* Initialize the SDIO peripheral */ + data->cyhal_sdio_config.resource = &data->hw_resource; + data->cyhal_sdio_config.host_config = &host_config, + data->cyhal_sdio_config.card_config = &sd_host_sd_card_config, + + ret = cyhal_sdio_init_cfg(&data->sdio_obj, &data->cyhal_sdio_config); + if (ret != CY_RSLT_SUCCESS) { + LOG_ERR("cyhal_sdio_init_cfg failed ret = %d \r\n", ret); + return ret; + } + + /* Register callback for SDIO events */ + cyhal_sdio_register_callback(&data->sdio_obj, ifx_cat1_sdio_event_callback, (void *)dev); + + return 0; +} + +static const struct sdhc_driver_api ifx_cat1_sdio_api = { + .reset = ifx_cat1_sdio_reset, + .request = ifx_cat1_sdio_request, + .set_io = ifx_cat1_sdio_set_io, + .get_card_present = ifx_cat1_sdio_get_card_present, + .card_busy = ifx_cat1_sdio_card_busy, + .get_host_props = ifx_cat1_sdio_get_host_props, + .enable_interrupt = ifx_cat1_sdio_enable_interrupt, + .disable_interrupt = ifx_cat1_sdio_disable_interrupt, +}; + +#define IFX_CAT1_SDHC_INIT(n) \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static const struct ifx_cat1_sdio_config ifx_cat1_sdio_##n##_config = { \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .reg_addr = (SDHC_Type *)DT_INST_REG_ADDR(n), \ + .irq_priority = DT_INST_IRQ(n, priority)}; \ + \ + static struct ifx_cat1_sdio_data ifx_cat1_sdio_##n##_data; \ + \ + DEVICE_DT_INST_DEFINE(n, &ifx_cat1_sdio_init, NULL, &ifx_cat1_sdio_##n##_data, \ + &ifx_cat1_sdio_##n##_config, POST_KERNEL, CONFIG_SDHC_INIT_PRIORITY, \ + &ifx_cat1_sdio_api); + +DT_INST_FOREACH_STATUS_OKAY(IFX_CAT1_SDHC_INIT) diff --git a/dts/arm/infineon/psoc6/psoc6_01/psoc6_01.dtsi b/dts/arm/infineon/psoc6/psoc6_01/psoc6_01.dtsi index 4f3b420d3e..56957dddab 100644 --- a/dts/arm/infineon/psoc6/psoc6_01/psoc6_01.dtsi +++ b/dts/arm/infineon/psoc6/psoc6_01/psoc6_01.dtsi @@ -537,5 +537,12 @@ resolution = <16>; status = "disabled"; }; + + sdhc0: sdhc@40460000 { + compatible = "infineon,cat1-sdhc-sdio"; + reg = <0x40460000 0x2000>; + interrupts = <164 6>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/infineon/psoc6/psoc6_02/psoc6_02.dtsi b/dts/arm/infineon/psoc6/psoc6_02/psoc6_02.dtsi index a4109291df..c61ba9e0ca 100644 --- a/dts/arm/infineon/psoc6/psoc6_02/psoc6_02.dtsi +++ b/dts/arm/infineon/psoc6/psoc6_02/psoc6_02.dtsi @@ -541,5 +541,12 @@ resolution = <16>; status = "disabled"; }; + + sdhc0: sdhc@40460000 { + compatible = "infineon,cat1-sdhc-sdio"; + reg = <0x40460000 0x2000>; + interrupts = <164 6>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/infineon/psoc6/psoc6_03/psoc6_03.dtsi b/dts/arm/infineon/psoc6/psoc6_03/psoc6_03.dtsi index 8e8ada040d..14aefdc5ad 100644 --- a/dts/arm/infineon/psoc6/psoc6_03/psoc6_03.dtsi +++ b/dts/arm/infineon/psoc6/psoc6_03/psoc6_03.dtsi @@ -241,6 +241,12 @@ interrupts = <18 6>; status = "disabled"; }; + sdhc0: sdhc@40460000 { + compatible = "infineon,cat1-sdhc-sdio"; + reg = <0x40460000 0x2000>; + interrupts = <164 6>; + status = "disabled"; + }; }; }; diff --git a/dts/arm/infineon/psoc6/psoc6_04/psoc6_04.dtsi b/dts/arm/infineon/psoc6/psoc6_04/psoc6_04.dtsi index 73282624e3..d44b0583f7 100644 --- a/dts/arm/infineon/psoc6/psoc6_04/psoc6_04.dtsi +++ b/dts/arm/infineon/psoc6/psoc6_04/psoc6_04.dtsi @@ -247,6 +247,12 @@ interrupts = <18 6>; status = "disabled"; }; + sdhc0: sdhc@40460000 { + compatible = "infineon,cat1-sdhc-sdio"; + reg = <0x40460000 0x2000>; + interrupts = <164 6>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/sdhc/infineon,cat1-sdhc-sdio.yaml b/dts/bindings/sdhc/infineon,cat1-sdhc-sdio.yaml new file mode 100644 index 0000000000..8bb1071b02 --- /dev/null +++ b/dts/bindings/sdhc/infineon,cat1-sdhc-sdio.yaml @@ -0,0 +1,20 @@ +# Copyright (c) 2023 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon CAT1 SDHC/SDIO controller + +compatible: "infineon,cat1-sdhc-sdio" + +include: [sdhc.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true diff --git a/modules/hal_infineon/Kconfig b/modules/hal_infineon/Kconfig index 0522c96769..83d24bfacd 100644 --- a/modules/hal_infineon/Kconfig +++ b/modules/hal_infineon/Kconfig @@ -27,6 +27,12 @@ config USE_INFINEON_SDIO Enable Secure Digital Input/Output interface (SDIO) HAL module for Infineon devices driver +config USE_INFINEON_SDHC + bool + help + Enable SDHC HAL module for Infineon devices + driver + config USE_INFINEON_SPI bool help @@ -68,7 +74,6 @@ config USE_INFINEON_WDT config ABSTRACTION_RTOS_COMPONENT_ZEPHYR bool "Abstraction RTOS component (Zephyr support)" - default n help Enable Abstraction RTOS component with Zephyr support