/* * Copyright (c) 2023 Intel Corporation. * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT intel_penwell_spi #include #include #include #include #include #include #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) BUILD_ASSERT(IS_ENABLED(CONFIG_PCIE), "DT need CONFIG_PCIE"); #include #endif #include LOG_MODULE_REGISTER(spi_pw, CONFIG_SPI_LOG_LEVEL); #include "spi_pw.h" static uint32_t spi_pw_reg_read(const struct device *dev, uint32_t offset) { return sys_read32(DEVICE_MMIO_GET(dev) + offset); } static void spi_pw_reg_write(const struct device *dev, uint32_t offset, uint32_t val) { sys_write32(val, DEVICE_MMIO_GET(dev) + offset); } static void spi_pw_ssp_reset(const struct device *dev) { /* Bring the controller from reset state in to operational mode */ spi_pw_reg_write(dev, PW_SPI_REG_RESETS, 0x00); spi_pw_reg_write(dev, PW_SPI_REG_RESETS, PW_SPI_INST_RESET); } #ifndef CONFIG_SPI_PW_INTERRUPT static bool is_spi_transfer_ongoing(struct spi_pw_data *spi) { return spi_context_tx_on(&spi->ctx) || spi_context_rx_on(&spi->ctx); } #endif static void spi_pw_enable_cs_hw_ctrl(const struct device *dev) { uint32_t cs_ctrl; cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL); cs_ctrl &= PW_SPI_CS_CTRL_HW_MODE; spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl); } static void spi_pw_cs_sw_ctrl(const struct device *dev, bool enable) { uint32_t cs_ctrl; cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL); cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK); /* Enable chip select software control method */ cs_ctrl |= PW_SPI_CS_CTRL_SW_MODE; if (enable) { cs_ctrl &= PW_SPI_CS_LOW; } else { cs_ctrl |= PW_SPI_CS_HIGH; } spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl); } #ifdef CONFIG_SPI_PW_INTERRUPT static void spi_pw_intr_enable(const struct device *dev, bool rx_mask) { uint32_t ctrlr1; ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1); if (rx_mask) { ctrlr1 |= PW_SPI_INTR_BITS; } else { ctrlr1 |= PW_SPI_INTR_BITS; ctrlr1 &= ~(PW_SPI_INTR_MASK_RX); } spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1); } static void spi_pw_intr_disable(const struct device *dev) { uint32_t ctrlr1; ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1); ctrlr1 &= ~(PW_SPI_INTR_BITS); spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1); } #endif static void spi_pw_ssp_enable(const struct device *dev) { uint32_t ctrlr0; ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0); ctrlr0 |= PW_SPI_CTRLR0_SSE_BIT; spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0); } static void spi_pw_ssp_disable(const struct device *dev) { uint32_t ctrlr0; ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0); ctrlr0 &= ~(PW_SPI_CTRLR0_SSE_BIT); spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0); } static bool is_pw_ssp_busy(const struct device *dev) { uint32_t status; status = spi_pw_reg_read(dev, PW_SPI_REG_SSSR); return (status & PW_SPI_SSSR_BSY_BIT) ? true : false; } static uint8_t spi_pw_get_frame_size(const struct spi_config *config) { uint8_t dfs = SPI_WORD_SIZE_GET(config->operation); dfs /= PW_SPI_WIDTH_8BITS; if ((dfs == 0) || (dfs > PW_SPI_FRAME_SIZE_4_BYTES)) { LOG_WRN("Unsupported dfs, 1-byte size will be used"); dfs = PW_SPI_FRAME_SIZE_1_BYTE; } return dfs; } void spi_pw_cs_ctrl_enable(const struct device *dev, bool enable) { struct spi_pw_data *spi = dev->data; if (enable == true) { if (spi->cs_mode == CS_SW_MODE) { spi_pw_cs_sw_ctrl(dev, true); } else if (spi->cs_mode == CS_GPIO_MODE) { spi_context_cs_control(&spi->ctx, true); } } else { if (spi->cs_mode == CS_SW_MODE) { spi_pw_cs_sw_ctrl(dev, false); } else if (spi->cs_mode == CS_GPIO_MODE) { spi_context_cs_control(&spi->ctx, false); } } } static void spi_pw_cs_ctrl_init(const struct device *dev) { uint32_t cs_ctrl; struct spi_pw_data *spi = dev->data; /* Enable chip select output CS0/CS1 */ cs_ctrl = spi_pw_reg_read(dev, PW_SPI_REG_CS_CTRL); if (spi->cs_output == PW_SPI_CS1_OUTPUT_SELECT) { cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK << PW_SPI_CS_EN_SHIFT); /* Set chip select CS1 */ cs_ctrl |= PW_SPI_CS1_SELECT; } else { /* Set chip select CS0 */ cs_ctrl &= ~(PW_SPI_CS_CTRL_CS_MASK << PW_SPI_CS_EN_SHIFT); } spi_pw_reg_write(dev, PW_SPI_REG_CS_CTRL, cs_ctrl); if (spi->cs_mode == CS_HW_MODE) { spi_pw_enable_cs_hw_ctrl(dev); } else if (spi->cs_mode == CS_SW_MODE) { spi_pw_cs_sw_ctrl(dev, false); } else if (spi->cs_mode == CS_GPIO_MODE) { spi_pw_cs_sw_ctrl(dev, false); } } static void spi_pw_tx_thld_set(const struct device *dev) { uint32_t reg_data; /* Tx threshold */ reg_data = spi_pw_reg_read(dev, PW_SPI_REG_SITF); /* mask high water mark bits in tx fifo reg */ reg_data &= ~(PW_SPI_WM_MASK); /* mask low water mark bits in tx fifo reg */ reg_data &= ~(PW_SPI_WM_MASK << PW_SPI_SITF_LWMTF_SHIFT); reg_data |= (PW_SPI_SITF_HIGH_WM_DFLT | PW_SPI_SITF_LOW_WM_DFLT); spi_pw_reg_write(dev, PW_SPI_REG_SITF, reg_data); } static void spi_pw_rx_thld_set(const struct device *dev, struct spi_pw_data *spi) { uint32_t reg_data; /* Rx threshold */ reg_data = spi_pw_reg_read(dev, PW_SPI_REG_SIRF); reg_data &= (uint32_t) ~(PW_SPI_WM_MASK); reg_data |= PW_SPI_SIRF_WM_DFLT; if (spi->ctx.rx_len && spi->ctx.rx_len < spi->fifo_depth) { reg_data = spi->ctx.rx_len - 1; } spi_pw_reg_write(dev, PW_SPI_REG_SIRF, reg_data); } static int spi_pw_set_data_size(const struct device *dev, const struct spi_config *config) { uint32_t ctrlr0; ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0); /* Full duplex mode */ ctrlr0 &= ~(PW_SPI_CTRLR0_MOD_BIT); ctrlr0 &= PW_SPI_CTRLR0_DATA_MASK; ctrlr0 &= PW_SPI_CTRLR0_EDSS_MASK; /* Set the word size */ if (SPI_WORD_SIZE_GET(config->operation) == 4) { ctrlr0 |= PW_SPI_DATA_SIZE_4_BIT; } else if (SPI_WORD_SIZE_GET(config->operation) == 8) { ctrlr0 |= PW_SPI_DATA_SIZE_8_BIT; } else if (SPI_WORD_SIZE_GET(config->operation) == 16) { ctrlr0 |= PW_SPI_DATA_SIZE_16_BIT; } else if (SPI_WORD_SIZE_GET(config->operation) == 32) { ctrlr0 |= PW_SPI_DATA_SIZE_32_BIT; } else { LOG_ERR("Invalid word size"); return -ENOTSUP; } spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0); return 0; } static void spi_pw_config_phase_polarity(const struct device *dev, const struct spi_config *config) { uint8_t mode; uint32_t ctrlr1; ctrlr1 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR1); mode = (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) | (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA); LOG_DBG("mode: 0x%x", (mode >> 1)); switch (mode >> 1) { case SPI_PW_MODE0: ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK); ctrlr1 &= ~(PW_SPI_CTRL1_SPO_BIT); ctrlr1 &= ~(PW_SPI_CTRL1_SPH_BIT); break; case SPI_PW_MODE1: ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK); ctrlr1 |= PW_SPI_CTRL1_SPO_BIT; ctrlr1 &= ~(PW_SPI_CTRL1_SPH_BIT); break; case SPI_PW_MODE2: ctrlr1 &= ~(PW_SPI_CTRL1_SPO_SPH_MASK); ctrlr1 &= ~(PW_SPI_CTRL1_SPO_BIT); ctrlr1 |= PW_SPI_CTRL1_SPH_BIT; break; case SPI_PW_MODE3: ctrlr1 |= PW_SPI_CTRL1_SPO_BIT; ctrlr1 |= PW_SPI_CTRL1_SPH_BIT; break; } /* Set Polarity & Phase */ spi_pw_reg_write(dev, PW_SPI_REG_CTRLR1, ctrlr1); } static void spi_pw_enable_clk(const struct device *dev) { uint32_t clks; /*Update M:N value & enable clock */ clks = spi_pw_reg_read(dev, PW_SPI_REG_CLKS); clks &= ~(PW_SPI_CLKS_MVAL_MASK); clks &= ~(PW_SPI_CLKS_NVAL_MASK); clks |= (PW_SPI_CLKS_MVAL | PW_SPI_CLKS_NVAL | PW_SPI_CLKS_EN_BIT | PW_SPI_CLKS_UPDATE_BIT); spi_pw_reg_write(dev, PW_SPI_REG_CLKS, clks); } static void spi_pw_config_clk(const struct device *dev, const struct spi_pw_config *info, const struct spi_config *config) { uint32_t ctrlr0, scr; /* Update scr control bits */ if (!config->frequency) { scr = PW_SPI_BR_2MHZ; } else if (config->frequency > PW_SPI_BR_MAX_FRQ) { scr = (info->clock_freq / PW_SPI_BR_MAX_FRQ) - 1; } else { scr = (info->clock_freq / config->frequency) - 1; } ctrlr0 = spi_pw_reg_read(dev, PW_SPI_REG_CTRLR0); ctrlr0 &= ~(PW_SPI_SCR_MASK); ctrlr0 |= (scr << PW_SPI_SCR_SHIFT); spi_pw_reg_write(dev, PW_SPI_REG_CTRLR0, ctrlr0); } static void spi_pw_completed(const struct device *dev, int err) { struct spi_pw_data *spi = dev->data; if (!err && (spi_context_tx_on(&spi->ctx) || spi_context_rx_on(&spi->ctx))) { return; } /* need to give time for FIFOs to drain before issuing more commands */ while (is_pw_ssp_busy(dev)) { } #ifdef CONFIG_SPI_PW_INTERRUPT /* Disabling interrupts */ spi_pw_intr_disable(dev); #endif /* Disabling the controller operation, which also clear's all status bits * in status register */ spi_pw_ssp_disable(dev); spi_pw_cs_ctrl_enable(dev, false); LOG_DBG("SPI transaction completed %s error\n", err ? "with" : "without"); spi_context_complete(&spi->ctx, dev, err); } static void spi_pw_clear_intr(const struct device *dev) { uint32_t sssr; sssr = spi_pw_reg_read(dev, PW_SPI_REG_SSSR); sssr &= ~(PW_SPI_INTR_ERRORS_MASK); spi_pw_reg_write(dev, PW_SPI_REG_SSSR, sssr); } static int spi_pw_get_tx_fifo_level(const struct device *dev) { uint32_t tx_fifo_level; tx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SITF); tx_fifo_level = ((tx_fifo_level & PW_SPI_SITF_SITFL_MASK) >> PW_SPI_SITF_SITFL_SHIFT); return tx_fifo_level; } static int spi_pw_get_rx_fifo_level(const struct device *dev) { uint32_t rx_fifo_level; rx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SIRF); rx_fifo_level = ((rx_fifo_level & PW_SPI_SIRF_SIRFL_MASK) >> PW_SPI_SIRF_SIRFL_SHIFT); return rx_fifo_level; } static void spi_pw_reset_tx_fifo_level(const struct device *dev) { uint32_t tx_fifo_level; tx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SITF); tx_fifo_level &= ~(PW_SPI_SITF_SITFL_MASK); spi_pw_reg_write(dev, PW_SPI_REG_SITF, tx_fifo_level); } static void spi_pw_update_rx_fifo_level(uint32_t len, const struct device *dev) { uint32_t rx_fifo_level; rx_fifo_level = spi_pw_reg_read(dev, PW_SPI_REG_SIRF); rx_fifo_level &= ~(PW_SPI_SIRF_SIRFL_MASK); rx_fifo_level |= (len << PW_SPI_SIRF_SIRFL_SHIFT); spi_pw_reg_write(dev, PW_SPI_REG_SIRF, rx_fifo_level); } static void spi_pw_tx_data(const struct device *dev) { struct spi_pw_data *spi = dev->data; uint32_t data = 0U; int32_t fifo_len; if (spi_context_rx_on(&spi->ctx)) { fifo_len = spi->fifo_depth - spi_pw_get_tx_fifo_level(dev) - spi_pw_get_rx_fifo_level(dev); if (fifo_len < 0) { fifo_len = 0U; } } else { fifo_len = spi->fifo_depth - spi_pw_get_tx_fifo_level(dev); } while (fifo_len > 0) { if (spi_context_tx_buf_on(&spi->ctx)) { switch (spi->dfs) { case 1: data = UNALIGNED_GET((uint8_t *) (spi->ctx.tx_buf)); break; case 2: data = UNALIGNED_GET((uint16_t *) (spi->ctx.tx_buf)); break; case 4: data = UNALIGNED_GET((uint32_t *) (spi->ctx.tx_buf)); break; } } else if (spi_context_rx_on(&spi->ctx)) { if ((int)(spi->ctx.rx_len - spi->fifo_diff) <= 0) { break; } data = 0U; } else if (spi_context_tx_on(&spi->ctx)) { data = 0U; } else { break; } spi_pw_reg_write(dev, PW_SPI_REG_SSDR, data); spi_context_update_tx(&spi->ctx, spi->dfs, 1); spi->fifo_diff++; fifo_len--; } if (!spi_context_tx_on(&spi->ctx)) { spi_pw_reset_tx_fifo_level(dev); } } static void spi_pw_rx_data(const struct device *dev) { struct spi_pw_data *spi = dev->data; while (spi_pw_get_rx_fifo_level(dev)) { uint32_t data = spi_pw_reg_read(dev, PW_SPI_REG_SSDR); if (spi_context_rx_buf_on(&spi->ctx)) { switch (spi->dfs) { case 1: UNALIGNED_PUT(data, (uint8_t *)spi->ctx.rx_buf); break; case 2: UNALIGNED_PUT(data, (uint16_t *)spi->ctx.rx_buf); break; case 4: UNALIGNED_PUT(data, (uint32_t *)spi->ctx.rx_buf); break; } } spi_context_update_rx(&spi->ctx, spi->dfs, 1); spi->fifo_diff--; } if (!spi->ctx.rx_len && spi->ctx.tx_len < spi->fifo_depth) { spi_pw_update_rx_fifo_level(spi->ctx.tx_len - 1, dev); } else if (spi_pw_get_rx_fifo_level(dev) >= spi->ctx.rx_len) { spi_pw_update_rx_fifo_level(spi->ctx.rx_len - 1, dev); } } static int spi_pw_transfer(const struct device *dev) { uint32_t intr_status; int err; intr_status = spi_pw_reg_read(dev, PW_SPI_REG_SSSR); if (intr_status & PW_SPI_SSSR_ROR_BIT) { LOG_ERR("Receive FIFO overrun"); err = -EIO; goto out; } if (intr_status & PW_SPI_SSSR_TUR_BIT) { LOG_ERR("Transmit FIFO underrun"); err = -EIO; goto out; } if (intr_status & PW_SPI_SSSR_TINT_BIT) { LOG_ERR("Receiver timeout interrupt"); err = -EIO; goto out; } err = 0; if (intr_status & PW_SPI_SSSR_RNE_BIT) { spi_pw_rx_data(dev); } if (intr_status & PW_SPI_SSSR_TNF_BIT) { spi_pw_tx_data(dev); } out: if (err) { spi_pw_clear_intr(dev); } return err; } static int spi_pw_configure(const struct device *dev, const struct spi_pw_config *info, struct spi_pw_data *spi, const struct spi_config *config) { int err; /* At this point, it's mandatory to set this on the context! */ spi->ctx.config = config; if (!spi_cs_is_gpio(spi->ctx.config)) { if (spi->cs_mode == CS_GPIO_MODE) { LOG_DBG("cs gpio is NULL, switch to hw mode"); spi->cs_mode = CS_HW_MODE; spi_pw_enable_cs_hw_ctrl(dev); } } if (config->operation & SPI_HALF_DUPLEX) { LOG_ERR("Half-duplex not supported"); return -ENOTSUP; } /* Verify if requested op mode is relevant to this controller */ if (config->operation & SPI_OP_MODE_SLAVE) { LOG_ERR("Slave mode not supported"); return -ENOTSUP; } if ((config->operation & SPI_TRANSFER_LSB) || (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && (config->operation & (SPI_LINES_DUAL | SPI_LINES_QUAD | SPI_LINES_OCTAL)))) { LOG_ERR("Extended mode Unsupported configuration"); return -EINVAL; } if (config->operation & SPI_FRAME_FORMAT_TI) { LOG_ERR("TI frame format not supported"); return -ENOTSUP; } if (config->operation & SPI_HOLD_ON_CS) { LOG_ERR("Chip select hold not supported"); return -ENOTSUP; } /* Set mode & data size */ err = spi_pw_set_data_size(dev, config); if (err) { LOG_ERR("Invalid data size"); return -ENOTSUP; } /* Set Polarity & Phase */ spi_pw_config_phase_polarity(dev, config); /* enable clock */ spi_pw_enable_clk(dev); /* configure */ spi_pw_config_clk(dev, info, config); return 0; } static int transceive(const struct device *dev, const struct spi_config *config, const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs, bool asynchronous, spi_callback_t cb, void *userdata) { const struct spi_pw_config *info = dev->config; struct spi_pw_data *spi = dev->data; int err; if (!tx_bufs && !rx_bufs) { LOG_ERR(" Tx & Rx buff null"); return 0; } if (asynchronous) { LOG_ERR("Async not supported"); return -ENOTSUP; } spi_context_lock(&spi->ctx, asynchronous, cb, userdata, config); /* Configure */ err = spi_pw_configure(dev, info, spi, config); if (err) { LOG_ERR("spi pw config fail"); goto out; } /* Frame size in number of data bytes */ spi->dfs = spi_pw_get_frame_size(config); spi_context_buffers_setup(&spi->ctx, tx_bufs, rx_bufs, spi->dfs); spi->fifo_diff = 0U; /* Tx threshold */ spi_pw_tx_thld_set(dev); /* Rx threshold */ spi_pw_rx_thld_set(dev, spi); spi_pw_cs_ctrl_enable(dev, true); /* Enable ssp operation */ spi_pw_ssp_enable(dev); #ifdef CONFIG_SPI_PW_INTERRUPT LOG_DBG("Interrupt Mode"); /* Enable interrupts */ if (rx_bufs) { spi_pw_intr_enable(dev, true); } else { spi_pw_intr_enable(dev, false); } err = spi_context_wait_for_completion(&spi->ctx); #else LOG_DBG("Polling Mode"); do { err = spi_pw_transfer(dev); } while ((!err) && is_spi_transfer_ongoing(spi)); spi_pw_completed(dev, err); #endif out: spi_context_release(&spi->ctx, err); return err; } static int spi_pw_transceive(const struct device *dev, const struct spi_config *config, const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) { LOG_DBG("%p, %p, %p\n", dev, tx_bufs, rx_bufs); return transceive(dev, config, tx_bufs, rx_bufs, false, NULL, NULL); } #ifdef CONFIG_SPI_ASYNC static int spi_pw_transceive_async(const struct device *dev, const struct spi_config *config, const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs, spi_callback_t cb, void *userdata) { LOG_DBG("%p, %p, %p, %p, %p\n", dev, tx_bufs, rx_bufs, cb, userdata); return transceive(dev, config, tx_bufs, rx_bufs, true, cb, userdata); } #endif /* CONFIG_SPI_ASYNC */ static int spi_pw_release(const struct device *dev, const struct spi_config *config) { struct spi_pw_data *spi = dev->data; if (!spi_context_configured(&spi->ctx, config)) { return -EINVAL; } spi_context_unlock_unconditionally(&spi->ctx); return 0; } #ifdef CONFIG_SPI_PW_INTERRUPT static void spi_pw_isr(const void *arg) { const struct device *dev = (const struct device *)arg; int err; err = spi_pw_transfer(dev); spi_pw_completed(dev, err); } #endif static DEVICE_API(spi, pw_spi_api) = { .transceive = spi_pw_transceive, .release = spi_pw_release, #ifdef CONFIG_SPI_ASYNC .transceive_async = spi_pw_transceive_async, #endif /* CONFIG_SPI_ASYNC */ #ifdef CONFIG_SPI_RTIO .iodev_submit = spi_rtio_iodev_default_submit, #endif }; static int spi_pw_init(const struct device *dev) { const struct spi_pw_config *info = dev->config; struct spi_pw_data *spi = dev->data; int err; #if DT_ANY_INST_ON_BUS_STATUS_OKAY(pcie) if (info->pcie) { struct pcie_bar mbar; if (info->pcie->bdf == PCIE_BDF_NONE) { LOG_ERR("Cannot probe PCI device"); return -ENODEV; } if (!pcie_probe_mbar(info->pcie->bdf, 0, &mbar)) { LOG_ERR("MBAR not found"); return -EINVAL; } pcie_set_cmd(info->pcie->bdf, PCIE_CONF_CMDSTAT_MEM, true); device_map(DEVICE_MMIO_RAM_PTR(dev), mbar.phys_addr, mbar.size, K_MEM_CACHE_NONE); pcie_set_cmd(info->pcie->bdf, PCIE_CONF_CMDSTAT_MASTER, true); } else { DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); } #else DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); #endif /* Bring ssp out of reset */ spi_pw_ssp_reset(dev); /* Disable ssp operation */ spi_pw_ssp_disable(dev); /* Chip select control */ spi_pw_cs_ctrl_init(dev); #if defined(CONFIG_SPI_PW_INTERRUPT) /* Mask interrupts */ spi_pw_intr_disable(dev); /* Init and connect IRQ */ info->irq_config(dev); #endif if (spi->cs_mode == CS_GPIO_MODE) { err = spi_context_cs_configure_all(&spi->ctx); if (err < 0) { LOG_ERR("Failed to configure CS pins: %d", err); return err; } } spi_context_unlock_unconditionally(&spi->ctx); LOG_DBG("SPI pw init success"); return 0; } #define INIT_PCIE0(n) #define INIT_PCIE1(n) DEVICE_PCIE_INST_INIT(n, pcie), #define INIT_PCIE(n) _CONCAT(INIT_PCIE, DT_INST_ON_BUS(n, pcie))(n) #define DEFINE_PCIE0(n) #define DEFINE_PCIE1(n) DEVICE_PCIE_INST_DECLARE(n) #define SPI_PCIE_DEFINE(n) _CONCAT(DEFINE_PCIE, DT_INST_ON_BUS(n, pcie))(n) #ifdef CONFIG_SPI_PW_INTERRUPT #define SPI_INTEL_IRQ_FLAGS_SENSE0(n) 0 #define SPI_INTEL_IRQ_FLAGS_SENSE1(n) DT_INST_IRQ(n, sense) #define SPI_INTEL_IRQ_FLAGS(n) \ _CONCAT(SPI_INTEL_IRQ_FLAGS_SENSE, DT_INST_IRQ_HAS_CELL(n, sense))(n) #define SPI_INTEL_IRQ_INIT(n) \ BUILD_ASSERT(IS_ENABLED(CONFIG_DYNAMIC_INTERRUPTS), \ "SPI PCIe requires dynamic interrupts"); \ static void spi_##n##_irq_init(const struct device *dev) \ { \ const struct spi_pw_config *info = dev->config; \ unsigned int irq; \ if (DT_INST_IRQN(n) == PCIE_IRQ_DETECT) { \ irq = pcie_alloc_irq(info->pcie->bdf); \ if (irq == PCIE_CONF_INTR_IRQ_NONE) { \ return; \ } \ } else { \ irq = DT_INST_IRQN(n); \ pcie_conf_write(info->pcie->bdf, \ PCIE_CONF_INTR, irq); \ } \ pcie_connect_dynamic_irq(info->pcie->bdf, irq, \ DT_INST_IRQ(n, priority), \ (void (*)(const void *))spi_pw_isr, \ DEVICE_DT_INST_GET(n), \ SPI_INTEL_IRQ_FLAGS(n)); \ pcie_irq_enable(info->pcie->bdf, irq); \ LOG_DBG("lpass spi Configure irq %d", irq); \ } #define SPI_PW_DEV_INIT(n) \ static struct spi_pw_data spi_##n##_data = { \ SPI_CONTEXT_INIT_LOCK(spi_##n##_data, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_##n##_data, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \ .cs_mode = DT_INST_PROP(n, pw_cs_mode), \ .cs_output = DT_INST_PROP(n, pw_cs_output), \ .fifo_depth = DT_INST_PROP(n, pw_fifo_depth), \ }; \ SPI_PCIE_DEFINE(n); \ SPI_INTEL_IRQ_INIT(n) \ static const struct spi_pw_config spi_##n##_config = { \ .irq_config = spi_##n##_irq_init, \ .clock_freq = DT_INST_PROP(n, clock_frequency), \ INIT_PCIE(n) \ }; \ SPI_DEVICE_DT_INST_DEFINE(n, spi_pw_init, NULL, \ &spi_##n##_data, &spi_##n##_config, \ POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ &pw_spi_api); #else #define SPI_PW_DEV_INIT(n) \ static struct spi_pw_data spi_##n##_data = { \ SPI_CONTEXT_INIT_LOCK(spi_##n##_data, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_##n##_data, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \ .cs_mode = DT_INST_PROP(n, pw_cs_mode), \ .cs_output = DT_INST_PROP(n, pw_cs_output), \ .fifo_depth = DT_INST_PROP(n, pw_fifo_depth), \ }; \ SPI_PCIE_DEFINE(n); \ static const struct spi_pw_config spi_##n##_config = { \ .clock_freq = DT_INST_PROP(n, clock_frequency), \ INIT_PCIE(n) \ }; \ SPI_DEVICE_DT_INST_DEFINE(n, spi_pw_init, NULL, \ &spi_##n##_data, &spi_##n##_config, \ POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ &pw_spi_api); #endif DT_INST_FOREACH_STATUS_OKAY(SPI_PW_DEV_INIT)