forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
drivers: spi: litex: add litespi driver
add litespi driver for flash. Signed-off-by: Fin Maaß <[email protected]>
- Loading branch information
1 parent
0f3955c
commit d71ad16
Showing
5 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
description: LiteX SPI LiteSPI Controller | ||
|
||
compatible: "litex,spi-litespi" | ||
|
||
include: spi-controller.yaml | ||
|
||
properties: | ||
reg: | ||
required: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters