1c42f10a2SRuslan Bukin /*-
2c42f10a2SRuslan Bukin * Copyright (c) 2017-2018 Ruslan Bukin <br@bsdpad.com>
3c42f10a2SRuslan Bukin * All rights reserved.
4c42f10a2SRuslan Bukin *
5c42f10a2SRuslan Bukin * This software was developed by SRI International and the University of
6c42f10a2SRuslan Bukin * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
7c42f10a2SRuslan Bukin * ("CTSRD"), as part of the DARPA CRASH research programme.
8c42f10a2SRuslan Bukin *
9c42f10a2SRuslan Bukin * Redistribution and use in source and binary forms, with or without
10c42f10a2SRuslan Bukin * modification, are permitted provided that the following conditions
11c42f10a2SRuslan Bukin * are met:
12c42f10a2SRuslan Bukin * 1. Redistributions of source code must retain the above copyright
13c42f10a2SRuslan Bukin * notice, this list of conditions and the following disclaimer.
14c42f10a2SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright
15c42f10a2SRuslan Bukin * notice, this list of conditions and the following disclaimer in the
16c42f10a2SRuslan Bukin * documentation and/or other materials provided with the distribution.
17c42f10a2SRuslan Bukin *
18c42f10a2SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19c42f10a2SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20c42f10a2SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21c42f10a2SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22c42f10a2SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23c42f10a2SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24c42f10a2SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25c42f10a2SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26c42f10a2SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27c42f10a2SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28c42f10a2SRuslan Bukin * SUCH DAMAGE.
29c42f10a2SRuslan Bukin */
30c42f10a2SRuslan Bukin
31c42f10a2SRuslan Bukin /* ARM PrimeCell DMA Controller (PL330) driver. */
32c42f10a2SRuslan Bukin
33c42f10a2SRuslan Bukin #include <sys/cdefs.h>
34c42f10a2SRuslan Bukin #include "opt_platform.h"
35c42f10a2SRuslan Bukin #include <sys/param.h>
36c42f10a2SRuslan Bukin #include <sys/endian.h>
37c42f10a2SRuslan Bukin #include <sys/systm.h>
38c42f10a2SRuslan Bukin #include <sys/conf.h>
39c42f10a2SRuslan Bukin #include <sys/bus.h>
40c42f10a2SRuslan Bukin #include <sys/kernel.h>
41c42f10a2SRuslan Bukin #include <sys/kthread.h>
42c42f10a2SRuslan Bukin #include <sys/sglist.h>
43c42f10a2SRuslan Bukin #include <sys/module.h>
44c42f10a2SRuslan Bukin #include <sys/lock.h>
45c42f10a2SRuslan Bukin #include <sys/resource.h>
46c42f10a2SRuslan Bukin #include <sys/rman.h>
47c42f10a2SRuslan Bukin
48c42f10a2SRuslan Bukin #include <vm/vm.h>
49c42f10a2SRuslan Bukin #include <vm/vm_extern.h>
50c42f10a2SRuslan Bukin #include <vm/vm_kern.h>
51c42f10a2SRuslan Bukin #include <vm/pmap.h>
52c42f10a2SRuslan Bukin
53c42f10a2SRuslan Bukin #include <machine/bus.h>
54c42f10a2SRuslan Bukin
55c42f10a2SRuslan Bukin #ifdef FDT
56c42f10a2SRuslan Bukin #include <dev/fdt/fdt_common.h>
57c42f10a2SRuslan Bukin #include <dev/ofw/ofw_bus.h>
58c42f10a2SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
59c42f10a2SRuslan Bukin #endif
60c42f10a2SRuslan Bukin
61c42f10a2SRuslan Bukin #include <dev/xdma/xdma.h>
62c42f10a2SRuslan Bukin #include <dev/xdma/controller/pl330.h>
63c42f10a2SRuslan Bukin
64c42f10a2SRuslan Bukin #include "xdma_if.h"
65c42f10a2SRuslan Bukin
66c42f10a2SRuslan Bukin #define PL330_DEBUG
67c42f10a2SRuslan Bukin #undef PL330_DEBUG
68c42f10a2SRuslan Bukin
69c42f10a2SRuslan Bukin #ifdef PL330_DEBUG
70c42f10a2SRuslan Bukin #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
71c42f10a2SRuslan Bukin #else
72c42f10a2SRuslan Bukin #define dprintf(fmt, ...)
73c42f10a2SRuslan Bukin #endif
74c42f10a2SRuslan Bukin
75c42f10a2SRuslan Bukin #define READ4(_sc, _reg) \
76c42f10a2SRuslan Bukin bus_read_4(_sc->res[0], _reg)
77c42f10a2SRuslan Bukin #define WRITE4(_sc, _reg, _val) \
78c42f10a2SRuslan Bukin bus_write_4(_sc->res[0], _reg, _val)
79c42f10a2SRuslan Bukin
80c42f10a2SRuslan Bukin #define PL330_NCHANNELS 32
81c42f10a2SRuslan Bukin #define PL330_MAXLOAD 2048
82c42f10a2SRuslan Bukin
83c42f10a2SRuslan Bukin struct pl330_channel {
84c42f10a2SRuslan Bukin struct pl330_softc *sc;
85c42f10a2SRuslan Bukin xdma_channel_t *xchan;
86c42f10a2SRuslan Bukin int used;
87c42f10a2SRuslan Bukin int index;
88c42f10a2SRuslan Bukin uint8_t *ibuf;
89c42f10a2SRuslan Bukin bus_addr_t ibuf_phys;
90c42f10a2SRuslan Bukin uint32_t enqueued;
91c42f10a2SRuslan Bukin uint32_t capacity;
92c42f10a2SRuslan Bukin };
93c42f10a2SRuslan Bukin
94c42f10a2SRuslan Bukin struct pl330_fdt_data {
95c42f10a2SRuslan Bukin uint32_t periph_id;
96c42f10a2SRuslan Bukin };
97c42f10a2SRuslan Bukin
98c42f10a2SRuslan Bukin struct pl330_softc {
99c42f10a2SRuslan Bukin device_t dev;
100c42f10a2SRuslan Bukin struct resource *res[PL330_NCHANNELS + 1];
101c42f10a2SRuslan Bukin void *ih[PL330_NCHANNELS];
102c42f10a2SRuslan Bukin struct pl330_channel channels[PL330_NCHANNELS];
103c42f10a2SRuslan Bukin };
104c42f10a2SRuslan Bukin
105c42f10a2SRuslan Bukin static struct resource_spec pl330_spec[] = {
106c42f10a2SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE },
107c42f10a2SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE },
108c42f10a2SRuslan Bukin { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL },
109c42f10a2SRuslan Bukin { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL },
110c42f10a2SRuslan Bukin { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL },
111c42f10a2SRuslan Bukin { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL },
112c42f10a2SRuslan Bukin { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL },
113c42f10a2SRuslan Bukin { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL },
114c42f10a2SRuslan Bukin { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL },
115c42f10a2SRuslan Bukin { SYS_RES_IRQ, 8, RF_ACTIVE | RF_OPTIONAL },
116c42f10a2SRuslan Bukin { SYS_RES_IRQ, 9, RF_ACTIVE | RF_OPTIONAL },
117c42f10a2SRuslan Bukin { SYS_RES_IRQ, 10, RF_ACTIVE | RF_OPTIONAL },
118c42f10a2SRuslan Bukin { SYS_RES_IRQ, 11, RF_ACTIVE | RF_OPTIONAL },
119c42f10a2SRuslan Bukin { SYS_RES_IRQ, 12, RF_ACTIVE | RF_OPTIONAL },
120c42f10a2SRuslan Bukin { SYS_RES_IRQ, 13, RF_ACTIVE | RF_OPTIONAL },
121c42f10a2SRuslan Bukin { SYS_RES_IRQ, 14, RF_ACTIVE | RF_OPTIONAL },
122c42f10a2SRuslan Bukin { SYS_RES_IRQ, 15, RF_ACTIVE | RF_OPTIONAL },
123c42f10a2SRuslan Bukin { SYS_RES_IRQ, 16, RF_ACTIVE | RF_OPTIONAL },
124c42f10a2SRuslan Bukin { SYS_RES_IRQ, 17, RF_ACTIVE | RF_OPTIONAL },
125c42f10a2SRuslan Bukin { SYS_RES_IRQ, 18, RF_ACTIVE | RF_OPTIONAL },
126c42f10a2SRuslan Bukin { SYS_RES_IRQ, 19, RF_ACTIVE | RF_OPTIONAL },
127c42f10a2SRuslan Bukin { SYS_RES_IRQ, 20, RF_ACTIVE | RF_OPTIONAL },
128c42f10a2SRuslan Bukin { SYS_RES_IRQ, 21, RF_ACTIVE | RF_OPTIONAL },
129c42f10a2SRuslan Bukin { SYS_RES_IRQ, 22, RF_ACTIVE | RF_OPTIONAL },
130c42f10a2SRuslan Bukin { SYS_RES_IRQ, 23, RF_ACTIVE | RF_OPTIONAL },
131c42f10a2SRuslan Bukin { SYS_RES_IRQ, 24, RF_ACTIVE | RF_OPTIONAL },
132c42f10a2SRuslan Bukin { SYS_RES_IRQ, 25, RF_ACTIVE | RF_OPTIONAL },
133c42f10a2SRuslan Bukin { SYS_RES_IRQ, 26, RF_ACTIVE | RF_OPTIONAL },
134c42f10a2SRuslan Bukin { SYS_RES_IRQ, 27, RF_ACTIVE | RF_OPTIONAL },
135c42f10a2SRuslan Bukin { SYS_RES_IRQ, 28, RF_ACTIVE | RF_OPTIONAL },
136c42f10a2SRuslan Bukin { SYS_RES_IRQ, 29, RF_ACTIVE | RF_OPTIONAL },
137c42f10a2SRuslan Bukin { SYS_RES_IRQ, 30, RF_ACTIVE | RF_OPTIONAL },
138c42f10a2SRuslan Bukin { SYS_RES_IRQ, 31, RF_ACTIVE | RF_OPTIONAL },
139c42f10a2SRuslan Bukin { -1, 0 }
140c42f10a2SRuslan Bukin };
141c42f10a2SRuslan Bukin
142c42f10a2SRuslan Bukin #define HWTYPE_NONE 0
143c42f10a2SRuslan Bukin #define HWTYPE_STD 1
144c42f10a2SRuslan Bukin
145c42f10a2SRuslan Bukin static struct ofw_compat_data compat_data[] = {
146c42f10a2SRuslan Bukin { "arm,pl330", HWTYPE_STD },
147c42f10a2SRuslan Bukin { NULL, HWTYPE_NONE },
148c42f10a2SRuslan Bukin };
149c42f10a2SRuslan Bukin
150c42f10a2SRuslan Bukin static void
pl330_intr(void * arg)151c42f10a2SRuslan Bukin pl330_intr(void *arg)
152c42f10a2SRuslan Bukin {
153c42f10a2SRuslan Bukin xdma_transfer_status_t status;
154c42f10a2SRuslan Bukin struct xdma_transfer_status st;
155c42f10a2SRuslan Bukin struct pl330_channel *chan;
156c42f10a2SRuslan Bukin struct xdma_channel *xchan;
157c42f10a2SRuslan Bukin struct pl330_softc *sc;
158c42f10a2SRuslan Bukin uint32_t pending;
159c42f10a2SRuslan Bukin int i;
160c42f10a2SRuslan Bukin int c;
161c42f10a2SRuslan Bukin
162c42f10a2SRuslan Bukin sc = arg;
163c42f10a2SRuslan Bukin
164c42f10a2SRuslan Bukin pending = READ4(sc, INTMIS);
165c42f10a2SRuslan Bukin
166c42f10a2SRuslan Bukin dprintf("%s: 0x%x, LC0 %x, SAR %x DAR %x\n",
167c42f10a2SRuslan Bukin __func__, pending, READ4(sc, LC0(0)),
168c42f10a2SRuslan Bukin READ4(sc, SAR(0)), READ4(sc, DAR(0)));
169c42f10a2SRuslan Bukin
170c42f10a2SRuslan Bukin WRITE4(sc, INTCLR, pending);
171c42f10a2SRuslan Bukin
172c42f10a2SRuslan Bukin for (c = 0; c < PL330_NCHANNELS; c++) {
173c42f10a2SRuslan Bukin if ((pending & (1 << c)) == 0) {
174c42f10a2SRuslan Bukin continue;
175c42f10a2SRuslan Bukin }
176c42f10a2SRuslan Bukin chan = &sc->channels[c];
177c42f10a2SRuslan Bukin xchan = chan->xchan;
178c42f10a2SRuslan Bukin st.error = 0;
179c42f10a2SRuslan Bukin st.transferred = 0;
180c42f10a2SRuslan Bukin for (i = 0; i < chan->enqueued; i++) {
181c42f10a2SRuslan Bukin xchan_seg_done(xchan, &st);
182c42f10a2SRuslan Bukin }
183c42f10a2SRuslan Bukin
184c42f10a2SRuslan Bukin /* Accept new requests. */
185c42f10a2SRuslan Bukin chan->capacity = PL330_MAXLOAD;
186c42f10a2SRuslan Bukin
187c42f10a2SRuslan Bukin /* Finish operation */
188c42f10a2SRuslan Bukin status.error = 0;
189c42f10a2SRuslan Bukin status.transferred = 0;
190c42f10a2SRuslan Bukin xdma_callback(chan->xchan, &status);
191c42f10a2SRuslan Bukin }
192c42f10a2SRuslan Bukin }
193c42f10a2SRuslan Bukin
194c42f10a2SRuslan Bukin static uint32_t
emit_mov(uint8_t * buf,uint32_t reg,uint32_t val)195c42f10a2SRuslan Bukin emit_mov(uint8_t *buf, uint32_t reg, uint32_t val)
196c42f10a2SRuslan Bukin {
197c42f10a2SRuslan Bukin
198c42f10a2SRuslan Bukin buf[0] = DMAMOV;
199c42f10a2SRuslan Bukin buf[1] = reg;
200c42f10a2SRuslan Bukin buf[2] = val;
201c42f10a2SRuslan Bukin buf[3] = val >> 8;
202c42f10a2SRuslan Bukin buf[4] = val >> 16;
203c42f10a2SRuslan Bukin buf[5] = val >> 24;
204c42f10a2SRuslan Bukin
205c42f10a2SRuslan Bukin return (6);
206c42f10a2SRuslan Bukin }
207c42f10a2SRuslan Bukin
208c42f10a2SRuslan Bukin static uint32_t
emit_lp(uint8_t * buf,uint8_t idx,uint32_t iter)209c42f10a2SRuslan Bukin emit_lp(uint8_t *buf, uint8_t idx, uint32_t iter)
210c42f10a2SRuslan Bukin {
211c42f10a2SRuslan Bukin
212c42f10a2SRuslan Bukin if (idx > 1)
213c42f10a2SRuslan Bukin return (0); /* We have two loops only. */
214c42f10a2SRuslan Bukin
215c42f10a2SRuslan Bukin buf[0] = DMALP;
216c42f10a2SRuslan Bukin buf[0] |= (idx << 1);
217c42f10a2SRuslan Bukin buf[1] = (iter - 1) & 0xff;
218c42f10a2SRuslan Bukin
219c42f10a2SRuslan Bukin return (2);
220c42f10a2SRuslan Bukin }
221c42f10a2SRuslan Bukin
222c42f10a2SRuslan Bukin static uint32_t
emit_lpend(uint8_t * buf,uint8_t idx,uint8_t burst,uint8_t jump_addr_relative)223c42f10a2SRuslan Bukin emit_lpend(uint8_t *buf, uint8_t idx,
224c42f10a2SRuslan Bukin uint8_t burst, uint8_t jump_addr_relative)
225c42f10a2SRuslan Bukin {
226c42f10a2SRuslan Bukin
227c42f10a2SRuslan Bukin buf[0] = DMALPEND;
228c42f10a2SRuslan Bukin buf[0] |= DMALPEND_NF;
229c42f10a2SRuslan Bukin buf[0] |= (idx << 2);
230c42f10a2SRuslan Bukin if (burst)
231c42f10a2SRuslan Bukin buf[0] |= (1 << 1) | (1 << 0);
232c42f10a2SRuslan Bukin else
233c42f10a2SRuslan Bukin buf[0] |= (0 << 1) | (1 << 0);
234c42f10a2SRuslan Bukin buf[1] = jump_addr_relative;
235c42f10a2SRuslan Bukin
236c42f10a2SRuslan Bukin return (2);
237c42f10a2SRuslan Bukin }
238c42f10a2SRuslan Bukin
239c42f10a2SRuslan Bukin static uint32_t
emit_ld(uint8_t * buf,uint8_t burst)240c42f10a2SRuslan Bukin emit_ld(uint8_t *buf, uint8_t burst)
241c42f10a2SRuslan Bukin {
242c42f10a2SRuslan Bukin
243c42f10a2SRuslan Bukin buf[0] = DMALD;
244c42f10a2SRuslan Bukin if (burst)
245c42f10a2SRuslan Bukin buf[0] |= (1 << 1) | (1 << 0);
246c42f10a2SRuslan Bukin else
247c42f10a2SRuslan Bukin buf[0] |= (0 << 1) | (1 << 0);
248c42f10a2SRuslan Bukin
249c42f10a2SRuslan Bukin return (1);
250c42f10a2SRuslan Bukin }
251c42f10a2SRuslan Bukin
252c42f10a2SRuslan Bukin static uint32_t
emit_st(uint8_t * buf,uint8_t burst)253c42f10a2SRuslan Bukin emit_st(uint8_t *buf, uint8_t burst)
254c42f10a2SRuslan Bukin {
255c42f10a2SRuslan Bukin
256c42f10a2SRuslan Bukin buf[0] = DMAST;
257c42f10a2SRuslan Bukin if (burst)
258c42f10a2SRuslan Bukin buf[0] |= (1 << 1) | (1 << 0);
259c42f10a2SRuslan Bukin else
260c42f10a2SRuslan Bukin buf[0] |= (0 << 1) | (1 << 0);
261c42f10a2SRuslan Bukin
262c42f10a2SRuslan Bukin return (1);
263c42f10a2SRuslan Bukin }
264c42f10a2SRuslan Bukin
265c42f10a2SRuslan Bukin static uint32_t
emit_end(uint8_t * buf)266c42f10a2SRuslan Bukin emit_end(uint8_t *buf)
267c42f10a2SRuslan Bukin {
268c42f10a2SRuslan Bukin
269c42f10a2SRuslan Bukin buf[0] = DMAEND;
270c42f10a2SRuslan Bukin
271c42f10a2SRuslan Bukin return (1);
272c42f10a2SRuslan Bukin }
273c42f10a2SRuslan Bukin
274c42f10a2SRuslan Bukin static uint32_t
emit_sev(uint8_t * buf,uint32_t ev)275c42f10a2SRuslan Bukin emit_sev(uint8_t *buf, uint32_t ev)
276c42f10a2SRuslan Bukin {
277c42f10a2SRuslan Bukin
278c42f10a2SRuslan Bukin buf[0] = DMASEV;
279c42f10a2SRuslan Bukin buf[1] = (ev << 3);
280c42f10a2SRuslan Bukin
281c42f10a2SRuslan Bukin return (2);
282c42f10a2SRuslan Bukin }
283c42f10a2SRuslan Bukin
284c42f10a2SRuslan Bukin static uint32_t
emit_wfp(uint8_t * buf,uint32_t p_id)285c42f10a2SRuslan Bukin emit_wfp(uint8_t *buf, uint32_t p_id)
286c42f10a2SRuslan Bukin {
287c42f10a2SRuslan Bukin
288c42f10a2SRuslan Bukin buf[0] = DMAWFP;
289c42f10a2SRuslan Bukin buf[0] |= (1 << 0);
290c42f10a2SRuslan Bukin buf[1] = (p_id << 3);
291c42f10a2SRuslan Bukin
292c42f10a2SRuslan Bukin return (2);
293c42f10a2SRuslan Bukin }
294c42f10a2SRuslan Bukin
295c42f10a2SRuslan Bukin static uint32_t
emit_go(uint8_t * buf,uint32_t chan_id,uint32_t addr,uint8_t non_secure)296c42f10a2SRuslan Bukin emit_go(uint8_t *buf, uint32_t chan_id,
297c42f10a2SRuslan Bukin uint32_t addr, uint8_t non_secure)
298c42f10a2SRuslan Bukin {
299c42f10a2SRuslan Bukin
300c42f10a2SRuslan Bukin buf[0] = DMAGO;
301c42f10a2SRuslan Bukin buf[0] |= (non_secure << 1);
302c42f10a2SRuslan Bukin
303c42f10a2SRuslan Bukin buf[1] = chan_id;
304c42f10a2SRuslan Bukin buf[2] = addr;
305c42f10a2SRuslan Bukin buf[3] = addr >> 8;
306c42f10a2SRuslan Bukin buf[4] = addr >> 16;
307c42f10a2SRuslan Bukin buf[5] = addr >> 24;
308c42f10a2SRuslan Bukin
309c42f10a2SRuslan Bukin return (6);
310c42f10a2SRuslan Bukin }
311c42f10a2SRuslan Bukin
312c42f10a2SRuslan Bukin static int
pl330_probe(device_t dev)313c42f10a2SRuslan Bukin pl330_probe(device_t dev)
314c42f10a2SRuslan Bukin {
315c42f10a2SRuslan Bukin int hwtype;
316c42f10a2SRuslan Bukin
317c42f10a2SRuslan Bukin if (!ofw_bus_status_okay(dev))
318c42f10a2SRuslan Bukin return (ENXIO);
319c42f10a2SRuslan Bukin
320c42f10a2SRuslan Bukin hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
321c42f10a2SRuslan Bukin if (hwtype == HWTYPE_NONE)
322c42f10a2SRuslan Bukin return (ENXIO);
323c42f10a2SRuslan Bukin
324c42f10a2SRuslan Bukin device_set_desc(dev, "ARM PrimeCell DMA Controller (PL330)");
325c42f10a2SRuslan Bukin
326c42f10a2SRuslan Bukin return (BUS_PROBE_DEFAULT);
327c42f10a2SRuslan Bukin }
328c42f10a2SRuslan Bukin
329c42f10a2SRuslan Bukin static int
pl330_attach(device_t dev)330c42f10a2SRuslan Bukin pl330_attach(device_t dev)
331c42f10a2SRuslan Bukin {
332c42f10a2SRuslan Bukin struct pl330_softc *sc;
333c42f10a2SRuslan Bukin phandle_t xref, node;
334c42f10a2SRuslan Bukin int err;
335c42f10a2SRuslan Bukin int i;
336c42f10a2SRuslan Bukin
337c42f10a2SRuslan Bukin sc = device_get_softc(dev);
338c42f10a2SRuslan Bukin sc->dev = dev;
339c42f10a2SRuslan Bukin
340c42f10a2SRuslan Bukin if (bus_alloc_resources(dev, pl330_spec, sc->res)) {
341c42f10a2SRuslan Bukin device_printf(dev, "could not allocate resources for device\n");
342c42f10a2SRuslan Bukin return (ENXIO);
343c42f10a2SRuslan Bukin }
344c42f10a2SRuslan Bukin
345c42f10a2SRuslan Bukin /* Setup interrupt handler */
346c42f10a2SRuslan Bukin for (i = 0; i < PL330_NCHANNELS; i++) {
347c42f10a2SRuslan Bukin if (sc->res[i + 1] == NULL)
348c42f10a2SRuslan Bukin break;
349c42f10a2SRuslan Bukin err = bus_setup_intr(dev, sc->res[i + 1], INTR_TYPE_MISC | INTR_MPSAFE,
350c42f10a2SRuslan Bukin NULL, pl330_intr, sc, sc->ih[i]);
351c42f10a2SRuslan Bukin if (err) {
352c42f10a2SRuslan Bukin device_printf(dev, "Unable to alloc interrupt resource.\n");
353c42f10a2SRuslan Bukin return (ENXIO);
354c42f10a2SRuslan Bukin }
355c42f10a2SRuslan Bukin }
356c42f10a2SRuslan Bukin
357c42f10a2SRuslan Bukin node = ofw_bus_get_node(dev);
358c42f10a2SRuslan Bukin xref = OF_xref_from_node(node);
359c42f10a2SRuslan Bukin OF_device_register_xref(xref, dev);
360c42f10a2SRuslan Bukin
361c42f10a2SRuslan Bukin return (0);
362c42f10a2SRuslan Bukin }
363c42f10a2SRuslan Bukin
364c42f10a2SRuslan Bukin static int
pl330_detach(device_t dev)365c42f10a2SRuslan Bukin pl330_detach(device_t dev)
366c42f10a2SRuslan Bukin {
367c42f10a2SRuslan Bukin return (0);
368c42f10a2SRuslan Bukin }
369c42f10a2SRuslan Bukin
370c42f10a2SRuslan Bukin static int
pl330_channel_alloc(device_t dev,struct xdma_channel * xchan)371c42f10a2SRuslan Bukin pl330_channel_alloc(device_t dev, struct xdma_channel *xchan)
372c42f10a2SRuslan Bukin {
373c42f10a2SRuslan Bukin struct pl330_channel *chan;
374c42f10a2SRuslan Bukin struct pl330_softc *sc;
375c42f10a2SRuslan Bukin int i;
376c42f10a2SRuslan Bukin
377c42f10a2SRuslan Bukin sc = device_get_softc(dev);
378c42f10a2SRuslan Bukin
379c42f10a2SRuslan Bukin for (i = 0; i < PL330_NCHANNELS; i++) {
380c42f10a2SRuslan Bukin chan = &sc->channels[i];
381c42f10a2SRuslan Bukin if (chan->used == 0) {
382c42f10a2SRuslan Bukin chan->xchan = xchan;
383c42f10a2SRuslan Bukin xchan->chan = (void *)chan;
384c42f10a2SRuslan Bukin xchan->caps |= XCHAN_CAP_BUSDMA;
385c42f10a2SRuslan Bukin chan->index = i;
386c42f10a2SRuslan Bukin chan->sc = sc;
387c42f10a2SRuslan Bukin chan->used = 1;
388c42f10a2SRuslan Bukin
38944d0efb2SAlan Cox chan->ibuf = (void *)kmem_alloc_contig(PAGE_SIZE * 8,
39044d0efb2SAlan Cox M_ZERO, 0, ~0, PAGE_SIZE, 0,
391c42f10a2SRuslan Bukin VM_MEMATTR_UNCACHEABLE);
392c42f10a2SRuslan Bukin chan->ibuf_phys = vtophys(chan->ibuf);
393c42f10a2SRuslan Bukin
394c42f10a2SRuslan Bukin return (0);
395c42f10a2SRuslan Bukin }
396c42f10a2SRuslan Bukin }
397c42f10a2SRuslan Bukin
398c42f10a2SRuslan Bukin return (-1);
399c42f10a2SRuslan Bukin }
400c42f10a2SRuslan Bukin
401c42f10a2SRuslan Bukin static int
pl330_channel_free(device_t dev,struct xdma_channel * xchan)402c42f10a2SRuslan Bukin pl330_channel_free(device_t dev, struct xdma_channel *xchan)
403c42f10a2SRuslan Bukin {
404c42f10a2SRuslan Bukin struct pl330_channel *chan;
405c42f10a2SRuslan Bukin
406c42f10a2SRuslan Bukin chan = (struct pl330_channel *)xchan->chan;
407c42f10a2SRuslan Bukin chan->used = 0;
408c42f10a2SRuslan Bukin
409c42f10a2SRuslan Bukin return (0);
410c42f10a2SRuslan Bukin }
411c42f10a2SRuslan Bukin
412c42f10a2SRuslan Bukin static int
pl330_channel_capacity(device_t dev,xdma_channel_t * xchan,uint32_t * capacity)413c42f10a2SRuslan Bukin pl330_channel_capacity(device_t dev, xdma_channel_t *xchan,
414c42f10a2SRuslan Bukin uint32_t *capacity)
415c42f10a2SRuslan Bukin {
416c42f10a2SRuslan Bukin struct pl330_channel *chan;
417c42f10a2SRuslan Bukin
418c42f10a2SRuslan Bukin chan = (struct pl330_channel *)xchan->chan;
419c42f10a2SRuslan Bukin
420c42f10a2SRuslan Bukin *capacity = chan->capacity;
421c42f10a2SRuslan Bukin
422c42f10a2SRuslan Bukin return (0);
423c42f10a2SRuslan Bukin }
424c42f10a2SRuslan Bukin
425c42f10a2SRuslan Bukin static int
pl330_ccr_port_width(struct xdma_sglist * sg,uint32_t * addr)426c42f10a2SRuslan Bukin pl330_ccr_port_width(struct xdma_sglist *sg, uint32_t *addr)
427c42f10a2SRuslan Bukin {
428c42f10a2SRuslan Bukin uint32_t reg;
429c42f10a2SRuslan Bukin
430c42f10a2SRuslan Bukin reg = 0;
431c42f10a2SRuslan Bukin
432c42f10a2SRuslan Bukin switch (sg->src_width) {
433c42f10a2SRuslan Bukin case 1:
434c42f10a2SRuslan Bukin reg |= CCR_SRC_BURST_SIZE_1;
435c42f10a2SRuslan Bukin break;
436c42f10a2SRuslan Bukin case 2:
437c42f10a2SRuslan Bukin reg |= CCR_SRC_BURST_SIZE_2;
438c42f10a2SRuslan Bukin break;
439c42f10a2SRuslan Bukin case 4:
440c42f10a2SRuslan Bukin reg |= CCR_SRC_BURST_SIZE_4;
441c42f10a2SRuslan Bukin break;
442c42f10a2SRuslan Bukin default:
443c42f10a2SRuslan Bukin return (-1);
444c42f10a2SRuslan Bukin }
445c42f10a2SRuslan Bukin
446c42f10a2SRuslan Bukin switch (sg->dst_width) {
447c42f10a2SRuslan Bukin case 1:
448c42f10a2SRuslan Bukin reg |= CCR_DST_BURST_SIZE_1;
449c42f10a2SRuslan Bukin break;
450c42f10a2SRuslan Bukin case 2:
451c42f10a2SRuslan Bukin reg |= CCR_DST_BURST_SIZE_2;
452c42f10a2SRuslan Bukin break;
453c42f10a2SRuslan Bukin case 4:
454c42f10a2SRuslan Bukin reg |= CCR_DST_BURST_SIZE_4;
455c42f10a2SRuslan Bukin break;
456c42f10a2SRuslan Bukin default:
457c42f10a2SRuslan Bukin return (-1);
458c42f10a2SRuslan Bukin }
459c42f10a2SRuslan Bukin
460c42f10a2SRuslan Bukin *addr |= reg;
461c42f10a2SRuslan Bukin
462c42f10a2SRuslan Bukin return (0);
463c42f10a2SRuslan Bukin }
464c42f10a2SRuslan Bukin
465c42f10a2SRuslan Bukin static int
pl330_channel_submit_sg(device_t dev,struct xdma_channel * xchan,struct xdma_sglist * sg,uint32_t sg_n)466c42f10a2SRuslan Bukin pl330_channel_submit_sg(device_t dev, struct xdma_channel *xchan,
467c42f10a2SRuslan Bukin struct xdma_sglist *sg, uint32_t sg_n)
468c42f10a2SRuslan Bukin {
469c42f10a2SRuslan Bukin struct pl330_fdt_data *data;
470c42f10a2SRuslan Bukin xdma_controller_t *xdma;
471c42f10a2SRuslan Bukin struct pl330_channel *chan;
472c42f10a2SRuslan Bukin struct pl330_softc *sc;
473c42f10a2SRuslan Bukin uint32_t src_addr_lo;
474c42f10a2SRuslan Bukin uint32_t dst_addr_lo;
475c42f10a2SRuslan Bukin uint32_t len;
476c42f10a2SRuslan Bukin uint32_t reg;
477c42f10a2SRuslan Bukin uint32_t offs;
478c42f10a2SRuslan Bukin uint32_t cnt;
479c42f10a2SRuslan Bukin uint8_t *ibuf;
480c42f10a2SRuslan Bukin uint8_t dbuf[6];
481c42f10a2SRuslan Bukin uint8_t offs0, offs1;
482c42f10a2SRuslan Bukin int err;
483c42f10a2SRuslan Bukin int i;
484c42f10a2SRuslan Bukin
485c42f10a2SRuslan Bukin sc = device_get_softc(dev);
486c42f10a2SRuslan Bukin
487c42f10a2SRuslan Bukin xdma = xchan->xdma;
488c42f10a2SRuslan Bukin data = (struct pl330_fdt_data *)xdma->data;
489c42f10a2SRuslan Bukin
490c42f10a2SRuslan Bukin chan = (struct pl330_channel *)xchan->chan;
491c42f10a2SRuslan Bukin ibuf = chan->ibuf;
492c42f10a2SRuslan Bukin
493c42f10a2SRuslan Bukin dprintf("%s: chan->index %d\n", __func__, chan->index);
494c42f10a2SRuslan Bukin
495c42f10a2SRuslan Bukin offs = 0;
496c42f10a2SRuslan Bukin
497c42f10a2SRuslan Bukin for (i = 0; i < sg_n; i++) {
498c42f10a2SRuslan Bukin if (sg[i].direction == XDMA_DEV_TO_MEM)
499c42f10a2SRuslan Bukin reg = CCR_DST_INC;
500c42f10a2SRuslan Bukin else {
501c42f10a2SRuslan Bukin reg = CCR_SRC_INC;
502c42f10a2SRuslan Bukin reg |= (CCR_DST_PROT_PRIV);
503c42f10a2SRuslan Bukin }
504c42f10a2SRuslan Bukin
505c42f10a2SRuslan Bukin err = pl330_ccr_port_width(&sg[i], ®);
506c42f10a2SRuslan Bukin if (err != 0)
507c42f10a2SRuslan Bukin return (err);
508c42f10a2SRuslan Bukin
509c42f10a2SRuslan Bukin offs += emit_mov(&chan->ibuf[offs], R_CCR, reg);
510c42f10a2SRuslan Bukin
511c42f10a2SRuslan Bukin src_addr_lo = (uint32_t)sg[i].src_addr;
512c42f10a2SRuslan Bukin dst_addr_lo = (uint32_t)sg[i].dst_addr;
513c42f10a2SRuslan Bukin len = (uint32_t)sg[i].len;
514c42f10a2SRuslan Bukin
515c42f10a2SRuslan Bukin dprintf("%s: src %x dst %x len %d periph_id %d\n", __func__,
516c42f10a2SRuslan Bukin src_addr_lo, dst_addr_lo, len, data->periph_id);
517c42f10a2SRuslan Bukin
518c42f10a2SRuslan Bukin offs += emit_mov(&ibuf[offs], R_SAR, src_addr_lo);
519c42f10a2SRuslan Bukin offs += emit_mov(&ibuf[offs], R_DAR, dst_addr_lo);
520c42f10a2SRuslan Bukin
521c42f10a2SRuslan Bukin if (sg[i].src_width != sg[i].dst_width)
522c42f10a2SRuslan Bukin return (-1); /* Not supported. */
523c42f10a2SRuslan Bukin
524c42f10a2SRuslan Bukin cnt = (len / sg[i].src_width);
525c42f10a2SRuslan Bukin if (cnt > 128) {
526c42f10a2SRuslan Bukin offs += emit_lp(&ibuf[offs], 0, cnt / 128);
527c42f10a2SRuslan Bukin offs0 = offs;
528c42f10a2SRuslan Bukin offs += emit_lp(&ibuf[offs], 1, 128);
529c42f10a2SRuslan Bukin offs1 = offs;
530c42f10a2SRuslan Bukin } else {
531c42f10a2SRuslan Bukin offs += emit_lp(&ibuf[offs], 0, cnt);
532c42f10a2SRuslan Bukin offs0 = offs;
533c42f10a2SRuslan Bukin }
534c42f10a2SRuslan Bukin offs += emit_wfp(&ibuf[offs], data->periph_id);
535c42f10a2SRuslan Bukin offs += emit_ld(&ibuf[offs], 1);
536c42f10a2SRuslan Bukin offs += emit_st(&ibuf[offs], 1);
537c42f10a2SRuslan Bukin
538c42f10a2SRuslan Bukin if (cnt > 128)
539c42f10a2SRuslan Bukin offs += emit_lpend(&ibuf[offs], 1, 1, (offs - offs1));
540c42f10a2SRuslan Bukin
541c42f10a2SRuslan Bukin offs += emit_lpend(&ibuf[offs], 0, 1, (offs - offs0));
542c42f10a2SRuslan Bukin }
543c42f10a2SRuslan Bukin
544c42f10a2SRuslan Bukin offs += emit_sev(&ibuf[offs], chan->index);
545c42f10a2SRuslan Bukin offs += emit_end(&ibuf[offs]);
546c42f10a2SRuslan Bukin
547c42f10a2SRuslan Bukin emit_go(dbuf, chan->index, chan->ibuf_phys, 0);
548c42f10a2SRuslan Bukin
549c42f10a2SRuslan Bukin reg = (dbuf[1] << 24) | (dbuf[0] << 16);
550c42f10a2SRuslan Bukin WRITE4(sc, DBGINST0, reg);
551c42f10a2SRuslan Bukin reg = (dbuf[5] << 24) | (dbuf[4] << 16) | (dbuf[3] << 8) | dbuf[2];
552c42f10a2SRuslan Bukin WRITE4(sc, DBGINST1, reg);
553c42f10a2SRuslan Bukin
554c42f10a2SRuslan Bukin WRITE4(sc, INTCLR, 0xffffffff);
555c42f10a2SRuslan Bukin WRITE4(sc, INTEN, (1 << chan->index));
556c42f10a2SRuslan Bukin
557c42f10a2SRuslan Bukin chan->enqueued = sg_n;
558c42f10a2SRuslan Bukin chan->capacity = 0;
559c42f10a2SRuslan Bukin
560c42f10a2SRuslan Bukin /* Start operation */
561c42f10a2SRuslan Bukin WRITE4(sc, DBGCMD, 0);
562c42f10a2SRuslan Bukin
563c42f10a2SRuslan Bukin return (0);
564c42f10a2SRuslan Bukin }
565c42f10a2SRuslan Bukin
566c42f10a2SRuslan Bukin static int
pl330_channel_prep_sg(device_t dev,struct xdma_channel * xchan)567c42f10a2SRuslan Bukin pl330_channel_prep_sg(device_t dev, struct xdma_channel *xchan)
568c42f10a2SRuslan Bukin {
569c42f10a2SRuslan Bukin struct pl330_channel *chan;
570c42f10a2SRuslan Bukin
571c42f10a2SRuslan Bukin dprintf("%s(%d)\n", __func__, device_get_unit(dev));
572c42f10a2SRuslan Bukin
573c42f10a2SRuslan Bukin chan = (struct pl330_channel *)xchan->chan;
574c42f10a2SRuslan Bukin chan->capacity = PL330_MAXLOAD;
575c42f10a2SRuslan Bukin
576c42f10a2SRuslan Bukin return (0);
577c42f10a2SRuslan Bukin }
578c42f10a2SRuslan Bukin
579c42f10a2SRuslan Bukin static int
pl330_channel_control(device_t dev,xdma_channel_t * xchan,int cmd)580c42f10a2SRuslan Bukin pl330_channel_control(device_t dev, xdma_channel_t *xchan, int cmd)
581c42f10a2SRuslan Bukin {
582c42f10a2SRuslan Bukin switch (cmd) {
583c42f10a2SRuslan Bukin case XDMA_CMD_BEGIN:
584c42f10a2SRuslan Bukin case XDMA_CMD_TERMINATE:
585c42f10a2SRuslan Bukin case XDMA_CMD_PAUSE:
586c42f10a2SRuslan Bukin /* TODO: implement me */
587c42f10a2SRuslan Bukin return (-1);
588c42f10a2SRuslan Bukin }
589c42f10a2SRuslan Bukin
590c42f10a2SRuslan Bukin return (0);
591c42f10a2SRuslan Bukin }
592c42f10a2SRuslan Bukin
593c42f10a2SRuslan Bukin #ifdef FDT
594c42f10a2SRuslan Bukin static int
pl330_ofw_md_data(device_t dev,pcell_t * cells,int ncells,void ** ptr)595c42f10a2SRuslan Bukin pl330_ofw_md_data(device_t dev, pcell_t *cells, int ncells, void **ptr)
596c42f10a2SRuslan Bukin {
597c42f10a2SRuslan Bukin struct pl330_fdt_data *data;
598c42f10a2SRuslan Bukin
599c42f10a2SRuslan Bukin if (ncells != 1)
600c42f10a2SRuslan Bukin return (-1);
601c42f10a2SRuslan Bukin
602c42f10a2SRuslan Bukin data = malloc(sizeof(struct pl330_fdt_data),
603c42f10a2SRuslan Bukin M_DEVBUF, (M_WAITOK | M_ZERO));
604c42f10a2SRuslan Bukin data->periph_id = cells[0];
605c42f10a2SRuslan Bukin
606c42f10a2SRuslan Bukin *ptr = data;
607c42f10a2SRuslan Bukin
608c42f10a2SRuslan Bukin return (0);
609c42f10a2SRuslan Bukin }
610c42f10a2SRuslan Bukin #endif
611c42f10a2SRuslan Bukin
612c42f10a2SRuslan Bukin static device_method_t pl330_methods[] = {
613c42f10a2SRuslan Bukin /* Device interface */
614c42f10a2SRuslan Bukin DEVMETHOD(device_probe, pl330_probe),
615c42f10a2SRuslan Bukin DEVMETHOD(device_attach, pl330_attach),
616c42f10a2SRuslan Bukin DEVMETHOD(device_detach, pl330_detach),
617c42f10a2SRuslan Bukin
618c42f10a2SRuslan Bukin /* xDMA Interface */
619c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_alloc, pl330_channel_alloc),
620c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_free, pl330_channel_free),
621c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_control, pl330_channel_control),
622c42f10a2SRuslan Bukin
623c42f10a2SRuslan Bukin /* xDMA SG Interface */
624c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_capacity, pl330_channel_capacity),
625c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_prep_sg, pl330_channel_prep_sg),
626c42f10a2SRuslan Bukin DEVMETHOD(xdma_channel_submit_sg, pl330_channel_submit_sg),
627c42f10a2SRuslan Bukin
628c42f10a2SRuslan Bukin #ifdef FDT
629c42f10a2SRuslan Bukin DEVMETHOD(xdma_ofw_md_data, pl330_ofw_md_data),
630c42f10a2SRuslan Bukin #endif
631c42f10a2SRuslan Bukin
632c42f10a2SRuslan Bukin DEVMETHOD_END
633c42f10a2SRuslan Bukin };
634c42f10a2SRuslan Bukin
635c42f10a2SRuslan Bukin static driver_t pl330_driver = {
636c42f10a2SRuslan Bukin "pl330",
637c42f10a2SRuslan Bukin pl330_methods,
638c42f10a2SRuslan Bukin sizeof(struct pl330_softc),
639c42f10a2SRuslan Bukin };
640c42f10a2SRuslan Bukin
641*a3f08403SJohn Baldwin EARLY_DRIVER_MODULE(pl330, simplebus, pl330_driver, 0, 0,
642c42f10a2SRuslan Bukin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
643