svn commit: r297541 - in head/sys/arm/ti: . omap4

Svatopluk Kraus skra at FreeBSD.org
Mon Apr 4 09:29:32 UTC 2016


Author: skra
Date: Mon Apr  4 09:29:30 2016
New Revision: 297541
URL: https://svnweb.freebsd.org/changeset/base/297541

Log:
  Rework TI gpio interrupt controller for INTRNG. It's used on PANDABOARD
  and BEAGLEBONE where INTRNG is already enabled by default.
  
  Reviewed by:	gonzo
  Differential Revision:	https://reviews.freebsd.org/D5806

Modified:
  head/sys/arm/ti/omap4/omap4_gpio.c
  head/sys/arm/ti/ti_gpio.c
  head/sys/arm/ti/ti_gpio.h

Modified: head/sys/arm/ti/omap4/omap4_gpio.c
==============================================================================
--- head/sys/arm/ti/omap4/omap4_gpio.c	Mon Apr  4 09:23:21 2016	(r297540)
+++ head/sys/arm/ti/omap4/omap4_gpio.c	Mon Apr  4 09:29:30 2016	(r297541)
@@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/gpio.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 
 #include <dev/fdt/fdt_common.h>
 #include <dev/ofw/ofw_bus.h>

Modified: head/sys/arm/ti/ti_gpio.c
==============================================================================
--- head/sys/arm/ti/ti_gpio.c	Mon Apr  4 09:23:21 2016	(r297540)
+++ head/sys/arm/ti/ti_gpio.c	Mon Apr  4 09:29:30 2016	(r297541)
@@ -33,12 +33,15 @@
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
+#include "opt_platform.h"
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/bus.h>
 
 #include <sys/kernel.h>
 #include <sys/module.h>
+#include <sys/proc.h>
 #include <sys/rman.h>
 #include <sys/lock.h>
 #include <sys/mutex.h>
@@ -46,6 +49,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/interrupt.h>
 
 #include <machine/bus.h>
+#include <machine/intr.h>
 #include <machine/resource.h>
 
 #include <arm/ti/ti_cpuid.h>
@@ -62,6 +66,9 @@ __FBSDID("$FreeBSD$");
 
 #include "gpio_if.h"
 #include "ti_gpio_if.h"
+#ifdef ARM_INTRNG
+#include "pic_if.h"
+#endif
 
 #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X)
 #error "Unknown SoC"
@@ -72,12 +79,12 @@ __FBSDID("$FreeBSD$");
 #define	TI_GPIO_SYSCONFIG		0x0010
 #define	TI_GPIO_IRQSTATUS_RAW_0		0x0024
 #define	TI_GPIO_IRQSTATUS_RAW_1		0x0028
-#define	TI_GPIO_IRQSTATUS_0		0x002C
-#define	TI_GPIO_IRQSTATUS_1		0x0030
-#define	TI_GPIO_IRQSTATUS_SET_0		0x0034
-#define	TI_GPIO_IRQSTATUS_SET_1		0x0038
-#define	TI_GPIO_IRQSTATUS_CLR_0		0x003C
-#define	TI_GPIO_IRQSTATUS_CLR_1		0x0040
+#define	TI_GPIO_IRQSTATUS_0		0x002C	/* writing a 0 has no effect */
+#define	TI_GPIO_IRQSTATUS_1		0x0030	/* writing a 0 has no effect */
+#define	TI_GPIO_IRQSTATUS_SET_0		0x0034	/* writing a 0 has no effect */
+#define	TI_GPIO_IRQSTATUS_SET_1		0x0038	/* writing a 0 has no effect */
+#define	TI_GPIO_IRQSTATUS_CLR_0		0x003C	/* writing a 0 has no effect */
+#define	TI_GPIO_IRQSTATUS_CLR_1		0x0040	/* writing a 0 has no effect */
 #define	TI_GPIO_IRQWAKEN_0		0x0044
 #define	TI_GPIO_IRQWAKEN_1		0x0048
 #define	TI_GPIO_SYSSTATUS		0x0114
@@ -90,10 +97,10 @@ __FBSDID("$FreeBSD$");
 #define	TI_GPIO_OE			0x0134
 #define	TI_GPIO_DATAIN			0x0138
 #define	TI_GPIO_DATAOUT			0x013C
-#define	TI_GPIO_LEVELDETECT0		0x0140
-#define	TI_GPIO_LEVELDETECT1		0x0144
-#define	TI_GPIO_RISINGDETECT		0x0148
-#define	TI_GPIO_FALLINGDETECT		0x014C
+#define	TI_GPIO_LEVELDETECT0		0x0140	/* RW register */
+#define	TI_GPIO_LEVELDETECT1		0x0144	/* RW register */
+#define	TI_GPIO_RISINGDETECT		0x0148	/* RW register */
+#define	TI_GPIO_FALLINGDETECT		0x014C	/* RW register */
 #define	TI_GPIO_DEBOUNCENABLE		0x0150
 #define	TI_GPIO_DEBOUNCINGTIME		0x0154
 #define	TI_GPIO_CLEARWKUPENA		0x0180
@@ -111,8 +118,14 @@ __FBSDID("$FreeBSD$");
 #define	PINS_PER_BANK			32
 #define	TI_GPIO_MASK(p)			(1U << ((p) % PINS_PER_BANK))
 
+static int ti_gpio_intr(void *arg);
 static int ti_gpio_detach(device_t);
 
+#ifdef ARM_INTRNG
+static int ti_gpio_pic_attach(struct ti_gpio_softc *sc);
+static int ti_gpio_pic_detach(struct ti_gpio_softc *sc);
+#endif
+
 static u_int
 ti_first_gpio_bank(void)
 {
@@ -533,6 +546,7 @@ ti_gpio_pin_toggle(device_t dev, uint32_
 	return (0);
 }
 
+#ifndef ARM_INTRNG
 /**
  *	ti_gpio_intr - ISR for all GPIO modules
  *	@arg: the soft context pointer
@@ -567,6 +581,7 @@ ti_gpio_intr(void *arg)
 
 	return (FILTER_HANDLED);
 }
+#endif
 
 static int
 ti_gpio_bank_init(device_t dev)
@@ -640,7 +655,9 @@ static int
 ti_gpio_attach(device_t dev)
 {
 	struct ti_gpio_softc *sc;
+#ifndef ARM_INTRNG
 	unsigned int i;
+#endif
 	int err;
 
 	sc = device_get_softc(dev);
@@ -679,6 +696,13 @@ ti_gpio_attach(device_t dev)
 		return (ENXIO);
 	}
 
+#ifdef ARM_INTRNG
+	if (ti_gpio_pic_attach(sc) != 0) {
+		device_printf(dev, "WARNING: unable to attach PIC\n");
+		ti_gpio_detach(dev);
+		return (ENXIO);
+	}
+#else
 	/*
 	 * Initialize the interrupt settings.  The default is active-low
 	 * interrupts.
@@ -699,7 +723,7 @@ ti_gpio_attach(device_t dev)
 
 	sc->sc_mask_args = malloc(sizeof(struct ti_gpio_mask_arg) * sc->sc_maxpin,
 	    M_DEVBUF, M_WAITOK | M_ZERO);
-
+#endif
 	/* We need to go through each block and ensure the clocks are running and
 	 * the module is enabled.  It might be better to do this only when the
 	 * pins are configured which would result in less power used if the GPIO
@@ -747,6 +771,10 @@ ti_gpio_detach(device_t dev)
 	if (sc->sc_mem_res != NULL)
 		ti_gpio_intr_clr(sc, 0xffffffff);
 	gpiobus_detach_bus(dev);
+#ifdef	ARM_INTRNG
+	if (sc->sc_isrcs != NULL)
+		ti_gpio_pic_detach(sc);
+#else
 	if (sc->sc_events)
 		free(sc->sc_events, M_DEVBUF);
 	if (sc->sc_mask_args)
@@ -755,6 +783,7 @@ ti_gpio_detach(device_t dev)
 		free(sc->sc_irq_polarity, M_DEVBUF);
 	if (sc->sc_irq_trigger)
 		free(sc->sc_irq_trigger, M_DEVBUF);
+#endif
 	/* Release the memory and IRQ resources. */
 	if (sc->sc_irq_hdl) {
 		bus_teardown_intr(dev, sc->sc_irq_res,
@@ -769,6 +798,282 @@ ti_gpio_detach(device_t dev)
 	return (0);
 }
 
+#ifdef ARM_INTRNG
+static inline void
+ti_gpio_rwreg_set(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask)
+{
+
+	ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) | mask);
+}
+
+static inline void
+ti_gpio_rwreg_clr(struct ti_gpio_softc *sc, uint32_t reg, uint32_t mask)
+{
+
+	ti_gpio_write_4(sc, reg, ti_gpio_read_4(sc, reg) & ~mask);
+}
+
+static inline void
+ti_gpio_isrc_mask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+	/* Writing a 0 has no effect. */
+	ti_gpio_intr_clr(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_unmask(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+	/* Writing a 0 has no effect. */
+	ti_gpio_intr_set(sc, tgi->tgi_mask);
+}
+
+static inline void
+ti_gpio_isrc_eoi(struct ti_gpio_softc *sc, struct ti_gpio_irqsrc *tgi)
+{
+
+	/* Writing a 0 has no effect. */
+	ti_gpio_intr_ack(sc, tgi->tgi_mask);
+}
+
+static inline bool
+ti_gpio_isrc_is_level(struct ti_gpio_irqsrc *tgi)
+{
+
+	return (tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT0 ||
+	    tgi->tgi_cfgreg == TI_GPIO_LEVELDETECT1);
+}
+
+static int
+ti_gpio_intr(void *arg)
+{
+	u_int irq;
+	uint32_t reg;
+	struct ti_gpio_softc *sc;
+	struct trapframe *tf;
+	struct ti_gpio_irqsrc *tgi;
+
+	sc = (struct ti_gpio_softc *)arg;
+	tf = curthread->td_intr_frame;
+
+	reg = ti_gpio_intr_status(sc);
+	for (irq = 0; irq < sc->sc_maxpin; irq++) {
+		tgi = &sc->sc_isrcs[irq];
+		if ((reg & tgi->tgi_mask) == 0)
+			continue;
+		if (!ti_gpio_isrc_is_level(tgi))
+			ti_gpio_isrc_eoi(sc, tgi);
+		if (intr_isrc_dispatch(&tgi->tgi_isrc, tf) != 0) {
+			ti_gpio_isrc_mask(sc, tgi);
+			if (ti_gpio_isrc_is_level(tgi))
+				ti_gpio_isrc_eoi(sc, tgi);
+			device_printf(sc->sc_dev, "Stray irq %u disabled\n",
+			    irq);
+		}
+	}
+	return (FILTER_HANDLED);
+}
+
+static int
+ti_gpio_pic_attach(struct ti_gpio_softc *sc)
+{
+	int error;
+	uint32_t irq;
+	const char *name;
+
+	sc->sc_isrcs = malloc(sizeof(*sc->sc_isrcs) * sc->sc_maxpin, M_DEVBUF,
+	    M_WAITOK | M_ZERO);
+
+	name = device_get_nameunit(sc->sc_dev);
+	for (irq = 0; irq < sc->sc_maxpin; irq++) {
+		sc->sc_isrcs[irq].tgi_irq = irq;
+		sc->sc_isrcs[irq].tgi_mask = TI_GPIO_MASK(irq);
+		sc->sc_isrcs[irq].tgi_cfgreg = 0;
+
+		error = intr_isrc_register(&sc->sc_isrcs[irq].tgi_isrc,
+		    sc->sc_dev, 0, "%s,%u", name, irq);
+		if (error != 0)
+			return (error); /* XXX deregister ISRCs */
+	}
+	return (intr_pic_register(sc->sc_dev,
+	    OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))));
+}
+
+static int
+ti_gpio_pic_detach(struct ti_gpio_softc *sc)
+{
+
+	/*
+	 *  There has not been established any procedure yet
+	 *  how to detach PIC from living system correctly.
+	 */
+	device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__);
+	return (EBUSY);
+}
+
+static void
+ti_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	ti_gpio_isrc_mask(sc, tgi);
+}
+
+static void
+ti_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	arm_irq_memory_barrier(tgi->tgi_irq);
+	ti_gpio_isrc_unmask(sc, tgi);
+}
+
+static int
+ti_gpio_pic_map_fdt(struct ti_gpio_softc *sc, u_int ncells, pcell_t *cells,
+    u_int *irqp, uint32_t *regp)
+{
+	uint32_t reg;
+
+	/*
+	 * The first cell is the interrupt number.
+	 * The second cell is used to specify flags:
+	 *	bits[3:0] trigger type and level flags:
+	 *		1 = low-to-high edge triggered.
+	 *		2 = high-to-low edge triggered.
+	 *		4 = active high level-sensitive.
+	 *		8 = active low level-sensitive.
+	 */
+	if (ncells != 2 || cells[0] >= sc->sc_maxpin)
+		return (EINVAL);
+
+	/*
+	 * All interrupt types could be set for an interrupt at one moment.
+	 * At least, the combination of 'low-to-high' and 'high-to-low' edge
+	 * triggered interrupt types can make a sense. However, no combo is
+	 * supported now.
+	 */
+	if (cells[1] == 1)
+		reg = TI_GPIO_RISINGDETECT;
+	else if (cells[1] == 2)
+		reg = TI_GPIO_FALLINGDETECT;
+	else if (cells[1] == 4)
+		reg = TI_GPIO_LEVELDETECT1;
+	else if (cells[1] == 8)
+		reg = TI_GPIO_LEVELDETECT0;
+	else
+		return (EINVAL);
+
+	*irqp = cells[0];
+	if (regp != NULL)
+		*regp = reg;
+	return (0);
+}
+
+static int
+ti_gpio_pic_map_intr(device_t dev, struct intr_map_data *data,
+    struct intr_irqsrc **isrcp)
+{
+	int error;
+	u_int irq;
+	struct ti_gpio_softc *sc;
+
+	if (data->type != INTR_MAP_DATA_FDT)
+		return (ENOTSUP);
+
+	sc = device_get_softc(dev);
+	error = ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq,
+	    NULL);
+	if (error == 0)
+		*isrcp = &sc->sc_isrcs[irq].tgi_isrc;
+	return (error);
+}
+
+static void
+ti_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	if (ti_gpio_isrc_is_level(tgi))
+		ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static void
+ti_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+
+	ti_gpio_pic_enable_intr(dev, isrc);
+}
+
+static void
+ti_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	ti_gpio_isrc_mask(sc, tgi);
+	if (ti_gpio_isrc_is_level(tgi))
+		ti_gpio_isrc_eoi(sc, tgi);
+}
+
+static int
+ti_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	u_int irq;
+	uint32_t cfgreg;
+	struct ti_gpio_softc *sc;
+	struct ti_gpio_irqsrc *tgi;
+
+	if (data == NULL || data->type != INTR_MAP_DATA_FDT)
+		return (ENOTSUP);
+
+	sc = device_get_softc(dev);
+	tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	/* Get and check config for an interrupt. */
+	if (ti_gpio_pic_map_fdt(sc, data->fdt.ncells, data->fdt.cells, &irq,
+	    &cfgreg) != 0 || tgi->tgi_irq != irq)
+		return (EINVAL);
+
+	/*
+	 * If this is a setup for another handler,
+	 * only check that its configuration match.
+	 */
+	if (isrc->isrc_handlers != 0)
+		return (tgi->tgi_cfgreg == cfgreg ? 0 : EINVAL);
+
+	TI_GPIO_LOCK(sc);
+	ti_gpio_rwreg_clr(sc, TI_GPIO_RISINGDETECT, tgi->tgi_mask);
+	ti_gpio_rwreg_clr(sc, TI_GPIO_FALLINGDETECT, tgi->tgi_mask);
+	ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT1, tgi->tgi_mask);
+	ti_gpio_rwreg_clr(sc, TI_GPIO_LEVELDETECT0, tgi->tgi_mask);
+	tgi->tgi_cfgreg = cfgreg;
+	ti_gpio_rwreg_set(sc, cfgreg, tgi->tgi_mask);
+	TI_GPIO_UNLOCK(sc);
+	return (0);
+}
+
+static int
+ti_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
+    struct resource *res, struct intr_map_data *data)
+{
+	struct ti_gpio_softc *sc = device_get_softc(dev);
+	struct ti_gpio_irqsrc *tgi = (struct ti_gpio_irqsrc *)isrc;
+
+	if (isrc->isrc_handlers == 0) {
+		TI_GPIO_LOCK(sc);
+		ti_gpio_rwreg_clr(sc, tgi->tgi_cfgreg, tgi->tgi_mask);
+		tgi->tgi_cfgreg = 0;
+		TI_GPIO_UNLOCK(sc);
+	}
+	return (0);
+}
+
+#else
 static uint32_t
 ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq)
 {
@@ -970,6 +1275,7 @@ ti_gpio_teardown_intr(device_t dev, devi
 
 	return (err);
 }
+#endif
 
 static phandle_t
 ti_gpio_get_node(device_t bus, device_t dev)
@@ -994,12 +1300,24 @@ static device_method_t ti_gpio_methods[]
 	DEVMETHOD(gpio_pin_set, ti_gpio_pin_set),
 	DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle),
 
+#ifdef ARM_INTRNG
+	/* Interrupt controller interface */
+	DEVMETHOD(pic_disable_intr,	ti_gpio_pic_disable_intr),
+	DEVMETHOD(pic_enable_intr,	ti_gpio_pic_enable_intr),
+	DEVMETHOD(pic_map_intr,		ti_gpio_pic_map_intr),
+	DEVMETHOD(pic_setup_intr,	ti_gpio_pic_setup_intr),
+	DEVMETHOD(pic_teardown_intr,	ti_gpio_pic_teardown_intr),
+	DEVMETHOD(pic_post_filter,	ti_gpio_pic_post_filter),
+	DEVMETHOD(pic_post_ithread,	ti_gpio_pic_post_ithread),
+	DEVMETHOD(pic_pre_ithread,	ti_gpio_pic_pre_ithread),
+#else
 	/* Bus interface */
 	DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource),
 	DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource),
 	DEVMETHOD(bus_config_intr, ti_gpio_config_intr),
 	DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr),
 	DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr),
+#endif
 
 	/* ofw_bus interface */
 	DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node),

Modified: head/sys/arm/ti/ti_gpio.h
==============================================================================
--- head/sys/arm/ti/ti_gpio.h	Mon Apr  4 09:23:21 2016	(r297540)
+++ head/sys/arm/ti/ti_gpio.h	Mon Apr  4 09:29:30 2016	(r297541)
@@ -39,10 +39,19 @@
  */
 #define	MAX_GPIO_INTRS			8
 
