Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | /*
* 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, 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 (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 = (uint16_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(const struct device *dev, uint8_t frame,
uint16_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 uint8_t spi_litespi_recv(void)
{
/* Return data inside MISO register */
return litex_read8(SPI_MISO_DATA_REG);
}
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, 0);
}
/* API Functions */
static int spi_litespi_init(const struct device *dev)
{
return 0;
}
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_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)
|