[PATCH 08/11] Add LPC32XX touch screen driver

Kevin Kirspel kevin-kirspel at idexx.com
Mon May 15 17:26:35 UTC 2017


---
 rtemsbsd/include/bsp/nexus-devices.h             |   1 +
 rtemsbsd/include/machine/rtems-bsd-nexus-bus.h   |  22 ++
 rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c | 403 +++++++++++++++++++++++
 3 files changed, 426 insertions(+)
 mode change 100755 => 100644 rtemsbsd/include/bsp/nexus-devices.h
 mode change 100755 => 100644 rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
 create mode 100644 rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c

diff --git a/rtemsbsd/include/bsp/nexus-devices.h b/rtemsbsd/include/bsp/nexus-devices.h
old mode 100755
new mode 100644
index cfd6730..1fbf756
--- a/rtemsbsd/include/bsp/nexus-devices.h
+++ b/rtemsbsd/include/bsp/nexus-devices.h
@@ -52,6 +52,7 @@ RTEMS_BSD_DRIVER_SMC0(0x4e000000,  RVPBXA9_IRQ_ETHERNET);
 
 RTEMS_BSD_DRIVER_LPC32XX_PWR;
 RTEMS_BSD_DRIVER_LPC32XX_LPE;
+RTEMS_BSD_DRIVER_LPC32XX_TSC;
 RTEMS_BSD_DRIVER_ICSPHY;
 RTEMS_BSD_DRIVER_LPC32XX_OHCI;
 SYSINIT_DRIVER_REFERENCE(usbus, ohci);
diff --git a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
old mode 100755
new mode 100644
index 232e8e2..06048b7
--- a/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
+++ b/rtemsbsd/include/machine/rtems-bsd-nexus-bus.h
@@ -39,6 +39,7 @@
  *  Devices:
  *   RTEMS_BSD_DRIVER_XILINX_ZYNQ_SLCR
  *   RTEMS_BSD_DRIVER_LPC32XX_PWR
+ *   RTEMS_BSD_DRIVER_LPC32XX_TSC
  *
  *  Buses:
  *   RTEMS_BSD_DRIVER_PC_LEGACY
@@ -136,6 +137,27 @@ extern "C" {
                                   &lpc_pwr0_res[0])
 #endif /* RTEMS_BSD_DRIVER_LPC32XX_PWR */
 
+/*
+ * LPC32XX TSC.
+ */
+#if !defined(RTEMS_BSD_DRIVER_LPC32XX_TSC)
+  #define RTEMS_BSD_DRIVER_LPC32XX_TSC                                      \
+    static const rtems_bsd_device_resource lpc_tsc0_res[] = {               \
+      {                                                                     \
+        .type = RTEMS_BSD_RES_MEMORY,                                       \
+        .start_request = 0,                                                 \
+        .start_actual = LPC32XX_BASE_ADC                                    \
+      }, {                                                                  \
+        .type = RTEMS_BSD_RES_IRQ,                                          \
+        .start_request = 0,                                                 \
+        .start_actual = LPC32XX_IRQ_TS_IRQ_OR_ADC                           \
+      }                                                                     \
+    };                                                                      \
+    RTEMS_BSD_DEFINE_NEXUS_DEVICE(lpctsc, 0,                                \
+                                  RTEMS_ARRAY_SIZE(lpc_tsc0_res),           \
+                                  &lpc_tsc0_res[0])
+#endif /* RTEMS_BSD_DRIVER_LPC32XX_TSC */
+
 /**
  ** Physical Buses
  **/
