xref: /freebsd/sys/dev/gpio/pl061.c (revision fdafd315)
11fc1a228SAndrew Turner /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31fc1a228SAndrew Turner  *
41fc1a228SAndrew Turner  * Copyright (c) 2020 Amazon.com, Inc. or its affiliates.
51fc1a228SAndrew Turner  * All rights reserved.
61fc1a228SAndrew Turner  *
71fc1a228SAndrew Turner  * Redistribution and use in source and binary forms, with or without
81fc1a228SAndrew Turner  * modification, are permitted provided that the following conditions
91fc1a228SAndrew Turner  * are met:
101fc1a228SAndrew Turner  * 1. Redistributions of source code must retain the above copyright
111fc1a228SAndrew Turner  *    notice, this list of conditions and the following disclaimer.
121fc1a228SAndrew Turner  * 2. Redistributions in binary form must reproduce the above copyright
131fc1a228SAndrew Turner  *    notice, this list of conditions and the following disclaimer in the
141fc1a228SAndrew Turner  *    documentation and/or other materials provided with the distribution.
151fc1a228SAndrew Turner  *
161fc1a228SAndrew Turner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
171fc1a228SAndrew Turner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181fc1a228SAndrew Turner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191fc1a228SAndrew Turner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
201fc1a228SAndrew Turner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211fc1a228SAndrew Turner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221fc1a228SAndrew Turner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231fc1a228SAndrew Turner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241fc1a228SAndrew Turner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251fc1a228SAndrew Turner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261fc1a228SAndrew Turner  * SUCH DAMAGE.
271fc1a228SAndrew Turner  */
281fc1a228SAndrew Turner 
291fc1a228SAndrew Turner #include <sys/param.h>
301fc1a228SAndrew Turner #include <sys/systm.h>
311fc1a228SAndrew Turner #include <sys/bus.h>
321fc1a228SAndrew Turner #include <sys/kernel.h>
331fc1a228SAndrew Turner #include <sys/module.h>
341fc1a228SAndrew Turner #include <sys/proc.h>
351fc1a228SAndrew Turner #include <sys/rman.h>
361fc1a228SAndrew Turner #include <sys/lock.h>
371fc1a228SAndrew Turner #include <sys/mutex.h>
381fc1a228SAndrew Turner #include <sys/gpio.h>
391fc1a228SAndrew Turner #include <sys/interrupt.h>
401fc1a228SAndrew Turner 
411fc1a228SAndrew Turner #include <machine/bus.h>
421fc1a228SAndrew Turner #include <machine/intr.h>
431fc1a228SAndrew Turner #include <machine/resource.h>
441fc1a228SAndrew Turner 
451fc1a228SAndrew Turner #include <dev/gpio/gpiobusvar.h>
461fc1a228SAndrew Turner 
471fc1a228SAndrew Turner #include "pl061.h"
481fc1a228SAndrew Turner 
491fc1a228SAndrew Turner #include "gpio_if.h"
501fc1a228SAndrew Turner #include "pic_if.h"
511fc1a228SAndrew Turner 
521fc1a228SAndrew Turner #define	PL061_LOCK(_sc)			mtx_lock_spin(&(_sc)->sc_mtx)
531fc1a228SAndrew Turner #define	PL061_UNLOCK(_sc)		mtx_unlock_spin(&(_sc)->sc_mtx)
541fc1a228SAndrew Turner #define	PL061_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->sc_mtx)
551fc1a228SAndrew Turner #define	PL061_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
561fc1a228SAndrew Turner #define	PL061_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED)
571fc1a228SAndrew Turner 
581fc1a228SAndrew Turner #if 0
591fc1a228SAndrew Turner #define dprintf(fmt, args...) do { 	\
601fc1a228SAndrew Turner 	printf(fmt, ##args);	  	\
611fc1a228SAndrew Turner } while (0)
621fc1a228SAndrew Turner #else
631fc1a228SAndrew Turner #define dprintf(fmt, args...)
641fc1a228SAndrew Turner #endif
651fc1a228SAndrew Turner 
661fc1a228SAndrew Turner #define PL061_PIN_TO_ADDR(pin)  (1 << (pin + 2))
671fc1a228SAndrew Turner #define PL061_DATA		0x3FC
681fc1a228SAndrew Turner #define PL061_DIR		0x400
691fc1a228SAndrew Turner #define PL061_INTSENSE  	0x404
701fc1a228SAndrew Turner #define PL061_INTBOTHEDGES	0x408
711fc1a228SAndrew Turner #define PL061_INTEVENT		0x40C
721fc1a228SAndrew Turner #define PL061_INTMASK		0x410
731fc1a228SAndrew Turner #define PL061_RAWSTATUS		0x414
741fc1a228SAndrew Turner #define PL061_STATUS		0x418
751fc1a228SAndrew Turner #define PL061_INTCLR		0x41C
761fc1a228SAndrew Turner #define PL061_MODECTRL		0x420
771fc1a228SAndrew Turner 
781fc1a228SAndrew Turner #define PL061_ALLOWED_CAPS     (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_INTR_EDGE_BOTH | \
791fc1a228SAndrew Turner 				GPIO_INTR_EDGE_RISING | GPIO_INTR_EDGE_FALLING | \
801fc1a228SAndrew Turner 				GPIO_INTR_LEVEL_HIGH | GPIO_INTR_LEVEL_LOW )
811fc1a228SAndrew Turner 
821fc1a228SAndrew Turner #define PIC_INTR_ISRC(sc, irq) (&(sc->sc_isrcs[irq].isrc))
831fc1a228SAndrew Turner 
841fc1a228SAndrew Turner static device_t
pl061_get_bus(device_t dev)851fc1a228SAndrew Turner pl061_get_bus(device_t dev)
861fc1a228SAndrew Turner {
871fc1a228SAndrew Turner 	struct pl061_softc *sc;
881fc1a228SAndrew Turner 
891fc1a228SAndrew Turner 	sc = device_get_softc(dev);
901fc1a228SAndrew Turner 	return (sc->sc_busdev);
911fc1a228SAndrew Turner }
921fc1a228SAndrew Turner 
931fc1a228SAndrew Turner static int
pl061_pin_max(device_t dev,int * maxpin)941fc1a228SAndrew Turner pl061_pin_max(device_t dev, int *maxpin)
951fc1a228SAndrew Turner {
961fc1a228SAndrew Turner 	*maxpin = PL061_NUM_GPIO - 1;
971fc1a228SAndrew Turner 	return (0);
981fc1a228SAndrew Turner }
991fc1a228SAndrew Turner 
1001fc1a228SAndrew Turner static int
pl061_pin_getname(device_t dev,uint32_t pin,char * name)1011fc1a228SAndrew Turner pl061_pin_getname(device_t dev, uint32_t pin, char *name)
1021fc1a228SAndrew Turner {
1031fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
1041fc1a228SAndrew Turner 		return (EINVAL);
1051fc1a228SAndrew Turner 
1061fc1a228SAndrew Turner 	snprintf(name, GPIOMAXNAME, "p%u", pin);
1071fc1a228SAndrew Turner 	name[GPIOMAXNAME - 1] = '\0';
1081fc1a228SAndrew Turner 
1091fc1a228SAndrew Turner 	return (0);
1101fc1a228SAndrew Turner }
1111fc1a228SAndrew Turner 
1121fc1a228SAndrew Turner static int
pl061_pin_getflags(device_t dev,uint32_t pin,uint32_t * flags)1131fc1a228SAndrew Turner pl061_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
1141fc1a228SAndrew Turner {
1151fc1a228SAndrew Turner 	struct pl061_softc *sc;
1161fc1a228SAndrew Turner 	uint8_t mask = 1 << pin;
1171fc1a228SAndrew Turner 
1181fc1a228SAndrew Turner 	sc = device_get_softc(dev);
1191fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
1201fc1a228SAndrew Turner 		return (EINVAL);
1211fc1a228SAndrew Turner 
1221fc1a228SAndrew Turner 	PL061_LOCK(sc);
1231fc1a228SAndrew Turner 	*flags = 0;
1241fc1a228SAndrew Turner 
1251fc1a228SAndrew Turner 	if (mask & bus_read_1(sc->sc_mem_res, PL061_DIR))
1261fc1a228SAndrew Turner 		*flags |= GPIO_PIN_OUTPUT;
1271fc1a228SAndrew Turner 	else
1281fc1a228SAndrew Turner 		*flags |= GPIO_PIN_INPUT;
1291fc1a228SAndrew Turner 
1301fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
1311fc1a228SAndrew Turner 	return (0);
1321fc1a228SAndrew Turner }
1331fc1a228SAndrew Turner 
1341fc1a228SAndrew Turner static int
pl061_pin_getcaps(device_t dev,uint32_t pin,uint32_t * caps)1351fc1a228SAndrew Turner pl061_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
1361fc1a228SAndrew Turner {
1371fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
1381fc1a228SAndrew Turner 		return (EINVAL);
1391fc1a228SAndrew Turner 
1401fc1a228SAndrew Turner 	*caps = PL061_ALLOWED_CAPS;
1411fc1a228SAndrew Turner 
1421fc1a228SAndrew Turner 	return (0);
1431fc1a228SAndrew Turner }
1441fc1a228SAndrew Turner 
1451fc1a228SAndrew Turner static void
mask_and_set(struct pl061_softc * sc,long a,uint8_t m,uint8_t b)1461fc1a228SAndrew Turner mask_and_set(struct pl061_softc *sc, long a, uint8_t m, uint8_t b)
1471fc1a228SAndrew Turner {
1481fc1a228SAndrew Turner 	uint8_t tmp;
1491fc1a228SAndrew Turner 
1501fc1a228SAndrew Turner 	tmp = bus_read_1(sc->sc_mem_res, a);
1511fc1a228SAndrew Turner 	tmp &= ~m;
1521fc1a228SAndrew Turner 	tmp |= b;
1531fc1a228SAndrew Turner 	bus_write_1(sc->sc_mem_res, a, tmp);
1541fc1a228SAndrew Turner 	dprintf("%s: writing %#x to register %#lx\n", __func__, tmp, a);
1551fc1a228SAndrew Turner }
1561fc1a228SAndrew Turner 
1571fc1a228SAndrew Turner static int
pl061_pin_setflags(device_t dev,uint32_t pin,uint32_t flags)1581fc1a228SAndrew Turner pl061_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
1591fc1a228SAndrew Turner {
1601fc1a228SAndrew Turner 	struct pl061_softc *sc;
1611fc1a228SAndrew Turner 	uint8_t mask = 1 << pin;
1621fc1a228SAndrew Turner 	const uint32_t in_out = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1631fc1a228SAndrew Turner 
1641fc1a228SAndrew Turner 	sc = device_get_softc(dev);
1651fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
1661fc1a228SAndrew Turner 		return (EINVAL);
1671fc1a228SAndrew Turner 
1681fc1a228SAndrew Turner 	if (flags & ~PL061_ALLOWED_CAPS)
1691fc1a228SAndrew Turner 		return (EINVAL);
1701fc1a228SAndrew Turner 
1711fc1a228SAndrew Turner 	/* can't be both input and output */
1721fc1a228SAndrew Turner 	if ((flags & in_out) == in_out)
1731fc1a228SAndrew Turner 		return (EINVAL);
1741fc1a228SAndrew Turner 
1751fc1a228SAndrew Turner 
1761fc1a228SAndrew Turner 	PL061_LOCK(sc);
1771fc1a228SAndrew Turner 	mask_and_set(sc, PL061_DIR, mask, flags & GPIO_PIN_OUTPUT ? mask : 0);
1781fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
1791fc1a228SAndrew Turner 	return (0);
1801fc1a228SAndrew Turner }
1811fc1a228SAndrew Turner 
1821fc1a228SAndrew Turner static int
pl061_pin_get(device_t dev,uint32_t pin,uint32_t * value)1831fc1a228SAndrew Turner pl061_pin_get(device_t dev, uint32_t pin, uint32_t *value)
1841fc1a228SAndrew Turner {
1851fc1a228SAndrew Turner 	struct pl061_softc *sc;
1861fc1a228SAndrew Turner 
1871fc1a228SAndrew Turner 	sc = device_get_softc(dev);
1881fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
1891fc1a228SAndrew Turner 		return (EINVAL);
1901fc1a228SAndrew Turner 
1911fc1a228SAndrew Turner 	PL061_LOCK(sc);
1921fc1a228SAndrew Turner 	if (bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin)))
1931fc1a228SAndrew Turner 		*value = GPIO_PIN_HIGH;
1941fc1a228SAndrew Turner 	else
1951fc1a228SAndrew Turner 		*value = GPIO_PIN_LOW;
1961fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
1971fc1a228SAndrew Turner 
1981fc1a228SAndrew Turner 	return (0);
1991fc1a228SAndrew Turner }
2001fc1a228SAndrew Turner 
2011fc1a228SAndrew Turner static int
pl061_pin_set(device_t dev,uint32_t pin,uint32_t value)2021fc1a228SAndrew Turner pl061_pin_set(device_t dev, uint32_t pin, uint32_t value)
2031fc1a228SAndrew Turner {
2041fc1a228SAndrew Turner 	struct pl061_softc *sc;
2051fc1a228SAndrew Turner 	uint8_t d = (value == GPIO_PIN_HIGH) ? 0xff : 0x00;
2061fc1a228SAndrew Turner 
2071fc1a228SAndrew Turner 	sc = device_get_softc(dev);
2081fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
2091fc1a228SAndrew Turner 		return (EINVAL);
2101fc1a228SAndrew Turner 
2111fc1a228SAndrew Turner 	PL061_LOCK(sc);
2121fc1a228SAndrew Turner 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
2131fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
2141fc1a228SAndrew Turner 
2151fc1a228SAndrew Turner 	return (0);
2161fc1a228SAndrew Turner }
2171fc1a228SAndrew Turner 
2181fc1a228SAndrew Turner static int
pl061_pin_toggle(device_t dev,uint32_t pin)2191fc1a228SAndrew Turner pl061_pin_toggle(device_t dev, uint32_t pin)
2201fc1a228SAndrew Turner {
2211fc1a228SAndrew Turner 	struct pl061_softc *sc;
2221fc1a228SAndrew Turner 	uint8_t d;
2231fc1a228SAndrew Turner 
2241fc1a228SAndrew Turner 	sc = device_get_softc(dev);
2251fc1a228SAndrew Turner 	if (pin >= PL061_NUM_GPIO)
2261fc1a228SAndrew Turner 		return (EINVAL);
2271fc1a228SAndrew Turner 
2281fc1a228SAndrew Turner 	PL061_LOCK(sc);
2291fc1a228SAndrew Turner 	d = ~bus_read_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin));
2301fc1a228SAndrew Turner 	bus_write_1(sc->sc_mem_res, PL061_PIN_TO_ADDR(pin), d);
2311fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
2321fc1a228SAndrew Turner 
2331fc1a228SAndrew Turner 	return (0);
2341fc1a228SAndrew Turner }
2351fc1a228SAndrew Turner 
2361fc1a228SAndrew Turner static void
pl061_pic_disable_intr(device_t dev,struct intr_irqsrc * isrc)2371fc1a228SAndrew Turner pl061_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
2381fc1a228SAndrew Turner {
2391fc1a228SAndrew Turner 	struct pl061_softc *sc;
2401fc1a228SAndrew Turner 	uint8_t mask;
2411fc1a228SAndrew Turner 
2421fc1a228SAndrew Turner 	sc = device_get_softc(dev);
2431fc1a228SAndrew Turner 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
2441fc1a228SAndrew Turner 
2451fc1a228SAndrew Turner 	dprintf("%s: calling disable interrupt %#x\n", __func__, mask);
2461fc1a228SAndrew Turner 	PL061_LOCK(sc);
2471fc1a228SAndrew Turner 	mask_and_set(sc, PL061_INTMASK, mask, 0);
2481fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
2491fc1a228SAndrew Turner }
2501fc1a228SAndrew Turner 
2511fc1a228SAndrew Turner 
2521fc1a228SAndrew Turner 
2531fc1a228SAndrew Turner static void
pl061_pic_enable_intr(device_t dev,struct intr_irqsrc * isrc)2541fc1a228SAndrew Turner pl061_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
2551fc1a228SAndrew Turner {
2561fc1a228SAndrew Turner 	struct pl061_softc *sc;
2571fc1a228SAndrew Turner 	uint8_t mask;
2581fc1a228SAndrew Turner 
2591fc1a228SAndrew Turner 	sc = device_get_softc(dev);
2601fc1a228SAndrew Turner 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
2611fc1a228SAndrew Turner 
2621fc1a228SAndrew Turner 
2631fc1a228SAndrew Turner 	dprintf("%s: calling enable interrupt %#x\n", __func__, mask);
2641fc1a228SAndrew Turner 	PL061_LOCK(sc);
2651fc1a228SAndrew Turner 	mask_and_set(sc, PL061_INTMASK, mask, mask);
2661fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
2671fc1a228SAndrew Turner }
2681fc1a228SAndrew Turner 
2691fc1a228SAndrew Turner static int
pl061_pic_map_intr(device_t dev,struct intr_map_data * data,struct intr_irqsrc ** isrcp)2701fc1a228SAndrew Turner pl061_pic_map_intr(device_t dev, struct intr_map_data *data,
2711fc1a228SAndrew Turner 	struct intr_irqsrc **isrcp)
2721fc1a228SAndrew Turner {
2731fc1a228SAndrew Turner 	struct pl061_softc *sc;
2741fc1a228SAndrew Turner 	struct intr_map_data_gpio *gdata;
2751fc1a228SAndrew Turner 	uint32_t irq;
2761fc1a228SAndrew Turner 
2771fc1a228SAndrew Turner 	sc = device_get_softc(dev);
2781fc1a228SAndrew Turner 	if (data->type != INTR_MAP_DATA_GPIO)
2791fc1a228SAndrew Turner 		return (ENOTSUP);
2801fc1a228SAndrew Turner 
2811fc1a228SAndrew Turner 	gdata = (struct intr_map_data_gpio *)data;
2821fc1a228SAndrew Turner 	irq = gdata->gpio_pin_num;
2831fc1a228SAndrew Turner 	if (irq >= PL061_NUM_GPIO) {
2841fc1a228SAndrew Turner 		device_printf(dev, "invalid interrupt number %u\n", irq);
2851fc1a228SAndrew Turner 		return (EINVAL);
2861fc1a228SAndrew Turner 	}
2871fc1a228SAndrew Turner 
2881fc1a228SAndrew Turner 	dprintf("%s: calling map interrupt %u\n", __func__, irq);
2891fc1a228SAndrew Turner 	*isrcp = PIC_INTR_ISRC(sc, irq);
2901fc1a228SAndrew Turner 
2911fc1a228SAndrew Turner 	return (0);
2921fc1a228SAndrew Turner }
2931fc1a228SAndrew Turner 
2941fc1a228SAndrew Turner static int
pl061_pic_setup_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)2951fc1a228SAndrew Turner pl061_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
2961fc1a228SAndrew Turner 	struct resource *res, struct intr_map_data *data)
2971fc1a228SAndrew Turner {
2981fc1a228SAndrew Turner 	struct pl061_softc *sc;
2991fc1a228SAndrew Turner 	struct intr_map_data_gpio *gdata;
3001fc1a228SAndrew Turner 	struct pl061_pin_irqsrc *irqsrc;
3011fc1a228SAndrew Turner 	uint32_t mode;
3021fc1a228SAndrew Turner 	uint8_t mask;
3031fc1a228SAndrew Turner 
3041fc1a228SAndrew Turner 	if (data == NULL)
3051fc1a228SAndrew Turner 		return (ENOTSUP);
3061fc1a228SAndrew Turner 
3071fc1a228SAndrew Turner 	sc = device_get_softc(dev);
3081fc1a228SAndrew Turner 	gdata = (struct intr_map_data_gpio *)data;
3091fc1a228SAndrew Turner 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
3101fc1a228SAndrew Turner 
3111fc1a228SAndrew Turner 	mode = gdata->gpio_intr_mode;
3121fc1a228SAndrew Turner 	mask = 1 << gdata->gpio_pin_num;
3131fc1a228SAndrew Turner 
3141fc1a228SAndrew Turner 	dprintf("%s: calling setup interrupt %u mode %#x\n", __func__,
3151fc1a228SAndrew Turner 	    irqsrc->irq, mode);
3161fc1a228SAndrew Turner 	if (irqsrc->irq != gdata->gpio_pin_num) {
3171fc1a228SAndrew Turner 		dprintf("%s: interrupts don't match\n", __func__);
3181fc1a228SAndrew Turner 		return (EINVAL);
3191fc1a228SAndrew Turner 	}
3201fc1a228SAndrew Turner 
3211fc1a228SAndrew Turner 	if (isrc->isrc_handlers != 0) {
3221fc1a228SAndrew Turner 		dprintf("%s: handler already attached\n", __func__);
3231fc1a228SAndrew Turner 		return (irqsrc->mode == mode ? 0 : EINVAL);
3241fc1a228SAndrew Turner 	}
3251fc1a228SAndrew Turner 	irqsrc->mode = mode;
3261fc1a228SAndrew Turner 
3271fc1a228SAndrew Turner 	PL061_LOCK(sc);
3281fc1a228SAndrew Turner 
329365ed84fSAndrew Turner 	if (mode & GPIO_INTR_EDGE_BOTH) {
3301fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, mask);
3311fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
332365ed84fSAndrew Turner 	} else if (mode & GPIO_INTR_EDGE_RISING) {
3331fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
3341fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
3351fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
336365ed84fSAndrew Turner 	} else if (mode & GPIO_INTR_EDGE_FALLING) {
3371fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
3381fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTSENSE, mask, 0);
3391fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
340365ed84fSAndrew Turner 	} else if (mode & GPIO_INTR_LEVEL_HIGH) {
3411fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
3421fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
3431fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTEVENT, mask, mask);
344365ed84fSAndrew Turner 	} else if (mode & GPIO_INTR_LEVEL_LOW) {
3451fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTBOTHEDGES, mask, 0);
3461fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTSENSE, mask, mask);
3471fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTEVENT, mask, 0);
3481fc1a228SAndrew Turner 	}
3491fc1a228SAndrew Turner 	PL061_UNLOCK(sc);
3501fc1a228SAndrew Turner 	return (0);
3511fc1a228SAndrew Turner }
3521fc1a228SAndrew Turner 
3531fc1a228SAndrew Turner static int
pl061_pic_teardown_intr(device_t dev,struct intr_irqsrc * isrc,struct resource * res,struct intr_map_data * data)3541fc1a228SAndrew Turner pl061_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
3551fc1a228SAndrew Turner 	struct resource *res, struct intr_map_data *data)
3561fc1a228SAndrew Turner {
3571fc1a228SAndrew Turner 	struct pl061_softc *sc;
3581fc1a228SAndrew Turner 	struct pl061_pin_irqsrc *irqsrc;
3591fc1a228SAndrew Turner 	uint8_t mask;
3601fc1a228SAndrew Turner 
3611fc1a228SAndrew Turner 	irqsrc = (struct pl061_pin_irqsrc *)isrc;
3621fc1a228SAndrew Turner 	mask = 1 << irqsrc->irq;
3631fc1a228SAndrew Turner 	dprintf("%s: calling teardown interrupt %#x\n", __func__, mask);
3641fc1a228SAndrew Turner 
3651fc1a228SAndrew Turner 	sc = device_get_softc(dev);
3661fc1a228SAndrew Turner 	if (isrc->isrc_handlers == 0) {
3671fc1a228SAndrew Turner 		irqsrc->mode = GPIO_INTR_CONFORM;
3681fc1a228SAndrew Turner 		PL061_LOCK(sc);
3691fc1a228SAndrew Turner 		mask_and_set(sc, PL061_INTMASK, mask, 0);
3701fc1a228SAndrew Turner 		PL061_UNLOCK(sc);
3711fc1a228SAndrew Turner 	}
3721fc1a228SAndrew Turner 	return (0);
3731fc1a228SAndrew Turner }
3741fc1a228SAndrew Turner 
3751fc1a228SAndrew Turner static void
pl061_pic_post_filter(device_t dev,struct intr_irqsrc * isrc)3761fc1a228SAndrew Turner pl061_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
3771fc1a228SAndrew Turner {
3781fc1a228SAndrew Turner 	struct pl061_softc *sc;
3791fc1a228SAndrew Turner 	uint8_t mask;
3801fc1a228SAndrew Turner 
3811fc1a228SAndrew Turner 	sc = device_get_softc(dev);
3821fc1a228SAndrew Turner 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
3831fc1a228SAndrew Turner 	dprintf("%s: calling post filter %#x\n", __func__, mask);
3841fc1a228SAndrew Turner 
3851fc1a228SAndrew Turner 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
3861fc1a228SAndrew Turner }
3871fc1a228SAndrew Turner 
3881fc1a228SAndrew Turner static void
pl061_pic_post_ithread(device_t dev,struct intr_irqsrc * isrc)3891fc1a228SAndrew Turner pl061_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
3901fc1a228SAndrew Turner {
3911fc1a228SAndrew Turner 	struct pl061_softc *sc;
3921fc1a228SAndrew Turner 	uint8_t mask;
3931fc1a228SAndrew Turner 
3941fc1a228SAndrew Turner 	sc = device_get_softc(dev);
3951fc1a228SAndrew Turner 	mask = 1 << ((struct pl061_pin_irqsrc *)isrc)->irq;
3961fc1a228SAndrew Turner 	dprintf("%s: calling post ithread %#x\n", __func__, mask);
3971fc1a228SAndrew Turner 	bus_write_1(sc->sc_mem_res, PL061_INTCLR, mask);
3981fc1a228SAndrew Turner 
3991fc1a228SAndrew Turner 	pl061_pic_enable_intr(dev, isrc);
4001fc1a228SAndrew Turner }
4011fc1a228SAndrew Turner 
4021fc1a228SAndrew Turner static void
pl061_pic_pre_ithread(device_t dev,struct intr_irqsrc * isrc)4031fc1a228SAndrew Turner pl061_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
4041fc1a228SAndrew Turner {
4051fc1a228SAndrew Turner 	pl061_pic_disable_intr(dev, isrc);
4061fc1a228SAndrew Turner }
4071fc1a228SAndrew Turner 
4081fc1a228SAndrew Turner static int
pl061_intr(void * arg)4091fc1a228SAndrew Turner pl061_intr(void *arg)
4101fc1a228SAndrew Turner {
4111fc1a228SAndrew Turner 	struct pl061_softc *sc;
4121fc1a228SAndrew Turner 	struct trapframe *tf;
4131fc1a228SAndrew Turner 	uint8_t status;
4141fc1a228SAndrew Turner 	int pin;
4151fc1a228SAndrew Turner 
4161fc1a228SAndrew Turner 	sc = (struct pl061_softc *)arg;
4171fc1a228SAndrew Turner 	tf = curthread->td_intr_frame;
4181fc1a228SAndrew Turner 
4191fc1a228SAndrew Turner 	status = bus_read_1(sc->sc_mem_res, PL061_STATUS);
4201fc1a228SAndrew Turner 
4211fc1a228SAndrew Turner 	while (status != 0) {
4221fc1a228SAndrew Turner 		pin = ffs(status) - 1;
4231fc1a228SAndrew Turner 		status &= ~(1 << pin);
4241fc1a228SAndrew Turner 
4251fc1a228SAndrew Turner 		if (intr_isrc_dispatch(PIC_INTR_ISRC(sc, pin), tf) != 0)
4261fc1a228SAndrew Turner 			device_printf(sc->sc_dev, "spurious interrupt %d\n",
4271fc1a228SAndrew Turner 			    pin);
4281fc1a228SAndrew Turner 
4291fc1a228SAndrew Turner 		dprintf("got IRQ on %d\n", pin);
4301fc1a228SAndrew Turner 
4311fc1a228SAndrew Turner 	}
4321fc1a228SAndrew Turner 	return (FILTER_HANDLED);
4331fc1a228SAndrew Turner }
4341fc1a228SAndrew Turner 
4351fc1a228SAndrew Turner int
pl061_attach(device_t dev)4361fc1a228SAndrew Turner pl061_attach(device_t dev)
4371fc1a228SAndrew Turner {
4381fc1a228SAndrew Turner 	struct pl061_softc *sc;
4391fc1a228SAndrew Turner 	int ret;
4401fc1a228SAndrew Turner 	int irq;
4411fc1a228SAndrew Turner 	const char *name;
4421fc1a228SAndrew Turner 
4431fc1a228SAndrew Turner 	sc = device_get_softc(dev);
4441fc1a228SAndrew Turner 	sc->sc_dev = dev;
4451fc1a228SAndrew Turner 
4461fc1a228SAndrew Turner 	sc->sc_mem_rid = 0;
4471fc1a228SAndrew Turner 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
4481fc1a228SAndrew Turner 	    &sc->sc_mem_rid, RF_ACTIVE);
4491fc1a228SAndrew Turner 	if (sc->sc_mem_res == NULL) {
4501fc1a228SAndrew Turner 		device_printf(dev, "can't allocate memory resource\n");
4511fc1a228SAndrew Turner 		return (ENXIO);
4521fc1a228SAndrew Turner 	}
4531fc1a228SAndrew Turner 
4541fc1a228SAndrew Turner 	sc->sc_irq_rid = 0;
4551fc1a228SAndrew Turner 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
4561fc1a228SAndrew Turner 	    &sc->sc_irq_rid, RF_ACTIVE);
4571fc1a228SAndrew Turner 
4581fc1a228SAndrew Turner 	if (sc->sc_irq_res == NULL) {
4591fc1a228SAndrew Turner 		device_printf(dev, "can't allocate IRQ resource\n");
4601fc1a228SAndrew Turner 		goto free_mem;
4611fc1a228SAndrew Turner 	}
4621fc1a228SAndrew Turner 
4631fc1a228SAndrew Turner 	ret = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
4641fc1a228SAndrew Turner 	    pl061_intr, NULL, sc, &sc->sc_irq_hdlr);
4651fc1a228SAndrew Turner 	if (ret) {
4661fc1a228SAndrew Turner 		device_printf(dev, "can't setup IRQ\n");
4671fc1a228SAndrew Turner 		goto free_pic;
4681fc1a228SAndrew Turner 	}
4691fc1a228SAndrew Turner 
4701fc1a228SAndrew Turner 	name = device_get_nameunit(dev);
4711fc1a228SAndrew Turner 
4721fc1a228SAndrew Turner 	for (irq = 0; irq < PL061_NUM_GPIO; irq++) {
4731fc1a228SAndrew Turner 		if (bootverbose) {
4741fc1a228SAndrew Turner 			device_printf(dev,
4751fc1a228SAndrew Turner 			    "trying to register pin %d name %s\n", irq, name);
4761fc1a228SAndrew Turner 		}
4771fc1a228SAndrew Turner 		sc->sc_isrcs[irq].irq = irq;
4781fc1a228SAndrew Turner 		sc->sc_isrcs[irq].mode = GPIO_INTR_CONFORM;
4791fc1a228SAndrew Turner 		ret = intr_isrc_register(PIC_INTR_ISRC(sc, irq), dev, 0,
4801fc1a228SAndrew Turner 		    "%s", name);
4811fc1a228SAndrew Turner 		if (ret) {
4821fc1a228SAndrew Turner 			device_printf(dev, "can't register isrc %d\n", ret);
4831fc1a228SAndrew Turner 			goto free_isrc;
4841fc1a228SAndrew Turner 		}
4851fc1a228SAndrew Turner 	}
4861fc1a228SAndrew Turner 
4871fc1a228SAndrew Turner 	sc->sc_busdev = gpiobus_attach_bus(dev);
4881fc1a228SAndrew Turner 	if (sc->sc_busdev == NULL) {
4891fc1a228SAndrew Turner 		device_printf(dev, "couldn't attach gpio bus\n");
4901fc1a228SAndrew Turner 		goto free_isrc;
4911fc1a228SAndrew Turner 	}
4921fc1a228SAndrew Turner 
4931fc1a228SAndrew Turner 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "pl061", MTX_SPIN);
4941fc1a228SAndrew Turner 
4951fc1a228SAndrew Turner 	return (0);
4961fc1a228SAndrew Turner 
4971fc1a228SAndrew Turner free_isrc:
4981fc1a228SAndrew Turner 	/*
4991fc1a228SAndrew Turner 	 * XXX isrc_release_counters() not implemented
5001fc1a228SAndrew Turner 	 * for (irq = 0; irq < PL061_NUM_GPIO; irq++)
5011fc1a228SAndrew Turner 	 *	intr_isrc_deregister(PIC_INTR_ISRC(sc, irq));
5021fc1a228SAndrew Turner 	*/
5031fc1a228SAndrew Turner 	bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
5041fc1a228SAndrew Turner 	    sc->sc_irq_res);
5051fc1a228SAndrew Turner free_pic:
5061fc1a228SAndrew Turner         /*
5071fc1a228SAndrew Turner 	 * XXX intr_pic_deregister: not implemented
5081fc1a228SAndrew Turner          * intr_pic_deregister(dev, 0);
5091fc1a228SAndrew Turner          */
5101fc1a228SAndrew Turner 
5111fc1a228SAndrew Turner free_mem:
5121fc1a228SAndrew Turner 	bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
5131fc1a228SAndrew Turner 	    sc->sc_mem_res);
5141fc1a228SAndrew Turner 
5151fc1a228SAndrew Turner 	return (ENXIO);
5161fc1a228SAndrew Turner 
5171fc1a228SAndrew Turner }
5181fc1a228SAndrew Turner 
5191fc1a228SAndrew Turner int
pl061_detach(device_t dev)5201fc1a228SAndrew Turner pl061_detach(device_t dev)
5211fc1a228SAndrew Turner {
5221fc1a228SAndrew Turner 	struct pl061_softc *sc;
5231fc1a228SAndrew Turner 	sc = device_get_softc(dev);
5241fc1a228SAndrew Turner 
5251fc1a228SAndrew Turner 	if (sc->sc_busdev)
5261fc1a228SAndrew Turner 		gpiobus_detach_bus(dev);
5271fc1a228SAndrew Turner 
5281fc1a228SAndrew Turner 	if (sc->sc_irq_hdlr != NULL)
5291fc1a228SAndrew Turner 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_hdlr);
5301fc1a228SAndrew Turner 
5311fc1a228SAndrew Turner 	if (sc->sc_irq_res != NULL)
5321fc1a228SAndrew Turner 		bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
5331fc1a228SAndrew Turner 		    sc->sc_irq_res);
5341fc1a228SAndrew Turner 
5351fc1a228SAndrew Turner 	if (sc->sc_mem_res != NULL)
5361fc1a228SAndrew Turner 		bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
5371fc1a228SAndrew Turner 		    sc->sc_mem_res);
5381fc1a228SAndrew Turner 	PL061_LOCK_DESTROY(sc);
5391fc1a228SAndrew Turner 	return (0);
5401fc1a228SAndrew Turner }
5411fc1a228SAndrew Turner 
5421fc1a228SAndrew Turner static device_method_t pl061_methods[] = {
5431fc1a228SAndrew Turner 	/* Device interface */
5441fc1a228SAndrew Turner 	DEVMETHOD(device_attach,	pl061_attach),
5451fc1a228SAndrew Turner 	DEVMETHOD(device_detach,	pl061_detach),
5461fc1a228SAndrew Turner 
547d729904aSAndrew Turner 	/* Bus interface */
548d729904aSAndrew Turner 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
549d729904aSAndrew Turner 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
550d729904aSAndrew Turner 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
551d729904aSAndrew Turner 
5521fc1a228SAndrew Turner 	/* GPIO protocol */
5531fc1a228SAndrew Turner 	DEVMETHOD(gpio_get_bus,		pl061_get_bus),
5541fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_max,		pl061_pin_max),
5551fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_getname,	pl061_pin_getname),
5561fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_getflags,	pl061_pin_getflags),
5571fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_getcaps,	pl061_pin_getcaps),
5581fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_setflags,	pl061_pin_setflags),
5591fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_get,		pl061_pin_get),
5601fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_set,		pl061_pin_set),
5611fc1a228SAndrew Turner 	DEVMETHOD(gpio_pin_toggle,	pl061_pin_toggle),
5621fc1a228SAndrew Turner 
5631fc1a228SAndrew Turner 	/* Interrupt controller interface */
5641fc1a228SAndrew Turner 	DEVMETHOD(pic_disable_intr,	pl061_pic_disable_intr),
5651fc1a228SAndrew Turner 	DEVMETHOD(pic_enable_intr,	pl061_pic_enable_intr),
5661fc1a228SAndrew Turner 	DEVMETHOD(pic_map_intr,		pl061_pic_map_intr),
5671fc1a228SAndrew Turner 	DEVMETHOD(pic_setup_intr,	pl061_pic_setup_intr),
5681fc1a228SAndrew Turner 	DEVMETHOD(pic_teardown_intr,	pl061_pic_teardown_intr),
5691fc1a228SAndrew Turner 	DEVMETHOD(pic_post_filter,	pl061_pic_post_filter),
5701fc1a228SAndrew Turner 	DEVMETHOD(pic_post_ithread,	pl061_pic_post_ithread),
5711fc1a228SAndrew Turner 	DEVMETHOD(pic_pre_ithread,	pl061_pic_pre_ithread),
5721fc1a228SAndrew Turner 
5731fc1a228SAndrew Turner 	DEVMETHOD_END
5741fc1a228SAndrew Turner };
5751fc1a228SAndrew Turner 
576128e746cSAndrew Turner DEFINE_CLASS_0(gpio, pl061_driver, pl061_methods, sizeof(struct pl061_softc));
577