ti_pruss.c revision 277958
1114402Sru/*-
2151497Sru * Copyright (c) 2013 Rui Paulo <rpaulo@FreeBSD.org>
3114402Sru * All rights reserved.
4114402Sru *
5114402Sru * Redistribution and use in source and binary forms, with or without
6114402Sru * modification, are permitted provided that the following conditions
7114402Sru * are met:
8114402Sru * 1. Redistributions of source code must retain the above copyright
9114402Sru *    notice, this list of conditions and the following disclaimer.
10114402Sru * 2. Redistributions in binary form must reproduce the above copyright
11114402Sru *    notice, this list of conditions and the following disclaimer in the
12114402Sru *    documentation and/or other materials provided with the distribution.
13114402Sru *
14114402Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15114402Sru * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16114402Sru * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17114402Sru * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
18114402Sru * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19151497Sru * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20114402Sru * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21114402Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22114402Sru * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
23114402Sru * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24114402Sru * POSSIBILITY OF SUCH DAMAGE.
25114402Sru */
26114402Sru#include <sys/cdefs.h>
27114402Sru__FBSDID("$FreeBSD: head/sys/arm/ti/ti_pruss.c 277958 2015-01-31 02:12:57Z rpaulo $");
28114402Sru
29114402Sru#include <sys/param.h>
30114402Sru#include <sys/systm.h>
31114402Sru#include <sys/bus.h>
32114402Sru#include <sys/conf.h>
33114402Sru#include <sys/kernel.h>
34114402Sru#include <sys/module.h>
35114402Sru#include <sys/malloc.h>
36114402Sru#include <sys/rman.h>
37151497Sru#include <sys/event.h>
38114402Sru#include <sys/selinfo.h>
39114402Sru#include <machine/bus.h>
40114402Sru#include <machine/cpu.h>
41114402Sru#include <machine/frame.h>
42114402Sru#include <machine/intr.h>
43151497Sru
44114402Sru#include <dev/fdt/fdt_common.h>
45114402Sru#include <dev/ofw/openfirm.h>
46114402Sru#include <dev/ofw/ofw_bus.h>
47151497Sru#include <dev/ofw/ofw_bus_subr.h>
48151497Sru
49114402Sru#include <machine/bus.h>
50151497Sru#include <machine/fdt.h>
51114402Sru
52114402Sru#include <arm/ti/ti_prcm.h>
53114402Sru#include <arm/ti/ti_pruss.h>
54114402Sru
55114402Sru#ifdef DEBUG
56114402Sru#define	DPRINTF(fmt, ...)	do {	\
57151497Sru	printf("%s: ", __func__);	\
58151497Sru	printf(fmt, __VA_ARGS__);	\
59151497Sru} while (0)
60151497Sru#else
61151497Sru#define	DPRINTF(fmt, ...)
62151497Sru#endif
63151497Sru
64151497Srustatic device_probe_t		ti_pruss_probe;
65151497Srustatic device_attach_t		ti_pruss_attach;
66151497Srustatic device_detach_t		ti_pruss_detach;
67151497Srustatic void			ti_pruss_intr(void *);
68151497Srustatic d_open_t			ti_pruss_open;
69151497Srustatic d_mmap_t			ti_pruss_mmap;
70151497Srustatic void 			ti_pruss_kq_read_detach(struct knote *);
71151497Srustatic int 			ti_pruss_kq_read_event(struct knote *, long);
72151497Srustatic d_kqfilter_t		ti_pruss_kqfilter;
73151497Sru
74151497Sru#define	TI_PRUSS_IRQS	8
75151497Srustruct ti_pruss_softc {
76151497Sru	struct mtx		sc_mtx;
77151497Sru	struct resource 	*sc_mem_res;
78151497Sru	struct resource 	*sc_irq_res[TI_PRUSS_IRQS];
79151497Sru	void            	*sc_intr[TI_PRUSS_IRQS];
80151497Sru	bus_space_tag_t		sc_bt;
81151497Sru	bus_space_handle_t	sc_bh;
82151497Sru	struct cdev		*sc_pdev;
83151497Sru	struct selinfo		sc_selinfo;
84151497Sru};
85151497Sru
86151497Srustatic struct cdevsw ti_pruss_cdevsw = {
87151497Sru	.d_version =	D_VERSION,
88151497Sru	.d_name =	"ti_pruss",
89151497Sru	.d_open =	ti_pruss_open,
90151497Sru	.d_mmap =	ti_pruss_mmap,
91151497Sru	.d_kqfilter =	ti_pruss_kqfilter,
92151497Sru};
93151497Sru
94151497Srustatic device_method_t ti_pruss_methods[] = {
95151497Sru	DEVMETHOD(device_probe,		ti_pruss_probe),
96151497Sru	DEVMETHOD(device_attach,	ti_pruss_attach),
97151497Sru	DEVMETHOD(device_detach,	ti_pruss_detach),
98151497Sru
99151497Sru	DEVMETHOD_END
100151497Sru};
101151497Sru
102151497Srustatic driver_t ti_pruss_driver = {
103151497Sru	"ti_pruss",
104151497Sru	ti_pruss_methods,
105151497Sru	sizeof(struct ti_pruss_softc)
106151497Sru};
107151497Sru
108151497Srustatic devclass_t ti_pruss_devclass;
109151497Sru
110151497SruDRIVER_MODULE(ti_pruss, simplebus, ti_pruss_driver, ti_pruss_devclass, 0, 0);
111151497Sru
112151497Srustatic struct resource_spec ti_pruss_irq_spec[] = {
113151497Sru	{ SYS_RES_IRQ,	    0,  RF_ACTIVE },
114151497Sru	{ SYS_RES_IRQ,	    1,  RF_ACTIVE },
115151497Sru	{ SYS_RES_IRQ,	    2,  RF_ACTIVE },
116151497Sru	{ SYS_RES_IRQ,	    3,  RF_ACTIVE },
117151497Sru	{ SYS_RES_IRQ,	    4,  RF_ACTIVE },
118151497Sru	{ SYS_RES_IRQ,	    5,  RF_ACTIVE },
119151497Sru	{ SYS_RES_IRQ,	    6,  RF_ACTIVE },
120151497Sru	{ SYS_RES_IRQ,	    7,  RF_ACTIVE },
121151497Sru	{ -1,               0,  0 }
122151497Sru};
123151497Sru
124151497Srustatic struct ti_pruss_irq_arg {
125151497Sru	int 		       irq;
126151497Sru	struct ti_pruss_softc *sc;
127151497Sru} ti_pruss_irq_args[TI_PRUSS_IRQS];
128151497Sru
129151497Srustatic __inline uint32_t
130151497Sruti_pruss_reg_read(struct ti_pruss_softc *sc, uint32_t reg)
131151497Sru{
132151497Sru	return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg));
133151497Sru}
134151497Sru
135151497Srustatic __inline void
136151497Sruti_pruss_reg_write(struct ti_pruss_softc *sc, uint32_t reg, uint32_t val)
137151497Sru{
138151497Sru	bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val);
139114402Sru}
140114402Sru
141114402Srustatic int
142114402Sruti_pruss_probe(device_t dev)
143114402Sru{
144114402Sru
145151497Sru	if (!ofw_bus_status_okay(dev))
146151497Sru		return (ENXIO);
147151497Sru
148151497Sru	if (ofw_bus_is_compatible(dev, "ti,pruss-v1") ||
149114402Sru	    ofw_bus_is_compatible(dev, "ti,pruss-v2")) {
150151497Sru		device_set_desc(dev, "TI Programmable Realtime Unit Subsystem");
151151497Sru		return (BUS_PROBE_DEFAULT);
152151497Sru	}
153151497Sru
154151497Sru	return (ENXIO);
155114402Sru}
156114402Sru
157114402Srustatic int
158114402Sruti_pruss_attach(device_t dev)
159114402Sru{
160114402Sru	struct ti_pruss_softc *sc;
161114402Sru	int rid, i;
162114402Sru
163114402Sru	if (ti_prcm_clk_enable(PRUSS_CLK) != 0) {
164151497Sru		device_printf(dev, "could not enable PRUSS clock\n");
165114402Sru		return (ENXIO);
166114402Sru	}
167114402Sru	sc = device_get_softc(dev);
168151497Sru	rid = 0;
169114402Sru	mtx_init(&sc->sc_mtx, "TI PRUSS", NULL, MTX_DEF);
170114402Sru	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
171151497Sru	    RF_ACTIVE);
172114402Sru	if (sc->sc_mem_res == NULL) {
173114402Sru		device_printf(dev, "could not allocate memory resource\n");
174151497Sru		return (ENXIO);
175151497Sru	}
176151497Sru	sc->sc_bt = rman_get_bustag(sc->sc_mem_res);
177114402Sru	sc->sc_bh = rman_get_bushandle(sc->sc_mem_res);
178151497Sru	if (bus_alloc_resources(dev, ti_pruss_irq_spec, sc->sc_irq_res) != 0) {
179151497Sru		device_printf(dev, "could not allocate interrupt resource\n");
180151497Sru		ti_pruss_detach(dev);
181151497Sru		return (ENXIO);
182151497Sru	}
183151497Sru	for (i = 0; i < TI_PRUSS_IRQS; i++) {
184151497Sru		ti_pruss_irq_args[i].irq = i;
185151497Sru		ti_pruss_irq_args[i].sc = sc;
186151497Sru		if (bus_setup_intr(dev, sc->sc_irq_res[i],
187151497Sru		    INTR_MPSAFE | INTR_TYPE_MISC,
188151497Sru		    NULL, ti_pruss_intr, &ti_pruss_irq_args[i],
189151497Sru		    &sc->sc_intr[i]) != 0) {
190114402Sru			device_printf(dev,
191151497Sru			    "unable to setup the interrupt handler\n");
192151497Sru			ti_pruss_detach(dev);
193151497Sru			return (ENXIO);
194151497Sru		}
195151497Sru	}
196151497Sru	if (ti_pruss_reg_read(sc, PRUSS_AM18XX_INTC) == PRUSS_AM18XX_REV)
197151497Sru		device_printf(dev, "AM18xx PRU-ICSS\n");
198151497Sru	else if (ti_pruss_reg_read(sc, PRUSS_AM33XX_INTC) == PRUSS_AM33XX_REV)
199151497Sru		device_printf(dev, "AM33xx PRU-ICSS\n");
200151497Sru
201151497Sru	sc->sc_pdev = make_dev(&ti_pruss_cdevsw, 0, UID_ROOT, GID_WHEEL,
202151497Sru	    0600, "pruss%d", device_get_unit(dev));
203151497Sru	sc->sc_pdev->si_drv1 = dev;
204114402Sru
205151497Sru	return (0);
206151497Sru}
207151497Sru
208151497Srustatic int
209151497Sruti_pruss_detach(device_t dev)
210114402Sru{
211114402Sru	struct ti_pruss_softc *sc;
212151497Sru	int i;
213114402Sru
214151497Sru	sc = device_get_softc(dev);
215114402Sru	for (i = 0; i < TI_PRUSS_IRQS; i++) {
216114402Sru		if (sc->sc_intr[i])
217114402Sru			bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_intr[i]);
218114402Sru		if (sc->sc_irq_res[i])
219114402Sru			bus_release_resource(dev, SYS_RES_IRQ,
220114402Sru			    rman_get_rid(sc->sc_irq_res[i]),
221151497Sru			    sc->sc_irq_res[i]);
222114402Sru	}
223151497Sru	if (sc->sc_mem_res)
224114402Sru		bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem_res),
225114402Sru		    sc->sc_mem_res);
226114402Sru	if (sc->sc_pdev)
227114402Sru		destroy_dev(sc->sc_pdev);
228151497Sru
229114402Sru	return (0);
230151497Sru}
231151497Sru
232114402Srustatic void
233114402Sruti_pruss_intr(void *arg)
234151497Sru{
235114402Sru	struct ti_pruss_irq_arg *iap;
236114402Sru	struct ti_pruss_softc *sc;
237114402Sru
238114402Sru	iap = arg;
239114402Sru	sc = iap->sc;
240151497Sru	DPRINTF("interrupt %p", sc);
241151497Sru	KNOTE_UNLOCKED(&sc->sc_selinfo.si_note, iap->irq);
242114402Sru}
243114402Sru
244151497Srustatic int
245114402Sruti_pruss_open(struct cdev *cdev __unused, int oflags __unused,
246114402Sru    int devtype __unused, struct thread *td __unused)
247114402Sru{
248114402Sru	return (0);
249114402Sru}
250114402Sru
251114402Srustatic int
252114402Sruti_pruss_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
253114402Sru    int nprot, vm_memattr_t *memattr)
254114402Sru{
255114402Sru	device_t dev = cdev->si_drv1;
256114402Sru	struct ti_pruss_softc *sc = device_get_softc(dev);
257114402Sru
258114402Sru	if (offset > rman_get_size(sc->sc_mem_res))
259114402Sru		return (-1);
260114402Sru	*paddr = rman_get_start(sc->sc_mem_res) + offset;
261114402Sru	*memattr = VM_MEMATTR_UNCACHEABLE;
262114402Sru
263114402Sru	return (0);
264114402Sru}
265114402Sru
266114402Srustatic struct filterops ti_pruss_kq_read = {
267114402Sru	.f_isfd = 1,
268114402Sru	.f_detach = ti_pruss_kq_read_detach,
269114402Sru	.f_event = ti_pruss_kq_read_event,
270114402Sru};
271114402Sru
272114402Srustatic void
273114402Sruti_pruss_kq_read_detach(struct knote *kn)
274114402Sru{
275114402Sru	struct ti_pruss_softc *sc = kn->kn_hook;
276114402Sru
277114402Sru	knlist_remove(&sc->sc_selinfo.si_note, kn, 0);
278114402Sru}
279114402Sru
280114402Srustatic int
281114402Sruti_pruss_kq_read_event(struct knote *kn, long hint)
282114402Sru{
283114402Sru	kn->kn_data = hint;
284114402Sru
285151497Sru	return (hint);
286114402Sru}
287114402Sru
288151497Srustatic int
289114402Sruti_pruss_kqfilter(struct cdev *cdev, struct knote *kn)
290114402Sru{
291114402Sru	device_t dev = cdev->si_drv1;
292114402Sru	struct ti_pruss_softc *sc = device_get_softc(dev);
293114402Sru
294114402Sru	switch (kn->kn_filter) {
295114402Sru	case EVFILT_READ:
296114402Sru		kn->kn_hook = sc;
297114402Sru		kn->kn_fop = &ti_pruss_kq_read;
298114402Sru		knlist_add(&sc->sc_selinfo.si_note, kn, 1);
299114402Sru		break;
300114402Sru	default:
301114402Sru		return (EINVAL);
302114402Sru	}
303114402Sru
304114402Sru	return (0);
305114402Sru}
306114402Sru