|  | /* | 
|  | * 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_DT_INST_DEFINE(n, \ | 
|  | spi_litespi_init, \ | 
|  | NULL, \ | 
|  | &spi_litespi_data_##n, \ | 
|  | &spi_litespi_cfg_##n, \ | 
|  | POST_KERNEL, \ | 
|  | CONFIG_SPI_INIT_PRIORITY, \ | 
|  | &spi_litespi_api); | 
|  |  | 
|  | DT_INST_FOREACH_STATUS_OKAY(SPI_INIT) |