1 /*
2  * Copyright (c) 2019 Antmicro <www.antmicro.com>
3  * Copyright (c) 2024 Vogl Electronic GmbH
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT litex_spi
9 
10 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
11 #include <zephyr/logging/log.h>
12 LOG_MODULE_REGISTER(spi_litex);
13 
14 #include "spi_litex_common.h"
15 
16 #define POSITION_WORD_SIZE 8
17 
18 struct spi_litex_data {
19 	struct spi_context ctx;
20 	uint8_t dfs;	/* dfs in bytes: 1,2,3 or 4 */
21 };
22 
23 struct spi_litex_cfg {
24 	uint32_t control_addr;
25 	uint32_t status_addr;
26 	uint32_t mosi_addr;
27 	uint32_t miso_addr;
28 	uint32_t cs_addr;
29 	uint32_t loopback_addr;
30 	uint32_t clk_divider_addr;
31 	bool clk_divider_exists;
32 	int data_width;
33 	int max_cs;
34 };
35 
spi_set_frequency(const struct device * dev,const struct spi_config * config)36 static void spi_set_frequency(const struct device *dev, const struct spi_config *config)
37 {
38 	const struct spi_litex_cfg *dev_config = dev->config;
39 
40 	if (!dev_config->clk_divider_exists) {
41 		/* The clk_divider is optional, thats why we check. */
42 		LOG_WRN("No clk_divider found, can't change frequency");
43 		return;
44 	}
45 
46 	uint16_t divisor = DIV_ROUND_UP(sys_clock_hw_cycles_per_sec(), config->frequency);
47 
48 	litex_write16(divisor, dev_config->clk_divider_addr);
49 }
50 
51 /* Helper Functions */
spi_config(const struct device * dev,const struct spi_config * config,uint16_t * control)52 static int spi_config(const struct device *dev, const struct spi_config *config, uint16_t *control)
53 {
54 	const struct spi_litex_cfg *dev_config = dev->config;
55 	struct spi_litex_data *dev_data = dev->data;
56 
57 	if (config->slave >= dev_config->max_cs) {
58 		LOG_ERR("More slaves than supported");
59 		return -ENOTSUP;
60 	}
61 
62 	if (config->operation & SPI_HALF_DUPLEX) {
63 		LOG_ERR("Half-duplex not supported");
64 		return -ENOTSUP;
65 	}
66 
67 	if (SPI_WORD_SIZE_GET(config->operation) > dev_config->data_width) {
68 		LOG_ERR("Word size must be <= %d", dev_config->data_width);
69 		return -ENOTSUP;
70 	}
71 
72 	if (config->operation & SPI_CS_ACTIVE_HIGH) {
73 		LOG_ERR("CS active high not supported");
74 		return -ENOTSUP;
75 	}
76 
77 	if (config->operation & SPI_LOCK_ON) {
78 		LOG_ERR("Lock On not supported");
79 		return -ENOTSUP;
80 	}
81 
82 	if (IS_ENABLED(CONFIG_SPI_EXTENDED_MODES) &&
83 	    (config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
84 		LOG_ERR("Only supports single mode");
85 		return -ENOTSUP;
86 	}
87 
88 	if (config->operation & SPI_TRANSFER_LSB) {
89 		LOG_ERR("LSB first not supported");
90 		return -ENOTSUP;
91 	}
92 
93 	if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) {
94 		LOG_ERR("Only supports CPOL=CPHA=0");
95 		return -ENOTSUP;
96 	}
97 
98 	if (config->operation & SPI_OP_MODE_SLAVE) {
99 		LOG_ERR("Slave mode not supported");
100 		return -ENOTSUP;
101 	}
102 
103 	/* Set Loopback */
104 	if (!litex_read8(dev_config->loopback_addr) != !(config->operation & SPI_MODE_LOOP)) {
105 		litex_write8(((config->operation & SPI_MODE_LOOP) ? 0x1 : 0x0),
106 			     dev_config->loopback_addr);
107 	}
108 	/* Set word size */
109 	*control = (uint16_t) (SPI_WORD_SIZE_GET(config->operation)
110 			<< POSITION_WORD_SIZE);
111 
112 	dev_data->dfs = get_dfs_value(config);
113 
114 	/* Write configurations */
115 	litex_write16(*control, dev_config->control_addr);
116 
117 	spi_set_frequency(dev, config);
118 
119 	return 0;
120 }
121 
spi_litex_send(const struct device * dev,uint32_t frame,uint16_t control)122 static void spi_litex_send(const struct device *dev, uint32_t frame,
123 			   uint16_t control)
124 {
125 	const struct spi_litex_cfg *dev_config = dev->config;
126 	/* Write frame to register */
127 	litex_write32(frame, dev_config->mosi_addr);
128 	/* Start the transfer */
129 	litex_write16(control | BIT(0), dev_config->control_addr);
130 	/* Wait until the transfer ends */
131 	while (!(litex_read8(dev_config->status_addr) & BIT(0))) {
132 		;/* Wait */
133 	}
134 }
135 
spi_litex_recv(const struct device * dev)136 static uint32_t spi_litex_recv(const struct device *dev)
137 {
138 	const struct spi_litex_cfg *dev_config = dev->config;
139 
140 	/* Return data inside MISO register */
141 	return litex_read32(dev_config->miso_addr);
142 }
143 
spi_litex_xfer(const struct device * dev,const struct spi_config * config,uint16_t control)144 static void spi_litex_xfer(const struct device *dev,
145 			   const struct spi_config *config,
146 			   uint16_t control)
147 {
148 	const struct spi_litex_cfg *dev_config = dev->config;
149 	struct spi_litex_data *dev_data = dev->data;
150 	struct spi_context *ctx = &dev_data->ctx;
151 	uint32_t txd, rxd;
152 
153 	/* Set CS */
154 	litex_write16(BIT(config->slave), dev_config->cs_addr);
155 
156 	do {
157 		/* Send a frame */
158 		if (spi_context_tx_buf_on(ctx)) {
159 			litex_spi_tx_put(dev_data->dfs, &txd, ctx->tx_buf);
160 		} else {
161 			txd = 0U;
162 		}
163 
164 		LOG_DBG("txd: 0x%x", txd);
165 		spi_litex_send(dev, txd, control);
166 
167 		spi_context_update_tx(ctx, dev_data->dfs, 1);
168 
169 		rxd = spi_litex_recv(dev);
170 		LOG_DBG("rxd: 0x%x", rxd);
171 
172 		if (spi_context_rx_buf_on(ctx)) {
173 			litex_spi_rx_put(dev_data->dfs, &rxd, ctx->rx_buf);
174 		}
175 
176 		spi_context_update_rx(ctx, dev_data->dfs, 1);
177 
178 	} while (spi_context_tx_on(ctx) || spi_context_rx_on(ctx));
179 
180 	spi_context_complete(ctx, dev, 0);
181 
182 	/* Clear CS */
183 	litex_write16(0, dev_config->cs_addr);
184 }
185 
186 /* API Functions */
187 
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)188 static int spi_litex_transceive(const struct device *dev, const struct spi_config *config,
189 				const struct spi_buf_set *tx_bufs,
190 				const struct spi_buf_set *rx_bufs)
191 {
192 	struct spi_litex_data *dev_data = dev->data;
193 	uint16_t control = 0;
194 	int ret = 0;
195 
196 	ret = spi_config(dev, config, &control);
197 	if (ret < 0) {
198 		return ret;
199 	}
200 	spi_context_buffers_setup(&dev_data->ctx, tx_bufs, rx_bufs, dev_data->dfs);
201 	spi_litex_xfer(dev, config, control);
202 	return 0;
203 }
204 
205 #ifdef CONFIG_SPI_ASYNC
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)206 static int spi_litex_transceive_async(const struct device *dev, const struct spi_config *config,
207 				      const struct spi_buf_set *tx_bufs,
208 				      const struct spi_buf_set *rx_bufs,
209 				      struct k_poll_signal *async)
210 {
211 	return -ENOTSUP;
212 }
213 #endif /* CONFIG_SPI_ASYNC */
214 
spi_litex_release(const struct device * dev,const struct spi_config * config)215 static int spi_litex_release(const struct device *dev, const struct spi_config *config)
216 {
217 	const struct spi_litex_cfg *dev_config = dev->config;
218 
219 	if (!(litex_read8(dev_config->status_addr) & BIT(0))) {
220 		return -EBUSY;
221 	}
222 	return 0;
223 }
224 
225 /* Device Instantiation */
226 static const struct spi_driver_api spi_litex_api = {
227 	.transceive = spi_litex_transceive,
228 #ifdef CONFIG_SPI_ASYNC
229 	.transceive_async = spi_litex_transceive_async,
230 #endif /* CONFIG_SPI_ASYNC */
231 	.release = spi_litex_release,
232 };
233 
234 #define SPI_INIT(n)                                                                                \
235 	static struct spi_litex_data spi_litex_data_##n = {                                        \
236 		SPI_CONTEXT_INIT_LOCK(spi_litex_data_##n, ctx),                                    \
237 		SPI_CONTEXT_INIT_SYNC(spi_litex_data_##n, ctx),                                    \
238 	};                                                                                         \
239 	static struct spi_litex_cfg spi_litex_cfg_##n = {                                          \
240 		.control_addr = DT_INST_REG_ADDR_BY_NAME(n, control),                              \
241 		.status_addr = DT_INST_REG_ADDR_BY_NAME(n, status),                                \
242 		.mosi_addr = DT_INST_REG_ADDR_BY_NAME(n, mosi),                                    \
243 		.miso_addr = DT_INST_REG_ADDR_BY_NAME(n, miso),                                    \
244 		.cs_addr = DT_INST_REG_ADDR_BY_NAME(n, cs),                                        \
245 		.loopback_addr = DT_INST_REG_ADDR_BY_NAME(n, loopback),                            \
246 		.clk_divider_exists = DT_INST_REG_HAS_NAME(n, clk_divider),                        \
247 		.clk_divider_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, clk_divider, 0),                \
248 		.data_width = DT_INST_PROP(n, data_width),                                         \
249 		.max_cs = DT_INST_PROP(n, max_cs),                                                 \
250 	};                                                                                         \
251 	DEVICE_DT_INST_DEFINE(n,                                                                   \
252 			NULL,                                                                      \
253 			NULL,                                                                      \
254 			&spi_litex_data_##n,                                                       \
255 			&spi_litex_cfg_##n,                                                        \
256 			POST_KERNEL,                                                               \
257 			CONFIG_SPI_INIT_PRIORITY,                                                  \
258 			&spi_litex_api);
259 
260 DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
261