xref: /freebsd-src/sys/dev/xdma/controller/pl330.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
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], &reg);
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