From 326691155e0b1405950b3e242f3953302025e30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 4 Jun 2025 13:04:36 +0200 Subject: [PATCH 1/5] drivers: i2c: litex: litei2c: support interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit support interrupts for rx_ready, so we can use the time we are waiting for other stuff. Signed-off-by: Fin Maaß --- drivers/i2c/i2c_litex_litei2c.c | 129 ++++++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 13 deletions(-) diff --git a/drivers/i2c/i2c_litex_litei2c.c b/drivers/i2c/i2c_litex_litei2c.c index d73c8230854e..de9337dc90f7 100644 --- a/drivers/i2c/i2c_litex_litei2c.c +++ b/drivers/i2c/i2c_litex_litei2c.c @@ -16,6 +16,11 @@ LOG_MODULE_REGISTER(i2c_litex_litei2c, CONFIG_I2C_LOG_LEVEL); #include +#define I2C_LITEX_ANY_HAS_IRQ DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) +#define I2C_LITEX_ALL_HAS_IRQ DT_ALL_INST_HAS_PROP_STATUS_OKAY(interrupts) + +#define I2C_LITEX_HAS_IRQ UTIL_OR(I2C_LITEX_ALL_HAS_IRQ, config->has_irq) + #define MASTER_STATUS_TX_READY_OFFSET 0x0 #define MASTER_STATUS_RX_READY_OFFSET 0x1 #define MASTER_STATUS_NACK_OFFSET 0x8 @@ -28,10 +33,21 @@ struct i2c_litex_litei2c_config { uint32_t master_rxtx_addr; uint32_t master_status_addr; uint32_t bitrate; +#if I2C_LITEX_ANY_HAS_IRQ + uint32_t master_ev_pending_addr; + uint32_t master_ev_enable_addr; + void (*irq_config_func)(const struct device *dev); +#if !I2C_LITEX_ALL_HAS_IRQ + bool has_irq; +#endif /* !I2C_LITEX_ALL_HAS_IRQ */ +#endif /* I2C_LITEX_ANY_HAS_IRQ */ }; struct i2c_litex_litei2c_data { struct k_mutex mutex; +#if I2C_LITEX_ANY_HAS_IRQ + struct k_sem sem_rx_ready; +#endif /* I2C_LITEX_ANY_HAS_IRQ */ }; static int i2c_litex_configure(const struct device *dev, uint32_t dev_config) @@ -105,6 +121,26 @@ static int i2c_litex_write_settings(const struct device *dev, uint8_t len_tx, ui return 0; } +static void i2c_litex_wait_for_rx_ready(const struct device *dev) +{ + const struct i2c_litex_litei2c_config *config = dev->config; + +#if I2C_LITEX_ANY_HAS_IRQ + struct i2c_litex_litei2c_data *data = dev->data; + + if (I2C_LITEX_HAS_IRQ) { + /* Wait for the RX ready event */ + k_sem_take(&data->sem_rx_ready, K_FOREVER); + return; + } +#endif /* I2C_LITEX_ANY_HAS_IRQ */ + + while (!(litex_read8(config->master_status_addr) & + BIT(MASTER_STATUS_RX_READY_OFFSET))) { + /* Wait until RX is ready */ + } +} + static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) { @@ -130,6 +166,26 @@ static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, ui litex_write8(1, config->master_active_addr); + /* Flush RX buffer */ + while ((litex_read8(config->master_status_addr) & + BIT(MASTER_STATUS_RX_READY_OFFSET))) { + rx_buf = litex_read32(config->master_rxtx_addr); + LOG_DBG("flushed rxd: 0x%x", rx_buf); + } + + while (!(litex_read8(config->master_status_addr) & + BIT(MASTER_STATUS_TX_READY_OFFSET))) { + (void)litex_read32(config->master_rxtx_addr); + } + +#if I2C_LITEX_ANY_HAS_IRQ + if (I2C_LITEX_HAS_IRQ) { + litex_write8(BIT(0), config->master_ev_enable_addr); + litex_write8(BIT(0), config->master_ev_pending_addr); + k_sem_reset(&data->sem_rx_ready); + } +#endif /* I2C_LITEX_ANY_HAS_IRQ */ + LOG_DBG("addr: 0x%x", addr); litex_write8((uint8_t)addr, config->master_addr_addr); @@ -206,18 +262,10 @@ static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, ui LOG_DBG("len_tx: %d, len_rx: %d", len_tx, len_rx); i2c_litex_write_settings(dev, len_tx, len_rx, false); - while (!(litex_read8(config->master_status_addr) & - BIT(MASTER_STATUS_TX_READY_OFFSET))) { - ; - } - LOG_DBG("tx_buf: 0x%x", tx_buf); litex_write32(tx_buf, config->master_rxtx_addr); - while (!(litex_read8(config->master_status_addr) & - BIT(MASTER_STATUS_RX_READY_OFFSET))) { - ; - } + i2c_litex_wait_for_rx_ready(dev); if (litex_read16(config->master_status_addr) & BIT(MASTER_STATUS_NACK_OFFSET)) { @@ -269,6 +317,12 @@ static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, ui litex_write8(0, config->master_active_addr); +#if I2C_LITEX_ANY_HAS_IRQ + if (I2C_LITEX_HAS_IRQ) { + litex_write8(0, config->master_ev_enable_addr); + } +#endif /* I2C_LITEX_ANY_HAS_IRQ */ + k_mutex_unlock(&data->mutex); return ret; @@ -304,19 +358,40 @@ static int i2c_litex_recover_bus(const struct device *dev) return 0; } -static int i2c_litex_init(const struct device *dev) +#if I2C_LITEX_ANY_HAS_IRQ +static void i2c_litex_irq_handler(const struct device *dev) { const struct i2c_litex_litei2c_config *config = dev->config; struct i2c_litex_litei2c_data *data = dev->data; - int ret; - k_mutex_init(&data->mutex); + if (litex_read8(config->master_ev_pending_addr) & BIT(0)) { + k_sem_give(&data->sem_rx_ready); + + /* ack reader irq */ + litex_write8(BIT(0), config->master_ev_pending_addr); + } +} +#endif /* I2C_LITEX_ANY_HAS_IRQ */ + +static int i2c_litex_init(const struct device *dev) +{ + const struct i2c_litex_litei2c_config *config = dev->config; + int ret; ret = i2c_litex_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate)); if (ret != 0) { LOG_ERR("failed to configure I2C: %d", ret); } +#if I2C_LITEX_ANY_HAS_IRQ + if (I2C_LITEX_HAS_IRQ) { + /* Disable interrupts initially */ + litex_write8(0, config->master_ev_enable_addr); + + config->irq_config_func(dev); + } +#endif /* I2C_LITEX_ANY_HAS_IRQ */ + return ret; } @@ -332,8 +407,35 @@ static DEVICE_API(i2c, i2c_litex_litei2c_driver_api) = { /* Device Instantiation */ +#define I2C_LITEX_IRQ(n) \ + BUILD_ASSERT(DT_INST_REG_HAS_NAME(n, master_ev_pending) && \ + DT_INST_REG_HAS_NAME(n, master_ev_enable), "registers for interrupts missing"); \ + \ + static void i2c_litex_irq_config##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), i2c_litex_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + }; + +#define I2C_LITEC_IRQ_DATA(n) \ + .sem_rx_ready = Z_SEM_INITIALIZER(i2c_litex_litei2c_data_##n.sem_rx_ready, 0, 1), + +#define I2C_LITEC_IRQ_CONFIG(n) \ + IF_DISABLED(I2C_LITEX_ALL_HAS_IRQ, (.has_irq = DT_INST_IRQ_HAS_IDX(n, 0),)) \ + .master_ev_pending_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_pending, 0), \ + .master_ev_enable_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_enable, 0), \ + .irq_config_func = COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), \ + (i2c_litex_irq_config##n), (NULL)), + #define I2C_LITEX_INIT(n) \ - static struct i2c_litex_litei2c_data i2c_litex_litei2c_data_##n; \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), (I2C_LITEX_IRQ(n))) \ + \ + static struct i2c_litex_litei2c_data i2c_litex_litei2c_data_##n = { \ + .mutex = Z_MUTEX_INITIALIZER(i2c_litex_litei2c_data_##n.mutex), \ + IF_ENABLED(I2C_LITEX_ANY_HAS_IRQ, (I2C_LITEC_IRQ_DATA(n))) \ + }; \ \ static const struct i2c_litex_litei2c_config i2c_litex_litei2c_config_##n = { \ .phy_speed_mode_addr = DT_INST_REG_ADDR_BY_NAME(n, phy_speed_mode), \ @@ -343,6 +445,7 @@ static DEVICE_API(i2c, i2c_litex_litei2c_driver_api) = { .master_rxtx_addr = DT_INST_REG_ADDR_BY_NAME(n, master_rxtx), \ .master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, master_status), \ .bitrate = DT_INST_PROP(n, clock_frequency), \ + IF_ENABLED(I2C_LITEX_ANY_HAS_IRQ, (I2C_LITEC_IRQ_CONFIG(n))) \ }; \ \ I2C_DEVICE_DT_INST_DEFINE(n, i2c_litex_init, NULL, &i2c_litex_litei2c_data_##n, \ From 385a063b095faab17672c305a4f0bbd9301691b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 8 Jul 2025 10:45:29 +0200 Subject: [PATCH 2/5] drivers: i2c: litex: remove warnings from CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit remove warnings from CI. Signed-off-by: Fin Maaß --- drivers/i2c/i2c_litex_litei2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/i2c_litex_litei2c.c b/drivers/i2c/i2c_litex_litei2c.c index de9337dc90f7..f32aee5ca12a 100644 --- a/drivers/i2c/i2c_litex_litei2c.c +++ b/drivers/i2c/i2c_litex_litei2c.c @@ -340,13 +340,13 @@ static int i2c_litex_recover_bus(const struct device *dev) i2c_litex_write_settings(dev, 0, 0, true); while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_TX_READY_OFFSET))) { - ; + /* Wait for TX ready */ } litex_write32(0, config->master_rxtx_addr); while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_RX_READY_OFFSET))) { - ; + /* Wait for RX data */ } (void)litex_read32(config->master_rxtx_addr); From 3f02c4f791fff315cd4c4872480fecfdcaf90c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Tue, 1 Jul 2025 14:24:42 +0200 Subject: [PATCH 3/5] drivers: spi: litex: increase SPI_MAX_CS_SIZE to 32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LiteSPI now has support for multiple CS. Signed-off-by: Fin Maaß --- drivers/spi/spi_litex_litespi.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spi_litex_litespi.c b/drivers/spi/spi_litex_litespi.c index 706426437723..916b53cbbd94 100644 --- a/drivers/spi/spi_litex_litespi.c +++ b/drivers/spi/spi_litex_litespi.c @@ -21,7 +21,7 @@ LOG_MODULE_REGISTER(spi_litex_litespi); #define SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET 0x1 #define SPI_MAX_WORD_SIZE 32 -#define SPI_MAX_CS_SIZE 4 +#define SPI_MAX_CS_SIZE 32 struct spi_litex_dev_config { uint32_t core_master_cs_addr; @@ -60,11 +60,9 @@ static int spi_config(const struct device *dev, const struct spi_config *config) { struct spi_litex_data *dev_data = dev->data; - if (config->slave != 0) { - if (config->slave >= SPI_MAX_CS_SIZE) { - LOG_ERR("More slaves than supported"); - return -ENOTSUP; - } + if (config->slave >= SPI_MAX_CS_SIZE) { + LOG_ERR("More slaves than supported"); + return -ENOTSUP; } if (config->operation & SPI_HALF_DUPLEX) { From 5ccbea8dc17bfa7dba480e5e587ce641c16b61a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Fri, 27 Jun 2025 14:00:41 +0200 Subject: [PATCH 4/5] drivers: spi: litex: remove `core_` prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit remove `core_` prefix from code and register names, got dropped in litex in https://github.com/enjoy-digital/litex/pull/2253 Signed-off-by: Fin Maaß --- drivers/spi/spi_litex_litespi.c | 64 +++++++++++++-------------- dts/riscv/riscv32-litex-vexriscv.dtsi | 10 ++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/drivers/spi/spi_litex_litespi.c b/drivers/spi/spi_litex_litespi.c index 916b53cbbd94..0bdf76ff8738 100644 --- a/drivers/spi/spi_litex_litespi.c +++ b/drivers/spi/spi_litex_litespi.c @@ -13,22 +13,22 @@ LOG_MODULE_REGISTER(spi_litex_litespi); #include #include "spi_litex_common.h" -#define SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET 0x0 -#define SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET 0x1 -#define SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET 0x2 +#define SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET 0x0 +#define SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET 0x1 +#define SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET 0x2 -#define SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET 0x0 -#define SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET 0x1 +#define SPIFLASH_MASTER_STATUS_TX_READY_OFFSET 0x0 +#define SPIFLASH_MASTER_STATUS_RX_READY_OFFSET 0x1 #define SPI_MAX_WORD_SIZE 32 #define SPI_MAX_CS_SIZE 32 struct spi_litex_dev_config { - uint32_t core_master_cs_addr; - uint32_t core_master_phyconfig_addr; - uint32_t core_master_rxtx_addr; - uint32_t core_master_rxtx_size; - uint32_t core_master_status_addr; + uint32_t master_cs_addr; + uint32_t master_phyconfig_addr; + uint32_t master_rxtx_addr; + uint32_t master_rxtx_size; + uint32_t master_status_addr; uint32_t phy_clk_divisor_addr; bool phy_clk_divisor_exists; }; @@ -123,12 +123,12 @@ static void spiflash_len_mask_width_write(uint32_t len, uint32_t width, uint32_t uint32_t addr) { uint32_t tmp = len & BIT_MASK(8); - uint32_t word = tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET * 8); + uint32_t word = tmp << (SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET * 8); tmp = width & BIT_MASK(8); - word |= tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET * 8); + word |= tmp << (SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET * 8); tmp = mask & BIT_MASK(8); - word |= tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET * 8); + word |= tmp << (SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET * 8); litex_write32(word, addr); } @@ -145,22 +145,22 @@ static int spi_litex_xfer(const struct device *dev, const struct spi_config *con uint8_t width = BIT(0); /* SPI Xfer width*/ uint8_t mask = BIT(0); /* SPI Xfer mask*/ - spiflash_len_mask_width_write(len * 8, width, mask, dev_config->core_master_phyconfig_addr); + spiflash_len_mask_width_write(len * 8, width, mask, dev_config->master_phyconfig_addr); - litex_write32(BIT(config->slave), dev_config->core_master_cs_addr); + litex_write32(BIT(config->slave), dev_config->master_cs_addr); /* Flush RX buffer */ - while ((litex_read8(dev_config->core_master_status_addr) & - BIT(SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET))) { - rxd = litex_read32(dev_config->core_master_rxtx_addr); + while ((litex_read8(dev_config->master_status_addr) & + BIT(SPIFLASH_MASTER_STATUS_RX_READY_OFFSET))) { + rxd = litex_read32(dev_config->master_rxtx_addr); LOG_DBG("flushed rxd: 0x%x", rxd); } do { - len = MIN(spi_context_max_continuous_chunk(ctx), dev_config->core_master_rxtx_size); + len = MIN(spi_context_max_continuous_chunk(ctx), dev_config->master_rxtx_size); if (len != old_len) { spiflash_len_mask_width_write(len * 8, width, mask, - dev_config->core_master_phyconfig_addr); + dev_config->master_phyconfig_addr); old_len = len; } @@ -170,22 +170,22 @@ static int spi_litex_xfer(const struct device *dev, const struct spi_config *con txd = 0U; } - while (!(litex_read8(dev_config->core_master_status_addr) & - BIT(SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET))) { + while (!(litex_read8(dev_config->master_status_addr) & + BIT(SPIFLASH_MASTER_STATUS_TX_READY_OFFSET))) { ; } LOG_DBG("txd: 0x%x", txd); - litex_write32(txd, dev_config->core_master_rxtx_addr); + litex_write32(txd, dev_config->master_rxtx_addr); spi_context_update_tx(ctx, data->dfs, len / data->dfs); - while (!(litex_read8(dev_config->core_master_status_addr) & - BIT(SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET))) { + while (!(litex_read8(dev_config->master_status_addr) & + BIT(SPIFLASH_MASTER_STATUS_RX_READY_OFFSET))) { ; } - rxd = litex_read32(dev_config->core_master_rxtx_addr); + rxd = litex_read32(dev_config->master_rxtx_addr); LOG_DBG("rxd: 0x%x", rxd); if (spi_context_rx_buf_on(ctx)) { @@ -196,7 +196,7 @@ static int spi_litex_xfer(const struct device *dev, const struct spi_config *con } while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx)); - litex_write32(0, dev_config->core_master_cs_addr); + litex_write32(0, dev_config->master_cs_addr); spi_context_complete(ctx, dev, 0); @@ -260,11 +260,11 @@ static DEVICE_API(spi, spi_litex_api) = { SPI_CONTEXT_INIT_SYNC(spi_litex_data_##n, ctx), \ }; \ static struct spi_litex_dev_config spi_litex_cfg_##n = { \ - .core_master_cs_addr = DT_INST_REG_ADDR_BY_NAME(n, core_master_cs), \ - .core_master_phyconfig_addr = DT_INST_REG_ADDR_BY_NAME(n, core_master_phyconfig), \ - .core_master_rxtx_addr = DT_INST_REG_ADDR_BY_NAME(n, core_master_rxtx), \ - .core_master_rxtx_size = DT_INST_REG_SIZE_BY_NAME(n, core_master_rxtx), \ - .core_master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, core_master_status), \ + .master_cs_addr = DT_INST_REG_ADDR_BY_NAME(n, master_cs), \ + .master_phyconfig_addr = DT_INST_REG_ADDR_BY_NAME(n, master_phyconfig), \ + .master_rxtx_addr = DT_INST_REG_ADDR_BY_NAME(n, master_rxtx), \ + .master_rxtx_size = DT_INST_REG_SIZE_BY_NAME(n, master_rxtx), \ + .master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, master_status), \ .phy_clk_divisor_exists = DT_INST_REG_HAS_NAME(n, phy_clk_divisor), \ .phy_clk_divisor_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, phy_clk_divisor, 0) \ \ diff --git a/dts/riscv/riscv32-litex-vexriscv.dtsi b/dts/riscv/riscv32-litex-vexriscv.dtsi index 6d144b69abaf..341e4f5af6c3 100644 --- a/dts/riscv/riscv32-litex-vexriscv.dtsi +++ b/dts/riscv/riscv32-litex-vexriscv.dtsi @@ -102,11 +102,11 @@ <0xe000c010 0x4>, <0xe000c800 0x4>, <0x60000000 0x1000000>; - reg-names = "core_mmap_dummy_bits", - "core_master_cs", - "core_master_phyconfig", - "core_master_rxtx", - "core_master_status", + reg-names = "mmap_dummy_bits", + "master_cs", + "master_phyconfig", + "master_rxtx", + "master_status", "phy_clk_divisor", "flash_mmap"; #address-cells = <1>; From e027a830cb12175ce59d8850f72c7b7add74074c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Wed, 4 Jun 2025 13:08:42 +0200 Subject: [PATCH 5/5] drivers: spi: litex: litespi: support interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit support interrupts for rx_ready, so we can use the time we are waiting for other stuff. implement async spi transfers. Signed-off-by: Fin Maaß --- drivers/spi/spi_litex_litespi.c | 282 ++++++++++++++++++++++++-------- 1 file changed, 217 insertions(+), 65 deletions(-) diff --git a/drivers/spi/spi_litex_litespi.c b/drivers/spi/spi_litex_litespi.c index 0bdf76ff8738..34608a948ef0 100644 --- a/drivers/spi/spi_litex_litespi.c +++ b/drivers/spi/spi_litex_litespi.c @@ -13,6 +13,11 @@ LOG_MODULE_REGISTER(spi_litex_litespi); #include #include "spi_litex_common.h" +#define SPI_LITEX_ANY_HAS_IRQ DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts) +#define SPI_LITEX_ALL_HAS_IRQ DT_ALL_INST_HAS_PROP_STATUS_OKAY(interrupts) + +#define SPI_LITEX_HAS_IRQ UTIL_OR(SPI_LITEX_ALL_HAS_IRQ, dev_config->has_irq) + #define SPIFLASH_MASTER_PHYCONFIG_LEN_OFFSET 0x0 #define SPIFLASH_MASTER_PHYCONFIG_WIDTH_OFFSET 0x1 #define SPIFLASH_MASTER_PHYCONFIG_MASK_OFFSET 0x2 @@ -23,6 +28,9 @@ LOG_MODULE_REGISTER(spi_litex_litespi); #define SPI_MAX_WORD_SIZE 32 #define SPI_MAX_CS_SIZE 32 +#define SPI_LITEX_WIDTH BIT(0) +#define SPI_LITEX_MASK BIT(0) + struct spi_litex_dev_config { uint32_t master_cs_addr; uint32_t master_phyconfig_addr; @@ -31,14 +39,22 @@ struct spi_litex_dev_config { uint32_t master_status_addr; uint32_t phy_clk_divisor_addr; bool phy_clk_divisor_exists; +#if SPI_LITEX_ANY_HAS_IRQ +#if !SPI_LITEX_ALL_HAS_IRQ + bool has_irq; +#endif /* !SPI_LITEX_ALL_HAS_IRQ */ + void (*irq_config_func)(const struct device *dev); + uint32_t master_ev_pending_addr; + uint32_t master_ev_enable_addr; +#endif /* SPI_LITEX_ANY_HAS_IRQ */ }; struct spi_litex_data { struct spi_context ctx; uint8_t dfs; /* dfs in bytes: 1,2 or 4 */ + uint8_t len; /* length of the last transfer in bytes */ }; - static int spi_litex_set_frequency(const struct device *dev, const struct spi_config *config) { const struct spi_litex_dev_config *dev_config = dev->config; @@ -60,6 +76,11 @@ static int spi_config(const struct device *dev, const struct spi_config *config) { struct spi_litex_data *dev_data = dev->data; + if (spi_context_configured(&dev_data->ctx, config)) { + /* Context is already configured */ + return 0; + } + if (config->slave >= SPI_MAX_CS_SIZE) { LOG_ERR("More slaves than supported"); return -ENOTSUP; @@ -81,11 +102,6 @@ static int spi_config(const struct device *dev, const struct spi_config *config) return -ENOTSUP; } - if (config->operation & SPI_LOCK_ON) { - LOG_ERR("Lock On not supported"); - return -ENOTSUP; - } - if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) && (config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) { LOG_ERR("Only supports single mode"); @@ -116,6 +132,8 @@ static int spi_config(const struct device *dev, const struct spi_config *config) spi_litex_set_frequency(dev, config); + dev_data->ctx.config = config; + return 0; } @@ -132,20 +150,54 @@ static void spiflash_len_mask_width_write(uint32_t len, uint32_t width, uint32_t litex_write32(word, addr); } -static int spi_litex_xfer(const struct device *dev, const struct spi_config *config) +static void spi_litex_spi_do_tx(const struct device *dev) +{ + const struct spi_litex_dev_config *dev_config = dev->config; + struct spi_litex_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint8_t len; + uint32_t txd = 0U; + + len = MIN(spi_context_max_continuous_chunk(ctx), dev_config->master_rxtx_size); + if (len != data->len) { + spiflash_len_mask_width_write(len * 8, SPI_LITEX_WIDTH, SPI_LITEX_MASK, + dev_config->master_phyconfig_addr); + data->len = len; + } + + if (spi_context_tx_buf_on(ctx)) { + litex_spi_tx_put(len, &txd, ctx->tx_buf); + } + + LOG_DBG("txd: 0x%x", txd); + litex_write32(txd, dev_config->master_rxtx_addr); + + spi_context_update_tx(ctx, data->dfs, len / data->dfs); +} + +static void spi_litex_spi_do_rx(const struct device *dev) { const struct spi_litex_dev_config *dev_config = dev->config; struct spi_litex_data *data = dev->data; struct spi_context *ctx = &data->ctx; - uint32_t txd, rxd; - int ret = 0; + uint32_t rxd; + + rxd = litex_read32(dev_config->master_rxtx_addr); + LOG_DBG("rxd: 0x%x", rxd); + + if (spi_context_rx_buf_on(ctx)) { + litex_spi_rx_put(data->len, &rxd, ctx->rx_buf); + } - uint8_t len = data->dfs; /* SPI Xfer length*/ - uint8_t old_len = len; /* old SPI Xfer length*/ - uint8_t width = BIT(0); /* SPI Xfer width*/ - uint8_t mask = BIT(0); /* SPI Xfer mask*/ + spi_context_update_rx(ctx, data->dfs, data->len / data->dfs); +} - spiflash_len_mask_width_write(len * 8, width, mask, dev_config->master_phyconfig_addr); +static int spi_litex_xfer(const struct device *dev, const struct spi_config *config) +{ + const struct spi_litex_dev_config *dev_config = dev->config; + struct spi_litex_data *data = dev->data; + struct spi_context *ctx = &data->ctx; + uint32_t rxd; litex_write32(BIT(config->slave), dev_config->master_cs_addr); @@ -156,88 +208,163 @@ static int spi_litex_xfer(const struct device *dev, const struct spi_config *con LOG_DBG("flushed rxd: 0x%x", rxd); } - do { - len = MIN(spi_context_max_continuous_chunk(ctx), dev_config->master_rxtx_size); - if (len != old_len) { - spiflash_len_mask_width_write(len * 8, width, mask, - dev_config->master_phyconfig_addr); - old_len = len; - } + while (!(litex_read8(dev_config->master_status_addr) & + BIT(SPIFLASH_MASTER_STATUS_TX_READY_OFFSET))) { + (void)litex_read32(dev_config->master_rxtx_addr); + } - if (spi_context_tx_buf_on(ctx)) { - litex_spi_tx_put(len, &txd, ctx->tx_buf); - } else { - txd = 0U; - } +#if SPI_LITEX_ANY_HAS_IRQ + if (SPI_LITEX_HAS_IRQ) { + litex_write8(BIT(0), dev_config->master_ev_enable_addr); + litex_write8(BIT(0), dev_config->master_ev_pending_addr); - while (!(litex_read8(dev_config->master_status_addr) & - BIT(SPIFLASH_MASTER_STATUS_TX_READY_OFFSET))) { - ; - } + spi_litex_spi_do_tx(dev); - LOG_DBG("txd: 0x%x", txd); - litex_write32(txd, dev_config->master_rxtx_addr); + return spi_context_wait_for_completion(ctx); + } +#endif /* SPI_LITEX_ANY_HAS_IRQ */ - spi_context_update_tx(ctx, data->dfs, len / data->dfs); + do { + spi_litex_spi_do_tx(dev); while (!(litex_read8(dev_config->master_status_addr) & BIT(SPIFLASH_MASTER_STATUS_RX_READY_OFFSET))) { - ; + /* Wait for RX data */ } - rxd = litex_read32(dev_config->master_rxtx_addr); - LOG_DBG("rxd: 0x%x", rxd); - - if (spi_context_rx_buf_on(ctx)) { - litex_spi_rx_put(len, &rxd, ctx->rx_buf); - } - - spi_context_update_rx(ctx, data->dfs, len / data->dfs); - + spi_litex_spi_do_rx(dev); } while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx)); - litex_write32(0, dev_config->master_cs_addr); + if ((config->operation & SPI_HOLD_ON_CS) == 0) { + litex_write32(0, dev_config->master_cs_addr); + } spi_context_complete(ctx, dev, 0); - return ret; + return 0; } -static int spi_litex_transceive(const struct device *dev, const struct spi_config *config, - const struct spi_buf_set *tx_bufs, - const struct spi_buf_set *rx_bufs) +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) { struct spi_litex_data *data = dev->data; - - int ret = spi_config(dev, config); - - if (ret) { - return ret; - } + int ret; if (!tx_bufs && !rx_bufs) { return 0; } + spi_context_lock(&data->ctx, asynchronous, cb, userdata, config); + + ret = spi_config(dev, config); + if (ret < 0) { + goto end; + } + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, data->dfs); ret = spi_litex_xfer(dev, config); +end: + spi_context_release(&data->ctx, ret); + return ret; } +static int spi_litex_transceive(const struct device *dev, + const struct spi_config *config, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs) +{ + return transceive(dev, config, tx_bufs, rx_bufs, false, NULL, NULL); +} + #ifdef CONFIG_SPI_ASYNC -static int spi_litex_transceive_async(const struct device *dev, const struct spi_config *config, +static int spi_litex_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, - struct k_poll_signal *async) + spi_callback_t cb, + void *userdata) { +#if SPI_LITEX_ANY_HAS_IRQ +#if !SPI_LITEX_ALL_HAS_IRQ + const struct spi_litex_dev_config *dev_config = dev->config; +#endif /* !SPI_LITEX_ALL_HAS_IRQ */ + + if (SPI_LITEX_HAS_IRQ) { + return transceive(dev, config, tx_bufs, rx_bufs, true, cb, userdata); + } +#endif /* SPI_LITEX_ANY_HAS_IRQ */ return -ENOTSUP; } #endif /* CONFIG_SPI_ASYNC */ static int spi_litex_release(const struct device *dev, const struct spi_config *config) { + struct spi_litex_data *data = dev->data; + const struct spi_litex_dev_config *dev_config = dev->config; + + if (!spi_context_configured(&data->ctx, config)) { + return -EINVAL; + } + + litex_write32(0, dev_config->master_cs_addr); + + spi_context_unlock_unconditionally(&data->ctx); + return 0; +} + +#if SPI_LITEX_ANY_HAS_IRQ +static void spi_litex_irq_handler(const struct device *dev) +{ + struct spi_litex_data *data = dev->data; + const struct spi_litex_dev_config *dev_config = dev->config; + struct spi_context *ctx = &data->ctx; + + if (litex_read8(dev_config->master_ev_pending_addr) & BIT(0)) { + spi_litex_spi_do_rx(dev); + + /* ack reader irq */ + litex_write8(BIT(0), dev_config->master_ev_pending_addr); + + if (spi_context_tx_on(ctx) || spi_context_rx_on(ctx)) { + spi_litex_spi_do_tx(dev); + } else { + litex_write8(0, dev_config->master_ev_enable_addr); + + if ((ctx->config->operation & SPI_HOLD_ON_CS) == 0) { + litex_write32(0, dev_config->master_cs_addr); + } + + spi_context_complete(ctx, dev, 0); + } + } +} +#endif /* SPI_LITEX_ANY_HAS_IRQ */ + +static int spi_litex_init(const struct device *dev) +{ + const struct spi_litex_dev_config *dev_config = dev->config; + struct spi_litex_data *data = dev->data; + +#if SPI_LITEX_ANY_HAS_IRQ + if (SPI_LITEX_HAS_IRQ) { + dev_config->irq_config_func(dev); + } +#endif /* SPI_LITEX_ANY_HAS_IRQ */ + + data->len = dev_config->master_rxtx_size; + + spiflash_len_mask_width_write(data->len * 8, SPI_LITEX_WIDTH, SPI_LITEX_MASK, + dev_config->master_phyconfig_addr); + + spi_context_unlock_unconditionally(&data->ctx); return 0; } @@ -254,11 +381,34 @@ static DEVICE_API(spi, spi_litex_api) = { .release = spi_litex_release, }; -#define SPI_INIT(n) \ - static struct spi_litex_data spi_litex_data_##n = { \ - SPI_CONTEXT_INIT_LOCK(spi_litex_data_##n, ctx), \ - SPI_CONTEXT_INIT_SYNC(spi_litex_data_##n, ctx), \ - }; \ +#define SPI_LITEX_IRQ(n) \ + BUILD_ASSERT(DT_INST_REG_HAS_NAME(n, master_ev_pending) && \ + DT_INST_REG_HAS_NAME(n, master_ev_enable), \ + "registers for interrupts missing"); \ + \ + static void spi_litex_irq_config##n(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), spi_litex_irq_handler, \ + DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQN(n)); \ + }; + +#define SPI_LITEX_IRQ_CONFIG(n) \ + .irq_config_func = COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), \ + (spi_litex_irq_config##n), (NULL)), \ + .master_ev_pending_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_pending, 0), \ + .master_ev_enable_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_enable, 0), \ + IF_DISABLED(SPI_LITEX_ALL_HAS_IRQ, (.has_irq = DT_INST_IRQ_HAS_IDX(n, 0),)) + +#define SPI_INIT(n) \ + IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), (SPI_LITEX_IRQ(n))) \ + \ + static struct spi_litex_data spi_litex_data_##n = { \ + SPI_CONTEXT_INIT_LOCK(spi_litex_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(spi_litex_data_##n, ctx), \ + }; \ + \ static struct spi_litex_dev_config spi_litex_cfg_##n = { \ .master_cs_addr = DT_INST_REG_ADDR_BY_NAME(n, master_cs), \ .master_phyconfig_addr = DT_INST_REG_ADDR_BY_NAME(n, master_phyconfig), \ @@ -266,10 +416,12 @@ static DEVICE_API(spi, spi_litex_api) = { .master_rxtx_size = DT_INST_REG_SIZE_BY_NAME(n, master_rxtx), \ .master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, master_status), \ .phy_clk_divisor_exists = DT_INST_REG_HAS_NAME(n, phy_clk_divisor), \ - .phy_clk_divisor_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, phy_clk_divisor, 0) \ - \ + .phy_clk_divisor_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, phy_clk_divisor, 0), \ + IF_ENABLED(SPI_LITEX_ANY_HAS_IRQ, (SPI_LITEX_IRQ_CONFIG(n))) \ }; \ - SPI_DEVICE_DT_INST_DEFINE(n, NULL, NULL, &spi_litex_data_##n, &spi_litex_cfg_##n, \ - POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, &spi_litex_api); + \ + SPI_DEVICE_DT_INST_DEFINE(n, spi_litex_init, NULL, &spi_litex_data_##n, \ + &spi_litex_cfg_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &spi_litex_api); DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)