sun6i_dma.c revision 1.1 1 /* $NetBSD: sun6i_dma.c,v 1.1 2017/08/05 17:51:49 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2014-2017 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: sun6i_dma.c,v 1.1 2017/08/05 17:51:49 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/mutex.h>
38 #include <sys/bitops.h>
39 #include <sys/kmem.h>
40
41 #include <dev/fdt/fdtvar.h>
42
43 #define DMA_IRQ_EN_REG0_REG 0x0000
44 #define DMA_IRQ_EN_REG1_REG 0x0004
45 #define DMA_IRQ_EN_REG0_QUEUE_IRQ_EN(n) __BIT(n * 4 + 2)
46 #define DMA_IRQ_EN_REG0_PKG_IRQ_EN(n) __BIT(n * 4 + 1)
47 #define DMA_IRQ_EN_REG0_HLAF_IRQ_EN(n) __BIT(n * 4 + 0)
48 #define DMA_IRQ_EN_REG1_QUEUE_IRQ_EN(n) __BIT((n - 8) * 4 + 2)
49 #define DMA_IRQ_EN_REG1_PKG_IRQ_EN(n) __BIT((n - 8) * 4 + 1)
50 #define DMA_IRQ_EN_REG1_HLAF_IRQ_EN(n) __BIT((n - 8) * 4 + 0)
51 #define DMA_IRQ_PEND_REG0_REG 0x0010
52 #define DMA_IRQ_PEND_REG1_REG 0x0014
53 #define DMA_IRQ_QUEUE_MASK 0x4444444444444444ULL
54 #define DMA_IRQ_PKG_MASK 0x2222222222222222ULL
55 #define DMA_IRQ_HF_MASK 0x1111111111111111ULL
56 #define DMA_STA_REG 0x0030
57 #define DMA_EN_REG(n) (0x0100 + (n) * 0x40 + 0x00)
58 #define DMA_EN_EN __BIT(0)
59 #define DMA_PAU_REG(n) (0x0100 + (n) * 0x40 + 0x04)
60 #define DMA_PAU_PAUSE __BIT(0)
61 #define DMA_START_ADDR_REG(n) (0x0100 + (n) * 0x40 + 0x08)
62 #define DMA_CFG_REG(n) (0x0100 + (n) * 0x40 + 0x0C)
63 #define DMA_CFG_DEST_DATA_WIDTH __BITS(26,25)
64 #define DMA_CFG_DATA_WIDTH(n) ((n) >> 4)
65 #define DMA_CFG_DEST_BST_LEN __BITS(24,23)
66 #define DMA_CFG_BST_LEN(n) ((n) == 1 ? 0 : (((n) >> 3) + 1))
67 #define DMA_CFG_DEST_ADDR_MODE __BITS(22,21)
68 #define DMA_CFG_ADDR_MODE_LINEAR 0
69 #define DMA_CFG_ADDR_MODE_IO 1
70 #define DMA_CFG_DEST_DRQ_TYPE __BITS(20,16)
71 #define DMA_CFG_DRQ_TYPE_SDRAM 1
72 #define DMA_CFG_SRC_DATA_WIDTH __BITS(10,9)
73 #define DMA_CFG_SRC_BST_LEN __BITS(8,7)
74 #define DMA_CFG_SRC_ADDR_MODE __BITS(6,5)
75 #define DMA_CFG_SRC_DRQ_TYPE __BITS(4,0)
76 #define DMA_CUR_SRC_REG(n) (0x0100 + (n) * 0x40 + 0x10)
77 #define DMA_CUR_DEST_REG(n) (0x0100 + (n) * 0x40 + 0x14)
78 #define DMA_BCNT_LEFT_REG(n) (0x0100 + (n) * 0x40 + 0x18)
79 #define DMA_PARA_REG(n) (0x0100 + (n) * 0x40 + 0x1C)
80 #define DMA_PARA_DATA_BLK_SIZE __BITS(15,8)
81 #define DMA_PARA_WAIT_CYC __BITS(7,0)
82
83 struct sun6idma_desc {
84 uint32_t dma_config;
85 uint32_t dma_srcaddr;
86 uint32_t dma_dstaddr;
87 uint32_t dma_bcnt;
88 uint32_t dma_para;
89 uint32_t dma_next;
90 #define DMA_NULL 0xfffff800
91 };
92
93 static const struct of_compat_data compat_data[] = {
94 { "allwinner,sun6i-a31-dma", 16 },
95 { "allwinner,sun8i-a83t-dma", 8 },
96 { "allwinner,sun8i-h3-dma", 12 },
97 { NULL }
98 };
99
100 struct sun6idma_channel {
101 uint8_t ch_index;
102 void (*ch_callback)(void *);
103 void *ch_callbackarg;
104 u_int ch_portid;
105
106 bus_dma_segment_t ch_dmasegs[1];
107 bus_dmamap_t ch_dmamap;
108 void *ch_dmadesc;
109 bus_size_t ch_dmadesclen;
110 };
111
112 struct sun6idma_softc {
113 device_t sc_dev;
114 bus_space_tag_t sc_bst;
115 bus_space_handle_t sc_bsh;
116 bus_dma_tag_t sc_dmat;
117 int sc_phandle;
118 void *sc_ih;
119
120 kmutex_t sc_lock;
121
122 struct sun6idma_channel *sc_chan;
123 u_int sc_nchan;
124 };
125
126 #define DMA_READ(sc, reg) \
127 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
128 #define DMA_WRITE(sc, reg, val) \
129 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
130
131 static void *
132 sun6idma_acquire(device_t dev, const void *data, size_t len,
133 void (*cb)(void *), void *cbarg)
134 {
135 struct sun6idma_softc *sc = device_private(dev);
136 struct sun6idma_channel *ch = NULL;
137 uint32_t irqen;
138 uint8_t index;
139
140 if (len != 4)
141 return NULL;
142
143 const u_int portid = be32dec(data);
144 if (portid > __SHIFTOUT_MASK(DMA_CFG_SRC_DRQ_TYPE))
145 return NULL;
146
147 mutex_enter(&sc->sc_lock);
148
149 for (index = 0; index < sc->sc_nchan; index++) {
150 if (sc->sc_chan[index].ch_callback == NULL) {
151 ch = &sc->sc_chan[index];
152 ch->ch_callback = cb;
153 ch->ch_callbackarg = cbarg;
154 ch->ch_portid = portid;
155
156 irqen = DMA_READ(sc, index < 8 ?
157 DMA_IRQ_EN_REG0_REG :
158 DMA_IRQ_EN_REG1_REG);
159 irqen |= (index < 8 ?
160 DMA_IRQ_EN_REG0_PKG_IRQ_EN(index) :
161 DMA_IRQ_EN_REG1_PKG_IRQ_EN(index));
162 DMA_WRITE(sc, index < 8 ?
163 DMA_IRQ_EN_REG0_REG :
164 DMA_IRQ_EN_REG1_REG, irqen);
165
166 break;
167 }
168 }
169
170 mutex_exit(&sc->sc_lock);
171
172 return ch;
173 }
174
175 static void
176 sun6idma_release(device_t dev, void *priv)
177 {
178 struct sun6idma_softc *sc = device_private(dev);
179 struct sun6idma_channel *ch = priv;
180 uint32_t irqen;
181 uint8_t index = ch->ch_index;
182
183 mutex_enter(&sc->sc_lock);
184
185 irqen = DMA_READ(sc, index < 8 ?
186 DMA_IRQ_EN_REG0_REG :
187 DMA_IRQ_EN_REG1_REG);
188 irqen &= ~(index < 8 ?
189 DMA_IRQ_EN_REG0_PKG_IRQ_EN(index) :
190 DMA_IRQ_EN_REG1_PKG_IRQ_EN(index));
191 DMA_WRITE(sc, index < 8 ?
192 DMA_IRQ_EN_REG0_REG :
193 DMA_IRQ_EN_REG1_REG, irqen);
194
195 ch->ch_callback = NULL;
196 ch->ch_callbackarg = NULL;
197
198 mutex_exit(&sc->sc_lock);
199 }
200
201 static int
202 sun6idma_transfer(device_t dev, void *priv, struct fdtbus_dma_req *req)
203 {
204 struct sun6idma_softc *sc = device_private(dev);
205 struct sun6idma_channel *ch = priv;
206 struct sun6idma_desc *desc = ch->ch_dmadesc;
207 uint32_t src, dst, len, cfg, mem_cfg, dev_cfg;
208 uint32_t mem_width, dev_width, mem_burst, dev_burst;
209
210 if (req->dreq_nsegs != 1)
211 return EINVAL;
212
213 mem_width = DMA_CFG_DATA_WIDTH(req->dreq_mem_opt.opt_bus_width);
214 dev_width = DMA_CFG_DATA_WIDTH(req->dreq_dev_opt.opt_bus_width);
215 mem_burst = DMA_CFG_BST_LEN(req->dreq_mem_opt.opt_burst_len / NBBY);
216 dev_burst = DMA_CFG_BST_LEN(req->dreq_dev_opt.opt_burst_len / NBBY);
217
218 mem_cfg = __SHIFTIN(mem_width, DMA_CFG_SRC_DATA_WIDTH) |
219 __SHIFTIN(mem_burst, DMA_CFG_SRC_BST_LEN) |
220 __SHIFTIN(DMA_CFG_ADDR_MODE_LINEAR, DMA_CFG_SRC_ADDR_MODE) |
221 __SHIFTIN(DMA_CFG_DRQ_TYPE_SDRAM, DMA_CFG_SRC_DRQ_TYPE);
222 dev_cfg = __SHIFTIN(dev_width, DMA_CFG_SRC_DATA_WIDTH) |
223 __SHIFTIN(dev_burst, DMA_CFG_SRC_BST_LEN) |
224 __SHIFTIN(DMA_CFG_ADDR_MODE_IO, DMA_CFG_SRC_ADDR_MODE) |
225 __SHIFTIN(ch->ch_portid, DMA_CFG_SRC_DRQ_TYPE);
226
227 if (req->dreq_dir == FDT_DMA_READ) {
228 src = req->dreq_dev_phys;
229 dst = req->dreq_segs[0].ds_addr;
230 cfg = mem_cfg << 16 | dev_cfg;
231 } else {
232 src = req->dreq_segs[0].ds_addr;
233 dst = req->dreq_dev_phys;
234 cfg = dev_cfg << 16 | mem_cfg;
235 }
236 len = req->dreq_segs[0].ds_len;
237
238 desc->dma_config = htole32(cfg);
239 desc->dma_srcaddr = htole32(src);
240 desc->dma_dstaddr = htole32(dst);
241 desc->dma_bcnt = htole32(len);
242 desc->dma_para = htole32(0);
243 desc->dma_next = htole32(DMA_NULL);
244
245 bus_dmamap_sync(sc->sc_dmat, ch->ch_dmamap, 0, ch->ch_dmadesclen,
246 BUS_DMASYNC_PREWRITE);
247
248 DMA_WRITE(sc, DMA_START_ADDR_REG(ch->ch_index),
249 ch->ch_dmamap->dm_segs[0].ds_addr);
250 DMA_WRITE(sc, DMA_EN_REG(ch->ch_index), DMA_EN_EN);
251
252 return 0;
253 }
254
255 static void
256 sun6idma_halt(device_t dev, void *priv)
257 {
258 struct sun6idma_softc *sc = device_private(dev);
259 struct sun6idma_channel *ch = priv;
260
261 DMA_WRITE(sc, DMA_EN_REG(ch->ch_index), 0);
262 }
263
264 static const struct fdtbus_dma_controller_func sun6idma_funcs = {
265 .acquire = sun6idma_acquire,
266 .release = sun6idma_release,
267 .transfer = sun6idma_transfer,
268 .halt = sun6idma_halt
269 };
270
271 static int
272 sun6idma_intr(void *priv)
273 {
274 struct sun6idma_softc *sc = priv;
275 uint32_t pend0, pend1, bit;
276 uint64_t pend, mask;
277 uint8_t index;
278
279 pend0 = DMA_READ(sc, DMA_IRQ_PEND_REG0_REG);
280 pend1 = DMA_READ(sc, DMA_IRQ_PEND_REG1_REG);
281 if (!pend0 && !pend1)
282 return 0;
283
284 DMA_WRITE(sc, DMA_IRQ_PEND_REG0_REG, pend0);
285 DMA_WRITE(sc, DMA_IRQ_PEND_REG1_REG, pend1);
286
287 pend = pend0 | ((uint64_t)pend1 << 32);
288
289 while ((bit = ffs64(pend & DMA_IRQ_PKG_MASK)) != 0) {
290 mask = __BIT(bit - 1);
291 pend &= ~mask;
292 index = (bit - 1) / 4;
293
294 if (sc->sc_chan[index].ch_callback == NULL)
295 continue;
296 sc->sc_chan[index].ch_callback(
297 sc->sc_chan[index].ch_callbackarg);
298 }
299
300 return 1;
301 }
302
303 static int
304 sun6idma_match(device_t parent, cfdata_t cf, void *aux)
305 {
306 struct fdt_attach_args * const faa = aux;
307
308 return of_match_compat_data(faa->faa_phandle, compat_data);
309 }
310
311 static void
312 sun6idma_attach(device_t parent, device_t self, void *aux)
313 {
314 struct sun6idma_softc * const sc = device_private(self);
315 struct fdt_attach_args * const faa = aux;
316 const int phandle = faa->faa_phandle;
317 const size_t desclen = sizeof(struct sun6idma_desc);
318 struct fdtbus_reset *rst;
319 struct clk *clk;
320 char intrstr[128];
321 bus_addr_t addr;
322 bus_size_t size;
323 int error, nsegs;
324 u_int index;
325
326 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
327 aprint_error(": couldn't get registers\n");
328 return;
329 }
330
331 if ((clk = fdtbus_clock_get_index(phandle, 0)) == NULL ||
332 clk_enable(clk) != 0) {
333 aprint_error(": couldn't enable clock\n");
334 return;
335 }
336 if ((rst = fdtbus_reset_get_index(phandle, 0)) == NULL ||
337 fdtbus_reset_deassert(rst) != 0) {
338 aprint_error(": couldn't de-assert reset\n");
339 return;
340 }
341
342 sc->sc_dev = self;
343 sc->sc_phandle = phandle;
344 sc->sc_dmat = faa->faa_dmat;
345 sc->sc_bst = faa->faa_bst;
346 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
347 aprint_error(": couldn't map registers\n");
348 return;
349 }
350 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SCHED);
351
352 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
353 aprint_error(": failed to decode interrupt\n");
354 return;
355 }
356
357 sc->sc_nchan = of_search_compatible(phandle, compat_data)->data;
358 sc->sc_chan = kmem_alloc(sizeof(*sc->sc_chan) * sc->sc_nchan, KM_SLEEP);
359
360 aprint_naive("\n");
361 aprint_normal(": DMA controller (%u channels)\n", sc->sc_nchan);
362
363 DMA_WRITE(sc, DMA_IRQ_EN_REG0_REG, 0);
364 DMA_WRITE(sc, DMA_IRQ_EN_REG1_REG, 0);
365 DMA_WRITE(sc, DMA_IRQ_PEND_REG0_REG, ~0);
366 DMA_WRITE(sc, DMA_IRQ_PEND_REG1_REG, ~0);
367
368 for (index = 0; index < sc->sc_nchan; index++) {
369 struct sun6idma_channel *ch = &sc->sc_chan[index];
370 ch->ch_index = index;
371 ch->ch_callback = NULL;
372 ch->ch_callbackarg = NULL;
373 ch->ch_dmadesclen = desclen;
374
375 error = bus_dmamem_alloc(sc->sc_dmat, desclen, 0, 0,
376 ch->ch_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
377 if (error)
378 panic("bus_dmamem_alloc failed: %d", error);
379 error = bus_dmamem_map(sc->sc_dmat, ch->ch_dmasegs, nsegs,
380 desclen, &ch->ch_dmadesc, BUS_DMA_WAITOK);
381 if (error)
382 panic("bus_dmamem_map failed: %d", error);
383 error = bus_dmamap_create(sc->sc_dmat, desclen, 1, desclen, 0,
384 BUS_DMA_WAITOK, &ch->ch_dmamap);
385 if (error)
386 panic("bus_dmamap_create failed: %d", error);
387 error = bus_dmamap_load(sc->sc_dmat, ch->ch_dmamap,
388 ch->ch_dmadesc, desclen, NULL, BUS_DMA_WAITOK);
389 if (error)
390 panic("bus_dmamap_load failed: %d", error);
391
392 DMA_WRITE(sc, DMA_EN_REG(index), 0);
393 }
394
395 sc->sc_ih = fdtbus_intr_establish(phandle, 0, IPL_SCHED, FDT_INTR_MPSAFE,
396 sun6idma_intr, sc);
397 if (sc->sc_ih == NULL) {
398 aprint_error_dev(sc->sc_dev,
399 "couldn't establish interrupt on %s\n", intrstr);
400 return;
401 }
402 aprint_normal_dev(sc->sc_dev, "interrupting on %s\n", intrstr);
403
404 fdtbus_register_dma_controller(self, phandle, &sun6idma_funcs);
405 }
406
407 CFATTACH_DECL_NEW(sun6i_dma, sizeof(struct sun6idma_softc),
408 sun6idma_match, sun6idma_attach, NULL, NULL);
409