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