18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TSC2004/TSC2005 touchscreen driver core 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006-2010 Nokia Corporation 68c2ecf20Sopenharmony_ci * Copyright (C) 2015 QWERTY Embedded Design 78c2ecf20Sopenharmony_ci * Copyright (C) 2015 EMAC Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com> 108c2ecf20Sopenharmony_ci * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/input.h> 168c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h> 178c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/pm.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/regmap.h> 238c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 248c2ecf20Sopenharmony_ci#include "tsc200x-core.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * The touchscreen interface operates as follows: 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * 1) Pen is pressed against the touchscreen. 308c2ecf20Sopenharmony_ci * 2) TSC200X performs AD conversion. 318c2ecf20Sopenharmony_ci * 3) After the conversion is done TSC200X drives DAV line down. 328c2ecf20Sopenharmony_ci * 4) GPIO IRQ is received and tsc200x_irq_thread() is scheduled. 338c2ecf20Sopenharmony_ci * 5) tsc200x_irq_thread() queues up a transfer to fetch the x, y, z1, z2 348c2ecf20Sopenharmony_ci * values. 358c2ecf20Sopenharmony_ci * 6) tsc200x_irq_thread() reports coordinates to input layer and sets up 368c2ecf20Sopenharmony_ci * tsc200x_penup_timer() to be called after TSC200X_PENUP_TIME_MS (40ms). 378c2ecf20Sopenharmony_ci * 7) When the penup timer expires, there have not been touch or DAV interrupts 388c2ecf20Sopenharmony_ci * during the last 40ms which means the pen has been lifted. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * ESD recovery via a hardware reset is done if the TSC200X doesn't respond 418c2ecf20Sopenharmony_ci * after a configurable period (in ms) of activity. If esd_timeout is 0, the 428c2ecf20Sopenharmony_ci * watchdog is disabled. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const struct regmap_range tsc200x_writable_ranges[] = { 468c2ecf20Sopenharmony_ci regmap_reg_range(TSC200X_REG_AUX_HIGH, TSC200X_REG_CFR2), 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const struct regmap_access_table tsc200x_writable_table = { 508c2ecf20Sopenharmony_ci .yes_ranges = tsc200x_writable_ranges, 518c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(tsc200x_writable_ranges), 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciconst struct regmap_config tsc200x_regmap_config = { 558c2ecf20Sopenharmony_ci .reg_bits = 8, 568c2ecf20Sopenharmony_ci .val_bits = 16, 578c2ecf20Sopenharmony_ci .reg_stride = 0x08, 588c2ecf20Sopenharmony_ci .max_register = 0x78, 598c2ecf20Sopenharmony_ci .read_flag_mask = TSC200X_REG_READ, 608c2ecf20Sopenharmony_ci .write_flag_mask = TSC200X_REG_PND0, 618c2ecf20Sopenharmony_ci .wr_table = &tsc200x_writable_table, 628c2ecf20Sopenharmony_ci .use_single_read = true, 638c2ecf20Sopenharmony_ci .use_single_write = true, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tsc200x_regmap_config); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistruct tsc200x_data { 688c2ecf20Sopenharmony_ci u16 x; 698c2ecf20Sopenharmony_ci u16 y; 708c2ecf20Sopenharmony_ci u16 z1; 718c2ecf20Sopenharmony_ci u16 z2; 728c2ecf20Sopenharmony_ci} __packed; 738c2ecf20Sopenharmony_ci#define TSC200X_DATA_REGS 4 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistruct tsc200x { 768c2ecf20Sopenharmony_ci struct device *dev; 778c2ecf20Sopenharmony_ci struct regmap *regmap; 788c2ecf20Sopenharmony_ci __u16 bustype; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci struct input_dev *idev; 818c2ecf20Sopenharmony_ci char phys[32]; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci struct mutex mutex; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* raw copy of previous x,y,z */ 868c2ecf20Sopenharmony_ci int in_x; 878c2ecf20Sopenharmony_ci int in_y; 888c2ecf20Sopenharmony_ci int in_z1; 898c2ecf20Sopenharmony_ci int in_z2; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci spinlock_t lock; 928c2ecf20Sopenharmony_ci struct timer_list penup_timer; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci unsigned int esd_timeout; 958c2ecf20Sopenharmony_ci struct delayed_work esd_work; 968c2ecf20Sopenharmony_ci unsigned long last_valid_interrupt; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci unsigned int x_plate_ohm; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci bool opened; 1018c2ecf20Sopenharmony_ci bool suspended; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci bool pen_down; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci struct regulator *vio; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 1088c2ecf20Sopenharmony_ci int (*tsc200x_cmd)(struct device *dev, u8 cmd); 1098c2ecf20Sopenharmony_ci int irq; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void tsc200x_update_pen_state(struct tsc200x *ts, 1138c2ecf20Sopenharmony_ci int x, int y, int pressure) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (pressure) { 1168c2ecf20Sopenharmony_ci input_report_abs(ts->idev, ABS_X, x); 1178c2ecf20Sopenharmony_ci input_report_abs(ts->idev, ABS_Y, y); 1188c2ecf20Sopenharmony_ci input_report_abs(ts->idev, ABS_PRESSURE, pressure); 1198c2ecf20Sopenharmony_ci if (!ts->pen_down) { 1208c2ecf20Sopenharmony_ci input_report_key(ts->idev, BTN_TOUCH, !!pressure); 1218c2ecf20Sopenharmony_ci ts->pen_down = true; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } else { 1248c2ecf20Sopenharmony_ci input_report_abs(ts->idev, ABS_PRESSURE, 0); 1258c2ecf20Sopenharmony_ci if (ts->pen_down) { 1268c2ecf20Sopenharmony_ci input_report_key(ts->idev, BTN_TOUCH, 0); 1278c2ecf20Sopenharmony_ci ts->pen_down = false; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci input_sync(ts->idev); 1318c2ecf20Sopenharmony_ci dev_dbg(ts->dev, "point(%4d,%4d), pressure (%4d)\n", x, y, 1328c2ecf20Sopenharmony_ci pressure); 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic irqreturn_t tsc200x_irq_thread(int irq, void *_ts) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct tsc200x *ts = _ts; 1388c2ecf20Sopenharmony_ci unsigned long flags; 1398c2ecf20Sopenharmony_ci unsigned int pressure; 1408c2ecf20Sopenharmony_ci struct tsc200x_data tsdata; 1418c2ecf20Sopenharmony_ci int error; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* read the coordinates */ 1448c2ecf20Sopenharmony_ci error = regmap_bulk_read(ts->regmap, TSC200X_REG_X, &tsdata, 1458c2ecf20Sopenharmony_ci TSC200X_DATA_REGS); 1468c2ecf20Sopenharmony_ci if (unlikely(error)) 1478c2ecf20Sopenharmony_ci goto out; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* validate position */ 1508c2ecf20Sopenharmony_ci if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT)) 1518c2ecf20Sopenharmony_ci goto out; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Skip reading if the pressure components are out of range */ 1548c2ecf20Sopenharmony_ci if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT)) 1558c2ecf20Sopenharmony_ci goto out; 1568c2ecf20Sopenharmony_ci if (unlikely(tsdata.z1 >= tsdata.z2)) 1578c2ecf20Sopenharmony_ci goto out; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* 1608c2ecf20Sopenharmony_ci * Skip point if this is a pen down with the exact same values as 1618c2ecf20Sopenharmony_ci * the value before pen-up - that implies SPI fed us stale data 1628c2ecf20Sopenharmony_ci */ 1638c2ecf20Sopenharmony_ci if (!ts->pen_down && 1648c2ecf20Sopenharmony_ci ts->in_x == tsdata.x && ts->in_y == tsdata.y && 1658c2ecf20Sopenharmony_ci ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) { 1668c2ecf20Sopenharmony_ci goto out; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * At this point we are happy we have a valid and useful reading. 1718c2ecf20Sopenharmony_ci * Remember it for later comparisons. We may now begin downsampling. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci ts->in_x = tsdata.x; 1748c2ecf20Sopenharmony_ci ts->in_y = tsdata.y; 1758c2ecf20Sopenharmony_ci ts->in_z1 = tsdata.z1; 1768c2ecf20Sopenharmony_ci ts->in_z2 = tsdata.z2; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* Compute touch pressure resistance using equation #1 */ 1798c2ecf20Sopenharmony_ci pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1; 1808c2ecf20Sopenharmony_ci pressure = pressure * ts->x_plate_ohm / 4096; 1818c2ecf20Sopenharmony_ci if (unlikely(pressure > MAX_12BIT)) 1828c2ecf20Sopenharmony_ci goto out; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci spin_lock_irqsave(&ts->lock, flags); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci tsc200x_update_pen_state(ts, tsdata.x, tsdata.y, pressure); 1878c2ecf20Sopenharmony_ci mod_timer(&ts->penup_timer, 1888c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(TSC200X_PENUP_TIME_MS)); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ts->lock, flags); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ts->last_valid_interrupt = jiffies; 1938c2ecf20Sopenharmony_ciout: 1948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void tsc200x_penup_timer(struct timer_list *t) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct tsc200x *ts = from_timer(ts, t, penup_timer); 2008c2ecf20Sopenharmony_ci unsigned long flags; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock_irqsave(&ts->lock, flags); 2038c2ecf20Sopenharmony_ci tsc200x_update_pen_state(ts, 0, 0, 0); 2048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ts->lock, flags); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void tsc200x_start_scan(struct tsc200x *ts) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci regmap_write(ts->regmap, TSC200X_REG_CFR0, TSC200X_CFR0_INITVALUE); 2108c2ecf20Sopenharmony_ci regmap_write(ts->regmap, TSC200X_REG_CFR1, TSC200X_CFR1_INITVALUE); 2118c2ecf20Sopenharmony_ci regmap_write(ts->regmap, TSC200X_REG_CFR2, TSC200X_CFR2_INITVALUE); 2128c2ecf20Sopenharmony_ci ts->tsc200x_cmd(ts->dev, TSC200X_CMD_NORMAL); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void tsc200x_stop_scan(struct tsc200x *ts) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci ts->tsc200x_cmd(ts->dev, TSC200X_CMD_STOP); 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_cistatic void tsc200x_reset(struct tsc200x *ts) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if (ts->reset_gpio) { 2238c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 1); 2248c2ecf20Sopenharmony_ci usleep_range(100, 500); /* only 10us required */ 2258c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(ts->reset_gpio, 0); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* must be called with ts->mutex held */ 2308c2ecf20Sopenharmony_cistatic void __tsc200x_disable(struct tsc200x *ts) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci tsc200x_stop_scan(ts); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci disable_irq(ts->irq); 2358c2ecf20Sopenharmony_ci del_timer_sync(&ts->penup_timer); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ts->esd_work); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci enable_irq(ts->irq); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci/* must be called with ts->mutex held */ 2438c2ecf20Sopenharmony_cistatic void __tsc200x_enable(struct tsc200x *ts) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci tsc200x_start_scan(ts); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (ts->esd_timeout && ts->reset_gpio) { 2488c2ecf20Sopenharmony_ci ts->last_valid_interrupt = jiffies; 2498c2ecf20Sopenharmony_ci schedule_delayed_work(&ts->esd_work, 2508c2ecf20Sopenharmony_ci round_jiffies_relative( 2518c2ecf20Sopenharmony_ci msecs_to_jiffies(ts->esd_timeout))); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic ssize_t tsc200x_selftest_show(struct device *dev, 2568c2ecf20Sopenharmony_ci struct device_attribute *attr, 2578c2ecf20Sopenharmony_ci char *buf) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct tsc200x *ts = dev_get_drvdata(dev); 2608c2ecf20Sopenharmony_ci unsigned int temp_high; 2618c2ecf20Sopenharmony_ci unsigned int temp_high_orig; 2628c2ecf20Sopenharmony_ci unsigned int temp_high_test; 2638c2ecf20Sopenharmony_ci bool success = true; 2648c2ecf20Sopenharmony_ci int error; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mutex_lock(&ts->mutex); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * Test TSC200X communications via temp high register. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci __tsc200x_disable(ts); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high_orig); 2748c2ecf20Sopenharmony_ci if (error) { 2758c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed: read error %d\n", error); 2768c2ecf20Sopenharmony_ci success = false; 2778c2ecf20Sopenharmony_ci goto out; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci temp_high_test = (temp_high_orig - 1) & MAX_12BIT; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci error = regmap_write(ts->regmap, TSC200X_REG_TEMP_HIGH, temp_high_test); 2838c2ecf20Sopenharmony_ci if (error) { 2848c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed: write error %d\n", error); 2858c2ecf20Sopenharmony_ci success = false; 2868c2ecf20Sopenharmony_ci goto out; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high); 2908c2ecf20Sopenharmony_ci if (error) { 2918c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed: read error %d after write\n", 2928c2ecf20Sopenharmony_ci error); 2938c2ecf20Sopenharmony_ci success = false; 2948c2ecf20Sopenharmony_ci goto out; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (temp_high != temp_high_test) { 2988c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed: %d != %d\n", 2998c2ecf20Sopenharmony_ci temp_high, temp_high_test); 3008c2ecf20Sopenharmony_ci success = false; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* hardware reset */ 3048c2ecf20Sopenharmony_ci tsc200x_reset(ts); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci if (!success) 3078c2ecf20Sopenharmony_ci goto out; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* test that the reset really happened */ 3108c2ecf20Sopenharmony_ci error = regmap_read(ts->regmap, TSC200X_REG_TEMP_HIGH, &temp_high); 3118c2ecf20Sopenharmony_ci if (error) { 3128c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed: read error %d after reset\n", 3138c2ecf20Sopenharmony_ci error); 3148c2ecf20Sopenharmony_ci success = false; 3158c2ecf20Sopenharmony_ci goto out; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (temp_high != temp_high_orig) { 3198c2ecf20Sopenharmony_ci dev_warn(dev, "selftest failed after reset: %d != %d\n", 3208c2ecf20Sopenharmony_ci temp_high, temp_high_orig); 3218c2ecf20Sopenharmony_ci success = false; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ciout: 3258c2ecf20Sopenharmony_ci __tsc200x_enable(ts); 3268c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", success); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic DEVICE_ATTR(selftest, S_IRUGO, tsc200x_selftest_show, NULL); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic struct attribute *tsc200x_attrs[] = { 3348c2ecf20Sopenharmony_ci &dev_attr_selftest.attr, 3358c2ecf20Sopenharmony_ci NULL 3368c2ecf20Sopenharmony_ci}; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic umode_t tsc200x_attr_is_visible(struct kobject *kobj, 3398c2ecf20Sopenharmony_ci struct attribute *attr, int n) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci struct device *dev = container_of(kobj, struct device, kobj); 3428c2ecf20Sopenharmony_ci struct tsc200x *ts = dev_get_drvdata(dev); 3438c2ecf20Sopenharmony_ci umode_t mode = attr->mode; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (attr == &dev_attr_selftest.attr) { 3468c2ecf20Sopenharmony_ci if (!ts->reset_gpio) 3478c2ecf20Sopenharmony_ci mode = 0; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci return mode; 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const struct attribute_group tsc200x_attr_group = { 3548c2ecf20Sopenharmony_ci .is_visible = tsc200x_attr_is_visible, 3558c2ecf20Sopenharmony_ci .attrs = tsc200x_attrs, 3568c2ecf20Sopenharmony_ci}; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic void tsc200x_esd_work(struct work_struct *work) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct tsc200x *ts = container_of(work, struct tsc200x, esd_work.work); 3618c2ecf20Sopenharmony_ci int error; 3628c2ecf20Sopenharmony_ci unsigned int r; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (!mutex_trylock(&ts->mutex)) { 3658c2ecf20Sopenharmony_ci /* 3668c2ecf20Sopenharmony_ci * If the mutex is taken, it means that disable or enable is in 3678c2ecf20Sopenharmony_ci * progress. In that case just reschedule the work. If the work 3688c2ecf20Sopenharmony_ci * is not needed, it will be canceled by disable. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci goto reschedule; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (time_is_after_jiffies(ts->last_valid_interrupt + 3748c2ecf20Sopenharmony_ci msecs_to_jiffies(ts->esd_timeout))) 3758c2ecf20Sopenharmony_ci goto out; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* We should be able to read register without disabling interrupts. */ 3788c2ecf20Sopenharmony_ci error = regmap_read(ts->regmap, TSC200X_REG_CFR0, &r); 3798c2ecf20Sopenharmony_ci if (!error && 3808c2ecf20Sopenharmony_ci !((r ^ TSC200X_CFR0_INITVALUE) & TSC200X_CFR0_RW_MASK)) { 3818c2ecf20Sopenharmony_ci goto out; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* 3858c2ecf20Sopenharmony_ci * If we could not read our known value from configuration register 0 3868c2ecf20Sopenharmony_ci * then we should reset the controller as if from power-up and start 3878c2ecf20Sopenharmony_ci * scanning again. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci dev_info(ts->dev, "TSC200X not responding - resetting\n"); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci disable_irq(ts->irq); 3928c2ecf20Sopenharmony_ci del_timer_sync(&ts->penup_timer); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci tsc200x_update_pen_state(ts, 0, 0, 0); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci tsc200x_reset(ts); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci enable_irq(ts->irq); 3998c2ecf20Sopenharmony_ci tsc200x_start_scan(ts); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ciout: 4028c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 4038c2ecf20Sopenharmony_cireschedule: 4048c2ecf20Sopenharmony_ci /* re-arm the watchdog */ 4058c2ecf20Sopenharmony_ci schedule_delayed_work(&ts->esd_work, 4068c2ecf20Sopenharmony_ci round_jiffies_relative( 4078c2ecf20Sopenharmony_ci msecs_to_jiffies(ts->esd_timeout))); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int tsc200x_open(struct input_dev *input) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct tsc200x *ts = input_get_drvdata(input); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci mutex_lock(&ts->mutex); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (!ts->suspended) 4178c2ecf20Sopenharmony_ci __tsc200x_enable(ts); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ts->opened = true; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void tsc200x_close(struct input_dev *input) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct tsc200x *ts = input_get_drvdata(input); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci mutex_lock(&ts->mutex); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (!ts->suspended) 4338c2ecf20Sopenharmony_ci __tsc200x_disable(ts); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci ts->opened = false; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ciint tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id, 4418c2ecf20Sopenharmony_ci struct regmap *regmap, 4428c2ecf20Sopenharmony_ci int (*tsc200x_cmd)(struct device *dev, u8 cmd)) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct tsc200x *ts; 4458c2ecf20Sopenharmony_ci struct input_dev *input_dev; 4468c2ecf20Sopenharmony_ci u32 x_plate_ohm; 4478c2ecf20Sopenharmony_ci u32 esd_timeout; 4488c2ecf20Sopenharmony_ci int error; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (irq <= 0) { 4518c2ecf20Sopenharmony_ci dev_err(dev, "no irq\n"); 4528c2ecf20Sopenharmony_ci return -ENODEV; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) 4568c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!tsc200x_cmd) { 4598c2ecf20Sopenharmony_ci dev_err(dev, "no cmd function\n"); 4608c2ecf20Sopenharmony_ci return -ENODEV; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); 4648c2ecf20Sopenharmony_ci if (!ts) 4658c2ecf20Sopenharmony_ci return -ENOMEM; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci input_dev = devm_input_allocate_device(dev); 4688c2ecf20Sopenharmony_ci if (!input_dev) 4698c2ecf20Sopenharmony_ci return -ENOMEM; 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci ts->irq = irq; 4728c2ecf20Sopenharmony_ci ts->dev = dev; 4738c2ecf20Sopenharmony_ci ts->idev = input_dev; 4748c2ecf20Sopenharmony_ci ts->regmap = regmap; 4758c2ecf20Sopenharmony_ci ts->tsc200x_cmd = tsc200x_cmd; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci error = device_property_read_u32(dev, "ti,x-plate-ohms", &x_plate_ohm); 4788c2ecf20Sopenharmony_ci ts->x_plate_ohm = error ? TSC200X_DEF_RESISTOR : x_plate_ohm; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci error = device_property_read_u32(dev, "ti,esd-recovery-timeout-ms", 4818c2ecf20Sopenharmony_ci &esd_timeout); 4828c2ecf20Sopenharmony_ci ts->esd_timeout = error ? 0 : esd_timeout; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 4858c2ecf20Sopenharmony_ci if (IS_ERR(ts->reset_gpio)) { 4868c2ecf20Sopenharmony_ci error = PTR_ERR(ts->reset_gpio); 4878c2ecf20Sopenharmony_ci dev_err(dev, "error acquiring reset gpio: %d\n", error); 4888c2ecf20Sopenharmony_ci return error; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ts->vio = devm_regulator_get(dev, "vio"); 4928c2ecf20Sopenharmony_ci if (IS_ERR(ts->vio)) { 4938c2ecf20Sopenharmony_ci error = PTR_ERR(ts->vio); 4948c2ecf20Sopenharmony_ci dev_err(dev, "error acquiring vio regulator: %d", error); 4958c2ecf20Sopenharmony_ci return error; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci mutex_init(&ts->mutex); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci spin_lock_init(&ts->lock); 5018c2ecf20Sopenharmony_ci timer_setup(&ts->penup_timer, tsc200x_penup_timer, 0); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ts->esd_work, tsc200x_esd_work); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci snprintf(ts->phys, sizeof(ts->phys), 5068c2ecf20Sopenharmony_ci "%s/input-ts", dev_name(dev)); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (tsc_id->product == 2004) { 5098c2ecf20Sopenharmony_ci input_dev->name = "TSC200X touchscreen"; 5108c2ecf20Sopenharmony_ci } else { 5118c2ecf20Sopenharmony_ci input_dev->name = devm_kasprintf(dev, GFP_KERNEL, 5128c2ecf20Sopenharmony_ci "TSC%04d touchscreen", 5138c2ecf20Sopenharmony_ci tsc_id->product); 5148c2ecf20Sopenharmony_ci if (!input_dev->name) 5158c2ecf20Sopenharmony_ci return -ENOMEM; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci input_dev->phys = ts->phys; 5198c2ecf20Sopenharmony_ci input_dev->id = *tsc_id; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci input_dev->open = tsc200x_open; 5228c2ecf20Sopenharmony_ci input_dev->close = tsc200x_close; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, ts); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); 5278c2ecf20Sopenharmony_ci input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 5308c2ecf20Sopenharmony_ci 0, MAX_12BIT, TSC200X_DEF_X_FUZZ, 0); 5318c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 5328c2ecf20Sopenharmony_ci 0, MAX_12BIT, TSC200X_DEF_Y_FUZZ, 0); 5338c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 5348c2ecf20Sopenharmony_ci 0, MAX_12BIT, TSC200X_DEF_P_FUZZ, 0); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci touchscreen_parse_properties(input_dev, false, NULL); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Ensure the touchscreen is off */ 5398c2ecf20Sopenharmony_ci tsc200x_stop_scan(ts); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(dev, irq, NULL, 5428c2ecf20Sopenharmony_ci tsc200x_irq_thread, 5438c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 5448c2ecf20Sopenharmony_ci "tsc200x", ts); 5458c2ecf20Sopenharmony_ci if (error) { 5468c2ecf20Sopenharmony_ci dev_err(dev, "Failed to request irq, err: %d\n", error); 5478c2ecf20Sopenharmony_ci return error; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci error = regulator_enable(ts->vio); 5518c2ecf20Sopenharmony_ci if (error) 5528c2ecf20Sopenharmony_ci return error; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ts); 5558c2ecf20Sopenharmony_ci error = sysfs_create_group(&dev->kobj, &tsc200x_attr_group); 5568c2ecf20Sopenharmony_ci if (error) { 5578c2ecf20Sopenharmony_ci dev_err(dev, 5588c2ecf20Sopenharmony_ci "Failed to create sysfs attributes, err: %d\n", error); 5598c2ecf20Sopenharmony_ci goto disable_regulator; 5608c2ecf20Sopenharmony_ci } 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci error = input_register_device(ts->idev); 5638c2ecf20Sopenharmony_ci if (error) { 5648c2ecf20Sopenharmony_ci dev_err(dev, 5658c2ecf20Sopenharmony_ci "Failed to register input device, err: %d\n", error); 5668c2ecf20Sopenharmony_ci goto err_remove_sysfs; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci irq_set_irq_wake(irq, 1); 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_cierr_remove_sysfs: 5738c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); 5748c2ecf20Sopenharmony_cidisable_regulator: 5758c2ecf20Sopenharmony_ci regulator_disable(ts->vio); 5768c2ecf20Sopenharmony_ci return error; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tsc200x_probe); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ciint tsc200x_remove(struct device *dev) 5818c2ecf20Sopenharmony_ci{ 5828c2ecf20Sopenharmony_ci struct tsc200x *ts = dev_get_drvdata(dev); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->kobj, &tsc200x_attr_group); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci regulator_disable(ts->vio); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return 0; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tsc200x_remove); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int __maybe_unused tsc200x_suspend(struct device *dev) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct tsc200x *ts = dev_get_drvdata(dev); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci mutex_lock(&ts->mutex); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (!ts->suspended && ts->opened) 5998c2ecf20Sopenharmony_ci __tsc200x_disable(ts); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci ts->suspended = true; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int __maybe_unused tsc200x_resume(struct device *dev) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct tsc200x *ts = dev_get_drvdata(dev); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci mutex_lock(&ts->mutex); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci if (ts->suspended && ts->opened) 6158c2ecf20Sopenharmony_ci __tsc200x_enable(ts); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci ts->suspended = false; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci mutex_unlock(&ts->mutex); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci return 0; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ciSIMPLE_DEV_PM_OPS(tsc200x_pm_ops, tsc200x_suspend, tsc200x_resume); 6258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tsc200x_pm_ops); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>"); 6288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TSC200x Touchscreen Driver Core"); 6298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 630