2 * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/systm.h>
36 #include <sys/endian.h>
37 #include <sys/kernel.h>
38 #include <sys/kthread.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/mutex.h>
43 #include <sys/queue.h>
44 #include <sys/resource.h>
47 #include <sys/timetc.h>
48 #include <sys/watchdog.h>
52 #include <dev/ofw/ofw_bus.h>
53 #include <dev/ofw/ofw_bus_subr.h>
55 #include <arm/lpc/lpcreg.h>
56 #include <arm/lpc/lpcvar.h>
58 struct lpc_dmac_channel
60 struct lpc_dmac_channel_config
*ldc_config
;
68 struct resource
* ld_mem_res
;
69 struct resource
* ld_irq_res
;
70 bus_space_tag_t ld_bst
;
71 bus_space_handle_t ld_bsh
;
73 struct lpc_dmac_channel ld_channels
[8];
76 static struct lpc_dmac_softc
*lpc_dmac_sc
= NULL
;
78 static int lpc_dmac_probe(device_t
);
79 static int lpc_dmac_attach(device_t
);
80 static void lpc_dmac_intr(void *);
82 #define lpc_dmac_read_4(_sc, _reg) \
83 bus_space_read_4(_sc->ld_bst, _sc->ld_bsh, _reg)
84 #define lpc_dmac_write_4(_sc, _reg, _value) \
85 bus_space_write_4(_sc->ld_bst, _sc->ld_bsh, _reg, _value)
86 #define lpc_dmac_read_ch_4(_sc, _n, _reg) \
87 bus_space_read_4(_sc->ld_bst, _sc->ld_bsh, (_reg + LPC_DMAC_CHADDR(_n)))
88 #define lpc_dmac_write_ch_4(_sc, _n, _reg, _value) \
89 bus_space_write_4(_sc->ld_bst, _sc->ld_bsh, (_reg + LPC_DMAC_CHADDR(_n)), _value)
91 static int lpc_dmac_probe(device_t dev
)
94 if (!ofw_bus_status_okay(dev
))
97 if (!ofw_bus_is_compatible(dev
, "lpc,dmac"))
100 device_set_desc(dev
, "LPC32x0 General Purpose DMA controller");
101 return (BUS_PROBE_DEFAULT
);
104 static int lpc_dmac_attach(device_t dev
)
106 struct lpc_dmac_softc
*sc
= device_get_softc(dev
);
110 sc
->ld_mem_res
= bus_alloc_resource_any(dev
, SYS_RES_MEMORY
, &rid
,
112 if (!sc
->ld_mem_res
) {
113 device_printf(dev
, "cannot allocate memory window\n");
117 sc
->ld_bst
= rman_get_bustag(sc
->ld_mem_res
);
118 sc
->ld_bsh
= rman_get_bushandle(sc
->ld_mem_res
);
121 sc
->ld_irq_res
= bus_alloc_resource_any(dev
, SYS_RES_IRQ
, &rid
,
123 if (!sc
->ld_irq_res
) {
124 device_printf(dev
, "cannot allocate cmd interrupt\n");
125 bus_release_resource(dev
, SYS_RES_MEMORY
, 0, sc
->ld_mem_res
);
129 if (bus_setup_intr(dev
, sc
->ld_irq_res
, INTR_TYPE_MISC
| INTR_MPSAFE
,
130 NULL
, lpc_dmac_intr
, sc
, &sc
->ld_intrhand
))
132 bus_release_resource(dev
, SYS_RES_MEMORY
, 0, sc
->ld_mem_res
);
133 bus_release_resource(dev
, SYS_RES_IRQ
, 0, sc
->ld_irq_res
);
134 device_printf(dev
, "cannot setup interrupt handler\n");
140 lpc_pwr_write(dev
, LPC_CLKPWR_DMACLK_CTRL
, LPC_CLKPWR_DMACLK_CTRL_EN
);
141 lpc_dmac_write_4(sc
, LPC_DMAC_CONFIG
, LPC_DMAC_CONFIG_ENABLE
);
143 lpc_dmac_write_4(sc
, LPC_DMAC_INTTCCLEAR
, 0xff);
144 lpc_dmac_write_4(sc
, LPC_DMAC_INTERRCLEAR
, 0xff);
149 static void lpc_dmac_intr(void *arg
)
151 struct lpc_dmac_softc
*sc
= (struct lpc_dmac_softc
*)arg
;
152 struct lpc_dmac_channel
*ch
;
153 uint32_t intstat
, tcstat
, errstat
;
157 intstat
= lpc_dmac_read_4(sc
, LPC_DMAC_INTSTAT
);
159 for (i
= 0; i
< LPC_DMAC_CHNUM
; i
++) {
160 if ((intstat
& (1 << i
)) == 0)
163 ch
= &sc
->ld_channels
[i
];
164 tcstat
= lpc_dmac_read_4(sc
, LPC_DMAC_INTTCSTAT
);
165 errstat
= lpc_dmac_read_4(sc
, LPC_DMAC_INTERRSTAT
);
167 if (tcstat
& (1 << i
)) {
168 ch
->ldc_config
->ldc_success_handler(
169 ch
->ldc_config
->ldc_handler_arg
);
170 lpc_dmac_write_4(sc
, LPC_DMAC_INTTCCLEAR
, (1 << i
));
173 if (errstat
& (1 << i
)) {
174 ch
->ldc_config
->ldc_error_handler(
175 ch
->ldc_config
->ldc_handler_arg
);
176 lpc_dmac_write_4(sc
, LPC_DMAC_INTERRCLEAR
, (1 << i
));
184 lpc_dmac_config_channel(device_t dev
, int chno
, struct lpc_dmac_channel_config
*cfg
)
186 struct lpc_dmac_softc
*sc
= lpc_dmac_sc
;
187 struct lpc_dmac_channel
*ch
;
192 ch
= &sc
->ld_channels
[chno
];
193 ch
->ldc_config
= cfg
;
199 lpc_dmac_setup_transfer(device_t dev
, int chno
, bus_addr_t src
, bus_addr_t dst
,
200 bus_size_t size
, int flags
)
202 struct lpc_dmac_softc
*sc
= lpc_dmac_sc
;
203 struct lpc_dmac_channel
*ch
;
209 ch
= &sc
->ld_channels
[chno
];
211 ctrl
= LPC_DMAC_CH_CONTROL_I
|
212 (ch
->ldc_config
->ldc_dst_incr
? LPC_DMAC_CH_CONTROL_DI
: 0) |
213 (ch
->ldc_config
->ldc_src_incr
? LPC_DMAC_CH_CONTROL_SI
: 0) |
214 LPC_DMAC_CH_CONTROL_DWIDTH(ch
->ldc_config
->ldc_dst_width
) |
215 LPC_DMAC_CH_CONTROL_SWIDTH(ch
->ldc_config
->ldc_src_width
) |
216 LPC_DMAC_CH_CONTROL_DBSIZE(ch
->ldc_config
->ldc_dst_burst
) |
217 LPC_DMAC_CH_CONTROL_SBSIZE(ch
->ldc_config
->ldc_src_burst
) |
220 cfg
= LPC_DMAC_CH_CONFIG_ITC
| LPC_DMAC_CH_CONFIG_IE
|
221 LPC_DMAC_CH_CONFIG_FLOWCNTL(ch
->ldc_config
->ldc_fcntl
) |
222 LPC_DMAC_CH_CONFIG_DESTP(ch
->ldc_config
->ldc_dst_periph
) |
223 LPC_DMAC_CH_CONFIG_SRCP(ch
->ldc_config
->ldc_src_periph
) | LPC_DMAC_CH_CONFIG_E
;
224 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_SRCADDR
, src
);
225 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_DSTADDR
, dst
);
226 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_LLI
, 0);
227 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_CONTROL
, ctrl
);
228 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_CONFIG
, cfg
);
234 lpc_dmac_enable_channel(device_t dev
, int chno
)
236 struct lpc_dmac_softc
*sc
= lpc_dmac_sc
;
242 cfg
= lpc_dmac_read_ch_4(sc
, chno
, LPC_DMAC_CH_CONFIG
);
243 cfg
|= LPC_DMAC_CH_CONFIG_E
;
245 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_CONFIG
, cfg
);
251 lpc_dmac_disable_channel(device_t dev
, int chno
)
253 struct lpc_dmac_softc
*sc
= lpc_dmac_sc
;
259 cfg
= lpc_dmac_read_ch_4(sc
, chno
, LPC_DMAC_CH_CONFIG
);
260 cfg
&= ~LPC_DMAC_CH_CONFIG_E
;
262 lpc_dmac_write_ch_4(sc
, chno
, LPC_DMAC_CH_CONFIG
, cfg
);
268 lpc_dmac_start_burst(device_t dev
, int id
)
270 struct lpc_dmac_softc
*sc
= lpc_dmac_sc
;
272 lpc_dmac_write_4(sc
, LPC_DMAC_SOFTBREQ
, (1 << id
));
276 static device_method_t lpc_dmac_methods
[] = {
277 /* Device interface */
278 DEVMETHOD(device_probe
, lpc_dmac_probe
),
279 DEVMETHOD(device_attach
, lpc_dmac_attach
),
284 static devclass_t lpc_dmac_devclass
;
286 static driver_t lpc_dmac_driver
= {
289 sizeof(struct lpc_dmac_softc
),
292 DRIVER_MODULE(dmac
, simplebus
, lpc_dmac_driver
, lpc_dmac_devclass
, 0, 0);