+#ifndef ARM_INTRNG
 struct ti_gpio_mask_arg {
 	void	*softc;
 	int	pin;
 };
+#else
+struct ti_gpio_irqsrc {
+	struct intr_irqsrc	tgi_isrc;
+	u_int			tgi_irq;
+	uint32_t		tgi_mask;
+	uint32_t		tgi_cfgreg;
+};
+#endif
 
 /**
  *	Structure that stores the driver context.
@@ -52,11 +61,11 @@ struct ti_gpio_mask_arg {
 struct ti_gpio_softc {
 	device_t		sc_dev;
 	device_t		sc_busdev;
-
+#ifndef ARM_INTRNG
 	/* Interrupt trigger type and level. */
 	enum intr_trigger	*sc_irq_trigger;
 	enum intr_polarity	*sc_irq_polarity;
-
+#endif
 	int			sc_bank;
 	int			sc_maxpin;
 	struct mtx		sc_mtx;
@@ -65,11 +74,13 @@ struct ti_gpio_softc {
 	struct resource		*sc_mem_res;
 	int			sc_irq_rid;
 	struct resource		*sc_irq_res;
-
+#ifndef ARM_INTRNG
 	/* Interrupt events. */
 	struct intr_event	**sc_events;
 	struct ti_gpio_mask_arg	*sc_mask_args;
-
+#else
+	struct ti_gpio_irqsrc	*sc_isrcs;
+#endif
 	/* The handle for the register IRQ handlers. */
 	void			*sc_irq_hdl;
 };


More information about the svn-src-head mailing list