at91_pio.c revision 160072
1/*-
2 * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include <sys/cdefs.h>
26__FBSDID("$FreeBSD: head/sys/arm/at91/at91_pio.c 160072 2006-07-02 03:50:44Z imp $");
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/bus.h>
31#include <sys/conf.h>
32#include <sys/kernel.h>
33#include <sys/lock.h>
34#include <sys/mbuf.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/mutex.h>
38#include <sys/rman.h>
39#include <machine/bus.h>
40
41#include <arm/at91/at91rm92reg.h>
42#include <arm/at91/at91_pioreg.h>
43#include <arm/at91/at91_piovar.h>
44
45struct at91_pio_softc
46{
47	device_t dev;			/* Myself */
48	void *intrhand;			/* Interrupt handle */
49	struct resource *irq_res;	/* IRQ resource */
50	struct resource	*mem_res;	/* Memory resource */
51	struct mtx sc_mtx;		/* basically a perimeter lock */
52	struct cdev *cdev;
53	int flags;
54#define OPENED 1
55};
56
57static inline uint32_t
58RD4(struct at91_pio_softc *sc, bus_size_t off)
59{
60	return bus_read_4(sc->mem_res, off);
61}
62
63static inline void
64WR4(struct at91_pio_softc *sc, bus_size_t off, uint32_t val)
65{
66	bus_write_4(sc->mem_res, off, val);
67}
68
69#define AT91_PIO_LOCK(_sc)		mtx_lock_spin(&(_sc)->sc_mtx)
70#define	AT91_PIO_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
71#define AT91_PIO_LOCK_INIT(_sc) \
72	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
73	    "pio", MTX_SPIN)
74#define AT91_PIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
75#define AT91_PIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
76#define AT91_PIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
77#define CDEV2SOFTC(dev)		((dev)->si_drv1)
78
79static devclass_t at91_pio_devclass;
80
81/* bus entry points */
82
83static int at91_pio_probe(device_t dev);
84static int at91_pio_attach(device_t dev);
85static int at91_pio_detach(device_t dev);
86static void at91_pio_intr(void *);
87
88/* helper routines */
89static int at91_pio_activate(device_t dev);
90static void at91_pio_deactivate(device_t dev);
91
92/* cdev routines */
93static d_open_t at91_pio_open;
94static d_close_t at91_pio_close;
95static d_ioctl_t at91_pio_ioctl;
96
97static struct cdevsw at91_pio_cdevsw =
98{
99	.d_version = D_VERSION,
100	.d_open = at91_pio_open,
101	.d_close = at91_pio_close,
102	.d_ioctl = at91_pio_ioctl
103};
104
105static int
106at91_pio_probe(device_t dev)
107{
108	const char *name;
109
110	switch (device_get_unit(dev)) {
111	case 0:
112		name = "PIOA";
113		break;
114	case 1:
115		name = "PIOB";
116		break;
117	case 2:
118		name = "PIOC";
119		break;
120	case 3:
121		name = "PIOD";
122		break;
123	default:
124		name = "PIO";
125		break;
126	}
127	device_set_desc(dev, name);
128	return (0);
129}
130
131static int
132at91_pio_attach(device_t dev)
133{
134	struct at91_pio_softc *sc = device_get_softc(dev);
135	int err;
136
137	sc->dev = dev;
138	err = at91_pio_activate(dev);
139	if (err)
140		goto out;
141
142	device_printf(dev, "ABSR: %#x OSR: %#x PSR:%#x ODSR: %#x\n",
143	    RD4(sc, PIO_ABSR), RD4(sc, PIO_OSR), RD4(sc, PIO_PSR),
144	    RD4(sc, PIO_ODSR));
145	AT91_PIO_LOCK_INIT(sc);
146
147	/*
148	 * Activate the interrupt, but disable all interrupts in the hardware
149	 */
150	WR4(sc, PIO_IDR, 0xffffffff);
151	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_FAST,
152	    at91_pio_intr, sc, &sc->intrhand);
153	if (err) {
154		AT91_PIO_LOCK_DESTROY(sc);
155		goto out;
156	}
157	sc->cdev = make_dev(&at91_pio_cdevsw, device_get_unit(dev), UID_ROOT
158	    , GID_WHEEL, 0600, "pio%d", device_get_unit(dev));
159	if (sc->cdev == NULL) {
160		err = ENOMEM;
161		goto out;
162	}
163	sc->cdev->si_drv1 = sc;
164out:;
165	if (err)
166		at91_pio_deactivate(dev);
167	return (err);
168}
169
170static int
171at91_pio_detach(device_t dev)
172{
173	return (EBUSY);	/* XXX */
174}
175
176static int
177at91_pio_activate(device_t dev)
178{
179	struct at91_pio_softc *sc;
180	int rid;
181
182	sc = device_get_softc(dev);
183	rid = 0;
184	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
185	    RF_ACTIVE);
186	if (sc->mem_res == NULL)
187		goto errout;
188	rid = 0;
189	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
190	    RF_ACTIVE | RF_SHAREABLE);
191	if (sc->irq_res == NULL)
192		goto errout;
193	return (0);
194errout:
195	at91_pio_deactivate(dev);
196	return (ENOMEM);
197}
198
199static void
200at91_pio_deactivate(device_t dev)
201{
202	struct at91_pio_softc *sc;
203
204	sc = device_get_softc(dev);
205	if (sc->intrhand)
206		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
207	sc->intrhand = 0;
208	bus_generic_detach(sc->dev);
209	if (sc->mem_res)
210		bus_release_resource(dev, SYS_RES_IOPORT,
211		    rman_get_rid(sc->mem_res), sc->mem_res);
212	sc->mem_res = 0;
213	if (sc->irq_res)
214		bus_release_resource(dev, SYS_RES_IRQ,
215		    rman_get_rid(sc->irq_res), sc->irq_res);
216	sc->irq_res = 0;
217	return;
218}
219
220static void
221at91_pio_intr(void *xsc)
222{
223	struct at91_pio_softc *sc = xsc;
224#if 0
225	uint32_t status;
226
227	/* Reading the status also clears the interrupt */
228	status = RD4(sc, PIO_SR);
229	if (status == 0)
230		return;
231	AT91_PIO_LOCK(sc);
232	AT91_PIO_UNLOCK(sc);
233#endif
234	wakeup(sc);
235	return;
236}
237
238static int
239at91_pio_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
240{
241	struct at91_pio_softc *sc;
242
243	sc = CDEV2SOFTC(dev);
244	AT91_PIO_LOCK(sc);
245	if (!(sc->flags & OPENED)) {
246		sc->flags |= OPENED;
247#if 0
248	// Enable interrupts
249#endif
250	}
251	AT91_PIO_UNLOCK(sc);
252    	return (0);
253}
254
255static int
256at91_pio_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
257{
258	struct at91_pio_softc *sc;
259
260	sc = CDEV2SOFTC(dev);
261	AT91_PIO_LOCK(sc);
262	sc->flags &= ~OPENED;
263#if 0
264	// Disable interrupts
265#endif
266	AT91_PIO_UNLOCK(sc);
267	return (0);
268}
269
270static int
271at91_pio_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
272    struct thread *td)
273{
274	return (ENXIO);
275}
276
277/*
278 * The following functions are called early in the boot process, so
279 * don't use bus_space, as that isn't yet available when we need to use
280 * them.
281 */
282void
283at91_pio_use_periph_a(uint32_t pio, uint32_t periph_a_mask)
284{
285	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
286
287	PIO[PIO_ASR / 4] = periph_a_mask;
288	PIO[PIO_PDR / 4] = periph_a_mask;
289}
290
291void
292at91_pio_use_periph_b(uint32_t pio, uint32_t periph_b_mask)
293{
294	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
295
296	PIO[PIO_BSR / 4] = periph_b_mask;
297	PIO[PIO_PDR / 4] = periph_b_mask;
298}
299
300void
301at91_pio_use_gpio(uint32_t pio, uint32_t gpio_mask)
302{
303	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
304
305	PIO[PIO_PER / 4] = gpio_mask;
306}
307
308void
309at91_pio_gpio_input(uint32_t pio, uint32_t input_enable_mask)
310{
311	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
312
313	PIO[PIO_ODR / 4] = input_enable_mask;
314}
315
316void
317at91_pio_gpio_output(uint32_t pio, uint32_t output_enable_mask)
318{
319	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
320
321	PIO[PIO_OER / 4] = output_enable_mask;
322}
323
324void
325at91_pio_gpio_set(uint32_t pio, uint32_t data_mask)
326{
327	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
328
329	PIO[PIO_SODR / 4] = data_mask;
330}
331
332void
333at91_pio_gpio_clear(uint32_t pio, uint32_t data_mask)
334{
335	uint32_t *PIO = (uint32_t *)(AT91RM92_BASE + pio);
336
337	PIO[PIO_CODR / 4] = data_mask;
338}
339
340static device_method_t at91_pio_methods[] = {
341	/* Device interface */
342	DEVMETHOD(device_probe,		at91_pio_probe),
343	DEVMETHOD(device_attach,	at91_pio_attach),
344	DEVMETHOD(device_detach,	at91_pio_detach),
345
346	{ 0, 0 }
347};
348
349static driver_t at91_pio_driver = {
350	"at91_pio",
351	at91_pio_methods,
352	sizeof(struct at91_pio_softc),
353};
354
355DRIVER_MODULE(at91_pio, atmelarm, at91_pio_driver, at91_pio_devclass, 0, 0);
356