diff --git a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml index fa4ec97aa8afc..4efe8f2758082 100644 --- a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml +++ b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk.yaml @@ -14,4 +14,5 @@ toolchain: - gnuarmemb supported: - gpio + - spi vendor: infineon diff --git a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk_defconfig b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk_defconfig index 3ff25ee0a1713..1c88c7f14f026 100644 --- a/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk_defconfig +++ b/boards/infineon/kit_psc3m5_evk/kit_psc3m5_evk_defconfig @@ -20,3 +20,5 @@ CONFIG_UART_CONSOLE=y # Enable UART driver CONFIG_SERIAL=y + +CONFIG_ARM_TRUSTZONE_M=y diff --git a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55.yaml b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55.yaml index af184f3bfb135..93b7036c58e8a 100644 --- a/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55.yaml +++ b/boards/infineon/kit_pse84_eval/kit_pse84_eval_m55.yaml @@ -15,3 +15,4 @@ supported: - gpio - pin_ctrl - uart + - spi diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 7425ace593281..7561e422a1008 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -48,6 +48,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_NUMAKER spi_numaker.c) zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE spi_oc_simple.c) zephyr_library_sources_ifdef(CONFIG_SPI_OMAP_MCSPI spi_omap_mcspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_OPENTITAN spi_opentitan.c) +zephyr_library_sources_ifdef(CONFIG_SPI_PDL_INFINEON_CAT1 spi_ifx_cat1_pdl.c) zephyr_library_sources_ifdef(CONFIG_SPI_PL022 spi_pl022.c) zephyr_library_sources_ifdef(CONFIG_SPI_PSOC6 spi_psoc6.c) zephyr_library_sources_ifdef(CONFIG_SPI_PW spi_pw.c) diff --git a/drivers/spi/Kconfig.ifx_cat1 b/drivers/spi/Kconfig.ifx_cat1 index e08dc5e13ccc6..29f25611b00f7 100644 --- a/drivers/spi/Kconfig.ifx_cat1 +++ b/drivers/spi/Kconfig.ifx_cat1 @@ -12,3 +12,31 @@ config SPI_INFINEON_CAT1 select GPIO help This option enables the SPI driver for Infineon CAT1 family. + +config SPI_PDL_INFINEON_CAT1 + bool "Infineon CAT1 SPI driver" + default y + depends on DT_HAS_INFINEON_CAT1_SPI_PDL_ENABLED + select USE_INFINEON_SPI + select PINCTRL + select GPIO + help + This option enables the SPI driver for Infineon CAT1 family. + +if USE_INFINEON_SPI + +config IFX_CAT1_SPI_DMA + bool "Infineon CAT1 SPI Interrupt Support" + select DMA + help + Enable DMA during usage of SPI driver. + +config IFX_CAT1_SPI_DMA_TX_AUTO_TRIGGER + bool "Infineon CAT1 SPI Tx DMA channel trigger mechanism" + default y + depends on IFX_CAT1_SPI_DMA + select DMA + help + Automatically trigger SPI Tx DMA after config + +endif # USE_INFINEON_SPI diff --git a/drivers/spi/spi_ifx_cat1_pdl.c b/drivers/spi/spi_ifx_cat1_pdl.c new file mode 100644 index 0000000000000..04cc97482fa5a --- /dev/null +++ b/drivers/spi/spi_ifx_cat1_pdl.c @@ -0,0 +1,1130 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT infineon_cat1_spi_pdl + +#include +LOG_MODULE_REGISTER(cat1_spi, CONFIG_SPI_LOG_LEVEL); + +#include "spi_context.h" + +#include +#include +#include +#include +#include + +#ifdef CONFIG_IFX_CAT1_SPI_DMA +#include +#endif + +#include +#include + +#define IFX_CAT1_SPI_DEFAULT_OVERSAMPLE (4) +#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) +#define IFX_CAT1_SPI_MIN_DATA_WIDTH (4) +#else +#define IFX_CAT1_SPI_MIN_DATA_WIDTH (8) +#endif +#define IFX_CAT1_SPI_MAX_DATA_WIDTH (32) + +#define IFX_CAT1_SPI_OVERSAMPLE_MIN 4 +#define IFX_CAT1_SPI_OVERSAMPLE_MAX 16 + +#define IFX_CAT1_SPI_PENDING_NONE (0) +#define IFX_CAT1_SPI_PENDING_RX (1) +#define IFX_CAT1_SPI_PENDING_TX (2) +#define IFX_CAT1_SPI_PENDING_TX_RX (3) + +#define IFX_CAT1_SPI_DEFAULT_SPEED 100000 + +#define IFX_CAT1_SPI_RSLT_TRANSFER_ERROR (-2) +#define IFX_CAT1_SPI_RSLT_CLOCK_ERROR (-3) + +#if CY_SCB_DRV_VERSION_MINOR >= 20 && defined(COMPONENT_CAT1) && CY_SCB_DRV_VERSION_MAJOR == 3 +#define IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL +#endif + +#ifdef CONFIG_IFX_CAT1_SPI_DMA +/* dummy buffers to be used by driver for DMA operations when app gives a NULL buffer + * during an asymmetric transfer + */ +static uint32_t tx_dummy_data; +static uint32_t rx_dummy_data; +#endif + +typedef void (*ifx_cat1_spi_event_callback_t)(void *callback_arg, uint32_t event); + +/* Device config structure */ +struct ifx_cat1_spi_config { + CySCB_Type *reg_addr; + const struct pinctrl_dev_config *pcfg; + cy_stc_scb_spi_config_t scb_spi_config; + cy_cb_scb_spi_handle_events_t spi_handle_events_func; + + uint32_t irq_num; + void (*irq_config_func)(const struct device *dev); + cy_stc_syspm_callback_params_t spi_deep_sleep_param; + + uint8_t cs_oversample[32]; + uint8_t cs_oversample_cnt; +}; + +#ifdef CONFIG_IFX_CAT1_SPI_DMA +struct ifx_cat1_dma_stream { + const struct device *dev_dma; + uint32_t dma_channel; + struct dma_config dma_cfg; + struct dma_block_config blk_cfg; +}; +#endif + +typedef struct { + cy_israddress callback; + void *callback_arg; +} ifx_cat1_event_callback_data_t; + +/* Data structure */ +struct ifx_cat1_spi_data { + struct spi_context ctx; + uint8_t dfs_value; + size_t chunk_len; + bool dma_configured; + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + struct ifx_cat1_dma_stream dma_rx; + struct ifx_cat1_dma_stream dma_tx; + en_peri0_trig_input_pdma0_tr_t spi_rx_trigger; + en_peri0_trig_output_pdma0_tr_t dma_rx_trigger; +#endif + +#if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) + uint8_t clock_peri_group; +#endif + + struct ifx_cat1_resource_inst resource; + struct ifx_cat1_clock clock; + cy_en_scb_spi_sclk_mode_t clk_mode; + uint8_t data_bits; + bool is_slave; + uint8_t oversample_value; + bool msb_first; + cy_stc_scb_spi_context_t context; + uint32_t irq_cause; + + uint16_t volatile pending; + + uint8_t write_fill; + bool is_async; + void *rx_buffer; + uint32_t rx_buffer_size; + const void *tx_buffer; + uint32_t tx_buffer_size; + ifx_cat1_event_callback_data_t callback_data; + cy_stc_syspm_callback_t spi_deep_sleep; +}; + +cy_rslt_t ifx_cat1_spi_init_cfg(const struct device *dev, cy_stc_scb_spi_config_t *scb_spi_config); +void ifx_cat1_spi_register_callback(const struct device *dev, + ifx_cat1_spi_event_callback_t callback, void *callback_arg); +void spi_free(const struct device *dev); +cy_rslt_t spi_set_frequency(const struct device *dev, uint32_t hz); +static void spi_irq_handler(const struct device *dev); +static void ifx_cat1_spi_cb_wrapper(const struct device *dev, uint32_t event); +cy_rslt_t ifx_cat1_spi_transfer_async(const struct device *dev, const uint8_t *tx, size_t tx_length, + uint8_t *rx, size_t rx_length); + +int32_t ifx_cat1_uart_get_hw_block_num(CySCB_Type *reg_addr); + +static uint8_t get_dfs_value(struct spi_context *ctx) +{ + uint8_t word_size = SPI_WORD_SIZE_GET(ctx->config->operation); + + if (word_size <= 8) { + return 1; + } else if (word_size <= 16) { + return 2; + } else if (word_size <= 24) { + return 3; + } else { + return 4; + } +} + +static void transfer_chunk(const struct device *dev) +{ + struct ifx_cat1_spi_data *const data = dev->data; + struct spi_context *ctx = &data->ctx; + size_t chunk_len = spi_context_max_continuous_chunk(ctx); + int ret = 0; + + if (chunk_len == 0) { + goto exit; + } + + data->chunk_len = chunk_len; + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + const struct ifx_cat1_spi_config *const config = dev->config; + CySCB_Type *spi_reg = config->reg_addr; + + Cy_SCB_SetRxFifoLevel(spi_reg, chunk_len - 1); + + register struct ifx_cat1_dma_stream *dma_tx = &data->dma_tx; + register struct ifx_cat1_dma_stream *dma_rx = &data->dma_rx; + + if (data->dma_configured && spi_context_rx_buf_on(ctx) && spi_context_tx_buf_on(ctx)) { + /* Optimization to reduce config time if only buffer and size + * are changing from the previous DMA configuration + */ + dma_reload(dma_tx->dev_dma, dma_tx->dma_channel, (uint32_t)ctx->tx_buf, + dma_tx->blk_cfg.dest_address, chunk_len); + dma_reload(dma_rx->dev_dma, dma_rx->dma_channel, dma_rx->blk_cfg.source_address, + (uint32_t)ctx->rx_buf, chunk_len); + return; + } + + if (spi_context_rx_buf_on(ctx)) { + dma_rx->blk_cfg.dest_address = (uint32_t)ctx->rx_buf; + dma_rx->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + } else { + dma_rx->blk_cfg.dest_address = (uint32_t)&rx_dummy_data; + dma_rx->blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + } + + if (spi_context_tx_buf_on(ctx)) { + dma_tx->blk_cfg.source_address = (uint32_t)ctx->tx_buf; + dma_tx->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + + } else { + tx_dummy_data = 0; + dma_tx->blk_cfg.source_address = (uint32_t)&tx_dummy_data; + dma_tx->blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + } + + dma_rx->blk_cfg.block_size = dma_tx->blk_cfg.block_size = chunk_len; + ret = dma_config(dma_rx->dev_dma, dma_rx->dma_channel, &dma_rx->dma_cfg); + if (ret < 0) { + goto exit; + } + + ret = dma_config(dma_tx->dev_dma, dma_tx->dma_channel, &dma_tx->dma_cfg); + if (ret < 0) { + goto exit; + } + +#ifdef CONFIG_IFX_CAT1_SPI_DMA_TX_AUTO_TRIGGER + ret = dma_start(dma_tx->dev_dma, dma_tx->dma_channel); + if (ret == 0) { + return; + } +#else + if (ret == 0) { + data->dma_configured = 1; + return; + } +#endif +#else + cy_rslt_t result = ifx_cat1_spi_transfer_async( + dev, ctx->tx_buf, spi_context_tx_buf_on(ctx) ? chunk_len : 0, ctx->rx_buf, + spi_context_rx_buf_on(ctx) ? chunk_len : 0); + if (result == CY_RSLT_SUCCESS) { + return; + } +#endif + ret = -EIO; +exit: + spi_context_cs_control(ctx, false); + spi_context_complete(ctx, dev, ret); +} + +static void spi_interrupt_callback(void *arg, uint32_t event) +{ + const struct device *dev = (const struct device *)arg; + struct ifx_cat1_spi_data *const data = dev->data; + struct spi_context *ctx = &data->ctx; + + if (event & CY_SCB_SPI_TRANSFER_ERR_EVENT) { + const struct ifx_cat1_spi_config *const config = dev->config; + + Cy_SCB_SPI_AbortTransfer(config->reg_addr, &(data->context)); + data->pending = IFX_CAT1_SPI_PENDING_NONE; + } + + if (event & CY_SCB_SPI_TRANSFER_CMPLT_EVENT) { + spi_context_update_tx(ctx, data->dfs_value, data->chunk_len); + spi_context_update_rx(ctx, data->dfs_value, data->chunk_len); + + transfer_chunk(dev); + } +} + +#ifdef CONFIG_IFX_CAT1_SPI_DMA +static void dma_callback(const struct device *dma_dev, void *arg, uint32_t channel, int status) +{ + struct device *dev = arg; + struct ifx_cat1_spi_data *const data = dev->data; + struct spi_context *ctx = &data->ctx; + + if (channel == data->dma_rx.dma_channel) { + spi_context_update_tx(ctx, get_dfs_value(ctx), data->chunk_len); + spi_context_update_rx(ctx, get_dfs_value(ctx), data->chunk_len); + + transfer_chunk(dev); + } else if (channel == data->dma_tx.dma_channel) { + + } else { + LOG_ERR("Unknown\n"); + } +} +#endif + +int spi_config(const struct device *dev, const struct spi_config *spi_cfg) +{ + cy_rslt_t result; + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + cy_stc_scb_spi_config_t scb_spi_config = config->scb_spi_config; + struct spi_context *ctx = &data->ctx; + bool spi_mode_cpol = false; + bool spi_mode_cpha = false; + + if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_LOOP) { + return -ENOTSUP; + } + + if (SPI_WORD_SIZE_GET(spi_cfg->operation) > IFX_CAT1_SPI_MAX_DATA_WIDTH) { + LOG_ERR("Word size %d is greater than %d", SPI_WORD_SIZE_GET(spi_cfg->operation), + IFX_CAT1_SPI_MAX_DATA_WIDTH); + return -EINVAL; + } + + if (SPI_WORD_SIZE_GET(spi_cfg->operation) < IFX_CAT1_SPI_MIN_DATA_WIDTH) { + LOG_ERR("Word size %d is less than %d", SPI_WORD_SIZE_GET(spi_cfg->operation), + IFX_CAT1_SPI_MIN_DATA_WIDTH); + return -EINVAL; + } + + /* check if configuration was changed from previous run, if so skip setup again */ + if (spi_context_configured(ctx, spi_cfg)) { + /* Already configured. No need to do it again. */ + return 0; + } + + /* Store spi config in context */ + ctx->config = spi_cfg; + + if (spi_context_is_slave(ctx)) { + scb_spi_config.spiMode = CY_SCB_SPI_SLAVE; + scb_spi_config.oversample = 0; + scb_spi_config.enableMisoLateSample = false; + } else { + scb_spi_config.spiMode = CY_SCB_SPI_MASTER; + + /* If an oversample value for a given target is not defined in the relevant + * devicetree/overlay files, the default of four will be used from the + * default configuration + */ + if (config->cs_oversample_cnt > 0 && spi_cfg->slave < config->cs_oversample_cnt) { + scb_spi_config.oversample = config->cs_oversample[spi_cfg->slave]; + } + } + + if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPOL) { + spi_mode_cpol = true; + } + + if (SPI_MODE_GET(spi_cfg->operation) & SPI_MODE_CPHA) { + spi_mode_cpha = true; + } + + if (SPI_WORD_SIZE_GET(spi_cfg->operation)) { + scb_spi_config.txDataWidth = SPI_WORD_SIZE_GET(spi_cfg->operation); + scb_spi_config.rxDataWidth = SPI_WORD_SIZE_GET(spi_cfg->operation); + } + + if (spi_mode_cpha) { + scb_spi_config.sclkMode = + spi_mode_cpol ? CY_SCB_SPI_CPHA1_CPOL1 : CY_SCB_SPI_CPHA1_CPOL0; + } else { + scb_spi_config.sclkMode = + spi_mode_cpol ? CY_SCB_SPI_CPHA0_CPOL1 : CY_SCB_SPI_CPHA0_CPOL0; + } + + scb_spi_config.enableMsbFirst = (spi_cfg->operation & SPI_TRANSFER_LSB) ? false : true; + + /* Force free resource */ + if (config->reg_addr != NULL) { + spi_free(dev); + } + + /* Initialize the SPI peripheral */ + result = ifx_cat1_spi_init_cfg(dev, &scb_spi_config); + if (result != CY_RSLT_SUCCESS) { + return -ENOTSUP; + } + + /* Configure Slave select polarity */ + if (spi_context_is_slave(ctx)) { + Cy_SCB_SPI_SetActiveSlaveSelectPolarity(config->reg_addr, CY_SCB_SPI_SLAVE_SELECT0, + scb_spi_config.ssPolarity); + } + + /* Set the data rate */ + result = spi_set_frequency(dev, spi_cfg->frequency); + if (result != CY_RSLT_SUCCESS) { + return -EIO; + } + + /* Write 0 when NULL buffer is provided for Tx/Rx */ + data->write_fill = 0; + + /* Register common SPI callback */ + ifx_cat1_spi_register_callback(dev, spi_interrupt_callback, (void *)dev); + + /* Enable the spi event */ + data->irq_cause |= CY_SCB_SPI_TRANSFER_CMPLT_EVENT; + + /* Store spi config in context */ + ctx->config = spi_cfg; + + data->dfs_value = get_dfs_value(ctx); + + return 0; +} + +static int transceive(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs, + bool asynchronous, spi_callback_t cb, void *userdata) +{ + int result; + struct ifx_cat1_spi_data *const data = dev->data; + struct spi_context *ctx = &data->ctx; + + spi_context_lock(ctx, asynchronous, cb, userdata, spi_cfg); + + result = spi_config(dev, spi_cfg); + if (result) { + LOG_ERR("Error in SPI Configuration (result: 0x%x)", result); + spi_context_release(ctx, result); + return result; + } + + spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, data->dfs_value); + spi_context_cs_control(ctx, true); + + transfer_chunk(dev); + result = spi_context_wait_for_completion(&data->ctx); + + spi_context_release(ctx, result); + + return result; +} + +static int ifx_cat1_spi_transceive_sync(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL); +} + +#if defined(CONFIG_SPI_ASYNC) +static int ifx_cat1_spi_transceive_async(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, spi_callback_t cb, + void *userdata) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata); +} +#endif + +static int ifx_cat1_spi_release(const struct device *dev, const struct spi_config *spi_cfg) +{ + spi_free(dev); + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + struct ifx_cat1_spi_data *const data = dev->data; + + dma_stop(data->dma_tx.dev_dma, data->dma_tx.dma_channel); +#endif + + return 0; +} + +static const struct spi_driver_api ifx_cat1_spi_api = { + .transceive = ifx_cat1_spi_transceive_sync, +#if defined(CONFIG_SPI_ASYNC) + .transceive_async = ifx_cat1_spi_transceive_async, +#endif + .release = ifx_cat1_spi_release, +}; + +static int ifx_cat1_spi_init(const struct device *dev) +{ + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + int ret; + + /* Dedicate SCB HW resource */ + data->resource.type = IFX_RSC_SCB; + data->resource.block_num = ifx_cat1_uart_get_hw_block_num(config->reg_addr); + +#ifdef CONFIG_IFX_CAT1_SPI_DMA + /* spi_rx_trigger is initialized to PERI_0_TRIG_IN_MUX_0_SCB_RX_TR_OUT0, + * this is incremented by the resource.block_num to get the trigger for the selected SCB + * from the trigmux enumeration (en_peri0_trig_input_pdma0_tr_t) + */ + data->spi_rx_trigger += data->resource.block_num; + + if (data->dma_rx.dev_dma != NULL) { + if (!device_is_ready(data->dma_rx.dev_dma)) { + return -ENODEV; + } + data->dma_rx.blk_cfg.source_address = (uint32_t)(&(config->reg_addr->RX_FIFO_RD)); + data->dma_rx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + data->dma_rx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_INCREMENT; + data->dma_rx.dma_cfg.head_block = &data->dma_rx.blk_cfg; + data->dma_rx.dma_cfg.user_data = (void *)dev; + data->dma_rx.dma_cfg.dma_callback = dma_callback; + } + + if (data->dma_tx.dev_dma != NULL) { + if (!device_is_ready(data->dma_tx.dev_dma)) { + return -ENODEV; + } + data->dma_tx.blk_cfg.dest_address = (uint32_t)(&(config->reg_addr->TX_FIFO_WR)); + data->dma_tx.blk_cfg.source_addr_adj = DMA_ADDR_ADJ_INCREMENT; + data->dma_tx.blk_cfg.dest_addr_adj = DMA_ADDR_ADJ_NO_CHANGE; + data->dma_tx.dma_cfg.head_block = &data->dma_tx.blk_cfg; + data->dma_tx.dma_cfg.user_data = (void *)dev; + data->dma_tx.dma_cfg.dma_callback = dma_callback; + } + + Cy_TrigMux_Connect(data->spi_rx_trigger, data->dma_rx_trigger, false, TRIGGER_TYPE_LEVEL); +#endif + + /* Configure dt provided device signals when available */ + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; + } + + /* Configure slave select (master) */ + spi_context_cs_configure_all(&data->ctx); + + spi_context_unlock_unconditionally(&data->ctx); + + config->irq_config_func(dev); + +#ifdef CONFIG_PM + Cy_SysPm_RegisterCallback(&data->spi_deep_sleep); +#endif + return 0; +} + +#if defined(CONFIG_IFX_CAT1_SPI_DMA) +#define SPI_DMA_CHANNEL_INIT(index, dir, ch_dir, src_data_size, dst_data_size) \ + .dev_dma = DEVICE_DT_GET(DT_INST_DMAS_CTLR_BY_NAME(index, dir)), \ + .dma_channel = DT_INST_DMAS_CELL_BY_NAME(index, dir, channel), \ + .dma_cfg = { \ + .channel_direction = ch_dir, \ + .source_data_size = src_data_size, \ + .dest_data_size = dst_data_size, \ + .source_burst_length = 0, \ + .dest_burst_length = 0, \ + .block_count = 1, \ + .complete_callback_en = 1, \ + }, + +#define SPI_DMA_CHANNEL(index, dir, ch_dir, src_data_size, dst_data_size) \ + .dma_##dir = {COND_CODE_1( \ + DT_INST_DMAS_HAS_NAME(index, dir), \ + (SPI_DMA_CHANNEL_INIT(index, dir, ch_dir, src_data_size, dst_data_size)), \ + (NULL))}, + +#define SPI_DMA_TRIGGERS(index) \ + .spi_rx_trigger = (en_peri0_trig_input_pdma0_tr_t)(PERI_0_TRIG_IN_MUX_0_SCB_RX_TR_OUT0), \ + .dma_rx_trigger = \ + (en_peri0_trig_output_pdma0_tr_t)(PERI_0_TRIG_OUT_MUX_0_PDMA0_TR_IN0 + \ + DT_INST_DMAS_CELL_BY_NAME(index, rx, channel)), +#else +#define SPI_DMA_CHANNEL(index, dir, ch_dir, src_data_size, dst_data_size) +#define SPI_DMA_TRIGGERS(index) +#endif + +#if defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) +#define PERI_INFO(n) .clock_peri_group = DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 1), +#else +#define PERI_INFO(n) +#endif + +/* Account for spelling error in older version of the PDL */ +#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) +#define EN_XFER_SEPARATION enableTransferSeparation +#else +#define EN_XFER_SEPARATION enableTransferSeperation +#endif + +#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) +#define SPI_PERI_CLOCK_INIT(n) \ + .clock = \ + { \ + .block = IFX_CAT1_PERIPHERAL_GROUP_ADJUST( \ + DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 0), \ + DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 1), \ + DT_INST_PROP_BY_PHANDLE(n, clocks, div_type)), \ + .channel = DT_INST_PROP_BY_PHANDLE(n, clocks, channel), \ + }, \ + PERI_INFO(n) +#else +#define SPI_PERI_CLOCK_INIT(n) \ + .clock = \ + { \ + .block = IFX_CAT1_PERIPHERAL_GROUP_ADJUST( \ + DT_PROP_BY_IDX(DT_INST_PHANDLE(n, clocks), peri_group, 1), \ + DT_INST_PROP_BY_PHANDLE(n, clocks, div_type)), \ + .channel = DT_INST_PROP_BY_PHANDLE(n, clocks, channel), \ + }, \ + PERI_INFO(n) +#endif + +#define IFX_CAT1_SPI_INIT(n) \ + \ + void spi_handle_events_func_##n(uint32_t event) \ + { \ + ifx_cat1_spi_cb_wrapper(DEVICE_DT_INST_GET(n), event); \ + } \ + \ + static void ifx_cat1_spi_irq_config_func_##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), spi_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + } \ + \ + PINCTRL_DT_INST_DEFINE(n); \ + \ + static struct ifx_cat1_spi_config spi_cat1_config_##n = { \ + .reg_addr = (CySCB_Type *)DT_INST_REG_ADDR(n), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .cs_oversample_cnt = DT_INST_PROP_LEN_OR(n, oversample, 0), \ + .cs_oversample = DT_INST_PROP_OR(n, oversample, {0}), \ + .scb_spi_config = \ + {.spiMode = CY_SCB_SPI_MASTER, /* overwrite by cfg */ \ + .sclkMode = CY_SCB_SPI_CPHA0_CPOL0, /* overwrite by cfg */ \ + .rxDataWidth = 8, /* overwrite by cfg */ \ + .txDataWidth = 8, /* overwrite by cfg */ \ + .enableMsbFirst = true, /* overwrite by cfg */ \ + .subMode = DT_INST_PROP_OR(n, sub_mode, CY_SCB_SPI_MOTOROLA), \ + .oversample = IFX_CAT1_SPI_DEFAULT_OVERSAMPLE, \ + .enableFreeRunSclk = DT_INST_PROP_OR(n, enable_free_run_sclk, false), \ + .enableInputFilter = DT_INST_PROP_OR(n, enable_input_filter, false), \ + .enableMisoLateSample = \ + DT_INST_PROP_OR(n, enable_miso_late_sample, true), \ + .EN_XFER_SEPARATION = \ + DT_INST_PROP_OR(n, enable_transfer_separation, false), \ + .enableWakeFromSleep = DT_INST_PROP_OR(n, enableWakeFromSleep, false), \ + .ssPolarity = DT_INST_PROP_OR(n, ss_polarity, CY_SCB_SPI_ACTIVE_LOW), \ + .rxFifoTriggerLevel = DT_INST_PROP_OR(n, rx_fifo_trigger_level, 0), \ + .rxFifoIntEnableMask = DT_INST_PROP_OR(n, rx_fifo_int_enable_mask, 0), \ + .txFifoTriggerLevel = DT_INST_PROP_OR(n, tx_fifo_trigger_level, 0), \ + .txFifoIntEnableMask = DT_INST_PROP_OR(n, tx_fifo_int_enable_mask, 0), \ + .masterSlaveIntEnableMask = \ + DT_INST_PROP_OR(n, master_slave_int_enable_mask, 0)}, \ + \ + .irq_num = DT_INST_IRQN(n), \ + .irq_config_func = ifx_cat1_spi_irq_config_func_##n, \ + \ + .spi_handle_events_func = spi_handle_events_func_##n, \ + .spi_deep_sleep_param = {(CySCB_Type *)DT_INST_REG_ADDR(n), NULL}, \ + }; \ + \ + static struct ifx_cat1_spi_data spi_cat1_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_cat1_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_cat1_data_##n, ctx), \ + SPI_DMA_CHANNEL(n, tx, MEMORY_TO_PERIPHERAL, 1, 1) \ + SPI_DMA_CHANNEL(n, rx, PERIPHERAL_TO_MEMORY, 1, 1) SPI_DMA_TRIGGERS(n) \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \ + SPI_PERI_CLOCK_INIT(n) \ + .spi_deep_sleep = { \ + &Cy_SCB_SPI_DeepSleepCallback, CY_SYSPM_DEEPSLEEP, \ + CY_SYSPM_SKIP_BEFORE_TRANSITION, \ + &spi_cat1_config_##n.spi_deep_sleep_param, NULL, NULL, 1}}; \ + \ + DEVICE_DT_INST_DEFINE(n, &ifx_cat1_spi_init, NULL, &spi_cat1_data_##n, \ + &spi_cat1_config_##n, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &ifx_cat1_spi_api); + +DT_INST_FOREACH_STATUS_OKAY(IFX_CAT1_SPI_INIT) + +cy_rslt_t ifx_cat1_spi_transfer_async(const struct device *dev, const uint8_t *tx, size_t tx_length, + uint8_t *rx, size_t rx_length) +{ + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + cy_en_scb_spi_status_t spi_status; + + data->is_async = true; + + size_t tx_words = tx_length; + size_t rx_words = rx_length; + + /* Setup transfer */ + data->rx_buffer = NULL; + data->tx_buffer = NULL; + +#if !defined(IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL) + if (tx_words > rx_words) { + if (rx_words > 0) { + /* I) write + read, II) write only */ + data->pending = IFX_CAT1_SPI_PENDING_TX_RX; + + data->tx_buffer = tx + (rx_words); + data->tx_buffer_size = tx_words - rx_words; + + tx_words = rx_words; /* Use tx_words to store entire transfer length */ + } else { + /* I) write only */ + data->pending = IFX_CAT1_SPI_PENDING_TX; + + rx = NULL; + } + } else if (rx_words > tx_words) { + if (tx_words > 0) { + /* I) write + read, II) read only */ + data->pending = IFX_CAT1_SPI_PENDING_TX_RX; + + data->rx_buffer = rx + (tx_words); + data->rx_buffer_size = rx_words - tx_words; + } else { + /* I) read only. */ + data->pending = IFX_CAT1_SPI_PENDING_RX; + + data->rx_buffer = rx_words > 1 ? rx + 1 : NULL; + data->rx_buffer_size = rx_words - 1; + tx = &data->write_fill; + tx_words = 1; + } + } else { + /* RX and TX of the same size: I) write + read. */ + data->pending = IFX_CAT1_SPI_PENDING_TX_RX; + } + spi_status = + Cy_SCB_SPI_Transfer(config->reg_addr, (void *)tx, rx, tx_words, &data->context); +#else /* !defined(IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL) */ + + if (tx_words != rx_words) { + if (tx_words == 0) { + data->pending = IFX_CAT1_SPI_PENDING_RX; + tx = NULL; + } else if (rx_words == 0) { + data->pending = IFX_CAT1_SPI_PENDING_TX; + rx = NULL; + } else { + data->pending = IFX_CAT1_SPI_PENDING_TX_RX; + } + spi_status = Cy_SCB_SPI_Transfer_Buffer(config->reg_addr, (void *)tx, (void *)rx, + tx_words, rx_words, data->write_fill, + &data->context); + } else { + data->pending = IFX_CAT1_SPI_PENDING_TX_RX; + spi_status = Cy_SCB_SPI_Transfer(config->reg_addr, (void *)tx, rx, tx_words, + &data->context); + } + +#endif /* IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL */ + return spi_status == CY_SCB_SPI_SUCCESS ? CY_RSLT_SUCCESS + : IFX_CAT1_SPI_RSLT_TRANSFER_ERROR; +} + +bool ifx_cat1_spi_is_busy(const struct device *dev) +{ + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + return Cy_SCB_SPI_IsBusBusy(config->reg_addr) || + (data->pending != IFX_CAT1_SPI_PENDING_NONE); +} + +cy_rslt_t ifx_cat1_spi_abort_async(const struct device *dev) +{ + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + Cy_SCB_SPI_AbortTransfer(config->reg_addr, &(data->context)); + data->pending = IFX_CAT1_SPI_PENDING_NONE; + return CY_RSLT_SUCCESS; +} + +/* Registers a callback function, which notifies that + * SPI events occurred in the Cy_SCB_SPI_Interrupt. + */ +void ifx_cat1_spi_register_callback(const struct device *dev, + ifx_cat1_spi_event_callback_t callback, void *callback_arg) +{ + /* TODO: we need rework to removecallback_data structure */ + + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + uint32_t savedIntrStatus = Cy_SysLib_EnterCriticalSection(); + + data->callback_data.callback = (cy_israddress)callback; + data->callback_data.callback_arg = callback_arg; + Cy_SysLib_ExitCriticalSection(savedIntrStatus); + Cy_SCB_SPI_RegisterCallback(config->reg_addr, config->spi_handle_events_func, + &(data->context)); + + data->irq_cause = 0; +} + +#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) +#define IFX_CAT1_INSTANCE_GROUP(instance, group) (((instance) << 4) | (group)) +#endif + +static uint8_t ifx_cat1_get_hfclk_for_peri_group(uint8_t peri_group) +{ +#if defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) + switch (peri_group) { + case IFX_CAT1_INSTANCE_GROUP(0, 0): + case IFX_CAT1_INSTANCE_GROUP(1, 4): + return CLK_HF0; + case IFX_CAT1_INSTANCE_GROUP(0, 7): + case IFX_CAT1_INSTANCE_GROUP(1, 0): + return CLK_HF1; + case IFX_CAT1_INSTANCE_GROUP(0, 3): + case IFX_CAT1_INSTANCE_GROUP(1, 2): + return CLK_HF5; + case IFX_CAT1_INSTANCE_GROUP(0, 4): + case IFX_CAT1_INSTANCE_GROUP(1, 3): + return CLK_HF6; + case IFX_CAT1_INSTANCE_GROUP(1, 1): + return CLK_HF7; + case IFX_CAT1_INSTANCE_GROUP(0, 2): + return CLK_HF9; + case IFX_CAT1_INSTANCE_GROUP(0, 1): + case IFX_CAT1_INSTANCE_GROUP(0, 5): + return CLK_HF10; + case IFX_CAT1_INSTANCE_GROUP(0, 8): + return CLK_HF11; + case IFX_CAT1_INSTANCE_GROUP(0, 6): + case IFX_CAT1_INSTANCE_GROUP(0, 9): + return CLK_HF13; + default: + return -EINVAL; + } +#elif defined(CONFIG_SOC_FAMILY_INFINEON_CAT1B) + switch (peri_group) { + case 0: + case 2: + return CLK_HF0; + case 1: + case 3: + return CLK_HF1; + case 4: + return CLK_HF2; + case 5: + return CLK_HF3; + case 6: + return CLK_HF4; + default: + return -EINVAL; + } +#endif + return -EINVAL; +} + +static cy_rslt_t ifx_cat1_spi_int_frequency(const struct device *dev, uint32_t hz, + uint8_t *over_sample_val) +{ + + struct ifx_cat1_spi_data *const data = dev->data; + + cy_rslt_t result = CY_RSLT_SUCCESS; + uint8_t oversample_value; + uint32_t divider_value; + uint32_t last_diff = 0xFFFFFFFFU; + uint8_t last_ovrsmpl_val = 0; + uint32_t last_dvdr_val = 0; + uint32_t oversampled_freq = 0; + uint32_t divided_freq = 0; + uint32_t diff = 0; + +#if defined(COMPONENT_CAT1A) + uint32_t peri_freq = Cy_SysClk_ClkPeriGetFrequency(); +#elif defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \ + defined(CONFIG_SOC_FAMILY_INFINEON_EDGE) + uint8_t hfclk = ifx_cat1_get_hfclk_for_peri_group(data->clock_peri_group); + + uint32_t peri_freq = Cy_SysClk_ClkHfGetFrequency(hfclk); +#endif + + if (!data->is_slave) { + for (oversample_value = IFX_CAT1_SPI_OVERSAMPLE_MIN; + oversample_value <= IFX_CAT1_SPI_OVERSAMPLE_MAX; oversample_value++) { + oversampled_freq = hz * oversample_value; + if ((hz * oversample_value > peri_freq) && + (IFX_CAT1_SPI_OVERSAMPLE_MIN == oversample_value)) { + return IFX_CAT1_SPI_RSLT_CLOCK_ERROR; + } else if (hz * oversample_value > peri_freq) { + continue; + } + + divider_value = ((peri_freq + ((hz * oversample_value) / 2)) / + (hz * oversample_value)); + divided_freq = peri_freq / divider_value; + diff = (oversampled_freq > divided_freq) ? oversampled_freq - divided_freq + : divided_freq - oversampled_freq; + + if (diff < last_diff) { + last_diff = diff; + last_ovrsmpl_val = oversample_value; + last_dvdr_val = divider_value; + if (diff == 0) { + break; + } + } + } + *over_sample_val = last_ovrsmpl_val; + } else { + /* Slave requires such frequency: required_frequency = N / ((0.5 * desired_period) + * – 20 nsec - tDSI, N is 3 when "Enable Input Glitch Filter" is false and 4 when + * true. tDSI Is external master delay which is assumed to be 16.66 nsec + */ + + /* Divided by 2 desired period to avoid dividing in required_frequency formula */ + cy_float32_t desired_period_us_divided = 5e5f * (1 / (cy_float32_t)hz); + uint32_t required_frequency = + (uint32_t)(3e6f / (desired_period_us_divided - 36.66f / 1e3f)); + + if (required_frequency > peri_freq) { + return IFX_CAT1_SPI_RSLT_CLOCK_ERROR; + } + + last_dvdr_val = 1; + CY_UNUSED_PARAMETER(last_ovrsmpl_val); + } + + en_clk_dst_t clk_idx = ifx_cat1_scb_get_clock_index(data->resource.block_num); + + if ((data->clock.block & 0x02) == 0) { + result = ifx_cat1_utils_peri_pclk_set_divider(clk_idx, &(data->clock), + last_dvdr_val - 1); + } else { + result = ifx_cat1_utils_peri_pclk_set_frac_divider(clk_idx, &(data->clock), + last_dvdr_val - 1, 0); + } + + return result; +} + +cy_rslt_t spi_set_frequency(const struct device *dev, uint32_t hz) +{ + + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + cy_rslt_t result = CY_RSLT_SUCCESS; + cy_rslt_t scb_init_result = CY_RSLT_SUCCESS; + uint8_t ovr_sample_val; + + Cy_SCB_SPI_Disable(config->reg_addr, &data->context); + result = ifx_cat1_spi_int_frequency(dev, hz, &ovr_sample_val); + + /* No need to reconfigure slave since oversample value, that was changed in + * ifx_cat1_spi_int_frequency, in slave is ignored + */ + if ((CY_RSLT_SUCCESS == result) && !data->is_slave && + (data->oversample_value != ovr_sample_val)) { + cy_stc_scb_spi_config_t config_structure = config->scb_spi_config; + + Cy_SCB_SPI_DeInit(config->reg_addr); + config_structure.spiMode = + data->is_slave == false ? CY_SCB_SPI_MASTER : CY_SCB_SPI_SLAVE; + config_structure.enableMsbFirst = data->msb_first; + config_structure.sclkMode = data->clk_mode; + config_structure.rxDataWidth = data->data_bits; + config_structure.txDataWidth = data->data_bits; + config_structure.oversample = ovr_sample_val; + data->oversample_value = ovr_sample_val; + scb_init_result = (cy_rslt_t)Cy_SCB_SPI_Init(config->reg_addr, &config_structure, + &(data->context)); + } + if (CY_RSLT_SUCCESS == scb_init_result) { + Cy_SCB_SPI_Enable(config->reg_addr); + } + + return result; +} + +static cy_rslt_t spi_init_hw(const struct device *dev, cy_stc_scb_spi_config_t *cfg) +{ + cy_rslt_t result = CY_RSLT_SUCCESS; + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + data->oversample_value = cfg->oversample; + data->data_bits = cfg->txDataWidth; + data->msb_first = cfg->enableMsbFirst; + data->clk_mode = cfg->sclkMode; + + result = (cy_rslt_t)Cy_SCB_SPI_Init(config->reg_addr, cfg, &(data->context)); + + if (result == CY_RSLT_SUCCESS) { + data->callback_data.callback = NULL; + data->callback_data.callback_arg = NULL; + data->irq_cause = 0; + + irq_enable(config->irq_num); + Cy_SCB_SPI_Enable(config->reg_addr); + } else { + spi_free(dev); + } + return result; +} + +cy_rslt_t ifx_cat1_spi_init_cfg(const struct device *dev, cy_stc_scb_spi_config_t *scb_spi_config) +{ + struct ifx_cat1_spi_data *const data = dev->data; + + cy_stc_scb_spi_config_t cfg_local = *scb_spi_config; + + cy_rslt_t result = CY_RSLT_SUCCESS; + bool is_slave = (cfg_local.spiMode == CY_SCB_SPI_SLAVE); + + data->is_slave = is_slave; + data->write_fill = (uint8_t)CY_SCB_SPI_DEFAULT_TX; + + result = ifx_cat1_spi_int_frequency(dev, IFX_CAT1_SPI_DEFAULT_SPEED, + &data->oversample_value); + + if (result == CY_RSLT_SUCCESS) { + result = spi_init_hw(dev, &cfg_local); + } + + if (result != CY_RSLT_SUCCESS) { + spi_free(dev); + } + return result; +} + +void spi_free(const struct device *dev) +{ + const struct ifx_cat1_spi_config *const config = dev->config; + + Cy_SCB_SPI_Disable(config->reg_addr, NULL); + Cy_SCB_SPI_DeInit(config->reg_addr); + irq_disable(config->irq_num); +} + +static void spi_irq_handler(const struct device *dev) +{ + struct ifx_cat1_spi_data *const data = dev->data; + const struct ifx_cat1_spi_config *const config = dev->config; + + Cy_SCB_SPI_Interrupt(config->reg_addr, &(data->context)); + + if (!data->is_async) { + if (CY_SCB_MASTER_INTR_SPI_DONE & + Cy_SCB_GetMasterInterruptStatusMasked(config->reg_addr)) { + Cy_SCB_SetMasterInterruptMask( + config->reg_addr, (Cy_SCB_GetMasterInterruptMask(config->reg_addr) & + (uint32_t)~CY_SCB_MASTER_INTR_SPI_DONE)); + Cy_SCB_ClearMasterInterrupt(config->reg_addr, CY_SCB_MASTER_INTR_SPI_DONE); + } + return; + } + + if (0 == (Cy_SCB_SPI_GetTransferStatus(config->reg_addr, &data->context) & + CY_SCB_SPI_TRANSFER_ACTIVE)) { + +#if !defined(IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL) + if (NULL != data->tx_buffer) { + /* Start TX Transfer */ + data->pending = IFX_CAT1_SPI_PENDING_TX; + const uint8_t *buf = data->tx_buffer; + + data->tx_buffer = NULL; + + Cy_SCB_SPI_Transfer(config->reg_addr, (uint8_t *)buf, NULL, + data->tx_buffer_size, &data->context); + } else if (NULL != data->rx_buffer) { + /* Start RX Transfer */ + data->pending = IFX_CAT1_SPI_PENDING_RX; + uint8_t *rx_buf = data->rx_buffer; + uint8_t *tx_buf; + size_t trx_size = data->rx_buffer_size; + + if (data->rx_buffer_size > 1) { + /* In this case we don't have a transmit buffer; we only have a + * receive buffer. While the PDL is fine with passing NULL for + * transmit, we don't get to control what data it is sending in that + * case, which we allowed the user to set. To honor the user's + * request, we reuse the rx buffer as the tx buffer too. We set all + * bytes beyond the one we will start filling in with the user + * provided 'write_fill'. This means the tx buffer is 1 element + * smaller than the rx buffer. As a result, we must therefore + * transfer 1 less element then we really want to in this transfer. + * When this transfer is complete, it will call into this again to + * receive the final element. + */ + trx_size -= + 1; /* Transfer everything left except for the last byte*/ + + uint8_t **rx_buffer_p = (uint8_t **)&data->rx_buffer; + + /* Start at second byte to avoid trying + * to transmit and receive the same byte + */ + tx_buf = *rx_buffer_p + 1; + + memset(tx_buf, data->write_fill, trx_size); + /* Move to 1 byte before end */ + *rx_buffer_p += trx_size; + + /* Transfer the last byte on the next interrupt */ + data->rx_buffer_size = 1; + } else { + tx_buf = &data->write_fill; + + data->rx_buffer = NULL; + } + + Cy_SCB_SPI_Transfer(config->reg_addr, tx_buf, rx_buf, trx_size, + &data->context); + } else { +#endif /* IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL */ + /* Finish Async Transfer */ + data->pending = IFX_CAT1_SPI_PENDING_NONE; + data->is_async = false; +#if !defined(IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL) + } +#endif /* IFX_CAT1_SPI_ASYMM_PDL_FUNC_AVAIL */ + } +} + +static void ifx_cat1_spi_cb_wrapper(const struct device *dev, uint32_t event) +{ + struct ifx_cat1_spi_data *const data = dev->data; + uint32_t anded_events = (data->irq_cause & ((uint32_t)event)); + + /* Don't call the callback until the final transfer + * has put everything in the FIFO/completed + */ + if ((anded_events & + (CY_SCB_SPI_TRANSFER_IN_FIFO_EVENT | CY_SCB_SPI_TRANSFER_CMPLT_EVENT)) && + !(data->rx_buffer == NULL && data->tx_buffer == NULL)) { + return; + } + + if (anded_events) { + ifx_cat1_spi_event_callback_t callback = + (ifx_cat1_spi_event_callback_t)data->callback_data.callback; + callback(data->callback_data.callback_arg, anded_events); + } +} diff --git a/dts/bindings/spi/infineon,cat1-spi-pdl.yaml b/dts/bindings/spi/infineon,cat1-spi-pdl.yaml new file mode 100644 index 0000000000000..d3b6c97a2c96f --- /dev/null +++ b/dts/bindings/spi/infineon,cat1-spi-pdl.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Infineon Technologies AG, +# or an affiliate of Infineon Technologies AG. +# +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon CAT1 SPI + +compatible: "infineon,cat1-spi-pdl" + +include: infineon,cat1-spi.yaml diff --git a/include/zephyr/drivers/clock_control/clock_control_ifx_cat1.h b/include/zephyr/drivers/clock_control/clock_control_ifx_cat1.h index 8726743c492a5..9d305f5b884cf 100644 --- a/include/zephyr/drivers/clock_control/clock_control_ifx_cat1.h +++ b/include/zephyr/drivers/clock_control/clock_control_ifx_cat1.h @@ -43,6 +43,22 @@ IFX_CAT1_PERIPHERAL_GROUP_ADJUST((instance), (gr), CY_SYSCLK_DIV_24_5_BIT) #endif +/* High frequency clock indices. */ +#define CLK_HF0 (0U) +#define CLK_HF1 (1U) +#define CLK_HF2 (2U) +#define CLK_HF3 (3U) +#define CLK_HF4 (4U) +#define CLK_HF5 (5U) +#define CLK_HF6 (6U) +#define CLK_HF7 (7U) +#define CLK_HF8 (8U) +#define CLK_HF9 (9U) +#define CLK_HF10 (10U) +#define CLK_HF11 (11U) +#define CLK_HF12 (12U) +#define CLK_HF13 (13U) + enum ifx_cat1_clock_block { #if defined(CONFIG_SOC_FAMILY_INFINEON_CAT1A) /* The first four items are here for backwards compatibility with old clock APIs */ diff --git a/soc/infineon/cat1b/psc3/Kconfig b/soc/infineon/cat1b/psc3/Kconfig index 8ca1975b8e214..028629bc2ccab 100644 --- a/soc/infineon/cat1b/psc3/Kconfig +++ b/soc/infineon/cat1b/psc3/Kconfig @@ -29,3 +29,4 @@ config SOC_SERIES_PSC3 select SOC_EARLY_INIT_HOOK select CPU_CORTEX_M33 select NOINIT_SNIPPET_FIRST + select CORTEX_M_SYSTICK diff --git a/tests/drivers/spi/spi_loopback/boards/kit_psc3m5_evk.overlay b/tests/drivers/spi/spi_loopback/boards/kit_psc3m5_evk.overlay new file mode 100644 index 0000000000000..9ef771a24224f --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/kit_psc3m5_evk.overlay @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +spi1: &scb4 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "infineon,cat1-spi-pdl"; + status = "okay"; + + pinctrl-0 = <&p4_0_scb4_spi_m_mosi &p4_1_scb4_spi_m_miso &p4_2_scb4_spi_m_clk>; + pinctrl-names = "default"; + cs-gpios = <&gpio_prt4 3 GPIO_ACTIVE_LOW>; + + clocks = <&peri0_group4_16bit_0>; + + slow@0 { + compatible = "test-spi-loopback-slow"; + reg = <0>; + spi-max-frequency = <3000000>; + }; + + fast@0 { + compatible = "test-spi-loopback-fast"; + reg = <0>; + spi-max-frequency = <3000000>; + }; +}; + +&gpio_prt4 { + status = "okay"; +}; + +&p4_0_scb4_spi_m_mosi { + drive-strength = "full"; + drive-push-pull; +}; + +&p4_1_scb4_spi_m_miso { + drive-strength = "full"; + input-enable; +}; + +&p4_2_scb4_spi_m_clk { + drive-strength = "full"; + drive-push-pull; +}; + +&peri0_group4_16bit_0 { + status = "okay"; + resource-type = ; + resource-instance = <4>; +}; diff --git a/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_common.overlay b/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_common.overlay new file mode 100644 index 0000000000000..d8390cff0c4fb --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_common.overlay @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +spi1: &scb10 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "infineon,cat1-spi-pdl"; + status = "okay"; + + pinctrl-0 = <&p16_1_scb10_spi_m_mosi &p16_2_scb10_spi_m_miso &p16_0_scb10_spi_m_clk>; + pinctrl-names = "default"; + cs-gpios = <&gpio_prt16 3 GPIO_ACTIVE_LOW>; + + clocks = <&peri0_group1_16bit_2>; + + slow@0 { + compatible = "test-spi-loopback-slow"; + reg = <0>; + spi-max-frequency = <3000000>; + }; + + fast@0 { + compatible = "test-spi-loopback-fast"; + reg = <0>; + spi-max-frequency = <3000000>; + }; +}; + +&peri0_group1_16bit_2 { + status = "okay"; + resource-type = ; + resource-instance = <10>; +}; + +&p16_1_scb10_spi_m_mosi { + drive-strength = "full"; + drive-push-pull; +}; + +&p16_2_scb10_spi_m_miso { + drive-strength = "full"; + input-enable; +}; + +&p16_0_scb10_spi_m_clk { + drive-strength = "full"; + drive-push-pull; +}; diff --git a/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_pse846gps2dbzc4a_m55.overlay b/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_pse846gps2dbzc4a_m55.overlay new file mode 100644 index 0000000000000..533ab3852f0e4 --- /dev/null +++ b/tests/drivers/spi/spi_loopback/boards/kit_pse84_eval_pse846gps2dbzc4a_m55.overlay @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2025 Infineon Technologies AG, + * or an affiliate of Infineon Technologies AG. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "kit_pse84_eval_common.overlay"