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

dmaengine: add driver for Sophgo CV18XX/SG200X dmamux

Sophgo CV18XX/SG200X use DW AXI CORE with a multiplexer for remapping
its request lines. The multiplexer supports at most 8 request lines.

Add driver for Sophgo CV18XX/SG200X DMA multiplexer.

Signed-off-by: Inochi Amaoto <inochiama@gmail.com>
Tested-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
Link: https://lore.kernel.org/r/20250611081000.1187374-3-inochiama@gmail.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Inochi Amaoto and committed by
Vinod Koul
db7d07b5 994b5709

+269
+9
drivers/dma/Kconfig
··· 572 572 These are exposed via extra functions on the switch's 573 573 upstream port. Each function exposes one DMA channel. 574 574 575 + config SOPHGO_CV1800B_DMAMUX 576 + tristate "Sophgo CV1800/SG2000 series SoC DMA multiplexer support" 577 + depends on MFD_SYSCON 578 + depends on ARCH_SOPHGO || COMPILE_TEST 579 + help 580 + Support for the DMA multiplexer on Sophgo CV1800/SG2000 581 + series SoCs. 582 + Say Y here if your board have this soc. 583 + 575 584 config STE_DMA40 576 585 bool "ST-Ericsson DMA40 support" 577 586 depends on ARCH_U8500
+1
drivers/dma/Makefile
··· 71 71 obj-$(CONFIG_PXA_DMA) += pxa_dma.o 72 72 obj-$(CONFIG_RENESAS_DMA) += sh/ 73 73 obj-$(CONFIG_SF_PDMA) += sf-pdma/ 74 + obj-$(CONFIG_SOPHGO_CV1800B_DMAMUX) += cv1800b-dmamux.o 74 75 obj-$(CONFIG_STE_DMA40) += ste_dma40.o ste_dma40_ll.o 75 76 obj-$(CONFIG_SPRD_DMA) += sprd-dma.o 76 77 obj-$(CONFIG_TXX9_DMAC) += txx9dmac.o
+259
drivers/dma/cv1800b-dmamux.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025 Inochi Amaoto <inochiama@gmail.com> 4 + */ 5 + 6 + #include <linux/bitops.h> 7 + #include <linux/cleanup.h> 8 + #include <linux/module.h> 9 + #include <linux/of_dma.h> 10 + #include <linux/of_address.h> 11 + #include <linux/of_platform.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/llist.h> 14 + #include <linux/regmap.h> 15 + #include <linux/spinlock.h> 16 + #include <linux/mfd/syscon.h> 17 + 18 + #define REG_DMA_CHANNEL_REMAP0 0x154 19 + #define REG_DMA_CHANNEL_REMAP1 0x158 20 + #define REG_DMA_INT_MUX 0x298 21 + 22 + #define DMAMUX_NCELLS 2 23 + #define MAX_DMA_MAPPING_ID 42 24 + #define MAX_DMA_CPU_ID 2 25 + #define MAX_DMA_CH_ID 7 26 + 27 + #define DMAMUX_INTMUX_REGISTER_LEN 4 28 + #define DMAMUX_NR_CH_PER_REGISTER 4 29 + #define DMAMUX_BIT_PER_CH 8 30 + #define DMAMUX_CH_MASk GENMASK(5, 0) 31 + #define DMAMUX_INT_BIT_PER_CPU 10 32 + #define DMAMUX_CH_UPDATE_BIT BIT(31) 33 + 34 + #define DMAMUX_CH_REGPOS(chid) \ 35 + ((chid) / DMAMUX_NR_CH_PER_REGISTER) 36 + #define DMAMUX_CH_REGOFF(chid) \ 37 + ((chid) % DMAMUX_NR_CH_PER_REGISTER) 38 + #define DMAMUX_CH_REG(chid) \ 39 + ((DMAMUX_CH_REGPOS(chid) * sizeof(u32)) + \ 40 + REG_DMA_CHANNEL_REMAP0) 41 + #define DMAMUX_CH_SET(chid, val) \ 42 + (((val) << (DMAMUX_CH_REGOFF(chid) * DMAMUX_BIT_PER_CH)) | \ 43 + DMAMUX_CH_UPDATE_BIT) 44 + #define DMAMUX_CH_MASK(chid) \ 45 + DMAMUX_CH_SET(chid, DMAMUX_CH_MASk) 46 + 47 + #define DMAMUX_INT_BIT(chid, cpuid) \ 48 + BIT((cpuid) * DMAMUX_INT_BIT_PER_CPU + (chid)) 49 + #define DMAMUX_INTEN_BIT(cpuid) \ 50 + DMAMUX_INT_BIT(8, cpuid) 51 + #define DMAMUX_INT_CH_BIT(chid, cpuid) \ 52 + (DMAMUX_INT_BIT(chid, cpuid) | DMAMUX_INTEN_BIT(cpuid)) 53 + #define DMAMUX_INT_MASK(chid) \ 54 + (DMAMUX_INT_BIT(chid, 0) | \ 55 + DMAMUX_INT_BIT(chid, 1) | \ 56 + DMAMUX_INT_BIT(chid, 2)) 57 + #define DMAMUX_INT_CH_MASK(chid, cpuid) \ 58 + (DMAMUX_INT_MASK(chid) | DMAMUX_INTEN_BIT(cpuid)) 59 + 60 + struct cv1800_dmamux_data { 61 + struct dma_router dmarouter; 62 + struct regmap *regmap; 63 + spinlock_t lock; 64 + struct llist_head free_maps; 65 + struct llist_head reserve_maps; 66 + DECLARE_BITMAP(mapped_peripherals, MAX_DMA_MAPPING_ID); 67 + }; 68 + 69 + struct cv1800_dmamux_map { 70 + struct llist_node node; 71 + unsigned int channel; 72 + unsigned int peripheral; 73 + unsigned int cpu; 74 + }; 75 + 76 + static void cv1800_dmamux_free(struct device *dev, void *route_data) 77 + { 78 + struct cv1800_dmamux_data *dmamux = dev_get_drvdata(dev); 79 + struct cv1800_dmamux_map *map = route_data; 80 + 81 + guard(spinlock_irqsave)(&dmamux->lock); 82 + 83 + regmap_update_bits(dmamux->regmap, 84 + DMAMUX_CH_REG(map->channel), 85 + DMAMUX_CH_MASK(map->channel), 86 + DMAMUX_CH_UPDATE_BIT); 87 + 88 + regmap_update_bits(dmamux->regmap, REG_DMA_INT_MUX, 89 + DMAMUX_INT_CH_MASK(map->channel, map->cpu), 90 + DMAMUX_INTEN_BIT(map->cpu)); 91 + 92 + dev_dbg(dev, "free channel %u for req %u (cpu %u)\n", 93 + map->channel, map->peripheral, map->cpu); 94 + } 95 + 96 + static void *cv1800_dmamux_route_allocate(struct of_phandle_args *dma_spec, 97 + struct of_dma *ofdma) 98 + { 99 + struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 100 + struct cv1800_dmamux_data *dmamux = platform_get_drvdata(pdev); 101 + struct cv1800_dmamux_map *map; 102 + struct llist_node *node; 103 + unsigned long flags; 104 + unsigned int chid, devid, cpuid; 105 + int ret; 106 + 107 + if (dma_spec->args_count != DMAMUX_NCELLS) { 108 + dev_err(&pdev->dev, "invalid number of dma mux args\n"); 109 + return ERR_PTR(-EINVAL); 110 + } 111 + 112 + devid = dma_spec->args[0]; 113 + cpuid = dma_spec->args[1]; 114 + dma_spec->args_count = 1; 115 + 116 + if (devid > MAX_DMA_MAPPING_ID) { 117 + dev_err(&pdev->dev, "invalid device id: %u\n", devid); 118 + return ERR_PTR(-EINVAL); 119 + } 120 + 121 + if (cpuid > MAX_DMA_CPU_ID) { 122 + dev_err(&pdev->dev, "invalid cpu id: %u\n", cpuid); 123 + return ERR_PTR(-EINVAL); 124 + } 125 + 126 + dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 127 + if (!dma_spec->np) { 128 + dev_err(&pdev->dev, "can't get dma master\n"); 129 + return ERR_PTR(-EINVAL); 130 + } 131 + 132 + spin_lock_irqsave(&dmamux->lock, flags); 133 + 134 + if (test_bit(devid, dmamux->mapped_peripherals)) { 135 + llist_for_each_entry(map, dmamux->reserve_maps.first, node) { 136 + if (map->peripheral == devid && map->cpu == cpuid) 137 + goto found; 138 + } 139 + 140 + ret = -EINVAL; 141 + goto failed; 142 + } else { 143 + node = llist_del_first(&dmamux->free_maps); 144 + if (!node) { 145 + ret = -ENODEV; 146 + goto failed; 147 + } 148 + 149 + map = llist_entry(node, struct cv1800_dmamux_map, node); 150 + llist_add(&map->node, &dmamux->reserve_maps); 151 + set_bit(devid, dmamux->mapped_peripherals); 152 + } 153 + 154 + found: 155 + chid = map->channel; 156 + map->peripheral = devid; 157 + map->cpu = cpuid; 158 + 159 + regmap_set_bits(dmamux->regmap, 160 + DMAMUX_CH_REG(chid), 161 + DMAMUX_CH_SET(chid, devid)); 162 + 163 + regmap_update_bits(dmamux->regmap, REG_DMA_INT_MUX, 164 + DMAMUX_INT_CH_MASK(chid, cpuid), 165 + DMAMUX_INT_CH_BIT(chid, cpuid)); 166 + 167 + spin_unlock_irqrestore(&dmamux->lock, flags); 168 + 169 + dma_spec->args[0] = chid; 170 + 171 + dev_dbg(&pdev->dev, "register channel %u for req %u (cpu %u)\n", 172 + chid, devid, cpuid); 173 + 174 + return map; 175 + 176 + failed: 177 + spin_unlock_irqrestore(&dmamux->lock, flags); 178 + of_node_put(dma_spec->np); 179 + dev_err(&pdev->dev, "errno %d\n", ret); 180 + return ERR_PTR(ret); 181 + } 182 + 183 + static int cv1800_dmamux_probe(struct platform_device *pdev) 184 + { 185 + struct device *dev = &pdev->dev; 186 + struct device_node *mux_node = dev->of_node; 187 + struct cv1800_dmamux_data *data; 188 + struct cv1800_dmamux_map *tmp; 189 + struct device *parent = dev->parent; 190 + struct regmap *regmap = NULL; 191 + unsigned int i; 192 + 193 + if (!parent) 194 + return -ENODEV; 195 + 196 + regmap = device_node_to_regmap(parent->of_node); 197 + if (IS_ERR(regmap)) 198 + return PTR_ERR(regmap); 199 + 200 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 201 + if (!data) 202 + return -ENOMEM; 203 + 204 + spin_lock_init(&data->lock); 205 + init_llist_head(&data->free_maps); 206 + init_llist_head(&data->reserve_maps); 207 + 208 + for (i = 0; i <= MAX_DMA_CH_ID; i++) { 209 + tmp = devm_kmalloc(dev, sizeof(*tmp), GFP_KERNEL); 210 + if (!tmp) { 211 + /* It is OK for not allocating all channel */ 212 + dev_warn(dev, "can not allocate channel %u\n", i); 213 + continue; 214 + } 215 + 216 + init_llist_node(&tmp->node); 217 + tmp->channel = i; 218 + llist_add(&tmp->node, &data->free_maps); 219 + } 220 + 221 + /* if no channel is allocated, the probe must fail */ 222 + if (llist_empty(&data->free_maps)) 223 + return -ENOMEM; 224 + 225 + data->regmap = regmap; 226 + data->dmarouter.dev = dev; 227 + data->dmarouter.route_free = cv1800_dmamux_free; 228 + 229 + platform_set_drvdata(pdev, data); 230 + 231 + return of_dma_router_register(mux_node, 232 + cv1800_dmamux_route_allocate, 233 + &data->dmarouter); 234 + } 235 + 236 + static void cv1800_dmamux_remove(struct platform_device *pdev) 237 + { 238 + of_dma_controller_free(pdev->dev.of_node); 239 + } 240 + 241 + static const struct of_device_id cv1800_dmamux_ids[] = { 242 + { .compatible = "sophgo,cv1800b-dmamux", }, 243 + { } 244 + }; 245 + MODULE_DEVICE_TABLE(of, cv1800_dmamux_ids); 246 + 247 + static struct platform_driver cv1800_dmamux_driver = { 248 + .probe = cv1800_dmamux_probe, 249 + .remove = cv1800_dmamux_remove, 250 + .driver = { 251 + .name = "cv1800-dmamux", 252 + .of_match_table = cv1800_dmamux_ids, 253 + }, 254 + }; 255 + module_platform_driver(cv1800_dmamux_driver); 256 + 257 + MODULE_AUTHOR("Inochi Amaoto <inochiama@gmail.com>"); 258 + MODULE_DESCRIPTION("Sophgo CV1800/SG2000 Series SoC DMAMUX driver"); 259 + MODULE_LICENSE("GPL");