aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLars-Peter Clausen <lars@metafoo.de>2016-02-04 11:13:30 -0500
committerMark Brown <broonie@kernel.org>2016-02-05 13:12:57 -0500
commitb1353d1c1d4555b7c40066fa2cacc7da266e9904 (patch)
tree9312609fe10097a2d44da7f5a813d75dc5c6017a
parent2ec3b6287b12a7131c28cd9408b368cd451bdc48 (diff)
spi: Add Analog Devices AXI SPI Engine controller support
This patch adds support for the AXI SPI Engine controller which is a FPGA soft-peripheral which is used in some of Analog Devices' reference designs. The AXI SPI Engine controller is part of the SPI Engine framework[1] and allows memory mapped access to the SPI Engine control bus. This allows it to be used as a general purpose software driven SPI controller. The SPI Engine in addition offers some optional advanced acceleration and offloading capabilities, which are not part of this patch though and will be introduced separately. At the core of the SPI Engine framework is a small sort of co-processor that accepts a command stream and turns the commands into low-level SPI transactions. Communication is done through three memory mapped FIFOs in the register map of the AXI SPI Engine peripheral. One FIFO for the command stream and one each for transmit and receive data. The driver translates a spi_message in a command stream and writes it to the peripheral which executes it asynchronously. This allows it to perform very precise timings which are required for some SPI slave devices to achieve maximum performance (e.g. analog-to-digital and digital-to-analog converters). The execution flow is synchronized to the host system by a special synchronize instruction which generates a interrupt. [1] https://wiki.analog.com/resources/fpga/peripherals/spi_engine Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/spi/Kconfig8
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-axi-spi-engine.c591
3 files changed, 600 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 7caf18cb3e5e..d84adceb9b89 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -83,6 +83,14 @@ config SPI_AU1550
83 If you say yes to this option, support will be included for the 83 If you say yes to this option, support will be included for the
84 PSC SPI controller found on Au1550, Au1200 and Au1300 series. 84 PSC SPI controller found on Au1550, Au1200 and Au1300 series.
85 85
86config SPI_AXI_SPI_ENGINE
87 tristate "Analog Devices AXI SPI Engine controller"
88 depends on HAS_IOMEM
89 help
90 This enables support for the Analog Devices AXI SPI Engine SPI controller.
91 It is part of the SPI Engine framework that is used in some Analog Devices
92 reference designs for FPGAs.
93
86config SPI_BCM2835 94config SPI_BCM2835
87 tristate "BCM2835 SPI controller" 95 tristate "BCM2835 SPI controller"
88 depends on GPIOLIB 96 depends on GPIOLIB
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 8991ffce6e12..7ad880684a85 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
15obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o 15obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
16obj-$(CONFIG_SPI_ATH79) += spi-ath79.o 16obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
17obj-$(CONFIG_SPI_AU1550) += spi-au1550.o 17obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
18obj-$(CONFIG_SPI_AXI_SPI_ENGINE) += spi-axi-spi-engine.o
18obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o 19obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
19obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o 20obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o
20obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o 21obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c
new file mode 100644
index 000000000000..c968ab210a51
--- /dev/null
+++ b/drivers/spi/spi-axi-spi-engine.c
@@ -0,0 +1,591 @@
1/*
2 * SPI-Engine SPI controller driver
3 * Copyright 2015 Analog Devices Inc.
4 * Author: Lars-Peter Clausen <lars@metafoo.de>
5 *
6 * Licensed under the GPL-2.
7 */
8
9#include <linux/clk.h>
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
77struct spi_engine_program {
78 unsigned int length;
79 uint16_t instructions[];
80};
81
82struct spi_engine {
83 struct clk *clk;
84 struct clk *ref_clk;
85
86 spinlock_t lock;
87
88 void __iomem *base;
89
90 struct spi_message *msg;
91 struct spi_engine_program *p;
92 unsigned cmd_length;
93 const uint16_t *cmd_buf;
94
95 struct spi_transfer *tx_xfer;
96 unsigned int tx_length;
97 const uint8_t *tx_buf;
98
99 struct spi_transfer *rx_xfer;
100 unsigned int rx_length;
101 uint8_t *rx_buf;
102
103 unsigned int sync_id;
104 unsigned int completed_id;
105
106 unsigned int int_enable;
107};
108
109static void spi_engine_program_add_cmd(struct spi_engine_program *p,
110 bool dry, uint16_t cmd)
111{
112 if (!dry)
113 p->instructions[p->length] = cmd;
114 p->length++;
115}
116
117static unsigned int spi_engine_get_config(struct spi_device *spi)
118{
119 unsigned int config = 0;
120
121 if (spi->mode & SPI_CPOL)
122 config |= SPI_ENGINE_CONFIG_CPOL;
123 if (spi->mode & SPI_CPHA)
124 config |= SPI_ENGINE_CONFIG_CPHA;
125 if (spi->mode & SPI_3WIRE)
126 config |= SPI_ENGINE_CONFIG_3WIRE;
127
128 return config;
129}
130
131static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine,
132 struct spi_device *spi, struct spi_transfer *xfer)
133{
134 unsigned int clk_div;
135
136 clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk),
137 xfer->speed_hz * 2);
138 if (clk_div > 255)
139 clk_div = 255;
140 else if (clk_div > 0)
141 clk_div -= 1;
142
143 return clk_div;
144}
145
146static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry,
147 struct spi_transfer *xfer)
148{
149 unsigned int len = xfer->len;
150
151 while (len) {
152 unsigned int n = min(len, 256U);
153 unsigned int flags = 0;
154
155 if (xfer->tx_buf)
156 flags |= SPI_ENGINE_TRANSFER_WRITE;
157 if (xfer->rx_buf)
158 flags |= SPI_ENGINE_TRANSFER_READ;
159
160 spi_engine_program_add_cmd(p, dry,
161 SPI_ENGINE_CMD_TRANSFER(flags, n - 1));
162 len -= n;
163 }
164}
165
166static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry,
167 struct spi_engine *spi_engine, unsigned int clk_div, unsigned int delay)
168{
169 unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk);
170 unsigned int t;
171
172 if (delay == 0)
173 return;
174
175 t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2);
176 while (t) {
177 unsigned int n = min(t, 256U);
178
179 spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1));
180 t -= n;
181 }
182}
183
184static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry,
185 struct spi_device *spi, bool assert)
186{
187 unsigned int mask = 0xff;
188
189 if (assert)
190 mask ^= BIT(spi->chip_select);
191
192 spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask));
193}
194
195static int spi_engine_compile_message(struct spi_engine *spi_engine,
196 struct spi_message *msg, bool dry, struct spi_engine_program *p)
197{
198 struct spi_device *spi = msg->spi;
199 struct spi_transfer *xfer;
200 int clk_div, new_clk_div;
201 bool cs_change = true;
202
203 clk_div = -1;
204
205 spi_engine_program_add_cmd(p, dry,
206 SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG,
207 spi_engine_get_config(spi)));
208
209 list_for_each_entry(xfer, &msg->transfers, transfer_list) {
210 new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer);
211 if (new_clk_div != clk_div) {
212 clk_div = new_clk_div;
213 spi_engine_program_add_cmd(p, dry,
214 SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV,
215 clk_div));
216 }
217
218 if (cs_change)
219 spi_engine_gen_cs(p, dry, spi, true);
220
221 spi_engine_gen_xfer(p, dry, xfer);
222 spi_engine_gen_sleep(p, dry, spi_engine, clk_div,
223 xfer->delay_usecs);
224
225 cs_change = xfer->cs_change;
226 if (list_is_last(&xfer->transfer_list, &msg->transfers))
227 cs_change = !cs_change;
228
229 if (cs_change)
230 spi_engine_gen_cs(p, dry, spi, false);
231 }
232
233 return 0;
234}
235
236static void spi_engine_xfer_next(struct spi_engine *spi_engine,
237 struct spi_transfer **_xfer)
238{
239 struct spi_message *msg = spi_engine->msg;
240 struct spi_transfer *xfer = *_xfer;
241
242 if (!xfer) {
243 xfer = list_first_entry(&msg->transfers,
244 struct spi_transfer, transfer_list);
245 } else if (list_is_last(&xfer->transfer_list, &msg->transfers)) {
246 xfer = NULL;
247 } else {
248 xfer = list_next_entry(xfer, transfer_list);
249 }
250
251 *_xfer = xfer;
252}
253
254static void spi_engine_tx_next(struct spi_engine *spi_engine)
255{
256 struct spi_transfer *xfer = spi_engine->tx_xfer;
257
258 do {
259 spi_engine_xfer_next(spi_engine, &xfer);
260 } while (xfer && !xfer->tx_buf);
261
262 spi_engine->tx_xfer = xfer;
263 if (xfer) {
264 spi_engine->tx_length = xfer->len;
265 spi_engine->tx_buf = xfer->tx_buf;
266 } else {
267 spi_engine->tx_buf = NULL;
268 }
269}
270
271static void spi_engine_rx_next(struct spi_engine *spi_engine)
272{
273 struct spi_transfer *xfer = spi_engine->rx_xfer;
274
275 do {
276 spi_engine_xfer_next(spi_engine, &xfer);
277 } while (xfer && !xfer->rx_buf);
278
279 spi_engine->rx_xfer = xfer;
280 if (xfer) {
281 spi_engine->rx_length = xfer->len;
282 spi_engine->rx_buf = xfer->rx_buf;
283 } else {
284 spi_engine->rx_buf = NULL;
285 }
286}
287
288static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine)
289{
290 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO;
291 unsigned int n, m, i;
292 const uint16_t *buf;
293
294 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM);
295 while (n && spi_engine->cmd_length) {
296 m = min(n, spi_engine->cmd_length);
297 buf = spi_engine->cmd_buf;
298 for (i = 0; i < m; i++)
299 writel_relaxed(buf[i], addr);
300 spi_engine->cmd_buf += m;
301 spi_engine->cmd_length -= m;
302 n -= m;
303 }
304
305 return spi_engine->cmd_length != 0;
306}
307
308static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine)
309{
310 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO;
311 unsigned int n, m, i;
312 const uint8_t *buf;
313
314 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM);
315 while (n && spi_engine->tx_length) {
316 m = min(n, spi_engine->tx_length);
317 buf = spi_engine->tx_buf;
318 for (i = 0; i < m; i++)
319 writel_relaxed(buf[i], addr);
320 spi_engine->tx_buf += m;
321 spi_engine->tx_length -= m;
322 n -= m;
323 if (spi_engine->tx_length == 0)
324 spi_engine_tx_next(spi_engine);
325 }
326
327 return spi_engine->tx_length != 0;
328}
329
330static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine)
331{
332 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO;
333 unsigned int n, m, i;
334 uint8_t *buf;
335
336 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL);
337 while (n && spi_engine->rx_length) {
338 m = min(n, spi_engine->rx_length);
339 buf = spi_engine->rx_buf;
340 for (i = 0; i < m; i++)
341 buf[i] = readl_relaxed(addr);
342 spi_engine->rx_buf += m;
343 spi_engine->rx_length -= m;
344 n -= m;
345 if (spi_engine->rx_length == 0)
346 spi_engine_rx_next(spi_engine);
347 }
348
349 return spi_engine->rx_length != 0;
350}
351
352static irqreturn_t spi_engine_irq(int irq, void *devid)
353{
354 struct spi_master *master = devid;
355 struct spi_engine *spi_engine = spi_master_get_devdata(master);
356 unsigned int disable_int = 0;
357 unsigned int pending;
358
359 pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
360
361 if (pending & SPI_ENGINE_INT_SYNC) {
362 writel_relaxed(SPI_ENGINE_INT_SYNC,
363 spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
364 spi_engine->completed_id = readl_relaxed(
365 spi_engine->base + SPI_ENGINE_REG_SYNC_ID);
366 }
367
368 spin_lock(&spi_engine->lock);
369
370 if (pending & SPI_ENGINE_INT_CMD_ALMOST_EMPTY) {
371 if (!spi_engine_write_cmd_fifo(spi_engine))
372 disable_int |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
373 }
374
375 if (pending & SPI_ENGINE_INT_SDO_ALMOST_EMPTY) {
376 if (!spi_engine_write_tx_fifo(spi_engine))
377 disable_int |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
378 }
379
380 if (pending & (SPI_ENGINE_INT_SDI_ALMOST_FULL | SPI_ENGINE_INT_SYNC)) {
381 if (!spi_engine_read_rx_fifo(spi_engine))
382 disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
383 }
384
385 if (pending & SPI_ENGINE_INT_SYNC) {
386 if (spi_engine->msg &&
387 spi_engine->completed_id == spi_engine->sync_id) {
388 struct spi_message *msg = spi_engine->msg;
389
390 kfree(spi_engine->p);
391 msg->status = 0;
392 msg->actual_length = msg->frame_length;
393 spi_engine->msg = NULL;
394 spi_finalize_current_message(master);
395 disable_int |= SPI_ENGINE_INT_SYNC;
396 }
397 }
398
399 if (disable_int) {
400 spi_engine->int_enable &= ~disable_int;
401 writel_relaxed(spi_engine->int_enable,
402 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
403 }
404
405 spin_unlock(&spi_engine->lock);
406
407 return IRQ_HANDLED;
408}
409
410static int spi_engine_transfer_one_message(struct spi_master *master,
411 struct spi_message *msg)
412{
413 struct spi_engine_program p_dry, *p;
414 struct spi_engine *spi_engine = spi_master_get_devdata(master);
415 unsigned int int_enable = 0;
416 unsigned long flags;
417 size_t size;
418
419 p_dry.length = 0;
420 spi_engine_compile_message(spi_engine, msg, true, &p_dry);
421
422 size = sizeof(*p->instructions) * (p_dry.length + 1);
423 p = kzalloc(sizeof(*p) + size, GFP_KERNEL);
424 if (!p)
425 return -ENOMEM;
426 spi_engine_compile_message(spi_engine, msg, false, p);
427
428 spin_lock_irqsave(&spi_engine->lock, flags);
429 spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff;
430 spi_engine_program_add_cmd(p, false,
431 SPI_ENGINE_CMD_SYNC(spi_engine->sync_id));
432
433 spi_engine->msg = msg;
434 spi_engine->p = p;
435
436 spi_engine->cmd_buf = p->instructions;
437 spi_engine->cmd_length = p->length;
438 if (spi_engine_write_cmd_fifo(spi_engine))
439 int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY;
440
441 spi_engine_tx_next(spi_engine);
442 if (spi_engine_write_tx_fifo(spi_engine))
443 int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY;
444
445 spi_engine_rx_next(spi_engine);
446 if (spi_engine->rx_length != 0)
447 int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL;
448
449 int_enable |= SPI_ENGINE_INT_SYNC;
450
451 writel_relaxed(int_enable,
452 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
453 spi_engine->int_enable = int_enable;
454 spin_unlock_irqrestore(&spi_engine->lock, flags);
455
456 return 0;
457}
458
459static int spi_engine_probe(struct platform_device *pdev)
460{
461 struct spi_engine *spi_engine;
462 struct spi_master *master;
463 unsigned int version;
464 struct resource *res;
465 int irq;
466 int ret;
467
468 irq = platform_get_irq(pdev, 0);
469 if (irq <= 0)
470 return -ENXIO;
471
472 spi_engine = devm_kzalloc(&pdev->dev, sizeof(*spi_engine), GFP_KERNEL);
473 if (!spi_engine)
474 return -ENOMEM;
475
476 master = spi_alloc_master(&pdev->dev, 0);
477 if (!master)
478 return -ENOMEM;
479
480 spi_master_set_devdata(master, spi_engine);
481
482 spin_lock_init(&spi_engine->lock);
483
484 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
485 spi_engine->base = devm_ioremap_resource(&pdev->dev, res);
486 if (IS_ERR(spi_engine->base)) {
487 ret = PTR_ERR(spi_engine->base);
488 goto err_put_master;
489 }
490
491 version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION);
492 if (SPI_ENGINE_VERSION_MAJOR(version) != 1) {
493 dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n",
494 SPI_ENGINE_VERSION_MAJOR(version),
495 SPI_ENGINE_VERSION_MINOR(version),
496 SPI_ENGINE_VERSION_PATCH(version));
497 return -ENODEV;
498 }
499
500 spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
501 if (IS_ERR(spi_engine->clk)) {
502 ret = PTR_ERR(spi_engine->clk);
503 goto err_put_master;
504 }
505
506 spi_engine->ref_clk = devm_clk_get(&pdev->dev, "spi_clk");
507 if (IS_ERR(spi_engine->ref_clk)) {
508 ret = PTR_ERR(spi_engine->ref_clk);
509 goto err_put_master;
510 }
511
512 ret = clk_prepare_enable(spi_engine->clk);
513 if (ret)
514 goto err_put_master;
515
516 ret = clk_prepare_enable(spi_engine->ref_clk);
517 if (ret)
518 goto err_clk_disable;
519
520 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET);
521 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
522 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
523
524 ret = request_irq(irq, spi_engine_irq, 0, pdev->name, master);
525 if (ret)
526 goto err_ref_clk_disable;
527
528 master->dev.parent = &pdev->dev;
529 master->dev.of_node = pdev->dev.of_node;
530 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE;
531 master->bits_per_word_mask = SPI_BPW_MASK(8);
532 master->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2;
533 master->transfer_one_message = spi_engine_transfer_one_message;
534 master->num_chipselect = 8;
535
536 ret = spi_register_master(master);
537 if (ret)
538 goto err_free_irq;
539
540 platform_set_drvdata(pdev, master);
541
542 return 0;
543err_free_irq:
544 free_irq(irq, master);
545err_ref_clk_disable:
546 clk_disable_unprepare(spi_engine->ref_clk);
547err_clk_disable:
548 clk_disable_unprepare(spi_engine->clk);
549err_put_master:
550 spi_master_put(master);
551 return ret;
552}
553
554static int spi_engine_remove(struct platform_device *pdev)
555{
556 struct spi_master *master = platform_get_drvdata(pdev);
557 struct spi_engine *spi_engine = spi_master_get_devdata(master);
558 int irq = platform_get_irq(pdev, 0);
559
560 spi_unregister_master(master);
561
562 free_irq(irq, master);
563
564 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING);
565 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE);
566 writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET);
567
568 clk_disable_unprepare(spi_engine->ref_clk);
569 clk_disable_unprepare(spi_engine->clk);
570
571 return 0;
572}
573
574static const struct of_device_id spi_engine_match_table[] = {
575 { .compatible = "adi,axi-spi-engine-1.00.a" },
576 { },
577};
578
579static struct platform_driver spi_engine_driver = {
580 .probe = spi_engine_probe,
581 .remove = spi_engine_remove,
582 .driver = {
583 .name = "spi-engine",
584 .of_match_table = spi_engine_match_table,
585 },
586};
587module_platform_driver(spi_engine_driver);
588
589MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
590MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver");
591MODULE_LICENSE("GPL");