/* * Copyright (c) 2019 Antmicro * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT litex_spi #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL #include LOG_MODULE_REGISTER(spi_litespi); #include "spi_litespi.h" #include /* 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(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(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(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) { 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(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)