1 /*
2  * Copyright (c) 2023 Frontgrade Gaisler AB
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT gaisler_spimctrl
8 
9 #include <zephyr/drivers/spi.h>
10 #include <zephyr/drivers/spi/rtio.h>
11 
12 #include <zephyr/logging/log.h>
13 LOG_MODULE_REGISTER(spi_spimctrl);
14 #include "spi_context.h"
15 
16 
17 struct spimctrl_regs {
18 	uint32_t conf;
19 	uint32_t ctrl;
20 	uint32_t stat;
21 	uint32_t rx;
22 	uint32_t tx;
23 };
24 
25 #define CONF_READCMD    0x0000007f
26 #define CTRL_RST        0x00000010
27 #define CTRL_CSN        0x00000008
28 #define CTRL_EAS        0x00000004
29 #define CTRL_IEN        0x00000002
30 #define CTRL_USRC       0x00000001
31 #define STAT_INIT       0x00000004
32 #define STAT_BUSY       0x00000002
33 #define STAT_DONE       0x00000001
34 
35 #define SPI_DATA(dev) ((struct data *) ((dev)->data))
36 
37 struct cfg {
38 	volatile struct spimctrl_regs *regs;
39 	int interrupt;
40 };
41 
42 struct data {
43 	struct spi_context ctx;
44 };
45 
spi_config(struct spi_context * ctx,const struct spi_config * config)46 static int spi_config(struct spi_context *ctx, const struct spi_config *config)
47 {
48 	if (config->slave != 0) {
49 		LOG_ERR("More slaves than supported");
50 		return -ENOTSUP;
51 	}
52 
53 	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
54 		LOG_ERR("Word size must be 8");
55 		return -ENOTSUP;
56 	}
57 
58 	if (config->operation & SPI_CS_ACTIVE_HIGH) {
59 		LOG_ERR("CS active high not supported");
60 		return -ENOTSUP;
61 	}
62 
63 	if (config->operation & SPI_LOCK_ON) {
64 		LOG_ERR("Lock On not supported");
65 		return -ENOTSUP;
66 	}
67 
68 	if ((config->operation & SPI_LINES_MASK) != SPI_LINES_SINGLE) {
69 		LOG_ERR("Only supports single mode");
70 		return -ENOTSUP;
71 	}
72 
73 	if (config->operation & SPI_TRANSFER_LSB) {
74 		LOG_ERR("LSB first not supported");
75 		return -ENOTSUP;
76 	}
77 
78 	if (config->operation & (SPI_MODE_CPOL | SPI_MODE_CPHA)) {
79 		LOG_ERR("Only supports CPOL=CPHA=0");
80 		return -ENOTSUP;
81 	}
82 
83 	if (config->operation & SPI_OP_MODE_SLAVE) {
84 		LOG_ERR("Slave mode not supported");
85 		return -ENOTSUP;
86 	}
87 
88 	if (config->operation & SPI_MODE_LOOP) {
89 		LOG_ERR("Loopback not supported");
90 		return -ENOTSUP;
91 	}
92 
93 	ctx->config = config;
94 
95 	return 0;
96 }
97 
transceive(const struct device * dev,const struct spi_config * config,const struct spi_buf_set * tx_bufs,const struct spi_buf_set * rx_bufs)98 static int transceive(const struct device *dev,
99 		      const struct spi_config *config,
100 		      const struct spi_buf_set *tx_bufs,
101 		      const struct spi_buf_set *rx_bufs)
102 {
103 	const struct cfg *const cfg = dev->config;
104 	volatile struct spimctrl_regs *const regs = cfg->regs;
105 	struct spi_context *ctx = &SPI_DATA(dev)->ctx;
106 	uint8_t txval;
107 	int rc;
108 
109 	spi_context_lock(ctx, false, NULL, NULL, config);
110 
111 	rc = spi_config(ctx, config);
112 	if (rc) {
113 		LOG_ERR("%s: config", __func__);
114 		spi_context_release(ctx, rc);
115 		return rc;
116 	}
117 
118 	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1);
119 
120 	regs->ctrl |= (CTRL_USRC | CTRL_IEN);
121 	regs->ctrl &= ~CTRL_CSN;
122 
123 	if (spi_context_tx_buf_on(ctx)) {
124 		txval = *ctx->tx_buf;
125 		spi_context_update_tx(ctx, 1, 1);
126 	} else {
127 		txval = 0;
128 	}
129 	/* This will eventually trig the interrupt */
130 	regs->tx = txval;
131 
132 	rc = spi_context_wait_for_completion(ctx);
133 
134 	regs->ctrl |= CTRL_CSN;
135 	regs->ctrl &= ~CTRL_USRC;
136 	spi_context_release(ctx, rc);
137 
138 	return 0;
139 }
140 
141 #ifdef CONFIG_SPI_ASYNC
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)142 static int transceive_async(const struct device *dev,
143 			    const struct spi_config *config,
144 			    const struct spi_buf_set *tx_bufs,
145 			    const struct spi_buf_set *rx_bufs,
146 			    struct k_poll_signal *async)
147 {
148 	return -ENOTSUP;
149 }
150 #endif /* CONFIG_SPI_ASYNC */
151 
release(const struct device * dev,const struct spi_config * config)152 static int release(const struct device *dev, const struct spi_config *config)
153 {
154 	spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx);
155 	return 0;
156 }
157 
spim_isr(struct device * dev)158 static void spim_isr(struct device *dev)
159 {
160 	const struct cfg *const cfg = dev->config;
161 	volatile struct spimctrl_regs *const regs = cfg->regs;
162 	struct spi_context *ctx = &SPI_DATA(dev)->ctx;
163 	uint8_t rx_byte;
164 	uint8_t val;
165 
166 	if ((regs->stat & STAT_DONE) == 0) {
167 		return;
168 	}
169 
170 	regs->stat = STAT_DONE;
171 
172 	/* Always read register and maybe write mem. */
173 	rx_byte = regs->rx;
174 	if (spi_context_rx_on(ctx)) {
175 		*ctx->rx_buf = rx_byte;
176 		spi_context_update_rx(ctx, 1, 1);
177 	}
178 
179 	if (spi_context_tx_buf_on(ctx) == false && spi_context_rx_buf_on(ctx) == false) {
180 		regs->ctrl &= ~CTRL_IEN;
181 		spi_context_complete(ctx, dev, 0);
182 		return;
183 	}
184 
185 	val = 0;
186 	if (spi_context_tx_buf_on(ctx)) {
187 		val = *ctx->tx_buf;
188 		spi_context_update_tx(ctx, 1, 1);
189 	}
190 	regs->tx = val;
191 }
192 
init(const struct device * dev)193 static int init(const struct device *dev)
194 {
195 	const struct cfg *const cfg = dev->config;
196 	volatile struct spimctrl_regs *const regs = cfg->regs;
197 
198 	regs->ctrl = CTRL_CSN;
199 	while (regs->stat & STAT_BUSY) {
200 		;
201 	}
202 	regs->stat = STAT_DONE;
203 
204 	irq_connect_dynamic(
205 		cfg->interrupt,
206 		0,
207 		(void (*)(const void *)) spim_isr,
208 		dev,
209 		0
210 	);
211 	irq_enable(cfg->interrupt);
212 
213 	spi_context_unlock_unconditionally(&SPI_DATA(dev)->ctx);
214 
215 	return 0;
216 }
217 
218 static DEVICE_API(spi, api) = {
219 	.transceive             = transceive,
220 #ifdef CONFIG_SPI_ASYNC
221 	.transceive_async       = transceive_async,
222 #endif /* CONFIG_SPI_ASYNC */
223 #ifdef CONFIG_SPI_RTIO
224 	.iodev_submit = spi_rtio_iodev_default_submit,
225 #endif
226 	.release                = release,
227 };
228 
229 #define SPI_INIT(n)	                                                \
230 	static const struct cfg cfg_##n = {                             \
231 		.regs           = (struct spimctrl_regs *)              \
232 				  DT_INST_REG_ADDR(n),                  \
233 		.interrupt      = DT_INST_IRQN(n),                      \
234 	};                                                              \
235 	static struct data data_##n = {                                 \
236 		SPI_CONTEXT_INIT_LOCK(data_##n, ctx),                   \
237 		SPI_CONTEXT_INIT_SYNC(data_##n, ctx),                   \
238 	};                                                              \
239 	SPI_DEVICE_DT_INST_DEFINE(n,                                        \
240 			init,                                           \
241 			NULL,                                           \
242 			&data_##n,                                      \
243 			&cfg_##n,                                       \
244 			POST_KERNEL,                                    \
245 			CONFIG_SPI_INIT_PRIORITY,                       \
246 			&api);
247 
248 DT_INST_FOREACH_STATUS_OKAY(SPI_INIT)
249