Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: Add dma router for pl08x in LPC32XX SoC

LPC32XX connects few of its peripherals to pl08x DMA thru a multiplexer,
this driver allows to route a signal request line thru the multiplexer for
given peripheral.

Signed-off-by: Piotr Wojtaszczyk <piotr.wojtaszczyk@timesys.com>
Link: https://lore.kernel.org/r/20240628152022.274405-1-piotr.wojtaszczyk@timesys.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Piotr Wojtaszczyk and committed by
Vinod Koul
5d318b59 95429614

+207
+1
MAINTAINERS
··· 2442 2442 F: Documentation/devicetree/bindings/i2c/nxp,pnx-i2c.yaml 2443 2443 F: arch/arm/boot/dts/nxp/lpc/lpc32* 2444 2444 F: arch/arm/mach-lpc32xx/ 2445 + F: drivers/dma/lpc32xx-dmamux.c 2445 2446 F: drivers/i2c/busses/i2c-pnx.c 2446 2447 F: drivers/net/ethernet/nxp/lpc_eth.c 2447 2448 F: drivers/usb/host/ohci-nxp.c
+1
arch/arm/mach-lpc32xx/Kconfig
··· 8 8 select CLKSRC_LPC32XX 9 9 select CPU_ARM926T 10 10 select GPIOLIB 11 + select LPC32XX_DMAMUX if AMBA_PL08X 11 12 help 12 13 Support for the NXP LPC32XX family of processors
+9
drivers/dma/Kconfig
··· 387 387 Enable support for DMA on NXP LPC18xx/43xx platforms 388 388 with PL080 and multiplexed DMA request lines. 389 389 390 + config LPC32XX_DMAMUX 391 + bool "NXP LPC32xx DMA MUX for PL080" 392 + depends on ARCH_LPC32XX || COMPILE_TEST 393 + depends on OF && AMBA_PL08X 394 + select MFD_SYSCON 395 + help 396 + Support for PL080 multiplexed DMA request lines on 397 + LPC32XX platrofm. 398 + 390 399 config LS2X_APB_DMA 391 400 tristate "Loongson LS2X APB DMA support" 392 401 depends on LOONGARCH || COMPILE_TEST
+1
drivers/dma/Makefile
··· 51 51 obj-$(CONFIG_K3_DMA) += k3dma.o 52 52 obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o 53 53 obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o 54 + obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o 54 55 obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o 55 56 obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o 56 57 obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o
+195
drivers/dma/lpc32xx-dmamux.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // 3 + // Copyright 2024 Timesys Corporation <piotr.wojtaszczyk@timesys.com> 4 + // 5 + // Based on TI DMA Crossbar driver by: 6 + // Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com 7 + // Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 8 + 9 + #include <linux/err.h> 10 + #include <linux/init.h> 11 + #include <linux/mfd/syscon.h> 12 + #include <linux/of.h> 13 + #include <linux/of_dma.h> 14 + #include <linux/of_platform.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/regmap.h> 17 + #include <linux/spinlock.h> 18 + 19 + #define LPC32XX_SSP_CLK_CTRL 0x78 20 + #define LPC32XX_I2S_CLK_CTRL 0x7c 21 + 22 + struct lpc32xx_dmamux { 23 + int signal; 24 + char *name_sel0; 25 + char *name_sel1; 26 + int muxval; 27 + int muxreg; 28 + int bit; 29 + bool busy; 30 + }; 31 + 32 + struct lpc32xx_dmamux_data { 33 + struct dma_router dmarouter; 34 + struct regmap *reg; 35 + spinlock_t lock; /* protects busy status flag */ 36 + }; 37 + 38 + /* From LPC32x0 User manual "3.2.1 DMA request signals" */ 39 + static struct lpc32xx_dmamux lpc32xx_muxes[] = { 40 + { 41 + .signal = 3, 42 + .name_sel0 = "spi2-rx-tx", 43 + .name_sel1 = "ssp1-rx", 44 + .muxreg = LPC32XX_SSP_CLK_CTRL, 45 + .bit = 5, 46 + }, 47 + { 48 + .signal = 10, 49 + .name_sel0 = "uart7-rx", 50 + .name_sel1 = "i2s1-dma1", 51 + .muxreg = LPC32XX_I2S_CLK_CTRL, 52 + .bit = 4, 53 + }, 54 + { 55 + .signal = 11, 56 + .name_sel0 = "spi1-rx-tx", 57 + .name_sel1 = "ssp1-tx", 58 + .muxreg = LPC32XX_SSP_CLK_CTRL, 59 + .bit = 4, 60 + }, 61 + { 62 + .signal = 14, 63 + .name_sel0 = "none", 64 + .name_sel1 = "ssp0-rx", 65 + .muxreg = LPC32XX_SSP_CLK_CTRL, 66 + .bit = 3, 67 + }, 68 + { 69 + .signal = 15, 70 + .name_sel0 = "none", 71 + .name_sel1 = "ssp0-tx", 72 + .muxreg = LPC32XX_SSP_CLK_CTRL, 73 + .bit = 2, 74 + }, 75 + }; 76 + 77 + static void lpc32xx_dmamux_release(struct device *dev, void *route_data) 78 + { 79 + struct lpc32xx_dmamux_data *dmamux = dev_get_drvdata(dev); 80 + struct lpc32xx_dmamux *mux = route_data; 81 + 82 + dev_dbg(dev, "releasing dma request signal %d routed to %s\n", 83 + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 84 + 85 + guard(spinlock)(&dmamux->lock); 86 + 87 + mux->busy = false; 88 + } 89 + 90 + static void *lpc32xx_dmamux_reserve(struct of_phandle_args *dma_spec, 91 + struct of_dma *ofdma) 92 + { 93 + struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 94 + struct device *dev = &pdev->dev; 95 + struct lpc32xx_dmamux_data *dmamux = platform_get_drvdata(pdev); 96 + unsigned long flags; 97 + struct lpc32xx_dmamux *mux = NULL; 98 + int i; 99 + 100 + if (dma_spec->args_count != 3) { 101 + dev_err(&pdev->dev, "invalid number of dma mux args\n"); 102 + return ERR_PTR(-EINVAL); 103 + } 104 + 105 + for (i = 0; i < ARRAY_SIZE(lpc32xx_muxes); i++) { 106 + if (lpc32xx_muxes[i].signal == dma_spec->args[0]) { 107 + mux = &lpc32xx_muxes[i]; 108 + break; 109 + } 110 + } 111 + if (!mux) { 112 + dev_err(&pdev->dev, "invalid mux request number: %d\n", 113 + dma_spec->args[0]); 114 + return ERR_PTR(-EINVAL); 115 + } 116 + 117 + if (dma_spec->args[2] > 1) { 118 + dev_err(&pdev->dev, "invalid dma mux value: %d\n", 119 + dma_spec->args[1]); 120 + return ERR_PTR(-EINVAL); 121 + } 122 + 123 + /* The of_node_put() will be done in the core for the node */ 124 + dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 125 + if (!dma_spec->np) { 126 + dev_err(&pdev->dev, "can't get dma master\n"); 127 + return ERR_PTR(-EINVAL); 128 + } 129 + 130 + spin_lock_irqsave(&dmamux->lock, flags); 131 + if (mux->busy) { 132 + spin_unlock_irqrestore(&dmamux->lock, flags); 133 + dev_err(dev, "dma request signal %d busy, routed to %s\n", 134 + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 135 + of_node_put(dma_spec->np); 136 + return ERR_PTR(-EBUSY); 137 + } 138 + 139 + mux->busy = true; 140 + mux->muxval = dma_spec->args[2] ? BIT(mux->bit) : 0; 141 + 142 + regmap_update_bits(dmamux->reg, mux->muxreg, BIT(mux->bit), mux->muxval); 143 + spin_unlock_irqrestore(&dmamux->lock, flags); 144 + 145 + dma_spec->args[2] = 0; 146 + dma_spec->args_count = 2; 147 + 148 + dev_dbg(dev, "dma request signal %d routed to %s\n", 149 + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 150 + 151 + return mux; 152 + } 153 + 154 + static int lpc32xx_dmamux_probe(struct platform_device *pdev) 155 + { 156 + struct device_node *np = pdev->dev.of_node; 157 + struct lpc32xx_dmamux_data *dmamux; 158 + 159 + dmamux = devm_kzalloc(&pdev->dev, sizeof(*dmamux), GFP_KERNEL); 160 + if (!dmamux) 161 + return -ENOMEM; 162 + 163 + dmamux->reg = syscon_node_to_regmap(np->parent); 164 + if (IS_ERR(dmamux->reg)) { 165 + dev_err(&pdev->dev, "syscon lookup failed\n"); 166 + return PTR_ERR(dmamux->reg); 167 + } 168 + 169 + spin_lock_init(&dmamux->lock); 170 + platform_set_drvdata(pdev, dmamux); 171 + dmamux->dmarouter.dev = &pdev->dev; 172 + dmamux->dmarouter.route_free = lpc32xx_dmamux_release; 173 + 174 + return of_dma_router_register(np, lpc32xx_dmamux_reserve, 175 + &dmamux->dmarouter); 176 + } 177 + 178 + static const struct of_device_id lpc32xx_dmamux_match[] = { 179 + { .compatible = "nxp,lpc3220-dmamux" }, 180 + {}, 181 + }; 182 + 183 + static struct platform_driver lpc32xx_dmamux_driver = { 184 + .probe = lpc32xx_dmamux_probe, 185 + .driver = { 186 + .name = "lpc32xx-dmamux", 187 + .of_match_table = lpc32xx_dmamux_match, 188 + }, 189 + }; 190 + 191 + static int __init lpc32xx_dmamux_init(void) 192 + { 193 + return platform_driver_register(&lpc32xx_dmamux_driver); 194 + } 195 + arch_initcall(lpc32xx_dmamux_init);