drivers: spi: litex: add litespi driver
add litespi driver for flash. Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
parent
0f3955cc80
commit
d71ad169d4
5 changed files with 310 additions and 0 deletions
|
|
@ -25,6 +25,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM spi_nrfx_spim.c
|
||||||
spi_nrfx_common.c)
|
spi_nrfx_common.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIS spi_nrfx_spis.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 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_OC_SIMPLE spi_oc_simple.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI spi_xec_qmspi.c)
|
zephyr_library_sources_ifdef(CONFIG_SPI_XEC_QMSPI spi_xec_qmspi.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_SPI_GECKO spi_gecko.c)
|
zephyr_library_sources_ifdef(CONFIG_SPI_GECKO spi_gecko.c)
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,10 @@ config SPI_LITEX
|
||||||
depends on DT_HAS_LITEX_SPI_ENABLED
|
depends on DT_HAS_LITEX_SPI_ENABLED
|
||||||
help
|
help
|
||||||
Enable the SPI peripherals on LiteX
|
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
|
||||||
|
|
|
||||||
269
drivers/spi/spi_litex_litespi.c
Normal file
269
drivers/spi/spi_litex_litespi.c
Normal file
|
|
@ -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 <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(spi_litex_litespi);
|
||||||
|
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#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)
|
||||||
9
dts/bindings/spi/litex,spi-litespi.yaml
Normal file
9
dts/bindings/spi/litex,spi-litespi.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
description: LiteX SPI LiteSPI Controller
|
||||||
|
|
||||||
|
compatible: "litex,spi-litespi"
|
||||||
|
|
||||||
|
include: spi-controller.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
reg:
|
||||||
|
required: true
|
||||||
|
|
@ -93,6 +93,30 @@
|
||||||
#address-cells = <1>;
|
#address-cells = <1>;
|
||||||
#size-cells = <0>;
|
#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 {
|
timer0: timer@e0002800 {
|
||||||
compatible = "litex,timer0";
|
compatible = "litex,timer0";
|
||||||
interrupt-parent = <&intc0>;
|
interrupt-parent = <&intc0>;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue