7e0eed9235
Usually, we want to operate only on "available" device nodes ("available" means "status is okay and a matching binding is found"), but that's not true in all cases. Sometimes we want to operate on special nodes without matching bindings, such as those describing memory. To handle the distinction, change various additional devicetree APIs making it clear that they operate only on available device nodes, adjusting gen_defines and devicetree.h implementation details accordingly: - emit macros for all existing nodes in gen_defines.py, regardless of status or matching binding - rename DT_NUM_INST to DT_NUM_INST_STATUS_OKAY - rename DT_NODE_HAS_COMPAT to DT_NODE_HAS_COMPAT_STATUS_OKAY - rename DT_INST_FOREACH to DT_INST_FOREACH_STATUS_OKAY - rename DT_ANY_INST_ON_BUS to DT_ANY_INST_ON_BUS_STATUS_OKAY - rewrite DT_HAS_NODE_STATUS_OKAY in terms of a new DT_NODE_HAS_STATUS - resurrect DT_HAS_NODE in the form of DT_NODE_EXISTS - remove DT_COMPAT_ON_BUS as a public API - use the new default_prop_types edtlib parameter Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
186 lines
4.3 KiB
C
186 lines
4.3 KiB
C
/*
|
|
* Copyright (c) 2019 Antmicro <www.antmicro.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT litex_spi
|
|
|
|
#define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(spi_litespi);
|
|
#include "spi_litespi.h"
|
|
#include <stdbool.h>
|
|
|
|
/* Helper Functions */
|
|
static int spi_config(const struct spi_config *config, u16_t *control)
|
|
{
|
|
u8_t cs = 0x00;
|
|
|
|
if (config->slave != 0) {
|
|
if (config->slave >= SPI_MAX_CS_SIZE) {
|
|
LOG_ERR("More slaves than supported");
|
|
return -ENOTSUP;
|
|
}
|
|
cs = (u8_t)(config->slave);
|
|
}
|
|
|
|
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 ((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_REG);
|
|
}
|
|
/* Set word size */
|
|
*control = (u16_t) (SPI_WORD_SIZE_GET(config->operation)
|
|
<< POSITION_WORD_SIZE);
|
|
/* Write configurations */
|
|
litex_write8(cs, SPI_CS_REG);
|
|
litex_write16(*control, SPI_CONTROL_REG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void spi_litespi_send(struct device *dev, u8_t frame, u16_t control)
|
|
{
|
|
/* Write frame to register */
|
|
litex_write8(frame, SPI_MOSI_DATA_REG);
|
|
/* Start the transfer */
|
|
litex_write16(control | SPI_ENABLE, SPI_CONTROL_REG);
|
|
/* Wait until the transfer ends */
|
|
while (!(litex_read8(SPI_STATUS_REG)))
|
|
;
|
|
}
|
|
|
|
static u8_t spi_litespi_recv(void)
|
|
{
|
|
/* Return data inside MISO register */
|
|
return litex_read8(SPI_MISO_DATA_REG);
|
|
}
|
|
|
|
static void spi_litespi_xfer(struct device *dev,
|
|
const struct spi_config *config, u16_t control)
|
|
{
|
|
struct spi_context *ctx = &SPI_DATA(dev)->ctx;
|
|
u32_t send_len = spi_context_longest_current_buf(ctx);
|
|
u8_t read_data;
|
|
|
|
for (u32_t i = 0; i < send_len; i++) {
|
|
/* Send a frame */
|
|
if (i < ctx->tx_len) {
|
|
spi_litespi_send(dev, (u8_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, 0);
|
|
}
|
|
|
|
/* API Functions */
|
|
|
|
static int spi_litespi_init(struct device *dev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int spi_litespi_transceive(struct device *dev,
|
|
const struct spi_config *config,
|
|
const struct spi_buf_set *tx_bufs,
|
|
const struct spi_buf_set *rx_bufs)
|
|
{
|
|
u16_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(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(struct device *dev,
|
|
const struct spi_config *config)
|
|
{
|
|
if (!(litex_read8(SPI_STATUS_REG))) {
|
|
return -EBUSY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Device Instantiation */
|
|
static 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_AND_API_INIT(spi_##n, \
|
|
DT_INST_LABEL(n), \
|
|
spi_litespi_init, \
|
|
&spi_litespi_data_##n, \
|
|
&spi_litespi_cfg_##n, \
|
|
POST_KERNEL, \
|
|
CONFIG_SPI_INIT_PRIORITY, \
|
|
&spi_litespi_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
|