Home | History | Annotate | Line # | Download | only in sunxi
      1 /* $NetBSD: sunxi_ts.c,v 1.10 2021/08/07 16:18:45 thorpej Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 
     31 __KERNEL_RCSID(0, "$NetBSD: sunxi_ts.c,v 1.10 2021/08/07 16:18:45 thorpej Exp $");
     32 
     33 #include <sys/param.h>
     34 #include <sys/bus.h>
     35 #include <sys/device.h>
     36 #include <sys/intr.h>
     37 #include <sys/systm.h>
     38 #include <sys/time.h>
     39 
     40 #include <dev/fdt/fdtvar.h>
     41 
     42 #include <dev/sysmon/sysmonvar.h>
     43 
     44 #include <dev/wscons/wsconsio.h>
     45 #include <dev/wscons/wsmousevar.h>
     46 #include <dev/wscons/tpcalibvar.h>
     47 
     48 #define	TS_TP_SENSITIVITY_ADJUST_DEFAULT	15
     49 #define	TS_FILTER_TYPE_DEFAULT			1
     50 
     51 #define	TP_CTRL0		0x00
     52 #define	 TP_CTRL0_ADC_FIRST_DLY		__BITS(31,24)
     53 #define	 TP_CTRL0_ADC_FIRST_DLY_MODE	__BIT(23)
     54 #define	 TP_CTRL0_ADC_CLK_SELECT	__BIT(22)
     55 #define	 TP_CTRL0_ADC_CLK_DIVIDER	__BITS(21,20)
     56 #define	 TP_CTRL0_FS_DIV		__BITS(19,16)
     57 #define	 TP_CTRL0_T_ACQ			__BITS(15,0)
     58 #define	TP_CTRL1		0x04
     59 #define	 TP_CTRL1_STYLUS_UP_DEBOUNCE	__BITS(19,12)
     60 #define	 TP_CTRL1_STYLUS_UP_DEBOUNCE_EN	__BIT(9)
     61 #define	 TP_CTRL1_TOUCH_PAN_CALI_EN	__BIT(6)
     62 #define	 TP_CTRL1_TP_DUAL_EN		__BIT(5)
     63 #define	 TP_CTRL1_TP_MODE_EN		__BIT(4)
     64 #define	 TP_CTRL1_TP_ADC_SELECT		__BIT(3)
     65 #define	 TP_CTRL1_ADC_CHAN_SELECT	__BITS(2,0)
     66 #define	TP_CTRL2		0x08
     67 #define	 TP_CTRL2_SENSITIVE_ADJUST	__BITS(31,28)
     68 #define	 TP_CTRL2_MODE_SELECT		__BITS(27,26)
     69 #define	 TP_CTRL2_PRE_MEA_EN		__BIT(24)
     70 #define	 TP_CTRL2_PRE_MEA_THRE_CNT	__BITS(23,0)
     71 #define	TP_CTRL3		0x0c
     72 #define	 TP_CTRL3_FILTER_EN		__BIT(2)
     73 #define	 TP_CTRL3_FILTER_TYPE		__BITS(1,0)
     74 #define	TP_INT			0x10
     75 #define	 TP_INT_TEMP_IRQ_EN	__BIT(18)
     76 #define	 TP_INT_OVERRUN_IRQ_EN	__BIT(17)
     77 #define	 TP_INT_DATA_IRQ_EN	__BIT(16)
     78 #define	 TP_INT_DATA_XY_CHANGE	__BIT(13)
     79 #define	 TP_INT_FIFO_TRIG_LEVEL	__BITS(12,8)
     80 #define	 TP_INT_DATA_DRQ_EN	__BIT(7)
     81 #define	 TP_INT_FIFO_FLUSH	__BIT(4)
     82 #define	 TP_INT_UP_IRQ_EN	__BIT(1)
     83 #define	 TP_INT_DOWN_IRQ_EN	__BIT(0)
     84 #define	TP_FIFOCS		0x14
     85 #define	 TP_FIFOCS_TEMP_IRQ_PENDING __BIT(18)
     86 #define	 TP_FIFOCS_OVERRUN_PENDING __BIT(17)
     87 #define	 TP_FIFOCS_DATA_PENDING	__BIT(16)
     88 #define	 TP_FIFOCS_RXA_CNT	__BITS(12,8)
     89 #define	 TP_FIFOCS_IDLE_FLG	__BIT(2)
     90 #define	 TP_FIFOCS_UP_PENDING	__BIT(1)
     91 #define	 TP_FIFOCS_DOWN_PENDING	__BIT(0)
     92 #define	TP_TPR			0x18
     93 #define	 TP_TPR_TEMP_EN		__BIT(16)
     94 #define	 TP_TPR_TEMP_PER	__BITS(15,0)
     95 #define	TP_CDAT			0x1c
     96 #define	 TP_CDAT_MASK		__BITS(11,0)
     97 #define	TEMP_DATA		0x20
     98 #define	 TEMP_DATA_MASK		__BITS(11,0)
     99 #define	TP_DATA			0x24
    100 #define	 TP_DATA_MASK		__BITS(11,0)
    101 #define	TP_IO_CONFIG		0x28
    102 #define	TP_PORT_DATA		0x2c
    103 #define	 TP_PORT_DATA_MASK	__BITS(3,0)
    104 
    105 #define	TEMP_C_TO_K		273150000
    106 
    107 static int sunxi_ts_match(device_t, cfdata_t, void *);
    108 static void sunxi_ts_attach(device_t, device_t, void *);
    109 
    110 struct sunxi_ts_config {
    111 	int64_t	temp_offset;
    112 	int64_t	temp_step;
    113 	uint32_t tp_mode_en_mask;
    114 };
    115 
    116 static const struct sunxi_ts_config sun4i_a10_ts_config = {
    117 	.temp_offset = 257000000,
    118 	.temp_step = 133000,
    119 	.tp_mode_en_mask = TP_CTRL1_TP_MODE_EN,
    120 };
    121 
    122 static const struct sunxi_ts_config sun5i_a13_ts_config = {
    123 	.temp_offset = 144700000,
    124 	.temp_step = 100000,
    125 	.tp_mode_en_mask = TP_CTRL1_TP_MODE_EN,
    126 };
    127 
    128 static const struct sunxi_ts_config sun6i_a31_ts_config = {
    129 	.temp_offset = 271000000,
    130 	.temp_step = 167000,
    131 	.tp_mode_en_mask = TP_CTRL1_TP_DUAL_EN,
    132 };
    133 
    134 static const struct device_compatible_entry compat_data[] = {
    135 	{ .compat = "allwinner,sun4i-a10-ts",	.data = &sun4i_a10_ts_config },
    136 	{ .compat = "allwinner,sun5i-a13-ts",	.data = &sun5i_a13_ts_config },
    137 	{ .compat = "allwinner,sun6i-a31-ts",	.data = &sun6i_a31_ts_config },
    138 	DEVICE_COMPAT_EOL
    139 };
    140 
    141 static struct wsmouse_calibcoords sunxi_ts_default_calib = {
    142 	.minx = 0,
    143 	.miny = 0,
    144 	.maxx = __SHIFTOUT_MASK(TP_DATA_MASK),
    145 	.maxy = __SHIFTOUT_MASK(TP_DATA_MASK),
    146 	.samplelen = WSMOUSE_CALIBCOORDS_RESET,
    147 };
    148 
    149 struct sunxi_ts_softc {
    150 	device_t		sc_dev;
    151 	int			sc_phandle;
    152 	bus_space_tag_t		sc_bst;
    153 	bus_space_handle_t	sc_bsh;
    154 
    155 	const struct sunxi_ts_config *sc_conf;
    156 
    157 	bool			sc_ts_attached;
    158 	bool			sc_ts_inverted_x;
    159 	bool			sc_ts_inverted_y;
    160 
    161 	struct tpcalib_softc	sc_tpcalib;
    162 	device_t		sc_wsmousedev;
    163 	bool			sc_ignoredata;
    164 
    165 	u_int			sc_tp_x;
    166 	u_int			sc_tp_y;
    167 	u_int			sc_tp_btns;
    168 
    169 	struct sysmon_envsys	*sc_sme;
    170 	envsys_data_t		sc_temp_data;
    171 };
    172 
    173 CFATTACH_DECL_NEW(sunxi_ts, sizeof(struct sunxi_ts_softc),
    174 	sunxi_ts_match, sunxi_ts_attach, NULL, NULL);
    175 
    176 #define	TS_READ(sc, reg)		\
    177 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
    178 #define	TS_WRITE(sc, reg, val)		\
    179 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
    180 
    181 static int
    182 sunxi_ts_enable(void *v)
    183 {
    184 	struct sunxi_ts_softc * const sc = v;
    185 	uint32_t val;
    186 
    187 	/* reset state */
    188 	sc->sc_ignoredata = true;
    189 	sc->sc_tp_x = 0;
    190 	sc->sc_tp_y = 0;
    191 	sc->sc_tp_btns = 0;
    192 
    193 	/* Enable touchpanel IRQs */
    194 	val = TS_READ(sc, TP_INT);
    195 	val |= TP_INT_DATA_IRQ_EN | TP_INT_FIFO_FLUSH | TP_INT_UP_IRQ_EN;
    196 	val &= ~TP_INT_FIFO_TRIG_LEVEL;
    197 	val |= __SHIFTIN(1, TP_INT_FIFO_TRIG_LEVEL);
    198 	TS_WRITE(sc, TP_INT, val);
    199 
    200 	return 0;
    201 }
    202 
    203 static void
    204 sunxi_ts_disable(void *v)
    205 {
    206 	struct sunxi_ts_softc * const sc = v;
    207 	uint32_t val;
    208 
    209 	/* Disable touchpanel IRQs */
    210 	val = TS_READ(sc, TP_INT);
    211 	val &= ~(TP_INT_DATA_IRQ_EN | TP_INT_FIFO_FLUSH | TP_INT_UP_IRQ_EN);
    212 	TS_WRITE(sc, TP_INT, val);
    213 }
    214 
    215 static int
    216 sunxi_ts_ioctl(void *v, u_long cmd, void *data, int flag, lwp_t *l)
    217 {
    218 	struct sunxi_ts_softc * const sc = v;
    219 	struct wsmouse_id *id;
    220 
    221 	switch (cmd) {
    222 	case WSMOUSEIO_GTYPE:
    223 		*(int *)data = WSMOUSE_TYPE_TPANEL;
    224 		return 0;
    225 
    226 	case WSMOUSEIO_GETID:
    227 		id = data;
    228 		if (id->type != WSMOUSE_ID_TYPE_UIDSTR)
    229 			return EINVAL;
    230 		snprintf(id->data, WSMOUSE_ID_MAXLEN,
    231 		    "Allwinner TS SN000000");
    232 		id->length = strlen(id->data);
    233 		return 0;
    234 
    235 	case WSMOUSEIO_SCALIBCOORDS:
    236 	case WSMOUSEIO_GCALIBCOORDS:
    237 		return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l);
    238 	}
    239 
    240 	return EPASSTHROUGH;
    241 }
    242 
    243 static const struct wsmouse_accessops sunxi_ts_accessops = {
    244 	.enable = sunxi_ts_enable,
    245 	.disable = sunxi_ts_disable,
    246 	.ioctl = sunxi_ts_ioctl,
    247 };
    248 
    249 static int
    250 sunxi_ts_intr(void *priv)
    251 {
    252 	struct sunxi_ts_softc * const sc = priv;
    253 	uint32_t fifocs, x, y;
    254 	int s;
    255 
    256 	fifocs = TS_READ(sc, TP_FIFOCS);
    257 
    258 	if (fifocs & TP_FIFOCS_TEMP_IRQ_PENDING) {
    259 		sc->sc_temp_data.value_cur = (TS_READ(sc, TEMP_DATA) *
    260 		    sc->sc_conf->temp_step - sc->sc_conf->temp_offset) +
    261 		    TEMP_C_TO_K;
    262 		sc->sc_temp_data.state = ENVSYS_SVALID;
    263 	}
    264 
    265 	if (fifocs & TP_FIFOCS_DATA_PENDING) {
    266 		x = TS_READ(sc, TP_DATA);
    267 		y = TS_READ(sc, TP_DATA);
    268 		if (sc->sc_ignoredata) {
    269 			/* Discard the first report */
    270 			sc->sc_ignoredata = false;
    271 		} else if (sc->sc_wsmousedev != NULL) {
    272 			if (sc->sc_ts_inverted_x)
    273 				x = __SHIFTOUT_MASK(TP_DATA_MASK) - x;
    274 			if (sc->sc_ts_inverted_y)
    275 				y = __SHIFTOUT_MASK(TP_DATA_MASK) - y;
    276 			tpcalib_trans(&sc->sc_tpcalib, x, y,
    277 			    &sc->sc_tp_x, &sc->sc_tp_y);
    278 			sc->sc_tp_btns |= 1;
    279 
    280 			s = spltty();
    281 			wsmouse_input(sc->sc_wsmousedev,
    282 			    sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0,
    283 			    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
    284 			splx(s);
    285 		}
    286 	}
    287 
    288 	if (fifocs & TP_FIFOCS_UP_PENDING) {
    289 		sc->sc_ignoredata = true;
    290 		sc->sc_tp_btns &= ~1;
    291 		s = spltty();
    292 		wsmouse_input(sc->sc_wsmousedev,
    293 		    sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0,
    294 		    WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y);
    295 		splx(s);
    296 	}
    297 
    298 	TS_WRITE(sc, TP_FIFOCS, fifocs);
    299 
    300 	return 1;
    301 }
    302 
    303 static void
    304 sunxi_ts_init(struct sunxi_ts_softc *sc)
    305 {
    306 	u_int tp_sensitivity_adjust = TS_TP_SENSITIVITY_ADJUST_DEFAULT;
    307 	u_int filter_type = TS_FILTER_TYPE_DEFAULT;
    308 
    309 	of_getprop_uint32(sc->sc_phandle, "allwinner,tp-sensitive-adjust",
    310 	    &tp_sensitivity_adjust);
    311 	of_getprop_uint32(sc->sc_phandle, "allwinner,filter-type",
    312 	    &filter_type);
    313 
    314 	TS_WRITE(sc, TP_CTRL0,
    315 	    __SHIFTIN(0, TP_CTRL0_ADC_CLK_SELECT) |
    316 	    __SHIFTIN(2, TP_CTRL0_ADC_CLK_DIVIDER) |
    317 	    __SHIFTIN(7, TP_CTRL0_FS_DIV) |
    318 	    __SHIFTIN(63, TP_CTRL0_T_ACQ));
    319 	TS_WRITE(sc, TP_CTRL2,
    320 	    __SHIFTIN(0, TP_CTRL2_MODE_SELECT) |
    321 	    __SHIFTIN(tp_sensitivity_adjust, TP_CTRL2_SENSITIVE_ADJUST));
    322 	TS_WRITE(sc, TP_CTRL3,
    323 	    TP_CTRL3_FILTER_EN |
    324 	    __SHIFTIN(filter_type, TP_CTRL3_FILTER_TYPE));
    325 	TS_WRITE(sc, TP_CTRL1,
    326 	    sc->sc_conf->tp_mode_en_mask |
    327 	    TP_CTRL1_STYLUS_UP_DEBOUNCE_EN |
    328 	    __SHIFTIN(5, TP_CTRL1_STYLUS_UP_DEBOUNCE));
    329 
    330 	/* Enable temperature sensor */
    331 	TS_WRITE(sc, TP_TPR,
    332 	    TP_TPR_TEMP_EN | __SHIFTIN(1953, TP_TPR_TEMP_PER));
    333 
    334 	/* Enable temperature sensor IRQ */
    335 	TS_WRITE(sc, TP_INT, TP_INT_TEMP_IRQ_EN);
    336 
    337 	/* Clear pending IRQs */
    338 	TS_WRITE(sc, TP_FIFOCS, TS_READ(sc, TP_FIFOCS));
    339 }
    340 
    341 static int
    342 sunxi_ts_match(device_t parent, cfdata_t cf, void *aux)
    343 {
    344 	struct fdt_attach_args * const faa = aux;
    345 
    346 	return of_compatible_match(faa->faa_phandle, compat_data);
    347 }
    348 
    349 static void
    350 sunxi_ts_attach(device_t parent, device_t self, void *aux)
    351 {
    352 	struct sunxi_ts_softc * const sc = device_private(self);
    353 	struct fdt_attach_args * const faa = aux;
    354 	const int phandle = faa->faa_phandle;
    355 	struct wsmousedev_attach_args a;
    356 	char intrstr[128];
    357 	bus_addr_t addr;
    358 	bus_size_t size;
    359 	void *ih;
    360 
    361 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    362 		aprint_error(": couldn't get registers\n");
    363 		return;
    364 	}
    365 
    366 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
    367 		aprint_error(": failed to decode interrupt\n");
    368 		return;
    369 	}
    370 
    371 	sc->sc_dev = self;
    372 	sc->sc_phandle = phandle;
    373 	sc->sc_bst = faa->faa_bst;
    374 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    375 		aprint_error(": couldn't map registers\n");
    376 		return;
    377 	}
    378 	sc->sc_conf = of_compatible_lookup(phandle, compat_data)->data;
    379 
    380 	sc->sc_ts_attached = of_getprop_bool(phandle, "allwinner,ts-attached");
    381 	sc->sc_ts_inverted_x = of_getprop_bool(phandle,
    382 	    "touchscreen-inverted-x");
    383 	sc->sc_ts_inverted_y = of_getprop_bool(phandle,
    384 	    "touchscreen-inverted-y");
    385 
    386 	aprint_naive("\n");
    387 	aprint_normal(": Touch Screen Controller\n");
    388 
    389 	sunxi_ts_init(sc);
    390 
    391 	ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, sunxi_ts_intr,
    392 	    sc, device_xname(self));
    393 	if (ih == NULL) {
    394 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
    395 		    intrstr);
    396 		return;
    397 	}
    398 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
    399 
    400 	sc->sc_sme = sysmon_envsys_create();
    401 	sc->sc_sme->sme_name = device_xname(self);
    402 	sc->sc_sme->sme_cookie = sc;
    403 	sc->sc_sme->sme_flags = SME_DISABLE_REFRESH;
    404 
    405 	sc->sc_temp_data.units = ENVSYS_STEMP;
    406 	sc->sc_temp_data.state = ENVSYS_SINVALID;
    407 	snprintf(sc->sc_temp_data.desc, sizeof(sc->sc_temp_data.desc),
    408 	    "temperature");
    409 	sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_temp_data);
    410 
    411 	sysmon_envsys_register(sc->sc_sme);
    412 
    413 	if (sc->sc_ts_attached) {
    414 		tpcalib_init(&sc->sc_tpcalib);
    415 		tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS,
    416 		    &sunxi_ts_default_calib, 0, 0);
    417 
    418 		memset(&a, 0, sizeof(a));
    419 		a.accessops = &sunxi_ts_accessops;
    420 		a.accesscookie = sc;
    421 		sc->sc_wsmousedev =
    422 		    config_found(self, &a, wsmousedevprint, CFARGS_NONE);
    423 	}
    424 }
    425