Lines Matching +full:spi +full:- +full:base
1 // SPDX-License-Identifier: GPL-2.0-only
3 * SPI-Engine SPI controller driver
5 * Author: Lars-Peter Clausen <lars@metafoo.de>
9 #include <linux/fpga/adi-axi-common.h>
16 #include <linux/spi/spi.h>
78 * struct spi_engine_message_state - SPI engine per-message state
109 void __iomem *base; member
122 p->instructions[p->length] = cmd; in spi_engine_program_add_cmd()
123 p->length++; in spi_engine_program_add_cmd()
126 static unsigned int spi_engine_get_config(struct spi_device *spi) in spi_engine_get_config() argument
130 if (spi->mode & SPI_CPOL) in spi_engine_get_config()
132 if (spi->mode & SPI_CPHA) in spi_engine_get_config()
134 if (spi->mode & SPI_3WIRE) in spi_engine_get_config()
141 struct spi_device *spi, struct spi_transfer *xfer) in spi_engine_get_clk_div() argument
145 clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk), in spi_engine_get_clk_div()
146 xfer->speed_hz * 2); in spi_engine_get_clk_div()
150 clk_div -= 1; in spi_engine_get_clk_div()
158 unsigned int len = xfer->len; in spi_engine_gen_xfer()
164 if (xfer->tx_buf) in spi_engine_gen_xfer()
166 if (xfer->rx_buf) in spi_engine_gen_xfer()
170 SPI_ENGINE_CMD_TRANSFER(flags, n - 1)); in spi_engine_gen_xfer()
171 len -= n; in spi_engine_gen_xfer()
179 unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk); in spi_engine_gen_sleep()
183 delay = spi_delay_to_ns(&xfer->delay, xfer); in spi_engine_gen_sleep()
195 spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1)); in spi_engine_gen_sleep()
196 t -= n; in spi_engine_gen_sleep()
201 struct spi_device *spi, bool assert) in spi_engine_gen_cs() argument
206 mask ^= BIT(spi_get_chipselect(spi, 0)); in spi_engine_gen_cs()
214 struct spi_device *spi = msg->spi; in spi_engine_compile_message() local
219 clk_div = -1; in spi_engine_compile_message()
223 spi_engine_get_config(spi))); in spi_engine_compile_message()
225 list_for_each_entry(xfer, &msg->transfers, transfer_list) { in spi_engine_compile_message()
226 new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer); in spi_engine_compile_message()
235 spi_engine_gen_cs(p, dry, spi, true); in spi_engine_compile_message()
240 cs_change = xfer->cs_change; in spi_engine_compile_message()
241 if (list_is_last(&xfer->transfer_list, &msg->transfers)) in spi_engine_compile_message()
245 spi_engine_gen_cs(p, dry, spi, false); in spi_engine_compile_message()
254 struct spi_message *msg = spi_engine->msg; in spi_engine_xfer_next()
258 xfer = list_first_entry(&msg->transfers, in spi_engine_xfer_next()
260 } else if (list_is_last(&xfer->transfer_list, &msg->transfers)) { in spi_engine_xfer_next()
271 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_tx_next()
272 struct spi_transfer *xfer = st->tx_xfer; in spi_engine_tx_next()
276 } while (xfer && !xfer->tx_buf); in spi_engine_tx_next()
278 st->tx_xfer = xfer; in spi_engine_tx_next()
280 st->tx_length = xfer->len; in spi_engine_tx_next()
281 st->tx_buf = xfer->tx_buf; in spi_engine_tx_next()
283 st->tx_buf = NULL; in spi_engine_tx_next()
289 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_rx_next()
290 struct spi_transfer *xfer = st->rx_xfer; in spi_engine_rx_next()
294 } while (xfer && !xfer->rx_buf); in spi_engine_rx_next()
296 st->rx_xfer = xfer; in spi_engine_rx_next()
298 st->rx_length = xfer->len; in spi_engine_rx_next()
299 st->rx_buf = xfer->rx_buf; in spi_engine_rx_next()
301 st->rx_buf = NULL; in spi_engine_rx_next()
307 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO; in spi_engine_write_cmd_fifo()
308 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_write_cmd_fifo()
312 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM); in spi_engine_write_cmd_fifo()
313 while (n && st->cmd_length) { in spi_engine_write_cmd_fifo()
314 m = min(n, st->cmd_length); in spi_engine_write_cmd_fifo()
315 buf = st->cmd_buf; in spi_engine_write_cmd_fifo()
318 st->cmd_buf += m; in spi_engine_write_cmd_fifo()
319 st->cmd_length -= m; in spi_engine_write_cmd_fifo()
320 n -= m; in spi_engine_write_cmd_fifo()
323 return st->cmd_length != 0; in spi_engine_write_cmd_fifo()
328 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO; in spi_engine_write_tx_fifo()
329 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_write_tx_fifo()
333 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM); in spi_engine_write_tx_fifo()
334 while (n && st->tx_length) { in spi_engine_write_tx_fifo()
335 m = min(n, st->tx_length); in spi_engine_write_tx_fifo()
336 buf = st->tx_buf; in spi_engine_write_tx_fifo()
339 st->tx_buf += m; in spi_engine_write_tx_fifo()
340 st->tx_length -= m; in spi_engine_write_tx_fifo()
341 n -= m; in spi_engine_write_tx_fifo()
342 if (st->tx_length == 0) in spi_engine_write_tx_fifo()
346 return st->tx_length != 0; in spi_engine_write_tx_fifo()
351 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO; in spi_engine_read_rx_fifo()
352 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_read_rx_fifo()
356 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL); in spi_engine_read_rx_fifo()
357 while (n && st->rx_length) { in spi_engine_read_rx_fifo()
358 m = min(n, st->rx_length); in spi_engine_read_rx_fifo()
359 buf = st->rx_buf; in spi_engine_read_rx_fifo()
362 st->rx_buf += m; in spi_engine_read_rx_fifo()
363 st->rx_length -= m; in spi_engine_read_rx_fifo()
364 n -= m; in spi_engine_read_rx_fifo()
365 if (st->rx_length == 0) in spi_engine_read_rx_fifo()
369 return st->rx_length != 0; in spi_engine_read_rx_fifo()
379 pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_irq()
383 spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_irq()
384 spi_engine->completed_id = readl_relaxed( in spi_engine_irq()
385 spi_engine->base + SPI_ENGINE_REG_SYNC_ID); in spi_engine_irq()
388 spin_lock(&spi_engine->lock); in spi_engine_irq()
405 if (pending & SPI_ENGINE_INT_SYNC && spi_engine->msg) { in spi_engine_irq()
406 struct spi_engine_message_state *st = spi_engine->msg->state; in spi_engine_irq()
408 if (spi_engine->completed_id == st->sync_id) { in spi_engine_irq()
409 struct spi_message *msg = spi_engine->msg; in spi_engine_irq()
410 struct spi_engine_message_state *st = msg->state; in spi_engine_irq()
412 ida_free(&spi_engine->sync_ida, st->sync_id); in spi_engine_irq()
413 kfree(st->p); in spi_engine_irq()
415 msg->status = 0; in spi_engine_irq()
416 msg->actual_length = msg->frame_length; in spi_engine_irq()
417 spi_engine->msg = NULL; in spi_engine_irq()
424 spi_engine->int_enable &= ~disable_int; in spi_engine_irq()
425 writel_relaxed(spi_engine->int_enable, in spi_engine_irq()
426 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_irq()
429 spin_unlock(&spi_engine->lock); in spi_engine_irq()
447 return -ENOMEM; in spi_engine_transfer_one_message()
452 size = sizeof(*p->instructions) * (p_dry.length + 1); in spi_engine_transfer_one_message()
456 return -ENOMEM; in spi_engine_transfer_one_message()
459 ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL); in spi_engine_transfer_one_message()
466 st->sync_id = ret; in spi_engine_transfer_one_message()
470 spin_lock_irqsave(&spi_engine->lock, flags); in spi_engine_transfer_one_message()
471 spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id)); in spi_engine_transfer_one_message()
473 msg->state = st; in spi_engine_transfer_one_message()
474 spi_engine->msg = msg; in spi_engine_transfer_one_message()
475 st->p = p; in spi_engine_transfer_one_message()
477 st->cmd_buf = p->instructions; in spi_engine_transfer_one_message()
478 st->cmd_length = p->length; in spi_engine_transfer_one_message()
487 if (st->rx_length != 0) in spi_engine_transfer_one_message()
493 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_transfer_one_message()
494 spi_engine->int_enable = int_enable; in spi_engine_transfer_one_message()
495 spin_unlock_irqrestore(&spi_engine->lock, flags); in spi_engine_transfer_one_message()
512 host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi_engine)); in spi_engine_probe()
514 return -ENOMEM; in spi_engine_probe()
518 spin_lock_init(&spi_engine->lock); in spi_engine_probe()
519 ida_init(&spi_engine->sync_ida); in spi_engine_probe()
521 spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); in spi_engine_probe()
522 if (IS_ERR(spi_engine->clk)) in spi_engine_probe()
523 return PTR_ERR(spi_engine->clk); in spi_engine_probe()
525 spi_engine->ref_clk = devm_clk_get_enabled(&pdev->dev, "spi_clk"); in spi_engine_probe()
526 if (IS_ERR(spi_engine->ref_clk)) in spi_engine_probe()
527 return PTR_ERR(spi_engine->ref_clk); in spi_engine_probe()
529 spi_engine->base = devm_platform_ioremap_resource(pdev, 0); in spi_engine_probe()
530 if (IS_ERR(spi_engine->base)) in spi_engine_probe()
531 return PTR_ERR(spi_engine->base); in spi_engine_probe()
533 version = readl(spi_engine->base + ADI_AXI_REG_VERSION); in spi_engine_probe()
535 dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%u\n", in spi_engine_probe()
539 return -ENODEV; in spi_engine_probe()
542 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); in spi_engine_probe()
543 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_probe()
544 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_probe()
546 ret = request_irq(irq, spi_engine_irq, 0, pdev->name, host); in spi_engine_probe()
550 host->dev.of_node = pdev->dev.of_node; in spi_engine_probe()
551 host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE; in spi_engine_probe()
552 host->bits_per_word_mask = SPI_BPW_MASK(8); in spi_engine_probe()
553 host->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2; in spi_engine_probe()
554 host->transfer_one_message = spi_engine_transfer_one_message; in spi_engine_probe()
555 host->num_chipselect = 8; in spi_engine_probe()
579 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_remove()
580 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_remove()
581 writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET); in spi_engine_remove()
585 { .compatible = "adi,axi-spi-engine-1.00.a" },
594 .name = "spi-engine",
600 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
601 MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver");