Lines Matching +full:axi +full:- +full:spi +full:- +full:engine +full:- +full:1
1 // SPDX-License-Identifier: GPL-2.0-only
3 * SPI-Engine SPI controller driver
5 * Author: Lars-Peter Clausen <lars@metafoo.de>
15 #include <linux/spi/spi.h>
42 #define SPI_ENGINE_INT_SDO_ALMOST_EMPTY BIT(1)
47 #define SPI_ENGINE_CONFIG_CPOL BIT(1)
85 * struct spi_engine_message_state - SPI engine per-message state
128 p->instructions[p->length] = cmd; in spi_engine_program_add_cmd()
129 p->length++; in spi_engine_program_add_cmd()
132 static unsigned int spi_engine_get_config(struct spi_device *spi) in spi_engine_get_config() argument
136 if (spi->mode & SPI_CPOL) in spi_engine_get_config()
138 if (spi->mode & SPI_CPHA) in spi_engine_get_config()
140 if (spi->mode & SPI_3WIRE) in spi_engine_get_config()
151 if (xfer->bits_per_word <= 8) in spi_engine_gen_xfer()
152 len = xfer->len; in spi_engine_gen_xfer()
153 else if (xfer->bits_per_word <= 16) in spi_engine_gen_xfer()
154 len = xfer->len / 2; in spi_engine_gen_xfer()
156 len = xfer->len / 4; in spi_engine_gen_xfer()
162 if (xfer->tx_buf) in spi_engine_gen_xfer()
164 if (xfer->rx_buf) in spi_engine_gen_xfer()
168 SPI_ENGINE_CMD_TRANSFER(flags, n - 1)); in spi_engine_gen_xfer()
169 len -= n; in spi_engine_gen_xfer()
187 spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1)); in spi_engine_gen_sleep()
188 t -= n; in spi_engine_gen_sleep()
193 struct spi_device *spi, bool assert) in spi_engine_gen_cs() argument
198 mask ^= BIT(spi_get_chipselect(spi, 0)); in spi_engine_gen_cs()
206 * The SPI core does most of the message/transfer validation and filling in
211 * is called twice and would otherwise result in double-evaluation.
215 unsigned int clk_div, max_hz = msg->spi->controller->max_speed_hz; in spi_engine_precompile_message()
218 list_for_each_entry(xfer, &msg->transfers, transfer_list) { in spi_engine_precompile_message()
219 clk_div = DIV_ROUND_UP(max_hz, xfer->speed_hz); in spi_engine_precompile_message()
220 xfer->effective_speed_hz = max_hz / min(clk_div, 256U); in spi_engine_precompile_message()
227 struct spi_device *spi = msg->spi; in spi_engine_compile_message() local
228 struct spi_controller *host = spi->controller; in spi_engine_compile_message()
234 clk_div = 1; in spi_engine_compile_message()
238 spi_engine_get_config(spi))); in spi_engine_compile_message()
240 xfer = list_first_entry(&msg->transfers, struct spi_transfer, transfer_list); in spi_engine_compile_message()
241 spi_engine_gen_cs(p, dry, spi, !xfer->cs_off); in spi_engine_compile_message()
243 list_for_each_entry(xfer, &msg->transfers, transfer_list) { in spi_engine_compile_message()
244 new_clk_div = host->max_speed_hz / xfer->effective_speed_hz; in spi_engine_compile_message()
247 /* actual divider used is register value + 1 */ in spi_engine_compile_message()
250 clk_div - 1)); in spi_engine_compile_message()
253 if (bits_per_word != xfer->bits_per_word) { in spi_engine_compile_message()
254 bits_per_word = xfer->bits_per_word; in spi_engine_compile_message()
261 spi_engine_gen_sleep(p, dry, spi_delay_to_ns(&xfer->delay, xfer), in spi_engine_compile_message()
262 xfer->effective_speed_hz); in spi_engine_compile_message()
264 if (xfer->cs_change) { in spi_engine_compile_message()
265 if (list_is_last(&xfer->transfer_list, &msg->transfers)) { in spi_engine_compile_message()
268 if (!xfer->cs_off) in spi_engine_compile_message()
269 spi_engine_gen_cs(p, dry, spi, false); in spi_engine_compile_message()
272 &xfer->cs_change_delay, xfer), in spi_engine_compile_message()
273 xfer->effective_speed_hz); in spi_engine_compile_message()
275 if (!list_next_entry(xfer, transfer_list)->cs_off) in spi_engine_compile_message()
276 spi_engine_gen_cs(p, dry, spi, true); in spi_engine_compile_message()
278 } else if (!list_is_last(&xfer->transfer_list, &msg->transfers) && in spi_engine_compile_message()
279 xfer->cs_off != list_next_entry(xfer, transfer_list)->cs_off) { in spi_engine_compile_message()
280 spi_engine_gen_cs(p, dry, spi, xfer->cs_off); in spi_engine_compile_message()
285 spi_engine_gen_cs(p, dry, spi, false); in spi_engine_compile_message()
291 if (clk_div != 1) in spi_engine_compile_message()
302 xfer = list_first_entry(&msg->transfers, in spi_engine_xfer_next()
304 } else if (list_is_last(&xfer->transfer_list, &msg->transfers)) { in spi_engine_xfer_next()
315 struct spi_engine_message_state *st = msg->state; in spi_engine_tx_next()
316 struct spi_transfer *xfer = st->tx_xfer; in spi_engine_tx_next()
320 } while (xfer && !xfer->tx_buf); in spi_engine_tx_next()
322 st->tx_xfer = xfer; in spi_engine_tx_next()
324 st->tx_length = xfer->len; in spi_engine_tx_next()
325 st->tx_buf = xfer->tx_buf; in spi_engine_tx_next()
327 st->tx_buf = NULL; in spi_engine_tx_next()
333 struct spi_engine_message_state *st = msg->state; in spi_engine_rx_next()
334 struct spi_transfer *xfer = st->rx_xfer; in spi_engine_rx_next()
338 } while (xfer && !xfer->rx_buf); in spi_engine_rx_next()
340 st->rx_xfer = xfer; in spi_engine_rx_next()
342 st->rx_length = xfer->len; in spi_engine_rx_next()
343 st->rx_buf = xfer->rx_buf; in spi_engine_rx_next()
345 st->rx_buf = NULL; in spi_engine_rx_next()
352 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO; in spi_engine_write_cmd_fifo()
353 struct spi_engine_message_state *st = msg->state; in spi_engine_write_cmd_fifo()
357 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM); in spi_engine_write_cmd_fifo()
358 while (n && st->cmd_length) { in spi_engine_write_cmd_fifo()
359 m = min(n, st->cmd_length); in spi_engine_write_cmd_fifo()
360 buf = st->cmd_buf; in spi_engine_write_cmd_fifo()
363 st->cmd_buf += m; in spi_engine_write_cmd_fifo()
364 st->cmd_length -= m; in spi_engine_write_cmd_fifo()
365 n -= m; in spi_engine_write_cmd_fifo()
368 return st->cmd_length != 0; in spi_engine_write_cmd_fifo()
374 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO; in spi_engine_write_tx_fifo()
375 struct spi_engine_message_state *st = msg->state; in spi_engine_write_tx_fifo()
378 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM); in spi_engine_write_tx_fifo()
379 while (n && st->tx_length) { in spi_engine_write_tx_fifo()
380 if (st->tx_xfer->bits_per_word <= 8) { in spi_engine_write_tx_fifo()
381 const u8 *buf = st->tx_buf; in spi_engine_write_tx_fifo()
383 m = min(n, st->tx_length); in spi_engine_write_tx_fifo()
386 st->tx_buf += m; in spi_engine_write_tx_fifo()
387 st->tx_length -= m; in spi_engine_write_tx_fifo()
388 } else if (st->tx_xfer->bits_per_word <= 16) { in spi_engine_write_tx_fifo()
389 const u16 *buf = (const u16 *)st->tx_buf; in spi_engine_write_tx_fifo()
391 m = min(n, st->tx_length / 2); in spi_engine_write_tx_fifo()
394 st->tx_buf += m * 2; in spi_engine_write_tx_fifo()
395 st->tx_length -= m * 2; in spi_engine_write_tx_fifo()
397 const u32 *buf = (const u32 *)st->tx_buf; in spi_engine_write_tx_fifo()
399 m = min(n, st->tx_length / 4); in spi_engine_write_tx_fifo()
402 st->tx_buf += m * 4; in spi_engine_write_tx_fifo()
403 st->tx_length -= m * 4; in spi_engine_write_tx_fifo()
405 n -= m; in spi_engine_write_tx_fifo()
406 if (st->tx_length == 0) in spi_engine_write_tx_fifo()
410 return st->tx_length != 0; in spi_engine_write_tx_fifo()
416 void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO; in spi_engine_read_rx_fifo()
417 struct spi_engine_message_state *st = msg->state; in spi_engine_read_rx_fifo()
420 n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL); in spi_engine_read_rx_fifo()
421 while (n && st->rx_length) { in spi_engine_read_rx_fifo()
422 if (st->rx_xfer->bits_per_word <= 8) { in spi_engine_read_rx_fifo()
423 u8 *buf = st->rx_buf; in spi_engine_read_rx_fifo()
425 m = min(n, st->rx_length); in spi_engine_read_rx_fifo()
428 st->rx_buf += m; in spi_engine_read_rx_fifo()
429 st->rx_length -= m; in spi_engine_read_rx_fifo()
430 } else if (st->rx_xfer->bits_per_word <= 16) { in spi_engine_read_rx_fifo()
431 u16 *buf = (u16 *)st->rx_buf; in spi_engine_read_rx_fifo()
433 m = min(n, st->rx_length / 2); in spi_engine_read_rx_fifo()
436 st->rx_buf += m * 2; in spi_engine_read_rx_fifo()
437 st->rx_length -= m * 2; in spi_engine_read_rx_fifo()
439 u32 *buf = (u32 *)st->rx_buf; in spi_engine_read_rx_fifo()
441 m = min(n, st->rx_length / 4); in spi_engine_read_rx_fifo()
444 st->rx_buf += m * 4; in spi_engine_read_rx_fifo()
445 st->rx_length -= m * 4; in spi_engine_read_rx_fifo()
447 n -= m; in spi_engine_read_rx_fifo()
448 if (st->rx_length == 0) in spi_engine_read_rx_fifo()
452 return st->rx_length != 0; in spi_engine_read_rx_fifo()
458 struct spi_message *msg = host->cur_msg; in spi_engine_irq()
462 int completed_id = -1; in spi_engine_irq()
464 pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_irq()
468 spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_irq()
470 spi_engine->base + SPI_ENGINE_REG_SYNC_ID); in spi_engine_irq()
473 spin_lock(&spi_engine->lock); in spi_engine_irq()
491 struct spi_engine_message_state *st = msg->state; in spi_engine_irq()
493 if (completed_id == st->sync_id) { in spi_engine_irq()
494 if (timer_delete_sync(&spi_engine->watchdog_timer)) { in spi_engine_irq()
495 msg->status = 0; in spi_engine_irq()
496 msg->actual_length = msg->frame_length; in spi_engine_irq()
504 spi_engine->int_enable &= ~disable_int; in spi_engine_irq()
505 writel_relaxed(spi_engine->int_enable, in spi_engine_irq()
506 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_irq()
509 spin_unlock(&spi_engine->lock); in spi_engine_irq()
525 return -ENOMEM; in spi_engine_prepare_message()
532 size = sizeof(*p->instructions) * (p_dry.length + 1); in spi_engine_prepare_message()
536 return -ENOMEM; in spi_engine_prepare_message()
539 ret = ida_alloc_range(&spi_engine->sync_ida, 0, U8_MAX, GFP_KERNEL); in spi_engine_prepare_message()
546 st->sync_id = ret; in spi_engine_prepare_message()
550 spi_engine_program_add_cmd(p, false, SPI_ENGINE_CMD_SYNC(st->sync_id)); in spi_engine_prepare_message()
552 st->p = p; in spi_engine_prepare_message()
553 st->cmd_buf = p->instructions; in spi_engine_prepare_message()
554 st->cmd_length = p->length; in spi_engine_prepare_message()
555 msg->state = st; in spi_engine_prepare_message()
564 struct spi_engine_message_state *st = msg->state; in spi_engine_unprepare_message()
566 ida_free(&spi_engine->sync_ida, st->sync_id); in spi_engine_unprepare_message()
567 kfree(st->p); in spi_engine_unprepare_message()
577 struct spi_engine_message_state *st = msg->state; in spi_engine_transfer_one_message()
581 mod_timer(&spi_engine->watchdog_timer, jiffies + msecs_to_jiffies(5000)); in spi_engine_transfer_one_message()
583 spin_lock_irqsave(&spi_engine->lock, flags); in spi_engine_transfer_one_message()
593 if (st->rx_length != 0) in spi_engine_transfer_one_message()
599 spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_transfer_one_message()
600 spi_engine->int_enable = int_enable; in spi_engine_transfer_one_message()
601 spin_unlock_irqrestore(&spi_engine->lock, flags); in spi_engine_transfer_one_message()
609 struct spi_controller *host = spi_engine->controller; in spi_engine_timeout()
611 if (WARN_ON(!host->cur_msg)) in spi_engine_timeout()
614 dev_err(&host->dev, in spi_engine_timeout()
616 host->cur_msg->status = -ETIMEDOUT; in spi_engine_timeout()
624 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_release_hw()
625 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_release_hw()
626 writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET); in spi_engine_release_hw()
641 host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi_engine)); in spi_engine_probe()
643 return -ENOMEM; in spi_engine_probe()
647 spin_lock_init(&spi_engine->lock); in spi_engine_probe()
648 ida_init(&spi_engine->sync_ida); in spi_engine_probe()
649 timer_setup(&spi_engine->watchdog_timer, spi_engine_timeout, TIMER_IRQSAFE); in spi_engine_probe()
650 spi_engine->controller = host; in spi_engine_probe()
652 spi_engine->clk = devm_clk_get_enabled(&pdev->dev, "s_axi_aclk"); in spi_engine_probe()
653 if (IS_ERR(spi_engine->clk)) in spi_engine_probe()
654 return PTR_ERR(spi_engine->clk); in spi_engine_probe()
656 spi_engine->ref_clk = devm_clk_get_enabled(&pdev->dev, "spi_clk"); in spi_engine_probe()
657 if (IS_ERR(spi_engine->ref_clk)) in spi_engine_probe()
658 return PTR_ERR(spi_engine->ref_clk); in spi_engine_probe()
660 spi_engine->base = devm_platform_ioremap_resource(pdev, 0); in spi_engine_probe()
661 if (IS_ERR(spi_engine->base)) in spi_engine_probe()
662 return PTR_ERR(spi_engine->base); in spi_engine_probe()
664 version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION); in spi_engine_probe()
665 if (SPI_ENGINE_VERSION_MAJOR(version) != 1) { in spi_engine_probe()
666 dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n", in spi_engine_probe()
670 return -ENODEV; in spi_engine_probe()
673 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); in spi_engine_probe()
674 writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); in spi_engine_probe()
675 writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); in spi_engine_probe()
677 ret = devm_add_action_or_reset(&pdev->dev, spi_engine_release_hw, in spi_engine_probe()
682 ret = devm_request_irq(&pdev->dev, irq, spi_engine_irq, 0, pdev->name, in spi_engine_probe()
687 host->dev.of_node = pdev->dev.of_node; in spi_engine_probe()
688 host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE; in spi_engine_probe()
689 host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32); in spi_engine_probe()
690 host->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2; in spi_engine_probe()
691 host->transfer_one_message = spi_engine_transfer_one_message; in spi_engine_probe()
692 host->prepare_message = spi_engine_prepare_message; in spi_engine_probe()
693 host->unprepare_message = spi_engine_unprepare_message; in spi_engine_probe()
694 host->num_chipselect = 8; in spi_engine_probe()
696 if (host->max_speed_hz == 0) in spi_engine_probe()
697 return dev_err_probe(&pdev->dev, -EINVAL, "spi_clk rate is 0"); in spi_engine_probe()
699 ret = devm_spi_register_controller(&pdev->dev, host); in spi_engine_probe()
709 { .compatible = "adi,axi-spi-engine-1.00.a" },
717 .name = "spi-engine",
723 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
724 MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver");