diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index c577c8c862be5..d199e9850eae3 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -3889,7 +3889,6 @@ LiteX Platforms: files: - boards/enjoydigital/litex_vexriscv/ - drivers/*/*litex* - - drivers/spi/spi_litespi* - drivers/*/Kconfig.litex - dts/bindings/*/litex* - dts/riscv/riscv32-litex-vexriscv.dtsi diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 07fb6a2455be6..c625f9cb99cf4 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -24,7 +24,8 @@ zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPI spi_nrfx_spi.c zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM spi_nrfx_spim.c spi_nrfx_common.c) zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIS spi_nrfx_spis.c) -zephyr_library_sources_ifdef(CONFIG_SPI_LITESPI spi_litespi.c) +zephyr_library_sources_ifdef(CONFIG_SPI_LITEX spi_litex.c) +zephyr_library_sources_ifdef(CONFIG_SPI_LITEX_LITESPI spi_litex_litespi.c) zephyr_library_sources_ifdef(CONFIG_SPI_OC_SIMPLE spi_oc_simple.c) zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI spi_xec_qmspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_GECKO spi_gecko.c) diff --git a/drivers/spi/Kconfig.litex b/drivers/spi/Kconfig.litex index 1fe181b4097f9..54798d1fe2185 100644 --- a/drivers/spi/Kconfig.litex +++ b/drivers/spi/Kconfig.litex @@ -3,9 +3,16 @@ # Copyright (c) 2019 Antmicro # SPDX-License-Identifier: Apache-2.0 -config SPI_LITESPI +config SPI_LITEX bool "LiteX SPI controller driver" default y depends on DT_HAS_LITEX_SPI_ENABLED help Enable the SPI peripherals on LiteX + +config SPI_LITEX_LITESPI + bool "LiteX SPI LiteSPI controller driver" + default y + depends on DT_HAS_LITEX_SPI_LITESPI_ENABLED + help + Enable the SPI peripherals on LiteX diff --git a/drivers/spi/spi_litespi.c b/drivers/spi/spi_litespi.c deleted file mode 100644 index 3d36c90582940..0000000000000 --- a/drivers/spi/spi_litespi.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (c) 2019 Antmicro - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT litex_spi - -#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL -#include -LOG_MODULE_REGISTER(spi_litespi); -#include "spi_litespi.h" -#include -#include - -/* Helper Functions */ -static int spi_config(const struct spi_config *config, uint16_t *control) -{ - uint8_t cs = 0x00; - - if (config->slave != 0) { - if (config->slave >= SPI_MAX_CS_SIZE) { - LOG_ERR("More slaves than supported"); - return -ENOTSUP; - } - cs = (uint8_t)(config->slave); - } - - if (config->operation & SPI_HALF_DUPLEX) { - LOG_ERR("Half-duplex not supported"); - return -ENOTSUP; - } - - if (SPI_WORD_SIZE_GET(config->operation) != 8) { - LOG_ERR("Word size must be %d", SPI_WORD_SIZE); - return -ENOTSUP; - } - - if (config->operation & SPI_CS_ACTIVE_HIGH) { - LOG_ERR("CS active high not supported"); - 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"); - return -ENOTSUP; - } - - if (config->operation & SPI_TRANSFER_LSB) { - LOG_ERR("LSB first not supported"); - return -ENOTSUP; - } - - if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) { - LOG_ERR("Only supports CPOL=CPHA=0"); - return -ENOTSUP; - } - - if (config->operation & SPI_OP_MODE_SLAVE) { - LOG_ERR("Slave mode not supported"); - return -ENOTSUP; - } - - /* Set Loopback */ - if (config->operation & SPI_MODE_LOOP) { - litex_write8(SPI_ENABLE, SPI_LOOPBACK_ADDR); - } - /* Set word size */ - *control = (uint16_t) (SPI_WORD_SIZE_GET(config->operation) - << POSITION_WORD_SIZE); - /* Write configurations */ - litex_write8(cs, SPI_CS_ADDR); - litex_write16(*control, SPI_CONTROL_ADDR); - - return 0; -} - -static void spi_litespi_send(const struct device *dev, uint8_t frame, - uint16_t control) -{ - /* Write frame to register */ - litex_write8(frame, SPI_MOSI_DATA_ADDR); - /* Start the transfer */ - litex_write16(control | SPI_ENABLE, SPI_CONTROL_ADDR); - /* Wait until the transfer ends */ - while (!(litex_read8(SPI_STATUS_ADDR))) - ; -} - -static uint8_t spi_litespi_recv(void) -{ - /* Return data inside MISO register */ - return litex_read8(SPI_MISO_DATA_ADDR); -} - -static void spi_litespi_xfer(const struct device *dev, - const struct spi_config *config, - uint16_t control) -{ - struct spi_context *ctx = &SPI_DATA(dev)->ctx; - uint32_t send_len = spi_context_longest_current_buf(ctx); - uint8_t read_data; - - for (uint32_t i = 0; i < send_len; i++) { - /* Send a frame */ - if (i < ctx->tx_len) { - spi_litespi_send(dev, (uint8_t) (ctx->tx_buf)[i], - control); - } else { - /* Send dummy bytes */ - spi_litespi_send(dev, 0, control); - } - /* Receive a frame */ - read_data = spi_litespi_recv(); - if (i < ctx->rx_len) { - ctx->rx_buf[i] = read_data; - } - } - spi_context_complete(ctx, dev, 0); -} - -/* API Functions */ - -static int spi_litespi_transceive(const struct device *dev, - const struct spi_config *config, - const struct spi_buf_set *tx_bufs, - const struct spi_buf_set *rx_bufs) -{ - uint16_t control = 0; - - spi_config(config, &control); - spi_context_buffers_setup(&SPI_DATA(dev)->ctx, tx_bufs, rx_bufs, 1); - spi_litespi_xfer(dev, config, control); - return 0; -} - -#ifdef CONFIG_SPI_ASYNC -static int spi_litespi_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) -{ - return -ENOTSUP; -} -#endif /* CONFIG_SPI_ASYNC */ - -static int spi_litespi_release(const struct device *dev, - const struct spi_config *config) -{ - if (!(litex_read8(SPI_STATUS_ADDR))) { - return -EBUSY; - } - return 0; -} - -/* Device Instantiation */ -static const struct spi_driver_api spi_litespi_api = { - .transceive = spi_litespi_transceive, -#ifdef CONFIG_SPI_ASYNC - .transceive_async = spi_litespi_transceive_async, -#endif /* CONFIG_SPI_ASYNC */ - .release = spi_litespi_release, -}; - -#define SPI_INIT(n) \ - static struct spi_litespi_data spi_litespi_data_##n = { \ - SPI_CONTEXT_INIT_LOCK(spi_litespi_data_##n, ctx), \ - SPI_CONTEXT_INIT_SYNC(spi_litespi_data_##n, ctx), \ - }; \ - static struct spi_litespi_cfg spi_litespi_cfg_##n = { \ - .base = DT_INST_REG_ADDR_BY_NAME(n, control), \ - }; \ - DEVICE_DT_INST_DEFINE(n, \ - NULL, \ - NULL, \ - &spi_litespi_data_##n, \ - &spi_litespi_cfg_##n, \ - POST_KERNEL, \ - CONFIG_SPI_INIT_PRIORITY, \ - &spi_litespi_api); - -DT_INST_FOREACH_STATUS_OKAY(SPI_INIT) diff --git a/drivers/spi/spi_litespi.h b/drivers/spi/spi_litespi.h deleted file mode 100644 index 9f6bb230c464c..0000000000000 --- a/drivers/spi/spi_litespi.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2019 Antmicro - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef _SPI_LITESPI__H -#define _SPI_LITESPI__H - -#include "spi_context.h" - -#include -#include -#include - -#define SPI_BASE_ADDR DT_INST_REG_ADDR(0) -#define SPI_CONTROL_ADDR DT_INST_REG_ADDR_BY_NAME(0, control) -#define SPI_STATUS_ADDR DT_INST_REG_ADDR_BY_NAME(0, status) -#define SPI_MOSI_DATA_ADDR DT_INST_REG_ADDR_BY_NAME(0, mosi) -#define SPI_MISO_DATA_ADDR DT_INST_REG_ADDR_BY_NAME(0, miso) -#define SPI_CS_ADDR DT_INST_REG_ADDR_BY_NAME(0, cs) -#define SPI_LOOPBACK_ADDR DT_INST_REG_ADDR_BY_NAME(0, loopback) - -#define POSITION_WORD_SIZE 8 -#define SPI_MAX_CS_SIZE 0x100 -#define SPI_WORD_SIZE 8 - -#define SPI_ENABLE 0x1 - -#define SPI_DATA(dev) ((struct spi_litespi_data *) ((dev)->data)) - -/* Structure Declarations */ - -struct spi_litespi_data { - struct spi_context ctx; -}; - -struct spi_litespi_cfg { - uint32_t base; - uint32_t f_sys; -}; - -#endif /* _SPI_LITESPI__H */ diff --git a/drivers/spi/spi_litex.c b/drivers/spi/spi_litex.c new file mode 100644 index 0000000000000..0f257f085035a --- /dev/null +++ b/drivers/spi/spi_litex.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2019 Antmicro + * Copyright (c) 2024 Vogl Electronic GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT litex_spi + +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +LOG_MODULE_REGISTER(spi_litex); + +#include "spi_litex_common.h" + +#define POSITION_WORD_SIZE 8 + +struct spi_litex_data { + struct spi_context ctx; + uint8_t dfs; /* dfs in bytes: 1,2,3 or 4 */ +}; + +struct spi_litex_cfg { + uint32_t control_addr; + uint32_t status_addr; + uint32_t mosi_addr; + uint32_t miso_addr; + uint32_t cs_addr; + uint32_t loopback_addr; + uint32_t clk_divider_addr; + bool clk_divider_exists; + int data_width; + int max_cs; +}; + +static void spi_set_frequency(const struct device *dev, const struct spi_config *config) +{ + const struct spi_litex_cfg *dev_config = dev->config; + + if (!dev_config->clk_divider_exists) { + /* The clk_divider is optional, thats why we check. */ + LOG_WRN("No clk_divider found, can't change frequency"); + return; + } + + uint16_t divisor = DIV_ROUND_UP(sys_clock_hw_cycles_per_sec(), config->frequency); + + litex_write16(divisor, dev_config->clk_divider_addr); +} + +/* Helper Functions */ +static int spi_config(const struct device *dev, const struct spi_config *config, uint16_t *control) +{ + const struct spi_litex_cfg *dev_config = dev->config; + struct spi_litex_data *dev_data = dev->data; + + if (config->slave >= dev_config->max_cs) { + LOG_ERR("More slaves than supported"); + return -ENOTSUP; + } + + if (config->operation & SPI_HALF_DUPLEX) { + LOG_ERR("Half-duplex not supported"); + return -ENOTSUP; + } + + if (SPI_WORD_SIZE_GET(config->operation) > dev_config->data_width) { + LOG_ERR("Word size must be <= %d", dev_config->data_width); + return -ENOTSUP; + } + + if (config->operation & SPI_CS_ACTIVE_HIGH) { + LOG_ERR("CS active high not supported"); + 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"); + return -ENOTSUP; + } + + if (config->operation & SPI_TRANSFER_LSB) { + LOG_ERR("LSB first not supported"); + return -ENOTSUP; + } + + if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) { + LOG_ERR("Only supports CPOL=CPHA=0"); + return -ENOTSUP; + } + + if (config->operation & SPI_OP_MODE_SLAVE) { + LOG_ERR("Slave mode not supported"); + return -ENOTSUP; + } + + /* Set Loopback */ + if (!litex_read8(dev_config->loopback_addr) != !(config->operation & SPI_MODE_LOOP)) { + litex_write8(((config->operation & SPI_MODE_LOOP) ? 0x1 : 0x0), + dev_config->loopback_addr); + } + /* Set word size */ + *control = (uint16_t) (SPI_WORD_SIZE_GET(config->operation) + << POSITION_WORD_SIZE); + + dev_data->dfs = get_dfs_value(config); + + /* Write configurations */ + litex_write16(*control, dev_config->control_addr); + + spi_set_frequency(dev, config); + + return 0; +} + +static void spi_litex_send(const struct device *dev, uint32_t frame, + uint16_t control) +{ + const struct spi_litex_cfg *dev_config = dev->config; + /* Write frame to register */ + litex_write32(frame, dev_config->mosi_addr); + /* Start the transfer */ + litex_write16(control | BIT(0), dev_config->control_addr); + /* Wait until the transfer ends */ + while (!(litex_read8(dev_config->status_addr) & BIT(0))) { + ;/* Wait */ + } +} + +static uint32_t spi_litex_recv(const struct device *dev) +{ + const struct spi_litex_cfg *dev_config = dev->config; + + /* Return data inside MISO register */ + return litex_read32(dev_config->miso_addr); +} + +static void spi_litex_xfer(const struct device *dev, + const struct spi_config *config, + uint16_t control) +{ + const struct spi_litex_cfg *dev_config = dev->config; + struct spi_litex_data *dev_data = dev->data; + struct spi_context *ctx = &dev_data->ctx; + uint32_t txd, rxd; + + /* Set CS */ + litex_write16(BIT(config->slave), dev_config->cs_addr); + + do { + /* Send a frame */ + if (spi_context_tx_buf_on(ctx)) { + litex_spi_tx_put(dev_data->dfs, &txd, ctx->tx_buf); + } else { + txd = 0U; + } + + LOG_DBG("txd: 0x%x", txd); + spi_litex_send(dev, txd, control); + + spi_context_update_tx(ctx, dev_data->dfs, 1); + + rxd = spi_litex_recv(dev); + LOG_DBG("rxd: 0x%x", rxd); + + if (spi_context_rx_buf_on(ctx)) { + litex_spi_rx_put(dev_data->dfs, &rxd, ctx->rx_buf); + } + + spi_context_update_rx(ctx, dev_data->dfs, 1); + + } while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx)); + + spi_context_complete(ctx, dev, 0); + + /* Clear CS */ + litex_write16(0, dev_config->cs_addr); +} + +/* API Functions */ + +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) +{ + struct spi_litex_data *dev_data = dev->data; + uint16_t control = 0; + int ret = 0; + + ret = spi_config(dev, config, &control); + if (ret < 0) { + return ret; + } + spi_context_buffers_setup(&dev_data->ctx, tx_bufs, rx_bufs, dev_data->dfs); + spi_litex_xfer(dev, config, control); + return 0; +} + +#ifdef CONFIG_SPI_ASYNC +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) +{ + return -ENOTSUP; +} +#endif /* CONFIG_SPI_ASYNC */ + +static int spi_litex_release(const struct device *dev, const struct spi_config *config) +{ + const struct spi_litex_cfg *dev_config = dev->config; + + if (!(litex_read8(dev_config->status_addr) & BIT(0))) { + return -EBUSY; + } + return 0; +} + +/* Device Instantiation */ +static const struct spi_driver_api spi_litex_api = { + .transceive = spi_litex_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = spi_litex_transceive_async, +#endif /* CONFIG_SPI_ASYNC */ + .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), \ + }; \ + static struct spi_litex_cfg spi_litex_cfg_##n = { \ + .control_addr = DT_INST_REG_ADDR_BY_NAME(n, control), \ + .status_addr = DT_INST_REG_ADDR_BY_NAME(n, status), \ + .mosi_addr = DT_INST_REG_ADDR_BY_NAME(n, mosi), \ + .miso_addr = DT_INST_REG_ADDR_BY_NAME(n, miso), \ + .cs_addr = DT_INST_REG_ADDR_BY_NAME(n, cs), \ + .loopback_addr = DT_INST_REG_ADDR_BY_NAME(n, loopback), \ + .clk_divider_exists = DT_INST_REG_HAS_NAME(n, clk_divider), \ + .clk_divider_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, clk_divider, 0), \ + .data_width = DT_INST_PROP(n, data_width), \ + .max_cs = DT_INST_PROP(n, max_cs), \ + }; \ + DEVICE_DT_INST_DEFINE(n, \ + NULL, \ + 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) diff --git a/drivers/spi/spi_litex_common.h b/drivers/spi/spi_litex_common.h new file mode 100644 index 0000000000000..c62173449dbf8 --- /dev/null +++ b/drivers/spi/spi_litex_common.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Vogl Electronic GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include + +#include "spi_context.h" +#include + +static inline uint8_t get_dfs_value(const struct spi_config *config) +{ + switch (SPI_WORD_SIZE_GET(config->operation)) { + case 8: + return 1; + case 16: + return 2; + case 24: + return 3; + case 32: + return 4; + default: + return 1; + } +} + +static inline void litex_spi_tx_put(uint8_t len, uint32_t *txd, const uint8_t *tx_buf) +{ + switch (len) { + case 4: + *txd = sys_get_be32(tx_buf); + break; + case 3: + *txd = sys_get_be24(tx_buf); + break; + case 2: + *txd = sys_get_be16(tx_buf); + break; + default: + *txd = *tx_buf; + break; + } +} + +static inline void litex_spi_rx_put(uint8_t len, uint32_t *rxd, uint8_t *rx_buf) +{ + switch (len) { + case 4: + sys_put_be32(*rxd, rx_buf); + break; + case 3: + sys_put_be24(*rxd, rx_buf); + break; + case 2: + sys_put_be16(*rxd, rx_buf); + break; + default: + *rx_buf = *rxd; + break; + } +} diff --git a/drivers/spi/spi_litex_litespi.c b/drivers/spi/spi_litex_litespi.c new file mode 100644 index 0000000000000..fb3417b775137 --- /dev/null +++ b/drivers/spi/spi_litex_litespi.c @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2024 Vogl Electronic GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT litex_spi_litespi + +#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL +#include +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_CORE_MASTER_STATUS_TX_READY_OFFSET 0x0 +#define SPIFLASH_CORE_MASTER_STATUS_RX_READY_OFFSET 0x1 + +#define SPI_MAX_WORD_SIZE 32 +#define SPI_MAX_CS_SIZE 4 + +struct spi_litex_dev_config { + uint32_t core_mmap_dummy_bits_addr; + 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 phy_clk_divisor_addr; + bool phy_clk_divisor_exists; +}; + +struct spi_litex_data { + struct spi_context ctx; + uint8_t dfs; /* dfs in bytes: 1,2 or 4 */ +}; + + +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; + + if (!dev_config->phy_clk_divisor_exists) { + /* In the LiteX Simulator the phy_clk_divisor doesn't exists, thats why we check. */ + LOG_WRN("No phy_clk_divisor found, can't change frequency"); + return 0; + } + + uint32_t divisor = DIV_ROUND_UP(sys_clock_hw_cycles_per_sec(), (2 * config->frequency)) - 1; + + litex_write32(divisor, dev_config->phy_clk_divisor_addr); + return 0; +} + +/* Helper Functions */ +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->operation & SPI_HALF_DUPLEX) { + LOG_ERR("Half-duplex not supported"); + return -ENOTSUP; + } + + if (SPI_WORD_SIZE_GET(config->operation) > SPI_MAX_WORD_SIZE) { + LOG_ERR("Word size must be <= %d, is %d", SPI_MAX_WORD_SIZE, + SPI_WORD_SIZE_GET(config->operation)); + return -ENOTSUP; + } + + if (config->operation & SPI_CS_ACTIVE_HIGH) { + LOG_ERR("CS active high not supported"); + 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"); + return -ENOTSUP; + } + + if (config->operation & SPI_TRANSFER_LSB) { + LOG_ERR("LSB first not supported"); + return -ENOTSUP; + } + + if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) { + LOG_ERR("Only supports CPOL=CPHA=0"); + return -ENOTSUP; + } + + if (config->operation & SPI_OP_MODE_SLAVE) { + LOG_ERR("Slave mode not supported"); + return -ENOTSUP; + } + + if (config->operation & SPI_MODE_LOOP) { + LOG_ERR("Loopback mode not supported"); + return -ENOTSUP; + } + + dev_data->dfs = get_dfs_value(config); + + spi_litex_set_frequency(dev, config); + + return 0; +} + +static void spiflash_len_mask_width_write(uint32_t len, uint32_t width, uint32_t mask, + uint32_t addr) +{ + uint32_t tmp = len & BIT_MASK(8); + uint32_t word = tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_LEN_OFFSET * 8); + + tmp = width & BIT_MASK(8); + word |= tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_WIDTH_OFFSET * 8); + tmp = mask & BIT_MASK(8); + word |= tmp << (SPIFLASH_CORE_MASTER_PHYCONFIG_MASK_OFFSET * 8); + litex_write32(word, 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 txd, rxd; + int ret = 0; + + 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*/ + + spiflash_len_mask_width_write(len * 8, width, mask, dev_config->core_master_phyconfig_addr); + + litex_write32(BIT(config->slave), dev_config->core_master_cs_addr); + + do { + len = MIN(spi_context_max_continuous_chunk(ctx), dev_config->core_master_rxtx_size); + if (len != old_len) { + spiflash_len_mask_width_write(len * 8, width, mask, + dev_config->core_master_phyconfig_addr); + old_len = len; + } + + if (spi_context_tx_buf_on(ctx)) { + litex_spi_tx_put(len, &txd, ctx->tx_buf); + } else { + txd = 0U; + } + + while (!(litex_read8(dev_config->core_master_status_addr) & + BIT(SPIFLASH_CORE_MASTER_STATUS_TX_READY_OFFSET))) { + ; + } + + LOG_DBG("txd: 0x%x", txd); + litex_write32(txd, dev_config->core_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))) { + ; + } + + rxd = litex_read32(dev_config->core_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); + + } while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx)); + + litex_write32(0, dev_config->core_master_cs_addr); + + spi_context_complete(ctx, dev, 0); + + 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) +{ + struct spi_litex_data *data = dev->data; + + int ret = spi_config(dev, config); + + if (ret) { + return ret; + } + + if (!tx_bufs && !rx_bufs) { + return 0; + } + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, data->dfs); + + ret = spi_litex_xfer(dev, config); + + return ret; +} + +#ifdef CONFIG_SPI_ASYNC +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) +{ + return -ENOTSUP; +} +#endif /* CONFIG_SPI_ASYNC */ + +static int spi_litex_release(const struct device *dev, const struct spi_config *config) +{ + + return 0; +} + +/* Device Instantiation */ +static const struct spi_driver_api spi_litex_api = { + .transceive = spi_litex_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = spi_litex_transceive_async, +#endif /* CONFIG_SPI_ASYNC */ + .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), \ + }; \ + static struct spi_litex_dev_config spi_litex_cfg_##n = { \ + .core_mmap_dummy_bits_addr = DT_INST_REG_ADDR_BY_NAME(n, core_mmap_dummy_bits), \ + .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), \ + .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) \ + \ + }; \ + DEVICE_DT_INST_DEFINE(n, NULL, 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) diff --git a/dts/bindings/spi/litex,spi-litespi.yaml b/dts/bindings/spi/litex,spi-litespi.yaml new file mode 100644 index 0000000000000..20ed218a2a9ed --- /dev/null +++ b/dts/bindings/spi/litex,spi-litespi.yaml @@ -0,0 +1,9 @@ +description: LiteX SPI LiteSPI Controller + +compatible: "litex,spi-litespi" + +include: spi-controller.yaml + +properties: + reg: + required: true diff --git a/dts/bindings/spi/litex,spi.yaml b/dts/bindings/spi/litex,spi.yaml index 522182034171d..7eecbc70d8eb9 100644 --- a/dts/bindings/spi/litex,spi.yaml +++ b/dts/bindings/spi/litex,spi.yaml @@ -10,3 +10,15 @@ include: spi-controller.yaml properties: reg: required: true + + data-width: + type: int + description: | + Maximum data width of the SPI controller in bits. + default: 8 + + max-cs: + type: int + description: | + Maximum number of chip selects supported by the SPI controller. + default: 1 diff --git a/dts/riscv/riscv32-litex-vexriscv.dtsi b/dts/riscv/riscv32-litex-vexriscv.dtsi index 11d14e50bb03b..c4557351b1c18 100644 --- a/dts/riscv/riscv32-litex-vexriscv.dtsi +++ b/dts/riscv/riscv32-litex-vexriscv.dtsi @@ -93,6 +93,30 @@ #address-cells = <1>; #size-cells = <0>; }; + spi1: spi@e000c000 { + compatible = "litex,spi-litespi"; + reg = <0xe000c000 0x4>, + <0xe000c004 0x4>, + <0xe000c008 0x4>, + <0xe000c00c 0x4>, + <0xe000c010 0x4>, + <0xe000c800 0x4>, + <0x60000000 0x1000000>; + reg-names = "core_mmap_dummy_bits", + "core_master_cs", + "core_master_phyconfig", + "core_master_rxtx", + "core_master_status", + "phy_clk_divisor", + "flash_mmap"; + #address-cells = <1>; + #size-cells = <0>; + spiflash0: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <10000000>; + }; + }; timer0: timer@e0002800 { compatible = "litex,timer0"; interrupt-parent = <&intc0>;