From d71ad169d41b9fd8ebfdee37631f17d9649b698b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fin=20Maa=C3=9F?= Date: Fri, 24 May 2024 14:25:44 +0200 Subject: [PATCH] drivers: spi: litex: add litespi driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit add litespi driver for flash. Signed-off-by: Fin Maaß --- drivers/spi/CMakeLists.txt | 1 + drivers/spi/Kconfig.litex | 7 + drivers/spi/spi_litex_litespi.c | 269 ++++++++++++++++++++++++ dts/bindings/spi/litex,spi-litespi.yaml | 9 + dts/riscv/riscv32-litex-vexriscv.dtsi | 24 +++ 5 files changed, 310 insertions(+) create mode 100644 drivers/spi/spi_litex_litespi.c create mode 100644 dts/bindings/spi/litex,spi-litespi.yaml diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 78a115c1cfd..c625f9cb99c 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -25,6 +25,7 @@ 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_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 6d0e2b92770..54798d1fe21 100644 --- a/drivers/spi/Kconfig.litex +++ b/drivers/spi/Kconfig.litex @@ -9,3 +9,10 @@ config SPI_LITEX 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_litex_litespi.c b/drivers/spi/spi_litex_litespi.c new file mode 100644 index 00000000000..fb3417b7751 --- /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 00000000000..20ed218a2a9 --- /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/riscv/riscv32-litex-vexriscv.dtsi b/dts/riscv/riscv32-litex-vexriscv.dtsi index e4fa9db30dc..6d7cb7bd2fb 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>;