1 /*
2  * Copyright (c) 2023 Intel Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT intel_sedi_spi
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/drivers/spi.h>
11 #include <zephyr/drivers/spi/rtio.h>
12 #include <zephyr/pm/device.h>
13 
14 #define LOG_LEVEL CONFIG_SPI_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(spi_sedi);
17 
18 #include "sedi_driver_spi.h"
19 #include "spi_context.h"
20 
21 
22 struct spi_sedi_config {
23 	DEVICE_MMIO_ROM;
24 	sedi_spi_t spi_device;
25 	void (*irq_config)(void);
26 };
27 
28 struct spi_sedi_data {
29 	DEVICE_MMIO_RAM;
30 	struct spi_context ctx;
31 	bool tx_data_updated;
32 	bool rx_data_updated;
33 	uint32_t tx_dummy_len;
34 	uint32_t rx_dummy_len;
35 };
36 
spi_sedi_configure(const struct device * dev,const struct spi_config * config)37 static int spi_sedi_configure(const struct device *dev,
38 			      const struct spi_config *config)
39 {
40 	struct spi_sedi_data *data = dev->data;
41 	const struct spi_sedi_config *info = dev->config;
42 	uint32_t word_size, cpol, cpha, loopback;
43 
44 	if (spi_context_configured(&data->ctx, config) == true) {
45 		return 0;
46 	}
47 
48 	word_size = SPI_WORD_SIZE_GET(config->operation);
49 	sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_DATA_WIDTH,
50 			 word_size);
51 
52 	/* CPOL and CPHA */
53 	cpol = SPI_MODE_GET(config->operation) & SPI_MODE_CPOL;
54 	cpha = SPI_MODE_GET(config->operation) & SPI_MODE_CPHA;
55 
56 	if ((cpol == 0) && (cpha == 0)) {
57 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_CPOL0_CPHA0,
58 				 0);
59 	} else if ((cpol == 0) && (cpha == 1U)) {
60 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_CPOL0_CPHA1,
61 				 0);
62 	} else if ((cpol == 1) && (cpha == 0U)) {
63 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_CPOL1_CPHA0,
64 				 0);
65 	} else {
66 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_CPOL1_CPHA1,
67 				 0);
68 	}
69 
70 	/* MSB and LSB */
71 	if (config->operation & SPI_TRANSFER_LSB) {
72 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_LSB, 0);
73 	}
74 
75 	/* Set loopack */
76 	loopback = SPI_MODE_GET(config->operation) & SPI_MODE_LOOP;
77 	sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_LOOPBACK, loopback);
78 
79 	/* Set baudrate */
80 	sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_SPEED_SET,
81 			 config->frequency);
82 
83 	sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_CS_HW, config->slave);
84 
85 	data->ctx.config = config;
86 	spi_context_cs_control(&data->ctx, true);
87 
88 	return 0;
89 }
90 
transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs,bool asynchronous,spi_callback_t cb,void * userdata)91 static int transceive(const struct device *dev, const struct spi_config *config,
92 		      const struct spi_buf_set *tx_bufs,
93 		      const struct spi_buf_set *rx_bufs, bool asynchronous,
94 		      spi_callback_t cb,
95 		      void *userdata)
96 {
97 	const struct spi_sedi_config *info = dev->config;
98 	struct spi_sedi_data *spi = dev->data;
99 	struct spi_context *ctx = &spi->ctx;
100 	int ret;
101 	uint32_t transfer_bytes = 0;
102 	uint8_t *data_out = NULL, *data_in = NULL;
103 	uint32_t i, dummy_len = 0;
104 	const struct spi_buf *buf;
105 	bool is_multibufs = false;
106 
107 	spi_context_lock(&spi->ctx, asynchronous, cb, userdata, config);
108 	pm_device_busy_set(dev);
109 
110 	/* Power up use default setting */
111 	ret = sedi_spi_set_power(info->spi_device, SEDI_POWER_FULL);
112 	if (ret) {
113 		goto out;
114 	}
115 
116 	/* If need to configure, re-configure */
117 	spi_sedi_configure(dev, config);
118 
119 	spi->tx_data_updated = false;
120 	spi->rx_data_updated = false;
121 	/* Set buffers info */
122 	spi_context_buffers_setup(&spi->ctx, tx_bufs, rx_bufs, 1);
123 
124 	if ((ctx->tx_count > 1) || (ctx->rx_count > 1)) {
125 		is_multibufs = true;
126 	}
127 
128 	if (ctx->tx_count > ctx->rx_count) {
129 		spi->tx_dummy_len = 0;
130 		for (i = ctx->rx_count; i < ctx->tx_count; i++) {
131 			buf = ctx->current_tx + i;
132 			dummy_len += buf->len;
133 		}
134 		spi->rx_dummy_len = dummy_len;
135 	} else if (ctx->tx_count < ctx->rx_count) {
136 		spi->rx_dummy_len = 0;
137 		for (i = ctx->tx_count; i < ctx->rx_count; i++) {
138 			buf = ctx->current_rx + i;
139 			dummy_len += buf->len;
140 		}
141 		spi->tx_dummy_len = dummy_len;
142 	} else {
143 		spi->tx_dummy_len = 0;
144 		spi->rx_dummy_len = 0;
145 	}
146 
147 	if ((ctx->tx_len == 0) && (ctx->rx_len == 0)) {
148 		spi_context_cs_control(&spi->ctx, true);
149 		spi_context_complete(&spi->ctx, dev, 0);
150 		return 0;
151 	}
152 
153 	/* For multiple buffers, using continuous mode */
154 	if (is_multibufs) {
155 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_BUFFER_SETS, 1);
156 	}
157 
158 	if (ctx->tx_len == 0) {
159 		/* rx only, nothing to tx */
160 		data_out = NULL;
161 		data_in = (uint8_t *)ctx->rx_buf;
162 		transfer_bytes = ctx->rx_len;
163 		spi->tx_dummy_len -= transfer_bytes;
164 	} else if (ctx->rx_len == 0) {
165 		/* tx only, nothing to rx */
166 		data_out = (uint8_t *)ctx->tx_buf;
167 		data_in = NULL;
168 		transfer_bytes = ctx->tx_len;
169 		spi->rx_dummy_len -= transfer_bytes;
170 	} else if (ctx->tx_len == ctx->rx_len) {
171 		/* rx and tx are the same length */
172 		data_out = (uint8_t *)ctx->tx_buf;
173 		data_in = (uint8_t *)ctx->rx_buf;
174 		transfer_bytes = ctx->tx_len;
175 	} else if (ctx->tx_len > ctx->rx_len) {
176 		/* Break up the tx into multiple transfers so we don't have to
177 		 * rx into a longer intermediate buffer. Leave chip select
178 		 * active between transfers.
179 		 */
180 		data_out = (uint8_t *)ctx->tx_buf;
181 		data_in = ctx->rx_buf;
182 		transfer_bytes = ctx->rx_len;
183 	} else {
184 		/* Break up the rx into multiple transfers so we don't have to
185 		 * tx from a longer intermediate buffer. Leave chip select
186 		 * active between transfers.
187 		 */
188 		data_out = (uint8_t *)ctx->tx_buf;
189 		data_in = ctx->rx_buf;
190 		transfer_bytes = ctx->tx_len;
191 	}
192 
193 	spi_context_cs_control(&spi->ctx, false);
194 
195 	ret = sedi_spi_transfer(info->spi_device, data_out, data_in,
196 				transfer_bytes);
197 
198 	if (ret != SEDI_DRIVER_OK) {
199 		goto out;
200 	}
201 
202 	ret = spi_context_wait_for_completion(&spi->ctx);
203 	if (ret != 0) {
204 		sedi_spi_status_t spi_status = {0};
205 
206 		sedi_spi_get_status(info->spi_device, &spi_status);
207 
208 		/* SPI ABORT */
209 		sedi_spi_control(info->spi_device, SEDI_SPI_IOCTL_ABORT, 0);
210 		/* Toggle GPIO back */
211 		spi_context_cs_control(&spi->ctx, true);
212 	}
213 out:
214 	spi_context_release(&spi->ctx, ret);
215 	pm_device_busy_clear(dev);
216 
217 	return ret;
218 }
219 
spi_sedi_transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)220 static int spi_sedi_transceive(const struct device *dev,
221 			       const struct spi_config *config,
222 			       const struct spi_buf_set *tx_bufs,
223 			       const struct spi_buf_set *rx_bufs)
224 {
225 	return transceive(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
226 }
227 
228 #ifdef CONFIG_SPI_ASYNC
spi_sedi_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,spi_callback_t cb,void * userdata)229 static int spi_sedi_transceive_async(const struct device *dev,
230 				     const struct spi_config *config,
231 				     const struct spi_buf_set *tx_bufs,
232 				     const struct spi_buf_set *rx_bufs,
233 				     spi_callback_t cb,
234 				     void *userdata)
235 {
236 	return transceive(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
237 }
238 #endif /* CONFIG_SPI_ASYNC */
239 
spi_sedi_release(const struct device * dev,const struct spi_config * config)240 static int spi_sedi_release(const struct device *dev,
241 			    const struct spi_config *config)
242 {
243 	struct spi_sedi_data *spi = dev->data;
244 
245 	if (!spi_context_configured(&spi->ctx, config)) {
246 		return -EINVAL;
247 	}
248 
249 	spi_context_unlock_unconditionally(&spi->ctx);
250 
251 	return 0;
252 }
253 
254 extern void spi_isr(sedi_spi_t device);
255 
spi_sedi_callback(uint32_t event,void * param)256 void spi_sedi_callback(uint32_t event, void *param)
257 {
258 	const struct device *dev = (const struct device *)param;
259 	const struct spi_sedi_config *info = dev->config;
260 	struct spi_sedi_data *spi = dev->data;
261 	struct spi_context *ctx = &spi->ctx;
262 	int error;
263 
264 	if (event == SEDI_SPI_EVENT_DATA_LOST) {
265 		error = -EIO;
266 	} else {
267 		error = 0;
268 	}
269 
270 	if ((event == SEDI_SPI_EVENT_COMPLETE) ||
271 	    (event == SEDI_SPI_EVENT_DATA_LOST)) {
272 		spi_context_cs_control(&spi->ctx, true);
273 		spi_context_complete(&spi->ctx, dev, error);
274 	} else if (event == SEDI_SPI_EVENT_TX_FINISHED) {
275 		spi_context_update_tx(ctx, 1, ctx->tx_len);
276 		if (ctx->tx_len != 0) {
277 			sedi_spi_update_tx_buf(info->spi_device, ctx->tx_buf,
278 					       ctx->tx_len);
279 			if ((ctx->rx_len == 0) &&
280 			    (spi->rx_data_updated == false)) {
281 				/* Update rx length if always no rx */
282 				sedi_spi_update_rx_buf(info->spi_device, NULL,
283 						       spi->rx_dummy_len);
284 				spi->rx_data_updated = true;
285 			}
286 		} else if (spi->tx_data_updated == false) {
287 			sedi_spi_update_tx_buf(info->spi_device, NULL,
288 					       spi->tx_dummy_len);
289 			spi->tx_data_updated = true;
290 		}
291 	} else if (event == SEDI_SPI_EVENT_RX_FINISHED) {
292 		spi_context_update_rx(ctx, 1, ctx->rx_len);
293 		if (ctx->rx_len != 0) {
294 			sedi_spi_update_rx_buf(info->spi_device, ctx->rx_buf,
295 					       ctx->rx_len);
296 		}
297 	}
298 }
299 
300 static DEVICE_API(spi, sedi_spi_api) = {
301 	.transceive = spi_sedi_transceive,
302 #ifdef CONFIG_SPI_ASYNC
303 	.transceive_async = spi_sedi_transceive_async,
304 #endif  /* CONFIG_SPI_ASYNC */
305 #ifdef CONFIG_SPI_RTIO
306 	.iodev_submit = spi_rtio_iodev_default_submit,
307 #endif
308 	.release = spi_sedi_release,
309 };
310 
spi_sedi_init(const struct device * dev)311 static int spi_sedi_init(const struct device *dev)
312 {
313 	const struct spi_sedi_config *info = dev->config;
314 	struct spi_sedi_data *spi = dev->data;
315 	int ret;
316 
317 	DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
318 
319 	ret = sedi_spi_init(info->spi_device, spi_sedi_callback, (void *)dev,
320 			DEVICE_MMIO_GET(dev));
321 	if (ret != SEDI_DRIVER_OK) {
322 		return -ENODEV;
323 	}
324 
325 	/* Init and connect IRQ */
326 	info->irq_config();
327 
328 	spi_context_unlock_unconditionally(&spi->ctx);
329 
330 	return 0;
331 }
332 
333 #ifdef CONFIG_PM_DEVICE
334 
spi_suspend_device(const struct device * dev)335 static int spi_suspend_device(const struct device *dev)
336 {
337 	const struct spi_sedi_config *config = dev->config;
338 
339 	if (pm_device_is_busy(dev)) {
340 		return -EBUSY;
341 	}
342 
343 	int ret = sedi_spi_set_power(config->spi_device, SEDI_POWER_SUSPEND);
344 
345 	if (ret != SEDI_DRIVER_OK) {
346 		return -EIO;
347 	}
348 
349 	return 0;
350 }
351 
spi_resume_device_from_suspend(const struct device * dev)352 static int spi_resume_device_from_suspend(const struct device *dev)
353 {
354 	const struct spi_sedi_config *config = dev->config;
355 	int ret;
356 
357 	ret = sedi_spi_set_power(config->spi_device, SEDI_POWER_FULL);
358 	if (ret != SEDI_DRIVER_OK) {
359 		return -EIO;
360 	}
361 
362 	pm_device_busy_clear(dev);
363 
364 	return 0;
365 }
366 
spi_sedi_device_ctrl(const struct device * dev,enum pm_device_action action)367 static int spi_sedi_device_ctrl(const struct device *dev,
368 				enum pm_device_action action)
369 {
370 	int ret = 0;
371 
372 	switch (action) {
373 	case PM_DEVICE_ACTION_SUSPEND:
374 		ret = spi_suspend_device(dev);
375 		break;
376 	case PM_DEVICE_ACTION_RESUME:
377 		ret = spi_resume_device_from_suspend(dev);
378 		break;
379 	default:
380 		ret = -ENOTSUP;
381 	}
382 
383 	return ret;
384 }
385 
386 #endif /* CONFIG_PM_DEVICE */
387 
388 #define SPI_SEDI_IRQ_FLAGS_SENSE0(n) 0
389 #define SPI_SEDI_IRQ_FLAGS_SENSE1(n) DT_INST_IRQ(n, sense)
390 #define SPI_SEDI_IRQ_FLAGS(n) \
391 	_CONCAT(SPI_SEDI_IRQ_FLAGS_SENSE, DT_INST_IRQ_HAS_CELL(n, sense))(n)
392 
393 #define CREATE_SEDI_SPI_INSTANCE(num)					       \
394 	static void spi_##num##_irq_init(void)			               \
395 	{								       \
396 		IRQ_CONNECT(DT_INST_IRQN(num),				       \
397 			    DT_INST_IRQ(num, priority),			       \
398 			    spi_isr, num, SPI_SEDI_IRQ_FLAGS(num));	       \
399 		irq_enable(DT_INST_IRQN(num));				       \
400 	}								       \
401 	static struct spi_sedi_data spi_##num##_data = {		       \
402 		SPI_CONTEXT_INIT_LOCK(spi_##num##_data, ctx),	               \
403 		SPI_CONTEXT_INIT_SYNC(spi_##num##_data, ctx),	               \
404 	};								       \
405 	const static struct spi_sedi_config spi_##num##_config = {	       \
406 		DEVICE_MMIO_ROM_INIT(DT_DRV_INST(num)),                        \
407 		.spi_device = num, .irq_config = spi_##num##_irq_init,         \
408 	};								       \
409 	PM_DEVICE_DEFINE(spi_##num, spi_sedi_device_ctrl);		       \
410 	SPI_DEVICE_DT_INST_DEFINE(num,					       \
411 			      spi_sedi_init,				       \
412 			      PM_DEVICE_GET(spi_##num),		               \
413 			      &spi_##num##_data,			       \
414 			      &spi_##num##_config,			       \
415 			      POST_KERNEL,				       \
416 			      CONFIG_SPI_INIT_PRIORITY,			       \
417 			      &sedi_spi_api);
418 
419 DT_INST_FOREACH_STATUS_OKAY(CREATE_SEDI_SPI_INSTANCE)
420