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 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, CFARG_EOL); 423 } 424 } 425