Commit | Line | Data |
---|---|---|
fda8d26e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b1353d1c LPC |
2 | /* |
3 | * SPI-Engine SPI controller driver | |
4 | * Copyright 2015 Analog Devices Inc. | |
5 | * Author: Lars-Peter Clausen <lars@metafoo.de> | |
b1353d1c LPC |
6 | */ |
7 | ||
8 | #include <linux/clk.h> | |
7f970ecb | 9 | #include <linux/idr.h> |
b1353d1c LPC |
10 | #include <linux/interrupt.h> |
11 | #include <linux/io.h> | |
12 | #include <linux/of.h> | |
13 | #include <linux/module.h> | |
14 | #include <linux/platform_device.h> | |
15 | #include <linux/spi/spi.h> | |
16 | ||
17 | #define SPI_ENGINE_VERSION_MAJOR(x) ((x >> 16) & 0xff) | |
18 | #define SPI_ENGINE_VERSION_MINOR(x) ((x >> 8) & 0xff) | |
19 | #define SPI_ENGINE_VERSION_PATCH(x) (x & 0xff) | |
20 | ||
21 | #define SPI_ENGINE_REG_VERSION 0x00 | |
22 | ||
23 | #define SPI_ENGINE_REG_RESET 0x40 | |
24 | ||
25 | #define SPI_ENGINE_REG_INT_ENABLE 0x80 | |
26 | #define SPI_ENGINE_REG_INT_PENDING 0x84 | |
27 | #define SPI_ENGINE_REG_INT_SOURCE 0x88 | |
28 | ||
29 | #define SPI_ENGINE_REG_SYNC_ID 0xc0 | |
30 | ||
31 | #define SPI_ENGINE_REG_CMD_FIFO_ROOM 0xd0 | |
32 | #define SPI_ENGINE_REG_SDO_FIFO_ROOM 0xd4 | |
33 | #define SPI_ENGINE_REG_SDI_FIFO_LEVEL 0xd8 | |
34 | ||
35 | #define SPI_ENGINE_REG_CMD_FIFO 0xe0 | |
36 | #define SPI_ENGINE_REG_SDO_DATA_FIFO 0xe4 | |
37 | #define SPI_ENGINE_REG_SDI_DATA_FIFO 0xe8 | |
38 | #define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK 0xec | |
39 | ||
40 | #define SPI_ENGINE_INT_CMD_ALMOST_EMPTY BIT(0) | |
41 | #define SPI_ENGINE_INT_SDO_ALMOST_EMPTY BIT(1) | |
42 | #define SPI_ENGINE_INT_SDI_ALMOST_FULL BIT(2) | |
43 | #define SPI_ENGINE_INT_SYNC BIT(3) | |
44 | ||
45 | #define SPI_ENGINE_CONFIG_CPHA BIT(0) | |
46 | #define SPI_ENGINE_CONFIG_CPOL BIT(1) | |
47 | #define SPI_ENGINE_CONFIG_3WIRE BIT(2) | |
48 | ||
49 | #define SPI_ENGINE_INST_TRANSFER 0x0 | |
50 | #define SPI_ENGINE_INST_ASSERT 0x1 | |
51 | #define SPI_ENGINE_INST_WRITE 0x2 | |
52 | #define SPI_ENGINE_INST_MISC 0x3 | |
53 | ||
54 | #define SPI_ENGINE_CMD_REG_CLK_DIV 0x0 | |
55 | #define SPI_ENGINE_CMD_REG_CONFIG 0x1 | |
56 | ||
57 | #define SPI_ENGINE_MISC_SYNC 0x0 | |
58 | #define SPI_ENGINE_MISC_SLEEP 0x1 | |
59 | ||
60 | #define SPI_ENGINE_TRANSFER_WRITE 0x1 | |
61 | #define SPI_ENGINE_TRANSFER_READ 0x2 | |
62 | ||
63 | #define SPI_ENGINE_CMD(inst, arg1, arg2) \ | |
64 | (((inst) << 12) | ((arg1) << 8) | (arg2)) | |
65 | ||
66 | #define SPI_ENGINE_CMD_TRANSFER(flags, n) \ | |
67 | SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, (flags), (n)) | |
68 | #define SPI_ENGINE_CMD_ASSERT(delay, cs) \ | |
69 | SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, (delay), (cs)) | |
70 | #define SPI_ENGINE_CMD_WRITE(reg, val) \ | |
71 | SPI_ENGINE_CMD(SPI_ENGINE_INST_WRITE, (reg), (val)) | |
72 | #define SPI_ENGINE_CMD_SLEEP(delay) \ | |
73 | SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay)) | |
74 | #define SPI_ENGINE_CMD_SYNC(id) \ | |
75 | SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id)) | |
76 | ||
77 | struct spi_engine_program { | |
78 | unsigned int length; | |
79 | uint16_t instructions[]; | |
80 | }; | |
81 | ||
7f970ecb DL |
82 | /** |
83 | * struct spi_engine_message_state - SPI engine per-message state | |
84 | */ | |
85 | struct spi_engine_message_state { | |
86 | /** Instructions for executing this message. */ | |
b1353d1c | 87 | struct spi_engine_program *p; |
7f970ecb | 88 | /** Number of elements in cmd_buf array. */ |
b1353d1c | 89 | unsigned cmd_length; |
7f970ecb | 90 | /** Array of commands not yet written to CMD FIFO. */ |
b1353d1c | 91 | const uint16_t *cmd_buf; |
7f970ecb | 92 | /** Next xfer with tx_buf not yet fully written to TX FIFO. */ |
b1353d1c | 93 | struct spi_transfer *tx_xfer; |
7f970ecb | 94 | /** Size of tx_buf in bytes. */ |
b1353d1c | 95 | unsigned int tx_length; |
7f970ecb | 96 | /** Bytes not yet written to TX FIFO. */ |
b1353d1c | 97 | const uint8_t *tx_buf; |
7f970ecb | 98 | /** Next xfer with rx_buf not yet fully written to RX FIFO. */ |
b1353d1c | 99 | struct spi_transfer *rx_xfer; |
7f970ecb | 100 | /** Size of tx_buf in bytes. */ |
b1353d1c | 101 | unsigned int rx_length; |
7f970ecb | 102 | /** Bytes not yet written to the RX FIFO. */ |
b1353d1c | 103 | uint8_t *rx_buf; |
7f970ecb DL |
104 | /** ID to correlate SYNC interrupts with this message. */ |
105 | u8 sync_id; | |
106 | }; | |
107 | ||
108 | struct spi_engine { | |
109 | struct clk *clk; | |
110 | struct clk *ref_clk; | |
111 | ||
112 | spinlock_t lock; | |
113 | ||
114 | void __iomem *base; | |
7f970ecb | 115 | struct ida sync_ida; |
b1353d1c LPC |
116 | |
117 | unsigned int int_enable; | |
118 | }; | |
119 | ||
120 | static void spi_engine_program_add_cmd(struct spi_engine_program *p, | |
121 | bool dry, uint16_t cmd) | |
122 | { | |
123 | if (!dry) | |
124 | p->instructions[p->length] = cmd; | |
125 | p->length++; | |
126 | } | |
127 | ||
128 | static unsigned int spi_engine_get_config(struct spi_device *spi) | |
129 | { | |
130 | unsigned int config = 0; | |
131 | ||
132 | if (spi->mode & SPI_CPOL) | |
133 | config |= SPI_ENGINE_CONFIG_CPOL; | |
134 | if (spi->mode & SPI_CPHA) | |
135 | config |= SPI_ENGINE_CONFIG_CPHA; | |
136 | if (spi->mode & SPI_3WIRE) | |
137 | config |= SPI_ENGINE_CONFIG_3WIRE; | |
138 | ||
139 | return config; | |
140 | } | |
141 | ||
142 | static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine, | |
143 | struct spi_device *spi, struct spi_transfer *xfer) | |
144 | { | |
145 | unsigned int clk_div; | |
146 | ||
147 | clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk), | |
148 | xfer->speed_hz * 2); | |
149 | if (clk_div > 255) | |
150 | clk_div = 255; | |
151 | else if (clk_div > 0) | |
152 | clk_div -= 1; | |
153 | ||
154 | return clk_div; | |
155 | } | |
156 | ||
157 | static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, | |
158 | struct spi_transfer *xfer) | |
159 | { | |
160 | unsigned int len = xfer->len; | |
161 | ||
162 | while (len) { | |
163 | unsigned int n = min(len, 256U); | |
164 | unsigned int flags = 0; | |
165 | ||
166 | if (xfer->tx_buf) | |
167 | flags |= SPI_ENGINE_TRANSFER_WRITE; | |
168 | if (xfer->rx_buf) | |
169 | flags |= SPI_ENGINE_TRANSFER_READ; | |
170 | ||
171 | spi_engine_program_add_cmd(p, dry, | |
172 | SPI_ENGINE_CMD_TRANSFER(flags, n - 1)); | |
173 | len -= n; | |
174 | } | |
175 | } | |
176 | ||
177 | static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry, | |
acc7720d AA |
178 | struct spi_engine *spi_engine, unsigned int clk_div, |
179 | struct spi_transfer *xfer) | |
b1353d1c LPC |
180 | { |
181 | unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk); | |
182 | unsigned int t; | |
acc7720d AA |
183 | int delay; |
184 | ||
93c94144 AA |
185 | delay = spi_delay_to_ns(&xfer->delay, xfer); |
186 | if (delay < 0) | |
187 | return; | |
188 | delay /= 1000; | |
b1353d1c LPC |
189 | |
190 | if (delay == 0) | |
191 | return; | |
192 | ||
193 | t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2); | |
194 | while (t) { | |
195 | unsigned int n = min(t, 256U); | |
196 | ||
197 | spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1)); | |
198 | t -= n; | |
199 | } | |
200 | } | |
201 | ||
202 | static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry, | |
203 | struct spi_device *spi, bool assert) | |
204 | { | |
205 | unsigned int mask = 0xff; | |
206 | ||
207 | if (assert) | |
9e264f3f | 208 | mask ^= BIT(spi_get_chipselect(spi, 0)); |
b1353d1c LPC |
209 | |
210 | spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask)); | |
211 | } | |
212 | ||
213 | static int spi_engine_compile_message(struct spi_engine *spi_engine, | |
214 | struct spi_message *msg, bool dry, struct spi_engine_program *p) | |
215 | { | |
216 | struct spi_device *spi = msg->spi; | |
217 | struct spi_transfer *xfer; | |
218 | int clk_div, new_clk_div; | |
145bb2ae | 219 | bool keep_cs = false; |
b1353d1c LPC |
220 | |
221 | clk_div = -1; | |
222 | ||
223 | spi_engine_program_add_cmd(p, dry, | |
224 | SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, | |
225 | spi_engine_get_config(spi))); | |
226 | ||
145bb2ae DL |
227 | xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); |
228 | spi_engine_gen_cs(p, dry, spi, !xfer->cs_off); | |
229 | ||
b1353d1c LPC |
230 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { |
231 | new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer); | |
232 | if (new_clk_div != clk_div) { | |
233 | clk_div = new_clk_div; | |
234 | spi_engine_program_add_cmd(p, dry, | |
235 | SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, | |
236 | clk_div)); | |
237 | } | |
238 | ||
b1353d1c | 239 | spi_engine_gen_xfer(p, dry, xfer); |
acc7720d | 240 | spi_engine_gen_sleep(p, dry, spi_engine, clk_div, xfer); |
b1353d1c | 241 | |
145bb2ae DL |
242 | if (xfer->cs_change) { |
243 | if (list_is_last(&xfer->transfer_list, &msg->transfers)) { | |
244 | keep_cs = true; | |
245 | } else { | |
246 | if (!xfer->cs_off) | |
247 | spi_engine_gen_cs(p, dry, spi, false); | |
248 | ||
249 | if (!list_next_entry(xfer, transfer_list)->cs_off) | |
250 | spi_engine_gen_cs(p, dry, spi, true); | |
251 | } | |
252 | } else if (!list_is_last(&xfer->transfer_list, &msg->transfers) && | |
253 | xfer->cs_off != list_next_entry(xfer, transfer_list)->cs_off) { | |
254 | spi_engine_gen_cs(p, dry, spi, xfer->cs_off); | |
255 | } | |
b1353d1c LPC |
256 | } |
257 | ||
145bb2ae DL |
258 | if (!keep_cs) |
259 | spi_engine_gen_cs(p, dry, spi, false); | |
260 | ||
b1353d1c LPC |
261 | return 0; |
262 | } | |
263 | ||
4e991445 | 264 | static void spi_engine_xfer_next(struct spi_message *msg, |
b1353d1c LPC |
265 | struct spi_transfer **_xfer) |
266 | { | |
b1353d1c LPC |
267 | struct spi_transfer *xfer = *_xfer; |
268 | ||
269 | if (!xfer) { | |
270 | xfer = list_first_entry(&msg->transfers, | |
271 | struct spi_transfer, transfer_list); | |
272 | } else if (list_is_last(&xfer->transfer_list, &msg->transfers)) { | |
273 | xfer = NULL; | |
274 | } else { | |
275 | xfer = list_next_entry(xfer, transfer_list); | |
276 | } | |
277 | ||
278 | *_xfer = xfer; | |
279 | } | |
280 | ||
4e991445 | 281 | static void spi_engine_tx_next(struct spi_message *msg) |
b1353d1c | 282 | { |
4e991445 | 283 | struct spi_engine_message_state *st = msg->state; |
7f970ecb | 284 | struct spi_transfer *xfer = st->tx_xfer; |
b1353d1c LPC |
285 | |
286 | do { | |
4e991445 | 287 | spi_engine_xfer_next(msg, &xfer); |
b1353d1c LPC |
288 | } while (xfer && !xfer->tx_buf); |
289 | ||
7f970ecb | 290 | st->tx_xfer = xfer; |
b1353d1c | 291 | if (xfer) { |
7f970ecb DL |
292 | st->tx_length = xfer->len; |
293 | st->tx_buf = xfer->tx_buf; | |
b1353d1c | 294 | } else { |
7f970ecb | 295 | st->tx_buf = NULL; |
b1353d1c LPC |
296 | } |
297 | } | |
298 | ||
4e991445 | 299 | static void spi_engine_rx_next(struct spi_message *msg) |
b1353d1c | 300 | { |
4e991445 | 301 | struct spi_engine_message_state *st = msg->state; |
7f970ecb | 302 | struct spi_transfer *xfer = st->rx_xfer; |
b1353d1c LPC |
303 | |
304 | do { | |
4e991445 | 305 | spi_engine_xfer_next(msg, &xfer); |
b1353d1c LPC |
306 | } while (xfer && !xfer->rx_buf); |
307 | ||
7f970ecb | 308 | st->rx_xfer = xfer; |
b1353d1c | 309 | if (xfer) { |
7f970ecb DL |
310 | st->rx_length = xfer->len; |
311 | st->rx_buf = xfer->rx_buf; | |
b1353d1c | 312 | } else { |
7f970ecb | 313 | st->rx_buf = NULL; |
b1353d1c LPC |
314 | } |
315 | } | |
316 | ||
4e991445 DL |
317 | static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine, |
318 | struct spi_message *msg) | |
b1353d1c LPC |
319 | { |
320 | void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO; | |
4e991445 | 321 | struct spi_engine_message_state *st = msg->state; |
b1353d1c LPC |
322 | unsigned int n, m, i; |
323 | const uint16_t *buf; | |
324 | ||
325 | n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM); | |
7f970ecb DL |
326 | while (n && st->cmd_length) { |
327 | m = min(n, st->cmd_length); | |
328 | buf = st->cmd_buf; | |
b1353d1c LPC |
329 | for (i = 0; i < m; i++) |
330 | writel_relaxed(buf[i], addr); | |
7f970ecb DL |
331 | st->cmd_buf += m; |
332 | st->cmd_length -= m; | |
b1353d1c LPC |
333 | n -= m; |
334 | } | |
335 | ||
7f970ecb | 336 | return st->cmd_length != 0; |
b1353d1c LPC |
337 | } |
338 | ||
4e991445 DL |
339 | static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine, |
340 | struct spi_message *msg) | |
b1353d1c LPC |
341 | { |
342 | void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO; | |
4e991445 | 343 | struct spi_engine_message_state *st = msg->state; |
b1353d1c LPC |
344 | unsigned int n, m, i; |
345 | const uint8_t *buf; | |
346 | ||
347 | n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM); | |
7f970ecb DL |
348 | while (n && st->tx_length) { |
349 | m = min(n, st->tx_length); | |
350 | buf = st->tx_buf; | |
b1353d1c LPC |
351 | for (i = 0; i < m; i++) |
352 | writel_relaxed(buf[i], addr); | |
7f970ecb DL |
353 | st->tx_buf += m; |
354 | st->tx_length -= m; | |
b1353d1c | 355 | n -= m; |
7f970ecb | 356 | if (st->tx_length == 0) |
4e991445 | 357 | spi_engine_tx_next(msg); |
b1353d1c LPC |
358 | } |
359 | ||
7f970ecb | 360 | return st->tx_length != 0; |
b1353d1c LPC |
361 | } |
362 | ||
4e991445 DL |
363 | static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine, |
364 | struct spi_message *msg) | |
b1353d1c LPC |
365 | { |
366 | void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO; | |
4e991445 | 367 | struct spi_engine_message_state *st = msg->state; |
b1353d1c LPC |
368 | unsigned int n, m, i; |
369 | uint8_t *buf; | |
370 | ||
371 | n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL); | |
7f970ecb DL |
372 | while (n && st->rx_length) { |
373 | m = min(n, st->rx_length); | |
374 | buf = st->rx_buf; | |
b1353d1c LPC |
375 | for (i = 0; i < m; i++) |
376 | buf[i] = readl_relaxed(addr); | |
7f970ecb DL |
377 | st->rx_buf += m; |
378 | st->rx_length -= m; | |
b1353d1c | 379 | n -= m; |
7f970ecb | 380 | if (st->rx_length == 0) |
4e991445 | 381 | spi_engine_rx_next(msg); |
b1353d1c LPC |
382 | } |
383 | ||
7f970ecb | 384 | return st->rx_length != 0; |
b1353d1c LPC |
385 | } |
386 | ||
387 | static irqreturn_t spi_engine_irq(int irq, void *devid) | |
388 | { | |
9d5920b3 | 389 | struct spi_controller *host = devid; |
4e991445 | 390 | struct spi_message *msg = host->cur_msg; |
9d5920b3 | 391 | struct spi_engine *spi_engine = spi_controller_get_devdata(host); |
b1353d1c LPC |
392 | unsigned int disable_int = 0; |
393 | unsigned int pending; | |
4a074dde | 394 | int completed_id = -1; |
b1353d1c LPC |
395 | |
396 | pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING); | |
397 | ||
398 | if (pending & SPI_ENGINE_INT_SYNC) { | |
399 | writel_relaxed(SPI_ENGINE_INT_SYNC, | |
400 | spi_engine->base + SPI_ENGINE_REG_INT_PENDING); | |
4a074dde | 401 | completed_id = readl_relaxed( |
b1353d1c LPC |
402 | spi_engine->base + SPI_ENGINE_REG_SYNC_ID); |
403 | } | |
404 | ||
405 | spin_lock(&spi_engine->lock); | |
406 | ||
407 | if (pending & SPI_ENGINE_INT_CMD_ALMOST_EMPTY) { | |
4e991445 | 408 | if (!spi_engine_write_cmd_fifo(spi_engine, msg)) |
b1353d1c LPC |
409 | disable_int |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY; |
410 | } | |
411 | ||
412 | if (pending & SPI_ENGINE_INT_SDO_ALMOST_EMPTY) { | |
4e991445 | 413 | if (!spi_engine_write_tx_fifo(spi_engine, msg)) |
b1353d1c LPC |
414 | disable_int |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY; |
415 | } | |
416 | ||
417 | if (pending & (SPI_ENGINE_INT_SDI_ALMOST_FULL | SPI_ENGINE_INT_SYNC)) { | |
4e991445 | 418 | if (!spi_engine_read_rx_fifo(spi_engine, msg)) |
b1353d1c LPC |
419 | disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL; |
420 | } | |
421 | ||
4e991445 DL |
422 | if (pending & SPI_ENGINE_INT_SYNC && msg) { |
423 | struct spi_engine_message_state *st = msg->state; | |
7f970ecb | 424 | |
4a074dde | 425 | if (completed_id == st->sync_id) { |
b1353d1c LPC |
426 | msg->status = 0; |
427 | msg->actual_length = msg->frame_length; | |
9d5920b3 | 428 | spi_finalize_current_message(host); |
b1353d1c LPC |
429 | disable_int |= SPI_ENGINE_INT_SYNC; |
430 | } | |
431 | } | |
432 | ||
433 | if (disable_int) { | |
434 | spi_engine->int_enable &= ~disable_int; | |
435 | writel_relaxed(spi_engine->int_enable, | |
436 | spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); | |
437 | } | |
438 | ||
439 | spin_unlock(&spi_engine->lock); | |
440 | ||
441 | return IRQ_HANDLED; | |
442 | } | |
443 | ||
0c74de5c DL |
444 | static int spi_engine_prepare_message(struct spi_controller *host, |
445 | struct spi_message *msg) | |
b1353d1c LPC |
446 | { |
447 | struct spi_engine_program p_dry, *p; | |
9d5920b3 | 448 | struct spi_engine *spi_engine = spi_controller_get_devdata(host); |
7f970ecb | 449 | struct spi_engine_message_state *st; |
b1353d1c | 450 | size_t size; |
7f970ecb DL |
451 | int ret; |
452 | ||
453 | st = kzalloc(sizeof(*st), GFP_KERNEL); | |
454 | if (!st) | |
455 | return -ENOMEM; | |
b1353d1c LPC |
456 | |
457 | p_dry.length = 0; | |
458 | spi_engine_compile_message(spi_engine, msg, true, &p_dry); | |
459 | ||
460 | size = sizeof(*p->instructions) * (p_dry.length + 1); | |
461 | p = kzalloc(sizeof(*p) + size, GFP_KERNEL); | |
7f970ecb DL |
462 | if (!p) { |
463 | kfree(st); | |
b1353d1c | 464 | return -ENOMEM; |
7f970ecb DL |
465 | } |
466 | ||
467 | ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL); | |
468 | if (ret < 0) { | |
469 | kfree(p); | |
470 | kfree(st); | |
471 | return ret; | |
472 | } | |
473 | ||
474 | st->sync_id = ret; | |
475 | ||
b1353d1c LPC |
476 | spi_engine_compile_message(spi_engine, msg, false, p); |
477 | ||
7f970ecb | 478 | spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id)); |
b1353d1c | 479 | |
7f970ecb | 480 | st->p = p; |
7f970ecb DL |
481 | st->cmd_buf = p->instructions; |
482 | st->cmd_length = p->length; | |
0c74de5c DL |
483 | msg->state = st; |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
488 | static int spi_engine_unprepare_message(struct spi_controller *host, | |
489 | struct spi_message *msg) | |
490 | { | |
491 | struct spi_engine *spi_engine = spi_controller_get_devdata(host); | |
492 | struct spi_engine_message_state *st = msg->state; | |
493 | ||
494 | ida_free(&spi_engine->sync_ida, st->sync_id); | |
495 | kfree(st->p); | |
496 | kfree(st); | |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
501 | static int spi_engine_transfer_one_message(struct spi_controller *host, | |
502 | struct spi_message *msg) | |
503 | { | |
504 | struct spi_engine *spi_engine = spi_controller_get_devdata(host); | |
505 | struct spi_engine_message_state *st = msg->state; | |
506 | unsigned int int_enable = 0; | |
507 | unsigned long flags; | |
508 | ||
509 | spin_lock_irqsave(&spi_engine->lock, flags); | |
510 | ||
4e991445 | 511 | if (spi_engine_write_cmd_fifo(spi_engine, msg)) |
b1353d1c LPC |
512 | int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY; |
513 | ||
4e991445 DL |
514 | spi_engine_tx_next(msg); |
515 | if (spi_engine_write_tx_fifo(spi_engine, msg)) | |
b1353d1c LPC |
516 | int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY; |
517 | ||
4e991445 | 518 | spi_engine_rx_next(msg); |
7f970ecb | 519 | if (st->rx_length != 0) |
b1353d1c LPC |
520 | int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL; |
521 | ||
522 | int_enable |= SPI_ENGINE_INT_SYNC; | |
523 | ||
524 | writel_relaxed(int_enable, | |
525 | spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); | |
526 | spi_engine->int_enable = int_enable; | |
527 | spin_unlock_irqrestore(&spi_engine->lock, flags); | |
528 | ||
529 | return 0; | |
530 | } | |
531 | ||
e094de13 DL |
532 | static void spi_engine_release_hw(void *p) |
533 | { | |
534 | struct spi_engine *spi_engine = p; | |
535 | ||
536 | writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); | |
537 | writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); | |
538 | writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET); | |
539 | } | |
540 | ||
b1353d1c LPC |
541 | static int spi_engine_probe(struct platform_device *pdev) |
542 | { | |
543 | struct spi_engine *spi_engine; | |
9d5920b3 | 544 | struct spi_controller *host; |
b1353d1c | 545 | unsigned int version; |
b1353d1c LPC |
546 | int irq; |
547 | int ret; | |
548 | ||
549 | irq = platform_get_irq(pdev, 0); | |
8102d64c RJ |
550 | if (irq < 0) |
551 | return irq; | |
b1353d1c | 552 | |
e12cd96e | 553 | host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi_engine)); |
9d5920b3 | 554 | if (!host) |
b1353d1c LPC |
555 | return -ENOMEM; |
556 | ||
9e4ce522 | 557 | spi_engine = spi_controller_get_devdata(host); |
b1353d1c LPC |
558 | |
559 | spin_lock_init(&spi_engine->lock); | |
7f970ecb | 560 | ida_init(&spi_engine->sync_ida); |
b1353d1c | 561 | |
a08199b3 | 562 | spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); |
e12cd96e DL |
563 | if (IS_ERR(spi_engine->clk)) |
564 | return PTR_ERR(spi_engine->clk); | |
b1353d1c | 565 | |
a08199b3 | 566 | spi_engine->ref_clk = devm_clk_get_enabled(&pdev->dev, "spi_clk"); |
e12cd96e DL |
567 | if (IS_ERR(spi_engine->ref_clk)) |
568 | return PTR_ERR(spi_engine->ref_clk); | |
b1353d1c | 569 | |
d103729a | 570 | spi_engine->base = devm_platform_ioremap_resource(pdev, 0); |
e12cd96e DL |
571 | if (IS_ERR(spi_engine->base)) |
572 | return PTR_ERR(spi_engine->base); | |
d103729a RH |
573 | |
574 | version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION); | |
575 | if (SPI_ENGINE_VERSION_MAJOR(version) != 1) { | |
576 | dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n", | |
577 | SPI_ENGINE_VERSION_MAJOR(version), | |
578 | SPI_ENGINE_VERSION_MINOR(version), | |
579 | SPI_ENGINE_VERSION_PATCH(version)); | |
e12cd96e | 580 | return -ENODEV; |
d103729a RH |
581 | } |
582 | ||
b1353d1c LPC |
583 | writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); |
584 | writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); | |
585 | writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); | |
586 | ||
e094de13 DL |
587 | ret = devm_add_action_or_reset(&pdev->dev, spi_engine_release_hw, |
588 | spi_engine); | |
589 | if (ret) | |
590 | return ret; | |
591 | ||
076f32d5 DL |
592 | ret = devm_request_irq(&pdev->dev, irq, spi_engine_irq, 0, pdev->name, |
593 | host); | |
b1353d1c | 594 | if (ret) |
e12cd96e | 595 | return ret; |
b1353d1c | 596 | |
9d5920b3 YY |
597 | host->dev.of_node = pdev->dev.of_node; |
598 | host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE; | |
599 | host->bits_per_word_mask = SPI_BPW_MASK(8); | |
600 | host->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2; | |
601 | host->transfer_one_message = spi_engine_transfer_one_message; | |
0c74de5c DL |
602 | host->prepare_message = spi_engine_prepare_message; |
603 | host->unprepare_message = spi_engine_unprepare_message; | |
9d5920b3 | 604 | host->num_chipselect = 8; |
b1353d1c | 605 | |
e6d5eb85 DL |
606 | if (host->max_speed_hz == 0) |
607 | return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0"); | |
608 | ||
e16e71e3 | 609 | ret = devm_spi_register_controller(&pdev->dev, host); |
b1353d1c | 610 | if (ret) |
076f32d5 | 611 | return ret; |
b1353d1c | 612 | |
9d5920b3 | 613 | platform_set_drvdata(pdev, host); |
b1353d1c LPC |
614 | |
615 | return 0; | |
b1353d1c LPC |
616 | } |
617 | ||
b1353d1c LPC |
618 | static const struct of_device_id spi_engine_match_table[] = { |
619 | { .compatible = "adi,axi-spi-engine-1.00.a" }, | |
620 | { }, | |
621 | }; | |
01affe23 | 622 | MODULE_DEVICE_TABLE(of, spi_engine_match_table); |
b1353d1c LPC |
623 | |
624 | static struct platform_driver spi_engine_driver = { | |
625 | .probe = spi_engine_probe, | |
b1353d1c LPC |
626 | .driver = { |
627 | .name = "spi-engine", | |
628 | .of_match_table = spi_engine_match_table, | |
629 | }, | |
630 | }; | |
631 | module_platform_driver(spi_engine_driver); | |
632 | ||
633 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); | |
634 | MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver"); | |
635 | MODULE_LICENSE("GPL"); |