diff --git a/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c
new file mode 100644
index 0000000..e52f545
--- /dev/null
+++ b/rtemsbsd/sys/dev/input/touchscreen/tsc_lpc32xx.c
@@ -0,0 +1,403 @@
+/*
+ *  COPYRIGHT (c) 1989-2017.
+ *  On-Line Applications Research Corporation (OAR).
+ *
+ *  The license and distribution terms for this file may be
+ *  found in the file LICENSE in this distribution or at
+ *  http://www.rtems.org/license/LICENSE.
+ */
+#include <machine/rtems-bsd-kernel-space.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rtems/bsd/local/opt_evdev.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/systm.h>
+#include <sys/rman.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <rtems/bsd/sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/sysctl.h>
+#include <sys/selinfo.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+
+#include <dev/evdev/input.h>
+#include <dev/evdev/evdev.h>
+
+#include <machine/bus.h>
+
+#include <bsp.h>
+#if defined(LIBBSP_ARM_LPC32XX_BSP_H) && defined(EVDEV_SUPPORT)
+
+#include <arm/lpc/lpcreg.h>
+#include <arm/lpc/lpcvar.h>
+
+//#define LPC32XX_TSC_USE_PCLK
+/*
+ * Touchscreen controller register offsets
+ */
+#define LPC32XX_TSC_STAT										0x00
+#define LPC32XX_TSC_SEL											0x04
+#define LPC32XX_TSC_CON											0x08
+#define LPC32XX_TSC_FIFO										0x0C
+#define LPC32XX_TSC_DTR											0x10
+#define LPC32XX_TSC_RTR											0x14
+#define LPC32XX_TSC_UTR											0x18
+#define LPC32XX_TSC_TTR											0x1C
+#define LPC32XX_TSC_DXP											0x20
+#define LPC32XX_TSC_MIN_X										0x24
+#define LPC32XX_TSC_MAX_X										0x28
+#define LPC32XX_TSC_MIN_Y										0x2C
+#define LPC32XX_TSC_MAX_Y										0x30
+#define LPC32XX_TSC_AUX_UTR									0x34
+#define LPC32XX_TSC_AUX_MIN									0x38
+#define LPC32XX_TSC_AUX_MAX									0x3C
+
+#define LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL		(1 << 8)
+#define LPC32XX_CLKPWR_ADCCTRL1_RTDIV(n)		(((n) & 0xFF) << 0)
+#define LPC32XX_TSC_CLK											400000UL
+
+#define LPC32XX_TSC_STAT_FIFO_OVRRN					(1 << 8)
+#define LPC32XX_TSC_STAT_FIFO_EMPTY					(1 << 7)
+
+#define LPC32XX_TSC_SEL_DEFVAL							0x0284
+
+#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4		(0x1 << 11)
+#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s)	((10 - (s)) << 7)
+#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s)	((10 - (s)) << 4)
+#define LPC32XX_TSC_ADCCON_POWER_UP					(1 << 2)
+#define LPC32XX_TSC_ADCCON_AUTO_EN					(1 << 0)
+
+#define LPC32XX_TSC_FIFO_TS_P_LEVEL		(1 << 31)
+#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x)	(((x) & 0x03FF0000) >> 16)
+#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y)	((y) & 0x000003FF)
+
+#define LPC32XX_TSC_ADCDAT_VALUE_MASK		0x000003FF
+
+#define LPC32XX_TSC_MIN_XY_VAL			0x0
+#define LPC32XX_TSC_MAX_XY_VAL			0x3FF
+
+#define	LPC_TSC_LOCK(_sc)		\
+	mtx_lock(&(_sc)->sc_mtx)
+#define	LPC_TSC_UNLOCK(_sc)		\
+	mtx_unlock(&(_sc)->sc_mtx)
+#define	LPC_TSC_LOCK_INIT(_sc)	\
+	mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \
+		  "lpctsc", MTX_DEF)
+#define	LPC_TSC_LOCK_DESTROY(_sc)	\
+	mtx_destroy(&_sc->sc_mtx);
+#define	LPC_TSC_LOCK_ASSERT(_sc)	\
+	mtx_assert(&(_sc)->sc_mtx, MA_OWNED)
+
+#define	TSCBARR(sc) bus_space_barrier((sc)->sc_io_tag, (sc)->sc_io_hdl, 0, (sc)->sc_io_size, \
+			BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
+#define	TSCWRITE1(sc, r, x) \
+ do { TSCBARR(sc); bus_space_write_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define	TSCWRITE2(sc, r, x) \
+ do { TSCBARR(sc); bus_space_write_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define	TSCWRITE4(sc, r, x) \
+ do { TSCBARR(sc); bus_space_write_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r), (x)); } while (0)
+#define	TSCREAD1(sc, r) (TSCBARR(sc), bus_space_read_1((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define	TSCREAD2(sc, r) (TSCBARR(sc), bus_space_read_2((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+#define	TSCREAD4(sc, r) (TSCBARR(sc), bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, (r)))
+
+struct lpc_tsc_softc {
+	device_t sc_dev;
+	struct mtx sc_mtx;
+	struct resource	*sc_io_res;
+	struct resource *sc_irq_res;
+	void *sc_intr_hdl;
+	bus_space_tag_t sc_io_tag;
+	bus_space_handle_t sc_io_hdl;
+	bus_size_t sc_io_size;
+	struct evdev_dev *sc_evdev;
+};
+
+static evdev_open_t lpc_tsc_ev_open;
+static evdev_close_t lpc_tsc_ev_close;
+
+static const struct evdev_methods lpc_tsc_evdev_methods = {
+	.ev_open = &lpc_tsc_ev_open,
+	.ev_close = &lpc_tsc_ev_close,
+};
+
+static void
+lpc_adc_module_enable(struct lpc_tsc_softc *sc)
+{
+	uint32_t pclk, divider;
+
+  lpc_pwr_write(sc->sc_dev, LPC_CLKPWR_ADCLK_CTRL, 1);
+#ifdef LPC32XX_TSC_USE_PCLK
+  /* Get the clock frequency for the peripheral clock*/
+  pclk = LPC32XX_PERIPH_CLK;
+	/* compute the divider needed to ensure we are at or below 400KHz ADC Sampling rate*/
+  divider = ( pclk / LPC32XX_TSC_CLK ) + 1;
+  lpc_pwr_write(sc->sc_dev, LPC_CLKPWR_ADCLK_CTRL1,
+      LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL |
+      LPC32XX_CLKPWR_ADCCTRL1_RTDIV(divider));
+#else
+  lpc_pwr_write(sc->sc_dev, LPC_CLKPWR_ADCLK_CTRL1, 0);
+#endif
+}
+
+static void
+lpc_adc_module_disable(struct lpc_tsc_softc *sc)
+{
+  lpc_pwr_write(sc->sc_dev, LPC_CLKPWR_ADCLK_CTRL, 0);
+}
+
+static void
+lpc_adc_fifo_clear(struct lpc_tsc_softc *sc)
+{
+	uint32_t tmp;
+
+	while(!(TSCREAD4(sc, LPC32XX_TSC_STAT) & LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+		tmp = TSCREAD4(sc, LPC32XX_TSC_FIFO);
+	}
+}
+
+static void
+lpc_tsc_interrupt(void *arg)
+{
+	struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)arg;
+	uint32_t tmp, rv[4], xs[4], ys[4];
+	int idx;
+	int id, i, x, y;
+
+	LPC_TSC_LOCK(sc);
+
+	tmp = TSCREAD4(sc, LPC32XX_TSC_STAT);
+	if(tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
+		lpc_adc_fifo_clear(sc);
+		LPC_TSC_UNLOCK(sc);
+		return;
+	}
+
+	/*
+	 * Gather and normalize 4 samples. Pen-up events may have less
+	 * than 4 samples, but its ok to pop 4 and let the last sample
+	 * pen status check drop the samples.
+	 */
+	idx = 0;
+	while((idx < 4) && !(TSCREAD4(sc, LPC32XX_TSC_STAT) & LPC32XX_TSC_STAT_FIFO_EMPTY)) {
+		tmp = TSCREAD4(sc, LPC32XX_TSC_FIFO);
+		xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
+		ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK - LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
+		rv[idx] = tmp;
+		idx++;
+	}
+
+	/* Data is only valid if pen is still down in last sample */
+	if(!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && (idx == 4)) {
+		/* Use average of 2nd and 3rd sample for position */
+		evdev_push_event(sc->sc_evdev, EV_ABS, ABS_X, (xs[1] + xs[2]) / 2);
+		evdev_push_event(sc->sc_evdev, EV_ABS, ABS_Y, (ys[1] + ys[2]) / 2);
+		evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, 1);
+	} else {
+		evdev_push_event(sc->sc_evdev, EV_KEY, BTN_TOUCH, 0);
+	}
+	evdev_sync(sc->sc_evdev);
+
+	LPC_TSC_UNLOCK(sc);
+}
+
+static void
+lpc_tsc_init(struct lpc_tsc_softc *sc)
+{
+	uint32_t tmp;
+
+	tmp = TSCREAD4(sc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
+
+	/* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
+	tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
+	      LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
+	      LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
+	TSCWRITE4(sc, LPC32XX_TSC_CON, tmp);
+
+	/* These values are all preset */
+	TSCWRITE4(sc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
+	TSCWRITE4(sc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
+	TSCWRITE4(sc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
+	TSCWRITE4(sc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
+	TSCWRITE4(sc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
+
+	/* Aux support is not used */
+	TSCWRITE4(sc, LPC32XX_TSC_AUX_UTR, 0);
+	TSCWRITE4(sc, LPC32XX_TSC_AUX_MIN, 0);
+	TSCWRITE4(sc, LPC32XX_TSC_AUX_MAX, 0);
+
+	/*
+	 * Set sample rate to about 240Hz per X/Y pair. A single measurement
+	 * consists of 4 pairs which gives about a 60Hz sample rate based on
+	 * a stable 32768Hz clock source. Values are in clocks.
+	 * Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
+	 */
+	TSCWRITE4(sc, LPC32XX_TSC_RTR, 0x2);
+	TSCWRITE4(sc, LPC32XX_TSC_DTR, 0x2);
+	TSCWRITE4(sc, LPC32XX_TSC_TTR, 0x10);
+	TSCWRITE4(sc, LPC32XX_TSC_DXP, 0x4);
+	TSCWRITE4(sc, LPC32XX_TSC_UTR, 88);
+
+	lpc_adc_fifo_clear(sc);
+
+	/* Enable automatic ts event capture */
+	TSCWRITE4(sc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
+}
+
+static void
+lpc_tsc_ev_close(struct evdev_dev *evdev, void *data)
+{
+	struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data;
+	uint32_t tmp;
+
+	LPC_TSC_LOCK_ASSERT(sc);
+
+	/* Disable auto mode */
+	tmp = TSCREAD4(sc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_AUTO_EN;
+	TSCWRITE4(sc, LPC32XX_TSC_CON, tmp);
+
+	lpc_adc_module_disable(sc);
+}
+
+static int
+lpc_tsc_ev_open(struct evdev_dev *evdev, void *data)
+{
+	struct lpc_tsc_softc *sc = (struct lpc_tsc_softc *)data;
+
+	LPC_TSC_LOCK_ASSERT(sc);
+
+	lpc_adc_module_enable(sc);
+
+	lpc_tsc_init(sc);
+
+	return (0);
+}
+
+static int
+lpc_tsc_probe(device_t dev)
+{
+  device_set_desc(dev, "LPC32x0 Touchscreen controller");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+lpc_tsc_attach(device_t dev)
+{
+	struct lpc_tsc_softc *sc;
+	int rid;
+	int err;
+
+	/* set self dev */
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	rid = 0;
+	sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
+	if (!sc->sc_io_res) {
+		device_printf(dev, "cannot map LPC TSC register space\n");
+		goto fail;
+	}
+
+	sc->sc_io_tag = rman_get_bustag(sc->sc_io_res);
+	sc->sc_io_hdl = rman_get_bushandle(sc->sc_io_res);
+	sc->sc_io_size = rman_get_size(sc->sc_io_res);
+
+	rid = 0;
+	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE);
+	if (sc->sc_irq_res == NULL) {
+		device_printf(dev, "cannot allocate interrupt\n");
+		goto fail;
+	}
+
+	err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, (void *)lpc_tsc_interrupt, sc, &sc->sc_intr_hdl);
+	if (err) {
+		sc->sc_intr_hdl = NULL;
+		goto fail;
+	}
+
+	LPC_TSC_LOCK_INIT(sc);
+
+	sc->sc_evdev = evdev_alloc();
+	evdev_set_name(sc->sc_evdev, device_get_desc(sc->sc_dev));
+	evdev_set_phys(sc->sc_evdev, device_get_nameunit(sc->sc_dev));
+	evdev_set_id(sc->sc_evdev, BUS_HOST, 0, 0, 0);
+	evdev_set_methods(sc->sc_evdev, sc, &lpc_tsc_evdev_methods);
+	evdev_support_prop(sc->sc_evdev, INPUT_PROP_DIRECT);
+	evdev_support_event(sc->sc_evdev, EV_SYN);
+	evdev_support_event(sc->sc_evdev, EV_ABS);
+	evdev_support_event(sc->sc_evdev, EV_KEY);
+
+	evdev_support_abs(sc->sc_evdev, ABS_X, 0, LPC32XX_TSC_MIN_XY_VAL,
+	    LPC32XX_TSC_MAX_XY_VAL, 0, 0, 0);
+	evdev_support_abs(sc->sc_evdev, ABS_Y, 0, LPC32XX_TSC_MIN_XY_VAL,
+	    LPC32XX_TSC_MAX_XY_VAL, 0, 0, 0);
+
+	evdev_support_key(sc->sc_evdev, BTN_TOUCH);
+
+	err = evdev_register_mtx(sc->sc_evdev, &sc->sc_mtx);
+	if (err) {
+		evdev_free(sc->sc_evdev);
+		sc->sc_evdev = NULL;	/* Avoid double free */
+		goto fail;
+	}
+
+	return (0);
+
+fail:
+	if (sc->sc_intr_hdl)
+		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
+	if (sc->sc_irq_res)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+	if (sc->sc_io_res)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res);
+
+	return (ENXIO);
+}
+
+static int
+lpc_tsc_detach(device_t dev)
+{
+	struct lpc_tsc_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	evdev_free(sc->sc_evdev);
+
+	bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_hdl);
+	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_io_res);
+
+	LPC_TSC_LOCK_DESTROY(sc);
+	return (0);
+}
+
+static device_method_t lpc_tsc_methods[] = {
+	/* Device interface */
+  DEVMETHOD(device_probe,   lpc_tsc_probe),
+  DEVMETHOD(device_attach,  lpc_tsc_attach),
+  DEVMETHOD(device_detach,  lpc_tsc_detach),
+
+	DEVMETHOD_END
+};
+
+static driver_t lpc_tsc_driver = {
+	"lpctsc",
+  lpc_tsc_methods,
+	sizeof(struct lpc_tsc_softc),
+};
+
+static devclass_t lpc_tsc_devclass;
+
+DRIVER_MODULE(lpctsc, nexus, lpc_tsc_driver, lpc_tsc_devclass, 0, 0);
+MODULE_DEPEND(lpctsc, evdev, 1, 1, 1);
+
+#endif /* defined(LIBBSP_ARM_LPC32XX_BSP_H) */
-- 
1.9.1



More information about the devel mailing list