diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2025-12-04 11:24:24 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2025-12-04 11:24:24 -0800 |
| commit | fa5ef105618ae9b5aaa51b3f09e41d88d4514207 (patch) | |
| tree | 30780efabf844cc55a8d27082e6a7810d311d68b /drivers/spi/spi-dw-core.c | |
| parent | 0a9431fa74ac9b744bff5b65082ff96fd3d80297 (diff) | |
| parent | cb5c2eb459f4c98d584eaf3d3ea7c3612385d081 (diff) | |
Merge tag 'spi-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
Pull spi updates from Mark Brown:
"This release is almost entirely new drivers, with a couple of small
changes in generic code.
The biggest individual update is a rename of the existing Microchip
driver and the addition of a new driver for the silicon SPI controller
in their PolarFire SoCs. The overlap between the soft IP supported by
the current driver and this new one is regrettably all in the IP and
not in the register interface offered to software.
- Add a time offset parameter for offloads, allowing them to be
defined in relation to each other. This is useful for IIO type
applcations where you trigger an operation then read the result
after a delay.
- Add a tracepoint for flash exec_ops, bringing the flash support
more in line with the debuggability of vanilla SPI.
- Support for Airoha EN7523, Arduino MCUs, Aspeed AST2700, Microchip
PolarFire SPI controllers, NXP i.MX51 ECSPI target mode, Qualcomm
IPQ5414 and IPQ5332, Renesas RZ/T2H, RZ/V2N and RZ/2NH and SpacemiT
K1 QuadSPI.
There's also a small set of ASoC cleanups that I mistakenly applied to
the SPI tree and then put more stuff on top of before it was brought
to my attention, sorry about that"
* tag 'spi-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (80 commits)
spi: microchip-core: Refactor FIFO read and write handlers
spi: ch341: fix out-of-bounds memory access in ch341_transfer_one
spi: microchip-core: Remove unneeded PM related macro
spi: microchip-core: Use SPI_MODE_X_MASK
spi: microchip-core: Utilise temporary variable for struct device
spi: microchip-core: Replace dead code (-ENOMEM error message)
spi: microchip-core: use min() instead of min_t()
spi: dt-bindings: airoha: add compatible for EN7523
spi: airoha-snfi: en7523: workaround flash damaging if UART_TXD was short to GND
spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support
spi: dt-bindings: renesas,rzv2h-rspi: Document RZ/V2N SoC support
spi: microchip: Enable compile-testing for FPGA SPI controllers
spi: Fix potential uninitialized variable in probe()
spi: rzv2h-rspi: add support for RZ/T2H and RZ/N2H
spi: dt-bindings: renesas,rzv2h-rspi: document RZ/T2H and RZ/N2H
spi: rzv2h-rspi: add support for loopback mode
spi: rzv2h-rspi: add support for variable transfer clock
spi: rzv2h-rspi: add support for using PCLK for transfer clock
spi: rzv2h-rspi: make transfer clock rate finding chip-specific
spi: rzv2h-rspi: avoid recomputing transfer frequency
...
Diffstat (limited to 'drivers/spi/spi-dw-core.c')
| -rw-r--r-- | drivers/spi/spi-dw-core.c | 188 |
1 files changed, 110 insertions, 78 deletions
diff --git a/drivers/spi/spi-dw-core.c b/drivers/spi/spi-dw-core.c index b3b883cb9541..9ebf244294f8 100644 --- a/drivers/spi/spi-dw-core.c +++ b/drivers/spi/spi-dw-core.c @@ -63,7 +63,7 @@ static void dw_spi_debugfs_init(struct dw_spi *dws) { char name[32]; - snprintf(name, 32, "dw_spi%d", dws->host->bus_num); + snprintf(name, 32, "dw_spi%d", dws->ctlr->bus_num); dws->debugfs = debugfs_create_dir(name, NULL); dws->regset.regs = dw_spi_dbgfs_regs; @@ -185,25 +185,25 @@ int dw_spi_check_status(struct dw_spi *dws, bool raw) irq_status = dw_readl(dws, DW_SPI_ISR); if (irq_status & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "RX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO overflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_RXUI) { - dev_err(&dws->host->dev, "RX FIFO underflow detected\n"); + dev_err(&dws->ctlr->dev, "RX FIFO underflow detected\n"); ret = -EIO; } if (irq_status & DW_SPI_INT_TXOI) { - dev_err(&dws->host->dev, "TX FIFO overflow detected\n"); + dev_err(&dws->ctlr->dev, "TX FIFO overflow detected\n"); ret = -EIO; } /* Generically handle the erroneous situation */ if (ret) { dw_spi_reset_chip(dws); - if (dws->host->cur_msg) - dws->host->cur_msg->status = ret; + if (dws->ctlr->cur_msg) + dws->ctlr->cur_msg->status = ret; } return ret; @@ -215,7 +215,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) u16 irq_status = dw_readl(dws, DW_SPI_ISR); if (dw_spi_check_status(dws, false)) { - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); return IRQ_HANDLED; } @@ -229,7 +229,7 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) dw_reader(dws); if (!dws->rx_len) { dw_spi_mask_intr(dws, 0xff); - spi_finalize_current_transfer(dws->host); + spi_finalize_current_transfer(dws->ctlr); } else if (dws->rx_len <= dw_readl(dws, DW_SPI_RXFTLR)) { dw_writel(dws, DW_SPI_RXFTLR, dws->rx_len - 1); } @@ -250,14 +250,14 @@ static irqreturn_t dw_spi_transfer_handler(struct dw_spi *dws) static irqreturn_t dw_spi_irq(int irq, void *dev_id) { - struct spi_controller *host = dev_id; - struct dw_spi *dws = spi_controller_get_devdata(host); + struct spi_controller *ctlr = dev_id; + struct dw_spi *dws = spi_controller_get_devdata(ctlr); u16 irq_status = dw_readl(dws, DW_SPI_ISR) & DW_SPI_INT_MASK; if (!irq_status) return IRQ_NONE; - if (!host->cur_msg) { + if (!ctlr->cur_msg) { dw_spi_mask_intr(dws, 0xff); return IRQ_HANDLED; } @@ -332,6 +332,9 @@ void dw_spi_update_config(struct dw_spi *dws, struct spi_device *spi, dw_writel(dws, DW_SPI_CTRLR0, cr0); + if (spi_controller_is_target(dws->ctlr)) + return; + if (cfg->tmode == DW_SPI_CTRLR0_TMOD_EPROMREAD || cfg->tmode == DW_SPI_CTRLR0_TMOD_RO) dw_writel(dws, DW_SPI_CTRLR1, cfg->ndf ? cfg->ndf - 1 : 0); @@ -410,11 +413,11 @@ static int dw_spi_poll_transfer(struct dw_spi *dws, return 0; } -static int dw_spi_transfer_one(struct spi_controller *host, +static int dw_spi_transfer_one(struct spi_controller *ctlr, struct spi_device *spi, struct spi_transfer *transfer) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); struct dw_spi_cfg cfg = { .tmode = DW_SPI_CTRLR0_TMOD_TR, .dfs = transfer->bits_per_word, @@ -439,7 +442,7 @@ static int dw_spi_transfer_one(struct spi_controller *host, transfer->effective_speed_hz = dws->current_freq; /* Check if current transfer is a DMA transaction */ - dws->dma_mapped = spi_xfer_is_dma_mapped(host, spi, transfer); + dws->dma_mapped = spi_xfer_is_dma_mapped(ctlr, spi, transfer); /* For poll mode just disable all interrupts */ dw_spi_mask_intr(dws, 0xff); @@ -462,10 +465,9 @@ static int dw_spi_transfer_one(struct spi_controller *host, return 1; } -static void dw_spi_handle_err(struct spi_controller *host, - struct spi_message *msg) +static inline void dw_spi_abort(struct spi_controller *ctlr) { - struct dw_spi *dws = spi_controller_get_devdata(host); + struct dw_spi *dws = spi_controller_get_devdata(ctlr); if (dws->dma_mapped) dws->dma_ops->dma_stop(dws); @@ -473,6 +475,19 @@ static void dw_spi_handle_err(struct spi_controller *host, dw_spi_reset_chip(dws); } +static void dw_spi_handle_err(struct spi_controller *ctlr, + struct spi_message *msg) +{ + dw_spi_abort(ctlr); +} + +static int dw_spi_target_abort(struct spi_controller *ctlr) +{ + dw_spi_abort(ctlr); + + return 0; +} + static int dw_spi_adjust_mem_op_size(struct spi_mem *mem, struct spi_mem_op *op) { if (op->data.dir == SPI_MEM_DATA_IN) @@ -574,7 +589,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) while (len) { entries = readl_relaxed(dws->regs + DW_SPI_TXFLR); if (!entries) { - dev_err(&dws->host->dev, "CS de-assertion on Tx\n"); + dev_err(&dws->ctlr->dev, "CS de-assertion on Tx\n"); return -EIO; } room = min(dws->fifo_len - entries, len); @@ -594,7 +609,7 @@ static int dw_spi_write_then_read(struct dw_spi *dws, struct spi_device *spi) if (!entries) { sts = readl_relaxed(dws->regs + DW_SPI_RISR); if (sts & DW_SPI_INT_RXOI) { - dev_err(&dws->host->dev, "FIFO overflow on Rx\n"); + dev_err(&dws->ctlr->dev, "FIFO overflow on Rx\n"); return -EIO; } continue; @@ -635,7 +650,7 @@ static int dw_spi_wait_mem_op_done(struct dw_spi *dws) spi_delay_exec(&delay, NULL); if (retry < 0) { - dev_err(&dws->host->dev, "Mem op hanged up\n"); + dev_err(&dws->ctlr->dev, "Mem op hanged up\n"); return -EIO; } @@ -834,18 +849,23 @@ static void dw_spi_hw_init(struct device *dev, struct dw_spi *dws) DW_SPI_GET_BYTE(dws->ver, 1)); } - /* - * Try to detect the number of native chip-selects if the platform - * driver didn't set it up. There can be up to 16 lines configured. - */ - if (!dws->num_cs) { - u32 ser; + if (spi_controller_is_target(dws->ctlr)) { + /* There is only one CS input signal in target mode */ + dws->num_cs = 1; + } else { + /* + * Try to detect the number of native chip-selects if the platform + * driver didn't set it up. There can be up to 16 lines configured. + */ + if (!dws->num_cs) { + u32 ser; - dw_writel(dws, DW_SPI_SER, 0xffff); - ser = dw_readl(dws, DW_SPI_SER); - dw_writel(dws, DW_SPI_SER, 0); + dw_writel(dws, DW_SPI_SER, 0xffff); + ser = dw_readl(dws, DW_SPI_SER); + dw_writel(dws, DW_SPI_SER, 0); - dws->num_cs = hweight16(ser); + dws->num_cs = hweight16(ser); + } } /* @@ -898,60 +918,72 @@ static const struct spi_controller_mem_caps dw_spi_mem_caps = { .per_op_freq = true, }; -int dw_spi_add_host(struct device *dev, struct dw_spi *dws) +int dw_spi_add_controller(struct device *dev, struct dw_spi *dws) { - struct spi_controller *host; + struct spi_controller *ctlr; + bool target; int ret; if (!dws) return -EINVAL; - host = spi_alloc_host(dev, 0); - if (!host) + target = device_property_read_bool(dev, "spi-slave"); + if (target) + ctlr = spi_alloc_target(dev, 0); + else + ctlr = spi_alloc_host(dev, 0); + + if (!ctlr) return -ENOMEM; - device_set_node(&host->dev, dev_fwnode(dev)); + device_set_node(&ctlr->dev, dev_fwnode(dev)); - dws->host = host; + dws->ctlr = ctlr; dws->dma_addr = (dma_addr_t)(dws->paddr + DW_SPI_DR); - spi_controller_set_devdata(host, dws); + spi_controller_set_devdata(ctlr, dws); /* Basic HW init */ dw_spi_hw_init(dev, dws); ret = request_irq(dws->irq, dw_spi_irq, IRQF_SHARED, dev_name(dev), - host); + ctlr); if (ret < 0 && ret != -ENOTCONN) { dev_err(dev, "can not get IRQ\n"); - goto err_free_host; + goto err_free_ctlr; } dw_spi_init_mem_ops(dws); - host->use_gpio_descriptors = true; - host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP; + ctlr->mode_bits = SPI_CPOL | SPI_CPHA; if (dws->caps & DW_SPI_CAP_DFS32) - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); - else - host->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); - host->bus_num = dws->bus_num; - host->num_chipselect = dws->num_cs; - host->setup = dw_spi_setup; - host->cleanup = dw_spi_cleanup; - if (dws->set_cs) - host->set_cs = dws->set_cs; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); else - host->set_cs = dw_spi_set_cs; - host->transfer_one = dw_spi_transfer_one; - host->handle_err = dw_spi_handle_err; - if (dws->mem_ops.exec_op) { - host->mem_ops = &dws->mem_ops; - host->mem_caps = &dw_spi_mem_caps; + ctlr->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 16); + ctlr->bus_num = dws->bus_num; + ctlr->num_chipselect = dws->num_cs; + ctlr->setup = dw_spi_setup; + ctlr->cleanup = dw_spi_cleanup; + ctlr->transfer_one = dw_spi_transfer_one; + ctlr->handle_err = dw_spi_handle_err; + ctlr->auto_runtime_pm = true; + + if (!target) { + ctlr->use_gpio_descriptors = true; + ctlr->mode_bits |= SPI_LOOP; + if (dws->set_cs) + ctlr->set_cs = dws->set_cs; + else + ctlr->set_cs = dw_spi_set_cs; + if (dws->mem_ops.exec_op) { + ctlr->mem_ops = &dws->mem_ops; + ctlr->mem_caps = &dw_spi_mem_caps; + } + ctlr->max_speed_hz = dws->max_freq; + ctlr->flags = SPI_CONTROLLER_GPIO_SS; + } else { + ctlr->target_abort = dw_spi_target_abort; } - host->max_speed_hz = dws->max_freq; - host->flags = SPI_CONTROLLER_GPIO_SS; - host->auto_runtime_pm = true; /* Get default rx sample delay */ device_property_read_u32(dev, "rx-sample-delay-ns", @@ -964,14 +996,14 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) } else if (ret) { dev_warn(dev, "DMA init failed\n"); } else { - host->can_dma = dws->dma_ops->can_dma; - host->flags |= SPI_CONTROLLER_MUST_TX; + ctlr->can_dma = dws->dma_ops->can_dma; + ctlr->flags |= SPI_CONTROLLER_MUST_TX; } } - ret = spi_register_controller(host); + ret = spi_register_controller(ctlr); if (ret) { - dev_err_probe(dev, ret, "problem registering spi host\n"); + dev_err_probe(dev, ret, "problem registering spi controller\n"); goto err_dma_exit; } @@ -983,47 +1015,47 @@ err_dma_exit: dws->dma_ops->dma_exit(dws); dw_spi_enable_chip(dws, 0); err_free_irq: - free_irq(dws->irq, host); -err_free_host: - spi_controller_put(host); + free_irq(dws->irq, ctlr); +err_free_ctlr: + spi_controller_put(ctlr); return ret; } -EXPORT_SYMBOL_NS_GPL(dw_spi_add_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_add_controller, "SPI_DW_CORE"); -void dw_spi_remove_host(struct dw_spi *dws) +void dw_spi_remove_controller(struct dw_spi *dws) { dw_spi_debugfs_remove(dws); - spi_unregister_controller(dws->host); + spi_unregister_controller(dws->ctlr); if (dws->dma_ops && dws->dma_ops->dma_exit) dws->dma_ops->dma_exit(dws); dw_spi_shutdown_chip(dws); - free_irq(dws->irq, dws->host); + free_irq(dws->irq, dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_remove_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_remove_controller, "SPI_DW_CORE"); -int dw_spi_suspend_host(struct dw_spi *dws) +int dw_spi_suspend_controller(struct dw_spi *dws) { int ret; - ret = spi_controller_suspend(dws->host); + ret = spi_controller_suspend(dws->ctlr); if (ret) return ret; dw_spi_shutdown_chip(dws); return 0; } -EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_suspend_controller, "SPI_DW_CORE"); -int dw_spi_resume_host(struct dw_spi *dws) +int dw_spi_resume_controller(struct dw_spi *dws) { - dw_spi_hw_init(&dws->host->dev, dws); - return spi_controller_resume(dws->host); + dw_spi_hw_init(&dws->ctlr->dev, dws); + return spi_controller_resume(dws->ctlr); } -EXPORT_SYMBOL_NS_GPL(dw_spi_resume_host, "SPI_DW_CORE"); +EXPORT_SYMBOL_NS_GPL(dw_spi_resume_controller, "SPI_DW_CORE"); MODULE_AUTHOR("Feng Tang <feng.tang@intel.com>"); MODULE_DESCRIPTION("Driver for DesignWare SPI controller core"); |