vf_edma.c revision 266170
1181834Sroberto/*-
2280849Scy * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3280849Scy * All rights reserved.
4181834Sroberto *
5181834Sroberto * Redistribution and use in source and binary forms, with or without
6181834Sroberto * modification, are permitted provided that the following conditions
7280849Scy * are met:
8280849Scy * 1. Redistributions of source code must retain the above copyright
9280849Scy *    notice, this list of conditions and the following disclaimer.
10181834Sroberto * 2. Redistributions in binary form must reproduce the above copyright
11181834Sroberto *    notice, this list of conditions and the following disclaimer in the
12280849Scy *    documentation and/or other materials provided with the distribution.
13280849Scy *
14285169Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15181834Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16280849Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17280849Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18280849Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19181834Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20280849Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21280849Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22181834Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23280849Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24280849Scy * SUCH DAMAGE.
25181834Sroberto */
26280849Scy
27181834Sroberto/*
28280849Scy * Vybrid Family Enhanced Direct Memory Access Controller (eDMA)
29280849Scy * Chapter 21, Vybrid Reference Manual, Rev. 5, 07/2013
30280849Scy */
31181834Sroberto
32181834Sroberto#include <sys/cdefs.h>
33285169Scy__FBSDID("$FreeBSD: stable/10/sys/arm/freescale/vybrid/vf_edma.c 266170 2014-05-15 18:38:19Z ian $");
34285169Scy
35285169Scy#include <sys/param.h>
36285169Scy#include <sys/systm.h>
37285169Scy#include <sys/bus.h>
38280849Scy#include <sys/kernel.h>
39280849Scy#include <sys/module.h>
40280849Scy#include <sys/malloc.h>
41181834Sroberto#include <sys/rman.h>
42280849Scy#include <sys/timeet.h>
43280849Scy#include <sys/timetc.h>
44181834Sroberto#include <sys/watchdog.h>
45181834Sroberto
46280849Scy#include <dev/fdt/fdt_common.h>
47181834Sroberto#include <dev/ofw/openfirm.h>
48181834Sroberto#include <dev/ofw/ofw_bus.h>
49280849Scy#include <dev/ofw/ofw_bus_subr.h>
50181834Sroberto
51181834Sroberto#include <machine/bus.h>
52280849Scy#include <machine/fdt.h>
53181834Sroberto#include <machine/cpu.h>
54181834Sroberto#include <machine/intr.h>
55280849Scy
56181834Sroberto#include <arm/freescale/vybrid/vf_edma.h>
57181834Sroberto#include <arm/freescale/vybrid/vf_dmamux.h>
58280849Scy#include <arm/freescale/vybrid/vf_common.h>
59181834Sroberto
60181834Srobertostruct edma_channel {
61280849Scy	uint32_t	enabled;
62181834Sroberto	uint32_t	mux_num;
63181834Sroberto	uint32_t	mux_src;
64280849Scy	uint32_t	mux_chn;
65181834Sroberto	uint32_t	(*ih) (void *, int);
66181834Sroberto	void		*ih_user;
67280849Scy};
68181834Sroberto
69280849Scystatic struct edma_channel edma_map[EDMA_NUM_CHANNELS];
70280849Scy
71280849Scystatic struct resource_spec edma_spec[] = {
72181834Sroberto	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
73280849Scy	{ SYS_RES_MEMORY,	1,	RF_ACTIVE }, /* TCD */
74181834Sroberto	{ SYS_RES_IRQ,		0,	RF_ACTIVE }, /* Transfer complete */
75181834Sroberto	{ SYS_RES_IRQ,		1,	RF_ACTIVE }, /* Error Interrupt */
76280849Scy	{ -1, 0 }
77280849Scy};
78280849Scy
79280849Scystatic int
80280849Scyedma_probe(device_t dev)
81280849Scy{
82280849Scy
83280849Scy	if (!ofw_bus_status_okay(dev))
84280849Scy		return (ENXIO);
85280849Scy
86280849Scy	if (!ofw_bus_is_compatible(dev, "fsl,mvf600-edma"))
87280849Scy		return (ENXIO);
88280849Scy
89280849Scy	device_set_desc(dev, "Vybrid Family eDMA Controller");
90280849Scy	return (BUS_PROBE_DEFAULT);
91280849Scy}
92280849Scy
93280849Scystatic void
94280849Scyedma_transfer_complete_intr(void *arg)
95280849Scy{
96280849Scy	struct edma_channel *ch;
97280849Scy	struct edma_softc *sc;
98280849Scy	int interrupts;
99280849Scy	int i;
100280849Scy
101280849Scy	sc = arg;
102280849Scy
103280849Scy	interrupts = READ4(sc, DMA_INT);
104280849Scy	WRITE1(sc, DMA_CINT, CINT_CAIR);
105181834Sroberto
106181834Sroberto	for (i = 0; i < EDMA_NUM_CHANNELS; i++) {
107181834Sroberto		if (interrupts & (0x1 << i)) {
108181834Sroberto			ch = &edma_map[i];
109285169Scy			if (ch->enabled == 1) {
110181834Sroberto				if (ch->ih != NULL) {
111181834Sroberto					ch->ih(ch->ih_user, i);
112181834Sroberto				}
113181834Sroberto			}
114181834Sroberto		}
115280849Scy	}
116181834Sroberto}
117181834Sroberto
118181834Srobertostatic void
119181834Srobertoedma_err_intr(void *arg)
120181834Sroberto{
121181834Sroberto	struct edma_softc *sc;
122280849Scy	int reg;
123280849Scy
124181834Sroberto	sc = arg;
125280849Scy
126280849Scy	reg = READ4(sc, DMA_ERR);
127181834Sroberto
128280849Scy#if 0
129280849Scy	device_printf(sc->dev, "DMA_ERR 0x%08x, ES 0x%08x\n",
130181834Sroberto	    reg, READ4(sc, DMA_ES));
131280849Scy#endif
132181834Sroberto
133181834Sroberto	WRITE1(sc, DMA_CERR, CERR_CAEI);
134181834Sroberto}
135181834Sroberto
136280849Scystatic int
137280849Scychannel_free(struct edma_softc *sc, int chnum)
138280849Scy{
139280849Scy	struct edma_channel *ch;
140280849Scy
141181834Sroberto	ch = &edma_map[chnum];
142181834Sroberto	ch->enabled = 0;
143181834Sroberto
144181834Sroberto	dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 0);
145280849Scy
146181834Sroberto	return (0);
147280849Scy}
148181834Sroberto
149280849Scystatic int
150280849Scychannel_configure(struct edma_softc *sc, int mux_grp, int mux_src)
151280849Scy{
152280849Scy	struct edma_channel *ch;
153280849Scy	int channel_first;
154181834Sroberto	int mux_num;
155280849Scy	int chnum;
156181834Sroberto	int i;
157181834Sroberto
158181834Sroberto	if ((sc->device_id == 0 && mux_grp == 1) ||	\
159280849Scy	    (sc->device_id == 1 && mux_grp == 0)) {
160280849Scy		channel_first = NCHAN_PER_MUX;
161280849Scy		mux_num = (sc->device_id * 2) + 1;
162280849Scy	} else {
163181834Sroberto		channel_first = 0;
164181834Sroberto		mux_num = sc->device_id * 2;
165181834Sroberto	};
166280849Scy
167181834Sroberto	/* Take first unused eDMA channel */
168280849Scy	ch = NULL;
169280849Scy	for (i = channel_first; i < (channel_first + NCHAN_PER_MUX); i++) {
170280849Scy		ch = &edma_map[i];
171280849Scy		if (ch->enabled == 0) {
172280849Scy			break;
173181834Sroberto		}
174280849Scy		ch = NULL;
175181834Sroberto	};
176181834Sroberto
177181834Sroberto	if (ch == NULL) {
178280849Scy		/* Can't find free channel */
179181834Sroberto		return (-1);
180280849Scy	};
181280849Scy
182280849Scy	chnum = i;
183280849Scy
184280849Scy	ch->enabled = 1;
185181834Sroberto	ch->mux_num = mux_num;
186280849Scy	ch->mux_src = mux_src;
187280849Scy	ch->mux_chn = (chnum - channel_first);	/* 0 to 15 */
188280849Scy
189280849Scy	dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 1);
190280849Scy
191181834Sroberto	return (chnum);
192280849Scy}
193181834Sroberto
194181834Srobertostatic int
195181834Srobertodma_stop(struct edma_softc *sc, int chnum)
196280849Scy{
197280849Scy	int reg;
198280849Scy
199280849Scy	reg = READ4(sc, DMA_ERQ);
200280849Scy	reg &= ~(0x1 << chnum);
201181834Sroberto	WRITE4(sc, DMA_ERQ, reg);
202280849Scy
203280849Scy	return (0);
204280849Scy}
205280849Scy
206280849Scystatic int
207280849Scydma_setup(struct edma_softc *sc, struct tcd_conf *tcd)
208280849Scy{
209280849Scy	struct edma_channel *ch;
210280849Scy	int chnum;
211280849Scy	int reg;
212280849Scy
213280849Scy	chnum = tcd->channel;
214181834Sroberto
215181834Sroberto	ch = &edma_map[chnum];
216280849Scy	ch->ih = tcd->ih;
217280849Scy	ch->ih_user = tcd->ih_user;
218280849Scy
219280849Scy	TCD_WRITE4(sc, DMA_TCDn_SADDR(chnum), tcd->saddr);
220280849Scy	TCD_WRITE4(sc, DMA_TCDn_DADDR(chnum), tcd->daddr);
221280849Scy
222280849Scy	reg = (tcd->smod << TCD_ATTR_SMOD_SHIFT);
223280849Scy	reg |= (tcd->dmod << TCD_ATTR_DMOD_SHIFT);
224280849Scy	reg |= (tcd->ssize << TCD_ATTR_SSIZE_SHIFT);
225280849Scy	reg |= (tcd->dsize << TCD_ATTR_DSIZE_SHIFT);
226280849Scy	TCD_WRITE2(sc, DMA_TCDn_ATTR(chnum), reg);
227280849Scy
228280849Scy	TCD_WRITE2(sc, DMA_TCDn_SOFF(chnum), tcd->soff);
229280849Scy	TCD_WRITE2(sc, DMA_TCDn_DOFF(chnum), tcd->doff);
230280849Scy	TCD_WRITE4(sc, DMA_TCDn_SLAST(chnum), tcd->slast);
231280849Scy	TCD_WRITE4(sc, DMA_TCDn_DLASTSGA(chnum), tcd->dlast_sga);
232280849Scy	TCD_WRITE4(sc, DMA_TCDn_NBYTES_MLOFFYES(chnum), tcd->nbytes);
233280849Scy
234181834Sroberto	reg = tcd->nmajor; /* Current Major Iteration Count */
235280849Scy	TCD_WRITE2(sc, DMA_TCDn_CITER_ELINKNO(chnum), reg);
236280849Scy	TCD_WRITE2(sc, DMA_TCDn_BITER_ELINKNO(chnum), reg);
237280849Scy
238280849Scy	reg = (TCD_CSR_INTMAJOR);
239280849Scy	if(tcd->majorelink == 1) {
240280849Scy		reg |= TCD_CSR_MAJORELINK;
241280849Scy		reg |= (tcd->majorelinkch << TCD_CSR_MAJORELINKCH_SHIFT);
242280849Scy	}
243280849Scy	TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg);
244280849Scy
245280849Scy	/* Enable requests */
246280849Scy	reg = READ4(sc, DMA_ERQ);
247280849Scy	reg |= (0x1 << chnum);
248280849Scy	WRITE4(sc, DMA_ERQ, reg);
249280849Scy
250280849Scy	/* Enable error interrupts */
251280849Scy	reg = READ4(sc, DMA_EEI);
252280849Scy	reg |= (0x1 << chnum);
253280849Scy	WRITE4(sc, DMA_EEI, reg);
254280849Scy
255280849Scy	return (0);
256280849Scy}
257280849Scy
258280849Scystatic int
259280849Scydma_request(struct edma_softc *sc, int chnum)
260280849Scy{
261280849Scy	int reg;
262280849Scy
263280849Scy	/* Start */
264280849Scy	reg = TCD_READ2(sc, DMA_TCDn_CSR(chnum));
265280849Scy	reg |= TCD_CSR_START;
266280849Scy	TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg);
267280849Scy
268280849Scy	return (0);
269280849Scy}
270280849Scy
271280849Scystatic int
272280849Scyedma_attach(device_t dev)
273280849Scy{
274280849Scy	struct edma_softc *sc;
275280849Scy	phandle_t node;
276280849Scy	int dts_value;
277280849Scy	int len;
278280849Scy
279280849Scy	sc = device_get_softc(dev);
280280849Scy	sc->dev = dev;
281280849Scy
282280849Scy	if ((node = ofw_bus_get_node(sc->dev)) == -1)
283280849Scy		return (ENXIO);
284280849Scy
285280849Scy	if ((len = OF_getproplen(node, "device-id")) <= 0)
286280849Scy		return (ENXIO);
287181834Sroberto
288280849Scy	OF_getprop(node, "device-id", &dts_value, len);
289181834Sroberto	sc->device_id = fdt32_to_cpu(dts_value);
290280849Scy
291181834Sroberto	sc->dma_stop = dma_stop;
292181834Sroberto	sc->dma_setup = dma_setup;
293181834Sroberto	sc->dma_request = dma_request;
294280849Scy	sc->channel_configure = channel_configure;
295181834Sroberto	sc->channel_free = channel_free;
296181834Sroberto
297280849Scy	if (bus_alloc_resources(dev, edma_spec, sc->res)) {
298280849Scy		device_printf(dev, "could not allocate resources\n");
299181834Sroberto		return (ENXIO);
300280849Scy	}
301181834Sroberto
302280849Scy	/* Memory interface */
303280849Scy	sc->bst = rman_get_bustag(sc->res[0]);
304181834Sroberto	sc->bsh = rman_get_bushandle(sc->res[0]);
305280849Scy	sc->bst_tcd = rman_get_bustag(sc->res[1]);
306280849Scy	sc->bsh_tcd = rman_get_bushandle(sc->res[1]);
307181834Sroberto
308181834Sroberto	/* Setup interrupt handlers */
309181834Sroberto	if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
310280849Scy		NULL, edma_transfer_complete_intr, sc, &sc->tc_ih)) {
311280849Scy		device_printf(dev, "Unable to alloc DMA intr resource.\n");
312181834Sroberto		return (ENXIO);
313181834Sroberto	}
314280849Scy
315280849Scy	if (bus_setup_intr(dev, sc->res[3], INTR_TYPE_BIO | INTR_MPSAFE,
316280849Scy		NULL, edma_err_intr, sc, &sc->err_ih)) {
317280849Scy		device_printf(dev, "Unable to alloc DMA Err intr resource.\n");
318280849Scy		return (ENXIO);
319280849Scy	}
320280849Scy
321181834Sroberto	return (0);
322280849Scy}
323181834Sroberto
324280849Scystatic device_method_t edma_methods[] = {
325181834Sroberto	DEVMETHOD(device_probe,		edma_probe),
326181834Sroberto	DEVMETHOD(device_attach,	edma_attach),
327181834Sroberto	{ 0, 0 }
328280849Scy};
329181834Sroberto
330181834Srobertostatic driver_t edma_driver = {
331181834Sroberto	"edma",
332280849Scy	edma_methods,
333280849Scy	sizeof(struct edma_softc),
334280849Scy};
335181834Sroberto
336280849Scystatic devclass_t edma_devclass;
337280849Scy
338181834SrobertoDRIVER_MODULE(edma, simplebus, edma_driver, edma_devclass, 0, 0);
339181834Sroberto