sunxi_ts.c revision 1.5
1/* $NetBSD: sunxi_ts.c,v 1.5 2021/01/18 02:35:49 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.5 2021/01/18 02:35:49 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 139 { 0 } 140}; 141 142static struct wsmouse_calibcoords sunxi_ts_default_calib = { 143 .minx = 0, 144 .miny = 0, 145 .maxx = __SHIFTOUT_MASK(TP_DATA_MASK), 146 .maxy = __SHIFTOUT_MASK(TP_DATA_MASK), 147 .samplelen = WSMOUSE_CALIBCOORDS_RESET, 148}; 149 150struct sunxi_ts_softc { 151 device_t sc_dev; 152 int sc_phandle; 153 bus_space_tag_t sc_bst; 154 bus_space_handle_t sc_bsh; 155 156 const struct sunxi_ts_config *sc_conf; 157 158 bool sc_ts_attached; 159 bool sc_ts_inverted_x; 160 bool sc_ts_inverted_y; 161 162 struct tpcalib_softc sc_tpcalib; 163 device_t sc_wsmousedev; 164 bool sc_ignoredata; 165 166 u_int sc_tp_x; 167 u_int sc_tp_y; 168 u_int sc_tp_btns; 169 170 struct sysmon_envsys *sc_sme; 171 envsys_data_t sc_temp_data; 172}; 173 174CFATTACH_DECL_NEW(sunxi_ts, sizeof(struct sunxi_ts_softc), 175 sunxi_ts_match, sunxi_ts_attach, NULL, NULL); 176 177#define TS_READ(sc, reg) \ 178 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 179#define TS_WRITE(sc, reg, val) \ 180 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 181 182static int 183sunxi_ts_enable(void *v) 184{ 185 struct sunxi_ts_softc * const sc = v; 186 uint32_t val; 187 188 /* reset state */ 189 sc->sc_ignoredata = true; 190 sc->sc_tp_x = 0; 191 sc->sc_tp_y = 0; 192 sc->sc_tp_btns = 0; 193 194 /* Enable touchpanel IRQs */ 195 val = TS_READ(sc, TP_INT); 196 val |= TP_INT_DATA_IRQ_EN | TP_INT_FIFO_FLUSH | TP_INT_UP_IRQ_EN; 197 val &= ~TP_INT_FIFO_TRIG_LEVEL; 198 val |= __SHIFTIN(1, TP_INT_FIFO_TRIG_LEVEL); 199 TS_WRITE(sc, TP_INT, val); 200 201 return 0; 202} 203 204static void 205sunxi_ts_disable(void *v) 206{ 207 struct sunxi_ts_softc * const sc = v; 208 uint32_t val; 209 210 /* Disable touchpanel IRQs */ 211 val = TS_READ(sc, TP_INT); 212 val &= ~(TP_INT_DATA_IRQ_EN | TP_INT_FIFO_FLUSH | TP_INT_UP_IRQ_EN); 213 TS_WRITE(sc, TP_INT, val); 214} 215 216static int 217sunxi_ts_ioctl(void *v, u_long cmd, void *data, int flag, lwp_t *l) 218{ 219 struct sunxi_ts_softc * const sc = v; 220 struct wsmouse_id *id; 221 222 switch (cmd) { 223 case WSMOUSEIO_GTYPE: 224 *(int *)data = WSMOUSE_TYPE_TPANEL; 225 return 0; 226 227 case WSMOUSEIO_GETID: 228 id = data; 229 if (id->type != WSMOUSE_ID_TYPE_UIDSTR) 230 return EINVAL; 231 snprintf(id->data, WSMOUSE_ID_MAXLEN, 232 "Allwinner TS SN000000"); 233 id->length = strlen(id->data); 234 return 0; 235 236 case WSMOUSEIO_SCALIBCOORDS: 237 case WSMOUSEIO_GCALIBCOORDS: 238 return tpcalib_ioctl(&sc->sc_tpcalib, cmd, data, flag, l); 239 } 240 241 return EPASSTHROUGH; 242} 243 244static const struct wsmouse_accessops sunxi_ts_accessops = { 245 .enable = sunxi_ts_enable, 246 .disable = sunxi_ts_disable, 247 .ioctl = sunxi_ts_ioctl, 248}; 249 250static int 251sunxi_ts_intr(void *priv) 252{ 253 struct sunxi_ts_softc * const sc = priv; 254 uint32_t fifocs, x, y; 255 int s; 256 257 fifocs = TS_READ(sc, TP_FIFOCS); 258 259 if (fifocs & TP_FIFOCS_TEMP_IRQ_PENDING) { 260 sc->sc_temp_data.value_cur = (TS_READ(sc, TEMP_DATA) * 261 sc->sc_conf->temp_step - sc->sc_conf->temp_offset) + 262 TEMP_C_TO_K; 263 sc->sc_temp_data.state = ENVSYS_SVALID; 264 } 265 266 if (fifocs & TP_FIFOCS_DATA_PENDING) { 267 x = TS_READ(sc, TP_DATA); 268 y = TS_READ(sc, TP_DATA); 269 if (sc->sc_ignoredata) { 270 /* Discard the first report */ 271 sc->sc_ignoredata = false; 272 } else if (sc->sc_wsmousedev != NULL) { 273 if (sc->sc_ts_inverted_x) 274 x = __SHIFTOUT_MASK(TP_DATA_MASK) - x; 275 if (sc->sc_ts_inverted_y) 276 y = __SHIFTOUT_MASK(TP_DATA_MASK) - y; 277 tpcalib_trans(&sc->sc_tpcalib, x, y, 278 &sc->sc_tp_x, &sc->sc_tp_y); 279 sc->sc_tp_btns |= 1; 280 281 s = spltty(); 282 wsmouse_input(sc->sc_wsmousedev, 283 sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0, 284 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 285 splx(s); 286 } 287 } 288 289 if (fifocs & TP_FIFOCS_UP_PENDING) { 290 sc->sc_ignoredata = true; 291 sc->sc_tp_btns &= ~1; 292 s = spltty(); 293 wsmouse_input(sc->sc_wsmousedev, 294 sc->sc_tp_btns, sc->sc_tp_x, sc->sc_tp_y, 0, 0, 295 WSMOUSE_INPUT_ABSOLUTE_X | WSMOUSE_INPUT_ABSOLUTE_Y); 296 splx(s); 297 } 298 299 TS_WRITE(sc, TP_FIFOCS, fifocs); 300 301 return 1; 302} 303 304static void 305sunxi_ts_init(struct sunxi_ts_softc *sc) 306{ 307 u_int tp_sensitivity_adjust = TS_TP_SENSITIVITY_ADJUST_DEFAULT; 308 u_int filter_type = TS_FILTER_TYPE_DEFAULT; 309 310 of_getprop_uint32(sc->sc_phandle, "allwinner,tp-sensitive-adjust", 311 &tp_sensitivity_adjust); 312 of_getprop_uint32(sc->sc_phandle, "allwinner,filter-type", 313 &filter_type); 314 315 TS_WRITE(sc, TP_CTRL0, 316 __SHIFTIN(0, TP_CTRL0_ADC_CLK_SELECT) | 317 __SHIFTIN(2, TP_CTRL0_ADC_CLK_DIVIDER) | 318 __SHIFTIN(7, TP_CTRL0_FS_DIV) | 319 __SHIFTIN(63, TP_CTRL0_T_ACQ)); 320 TS_WRITE(sc, TP_CTRL2, 321 __SHIFTIN(0, TP_CTRL2_MODE_SELECT) | 322 __SHIFTIN(tp_sensitivity_adjust, TP_CTRL2_SENSITIVE_ADJUST)); 323 TS_WRITE(sc, TP_CTRL3, 324 TP_CTRL3_FILTER_EN | 325 __SHIFTIN(filter_type, TP_CTRL3_FILTER_TYPE)); 326 TS_WRITE(sc, TP_CTRL1, 327 sc->sc_conf->tp_mode_en_mask | 328 TP_CTRL1_STYLUS_UP_DEBOUNCE_EN | 329 __SHIFTIN(5, TP_CTRL1_STYLUS_UP_DEBOUNCE)); 330 331 /* Enable temperature sensor */ 332 TS_WRITE(sc, TP_TPR, 333 TP_TPR_TEMP_EN | __SHIFTIN(1953, TP_TPR_TEMP_PER)); 334 335 /* Enable temperature sensor IRQ */ 336 TS_WRITE(sc, TP_INT, TP_INT_TEMP_IRQ_EN); 337 338 /* Clear pending IRQs */ 339 TS_WRITE(sc, TP_FIFOCS, TS_READ(sc, TP_FIFOCS)); 340} 341 342static int 343sunxi_ts_match(device_t parent, cfdata_t cf, void *aux) 344{ 345 struct fdt_attach_args * const faa = aux; 346 347 return of_match_compat_data(faa->faa_phandle, compat_data); 348} 349 350static void 351sunxi_ts_attach(device_t parent, device_t self, void *aux) 352{ 353 struct sunxi_ts_softc * const sc = device_private(self); 354 struct fdt_attach_args * const faa = aux; 355 const int phandle = faa->faa_phandle; 356 struct wsmousedev_attach_args a; 357 char intrstr[128]; 358 bus_addr_t addr; 359 bus_size_t size; 360 void *ih; 361 362 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 363 aprint_error(": couldn't get registers\n"); 364 return; 365 } 366 367 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 368 aprint_error(": failed to decode interrupt\n"); 369 return; 370 } 371 372 sc->sc_dev = self; 373 sc->sc_phandle = phandle; 374 sc->sc_bst = faa->faa_bst; 375 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 376 aprint_error(": couldn't map registers\n"); 377 return; 378 } 379 sc->sc_conf = of_search_compatible(phandle, compat_data)->data; 380 381 sc->sc_ts_attached = of_getprop_bool(phandle, "allwinner,ts-attached"); 382 sc->sc_ts_inverted_x = of_getprop_bool(phandle, 383 "touchscreen-inverted-x"); 384 sc->sc_ts_inverted_y = of_getprop_bool(phandle, 385 "touchscreen-inverted-y"); 386 387 aprint_naive("\n"); 388 aprint_normal(": Touch Screen Controller\n"); 389 390 sunxi_ts_init(sc); 391 392 ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, sunxi_ts_intr, 393 sc, device_xname(self)); 394 if (ih == NULL) { 395 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 396 intrstr); 397 return; 398 } 399 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 400 401 sc->sc_sme = sysmon_envsys_create(); 402 sc->sc_sme->sme_name = device_xname(self); 403 sc->sc_sme->sme_cookie = sc; 404 sc->sc_sme->sme_flags = SME_DISABLE_REFRESH; 405 406 sc->sc_temp_data.units = ENVSYS_STEMP; 407 sc->sc_temp_data.state = ENVSYS_SINVALID; 408 snprintf(sc->sc_temp_data.desc, sizeof(sc->sc_temp_data.desc), 409 "temperature"); 410 sysmon_envsys_sensor_attach(sc->sc_sme, &sc->sc_temp_data); 411 412 sysmon_envsys_register(sc->sc_sme); 413 414 if (sc->sc_ts_attached) { 415 tpcalib_init(&sc->sc_tpcalib); 416 tpcalib_ioctl(&sc->sc_tpcalib, WSMOUSEIO_SCALIBCOORDS, 417 &sunxi_ts_default_calib, 0, 0); 418 419 memset(&a, 0, sizeof(a)); 420 a.accessops = &sunxi_ts_accessops; 421 a.accesscookie = sc; 422 sc->sc_wsmousedev = config_found_ia(self, "wsmousedev", 423 &a, wsmousedevprint); 424 } 425} 426