sunxi_ts.c revision 1.9
1/* $NetBSD: sunxi_ts.c,v 1.9 2021/04/24 23:36:29 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@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.9 2021/04/24 23:36:29 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 107static int sunxi_ts_match(device_t, cfdata_t, void *); 108static void sunxi_ts_attach(device_t, device_t, void *); 109 110struct sunxi_ts_config { 111 int64_t temp_offset; 112 int64_t temp_step; 113 uint32_t tp_mode_en_mask; 114}; 115 116static 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 122static 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 128static 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 134static 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 141static 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 149struct 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 173CFATTACH_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 181static int 182sunxi_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 203static void 204sunxi_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 215static int 216sunxi_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 243static const struct wsmouse_accessops sunxi_ts_accessops = { 244 .enable = sunxi_ts_enable, 245 .disable = sunxi_ts_disable, 246 .ioctl = sunxi_ts_ioctl, 247}; 248 249static int 250sunxi_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 303static void 304sunxi_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 341static int 342sunxi_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 349static void 350sunxi_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, CFARG_EOL); 423 } 424} 425