1157089Simp/*-
2157089Simp * Copyright (c) 2006 M. Warner Losh.  All rights reserved.
3157089Simp *
4157089Simp * Redistribution and use in source and binary forms, with or without
5157089Simp * modification, are permitted provided that the following conditions
6157089Simp * are met:
7157089Simp * 1. Redistributions of source code must retain the above copyright
8157089Simp *    notice, this list of conditions and the following disclaimer.
9157089Simp * 2. Redistributions in binary form must reproduce the above copyright
10157089Simp *    notice, this list of conditions and the following disclaimer in the
11157089Simp *    documentation and/or other materials provided with the distribution.
12157089Simp *
13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23185265Simp * SUCH DAMAGE.
24157089Simp */
25157089Simp
26157089Simp#include <sys/cdefs.h>
27157089Simp__FBSDID("$FreeBSD$");
28157089Simp
29157089Simp#include <sys/param.h>
30157089Simp#include <sys/systm.h>
31157089Simp#include <sys/bus.h>
32157089Simp#include <sys/conf.h>
33157089Simp#include <sys/kernel.h>
34157089Simp#include <sys/lock.h>
35157089Simp#include <sys/mbuf.h>
36157089Simp#include <sys/malloc.h>
37157089Simp#include <sys/module.h>
38157089Simp#include <sys/mutex.h>
39157089Simp#include <sys/rman.h>
40157089Simp#include <machine/bus.h>
41157089Simp
42213496Scognet#include <arm/at91/at91reg.h>
43157089Simp#include <arm/at91/at91_pioreg.h>
44160072Simp#include <arm/at91/at91_piovar.h>
45157089Simp
46157089Simpstruct at91_pio_softc
47157089Simp{
48157089Simp	device_t dev;			/* Myself */
49157089Simp	void *intrhand;			/* Interrupt handle */
50157089Simp	struct resource *irq_res;	/* IRQ resource */
51157089Simp	struct resource	*mem_res;	/* Memory resource */
52157089Simp	struct mtx sc_mtx;		/* basically a perimeter lock */
53157089Simp	struct cdev *cdev;
54157089Simp	int flags;
55236080Smarius#define	OPENED 1
56157089Simp};
57157089Simp
58157089Simpstatic inline uint32_t
59157089SimpRD4(struct at91_pio_softc *sc, bus_size_t off)
60157089Simp{
61236080Smarius
62213496Scognet	return (bus_read_4(sc->mem_res, off));
63157089Simp}
64157089Simp
65157089Simpstatic inline void
66157089SimpWR4(struct at91_pio_softc *sc, bus_size_t off, uint32_t val)
67157089Simp{
68236080Smarius
69157089Simp	bus_write_4(sc->mem_res, off, val);
70157089Simp}
71157089Simp
72236080Smarius#define	AT91_PIO_LOCK(_sc)		mtx_lock_spin(&(_sc)->sc_mtx)
73157089Simp#define	AT91_PIO_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
74236080Smarius#define	AT91_PIO_LOCK_INIT(_sc) \
75157089Simp	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \
76157089Simp	    "pio", MTX_SPIN)
77236080Smarius#define	AT91_PIO_LOCK_DESTROY(_sc)	mtx_destroy(&_sc->sc_mtx);
78236080Smarius#define	AT91_PIO_ASSERT_LOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_OWNED);
79236080Smarius#define	AT91_PIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&_sc->sc_mtx, MA_NOTOWNED);
80236080Smarius#define	CDEV2SOFTC(dev)			((dev)->si_drv1)
81157089Simp
82157089Simpstatic devclass_t at91_pio_devclass;
83157089Simp
84157089Simp/* bus entry points */
85157089Simp
86157089Simpstatic int at91_pio_probe(device_t dev);
87157089Simpstatic int at91_pio_attach(device_t dev);
88157089Simpstatic int at91_pio_detach(device_t dev);
89166901Spisostatic int at91_pio_intr(void *);
90157089Simp
91157089Simp/* helper routines */
92157089Simpstatic int at91_pio_activate(device_t dev);
93157089Simpstatic void at91_pio_deactivate(device_t dev);
94157089Simp
95157089Simp/* cdev routines */
96157089Simpstatic d_open_t at91_pio_open;
97157089Simpstatic d_close_t at91_pio_close;
98157089Simpstatic d_ioctl_t at91_pio_ioctl;
99157089Simp
100157089Simpstatic struct cdevsw at91_pio_cdevsw =
101157089Simp{
102157089Simp	.d_version = D_VERSION,
103157089Simp	.d_open = at91_pio_open,
104157089Simp	.d_close = at91_pio_close,
105157089Simp	.d_ioctl = at91_pio_ioctl
106157089Simp};
107157089Simp
108157089Simpstatic int
109157089Simpat91_pio_probe(device_t dev)
110157089Simp{
111160072Simp	const char *name;
112160072Simp
113160072Simp	switch (device_get_unit(dev)) {
114160072Simp	case 0:
115160072Simp		name = "PIOA";
116160072Simp		break;
117160072Simp	case 1:
118160072Simp		name = "PIOB";
119160072Simp		break;
120160072Simp	case 2:
121160072Simp		name = "PIOC";
122160072Simp		break;
123160072Simp	case 3:
124160072Simp		name = "PIOD";
125160072Simp		break;
126160072Simp	default:
127160072Simp		name = "PIO";
128160072Simp		break;
129160072Simp	}
130160072Simp	device_set_desc(dev, name);
131157089Simp	return (0);
132157089Simp}
133157089Simp
134157089Simpstatic int
135157089Simpat91_pio_attach(device_t dev)
136157089Simp{
137236080Smarius	struct at91_pio_softc *sc;
138157089Simp	int err;
139157089Simp
140236080Smarius	sc = device_get_softc(dev);
141157089Simp	sc->dev = dev;
142157089Simp	err = at91_pio_activate(dev);
143157089Simp	if (err)
144157089Simp		goto out;
145157089Simp
146160072Simp	device_printf(dev, "ABSR: %#x OSR: %#x PSR:%#x ODSR: %#x\n",
147160072Simp	    RD4(sc, PIO_ABSR), RD4(sc, PIO_OSR), RD4(sc, PIO_PSR),
148160072Simp	    RD4(sc, PIO_ODSR));
149157089Simp	AT91_PIO_LOCK_INIT(sc);
150157089Simp
151157089Simp	/*
152236080Smarius	 * Activate the interrupt, but disable all interrupts in the hardware.
153157089Simp	 */
154157089Simp	WR4(sc, PIO_IDR, 0xffffffff);
155166901Spiso	err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC,
156166901Spiso	    at91_pio_intr, NULL, sc, &sc->intrhand);
157157089Simp	if (err) {
158157089Simp		AT91_PIO_LOCK_DESTROY(sc);
159157089Simp		goto out;
160157089Simp	}
161164745Simp	sc->cdev = make_dev(&at91_pio_cdevsw, device_get_unit(dev), UID_ROOT,
162164745Simp	    GID_WHEEL, 0600, "pio%d", device_get_unit(dev));
163157089Simp	if (sc->cdev == NULL) {
164157089Simp		err = ENOMEM;
165157089Simp		goto out;
166157089Simp	}
167157089Simp	sc->cdev->si_drv1 = sc;
168237093Smariusout:
169157089Simp	if (err)
170157089Simp		at91_pio_deactivate(dev);
171157089Simp	return (err);
172157089Simp}
173157089Simp
174157089Simpstatic int
175157089Simpat91_pio_detach(device_t dev)
176157089Simp{
177236080Smarius
178157089Simp	return (EBUSY);	/* XXX */
179157089Simp}
180157089Simp
181157089Simpstatic int
182157089Simpat91_pio_activate(device_t dev)
183157089Simp{
184157089Simp	struct at91_pio_softc *sc;
185157089Simp	int rid;
186157089Simp
187157089Simp	sc = device_get_softc(dev);
188157089Simp	rid = 0;
189157089Simp	sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
190157089Simp	    RF_ACTIVE);
191157089Simp	if (sc->mem_res == NULL)
192157089Simp		goto errout;
193157089Simp	rid = 0;
194157089Simp	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
195157089Simp	    RF_ACTIVE | RF_SHAREABLE);
196157089Simp	if (sc->irq_res == NULL)
197157089Simp		goto errout;
198157089Simp	return (0);
199157089Simperrout:
200157089Simp	at91_pio_deactivate(dev);
201157089Simp	return (ENOMEM);
202157089Simp}
203157089Simp
204157089Simpstatic void
205157089Simpat91_pio_deactivate(device_t dev)
206157089Simp{
207157089Simp	struct at91_pio_softc *sc;
208157089Simp
209157089Simp	sc = device_get_softc(dev);
210157089Simp	if (sc->intrhand)
211157089Simp		bus_teardown_intr(dev, sc->irq_res, sc->intrhand);
212157089Simp	sc->intrhand = 0;
213157089Simp	bus_generic_detach(sc->dev);
214157089Simp	if (sc->mem_res)
215157089Simp		bus_release_resource(dev, SYS_RES_IOPORT,
216157089Simp		    rman_get_rid(sc->mem_res), sc->mem_res);
217157089Simp	sc->mem_res = 0;
218157089Simp	if (sc->irq_res)
219157089Simp		bus_release_resource(dev, SYS_RES_IRQ,
220157089Simp		    rman_get_rid(sc->irq_res), sc->irq_res);
221157089Simp	sc->irq_res = 0;
222157089Simp}
223157089Simp
224166901Spisostatic int
225157089Simpat91_pio_intr(void *xsc)
226157089Simp{
227157089Simp	struct at91_pio_softc *sc = xsc;
228157089Simp#if 0
229157089Simp	uint32_t status;
230157089Simp
231236080Smarius	/* Reading the status also clears the interrupt. */
232157089Simp	status = RD4(sc, PIO_SR);
233157089Simp	if (status == 0)
234157089Simp		return;
235157089Simp	AT91_PIO_LOCK(sc);
236157089Simp	AT91_PIO_UNLOCK(sc);
237157089Simp#endif
238157089Simp	wakeup(sc);
239166901Spiso	return (FILTER_HANDLED);
240157089Simp}
241157089Simp
242236080Smariusstatic int
243157089Simpat91_pio_open(struct cdev *dev, int oflags, int devtype, struct thread *td)
244157089Simp{
245157089Simp	struct at91_pio_softc *sc;
246157089Simp
247157089Simp	sc = CDEV2SOFTC(dev);
248157089Simp	AT91_PIO_LOCK(sc);
249157089Simp	if (!(sc->flags & OPENED)) {
250157089Simp		sc->flags |= OPENED;
251157089Simp#if 0
252236080Smarius	/* Enable interrupts. */
253157089Simp#endif
254157089Simp	}
255157089Simp	AT91_PIO_UNLOCK(sc);
256236080Smarius	return (0);
257157089Simp}
258157089Simp
259157089Simpstatic int
260157089Simpat91_pio_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
261157089Simp{
262157089Simp	struct at91_pio_softc *sc;
263157089Simp
264157089Simp	sc = CDEV2SOFTC(dev);
265157089Simp	AT91_PIO_LOCK(sc);
266157089Simp	sc->flags &= ~OPENED;
267157089Simp#if 0
268236080Smarius	/* Disable interrupts. */
269157089Simp#endif
270157089Simp	AT91_PIO_UNLOCK(sc);
271157089Simp	return (0);
272157089Simp}
273157089Simp
274157089Simpstatic int
275157089Simpat91_pio_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
276157089Simp    struct thread *td)
277157089Simp{
278236080Smarius
279157089Simp	return (ENXIO);
280157089Simp}
281157089Simp
282160072Simp/*
283160072Simp * The following functions are called early in the boot process, so
284160072Simp * don't use bus_space, as that isn't yet available when we need to use
285160072Simp * them.
286160072Simp */
287236080Smarius
288160072Simpvoid
289160363Simpat91_pio_use_periph_a(uint32_t pio, uint32_t periph_a_mask, int use_pullup)
290160072Simp{
291213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
292160072Simp
293160072Simp	PIO[PIO_ASR / 4] = periph_a_mask;
294160072Simp	PIO[PIO_PDR / 4] = periph_a_mask;
295160363Simp	if (use_pullup)
296160363Simp		PIO[PIO_PUER / 4] = periph_a_mask;
297160363Simp	else
298160363Simp		PIO[PIO_PUDR / 4] = periph_a_mask;
299160072Simp}
300160072Simp
301160072Simpvoid
302160363Simpat91_pio_use_periph_b(uint32_t pio, uint32_t periph_b_mask, int use_pullup)
303160072Simp{
304213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
305160072Simp
306160072Simp	PIO[PIO_BSR / 4] = periph_b_mask;
307160072Simp	PIO[PIO_PDR / 4] = periph_b_mask;
308160363Simp	if (use_pullup)
309160363Simp		PIO[PIO_PUER / 4] = periph_b_mask;
310160363Simp	else
311160363Simp		PIO[PIO_PUDR / 4] = periph_b_mask;
312160072Simp}
313160072Simp
314160072Simpvoid
315160072Simpat91_pio_use_gpio(uint32_t pio, uint32_t gpio_mask)
316160072Simp{
317213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
318160072Simp
319160072Simp	PIO[PIO_PER / 4] = gpio_mask;
320160072Simp}
321160072Simp
322160072Simpvoid
323160072Simpat91_pio_gpio_input(uint32_t pio, uint32_t input_enable_mask)
324160072Simp{
325213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
326160072Simp
327160072Simp	PIO[PIO_ODR / 4] = input_enable_mask;
328160072Simp}
329160072Simp
330160072Simpvoid
331160363Simpat91_pio_gpio_output(uint32_t pio, uint32_t output_enable_mask, int use_pullup)
332160072Simp{
333213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
334160072Simp
335160072Simp	PIO[PIO_OER / 4] = output_enable_mask;
336160363Simp	if (use_pullup)
337160363Simp		PIO[PIO_PUER / 4] = output_enable_mask;
338160363Simp	else
339160363Simp		PIO[PIO_PUDR / 4] = output_enable_mask;
340160072Simp}
341160072Simp
342160072Simpvoid
343160072Simpat91_pio_gpio_set(uint32_t pio, uint32_t data_mask)
344160072Simp{
345213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
346160072Simp
347160072Simp	PIO[PIO_SODR / 4] = data_mask;
348160072Simp}
349160072Simp
350160072Simpvoid
351160072Simpat91_pio_gpio_clear(uint32_t pio, uint32_t data_mask)
352160072Simp{
353213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
354160072Simp
355160072Simp	PIO[PIO_CODR / 4] = data_mask;
356160072Simp}
357160072Simp
358181884Simpuint8_t
359181884Simpat91_pio_gpio_get(uint32_t pio, uint32_t data_mask)
360181884Simp{
361213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
362181884Simp
363181884Simp	data_mask &= PIO[PIO_PDSR / 4];
364181884Simp
365181884Simp	return (data_mask ? 1 : 0);
366181884Simp}
367181884Simp
368181884Simpvoid
369181884Simpat91_pio_gpio_set_deglitch(uint32_t pio, uint32_t data_mask, int use_deglitch)
370181884Simp{
371213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
372181884Simp
373181884Simp	if (use_deglitch)
374181884Simp		PIO[PIO_IFER / 4] = data_mask;
375181884Simp	else
376181884Simp		PIO[PIO_IFDR / 4] = data_mask;
377181884Simp}
378181884Simp
379181884Simpvoid
380236080Smariusat91_pio_gpio_set_interrupt(uint32_t pio, uint32_t data_mask,
381181884Simp	int enable_interrupt)
382181884Simp{
383213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
384181884Simp
385181884Simp	if (enable_interrupt)
386181884Simp		PIO[PIO_IER / 4] = data_mask;
387181884Simp	else
388181884Simp		PIO[PIO_IDR / 4] = data_mask;
389181884Simp}
390181884Simp
391181884Simpuint32_t
392181884Simpat91_pio_gpio_clear_interrupt(uint32_t pio)
393181884Simp{
394213496Scognet	uint32_t *PIO = (uint32_t *)(AT91_BASE + pio);
395236080Smarius
396236080Smarius	/* Reading this register will clear the interrupts. */
397181884Simp	return (PIO[PIO_ISR / 4]);
398181884Simp}
399181884Simp
400157089Simpstatic device_method_t at91_pio_methods[] = {
401157089Simp	/* Device interface */
402157089Simp	DEVMETHOD(device_probe,		at91_pio_probe),
403157089Simp	DEVMETHOD(device_attach,	at91_pio_attach),
404157089Simp	DEVMETHOD(device_detach,	at91_pio_detach),
405157089Simp
406236080Smarius	DEVMETHOD_END
407157089Simp};
408157089Simp
409157089Simpstatic driver_t at91_pio_driver = {
410157089Simp	"at91_pio",
411157089Simp	at91_pio_methods,
412157089Simp	sizeof(struct at91_pio_softc),
413157089Simp};
414157089Simp
415236080SmariusDRIVER_MODULE(at91_pio, atmelarm, at91_pio_driver, at91_pio_devclass, NULL,
416236080Smarius    NULL);
417