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