File Index
Files and directories
Sign in
FILE

cv1800b-dmamux.c

File preview with syntax highlighting.

linux-6.19.14/drivers/dma/cv1800b-dmamux.c 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
 60struct 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
 69struct cv1800_dmamux_map {
 70	struct llist_node node;
 71	unsigned int channel;
 72	unsigned int peripheral;
 73	unsigned int cpu;
 74};
 75
 76static 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
 96static 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 = -EINVAL;
106
107	if (dma_spec->args_count != DMAMUX_NCELLS) {
108		dev_err(&pdev->dev, "invalid number of dma mux args\n");
109		goto err_put_pdev;
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		goto err_put_pdev;
119	}
120
121	if (cpuid > MAX_DMA_CPU_ID) {
122		dev_err(&pdev->dev, "invalid cpu id: %u\n", cpuid);
123		goto err_put_pdev;
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		goto err_put_pdev;
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		goto failed;
140	} else {
141		node = llist_del_first(&dmamux->free_maps);
142		if (!node) {
143			ret = -ENODEV;
144			goto failed;
145		}
146
147		map = llist_entry(node, struct cv1800_dmamux_map, node);
148		llist_add(&map->node, &dmamux->reserve_maps);
149		set_bit(devid, dmamux->mapped_peripherals);
150	}
151
152found:
153	chid = map->channel;
154	map->peripheral = devid;
155	map->cpu = cpuid;
156
157	regmap_set_bits(dmamux->regmap,
158			DMAMUX_CH_REG(chid),
159			DMAMUX_CH_SET(chid, devid));
160
161	regmap_update_bits(dmamux->regmap, REG_DMA_INT_MUX,
162			   DMAMUX_INT_CH_MASK(chid, cpuid),
163			   DMAMUX_INT_CH_BIT(chid, cpuid));
164
165	spin_unlock_irqrestore(&dmamux->lock, flags);
166
167	dma_spec->args[0] = chid;
168
169	dev_dbg(&pdev->dev, "register channel %u for req %u (cpu %u)\n",
170		chid, devid, cpuid);
171
172	put_device(&pdev->dev);
173
174	return map;
175
176failed:
177	spin_unlock_irqrestore(&dmamux->lock, flags);
178	of_node_put(dma_spec->np);
179	dev_err(&pdev->dev, "errno %d\n", ret);
180err_put_pdev:
181	put_device(&pdev->dev);
182
183	return ERR_PTR(ret);
184}
185
186static int cv1800_dmamux_probe(struct platform_device *pdev)
187{
188	struct device *dev = &pdev->dev;
189	struct device_node *mux_node = dev->of_node;
190	struct cv1800_dmamux_data *data;
191	struct cv1800_dmamux_map *tmp;
192	struct device *parent = dev->parent;
193	struct regmap *regmap = NULL;
194	unsigned int i;
195
196	if (!parent)
197		return -ENODEV;
198
199	regmap = device_node_to_regmap(parent->of_node);
200	if (IS_ERR(regmap))
201		return PTR_ERR(regmap);
202
203	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
204	if (!data)
205		return -ENOMEM;
206
207	spin_lock_init(&data->lock);
208	init_llist_head(&data->free_maps);
209	init_llist_head(&data->reserve_maps);
210
211	for (i = 0; i <= MAX_DMA_CH_ID; i++) {
212		tmp = devm_kmalloc(dev, sizeof(*tmp), GFP_KERNEL);
213		if (!tmp) {
214			/* It is OK for not allocating all channel */
215			dev_warn(dev, "can not allocate channel %u\n", i);
216			continue;
217		}
218
219		init_llist_node(&tmp->node);
220		tmp->channel = i;
221		llist_add(&tmp->node, &data->free_maps);
222	}
223
224	/* if no channel is allocated, the probe must fail */
225	if (llist_empty(&data->free_maps))
226		return -ENOMEM;
227
228	data->regmap = regmap;
229	data->dmarouter.dev = dev;
230	data->dmarouter.route_free = cv1800_dmamux_free;
231
232	platform_set_drvdata(pdev, data);
233
234	return of_dma_router_register(mux_node,
235				      cv1800_dmamux_route_allocate,
236				      &data->dmarouter);
237}
238
239static void cv1800_dmamux_remove(struct platform_device *pdev)
240{
241	of_dma_controller_free(pdev->dev.of_node);
242}
243
244static const struct of_device_id cv1800_dmamux_ids[] = {
245	{ .compatible = "sophgo,cv1800b-dmamux", },
246	{ }
247};
248MODULE_DEVICE_TABLE(of, cv1800_dmamux_ids);
249
250static struct platform_driver cv1800_dmamux_driver = {
251	.probe = cv1800_dmamux_probe,
252	.remove = cv1800_dmamux_remove,
253	.driver = {
254		.name = "cv1800-dmamux",
255		.of_match_table = cv1800_dmamux_ids,
256	},
257};
258module_platform_driver(cv1800_dmamux_driver);
259
260MODULE_AUTHOR("Inochi Amaoto <inochiama@gmail.com>");
261MODULE_DESCRIPTION("Sophgo CV1800/SG2000 Series SoC DMAMUX driver");
262MODULE_LICENSE("GPL");
~/ Read-only file preview.