1 // SPDX-License-Identifier: GPL-2.0-only
3 * DMA Router driver for LPC18xx/43xx DMA MUX
5 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
7 * Based on TI DMA Crossbar driver by:
8 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
9 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
12 #include <linux/err.h>
13 #include <linux/init.h>
14 #include <linux/mfd/syscon.h>
16 #include <linux/of_dma.h>
17 #include <linux/of_platform.h>
18 #include <linux/platform_device.h>
19 #include <linux/regmap.h>
20 #include <linux/spinlock.h>
22 /* CREG register offset and macros for mux manipulation */
23 #define LPC18XX_CREG_DMAMUX 0x11c
24 #define LPC18XX_DMAMUX_VAL(v, n) ((v) << (n * 2))
25 #define LPC18XX_DMAMUX_MASK(n) (0x3 << (n * 2))
26 #define LPC18XX_DMAMUX_MAX_VAL 0x3
28 struct lpc18xx_dmamux
{
33 struct lpc18xx_dmamux_data
{
34 struct dma_router dmarouter
;
35 struct lpc18xx_dmamux
*muxes
;
36 u32 dma_master_requests
;
42 static void lpc18xx_dmamux_free(struct device
*dev
, void *route_data
)
44 struct lpc18xx_dmamux_data
*dmamux
= dev_get_drvdata(dev
);
45 struct lpc18xx_dmamux
*mux
= route_data
;
48 spin_lock_irqsave(&dmamux
->lock
, flags
);
50 spin_unlock_irqrestore(&dmamux
->lock
, flags
);
53 static void *lpc18xx_dmamux_reserve(struct of_phandle_args
*dma_spec
,
56 struct platform_device
*pdev
= of_find_device_by_node(ofdma
->of_node
);
57 struct lpc18xx_dmamux_data
*dmamux
= platform_get_drvdata(pdev
);
61 if (dma_spec
->args_count
!= 3) {
62 dev_err(&pdev
->dev
, "invalid number of dma mux args\n");
63 return ERR_PTR(-EINVAL
);
66 mux
= dma_spec
->args
[0];
67 if (mux
>= dmamux
->dma_master_requests
) {
68 dev_err(&pdev
->dev
, "invalid mux number: %d\n",
70 return ERR_PTR(-EINVAL
);
73 if (dma_spec
->args
[1] > LPC18XX_DMAMUX_MAX_VAL
) {
74 dev_err(&pdev
->dev
, "invalid dma mux value: %d\n",
76 return ERR_PTR(-EINVAL
);
79 /* The of_node_put() will be done in the core for the node */
80 dma_spec
->np
= of_parse_phandle(ofdma
->of_node
, "dma-masters", 0);
82 dev_err(&pdev
->dev
, "can't get dma master\n");
83 return ERR_PTR(-EINVAL
);
86 spin_lock_irqsave(&dmamux
->lock
, flags
);
87 if (dmamux
->muxes
[mux
].busy
) {
88 spin_unlock_irqrestore(&dmamux
->lock
, flags
);
89 dev_err(&pdev
->dev
, "dma request %u busy with %u.%u\n",
90 mux
, mux
, dmamux
->muxes
[mux
].value
);
91 of_node_put(dma_spec
->np
);
92 return ERR_PTR(-EBUSY
);
95 dmamux
->muxes
[mux
].busy
= true;
96 dmamux
->muxes
[mux
].value
= dma_spec
->args
[1];
98 regmap_update_bits(dmamux
->reg
, LPC18XX_CREG_DMAMUX
,
99 LPC18XX_DMAMUX_MASK(mux
),
100 LPC18XX_DMAMUX_VAL(dmamux
->muxes
[mux
].value
, mux
));
101 spin_unlock_irqrestore(&dmamux
->lock
, flags
);
103 dma_spec
->args
[1] = dma_spec
->args
[2];
104 dma_spec
->args_count
= 2;
106 dev_dbg(&pdev
->dev
, "mapping dmamux %u.%u to dma request %u\n", mux
,
107 dmamux
->muxes
[mux
].value
, mux
);
109 return &dmamux
->muxes
[mux
];
112 static int lpc18xx_dmamux_probe(struct platform_device
*pdev
)
114 struct device_node
*dma_np
, *np
= pdev
->dev
.of_node
;
115 struct lpc18xx_dmamux_data
*dmamux
;
118 dmamux
= devm_kzalloc(&pdev
->dev
, sizeof(*dmamux
), GFP_KERNEL
);
122 dmamux
->reg
= syscon_regmap_lookup_by_compatible("nxp,lpc1850-creg");
123 if (IS_ERR(dmamux
->reg
)) {
124 dev_err(&pdev
->dev
, "syscon lookup failed\n");
125 return PTR_ERR(dmamux
->reg
);
128 ret
= of_property_read_u32(np
, "dma-requests",
129 &dmamux
->dma_mux_requests
);
131 dev_err(&pdev
->dev
, "missing dma-requests property\n");
135 dma_np
= of_parse_phandle(np
, "dma-masters", 0);
137 dev_err(&pdev
->dev
, "can't get dma master\n");
141 ret
= of_property_read_u32(dma_np
, "dma-requests",
142 &dmamux
->dma_master_requests
);
145 dev_err(&pdev
->dev
, "missing master dma-requests property\n");
149 dmamux
->muxes
= devm_kcalloc(&pdev
->dev
, dmamux
->dma_master_requests
,
150 sizeof(struct lpc18xx_dmamux
),
155 spin_lock_init(&dmamux
->lock
);
156 platform_set_drvdata(pdev
, dmamux
);
157 dmamux
->dmarouter
.dev
= &pdev
->dev
;
158 dmamux
->dmarouter
.route_free
= lpc18xx_dmamux_free
;
160 return of_dma_router_register(np
, lpc18xx_dmamux_reserve
,
164 static const struct of_device_id lpc18xx_dmamux_match
[] = {
165 { .compatible
= "nxp,lpc1850-dmamux" },
169 static struct platform_driver lpc18xx_dmamux_driver
= {
170 .probe
= lpc18xx_dmamux_probe
,
172 .name
= "lpc18xx-dmamux",
173 .of_match_table
= lpc18xx_dmamux_match
,
177 static int __init
lpc18xx_dmamux_init(void)
179 return platform_driver_register(&lpc18xx_dmamux_driver
);
181 arch_initcall(lpc18xx_dmamux_init
);